/* * This file is part of the Jikes RVM project (http://jikesrvm.org). * * This file is licensed to You under the Eclipse Public License (EPL); * You may not use this file except in compliance with the License. You * may obtain a copy of the License at * * http://www.opensource.org/licenses/eclipse-1.0.php * * See the COPYRIGHT.txt file distributed with this work for information * regarding copyright ownership. */ package org.jikesrvm.compilers.baseline; import org.jikesrvm.PrintLN; import org.jikesrvm.VM; import org.jikesrvm.ArchitectureSpecific.BaselineCompilerImpl; import org.jikesrvm.ArchitectureSpecific.BaselineConstants; import org.jikesrvm.ArchitectureSpecific.BaselineExceptionDeliverer; import org.jikesrvm.classloader.ExceptionHandlerMap; import org.jikesrvm.classloader.NormalMethod; import org.jikesrvm.classloader.RVMArray; import org.jikesrvm.classloader.RVMMethod; import org.jikesrvm.classloader.RVMType; import org.jikesrvm.classloader.TypeReference; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.common.ExceptionTable; import org.jikesrvm.runtime.DynamicLink; import org.jikesrvm.runtime.ExceptionDeliverer; import org.jikesrvm.runtime.StackBrowser; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.pragma.Unpreemptible; import org.vmmagic.unboxed.Offset; /** * Compiler-specific information associated with a method's machine * instructions. */ public final class BaselineCompiledMethod extends CompiledMethod implements BaselineConstants { /** Does the baseline compiled method have a counters array? */ private boolean hasCounters; /** * The lock acquistion offset for synchronized methods. For * synchronized methods, the offset (in the method prologue) after * which the monitor has been obtained. At, or before, this point, * the method does not own the lock. Used by deliverException to * determine whether the lock needs to be released. Note: for this * scheme to work, Lock must not allow a yield after it has been * obtained. */ private char lockOffset; /** * Baseline exception deliverer object */ private static final ExceptionDeliverer exceptionDeliverer = new BaselineExceptionDeliverer(); /** * Stack-slot reference maps for the compiled method. */ public ReferenceMaps referenceMaps; /** * Encoded representation of bytecode index to offset in code array * map. Currently needed to support dynamic bridge magic; Consider * integrating with GC maps */ private byte[] bytecodeMap; /** * Exception table, null if not present. */ private int[] eTable; /** Offset into stack frame when operand stack is empty */ private final short emptyStackOffset; /** PPC only: last general purpose register holding part of the operand stack */ private byte lastFixedStackRegister; /** PPC only: last floating point register holding part of the operand stack */ private byte lastFloatStackRegister; /** * PPC only: location of general purpose local variables, positive * values are register numbers, negative are stack offsets */ private final short[] localFixedLocations; /** * PPC only: location of floating point local variables, positive * values are register numbers, negative are stack offsets */ private final short[] localFloatLocations; /** @return offset into stack frame when operand stack is empty */ public int getEmptyStackOffset() { return emptyStackOffset; } /** * Location of local general purpose variable. These Locations are * positioned at the top of the stackslot that contains the value * before accessing, substract size of value you want to access. * e.g. to load int: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_INT * e.g. to load long: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_LONG */ @Uninterruptible public short getGeneralLocalLocation(int localIndex) { return BaselineCompilerImpl.getGeneralLocalLocation(localIndex, localFixedLocations, (NormalMethod) method); } /** * Location of local floating point variable. These Locations are * positioned at the top of the stackslot that contains the value * before accessing, substract size of value you want to access. * e.g. to load float: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_FLOAT * e.g. to load double: load at BaselineCompilerImpl.locationToOffset(location) - BYTES_IN_DOUBLE */ @Uninterruptible public short getFloatLocalLocation(int localIndex) { return BaselineCompilerImpl.getFloatLocalLocation(localIndex, localFloatLocations, (NormalMethod) method); } /** Offset onto stack of a particular general purpose operand stack location */ @Uninterruptible public short getGeneralStackLocation(int stackIndex) { return BaselineCompilerImpl.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS)); } /** Offset onto stack of a particular operand stack location for a floating point value */ @Uninterruptible public short getFloatStackLocation(int stackIndex) { // for now same implementation as getGeneralStackLocation return getGeneralStackLocation(stackIndex); } /** Last general purpose register holding part of the operand stack */ @Uninterruptible public int getLastFixedStackRegister() { return lastFixedStackRegister; } /** Last floating point register holding part of the operand stack */ @Uninterruptible public int getLastFloatStackRegister() { return lastFloatStackRegister; } /** Constructor */ public BaselineCompiledMethod(int id, RVMMethod m) { super(id, m); NormalMethod nm = (NormalMethod) m; //this.startLocalOffset = BaselineCompilerImpl.getStartLocalOffset(nm); this.emptyStackOffset = (short)BaselineCompilerImpl.getEmptyStackOffset(nm); this.localFixedLocations = VM.BuildForIA32 ? null : new short[nm.getLocalWords()]; this.localFloatLocations = VM.BuildForIA32 ? null : new short[nm.getLocalWords()]; this.lastFixedStackRegister = -1; this.lastFloatStackRegister = -1; } /** Compile method */ public void compile() { BaselineCompilerImpl comp = new BaselineCompilerImpl(this, localFixedLocations, localFloatLocations); comp.compile(); this.lastFixedStackRegister = comp.getLastFixedStackRegister(); this.lastFloatStackRegister = comp.getLastFloatStackRegister(); } /** @return BASELINE */ @Uninterruptible public int getCompilerType() { return BASELINE; } /** @return "baseline compiler" */ public String getCompilerName() { return "baseline compiler"; } /** * Get the exception deliverer for this kind of compiled method */ @Uninterruptible public ExceptionDeliverer getExceptionDeliverer() { return exceptionDeliverer; } /** * Find a catch block within the compiled method * @param instructionOffset offset of faulting instruction in compiled code * @param exceptionType the type of the thrown exception * @return the machine code offset of the catch block. */ @Unpreemptible public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) { if (eTable == null) { return -1; } else { return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType); } } /** * Fetch symbolic reference to a method that's called by one of * this method's instructions. * @param dynamicLink place to put return information * @param instructionOffset offset of machine instruction from start of * this method, in bytes * * Notes: * <ul> * <li> The "instructionOffset" must point to the instruction i * <em> following </em> the call * instruction whose target method is sought. * This allows us to properly handle the case where * the only address we have to work with is a return address * (ie. from a stackframe) * on a machine architecture with variable length instructions. * In such situations we'd have no idea how far to back up the * instruction pointer * to point to the "call site". * * <li> The implementation must not cause any allocations, * because it executes with * gc disabled when called by GCMapIterator. * <ul> */ @Uninterruptible public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) { int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset); ((NormalMethod) method).getDynamicLink(dynamicLink, bytecodeIndex); } /** * @return The line number, a positive integer. Zero means unable to find. */ @Uninterruptible public int findLineNumberForInstruction(Offset instructionOffset) { int bci = findBytecodeIndexForInstruction(instructionOffset); if (bci == -1) return 0; return ((NormalMethod) method).getLineNumberForBCIndex(bci); } /** * Return whether or not the instruction offset corresponds to an uninterruptible context. * * @param instructionOffset of addr from start of instructions in bytes * @return true if the IP is within an Uninterruptible method, false otherwise. */ public boolean isWithinUninterruptibleCode(Offset instructionOffset) { return method.isUninterruptible(); } /** * Find bytecode index corresponding to one of this method's * machine instructions. * * @param instructionOffset instruction offset to map to a bytecode index * Note: This method expects the offset to refer to the machine * instruction immediately FOLLOWING the bytecode in question. just * like findLineNumberForInstruction. See CompiledMethod for * rationale * NOTE: instructionIndex is in units of instructions, not bytes * (different from all the other methods in this interface!!) * @return the bytecode index for the machine instruction, -1 if not * available or not found. */ @Uninterruptible public int findBytecodeIndexForInstruction(Offset instructionOffset) { Offset instructionIndex = instructionOffset.toWord().rsha(LG_INSTRUCTION_WIDTH).toOffset(); int candidateIndex = -1; int bcIndex = 0; Offset instrIndex = Offset.zero(); for (int i = 0; i < bytecodeMap.length;) { int b0 = ((int) bytecodeMap[i++]) & 255; // unsign-extend int deltaBC, deltaIns; if (b0 != 255) { deltaBC = b0 >> 5; deltaIns = b0 & 31; } else { int b1 = ((int) bytecodeMap[i++]) & 255; // unsign-extend int b2 = ((int) bytecodeMap[i++]) & 255; // unsign-extend int b3 = ((int) bytecodeMap[i++]) & 255; // unsign-extend int b4 = ((int) bytecodeMap[i++]) & 255; // unsign-extend deltaBC = (b1 << 8) | b2; deltaIns = (b3 << 8) | b4; } bcIndex += deltaBC; instrIndex = instrIndex.plus(deltaIns); if (instrIndex.sGE(instructionIndex)) { break; } candidateIndex = bcIndex; } return candidateIndex; } /** * Set the stack browser to the innermost logical stack frame of this method */ public void set(StackBrowser browser, Offset instr) { browser.setMethod(method); browser.setCompiledMethod(this); browser.setBytecodeIndex(findBytecodeIndexForInstruction(instr)); if (VM.TraceStackTrace) { VM.sysWrite("setting stack to frame (base): "); VM.sysWrite(browser.getMethod()); VM.sysWrite(browser.getBytecodeIndex()); VM.sysWrite("\n"); } } /** * Advance the StackBrowser up one internal stack frame, if possible */ public boolean up(StackBrowser browser) { return false; } /** * Print this compiled method's portion of a stack trace * @param instructionOffset of machine instruction from start of method * @param out the PrintLN to print the stack trace to. */ public void printStackTrace(Offset instructionOffset, PrintLN out) { out.print("\tat "); out.print(method.getDeclaringClass()); // RVMClass out.print('.'); out.print(method.getName()); // a Atom, returned via MemberReference.getName(). out.print("("); out.print(method.getDeclaringClass().getSourceName()); // a Atom int lineNumber = findLineNumberForInstruction(instructionOffset); if (lineNumber <= 0) { // unknown line out.print("; machine code offset: "); out.printHex(instructionOffset.toInt()); } else { out.print(':'); out.print(lineNumber); } out.print(')'); out.println(); } /** * Print the eTable */ public void printExceptionTable() { if (eTable != null) ExceptionTable.printExceptionTable(eTable); } /** Set the lock acquisition offset for synchronized methods */ public void setLockAcquisitionOffset(int off) { if (VM.VerifyAssertions) VM._assert((off & 0xFFFF) == off); lockOffset = (char) off; } /** Get the lock acquisition offset */ @Uninterruptible public Offset getLockAcquisitionOffset() { return Offset.fromIntZeroExtend(lockOffset); } /** Set the method has a counters array */ void setHasCounterArray() { hasCounters = true; } /** Does the method have a counters array? */ @Uninterruptible public boolean hasCounterArray() { return hasCounters; } /** * Encode/compress the bytecode map, reference (GC) map and exception table * * @param referenceMaps to encode * @param bcMap unencoded bytecode to code array offset map */ public void encodeMappingInfo(ReferenceMaps referenceMaps, int[] bcMap) { int count = 0; int lastBC = 0, lastIns = 0; for (int i = 0; i < bcMap.length; i++) { if (bcMap[i] != 0) { int deltaBC = i - lastBC; int deltaIns = bcMap[i] - lastIns; if (VM.VerifyAssertions) { VM._assert(deltaBC >= 0 && deltaIns >= 0); } if (deltaBC <= 6 && deltaIns <= 31) { count++; } else { if (deltaBC > 65535 || deltaIns > 65535) { VM.sysFail("BaselineCompiledMethod: a fancier encoding is needed"); } count += 5; } lastBC = i; lastIns = bcMap[i]; } } bytecodeMap = new byte[count]; count = lastBC = lastIns = 0; for (int i = 0; i < bcMap.length; i++) { if (bcMap[i] != 0) { int deltaBC = i - lastBC; int deltaIns = bcMap[i] - lastIns; if (VM.VerifyAssertions) { VM._assert(deltaBC >= 0 && deltaIns >= 0); } if (deltaBC <= 6 && deltaIns <= 31) { bytecodeMap[count++] = (byte) ((deltaBC << 5) | deltaIns); } else { // From before, we know that deltaBC <= 65535 and deltaIns <= 65535 bytecodeMap[count++] = (byte) 255; bytecodeMap[count++] = (byte) (deltaBC >> 8); bytecodeMap[count++] = (byte) (deltaBC & 255); bytecodeMap[count++] = (byte) (deltaIns >> 8); bytecodeMap[count++] = (byte) (deltaIns & 255); } lastBC = i; lastIns = bcMap[i]; } } // TODO: it's likely for short methods we can share the bytecodeMap referenceMaps.translateByte2Machine(bcMap); this.referenceMaps = referenceMaps; ExceptionHandlerMap emap = ((NormalMethod) method).getExceptionHandlerMap(); if (emap != null) { eTable = BaselineExceptionTable.encode(emap, bcMap); } } /** * Return the number of bytes used to encode the compiler-specific mapping * information for this compiled method. * Used to gather stats on the space costs of mapping schemes. */ public int size() { TypeReference TYPE = TypeReference.findOrCreate(BaselineCompiledMethod.class); int size = TYPE.peekType().asClass().getInstanceSize(); if (bytecodeMap != null) size += RVMArray.ByteArray.getInstanceSize(bytecodeMap.length); if (eTable != null) size += RVMArray.IntArray.getInstanceSize(eTable.length); if (referenceMaps != null) size += referenceMaps.size(); return size; } }