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 --&gt; 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}