/*
* 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.opt;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.ArchitectureSpecific.VM_OptGCMapIteratorConstants;
import org.jikesrvm.VM;
import org.jikesrvm.VM_Constants;
import org.jikesrvm.compilers.common.VM_CompiledMethod;
import org.jikesrvm.compilers.common.VM_CompiledMethods;
import org.jikesrvm.memorymanagers.mminterface.MM_Interface;
import org.jikesrvm.memorymanagers.mminterface.VM_GCMapIterator;
import org.jikesrvm.runtime.VM_Magic;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.LocalAddress;
import org.vmmagic.unboxed.Offset;
import org.vmmagic.unboxed.WordArray;
/**
* This class contains its architecture-independent code for iteration
* across the references represented by a frame built by the OPT compiler.
*
* @see org.jikesrvm.ArchitectureSpecific.VM_OptGCMapIterator
*/
@Uninterruptible
public abstract class VM_OptGenericGCMapIterator extends VM_GCMapIterator
implements VM_OptGCMapIteratorConstants, VM_Constants {
/**
* The compiled method
*/
protected VM_OptCompiledMethod compiledMethod;
/**
* The GC map for this method
*/
private VM_OptMachineCodeMap map;
/**
* Used to index into the GC map
*/
private int mapIndex;
/**
* This shows which register to inspect and report on.
* If it is bigger than LAST_GCMAP_REG than we should look at the spills
*/
private int currentRegister;
/**
* This caches the spill location, so that we can check for missed refs
* hiding in spills
*/
private LocalAddress spillLoc;
/**
* just used for debugging, all output statements use VM.syswrite
*/
private static final boolean DEBUG = false;
/**
* just used for verbose debugging, all output statements use VM.syswrite
*/
static final boolean VERBOSE = false;
/**
* when set to true, all registers and spills will be inspected for
* values that look like references.
*
* THIS CAN BE COSTLY. USE WITH CARE
*/
static final boolean lookForMissedReferencesInRegs = false;
static final boolean lookForMissedReferencesInSpills = false;
// Constructor
protected VM_OptGenericGCMapIterator(WordArray registerLocations) {
super();
this.registerLocations = registerLocations;
}
/**
* Initialize the iterator for another stack frame scan
* @param cm The compiled method we are interested in
* @param instructionOffset The place in the method where we currently are
* @param framePtr The current frame pointer
*/
public final void setupIterator(VM_CompiledMethod cm, Offset instructionOffset, LocalAddress framePtr) {
if (DEBUG) {
VM.sysWrite("\n\t ==========================\n");
VM.sysWrite("Reference map request made");
VM.sysWrite(" for machine code offset: ");
VM.sysWrite(instructionOffset);
VM.sysWrite("\n");
VM.sysWrite("\tframePtr: ");
VM.sysWrite(framePtr);
VM.sysWrite("\n");
}
reset();
// retrieve and save the corresponding VM_OptMachineCodeMap for
// this method and instructionOffset
compiledMethod = (VM_OptCompiledMethod) cm;
map = compiledMethod.getMCMap();
mapIndex = map.findGCMapIndex(instructionOffset);
if (mapIndex == VM_OptGCMap.ERROR) {
if (instructionOffset.sLT(Offset.zero())) {
VM.sysWriteln("VM_OptGenericGCMapIterator.setupIterator called with negative instructionOffset",
instructionOffset);
} else {
Offset possibleLen =
Offset.fromIntZeroExtend(cm.numberOfInstructions() << ArchitectureSpecific.VM_RegisterConstants
.LG_INSTRUCTION_WIDTH);
if (possibleLen.sLT(instructionOffset)) {
VM.sysWriteln("VM_OptGenericGCMapIterator.setupIterator called with too big of an instructionOffset");
VM.sysWriteln("offset is", instructionOffset);
VM.sysWriteln(" bytes of machine code for method ", possibleLen);
} else {
VM.sysWriteln(
"VM_OptGenericGCMapIterator.setupIterator called with apparently valid offset, but no GC map found!");
VM.sysWrite("Method: ");
VM.sysWrite(compiledMethod.getMethod());
VM.sysWrite(", Machine Code (MC) Offset: ");
VM.sysWriteln(instructionOffset);
VM.sysFail("VM_OptGenericMapIterator: findGCMapIndex failed\n");
}
}
VM.sysWrite("Supposed method: ");
VM.sysWrite(compiledMethod.getMethod());
VM.sysWriteln("\nBase of its code array", VM_Magic.objectAsAddress(cm.getEntryCodeArray()));
LocalAddress ra = cm.getInstructionAddress(instructionOffset);
VM.sysWriteln("Calculated actual return address is ", ra);
VM_CompiledMethod realCM = VM_CompiledMethods.findMethodForInstruction(ra);
if (realCM == null) {
VM.sysWriteln("Unable to find compiled method corresponding to this return address");
} else {
VM.sysWrite("Found compiled method ");
VM.sysWrite(realCM.getMethod());
VM.sysWriteln(" whose code contains this return address");
}
VM.sysFail("VM_OptGenericMapIterator: setupIterator failed\n");
}
// save the frame pointer
this.framePtr = framePtr;
if (DEBUG) {
VM.sysWrite("\tMethod: ");
VM.sysWrite(compiledMethod.getMethod());
VM.sysWrite("\n ");
if (mapIndex == VM_OptGCMap.NO_MAP_ENTRY) {
VM.sysWrite("... empty map found\n");
} else {
VM.sysWrite("... found a map\n");
}
if (lookForMissedReferencesInSpills) {
VM.sysWrite("FramePtr: ");
VM.sysWrite(framePtr);
VM.sysWrite("\tFirst Spill: ");
VM.sysWrite(getFirstSpillLoc());
VM.sysWrite("\tLast Spill: ");
VM.sysWrite(getLastSpillLoc());
VM.sysWrite("\n");
}
}
}
/**
* Returns the next address that contains a reference
* @return the value of the next reference
*/
public final LocalAddress getNextReferenceAddress() {
if (DEBUG) { VM.sysWrite(" next => "); }
// make sure we have a map entry to look at
if (mapIndex == VM_OptGCMap.NO_MAP_ENTRY) {
if (DEBUG) {
VM.sysWrite(" No Map, returning 0\n");
}
if (lookForMissedReferencesInRegs) {
checkAllRegistersForMissedReferences();
}
// make sure we update the registerLocations before returning!
updateLocateRegisters();
return LocalAddress.zero();
}
// Have we gone through all the registers yet?
if (currentRegisterIsValid()) {
// See if there are any more
while (currentRegisterIsValid() && !map.registerIsSet(mapIndex, getCurrentRegister())) {
if (lookForMissedReferencesInRegs) {
// inspect the register we are skipping
checkCurrentRegisterForMissedReferences();
}
updateCurrentRegister();
}
// If we found a register, return the value
if (currentRegisterIsValid()) {
LocalAddress regLocation;
// currentRegister contains a reference, return that location
regLocation = registerLocations.get(getCurrentRegister()).toLocalAddress();
if (DEBUG) {
VM.sysWrite(" *** Ref found in reg#");
VM.sysWrite(getCurrentRegister());
VM.sysWrite(", location ==>");
VM.sysWrite(regLocation);
VM.sysWrite(", contents ==>");
VM.sysWrite(regLocation.loadWord());
VM.sysWrite("\n");
}
// update for the next call to this routine
updateCurrentRegister();
return regLocation;
}
}
// we already processes the registers, check to see if there are any
// references in spill locations.
// To do this we request the nextLocation from the ref map.
// If it returns a non-sentinel value we have a reference is a spill.
mapIndex = map.nextLocation(mapIndex);
if (mapIndex == VM_OptGCMap.NO_MAP_ENTRY) {
if (DEBUG) {
VM.sysWrite(" No more to return, returning 0\n");
}
if (lookForMissedReferencesInSpills) {
if (spillLoc.isZero()) {
// Didn't have any refs in spill locations, so we should
// check for spills among the whole spill area
checkForMissedSpills(LocalAddress.zero(), LocalAddress.zero());
} else {
// check for spills after the last one we saw
checkForMissedSpills(spillLoc, LocalAddress.zero());
}
}
// OK, we are done returning references for this GC point/method/FP
// so now we must update the LocateRegister array for the next
// stack frame
updateLocateRegisters();
return LocalAddress.zero();
} else {
// Determine the spill location given the frame ptr and spill offset.
// (The location of spills varies among architectures.)
LocalAddress newSpillLoc = getStackLocation(framePtr, map.gcMapInformation(mapIndex));
if (DEBUG) {
VM.sysWrite(" *** Ref found in Spill Loc: ");
VM.sysWrite(newSpillLoc);
VM.sysWrite(", offset: ");
VM.sysWrite(map.gcMapInformation(mapIndex));
VM.sysWrite(", value ==>");
VM.sysWrite(newSpillLoc.loadWord());
VM.sysWrite("\n");
}
if (lookForMissedReferencesInSpills) {
checkForMissedSpills(spillLoc, newSpillLoc);
}
spillLoc = newSpillLoc;
// found another ref, return it
return spillLoc;
}
}
/**
* This method is called repeatedly to process derived pointers related
* to JSRs. (They are pointers to code and need to be updated if the
* code moves.)
* @return the next code pointer or 0 if no more exist
*/
public final LocalAddress getNextReturnAddressAddress() {
// Since the Opt compiler inlines JSRs, this method will always return 0
// signaling the end of the list of such pointers.
if (DEBUG) {
VM.sysWrite("\t\t getNextReturnAddressOffset returning 0\n");
}
return LocalAddress.zero();
}
/**
* scan of this frame is complete
* clean up any pointers to allow GC to reclaim dead objects
*/
public final void cleanupPointers() {
// primitive types aren't worth reinitialing because setUpIterator
// will take care of this.
map = null;
compiledMethod = null;
}
/**
* lets GC ask what type of iterator without using instanceof which can
* cause an allocation
*/
public final int getType() {
return VM_CompiledMethod.OPT;
}
/**
* Externally visible method called to reset internal state
*/
public final void reset() {
currentRegister = FIRST_GCMAP_REG;
spillLoc = LocalAddress.zero();
}
/**
* return the current register we are processing
* @return the current register we are processing
*/
public final int getCurrentRegister() {
return currentRegister;
}
/**
* update the state of the current register we are processing
*/
public final void updateCurrentRegister() {
currentRegister++;
}
/**
* Determines if the value of "currentRegister" is valid, or if we
* processed all registers
* @return whether the currentRegister is valid
*/
public final boolean currentRegisterIsValid() {
return currentRegister <= LAST_GCMAP_REG;
}
/**
* If any non-volatile gprs were saved by the method being processed
* then update the registerLocations array with the locations where the
* registers were saved.
*/
protected abstract void updateLocateRegisters();
/**
* Determine the stack location given the frame ptr and spill offset.
* (The offset direction varies among architectures.)
* @param framePtr the frame pointer
* @param offset the offset
* @return the resulting stack location
*/
public abstract LocalAddress getStackLocation(LocalAddress framePtr, int offset);
/**
* Get address of the first spill location
* (The location of spills varies among architectures.)
* @return the first spill location
*/
public abstract LocalAddress getFirstSpillLoc();
/**
* Get address of the last spill location
* (The location of spills varies among architectures.)
* @return the last spill location
*/
public abstract LocalAddress getLastSpillLoc();
/**
* This method inspects the "current" register for values that look like refs.
*/
final void checkCurrentRegisterForMissedReferences() {
int currentReg = getCurrentRegister();
if (VERBOSE) {
VM.sysWrite(" Inspecting Regs: ");
VM.sysWrite(currentReg);
VM.sysWrite("\n");
}
checkRegistersForMissedReferences(currentReg, currentReg);
}
/**
* This method inspects all the registers for values that look like refs.
*/
final void checkAllRegistersForMissedReferences() {
if (VERBOSE) {
VM.sysWrite(" Inspecting Regs: ");
VM.sysWrite(FIRST_GCMAP_REG);
VM.sysWrite(" ... ");
VM.sysWrite(LAST_GCMAP_REG);
VM.sysWrite("\n");
}
checkRegistersForMissedReferences(FIRST_GCMAP_REG, LAST_GCMAP_REG);
}
/**
* This method inspects the registers from firstReg to lastReg (inclusive)
* for values that look like pointers.
* @param firstReg first reg to check
* @param lastReg last reg to check
*/
final void checkRegistersForMissedReferences(int firstReg, int lastReg) {
for (int i = firstReg; i <= lastReg; i++) {
Address regLocation = registerLocations.get(i).toAddress();
Address regValue = regLocation.loadAddress();
if (MM_Interface.addressInVM(regValue)) {
VM.sysWrite(" reg#", getCurrentRegister());
VM.sysWrite(", location ==>", regLocation);
VM.sysWriteln(", suspicious value ==>", regValue);
}
}
}
/**
* This method inspects spill locations between the parameters passed
* to determine if they look like heap points
* If the first parameter is 0, it looks from the begining of the frame
* until new.
* @param ref1 the last spill found as a reference
* @param ref2 the next spill found as a reference
*/
final void checkForMissedSpills(LocalAddress ref1, LocalAddress ref2) {
if (ref1.isZero()) {
// Search from start of spill area
ref1 = getFirstSpillLoc();
if (DEBUG) {
VM.sysWrite("Updated, ref1: ");
VM.sysWrite(ref1);
VM.sysWrite("\n");
}
}
if (ref2.isZero()) {
// Search up to end of spill area
ref2 = getLastSpillLoc();
if (DEBUG) {
VM.sysWrite("Updated, ref2: ");
VM.sysWrite(ref2);
VM.sysWrite("\n");
}
}
// since different archs will have the relative order of ref1, ref2
// differently, we normalize them by ensuring that ref1 < ref2;
if (ref1.GT(ref2)) {
LocalAddress tmp = ref1;
ref1 = ref2;
ref2 = tmp;
}
for (LocalAddress i = ref1.plus(BYTES_IN_ADDRESS); i.LT(ref2); i = i.plus(BYTES_IN_ADDRESS)) {
LocalAddress ptr = i.loadLocalAddress();
if (DEBUG) {
VM.sysWrite(" Inspecting Spill: ");
VM.sysWrite(i);
VM.sysWrite(" with value ==>");
VM.sysWrite(ptr);
VM.sysWrite("\n");
}
if (MM_Interface.addressInVM(VM_Magic.localAddressAsAddress(ptr))) {
VM.sysWrite(" spill location:");
VM.sysWrite(i);
VM.sysWrite(" contains a suspicious value ==>");
VM.sysWrite(ptr);
VM.sysWrite("\n");
VM.sysWrite("FramePtr: ");
VM.sysWrite(framePtr);
VM.sysWrite("\tFirst Spill: ");
VM.sysWrite(getFirstSpillLoc());
VM.sysWrite("\tLast Spill: ");
VM.sysWrite(getLastSpillLoc());
VM.sysWrite("\n");
}
}
}
}