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