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.opt.runtimesupport;
014    
015    import static org.jikesrvm.compilers.opt.ir.Operators.IG_PATCH_POINT;
016    
017    import org.jikesrvm.ArchitectureSpecific;
018    import org.jikesrvm.ArchitectureSpecificOpt;
019    import org.jikesrvm.VM;
020    import org.jikesrvm.PrintLN;
021    import org.jikesrvm.classloader.RVMArray;
022    import org.jikesrvm.classloader.MemberReference;
023    import org.jikesrvm.classloader.RVMMethod;
024    import org.jikesrvm.classloader.NormalMethod;
025    import org.jikesrvm.classloader.RVMType;
026    import org.jikesrvm.classloader.TypeReference;
027    import org.jikesrvm.compilers.common.CompiledMethod;
028    import org.jikesrvm.compilers.common.ExceptionTable;
029    import org.jikesrvm.compilers.opt.ir.IR;
030    import org.jikesrvm.compilers.opt.ir.InlineGuard;
031    import org.jikesrvm.compilers.opt.ir.Instruction;
032    import org.jikesrvm.osr.EncodedOSRMap;
033    import org.jikesrvm.runtime.DynamicLink;
034    import org.jikesrvm.runtime.ExceptionDeliverer;
035    import org.jikesrvm.runtime.Magic;
036    import org.jikesrvm.runtime.Memory;
037    import org.jikesrvm.runtime.StackBrowser;
038    import org.jikesrvm.scheduler.RVMThread;
039    import org.vmmagic.pragma.Interruptible;
040    import org.vmmagic.pragma.Uninterruptible;
041    import org.vmmagic.pragma.Unpreemptible;
042    import org.vmmagic.unboxed.Offset;
043    
044    /**
045     * An implementation of CompiledMethod for the OPT compiler.
046     *
047     * <p> NOTE: OptCompiledMethod live as long as their corresponding
048     * compiled machine code.  Therefore, they should only contain
049     * state that is really required to be persistent.  Anything
050     * transitory should be stored on the IR object.
051     */
052    @Uninterruptible
053    public final class OptCompiledMethod extends CompiledMethod {
054    
055      public OptCompiledMethod(int id, RVMMethod m) {
056        super(id, m);
057      }
058    
059      /**
060       * Get compiler that generated this method's machine code.
061       */
062      public int getCompilerType() {
063        return CompiledMethod.OPT;
064      }
065    
066      /**
067       * @return Name of the compiler that produced this compiled method.
068       */
069      public String getCompilerName() {
070        return "optimizing compiler";
071      }
072    
073      /**
074       * Get handler to deal with stack unwinding and exception delivery
075       * for this method's stackframes.
076       */
077      public ExceptionDeliverer getExceptionDeliverer() {
078        return exceptionDeliverer;
079      }
080    
081      /**
082       * Find "catch" block for a machine instruction of this method.
083       */
084      @Unpreemptible
085      public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) {
086        if (eTable == null) {
087          return -1;
088        } else {
089          return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType);
090        }
091      }
092    
093      /**
094       * Fetch symbolic reference to a method that's called
095       * by one of this method's instructions.
096       * @param dynamicLink place to put return information
097       * @param instructionOffset offset of machine instruction that issued
098       *                          the call
099       */
100      public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) {
101        int bci = _mcMap.getBytecodeIndexForMCOffset(instructionOffset);
102        NormalMethod realMethod = _mcMap.getMethodForMCOffset(instructionOffset);
103        if (bci == -1 || realMethod == null) {
104          VM.sysFail("Mapping to source code location not available at Dynamic Linking point\n");
105        }
106        realMethod.getDynamicLink(dynamicLink, bci);
107      }
108    
109      /**
110       * Return whether or not the instruction offset corresponds to an uninterruptible context.
111       *
112       * @param instructionOffset offset of addr from start of instructions in bytes
113       * @return true if the IP is within an Uninterruptible method, false otherwise.
114       */
115      @Interruptible
116      public boolean isWithinUninterruptibleCode(Offset instructionOffset) {
117        NormalMethod realMethod = _mcMap.getMethodForMCOffset(instructionOffset);
118        return realMethod.isUninterruptible();
119      }
120    
121      /**
122       * Find source line number corresponding to one of this method's
123       * machine instructions.
124       */
125      public int findLineNumberForInstruction(Offset instructionOffset) {
126        int bci = _mcMap.getBytecodeIndexForMCOffset(instructionOffset);
127        if (bci < 0) {
128          return 0;
129        }
130        return ((NormalMethod) method).getLineNumberForBCIndex(bci);
131      }
132    
133      /**
134       * Set the stack browser to the innermost logical stack frame of this method
135       */
136      @Interruptible
137      public void set(StackBrowser browser, Offset instr) {
138        OptMachineCodeMap map = getMCMap();
139        int iei = map.getInlineEncodingForMCOffset(instr);
140        if (iei >= 0) {
141          int[] inlineEncoding = map.inlineEncoding;
142          int mid = OptEncodedCallSiteTree.getMethodID(iei, inlineEncoding);
143    
144          browser.setInlineEncodingIndex(iei);
145          browser.setBytecodeIndex(map.getBytecodeIndexForMCOffset(instr));
146          browser.setCompiledMethod(this);
147          browser.setMethod(MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod());
148    
149          if (VM.TraceStackTrace) {
150            VM.sysWrite("setting stack to frame (opt): ");
151            VM.sysWrite(browser.getMethod());
152            VM.sysWrite(browser.getBytecodeIndex());
153            VM.sysWrite("\n");
154          }
155        } else {
156          if (VM.VerifyAssertions) VM._assert(VM.NOT_REACHED);
157        }
158      }
159    
160      /**
161       * Advance the StackBrowser up one internal stack frame, if possible
162       */
163      @Interruptible
164      public boolean up(StackBrowser browser) {
165        OptMachineCodeMap map = getMCMap();
166        int iei = browser.getInlineEncodingIndex();
167        int[] ie = map.inlineEncoding;
168        int next = OptEncodedCallSiteTree.getParent(iei, ie);
169        if (next >= 0) {
170          int mid = OptEncodedCallSiteTree.getMethodID(next, ie);
171          int bci = OptEncodedCallSiteTree.getByteCodeOffset(iei, ie);
172    
173          browser.setInlineEncodingIndex(next);
174          browser.setBytecodeIndex(bci);
175          browser.setMethod(MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod());
176    
177          if (VM.TraceStackTrace) {
178            VM.sysWrite("up within frame stack (opt): ");
179            VM.sysWrite(browser.getMethod());
180            VM.sysWrite(browser.getBytecodeIndex());
181            VM.sysWrite("\n");
182          }
183    
184          return true;
185        } else {
186          return false;
187        }
188      }
189    
190      /**
191       * Print this compiled method's portion of a stack trace.
192       * @param instructionOffset   The offset of machine instruction from
193       *                            start of method
194       * @param out    The PrintStream to print the stack trace to.
195       */
196      @Interruptible
197      public void printStackTrace(Offset instructionOffset, PrintLN out) {
198        OptMachineCodeMap map = getMCMap();
199        int iei = map.getInlineEncodingForMCOffset(instructionOffset);
200        if (iei >= 0) {
201          int[] inlineEncoding = map.inlineEncoding;
202          int bci = map.getBytecodeIndexForMCOffset(instructionOffset);
203          for (int j = iei; j >= 0; j = OptEncodedCallSiteTree.getParent(j, inlineEncoding)) {
204            int mid = OptEncodedCallSiteTree.getMethodID(j, inlineEncoding);
205            NormalMethod m =
206                (NormalMethod) MemberReference.getMemberRef(mid).asMethodReference().peekResolvedMethod();
207            int lineNumber = m.getLineNumberForBCIndex(bci); // might be 0 if unavailable.
208            out.print("\tat ");
209            out.print(m.getDeclaringClass());
210            out.print('.');
211            out.print(m.getName());
212            out.print('(');
213            out.print(m.getDeclaringClass().getSourceName());
214            out.print(':');
215            out.print(lineNumber);
216            out.print(')');
217            out.println();
218            if (j > 0) {
219              bci = OptEncodedCallSiteTree.getByteCodeOffset(j, inlineEncoding);
220            }
221          }
222        } else {
223          out.print("\tat ");
224          out.print(method.getDeclaringClass());
225          out.print('.');
226          out.print(method.getName());
227          out.print('(');
228          out.print(method.getDeclaringClass().getSourceName());
229          out.print("; machine code offset: ");
230          out.printHex(instructionOffset.toInt());
231          out.print(')');
232          out.println();
233        }
234      }
235    
236      @Interruptible
237      public int size() {
238        int size = TypeReference.ExceptionTable.peekType().asClass().getInstanceSize();
239        size += _mcMap.size();
240        if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length);
241        if (patchMap != null) size += RVMArray.IntArray.getInstanceSize(patchMap.length);
242        return size;
243      }
244    
245      //----------------//
246      // implementation //
247      //----------------//
248      private static final ArchitectureSpecificOpt.OptExceptionDeliverer exceptionDeliverer =
249          new ArchitectureSpecificOpt.OptExceptionDeliverer();
250    
251      private EncodedOSRMap _osrMap;
252    
253      @Interruptible
254      public void createFinalOSRMap(IR ir) {
255        this._osrMap = EncodedOSRMap.makeMap(ir.MIRInfo.osrVarMap);
256      }
257    
258      public EncodedOSRMap getOSRMap() {
259        return this._osrMap;
260      }
261    
262      //////////////////////////////////////
263      // Information the opt compiler needs to persistently associate
264      // with a particular compiled method.
265    
266      /** The primary machine code maps */
267      private OptMachineCodeMap _mcMap;
268      /** The encoded exception tables (null if there are none) */
269      private int[] eTable;
270      private int[] patchMap;
271    
272      /**
273       * unsigned offset (off the framepointer) of nonvolatile save area
274       * in bytes
275       */
276      private char nonvolatileOffset;
277      /**
278       * unsigned offset (off the framepointer) of caught exception
279       * object in bytes
280       */
281      private char exceptionObjectOffset;
282      /**
283       * size of the fixed portion of the stackframe
284       */
285      private char stackFrameFixedSize;
286      /**
287       * first saved nonvolatile integer register (-1 if no nonvolatile
288       * GPRs)
289       */
290      private byte firstNonvolatileGPR;
291      /**
292       * first saved nonvolatile floating point register (-1 if no
293       * nonvolatile FPRs)
294       */
295      private byte firstNonvolatileFPR;
296      /** opt level at which the method was compiled */
297      private byte optLevel;
298      /** were the volatile registers saved? */
299      private boolean volatilesSaved;
300      /** is the current method executing with instrumentation */
301      private boolean instrumented;
302    
303      public int getUnsignedNonVolatileOffset() {
304        return nonvolatileOffset;
305      }
306    
307      public int getUnsignedExceptionOffset() {
308        return exceptionObjectOffset;
309      }
310    
311      public int getFirstNonVolatileGPR() {
312        return firstNonvolatileGPR;
313      }
314    
315      public int getFirstNonVolatileFPR() {
316        return firstNonvolatileFPR;
317      }
318    
319      public int getOptLevel() {
320        return optLevel;
321      }
322    
323      public boolean isSaveVolatile() {
324        return volatilesSaved;
325      }
326    
327      public boolean isInstrumentedMethod() {
328        return instrumented;
329      }
330    
331      public int getFrameFixedSize() {
332        return stackFrameFixedSize;
333      }
334    
335      public void setUnsignedNonVolatileOffset(int x) {
336        if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF);
337        nonvolatileOffset = (char) x;
338      }
339    
340      public void setUnsignedExceptionOffset(int x) {
341        if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF);
342        exceptionObjectOffset = (char) x;
343      }
344    
345      public void setFirstNonVolatileGPR(int x) {
346        if (VM.VerifyAssertions) VM._assert(x >= -1 && x < 0x7F);
347        firstNonvolatileGPR = (byte) x;
348      }
349    
350      public void setFirstNonVolatileFPR(int x) {
351        if (VM.VerifyAssertions) VM._assert(x >= -1 && x < 0x7F);
352        firstNonvolatileFPR = (byte) x;
353      }
354    
355      public void setOptLevel(int x) {
356        if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0x7F);
357        optLevel = (byte) x;
358      }
359    
360      public void setSaveVolatile(boolean sv) {
361        volatilesSaved = sv;
362      }
363    
364      public void setInstrumentedMethod(boolean _instrumented) {
365        instrumented = _instrumented;
366      }
367    
368      public void setFrameFixedSize(int x) {
369        if (VM.VerifyAssertions) VM._assert(x >= 0 && x < 0xFFFF);
370        stackFrameFixedSize = (char) x;
371      }
372    
373      /**
374       * Return the number of non-volatile GPRs used by this method.
375       */
376      public int getNumberOfNonvolatileGPRs() {
377        if (VM.BuildForPowerPC) {
378          return ArchitectureSpecific.RegisterConstants.NUM_GPRS - getFirstNonVolatileGPR();
379        } else if (VM.BuildForIA32) {
380          return ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_GPRS - getFirstNonVolatileGPR();
381        } else if (VM.VerifyAssertions) {
382          VM._assert(VM.NOT_REACHED);
383        }
384        return -1;
385      }
386    
387      /**
388       * Return the number of non-volatile FPRs used by this method.
389       */
390      public int getNumberOfNonvolatileFPRs() {
391        if (VM.BuildForPowerPC) {
392          return ArchitectureSpecific.RegisterConstants.NUM_FPRS - getFirstNonVolatileFPR();
393        } else if (VM.BuildForIA32) {
394          return ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_FPRS - getFirstNonVolatileFPR();
395        } else if (VM.VerifyAssertions) {
396          VM._assert(VM.NOT_REACHED);
397        }
398        return -1;
399      }
400    
401      /**
402       * Set the number of non-volatile GPRs used by this method.
403       */
404      public void setNumberOfNonvolatileGPRs(short n) {
405        if (VM.BuildForPowerPC) {
406          setFirstNonVolatileGPR(ArchitectureSpecific.RegisterConstants.NUM_GPRS - n);
407        } else if (VM.BuildForIA32) {
408          setFirstNonVolatileGPR(ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_GPRS - n);
409        } else if (VM.VerifyAssertions) {
410          VM._assert(VM.NOT_REACHED);
411        }
412      }
413    
414      /**
415       * Set the number of non-volatile FPRs used by this method.
416       */
417      public void setNumberOfNonvolatileFPRs(short n) {
418        if (VM.BuildForPowerPC) {
419          setFirstNonVolatileFPR(ArchitectureSpecific.RegisterConstants.NUM_FPRS - n);
420        } else if (VM.BuildForIA32) {
421          setFirstNonVolatileFPR(ArchitectureSpecific.RegisterConstants.NUM_NONVOLATILE_FPRS - n);
422        } else if (VM.VerifyAssertions) {
423          VM._assert(VM.NOT_REACHED);
424        }
425      }
426    
427      /**
428       * Print the eTable
429       */
430      @Interruptible
431      public void printExceptionTable() {
432        if (eTable != null) ExceptionTable.printExceptionTable(eTable);
433      }
434    
435      /**
436       * @return the machine code map for the compiled method.
437       */
438      public OptMachineCodeMap getMCMap() {
439        return _mcMap;
440      }
441    
442      /**
443       * Create the final machine code map for the compiled method.
444       * Remember the offset for the end of prologue too for debugger.
445       * @param ir the ir
446       * @param machineCodeLength the number of machine code instructions.
447       */
448      @Interruptible
449      public void createFinalMCMap(IR ir, int machineCodeLength) {
450        _mcMap = OptMachineCodeMap.create(ir, machineCodeLength);
451      }
452    
453      /**
454       * Create the final exception table from the IR for the method.
455       * @param ir the ir
456       */
457      @Interruptible
458      public void createFinalExceptionTable(IR ir) {
459        if (ir.hasReachableExceptionHandlers()) {
460          eTable = OptExceptionTable.encode(ir);
461        }
462      }
463    
464      /**
465       * Create the code patching maps from the IR for the method
466       * @param ir the ir
467       */
468      @Interruptible
469      public void createCodePatchMaps(IR ir) {
470        // (1) count the patch points
471        int patchPoints = 0;
472        for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) {
473          if (s.operator() == IG_PATCH_POINT) {
474            patchPoints++;
475          }
476        }
477        // (2) if we have patch points, create the map.
478        if (patchPoints != 0) {
479          patchMap = new int[patchPoints * 2];
480          int idx = 0;
481          for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) {
482            if (s.operator() == IG_PATCH_POINT) {
483              int patchPoint = s.getmcOffset();
484              int newTarget = InlineGuard.getTarget(s).target.getmcOffset();
485              // A patch map is the offset of the last byte of the patch point
486              // and the new branch immediate to lay down if the code is ever patched.
487              if (VM.BuildForIA32) {
488                patchMap[idx++] = patchPoint - 1;
489                patchMap[idx++] = newTarget - patchPoint;
490              } else if (VM.BuildForPowerPC) {
491    
492                // otherwise, it must be RFOR_POWERPC
493                /* since currently we use only one NOP scheme, the offset
494                * is adjusted for one word
495                */
496                patchMap[idx++] = (patchPoint >> ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH) - 1;
497                patchMap[idx++] =
498                    (newTarget - patchPoint + (1 << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH));
499              } else if (VM.VerifyAssertions) {
500                VM._assert(VM.NOT_REACHED);
501              }
502            }
503          }
504        }
505      }
506    
507      /**
508       * Apply the code patches to the INSTRUCTION array of cm
509       */
510      @Interruptible
511      public void applyCodePatches(CompiledMethod cm) {
512        if (patchMap != null) {
513          for (int idx = 0; idx < patchMap.length; idx += 2) {
514            ArchitectureSpecific.CodeArray code = cm.codeArrayForOffset(Offset.fromIntZeroExtend(patchMap[idx]));
515            if (VM.BuildForIA32) {
516              ArchitectureSpecific.Assembler.patchCode(code, patchMap[idx], patchMap[idx + 1]);
517            } else if (VM.BuildForPowerPC) {
518              ArchitectureSpecificOpt.AssemblerOpt.patchCode(code, patchMap[idx], patchMap[idx + 1]);
519            } else if (VM.VerifyAssertions) {
520              VM._assert(VM.NOT_REACHED);
521            }
522          }
523    
524          if (VM.BuildForPowerPC) {
525            /* we need synchronization on PPC to handle the weak memory model
526             * and its icache/dcache synchronization requriements.
527             * before the class loading finish, other processor should get
528             * synchronized.
529             */
530            boolean DEBUG_CODE_PATCH = false;
531    
532            // let other processors see changes; although really physical processors
533            // need synchronization, we set each virtual processor to execute
534            // isync at thread switch point.
535            Magic.sync();
536    
537            // All other processors now will see the patched code in their data cache.
538            // We now need to force everyone's instruction caches to be in synch with their
539            // data caches.  Some of the work of this call is redundant (since we already have
540            // forced the data caches to be in synch), but we need the icbi instructions
541            Memory.sync(Magic.objectAsAddress(instructions),
542                           instructions.length() << ArchitectureSpecific.RegisterConstants.LG_INSTRUCTION_WIDTH);
543            RVMThread.softHandshake(codePatchSyncRequestVisitor);
544    
545            if (DEBUG_CODE_PATCH) {
546              VM.sysWrite("all processors get synchronized!\n");
547            }
548          }
549    
550        }
551      }
552    
553      private static RVMThread.SoftHandshakeVisitor codePatchSyncRequestVisitor =
554        new RVMThread.SoftHandshakeVisitor() {
555          @Uninterruptible
556          public boolean checkAndSignal(RVMThread t) {
557            t.codePatchSyncRequested = true;
558            return true; // handshake with everyone but ourselves.
559          }
560        };
561    }