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