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 acquistion 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.
105       * e.g. to load int: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_INT
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.
117       * e.g. to load float: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_FLOAT
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      @Uninterruptible
172      public int getCompilerType() {
173        return BASELINE;
174      }
175    
176      /** @return "baseline compiler" */
177      public String getCompilerName() {
178        return "baseline compiler";
179      }
180    
181      /**
182       * Get the exception deliverer for this kind of compiled method
183       */
184      @Uninterruptible
185      public ExceptionDeliverer getExceptionDeliverer() {
186        return exceptionDeliverer;
187      }
188    
189      /**
190       * Find a catch block within the compiled method
191       * @param instructionOffset offset of faulting instruction in compiled code
192       * @param exceptionType the type of the thrown exception
193       * @return the machine code offset of the catch block.
194       */
195      @Unpreemptible
196      public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) {
197        if (eTable == null) {
198          return -1;
199        } else {
200          return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType);
201        }
202      }
203    
204      /**
205       * Fetch symbolic reference to a method that's called by one of
206       * this method's instructions.
207       * @param dynamicLink place to put return information
208       * @param instructionOffset offset of machine instruction from start of
209       * this method, in bytes
210       *
211       * Notes:
212       * <ul>
213       * <li> The "instructionOffset" must point to the instruction i
214       * <em> following </em> the call
215       * instruction whose target method is sought.
216       * This allows us to properly handle the case where
217       * the only address we have to work with is a return address
218       * (ie. from a stackframe)
219       * on a machine architecture with variable length instructions.
220       * In such situations we'd have no idea how far to back up the
221       * instruction pointer
222       * to point to the "call site".
223       *
224       * <li> The implementation must not cause any allocations,
225       * because it executes with
226       * gc disabled when called by GCMapIterator.
227       * <ul>
228       */
229      @Uninterruptible
230      public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) {
231        int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset);
232        ((NormalMethod) method).getDynamicLink(dynamicLink, bytecodeIndex);
233      }
234    
235      /**
236       * @return The line number, a positive integer.  Zero means unable to find.
237       */
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      /**
246       * Return whether or not the instruction offset corresponds to an uninterruptible context.
247       *
248       * @param instructionOffset of addr from start of instructions in bytes
249       * @return true if the IP is within an Uninterruptible method, false otherwise.
250       */
251      public boolean isWithinUninterruptibleCode(Offset instructionOffset) {
252        return method.isUninterruptible();
253      }
254    
255      /**
256       * Find bytecode index corresponding to one of this method's
257       * machine instructions.
258       *
259       * @param instructionOffset instruction offset to map to a bytecode index
260       * Note: This method expects the offset to refer to the machine
261       * instruction immediately FOLLOWING the bytecode in question.  just
262       * like findLineNumberForInstruction. See CompiledMethod for
263       * rationale
264       * NOTE: instructionIndex is in units of instructions, not bytes
265       * (different from all the other methods in this interface!!)
266       * @return the bytecode index for the machine instruction, -1 if not
267       *         available or not found.
268       */
269      @Uninterruptible
270      public int findBytecodeIndexForInstruction(Offset instructionOffset) {
271        Offset instructionIndex = instructionOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset();
272        int candidateIndex = -1;
273        int bcIndex = 0;
274        Offset instrIndex = Offset.zero();
275        for (int i = 0; i < bytecodeMap.length;) {
276          int b0 = ((int) bytecodeMap[i++]) & 255;  // unsign-extend
277          int deltaBC, deltaIns;
278          if (b0 != 255) {
279            deltaBC = b0 >> 5;
280            deltaIns = b0 & 31;
281          } else {
282            int b1 = ((int) bytecodeMap[i++]) & 255;  // unsign-extend
283            int b2 = ((int) bytecodeMap[i++]) & 255;  // unsign-extend
284            int b3 = ((int) bytecodeMap[i++]) & 255;  // unsign-extend
285            int b4 = ((int) bytecodeMap[i++]) & 255;  // unsign-extend
286            deltaBC = (b1 << 8) | b2;
287            deltaIns = (b3 << 8) | b4;
288          }
289          bcIndex += deltaBC;
290          instrIndex = instrIndex.plus(deltaIns);
291          if (instrIndex.sGE(instructionIndex)) {
292            break;
293          }
294          candidateIndex = bcIndex;
295        }
296        return candidateIndex;
297      }
298    
299      /**
300       * Set the stack browser to the innermost logical stack frame of this method
301       */
302      public void set(StackBrowser browser, Offset instr) {
303        browser.setMethod(method);
304        browser.setCompiledMethod(this);
305        browser.setBytecodeIndex(findBytecodeIndexForInstruction(instr));
306    
307        if (VM.TraceStackTrace) {
308          VM.sysWrite("setting stack to frame (base): ");
309          VM.sysWrite(browser.getMethod());
310          VM.sysWrite(browser.getBytecodeIndex());
311          VM.sysWrite("\n");
312        }
313      }
314    
315      /**
316       * Advance the StackBrowser up one internal stack frame, if possible
317       */
318      public boolean up(StackBrowser browser) {
319        return false;
320      }
321    
322      /**
323       * Print this compiled method's portion of a stack trace
324       * @param instructionOffset of machine instruction from start of method
325       * @param out the PrintLN to print the stack trace to.
326       */
327      public void printStackTrace(Offset instructionOffset, PrintLN out) {
328        out.print("\tat ");
329        out.print(method.getDeclaringClass()); // RVMClass
330        out.print('.');
331        out.print(method.getName()); // a Atom, returned via MemberReference.getName().
332        out.print("(");
333        out.print(method.getDeclaringClass().getSourceName()); // a Atom
334        int lineNumber = findLineNumberForInstruction(instructionOffset);
335        if (lineNumber <= 0) {      // unknown line
336          out.print("; machine code offset: ");
337          out.printHex(instructionOffset.toInt());
338        } else {
339          out.print(':');
340          out.print(lineNumber);
341        }
342        out.print(')');
343        out.println();
344      }
345    
346      /**
347       * Print the eTable
348       */
349      public void printExceptionTable() {
350        if (eTable != null) ExceptionTable.printExceptionTable(eTable);
351      }
352    
353      /** Set the lock acquisition offset for synchronized methods */
354      public void setLockAcquisitionOffset(int off) {
355        if (VM.VerifyAssertions) VM._assert((off & 0xFFFF) == off);
356        lockOffset = (char) off;
357      }
358    
359      /** Get the lock acquisition offset */
360      @Uninterruptible
361      public Offset getLockAcquisitionOffset() {
362        return Offset.fromIntZeroExtend(lockOffset);
363      }
364    
365      /** Set the method has a counters array */
366      void setHasCounterArray() {
367        hasCounters = true;
368      }
369    
370      /** Does the method have a counters array? */
371      @Uninterruptible
372      public boolean hasCounterArray() {
373        return hasCounters;
374      }
375    
376      /**
377       * Encode/compress the bytecode map, reference (GC) map and exception table
378       *
379       * @param referenceMaps to encode
380       * @param bcMap unencoded bytecode to code array offset map
381       */
382      public void encodeMappingInfo(ReferenceMaps referenceMaps, int[] bcMap) {
383        int count = 0;
384        int lastBC = 0, lastIns = 0;
385        for (int i = 0; i < bcMap.length; i++) {
386          if (bcMap[i] != 0) {
387            int deltaBC = i - lastBC;
388            int deltaIns = bcMap[i] - lastIns;
389            if (VM.VerifyAssertions) {
390              VM._assert(deltaBC >= 0 && deltaIns >= 0);
391            }
392            if (deltaBC <= 6 && deltaIns <= 31) {
393              count++;
394            } else {
395              if (deltaBC > 65535 || deltaIns > 65535) {
396                VM.sysFail("BaselineCompiledMethod: a fancier encoding is needed");
397              }
398              count += 5;
399            }
400            lastBC = i;
401            lastIns = bcMap[i];
402          }
403        }
404        bytecodeMap = new byte[count];
405        count = lastBC = lastIns = 0;
406        for (int i = 0; i < bcMap.length; i++) {
407          if (bcMap[i] != 0) {
408            int deltaBC = i - lastBC;
409            int deltaIns = bcMap[i] - lastIns;
410            if (VM.VerifyAssertions) {
411              VM._assert(deltaBC >= 0 && deltaIns >= 0);
412            }
413            if (deltaBC <= 6 && deltaIns <= 31) {
414              bytecodeMap[count++] = (byte) ((deltaBC << 5) | deltaIns);
415            } else { // From before, we know that deltaBC <= 65535 and deltaIns <= 65535
416              bytecodeMap[count++] = (byte) 255;
417              bytecodeMap[count++] = (byte) (deltaBC >> 8);
418              bytecodeMap[count++] = (byte) (deltaBC & 255);
419              bytecodeMap[count++] = (byte) (deltaIns >> 8);
420              bytecodeMap[count++] = (byte) (deltaIns & 255);
421            }
422            lastBC = i;
423            lastIns = bcMap[i];
424          }
425        }
426        // TODO: it's likely for short methods we can share the bytecodeMap
427        referenceMaps.translateByte2Machine(bcMap);
428        this.referenceMaps = referenceMaps;
429        ExceptionHandlerMap emap = ((NormalMethod) method).getExceptionHandlerMap();
430        if (emap != null) {
431          eTable = BaselineExceptionTable.encode(emap, bcMap);
432        }
433      }
434    
435      /**
436       * Return the number of bytes used to encode the compiler-specific mapping
437       * information for this compiled method.
438       * Used to gather stats on the space costs of mapping schemes.
439       */
440      public int size() {
441        TypeReference TYPE = TypeReference.findOrCreate(BaselineCompiledMethod.class);
442        int size = TYPE.peekType().asClass().getInstanceSize();
443        if (bytecodeMap != null) size += RVMArray.ByteArray.getInstanceSize(bytecodeMap.length);
444        if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length);
445        if (referenceMaps != null) size += referenceMaps.size();
446        return size;
447      }
448    }