/*
* 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.VM;
import org.jikesrvm.architecture.ArchConstants;
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.jikesrvm.util.PrintLN;
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 abstract class BaselineCompiledMethod extends CompiledMethod {
/** Does the baseline compiled method have a counters array? */
private boolean hasCounters;
/**
* The lock acquisition 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;
static {
if (VM.BuildForIA32) {
exceptionDeliverer = new org.jikesrvm.compilers.baseline.ia32.BaselineExceptionDeliverer();
} else {
exceptionDeliverer = new org.jikesrvm.compilers.baseline.ppc.BaselineExceptionDeliverer();
if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
}
}
/**
* 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;
protected BaselineCompiledMethod(int id, RVMMethod m) {
super(id, m);
}
/**
* Architecture-specific subclasses are responsible for preserving any data about this
* method (such as local variable locations) that will be needed later.
*/
protected abstract void saveCompilerData(BaselineCompiler comp);
/** Compile method */
public void compile() {
BaselineCompiler comp;
if (VM.BuildForIA32) {
comp = new org.jikesrvm.compilers.baseline.ia32.BaselineCompilerImpl(this);
} else {
if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC);
comp = new org.jikesrvm.compilers.baseline.ppc.BaselineCompilerImpl(this);
}
comp.compile();
saveCompilerData(comp);
}
/** @return BASELINE */
@Override
@Uninterruptible
public int getCompilerType() {
return BASELINE;
}
/** @return "baseline compiler" */
@Override
public String getCompilerName() {
return "baseline compiler";
}
/**
* @return the exception deliverer for this kind of compiled method
*/
@Override
@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.
*/
@Override
@Unpreemptible
public int findCatchBlockForInstruction(Offset instructionOffset, RVMType exceptionType) {
if (eTable == null) {
return -1;
} else {
return ExceptionTable.findCatchBlockForInstruction(eTable, instructionOffset, exceptionType);
}
}
@Override
@Uninterruptible
public void getDynamicLink(DynamicLink dynamicLink, Offset instructionOffset) {
int bytecodeIndex = findBytecodeIndexForInstruction(instructionOffset);
((NormalMethod) method).getDynamicLink(dynamicLink, bytecodeIndex);
}
/**
* @param instructionOffset the instruction's offset in the code for the method
* @return The line number, a positive integer. Zero means unable to find.
*/
@Override
@Uninterruptible
public int findLineNumberForInstruction(Offset instructionOffset) {
int bci = findBytecodeIndexForInstruction(instructionOffset);
if (bci == -1) return 0;
return ((NormalMethod) method).getLineNumberForBCIndex(bci);
}
@Override
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.<br>
* 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.<br>
* 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(ArchConstants.getLogInstructionWidth()).toOffset();
int candidateIndex = -1;
int bcIndex = 0;
Offset instrIndex = Offset.zero();
for (int i = 0; i < bytecodeMap.length;) {
int b0 = (bytecodeMap[i++]) & 255; // unsign-extend
int deltaBC, deltaIns;
if (b0 != 255) {
deltaBC = b0 >> 5;
deltaIns = b0 & 31;
} else {
int b1 = (bytecodeMap[i++]) & 255; // unsign-extend
int b2 = (bytecodeMap[i++]) & 255; // unsign-extend
int b3 = (bytecodeMap[i++]) & 255; // unsign-extend
int b4 = (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;
}
@Override
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.sysWriteln();
}
}
@Override
public boolean up(StackBrowser browser) {
return false;
}
@Override
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);
}
/**
* Sets the lock acquisition offset for synchronized methods.
*
* @param off new offset
*/
public void setLockAcquisitionOffset(int off) {
if (VM.VerifyAssertions) VM._assert((off & 0xFFFF) == off);
lockOffset = (char) off;
}
/** @return the lock acquisition offset */
@Uninterruptible
public Offset getLockAcquisitionOffset() {
return Offset.fromIntZeroExtend(lockOffset);
}
/** Set the method has a counters array */
void setHasCounterArray() {
hasCounters = true;
}
/** @return whether the method has 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);
}
}
@Override
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;
}
}