/* * 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.opt.runtimesupport; import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS; import org.jikesrvm.VM; import org.jikesrvm.architecture.ArchConstants; import org.jikesrvm.compilers.common.CompiledMethod; import org.jikesrvm.compilers.common.CompiledMethods; import org.jikesrvm.mm.mminterface.GCMapIterator; import org.jikesrvm.mm.mminterface.MemoryManager; import org.jikesrvm.runtime.Magic; import org.vmmagic.pragma.Uninterruptible; import org.vmmagic.unboxed.Address; import org.vmmagic.unboxed.Offset; import org.vmmagic.unboxed.AddressArray; import static org.jikesrvm.compilers.opt.runtimesupport.OptGCMap.FIRST_GCMAP_REG; import static org.jikesrvm.compilers.opt.runtimesupport.OptGCMap.LAST_GCMAP_REG; /** * This class contains its architecture-independent code for iteration * across the references represented by a frame built by the OPT compiler.<p> * * The architecture-dependent code is provided by the subclasses of this class. */ @Uninterruptible public abstract class OptGenericGCMapIterator extends GCMapIterator { /** * The compiled method */ protected OptCompiledMethod compiledMethod; /** * The GC map for this method */ private 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 Address 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 OptGenericGCMapIterator(AddressArray registerLocations) { super(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 */ @Override public final void setupIterator(CompiledMethod cm, Offset instructionOffset, Address framePtr) { if (DEBUG) { VM.sysWriteln(); VM.sysWrite("\t =========================="); VM.sysWriteln(); VM.sysWrite("Reference map request made"); VM.sysWrite(" for machine code offset: "); VM.sysWrite(instructionOffset); VM.sysWriteln(); VM.sysWrite("\tframePtr: "); VM.sysWrite(framePtr); VM.sysWriteln(); } reset(); // retrieve and save the corresponding OptMachineCodeMap for // this method and instructionOffset compiledMethod = (OptCompiledMethod) cm; map = compiledMethod.getMCMap(); mapIndex = map.findGCMapIndex(instructionOffset); if (mapIndex == OptGCMap.ERROR) { if (instructionOffset.sLT(Offset.zero())) { VM.sysWriteln("OptGenericGCMapIterator.setupIterator called with negative instructionOffset", instructionOffset); } else { Offset possibleLen = Offset.fromIntZeroExtend(cm.numberOfInstructions() << ArchConstants.getLogInstructionWidth()); if (possibleLen.sLT(instructionOffset)) { VM.sysWriteln("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( "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("OptGenericMapIterator: findGCMapIndex failed\n"); } } VM.sysWrite("Supposed method: "); VM.sysWrite(compiledMethod.getMethod()); VM.sysWriteln(); VM.sysWriteln("Base of its code array", Magic.objectAsAddress(cm.getEntryCodeArray())); Address ra = cm.getInstructionAddress(instructionOffset); VM.sysWriteln("Calculated actual return address is ", ra); CompiledMethod realCM = 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("OptGenericMapIterator: setupIterator failed\n"); } // save the frame pointer this.framePtr = framePtr; if (DEBUG) { VM.sysWrite("\tMethod: "); VM.sysWrite(compiledMethod.getMethod()); VM.sysWriteln(); if (mapIndex == OptGCMap.NO_MAP_ENTRY) { VM.sysWriteln("... empty map found"); } else { VM.sysWriteln("... found a map"); } if (lookForMissedReferencesInSpills) { VM.sysWrite("FramePtr: "); VM.sysWrite(framePtr); VM.sysWrite("\tFirst Spill: "); VM.sysWrite(getFirstSpillLoc()); VM.sysWrite("\tLast Spill: "); VM.sysWrite(getLastSpillLoc()); VM.sysWriteln(); } } } /** * Returns the next address that contains a reference * @return the value of the next reference */ @Override public final Address getNextReferenceAddress() { if (DEBUG) { VM.sysWrite(" next => "); } // make sure we have a map entry to look at if (mapIndex == OptGCMap.NO_MAP_ENTRY) { if (DEBUG) { VM.sysWriteln(" No Map, returning 0"); } if (lookForMissedReferencesInRegs) { checkAllRegistersForMissedReferences(); } // make sure we update the registerLocations before returning! updateLocateRegisters(); return Address.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()) { Address regLocation; // currentRegister contains a reference, return that location regLocation = registerLocations.get(getCurrentRegister()); 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.sysWriteln(); } // 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 == OptGCMap.NO_MAP_ENTRY) { if (DEBUG) { VM.sysWriteln(" No more to return, returning 0"); } 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(Address.zero(), Address.zero()); } else { // check for spills after the last one we saw checkForMissedSpills(spillLoc, Address.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 Address.zero(); } else { // Determine the spill location given the frame ptr and spill offset. // (The location of spills varies among architectures.) Address 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.sysWriteln(); } 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 */ @Override public final Address 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.sysWriteln("\t\t getNextReturnAddressOffset returning 0"); } return Address.zero(); } /** * scan of this frame is complete * clean up any pointers to allow GC to reclaim dead objects */ @Override public final void cleanupPointers() { // primitive types aren't worth reinitializing because setUpIterator // will take care of this. map = null; compiledMethod = null; } @Override public final int getType() { return CompiledMethod.OPT; } /** * Externally visible method called to reset internal state */ @Override public final void reset() { currentRegister = FIRST_GCMAP_REG; spillLoc = Address.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 Address getStackLocation(Address framePtr, int offset); /** * Get address of the first spill location * (The location of spills varies among architectures.) * @return the first spill location */ public abstract Address getFirstSpillLoc(); /** * Get address of the last spill location * (The location of spills varies among architectures.) * @return the last spill location */ public abstract Address 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.sysWriteln(); } 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.sysWriteln(); } 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); Address regValue = regLocation.loadAddress(); if (MemoryManager.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 beginning 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(Address ref1, Address ref2) { if (ref1.isZero()) { // Search from start of spill area ref1 = getFirstSpillLoc(); if (DEBUG) { VM.sysWrite("Updated, ref1: "); VM.sysWrite(ref1); VM.sysWriteln(); } } if (ref2.isZero()) { // Search up to end of spill area ref2 = getLastSpillLoc(); if (DEBUG) { VM.sysWrite("Updated, ref2: "); VM.sysWrite(ref2); VM.sysWriteln(); } } // since different archs will have the relative order of ref1, ref2 // differently, we normalize them by ensuring that ref1 < ref2; if (ref1.GT(ref2)) { Address tmp = ref1; ref1 = ref2; ref2 = tmp; } for (Address i = ref1.plus(BYTES_IN_ADDRESS); i.LT(ref2); i = i.plus(BYTES_IN_ADDRESS)) { Address ptr = i.loadAddress(); if (DEBUG) { VM.sysWrite(" Inspecting Spill: "); VM.sysWrite(i); VM.sysWrite(" with value ==>"); VM.sysWrite(ptr); VM.sysWriteln(); } if (MemoryManager.addressInVM(ptr)) { VM.sysWrite(" spill location:"); VM.sysWrite(i); VM.sysWrite(" contains a suspicious value ==>"); VM.sysWrite(ptr); VM.sysWriteln(); VM.sysWrite("FramePtr: "); VM.sysWrite(framePtr); VM.sysWrite("\tFirst Spill: "); VM.sysWrite(getFirstSpillLoc()); VM.sysWrite("\tLast Spill: "); VM.sysWrite(getLastSpillLoc()); VM.sysWriteln(); } } } }