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    }