/*
* 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.ia32;
import org.jikesrvm.VM;
import org.jikesrvm.classloader.MethodReference;
import org.jikesrvm.classloader.NormalMethod;
import org.jikesrvm.classloader.TypeReference;
import org.jikesrvm.compilers.baseline.BaselineCompiledMethod;
import org.jikesrvm.compilers.baseline.ReferenceMaps;
import org.jikesrvm.compilers.common.CompiledMethod;
import org.jikesrvm.compilers.common.CompiledMethods;
import org.jikesrvm.ia32.BaselineConstants;
import org.jikesrvm.mm.mminterface.GCMapIterator;
import org.jikesrvm.runtime.DynamicLink;
import org.jikesrvm.runtime.Magic;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.WordArray;
/**
* Iterator for stack frame built by the Baseline compiler
* An Instance of this class will iterate through a particular
* reference map of a method returning the offsets of any refereces
* that are part of the input parameters, local variables, and
* java stack for the stack frame.
*/
@Uninterruptible
public abstract class BaselineGCMapIterator extends GCMapIterator implements BaselineConstants {
private static final boolean TRACE_ALL = false;
private static final boolean TRACE_DL = false; // dynamic link frames
/*
* Iterator state for mapping any stackframe.
*/
/** Compiled method for the frame */
private NormalMethod currentMethod;
/** Compiled method for the frame */
private BaselineCompiledMethod currentCompiledMethod;
private int currentNumLocals;
/** Current index in current map */
private int mapIndex;
/** id of current map out of all maps */
private int mapId;
/** set of maps for this method */
private ReferenceMaps maps;
/** have we reported the base ptr of the edge counter array? */
private boolean counterArrayBase;
/*
* Additional iterator state for mapping dynamic bridge stackframes.
*/
/** place to keep info returned by CompiledMethod.getDynamicLink */
private final DynamicLink dynamicLink;
/** method to be invoked via dynamic bridge (null: current frame is not a dynamic bridge) */
private MethodReference bridgeTarget;
/** parameter types passed by that method */
private TypeReference[] bridgeParameterTypes;
/** have all bridge parameters been mapped yet? */
private boolean bridgeParameterMappingRequired;
/** do we need to map spilled params (baseline compiler = no, opt = yes) */
private boolean bridgeSpilledParameterMappingRequired;
/** have the register location been updated */
private boolean bridgeRegistersLocationUpdated;
/** have we processed all the values in the regular map yet? */
private boolean finishedWithRegularMap;
/** first parameter to be mapped (-1 == "this") */
private int bridgeParameterInitialIndex;
/** current parameter being mapped (-1 == "this") */
private int bridgeParameterIndex;
/** gpr register it lives in */
private int bridgeRegisterIndex;
/** memory address at which that register was saved */
private Address bridgeRegisterLocation;
/** current spilled param location */
private Address bridgeSpilledParamLocation;
/** starting offset to stack location for param0 */
private int bridgeSpilledParamInitialOffset;
/**
* Constructor. Remember the location array for registers. This array needs to
* be updated with the location of any saved registers. This information is
* not used by this iterator but must be updated for the other types of
* iterators (ones for the opt compiler built frames) The locations are kept
* as addresses within the stack.
*/
public BaselineGCMapIterator(WordArray registerLocations) {
this.registerLocations = registerLocations; // (in superclass)
dynamicLink = new DynamicLink();
}
/*
* Interface
*/
/**
* Set the iterator to scan the map at the machine instruction offset
* provided. The iterator is positioned to the beginning of the map. NOTE: An
* iterator may be reused to scan a different method and map.
*
* @param compiledMethod
* identifies the method and class
* @param instructionOffset
* identifies the map to be scanned.
* @param fp
* identifies a specific occurrance of this method and allows for
* processing instance specific information i.e JSR return address
* values
*/
public void setupIterator(CompiledMethod compiledMethod, Offset instructionOffset, Address fp) {
currentCompiledMethod = (BaselineCompiledMethod) compiledMethod;
currentMethod = (NormalMethod) currentCompiledMethod.getMethod();
currentNumLocals = currentMethod.getLocalWords();
// setup superclass
//
framePtr = fp;
// setup stackframe mapping
//
maps = ((BaselineCompiledMethod) compiledMethod).referenceMaps;
mapId = maps.locateGCPoint(instructionOffset, currentMethod);
mapIndex = 0;
if (mapId < 0) {
// lock the jsr lock to serialize jsr processing
ReferenceMaps.jsrLock.lock();
int JSRindex = maps.setupJSRSubroutineMap(mapId);
while (JSRindex != 0) {
Address nextCallerAddress = framePtr.plus(convertIndexToOffset(JSRindex)).loadAddress();
Offset nextMachineCodeOffset = compiledMethod.getInstructionOffset(nextCallerAddress);
if (VM.TraceStkMaps) {
VM.sysWriteln(" setupJSRsubroutineMap- nested jsrs end of loop- = ");
VM.sysWriteln(" next jsraddress offset = ", JSRindex);
VM.sysWriteln(" next callers address = ", nextCallerAddress);
VM.sysWriteln(" next machinecodeoffset = ", nextMachineCodeOffset);
if (nextMachineCodeOffset.sLT(Offset.zero())) {
VM.sysWriteln("BAD MACHINE CODE OFFSET");
}
}
JSRindex = maps.getNextJSRAddressIndex(nextMachineCodeOffset, currentMethod);
}
}
if (VM.TraceStkMaps || TRACE_ALL) {
VM.sysWrite("BaselineGCMapIterator setupIterator mapId = ");
VM.sysWrite(mapId);
VM.sysWrite(" for ");
VM.sysWrite(compiledMethod.getMethod());
VM.sysWrite(".\n");
}
// setup dynamic bridge mapping
//
bridgeTarget = null;
bridgeParameterTypes = null;
bridgeParameterMappingRequired = false;
bridgeRegistersLocationUpdated = false;
bridgeParameterIndex = 0;
bridgeRegisterIndex = 0;
bridgeRegisterLocation = Address.zero();
bridgeSpilledParamLocation = Address.zero();
if (currentMethod.getDeclaringClass().hasDynamicBridgeAnnotation()) {
Address ip = Magic.getReturnAddress(fp);
fp = Magic.getCallerFramePointer(fp);
int callingCompiledMethodId = Magic.getCompiledMethodID(fp);
CompiledMethod callingCompiledMethod = CompiledMethods.getCompiledMethod(callingCompiledMethodId);
Offset callingInstructionOffset = callingCompiledMethod.getInstructionOffset(ip);
callingCompiledMethod.getDynamicLink(dynamicLink, callingInstructionOffset);
bridgeTarget = dynamicLink.methodRef();
bridgeParameterTypes = bridgeTarget.getParameterTypes();
if (dynamicLink.isInvokedWithImplicitThisParameter()) {
bridgeParameterInitialIndex = -1;
bridgeSpilledParamInitialOffset = 2*WORDSIZE; // this + return addr
} else {
bridgeParameterInitialIndex = 0;
bridgeSpilledParamInitialOffset = WORDSIZE; // return addr
}
bridgeSpilledParamInitialOffset += (bridgeTarget.getParameterWords() << LG_WORDSIZE);
bridgeSpilledParameterMappingRequired = callingCompiledMethod.getCompilerType() != CompiledMethod.BASELINE;
}
reset();
}
/**
* Reset iteration to initial state. This allows a map to be scanned multiple
* times.
*/
public void reset() {
mapIndex = 0;
finishedWithRegularMap = false;
// setup map to report EBX if this method is holding the base of counter array in it.
counterArrayBase = currentCompiledMethod.hasCounterArray();
if (bridgeTarget != null) {
bridgeParameterMappingRequired = true;
bridgeParameterIndex = bridgeParameterInitialIndex;
bridgeRegisterIndex = 0;
bridgeRegisterLocation = framePtr.plus(STACKFRAME_FIRST_PARAMETER_OFFSET); // top of frame
bridgeSpilledParamLocation = framePtr.plus(bridgeSpilledParamInitialOffset);
}
}
/**
* given a index in the local area (biased : local0 has index 1)
* this routine determines the correspondig offset in the stack
*/
public short convertIndexToLocation(int index) {
if (index == 0) return 0;
if (index <= currentNumLocals) { //index is biased by 1;
return currentCompiledMethod.getGeneralLocalLocation(index - 1);
} else {
return currentCompiledMethod.getGeneralStackLocation(index - 1 - currentNumLocals);
}
}
private int convertIndexToOffset(int index) {
//for ia32: always offset, never registers
if (index == 0) return 0; //invalid
// index is biased by 1, index 1 means local 0, this is at offset -BYTES_IN_ADDRESS from startLocalOffset
int offset = BaselineCompilerImpl.locationToOffset(convertIndexToLocation(index)) - BYTES_IN_ADDRESS; // no jsrbit here
if (VM.TraceStkMaps) {
VM.sysWriteln("convertIndexToOffset- input index = ", index, " offset = ", offset);
}
return offset;
}
/**
* Get location of next reference. A zero return indicates that no more
* references exist.
*/
public Address getNextReferenceAddress() {
if (!finishedWithRegularMap) {
if (counterArrayBase) {
counterArrayBase = false;
return registerLocations.get(EBX.value()).toAddress();
}
if (mapId < 0) {
mapIndex = maps.getNextJSRRefIndex(mapIndex);
} else {
mapIndex = maps.getNextRefIndex(mapIndex, mapId);
}
if (mapIndex != 0) {
int mapOffset = convertIndexToOffset(mapIndex);
if (VM.TraceStkMaps || TRACE_ALL) {
VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = ");
VM.sysWriteHex(mapOffset);
VM.sysWrite(".\n");
VM.sysWrite("Reference is ");
}
if (bridgeParameterMappingRequired) {
if (VM.TraceStkMaps || TRACE_ALL) {
VM.sysWriteHex(framePtr.plus(mapOffset - BRIDGE_FRAME_EXTRA_SIZE).loadAddress());
VM.sysWrite(".\n");
if (mapId < 0) {
VM.sysWrite("Offset is a JSR return address ie internal pointer.\n");
}
}
// TODO clean this
return (framePtr.plus(mapOffset - BRIDGE_FRAME_EXTRA_SIZE));
} else {
if (VM.TraceStkMaps || TRACE_ALL) {
VM.sysWriteHex(framePtr.plus(mapOffset).loadAddress());
VM.sysWrite(".\n");
if (mapId < 0) {
VM.sysWrite("Offset is a JSR return address ie internal pointer.\n");
}
}
return (framePtr.plus(mapOffset));
}
} else {
// remember that we are done with the map for future calls, and then
// drop down to the code below
finishedWithRegularMap = true;
}
}
if (bridgeParameterMappingRequired) {
if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
VM.sysWrite("getNextReferenceAddress: bridgeTarget=");
VM.sysWrite(bridgeTarget);
VM.sysWrite("\n");
}
if (!bridgeRegistersLocationUpdated) {
// point registerLocations[] to our callers stackframe
//
registerLocations.set(EDI.value(), framePtr.plus(EDI_SAVE_OFFSET).toWord());
registerLocations.set(T0.value(), framePtr.plus(T0_SAVE_OFFSET).toWord());
registerLocations.set(T1.value(), framePtr.plus(T1_SAVE_OFFSET).toWord());
registerLocations.set(EBX.value(), framePtr.plus(EBX_SAVE_OFFSET).toWord());
bridgeRegistersLocationUpdated = true;
}
// handle implicit "this" parameter, if any
//
if (bridgeParameterIndex == -1) {
bridgeParameterIndex += 1;
bridgeRegisterIndex += 1;
bridgeRegisterLocation = bridgeRegisterLocation.minus(WORDSIZE);
bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = dynamic link GPR this ");
VM.sysWrite(bridgeRegisterLocation.plus(WORDSIZE));
VM.sysWrite(".\n");
}
return bridgeRegisterLocation.plus(WORDSIZE);
}
// now the remaining parameters
//
while (bridgeParameterIndex < bridgeParameterTypes.length) {
TypeReference bridgeParameterType = bridgeParameterTypes[bridgeParameterIndex++];
if (bridgeParameterType.isReferenceType()) {
bridgeRegisterIndex += 1;
bridgeRegisterLocation = bridgeRegisterLocation.minus(WORDSIZE);
bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
if (bridgeRegisterIndex <= NUM_PARAMETER_GPRS) {
if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = dynamic link GPR parameter ");
VM.sysWrite(bridgeRegisterLocation.plus(WORDSIZE));
VM.sysWrite(".\n");
}
return bridgeRegisterLocation.plus(WORDSIZE);
} else {
if (bridgeSpilledParameterMappingRequired) {
if (VM.TraceStkMaps || TRACE_ALL || TRACE_DL) {
VM.sysWrite("BaselineGCMapIterator getNextReferenceOffset = dynamic link spilled parameter ");
VM.sysWrite(bridgeSpilledParamLocation.plus(WORDSIZE));
VM.sysWrite(".\n");
}
return bridgeSpilledParamLocation.plus(WORDSIZE);
} else {
break;
}
}
} else if (bridgeParameterType.isLongType()) {
bridgeRegisterIndex += VM.BuildFor32Addr ? 2 : 1;
bridgeRegisterLocation = bridgeRegisterLocation.minus(2*WORDSIZE);
bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(2*WORDSIZE);
} else if (bridgeParameterType.isDoubleType()) {
bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(2*WORDSIZE);
} else if (bridgeParameterType.isFloatType()) {
bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
} else {
// boolean, byte, char, short, int
bridgeRegisterIndex += 1;
bridgeRegisterLocation = bridgeRegisterLocation.minus(WORDSIZE);
bridgeSpilledParamLocation = bridgeSpilledParamLocation.minus(WORDSIZE);
}
}
} else {
// point registerLocations[] to our callers stackframe
//
registerLocations.set(EDI.value(), framePtr.plus(EDI_SAVE_OFFSET).toWord());
registerLocations.set(EBX.value(), framePtr.plus(EBX_SAVE_OFFSET).toWord());
if (currentMethod.hasBaselineSaveLSRegistersAnnotation()) {
registerLocations.set(EBP.value(), framePtr.plus(EBP_SAVE_OFFSET).toWord());
}
}
return Address.zero();
}
/**
* Gets the location of the next return address after the current position. A
* zero return indicates that no more references exist
*/
public Address getNextReturnAddressAddress() {
if (mapId >= 0) {
if (VM.TraceStkMaps || TRACE_ALL) {
VM.sysWrite("BaselineGCMapIterator getNextReturnAddressOffset mapId = ");
VM.sysWrite(mapId);
VM.sysWrite(".\n");
}
return Address.zero();
}
mapIndex = maps.getNextJSRReturnAddrIndex(mapIndex);
if (VM.TraceStkMaps || TRACE_ALL) {
VM.sysWrite("BaselineGCMapIterator getNextReturnAddressOffset = ");
VM.sysWrite(convertIndexToOffset(mapIndex));
VM.sysWrite(".\n");
}
return (mapIndex == 0) ? Address.zero() : framePtr.plus(convertIndexToOffset(mapIndex));
}
/**
* Cleanup pointers - used with method maps to release data structures early
* ... they may be in temporary storage ie storage only used during garbage
* collection
*/
public void cleanupPointers() {
maps.cleanupPointers();
maps = null;
if (mapId < 0) {
ReferenceMaps.jsrLock.unlock();
}
bridgeTarget = null;
bridgeParameterTypes = null;
}
public int getType() {
return CompiledMethod.BASELINE;
}
/**
* For debugging (used with checkRefMap)
*/
public int getStackDepth() {
return maps.getStackDepth(mapId);
}
}