001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.compilers.opt.runtimesupport; 014 015import static org.jikesrvm.VM.NOT_REACHED; 016import static org.jikesrvm.compilers.opt.ir.Operators.IG_PATCH_POINT; 017 018import org.jikesrvm.VM; 019import org.jikesrvm.architecture.ArchConstants; 020import org.jikesrvm.classloader.RVMArray; 021import org.jikesrvm.classloader.MemberReference; 022import org.jikesrvm.classloader.RVMMethod; 023import org.jikesrvm.classloader.NormalMethod; 024import org.jikesrvm.classloader.RVMType; 025import org.jikesrvm.classloader.TypeReference; 026import org.jikesrvm.compilers.common.CodeArray; 027import org.jikesrvm.compilers.common.CompiledMethod; 028import org.jikesrvm.compilers.common.ExceptionTable; 029import org.jikesrvm.compilers.opt.ir.IR; 030import org.jikesrvm.compilers.opt.ir.InlineGuard; 031import org.jikesrvm.compilers.opt.ir.Instruction; 032import org.jikesrvm.compilers.opt.mir2mc.MachineCodeOffsets; 033import org.jikesrvm.osr.EncodedOSRMap; 034import org.jikesrvm.runtime.DynamicLink; 035import org.jikesrvm.runtime.ExceptionDeliverer; 036import org.jikesrvm.runtime.Magic; 037import org.jikesrvm.runtime.Memory; 038import org.jikesrvm.runtime.StackBrowser; 039import org.jikesrvm.scheduler.RVMThread; 040import org.jikesrvm.util.PrintLN; 041import org.vmmagic.pragma.Interruptible; 042import org.vmmagic.pragma.Uninterruptible; 043import org.vmmagic.pragma.Unpreemptible; 044import org.vmmagic.unboxed.Offset; 045 046/** 047 * An implementation of CompiledMethod for the OPT compiler. 048 * 049 * <p> NOTE: OptCompiledMethod live as long as their corresponding 050 * compiled machine code. Therefore, they should only contain 051 * state that is really required to be persistent. Anything 052 * transitory should be stored on the IR object. 053 */ 054@Uninterruptible 055public final class OptCompiledMethod extends CompiledMethod { 056 057 public OptCompiledMethod(int id, RVMMethod m) { 058 super(id, m); 059 } 060 061 /** 062 * @return {@link CompiledMethod#OPT} 063 */ 064 @Override 065 public int getCompilerType() { 066 return CompiledMethod.OPT; 067 } 068 069 @Override 070 public String getCompilerName() { 071 return "optimizing compiler"; 072 } 073 074 @Override 075 public ExceptionDeliverer getExceptionDeliverer() { 076 return exceptionDeliverer; 077 } 078 079 /** 080 * Find "catch" block for a machine instruction of this method. 081 */ 082 @Override 083 @Unpreemptible 084 public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) { 085 if (eTable == null) { 086 return -1; 087 } else { 088 int catchOffset = ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType); 089 dealWithPossibleRemovalOfCatchBlockByTheOptCompiler(instructionOffset, 090 exceptionType, catchOffset); 091 return catchOffset; 092 } 093 } 094 095 @Uninterruptible 096 private void dealWithPossibleRemovalOfCatchBlockByTheOptCompiler( 097 Offset instructionOffset, RVMType exceptionType, int catchOffset) { 098 if (OptExceptionTable.belongsToUnreachableCatchBlock(catchOffset)) { 099 if (VM.VerifyAssertions) { 100 VM.sysWriteln("Attempted to use a catch block that was determined to be unreachable" + 101 " by the optimizing compiler and thus removed from the IR before " + 102 "code was generated for it."); 103 VM.sysWrite("Instruction offset: "); 104 VM.sysWrite(instructionOffset); 105 VM.sysWriteln(); 106 VM.sysWrite("Exception type: "); 107 VM.sysWrite(exceptionType.getDescriptor()); 108 VM.sysWriteln(); 109 ExceptionTable.printExceptionTableUninterruptible(eTable); 110 VM._assert(VM.NOT_REACHED, 111 "Attempted to use catch block that was removed from the code by the opt compiler!"); 112 } else { 113 if (VM.TraceExceptionDelivery) { 114 VM.sysWriteln("Found a catch block that was determined by the optimizing" + 115 " compiler to be unreachable (and thus removed), ignoring it."); 116 } 117 // Nothing more to do. The unreachable catch block marker is negative which 118 // will cause the exception delivery code to ignore the catch block. 119 } 120 } 121 } 122 123 124 125 /** 126 * Fetch symbolic reference to a method that's called 127 * by one of this method's instructions. 128 * @param dynamicLink place to put return information 129 * @param instructionOffset offset of machine instruction that issued 130 * the call 131 */ 132 @Override 133 public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) { 134 int bci = _mcMap.getBytecodeIndexForMCOffset(instructionOffset); 135 NormalMethod realMethod = _mcMap.getMethodForMCOffset(instructionOffset); 136 if (bci == -1 || realMethod == null) { 137 VM.sysFail("Mapping to source code location not available at Dynamic Linking point\n"); 138 } 139 realMethod.getDynamicLink(dynamicLink, bci); 140 } 141 142 @Override 143 @Interruptible 144 public boolean isWithinUninterruptibleCode(Offset instructionOffset) { 145 NormalMethod realMethod = _mcMap.getMethodForMCOffset(instructionOffset); 146 147 // Use an explicit null check here because this method is called from 148 // code for delivery of hardware exceptions. That code is unpreemptible, so 149 // a NullPointerException in this method would lead to a crash due to recursive 150 // use of hardware exception registers. It is better to crash with a reasonable 151 // error message when no method is found. 152 if (realMethod == null) { 153 VM.sysWrite("Failing instruction offset: "); 154 VM.sysWrite(instructionOffset); 155 VM.sysWrite(" in method "); 156 RVMMethod thisMethod = this.getMethod(); 157 VM.sysWrite(thisMethod.getName()); 158 VM.sysWrite(" with descriptor "); 159 VM.sysWriteln(thisMethod.getDescriptor()); 160 String msg = "Couldn't find a method for given instruction offset"; 161 if (VM.VerifyAssertions) { 162 VM._assert(NOT_REACHED, msg); 163 } else { 164 VM.sysFail(msg); 165 } 166 } 167 168 return realMethod.isUninterruptible(); 169 } 170 171 /** 172 * Find source line number corresponding to one of this method's 173 * machine instructions. 174 */ 175 @Override 176 public int findLineNumberForInstruction(Offset instructionOffset) { 177 int bci = _mcMap.getBytecodeIndexForMCOffset(instructionOffset); 178 if (bci < 0) { 179 return 0; 180 } 181 return ((NormalMethod) method).getLineNumberForBCIndex(bci); 182 } 183 184 @Override 185 @Interruptible 186 public void set(StackBrowser browser, Offset instr) { 187 OptMachineCodeMap map = getMCMap(); 188 int iei = map.getInlineEncodingForMCOffset(instr); 189 if (iei >= 0) { 190 int[] inlineEncoding = map.inlineEncoding; 191 int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding); 192 193 browser.setInlineEncodingIndex(iei); 194 browser.setBytecodeIndex(map.getBytecodeIndexForMCOffset(instr)); 195 browser.setCompiledMethod(this); 196 browser.setMethod(MemberReference.getMethodRef(mid).peekResolvedMethod()); 197 198 if (VM.TraceStackTrace) { 199 VM.sysWrite("setting stack to frame (opt): "); 200 VM.sysWrite(browser.getMethod()); 201 VM.sysWrite(browser.getBytecodeIndex()); 202 VM.sysWrite("\n"); 203 } 204 } else { 205 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 206 } 207 } 208 209 @Override 210 @Interruptible 211 public boolean up(StackBrowser browser) { 212 OptMachineCodeMap map = getMCMap(); 213 int iei = browser.getInlineEncodingIndex(); 214 int[] ie = map.inlineEncoding; 215 int next = OptEncodedCallSiteTree.getParent(iei, ie); 216 if (next >= 0) { 217 int mid = OptEncodedCallSiteTree.getMethodID(next, ie); 218 int bci = OptEncodedCallSiteTree.getByteCodeOffset(iei, ie); 219 220 browser.setInlineEncodingIndex(next); 221 browser.setBytecodeIndex(bci); 222 browser.setMethod(MemberReference.getMethodRef(mid).peekResolvedMethod()); 223 224 if (VM.TraceStackTrace) { 225 VM.sysWrite("up within frame stack (opt): "); 226 VM.sysWrite(browser.getMethod()); 227 VM.sysWrite(browser.getBytecodeIndex()); 228 VM.sysWrite("\n"); 229 } 230 231 return true; 232 } else { 233 return false; 234 } 235 } 236 237 @Override 238 @Interruptible 239 public void printStackTrace(Offset instructionOffset, PrintLN out) { 240 OptMachineCodeMap map = getMCMap(); 241 int iei = map.getInlineEncodingForMCOffset(instructionOffset); 242 if (iei >= 0) { 243 int[] inlineEncoding = map.inlineEncoding; 244 int bci = map.getBytecodeIndexForMCOffset(instructionOffset); 245 for (int j = iei; j >= 0; j = OptEncodedCallSiteTree.getParent(j, inlineEncoding)) { 246 int mid = OptEncodedCallSiteTree.getMethodID(j, inlineEncoding); 247 NormalMethod m = 248 (NormalMethod) MemberReference.getMethodRef(mid).peekResolvedMethod(); 249 int lineNumber = m.getLineNumberForBCIndex(bci); // might be 0 if unavailable. 250 out.print("\tat "); 251 out.print(m.getDeclaringClass()); 252 out.print('.'); 253 out.print(m.getName()); 254 out.print('('); 255 out.print(m.getDeclaringClass().getSourceName()); 256 out.print(':'); 257 out.print(lineNumber); 258 out.print(')'); 259 out.println(); 260 if (j > 0) { 261 bci = OptEncodedCallSiteTree.getByteCodeOffset(j, inlineEncoding); 262 } 263 } 264 } else { 265 out.print("\tat "); 266 out.print(method.getDeclaringClass()); 267 out.print('.'); 268 out.print(method.getName()); 269 out.print('('); 270 out.print(method.getDeclaringClass().getSourceName()); 271 out.print("; machine code offset: "); 272 out.printHex(instructionOffset.toInt()); 273 out.print(')'); 274 out.println(); 275 } 276 } 277 278 @Override 279 @Interruptible 280 public int size() { 281 int size = TypeReference.ExceptionTable.peekType().asClass().getInstanceSize(); 282 size += _mcMap.size(); 283 if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length); 284 if (patchMap != null) size += RVMArray.IntArray.getInstanceSize(patchMap.length); 285 return size; 286 } 287 288 //----------------// 289 // implementation // 290 //----------------// 291 private static final ExceptionDeliverer exceptionDeliverer; 292 293 static { 294 if (VM.BuildForIA32) { 295 exceptionDeliverer = new org.jikesrvm.compilers.opt.runtimesupport.ia32.OptExceptionDeliverer(); 296 } else { 297 if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); 298 exceptionDeliverer = new org.jikesrvm.compilers.opt.runtimesupport.ppc.OptExceptionDeliverer(); 299 } 300 } 301 302 private EncodedOSRMap _osrMap; 303 304 @Interruptible 305 public void createFinalOSRMap(IR ir) { 306 this._osrMap = EncodedOSRMap.makeMap(ir.MIRInfo.osrVarMap, ir.MIRInfo.mcOffsets); 307 } 308 309 public EncodedOSRMap getOSRMap() { 310 return this._osrMap; 311 } 312 313 ////////////////////////////////////// 314 // Information the opt compiler needs to persistently associate 315 // with a particular compiled method. 316 317 /** The primary machine code maps */ 318 private OptMachineCodeMap _mcMap; 319 /** The encoded exception tables (null if there are none) */ 320 private int[] eTable; 321 private int[] patchMap; 322 323 /** 324 * unsigned offset (off the framepointer) of nonvolatile save area 325 * in bytes 326 */ 327 private char nonvolatileOffset; 328 /** 329 * unsigned offset (off the framepointer) of caught exception 330 * object in bytes 331 */ 332 private char exceptionObjectOffset; 333 /** 334 * size of the fixed portion of the stackframe 335 */ 336 private char stackFrameFixedSize; 337 /** 338 * first saved nonvolatile integer register (-1 if no nonvolatile 339 * GPRs) 340 */ 341 private byte firstNonvolatileGPR; 342 /** 343 * first saved nonvolatile floating point register (-1 if no 344 * nonvolatile FPRs) 345 */ 346 private byte firstNonvolatileFPR; 347 /** opt level at which the method was compiled */ 348 private byte optLevel; 349 /** were the volatile registers saved? */ 350 private boolean volatilesSaved; 351 /** is the current method executing with instrumentation */ 352 private boolean instrumented; 353 354 public int getUnsignedNonVolatileOffset() { 355 return nonvolatileOffset; 356 } 357 358 public int getUnsignedExceptionOffset() { 359 return exceptionObjectOffset; 360 } 361 362 public int getFirstNonVolatileGPR() { 363 return firstNonvolatileGPR; 364 } 365 366 public int getFirstNonVolatileFPR() { 367 return firstNonvolatileFPR; 368 } 369 370 public int getOptLevel() { 371 return optLevel; 372 } 373 374 public boolean isSaveVolatile() { 375 return volatilesSaved; 376 } 377 378 public boolean isInstrumentedMethod() { 379 return instrumented; 380 } 381 382 public int getFrameFixedSize() { 383 return stackFrameFixedSize; 384 } 385 386 public void setUnsignedNonVolatileOffset(int x) { 387 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF); 388 nonvolatileOffset = (char) x; 389 } 390 391 public void setUnsignedExceptionOffset(int x) { 392 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF); 393 exceptionObjectOffset = (char) x; 394 } 395 396 public void setFirstNonVolatileGPR(int x) { 397 if (VM.VerifyAssertions) VM._assert(x >= -1 && x < 0x7F); 398 firstNonvolatileGPR = (byte) x; 399 } 400 401 public void setFirstNonVolatileFPR(int x) { 402 if (VM.VerifyAssertions) VM._assert(x >= -1 && x < 0x7F); 403 firstNonvolatileFPR = (byte) x; 404 } 405 406 public void setOptLevel(int x) { 407 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0x7F); 408 optLevel = (byte) x; 409 } 410 411 public void setSaveVolatile(boolean sv) { 412 volatilesSaved = sv; 413 } 414 415 public void setInstrumentedMethod(boolean _instrumented) { 416 instrumented = _instrumented; 417 } 418 419 public void setFrameFixedSize(int x) { 420 if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF); 421 stackFrameFixedSize = (char) x; 422 } 423 424 /** 425 * @return the number of non-volatile GPRs used by this method. 426 */ 427 public int getNumberOfNonvolatileGPRs() { 428 if (VM.BuildForPowerPC) { 429 return org.jikesrvm.ppc.RegisterConstants.NUM_GPRS - getFirstNonVolatileGPR(); 430 } else if (VM.BuildForIA32) { 431 return org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_GPRS - getFirstNonVolatileGPR(); 432 } else if (VM.VerifyAssertions) { 433 VM._assert(VM.NOT_REACHED); 434 } 435 return -1; 436 } 437 438 /** 439 * @return the number of non-volatile FPRs used by this method. 440 */ 441 public int getNumberOfNonvolatileFPRs() { 442 if (VM.BuildForPowerPC) { 443 return org.jikesrvm.ppc.RegisterConstants.NUM_FPRS - getFirstNonVolatileFPR(); 444 } else if (VM.BuildForIA32) { 445 return org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_FPRS - getFirstNonVolatileFPR(); 446 } else if (VM.VerifyAssertions) { 447 VM._assert(VM.NOT_REACHED); 448 } 449 return -1; 450 } 451 452 public void setNumberOfNonvolatileGPRs(short n) { 453 if (VM.BuildForPowerPC) { 454 setFirstNonVolatileGPR(org.jikesrvm.ppc.RegisterConstants.NUM_GPRS - n); 455 } else if (VM.BuildForIA32) { 456 setFirstNonVolatileGPR(org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_GPRS - n); 457 } else if (VM.VerifyAssertions) { 458 VM._assert(VM.NOT_REACHED); 459 } 460 } 461 462 public void setNumberOfNonvolatileFPRs(short n) { 463 if (VM.BuildForPowerPC) { 464 setFirstNonVolatileFPR(org.jikesrvm.ppc.RegisterConstants.NUM_FPRS - n); 465 } else if (VM.BuildForIA32) { 466 setFirstNonVolatileFPR(org.jikesrvm.ia32.RegisterConstants.NUM_NONVOLATILE_FPRS - n); 467 } else if (VM.VerifyAssertions) { 468 VM._assert(VM.NOT_REACHED); 469 } 470 } 471 472 @Interruptible 473 public void printExceptionTable() { 474 if (eTable != null) ExceptionTable.printExceptionTable(eTable); 475 } 476 477 /** 478 * @return the machine code map for the compiled method. 479 */ 480 public OptMachineCodeMap getMCMap() { 481 return _mcMap; 482 } 483 484 /** 485 * Create the final machine code map for the compiled method. 486 * Remember the offset for the end of prologue too for debugger. 487 * @param ir the ir 488 * @param machineCodeLength the number of machine code instructions. 489 */ 490 @Interruptible 491 public void createFinalMCMap(IR ir, int machineCodeLength) { 492 _mcMap = OptMachineCodeMap.create(ir, machineCodeLength); 493 } 494 495 /** 496 * Create the final exception table from the IR for the method. 497 * @param ir the ir 498 */ 499 @Interruptible 500 public void createFinalExceptionTable(IR ir) { 501 if (ir.hasReachableExceptionHandlers()) { 502 eTable = OptExceptionTable.encode(ir); 503 } 504 } 505 506 /** 507 * Create the code patching maps from the IR for the method 508 * @param ir the ir 509 */ 510 @Interruptible 511 public void createCodePatchMaps(IR ir) { 512 // (1) count the patch points 513 int patchPoints = 0; 514 for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) { 515 if (s.operator() == IG_PATCH_POINT) { 516 patchPoints++; 517 } 518 } 519 // (2) if we have patch points, create the map. 520 if (patchPoints != 0) { 521 patchMap = new int[patchPoints * 2]; 522 MachineCodeOffsets mcOffsets = ir.MIRInfo.mcOffsets; 523 int idx = 0; 524 for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) { 525 if (s.operator() == IG_PATCH_POINT) { 526 int patchPoint = mcOffsets.getMachineCodeOffset(s); 527 int newTarget = mcOffsets.getMachineCodeOffset(InlineGuard.getTarget(s).target); 528 // A patch map is the offset of the last byte of the patch point 529 // and the new branch immediate to lay down if the code is ever patched. 530 if (VM.BuildForIA32) { 531 patchMap[idx++] = patchPoint - 1; 532 patchMap[idx++] = newTarget - patchPoint; 533 } else if (VM.BuildForPowerPC) { 534 /* since currently we use only one NOP scheme, the offset 535 * is adjusted for one word 536 */ 537 patchMap[idx++] = (patchPoint >> ArchConstants.getLogInstructionWidth()) - 1; 538 patchMap[idx++] = 539 (newTarget - patchPoint + (1 << ArchConstants.getLogInstructionWidth())); 540 } else if (VM.VerifyAssertions) { 541 VM._assert(VM.NOT_REACHED); 542 } 543 } 544 } 545 } 546 } 547 548 /** 549 * Applies the code patches to the INSTRUCTION array of cm. 550 * 551 * @param cm the method which will be patched 552 */ 553 @Interruptible 554 public void applyCodePatches(CompiledMethod cm) { 555 if (patchMap != null) { 556 for (int idx = 0; idx < patchMap.length; idx += 2) { 557 CodeArray code = cm.codeArrayForOffset(Offset.fromIntZeroExtend(patchMap[idx])); 558 if (VM.BuildForIA32) { 559 org.jikesrvm.compilers.common.assembler.ia32.Assembler.patchCode(code, patchMap[idx], patchMap[idx + 1]); 560 } else if (VM.BuildForPowerPC) { 561 org.jikesrvm.compilers.opt.mir2mc.ppc.AssemblerOpt.patchCode(code, patchMap[idx], patchMap[idx + 1]); 562 } else if (VM.VerifyAssertions) { 563 VM._assert(VM.NOT_REACHED); 564 } 565 } 566 567 if (VM.BuildForPowerPC) { 568 // we need synchronization on PPC to handle the weak memory model 569 // and its icache/dcache synchronization requirements. 570 // Before the class loading finishes, other processors must get 571 // synchronized. 572 boolean DEBUG_CODE_PATCH = false; 573 574 // let other processors see changes. 575 Magic.sync(); 576 577 // All other processors now will see the patched code in their data cache. 578 // We now need to force everyone's instruction caches to be in synch with their 579 // data caches. Some of the work of this call is redundant (since we already have 580 // forced the data caches to be in synch), but we need the icbi instructions 581 // to invalidate the instruction caches. 582 Memory.sync(Magic.objectAsAddress(instructions), 583 instructions.length() << ArchConstants.getLogInstructionWidth()); 584 // Force all other threads to execute isync at the next thread switch point 585 // so that the icbi instructions take effect. Another effect is that 586 // prefetched instructions are discarded. 587 // Note: it would be sufficient to execute isync once for each 588 // physical processor. 589 RVMThread.softHandshake(codePatchSyncRequestVisitor); 590 591 if (DEBUG_CODE_PATCH) { 592 VM.sysWrite("all processors got synchronized!\n"); 593 } 594 } 595 596 } 597 } 598 599 private static RVMThread.SoftHandshakeVisitor codePatchSyncRequestVisitor = 600 new CodePatchSyncRequestVisitor(); 601}