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