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    }