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