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.baseline;
014
015import static org.jikesrvm.runtime.UnboxedSizeConstants.LOG_BYTES_IN_ADDRESS;
016
017import org.jikesrvm.VM;
018import org.jikesrvm.architecture.ArchConstants;
019import org.jikesrvm.classloader.ExceptionHandlerMap;
020import org.jikesrvm.classloader.NormalMethod;
021import org.jikesrvm.classloader.RVMArray;
022import org.jikesrvm.classloader.RVMMethod;
023import org.jikesrvm.classloader.RVMType;
024import org.jikesrvm.classloader.TypeReference;
025import org.jikesrvm.compilers.common.CompiledMethod;
026import org.jikesrvm.compilers.common.ExceptionTable;
027import org.jikesrvm.runtime.DynamicLink;
028import org.jikesrvm.runtime.ExceptionDeliverer;
029import org.jikesrvm.runtime.StackBrowser;
030import org.jikesrvm.util.PrintLN;
031import org.vmmagic.pragma.Uninterruptible;
032import org.vmmagic.pragma.Unpreemptible;
033import org.vmmagic.unboxed.Offset;
034
035/**
036 * Compiler-specific information associated with a method's machine
037 * instructions.
038 */
039public final class BaselineCompiledMethod extends CompiledMethod {
040
041  /** Does the baseline compiled method have a counters array? */
042  private boolean hasCounters;
043
044  /**
045   * The lock acquisition offset for synchronized methods.  For
046   * synchronized methods, the offset (in the method prologue) after
047   * which the monitor has been obtained.  At, or before, this point,
048   * the method does not own the lock.  Used by deliverException to
049   * determine whether the lock needs to be released.  Note: for this
050   * scheme to work, Lock must not allow a yield after it has been
051   * obtained.
052   */
053  private char lockOffset;
054
055  /**
056   * Baseline exception deliverer object
057   */
058  private static final ExceptionDeliverer exceptionDeliverer;
059
060  static {
061    if (VM.BuildForIA32) {
062      exceptionDeliverer = new org.jikesrvm.compilers.baseline.ia32.BaselineExceptionDeliverer();
063    } else {
064      exceptionDeliverer = new org.jikesrvm.compilers.baseline.ppc.BaselineExceptionDeliverer();
065      if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
066    }
067  }
068
069  /**
070   * Stack-slot reference maps for the compiled method.
071   */
072  public ReferenceMaps referenceMaps;
073
074  /**
075   * Encoded representation of bytecode index to offset in code array
076   * map.  Currently needed to support dynamic bridge magic; Consider
077   * integrating with GC maps
078   */
079  private byte[] bytecodeMap;
080
081  /**
082   * Exception table, null if not present.
083   */
084  private int[] eTable;
085
086  /** Offset into stack frame when operand stack is empty */
087  private final short emptyStackOffset;
088  /** PPC only: last general purpose register holding part of the operand stack */
089  private byte lastFixedStackRegister;
090  /** PPC only: last floating point register holding part of the operand stack */
091  private byte lastFloatStackRegister;
092
093  /**
094   * PPC only: location of general purpose local variables, positive
095   * values are register numbers, negative are stack offsets
096   */
097  private final short[] localFixedLocations;
098
099  /**
100   * PPC only: location of floating point local variables, positive
101   * values are register numbers, negative are stack offsets
102   */
103  private final short[] localFloatLocations;
104
105  /** @return offset into stack frame when operand stack is empty */
106  public int getEmptyStackOffset() {
107    return emptyStackOffset;
108  }
109
110  /**
111   * Location of local general purpose variable.  These Locations are
112   * positioned at the top of the stackslot that contains the value
113   * before accessing, substract size of value you want to access.<p>
114   * e.g. to load int: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_INT<br>
115   * e.g. to load long: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_LONG
116   *
117   * @param localIndex the index for the local general purpose variable
118   * @return location of the general purpose variable with the given index
119   */
120  @Uninterruptible
121  public short getGeneralLocalLocation(int localIndex) {
122    return BaselineCompiler.getGeneralLocalLocation(localIndex, localFixedLocations, (NormalMethod) method);
123  }
124
125  /**
126   * Location of local floating point variable.  These Locations are
127   * positioned at the top of the stackslot that contains the value
128   * before accessing, substract size of value you want to access.<p>
129   * e.g. to load float: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_FLOAT<br>
130   * e.g. to load double: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_DOUBLE
131   *
132   * @param localIndex the index for the local general purpose variable
133   * @return location of the floating point variable with the given index
134   */
135  @Uninterruptible
136  public short getFloatLocalLocation(int localIndex) {
137    return BaselineCompiler.getFloatLocalLocation(localIndex, localFloatLocations, (NormalMethod) method);
138  }
139
140  @Uninterruptible
141  public short getGeneralStackLocation(int stackIndex) {
142    return BaselineCompiler.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS));
143  }
144
145  @Uninterruptible
146  public short getFloatStackLocation(int stackIndex) {
147    // for now same implementation as getGeneralStackLocation
148    return getGeneralStackLocation(stackIndex);
149  }
150
151  /** @return last general purpose register holding part of the operand stack */
152  @Uninterruptible
153  public int getLastFixedStackRegister() {
154    return lastFixedStackRegister;
155  }
156
157  /** @return last floating point register holding part of the operand stack */
158  @Uninterruptible
159  public int getLastFloatStackRegister() {
160    return lastFloatStackRegister;
161  }
162
163  public BaselineCompiledMethod(int id, RVMMethod m) {
164    super(id, m);
165    NormalMethod nm = (NormalMethod) m;
166    //this.startLocalOffset = BaselineCompilerImpl.getStartLocalOffset(nm);
167    this.emptyStackOffset = BaselineCompiler.getEmptyStackOffset(nm);
168    this.localFixedLocations = VM.BuildForIA32 ? null : new short[nm.getLocalWords()];
169    this.localFloatLocations = VM.BuildForIA32 ? null : new short[nm.getLocalWords()];
170    this.lastFixedStackRegister = -1;
171    this.lastFloatStackRegister = -1;
172  }
173
174  /** Compile method */
175  public void compile() {
176    BaselineCompiler comp;
177    if (VM.BuildForIA32) {
178      comp = new org.jikesrvm.compilers.baseline.ia32.BaselineCompilerImpl(this, localFixedLocations, localFloatLocations);
179    } else {
180      if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
181      comp = new org.jikesrvm.compilers.baseline.ppc.BaselineCompilerImpl(this, localFixedLocations, localFloatLocations);
182    }
183    comp.compile();
184    this.lastFixedStackRegister = comp.getLastFixedStackRegister();
185    this.lastFloatStackRegister = comp.getLastFloatStackRegister();
186  }
187
188  /** @return BASELINE */
189  @Override
190  @Uninterruptible
191  public int getCompilerType() {
192    return BASELINE;
193  }
194
195  /** @return "baseline compiler" */
196  @Override
197  public String getCompilerName() {
198    return "baseline compiler";
199  }
200
201  /**
202   * @return the exception deliverer for this kind of compiled method
203   */
204  @Override
205  @Uninterruptible
206  public ExceptionDeliverer getExceptionDeliverer() {
207    return exceptionDeliverer;
208  }
209
210  /**
211   * Find a catch block within the compiled method
212   * @param instructionOffset offset of faulting instruction in compiled code
213   * @param exceptionType the type of the thrown exception
214   * @return the machine code offset of the catch block.
215   */
216  @Override
217  @Unpreemptible
218  public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) {
219    if (eTable == null) {
220      return -1;
221    } else {
222      return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType);
223    }
224  }
225
226  @Override
227  @Uninterruptible
228  public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) {
229    int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset);
230    ((NormalMethod) method).getDynamicLink(dynamicLink, bytecodeIndex);
231  }
232
233  /**
234   * @param instructionOffset the instruction's offset in the code for the method
235   * @return The line number, a positive integer.  Zero means unable to find.
236   */
237  @Override
238  @Uninterruptible
239  public int findLineNumberForInstruction(Offset instructionOffset) {
240    int bci = findBytecodeIndexForInstruction(instructionOffset);
241    if (bci == -1) return 0;
242    return ((NormalMethod) method).getLineNumberForBCIndex(bci);
243  }
244
245  @Override
246  public boolean isWithinUninterruptibleCode(Offset instructionOffset) {
247    return method.isUninterruptible();
248  }
249
250  /**
251   * Find bytecode index corresponding to one of this method's
252   * machine instructions.
253   *
254   * @param instructionOffset instruction offset to map to a bytecode index.<br>
255   * Note: This method expects the offset to refer to the machine
256   * instruction immediately FOLLOWING the bytecode in question.  just
257   * like findLineNumberForInstruction. See CompiledMethod for
258   * rationale.<br>
259   * NOTE: instructionIndex is in units of instructions, not bytes
260   * (different from all the other methods in this interface!!)
261   * @return the bytecode index for the machine instruction, -1 if not
262   *         available or not found.
263   */
264  @Uninterruptible
265  public int findBytecodeIndexForInstruction(Offset instructionOffset) {
266    Offset instructionIndex = instructionOffset.toWord().rsha(ArchConstants.getLogInstructionWidth()).toOffset();
267    int candidateIndex = -1;
268    int bcIndex = 0;
269    Offset instrIndex = Offset.zero();
270    for (int i = 0; i < bytecodeMap.length;) {
271      int b0 = (bytecodeMap[i++]) & 255;  // unsign-extend
272      int deltaBC, deltaIns;
273      if (b0 != 255) {
274        deltaBC = b0 >> 5;
275        deltaIns = b0 & 31;
276      } else {
277        int b1 = (bytecodeMap[i++]) & 255;  // unsign-extend
278        int b2 = (bytecodeMap[i++]) & 255;  // unsign-extend
279        int b3 = (bytecodeMap[i++]) & 255;  // unsign-extend
280        int b4 = (bytecodeMap[i++]) & 255;  // unsign-extend
281        deltaBC = (b1 << 8) | b2;
282        deltaIns = (b3 << 8) | b4;
283      }
284      bcIndex += deltaBC;
285      instrIndex = instrIndex.plus(deltaIns);
286      if (instrIndex.sGE(instructionIndex)) {
287        break;
288      }
289      candidateIndex = bcIndex;
290    }
291    return candidateIndex;
292  }
293
294  @Override
295  public void set(StackBrowser browser, Offset instr) {
296    browser.setMethod(method);
297    browser.setCompiledMethod(this);
298    browser.setBytecodeIndex(findBytecodeIndexForInstruction(instr));
299
300    if (VM.TraceStackTrace) {
301      VM.sysWrite("setting stack to frame (base): ");
302      VM.sysWrite(browser.getMethod());
303      VM.sysWrite(browser.getBytecodeIndex());
304      VM.sysWrite("\n");
305    }
306  }
307
308  @Override
309  public boolean up(StackBrowser browser) {
310    return false;
311  }
312
313  @Override
314  public void printStackTrace(Offset instructionOffset, PrintLN out) {
315    out.print("\tat ");
316    out.print(method.getDeclaringClass()); // RVMClass
317    out.print('.');
318    out.print(method.getName()); // a Atom, returned via MemberReference.getName().
319    out.print("(");
320    out.print(method.getDeclaringClass().getSourceName()); // a Atom
321    int lineNumber = findLineNumberForInstruction(instructionOffset);
322    if (lineNumber <= 0) {      // unknown line
323      out.print("; machine code offset: ");
324      out.printHex(instructionOffset.toInt());
325    } else {
326      out.print(':');
327      out.print(lineNumber);
328    }
329    out.print(')');
330    out.println();
331  }
332
333  /**
334   * Print the eTable
335   */
336  public void printExceptionTable() {
337    if (eTable != null) ExceptionTable.printExceptionTable(eTable);
338  }
339
340  /**
341   * Sets the lock acquisition offset for synchronized methods.
342   *
343   * @param off new offset
344   */
345  public void setLockAcquisitionOffset(int off) {
346    if (VM.VerifyAssertions) VM._assert((off & 0xFFFF) == off);
347    lockOffset = (char) off;
348  }
349
350  /** @return the lock acquisition offset */
351  @Uninterruptible
352  public Offset getLockAcquisitionOffset() {
353    return Offset.fromIntZeroExtend(lockOffset);
354  }
355
356  /** Set the method has a counters array */
357  void setHasCounterArray() {
358    hasCounters = true;
359  }
360
361  /** @return whether the method has a counters array */
362  @Uninterruptible
363  public boolean hasCounterArray() {
364    return hasCounters;
365  }
366
367  /**
368   * Encode/compress the bytecode map, reference (GC) map and exception table
369   *
370   * @param referenceMaps to encode
371   * @param bcMap unencoded bytecode to code array offset map
372   */
373  public void encodeMappingInfo(ReferenceMaps referenceMaps, int[] bcMap) {
374    int count = 0;
375    int lastBC = 0, lastIns = 0;
376    for (int i = 0; i < bcMap.length; i++) {
377      if (bcMap[i] != 0) {
378        int deltaBC = i - lastBC;
379        int deltaIns = bcMap[i] - lastIns;
380        if (VM.VerifyAssertions) {
381          VM._assert(deltaBC >= 0 && deltaIns >= 0);
382        }
383        if (deltaBC <= 6 && deltaIns <= 31) {
384          count++;
385        } else {
386          if (deltaBC > 65535 || deltaIns > 65535) {
387            VM.sysFail("BaselineCompiledMethod: a fancier encoding is needed");
388          }
389          count += 5;
390        }
391        lastBC = i;
392        lastIns = bcMap[i];
393      }
394    }
395    bytecodeMap = new byte[count];
396    count = lastBC = lastIns = 0;
397    for (int i = 0; i < bcMap.length; i++) {
398      if (bcMap[i] != 0) {
399        int deltaBC = i - lastBC;
400        int deltaIns = bcMap[i] - lastIns;
401        if (VM.VerifyAssertions) {
402          VM._assert(deltaBC >= 0 && deltaIns >= 0);
403        }
404        if (deltaBC <= 6 && deltaIns <= 31) {
405          bytecodeMap[count++] = (byte) ((deltaBC << 5) | deltaIns);
406        } else { // From before, we know that deltaBC <= 65535 and deltaIns <= 65535
407          bytecodeMap[count++] = (byte) 255;
408          bytecodeMap[count++] = (byte) (deltaBC >> 8);
409          bytecodeMap[count++] = (byte) (deltaBC & 255);
410          bytecodeMap[count++] = (byte) (deltaIns >> 8);
411          bytecodeMap[count++] = (byte) (deltaIns & 255);
412        }
413        lastBC = i;
414        lastIns = bcMap[i];
415      }
416    }
417    // TODO: it's likely for short methods we can share the bytecodeMap
418    referenceMaps.translateByte2Machine(bcMap);
419    this.referenceMaps = referenceMaps;
420    ExceptionHandlerMap emap = ((NormalMethod) method).getExceptionHandlerMap();
421    if (emap != null) {
422      eTable = BaselineExceptionTable.encode(emap, bcMap);
423    }
424  }
425
426  @Override
427  public int size() {
428    TypeReference TYPE = TypeReference.findOrCreate(BaselineCompiledMethod.class);
429    int size = TYPE.peekType().asClass().getInstanceSize();
430    if (bytecodeMap != null) size += RVMArray.ByteArray.getInstanceSize(bytecodeMap.length);
431    if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length);
432    if (referenceMaps != null) size += referenceMaps.size();
433    return size;
434  }
435}