/*
* This file is part of the Jikes RVM project (http://jikesrvm.org).
*
* This file is licensed to You under the Common Public License (CPL);
* 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/cpl1.0.php
*
* See the COPYRIGHT.txt file distributed with this work for information
* regarding copyright ownership.
*/
package org.jikesrvm.compilers.baseline;
import org.jikesrvm.SubordinateArchitecture;
import org.jikesrvm.VM;
import org.jikesrvm.VM_PrintLN;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.classloader.VM_Array;
import org.jikesrvm.classloader.VM_ExceptionHandlerMap;
import org.jikesrvm.classloader.VM_Method;
import org.jikesrvm.classloader.VM_NormalMethod;
import org.jikesrvm.classloader.VM_Type;
import org.jikesrvm.classloader.VM_TypeReference;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.common.VM_ExceptionTable;
import org.jikesrvm.runtime.VM_DynamicLink;
import org.jikesrvm.runtime.VM_ExceptionDeliverer;
import org.jikesrvm.runtime.VM_StackBrowser;
import org.vmmagic.pragma.SynchronizedObject;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.pragma.UninterruptibleNoWarn;
import org.vmmagic.unboxed.Offset;
/**
* Compiler-specific information associated with a method's machine
* instructions.
*/
@SynchronizedObject
public final class VM_BaselineCompiledMethod extends VM_CompiledMethod {
/** 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, VM_Lock must not allow a yield after it has been
* obtained.
*/
private char lockOffset;
/**
* Baseline exception deliverer object
*/
private static VM_ExceptionDeliverer exceptionDeliverer = null;
/**
* Stack-slot reference maps for the compiled method.
*/
public VM_ReferenceMaps referenceMaps;
/*
* Currently needed to support dynamic bridge magic;
* Consider integrating with GC maps
*/
private byte[] bytecodeMap;
/**
* Exception table, null if not present.
*/
private int[] eTable;
/* To make a compiled method's stack offset independ of
* original method, we move 'getEmptyStackOffset'
* here.
*
* TODO: redesign this. There has to be a cleaner way!
*/
//private int startLocalOffset;
private final int emptyStackOffset;
private int lastFixedStackRegister;
private int lastFloatStackRegister;
private final int[] localFixedLocations;
private final int[] localFloatLocations;
// public int getStartLocalOffset() {
// return startLocalOffset;
// }
public int getEmptyStackOffset() {
return emptyStackOffset;
}
//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 VM_Compiler.locationToOffset(location) - BYTES_IN_INT
//e.g. to load double: load at VM_Compiler.locationToOffset(location) - BYTES_IN_DOUBLE
@UninterruptibleNoWarn
public int getGeneralLocalLocation(int localIndex) {
if (!this.isSubArchCompilation()) {
return ArchitectureSpecific.VM_Compiler.getGeneralLocalLocation(localIndex, localFixedLocations, (VM_NormalMethod) method);
} else {
return SubordinateArchitecture.VM_Compiler.getGeneralLocalLocation(localIndex, localFixedLocations, (VM_NormalMethod) method);
}
}
@UninterruptibleNoWarn
public int getFloatLocalLocation(int localIndex) {
if (!this.isSubArchCompilation()) {
return ArchitectureSpecific.VM_Compiler.getFloatLocalLocation(localIndex, localFloatLocations, (VM_NormalMethod) method);
} else {
return SubordinateArchitecture.VM_Compiler.getFloatLocalLocation(localIndex, localFloatLocations, (VM_NormalMethod) method);
}
}
@UninterruptibleNoWarn
public int getGeneralStackLocation(int stackIndex) {
if (!this.isSubArchCompilation()) {
return ArchitectureSpecific.VM_Compiler.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS));
} else {
return SubordinateArchitecture.VM_Compiler.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS));
}
}
@UninterruptibleNoWarn
public int getFloatStackLocation(int stackIndex) { //for now same implementation as getGeneralStackLocation, todo
if (!this.isSubArchCompilation()) {
return ArchitectureSpecific.VM_Compiler.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS));
} else {
return SubordinateArchitecture.VM_Compiler.offsetToLocation(emptyStackOffset - (stackIndex << LOG_BYTES_IN_ADDRESS));
}
}
@Uninterruptible
public int getLastFixedStackRegister() {
return lastFixedStackRegister;
}
@Uninterruptible
public int getLastFloatStackRegister() {
return lastFloatStackRegister;
}
public VM_BaselineCompiledMethod(int id, VM_Method m, boolean forSubArch) {
super(id, m, forSubArch);
VM_NormalMethod nm = (VM_NormalMethod) m;
if (!forSubArch) {
exceptionDeliverer = new ArchitectureSpecific.VM_BaselineExceptionDeliverer();
this.emptyStackOffset = ArchitectureSpecific.VM_Compiler.getEmptyStackOffset(nm);
} else {
exceptionDeliverer = null; // FIXME: new SubordinateArchitecture.VM_BaselineExceptionDeliverer();
this.emptyStackOffset = SubordinateArchitecture.VM_Compiler.getEmptyStackOffset(nm);
}
this.localFixedLocations = new int[nm.getLocalWords()];
this.localFloatLocations = new int[nm.getLocalWords()];
this.lastFixedStackRegister = -1;
this.lastFloatStackRegister = -1;
}
public void compile() {
if (!this.isSubArchCompilation()) {
ArchitectureSpecific.VM_Compiler comp = new ArchitectureSpecific.VM_Compiler(this, localFixedLocations, localFloatLocations);
if (this.method.isCompilableForMainArch()) {
comp.compile();
} else {
comp.compileStub(); // don't compile for this architecture
}
this.lastFixedStackRegister = comp.getLastFixedStackRegister();
this.lastFloatStackRegister = comp.getLastFloatStackRegister();
} else {
SubordinateArchitecture.VM_Compiler subComp = new SubordinateArchitecture.VM_Compiler(this, localFixedLocations, localFloatLocations);
if (this.method.isCompilableForSubArch()) {
subComp.compile();
} else {
subComp.compileStub(); // don't compile for this architecture
}
this.lastFixedStackRegister = subComp.getLastFixedStackRegister();
this.lastFloatStackRegister = subComp.getLastFloatStackRegister();
}
}
@Uninterruptible
public int getCompilerType() {
return BASELINE;
}
public String getCompilerName() {
return "baseline compiler";
}
@Uninterruptible
public VM_ExceptionDeliverer getExceptionDeliverer() {
return exceptionDeliverer;
}
public int findCatchBlockForInstruction(Offset instructionOffset, VM_Type exceptionType) {
if (eTable == null) {
return -1;
} else {
return VM_ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType, this.isSubArchCompilation());
}
}
@Uninterruptible
public void getDynamicLink(VM_DynamicLink dynamicLink, Offset instructionOffset) {
int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset);
((VM_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 ((VM_NormalMethod) method).getLineNumberForBCIndex(bci);
}
/**
* Return whether or not the instruction offset corresponds to an uninterruptible context.
*
* @param offset 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.
*
* Note: This method expects the instructionIndex to refer to the machine
* instruction immediately FOLLOWING the bytecode in question.
* just like findLineNumberForInstruction. See VM_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) {
int lg_istr_width;
if (!this.isSubArchCompilation()) {
lg_istr_width = ArchitectureSpecific.VM_BaselineConstants.LG_INSTRUCTION_WIDTH;
} else {
lg_istr_width = SubordinateArchitecture.VM_BaselineConstants.LG_INSTRUCTION_WIDTH;
}
Offset instructionIndex = instructionOffset.toWord().rsha(lg_istr_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(VM_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 VM_StackBrowser up one internal stack frame, if possible
*/
public boolean up(VM_StackBrowser browser) {
return false;
}
// Print this compiled method's portion of a stack trace
// Taken: offset of machine instruction from start of method
// the VM_PrintLN to print the stack trace to.
public void printStackTrace(Offset instructionOffset, VM_PrintLN out) {
out.print("\tat ");
out.print(method.getDeclaringClass()); // VM_Class
out.print('.');
out.print(method.getName()); // a VM_Atom, returned via VM_MemberReference.getName().
out.print("(");
out.print(method.getDeclaringClass().getSourceName()); // a VM_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) VM_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 */
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;
}
// Taken: method that was compiled
// bytecode-index to machine-instruction-index map for method
// number of instructions for method
//
public void encodeMappingInfo(VM_ReferenceMaps referenceMaps, int[] bcMap, int numInstructions, boolean forSubArch) {
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("VM_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];
}
}
referenceMaps.translateByte2Machine(bcMap);
this.referenceMaps = referenceMaps;
VM_ExceptionHandlerMap emap = ((VM_NormalMethod) method).getExceptionHandlerMap();
if (emap != null) {
eTable = VM_BaselineExceptionTable.encode(emap, bcMap, forSubArch);
}
}
private static final VM_TypeReference TYPE = VM_TypeReference.findOrCreate(VM_BaselineCompiledMethod.class);
public int size() {
int size = TYPE.peekType().asClass().getInstanceSize();
if (bytecodeMap != null) size += VM_Array.ByteArray.getInstanceSize(bytecodeMap.length);
if (eTable != null) size += VM_Array.IntArray.getInstanceSize(eTable.length);
if (referenceMaps != null) size += referenceMaps.size();
return size;
}
}