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