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.common; 014 015import org.jikesrvm.VM; 016import org.jikesrvm.architecture.ArchConstants; 017import org.jikesrvm.classloader.RVMMethod; 018import org.jikesrvm.classloader.RVMType; 019import org.jikesrvm.runtime.DynamicLink; 020import org.jikesrvm.runtime.ExceptionDeliverer; 021import org.jikesrvm.runtime.Magic; 022import org.jikesrvm.runtime.StackBrowser; 023import org.jikesrvm.runtime.Statics; 024import org.jikesrvm.scheduler.RVMThread; 025import org.vmmagic.pragma.Interruptible; 026import org.vmmagic.pragma.Uninterruptible; 027import org.vmmagic.pragma.Unpreemptible; 028import org.vmmagic.unboxed.Address; 029import org.vmmagic.unboxed.Offset; 030import org.vmmagic.unboxed.Word; 031 032/** 033 * A method that has been compiled into machine code by one of our compilers. 034 */ 035public abstract class CompiledMethod { 036 037 /* 038 * constants for compiler types 039 */ 040 public static final int TRAP = 0; // no code: special trap handling stackframe 041 public static final int BASELINE = 1; // baseline code 042 public static final int OPT = 3; // opt code 043 public static final int JNI = 4; // java to Native C transition frame 044 public static final int NUM_COMPILER_TYPES = 4; 045 046 /** Line number for native methods defined by the constructor of java.lang.StackTraceElement */ 047 public static final int NATIVE_METHOD_LINE_NUMBER = -2; 048 049 /* 050 * constants for flags 051 */ 052 private static final byte COMPILED = 0x08; 053 private static final byte INVALID = 0x04; 054 private static final byte OBSOLETE = 0x02; 055 private static final byte ACTIVE_ON_STACK = 0x01; 056 /** flags the compiled method as outdated, needs OSR */ 057 private static final byte OUTDATED = 0x10; 058 /** 059 * Has the method sample data for this compiled method been reset? 060 */ 061 private static final byte SAMPLES_RESET = 0x20; 062 private static final byte SPECIAL_FOR_OSR = 0x40; 063 /** Has bridge from native annotation, NB this makes the flags byte negative */ 064 private static final byte BRIDGE_FROM_NATIVE = (byte)0x80; 065 static { 066 if (VM.VerifyAssertions) VM._assert(BRIDGE_FROM_NATIVE < 0); 067 } 068 069 /** Flags bit field */ 070 private byte flags; 071 072 /** 073 * The compiled method id of this compiled method (index into CompiledMethods) 074 */ 075 protected final int cmid; 076 077 /** 078 * The RVMMethod that was compiled 079 */ 080 public final RVMMethod method; 081 082 /** 083 * The compiled machine code for said method. 084 */ 085 protected CodeArray instructions; 086 087 /** 088 * the offset of instructions in JTOC, for osr-special compiled 089 * method only. all osr-ed method is treated like static. 090 * TODO: OSR redesign: put in subclass? Stick somewhere else? 091 * Don't want to waste space for this on every compiled 092 * method. 093 */ 094 protected int osrJTOCoffset = 0; 095 096 /** 097 * The time in milliseconds taken to compile the method. 098 */ 099 protected float compilationTime; 100 101 public void setSamplesReset() { 102 flags |= SAMPLES_RESET; 103 } 104 105 public boolean getSamplesReset() { 106 return (flags & SAMPLES_RESET) != 0; 107 } 108 109 public void setSpecialForOSR() { 110 flags |= SPECIAL_FOR_OSR; 111 // set JTOC 112 this.osrJTOCoffset = Statics.allocateReferenceSlot(false).toInt(); 113 Statics.setSlotContents(this.getOsrJTOCoffset(), this.instructions); 114 } 115 116 public boolean isSpecialForOSR() { 117 return (flags & SPECIAL_FOR_OSR) != 0; 118 } 119 120 public final Offset getOsrJTOCoffset() { 121 if (VM.VerifyAssertions) VM._assert(isSpecialForOSR()); 122 return Offset.fromIntSignExtend(this.osrJTOCoffset); 123 } 124 125 /** 126 * @param id the compiled method id 127 * @param m the method that this compiled method belongs to 128 */ 129 public CompiledMethod(int id, RVMMethod m) { 130 cmid = id; 131 method = m; 132 if (m != null && m.getDeclaringClass().hasBridgeFromNativeAnnotation()) { 133 flags = BRIDGE_FROM_NATIVE; 134 } 135 } 136 137 /** 138 * @return the compiled method id for this compiled method 139 */ 140 @Uninterruptible 141 public final int getId() { 142 return cmid; 143 } 144 145 /** 146 * @return the RVMMethod associated with this compiled method 147 */ 148 @Uninterruptible 149 public final RVMMethod getMethod() { 150 return method; 151 } 152 153 /** 154 * @return whether this method has a bridge from native annotation (important when 155 * walking the stack) 156 */ 157 @Uninterruptible 158 public final boolean hasBridgeFromNativeAnnotation() { 159 return flags < 0; 160 } 161 162 /** 163 * @return the CodeArray to jump to to invoke this method (ie, 164 * code_array[0] contains the first instruction of the method's prologue). 165 */ 166 @Uninterruptible 167 public final CodeArray getEntryCodeArray() { 168 if (VM.VerifyAssertions) VM._assert((flags & COMPILED) != 0); 169 return instructions; 170 } 171 172 /** 173 * @return the number of machine instructions for compiled method; 174 * may be an overestimate if we have adding padding to machine code. 175 */ 176 @Uninterruptible 177 public final int numberOfInstructions() { 178 if (VM.VerifyAssertions) VM._assert((flags & COMPILED) != 0); 179 return instructions.length(); 180 } 181 182 /** 183 * Return the offset in bytes of the given Address from the start 184 * of the machine code array. 185 * @param ip a Address (should be an interior pointer to instructions) 186 * @return offset of addr from start of instructions in bytes 187 */ 188 @Uninterruptible 189 public final Offset getInstructionOffset(Address ip) { 190 return getInstructionOffset(ip, true); 191 } 192 193 /** 194 * Return the offset in bytes of the given Address from the start 195 * of the machine code array. 196 * @param ip a Address (should be an interior pointer to instructions) 197 * @param dieOnFailure if ip is invalid should we kill the VM (we don't want 198 * to if already in the process of killing the VM) 199 * @return offset of addr from start of instructions in bytes 200 */ 201 @Uninterruptible 202 public final Offset getInstructionOffset(Address ip, boolean dieOnFailure) { 203 if (getCompilerType() == JNI || getCompilerType() == TRAP) { 204 return Offset.zero(); 205 } else { 206 Offset offset = ip.diff(Magic.objectAsAddress(instructions)); 207 int max = (instructions.length() + 1) << ArchConstants.getLogInstructionWidth(); 208 if (!offset.toWord().LT(Word.fromIntZeroExtend(max))) { 209 if (RVMThread.isTrampolineIP(ip)) { 210 ip = RVMThread.getCurrentThread().getTrampolineHijackedReturnAddress(); 211 offset = ip.diff(Magic.objectAsAddress(instructions)); 212 if (offset.toWord().LT(Word.fromIntZeroExtend(max))) 213 return offset; 214 } 215 Address instructionStart = Magic.objectAsAddress(instructions); 216 VM.sysWriteln("\nIn thread ",RVMThread.getCurrentThreadSlot()," getInstructionOffset: ip is not within compiled code for method: ",ip); 217 VM.sysWrite("\tsupposed method is "); 218 VM.sysWrite(method); 219 VM.sysWriteln(); 220 VM.sysWriteln("\tcode for this method starts at ", instructionStart); 221 VM.sysWriteln("\t and has last valid return address of ", instructionStart.plus(max)); 222 VM.sysWriteln("The requested instruction address was ", ip); 223 CompiledMethod realCM = CompiledMethods.findMethodForInstruction(ip); 224 if (realCM == null) { 225 VM.sysWriteln("\tUnable to find compiled method corresponding to this return address"); 226 } else { 227 VM.sysWrite("\tFound compiled method "); 228 VM.sysWrite(realCM.getMethod()); 229 VM.sysWriteln(" whose code contains this return address"); 230 } 231 if (dieOnFailure) { 232 VM.sysWriteln("Attempting to dump virtual machine state before exiting"); 233 RVMThread.dumpVirtualMachine(); 234 VM.sysFail("Terminating VM due to invalid request for instruction offset"); 235 } 236 } 237 // NOTE: we are absolutely positive that offset will fit in 32 bits 238 // because we don't create CodeArrays that are so massive it won't. 239 // Thus, we do the assertion checking above to ensure that ip is in range. 240 return offset; 241 } 242 } 243 244 /** 245 * Return the address of the instruction at offset offset in the method's instruction stream. 246 * @param offset the offset of the desired instruction (as returned by getInstructionOffset) 247 * @return Address of the specified instruction 248 */ 249 @Uninterruptible 250 public final Address getInstructionAddress(Offset offset) { 251 Address startAddress = Magic.objectAsAddress(instructions); 252 return startAddress.plus(offset); 253 } 254 255 /** 256 * Return the code array for this method that contains the given offset. 257 * @param offset the offset of the desired instruction (as returned by getInstructionOffset) 258 * @return CodeArray that contains the specified instruction 259 */ 260 @Uninterruptible 261 public final CodeArray codeArrayForOffset(Offset offset) { 262 return instructions; 263 } 264 265 /** 266 * Does the code for the compiled method contain the given return address? 267 * @param ip a return address 268 * @return {@code true} if it belongs to this method's code, {@code false} otherwise. 269 */ 270 @Uninterruptible 271 public final boolean containsReturnAddress(Address ip) { 272 Address beg = Magic.objectAsAddress(instructions); 273 Address end = beg.plus(instructions.length() << ArchConstants.getLogInstructionWidth()); 274 275 // note that "ip" points to a return site (not a call site) 276 // so the range check here must be "ip <= beg || ip > end" 277 // and not "ip < beg || ip >= end" 278 // 279 return !(ip.LE(beg) || ip.GT(end)); 280 } 281 282 /** 283 * Records that the compilation is complete. 284 * 285 * @param code the method's code 286 */ 287 public final void compileComplete(CodeArray code) { 288 instructions = code; 289 flags |= COMPILED; 290 } 291 292 /** 293 * Mark the compiled method as invalid 294 */ 295 public final void setInvalid() { 296 flags |= INVALID; 297 } 298 299 /** 300 * Mark the compiled method as obsolete (ie a candidate for eventual GC) 301 */ 302 @Uninterruptible 303 public final void setObsolete() { 304 flags |= OBSOLETE; 305 } 306 307 @Uninterruptible 308 public final void setActiveOnStack() { 309 flags |= ACTIVE_ON_STACK; 310 } 311 312 @Uninterruptible 313 public final void clearActiveOnStack() { 314 flags &= ~ACTIVE_ON_STACK; 315 } 316 317 /** 318 * Mark the compiled method as outdated (i.e. requires OSR), 319 * the flag is set in AnalyticModel 320 */ 321 @Uninterruptible 322 public final void setOutdated() { 323 if (VM.VerifyAssertions) VM._assert(this.getCompilerType() == BASELINE); 324 flags |= OUTDATED; 325 } 326 327 /** 328 * @return {@code true} if the compiled method is marked as outdated 329 */ 330 @Uninterruptible 331 public final boolean isOutdated() { 332 return (flags & OUTDATED) != 0; 333 } 334 335 /** 336 * @return {@code true} if compilation has completed 337 */ 338 @Uninterruptible 339 public final boolean isCompiled() { 340 return (flags & COMPILED) != 0; 341 } 342 343 /** 344 * @return {@code true} if the compiled code is invalid 345 */ 346 @Uninterruptible 347 public final boolean isInvalid() { 348 return (flags & INVALID) != 0; 349 } 350 351 /** 352 * @return {@code true} if the compiled code is obsolete 353 */ 354 @Uninterruptible 355 public final boolean isObsolete() { 356 return (flags & OBSOLETE) != 0; 357 } 358 359 @Uninterruptible 360 public final boolean isActiveOnStack() { 361 return (flags & ACTIVE_ON_STACK) != 0; 362 } 363 364 public final double getCompilationTime() { 365 return compilationTime; 366 } 367 368 public final void setCompilationTime(double ct) { 369 compilationTime = (float) ct; 370 } 371 372 /** 373 * Identify the compiler that produced this compiled method. 374 * @return one of TRAP, BASELINE, OPT, or JNI. 375 * Note: use this instead of "instanceof" when GC is disabled (i.e. during GC) 376 */ 377 @Uninterruptible 378 public abstract int getCompilerType(); 379 380 @Uninterruptible 381 public static String compilerTypeToString(int compilerType) { 382 switch (compilerType) { 383 case TRAP: 384 return "TRAP"; 385 case BASELINE: 386 return "BASELINE"; 387 case OPT: 388 return "OPT"; 389 case JNI: 390 return "JNI"; 391 default: 392 if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED); 393 return null; 394 } 395 } 396 397 /** 398 * @return Name of the compiler that produced this compiled method. 399 */ 400 public abstract String getCompilerName(); 401 402 /** 403 * @return a handler to deal with stack unwinding and exception delivery for this 404 * compiled method's stackframes. 405 */ 406 @Uninterruptible 407 public abstract ExceptionDeliverer getExceptionDeliverer(); 408 409 /** 410 * Find "catch" block for a machine instruction of 411 * this method that might be guarded 412 * against specified class of exceptions by a "try" block.<p> 413 * 414 * Notes: 415 * <ul> 416 * <li> The "instructionOffset" must point to the instruction 417 * <em> following </em> the actual 418 * instruction whose catch block is sought. 419 * This allows us to properly handle the case where 420 * the only address we have to work with is a return address 421 * (i.e. from a stackframe) 422 * or an exception address 423 * (i.e. from a {@code null} pointer dereference, array bounds check, 424 * or divide by zero) on a machine architecture with variable length 425 * instructions. 426 * In such situations we'd have no idea how far to back up the 427 * instruction pointer 428 * to point to the "call site" or "exception site". 429 * 430 * <li> This method must not cause any allocations, because it executes with 431 * GC disabled when called by RuntimeEntrypoints.deliverException(). 432 * </ul> 433 * 434 * @param instructionOffset offset of machine instruction from start of this method, in bytes 435 * @param exceptionType type of exception being thrown - something like "NullPointerException" 436 * @return offset of machine instruction for catch block 437 * (-1 --> no catch block) 438 */ 439 @Unpreemptible 440 public abstract int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType); 441 442 /** 443 * Fetch symbolic reference to a method that's called by one of 444 * this method's instructions.<p> 445 * 446 * Notes: 447 * <ul> 448 * <li> The "instructionOffset" must point to the instruction i 449 * <em> following </em> the call 450 * instruction whose target method is sought. 451 * This allows us to properly handle the case where 452 * the only address we have to work with is a return address 453 * (i.e. from a stackframe) 454 * on a machine architecture with variable length instructions. 455 * In such situations we'd have no idea how far to back up the 456 * instruction pointer 457 * to point to the "call site". 458 * 459 * <li> The implementation must not cause any allocations, 460 * because it executes with 461 * GC disabled when called by GCMapIterator. 462 * </ul> 463 * 464 * @param dynamicLink place to put return information 465 * @param instructionOffset offset of machine instruction from start of 466 * this method, in bytes 467 */ 468 @Uninterruptible 469 public abstract void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset); 470 471 /** 472 * Find source line number corresponding to one of this method's 473 * machine instructions. 474 * 475 * <p> Usage note: "instructionOffset" must point to the 476 * instruction <em> following </em> the actual instruction 477 * whose line number is sought. 478 * This allows us to properly handle the case where 479 * the only address we have to work with is a return address 480 * (ie. from a stackframe) 481 * or an exception address 482 * (ie. from a null pointer dereference, array bounds check, 483 * or divide by zero) on a machine architecture with variable length 484 * instructions. 485 * In such situations we'd have no idea how far to back up the 486 * instruction pointer 487 * to point to the "call site" or "exception site". 488 * 489 * @param instructionOffset of machine instruction from start of this method, in bytes 490 * @return source line number 491 * (0 == no line info available, 1 == first line of source file, -2 == native method)) 492 * 493 */ 494 @Uninterruptible 495 public int findLineNumberForInstruction(Offset instructionOffset) { 496 return 0; 497 } 498 499 /** 500 * Return whether or not the given address (which is purported to be inside 501 * of the compiled method's code array) corresponds to an uninterruptible context. 502 * 503 * @param instructionOffset of addr from start of instructions in bytes 504 * @return {@code true} if the IP is within an Uninterruptible method, {@code false} otherwise. 505 */ 506 @Interruptible 507 public abstract boolean isWithinUninterruptibleCode(Offset instructionOffset); 508 509 /** 510 * Print this compiled method's portion of a stack trace 511 * @param instructionOffset offset of machine instruction from start of method 512 * @param out the PrintLN to print the stack trace to. 513 */ 514 public abstract void printStackTrace(Offset instructionOffset, org.jikesrvm.util.PrintLN out); 515 516 /** 517 * Set the stack browser to the innermost logical stack frame of this method. 518 * 519 * @param browser the browser 520 * @param instr the offset of the instruction 521 */ 522 public abstract void set(StackBrowser browser, Offset instr); 523 524 /** 525 * Advance the StackBrowser up one internal stack frame, if possible 526 * 527 * @param browser the browser to advance 528 * @return whether the browser advanced a frame 529 */ 530 public boolean up(StackBrowser browser) { 531 return false; 532 } 533 534 /** 535 * @return the number of bytes used to encode the compiler-specific mapping 536 * information for this compiled method. 537 * Used to gather statistics on the space costs of mapping schemes. 538 */ 539 public int size() { 540 return 0; 541 } 542 543}