/* * 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.regalloc; import static org.jikesrvm.VM.NOT_REACHED; import static org.jikesrvm.compilers.opt.ir.Operators.BBEND; import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE; import static org.jikesrvm.compilers.opt.ir.Operators.IR_PROLOGUE_opcode; import static org.jikesrvm.compilers.opt.ir.Operators.LOWTABLESWITCH; import static org.jikesrvm.compilers.opt.ir.Operators.YIELDPOINT_OSR; import static org.jikesrvm.runtime.UnboxedSizeConstants.BYTES_IN_ADDRESS; import java.util.ArrayList; import java.util.Enumeration; import java.util.Iterator; import org.jikesrvm.VM; import org.jikesrvm.architecture.ArchConstants; import org.jikesrvm.architecture.StackFrameLayout; import org.jikesrvm.compilers.opt.OptimizingCompilerException; import org.jikesrvm.compilers.opt.ir.BasicBlock; import org.jikesrvm.compilers.opt.ir.GenericPhysicalRegisterSet; import org.jikesrvm.compilers.opt.ir.IR; import org.jikesrvm.compilers.opt.ir.IRTools; import org.jikesrvm.compilers.opt.ir.Instruction; import org.jikesrvm.compilers.opt.ir.Register; import org.jikesrvm.compilers.opt.ir.operand.Operand; import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand; /** * Class to manage the allocation of the "compiler-independent" portion of * the stackframe. */ public abstract class GenericStackManager extends IRTools { protected static final boolean DEBUG = false; protected static final boolean VERBOSE = false; protected static final boolean VERBOSE_DEBUG = false; /** * Size of a word, in bytes */ protected static final int WORDSIZE = BYTES_IN_ADDRESS; protected IR ir; protected RegisterAllocatorState regAllocState; protected int frameSize; protected boolean allocFrame; /** * Object holding register preferences */ protected final GenericRegisterPreferences pref; { if (VM.BuildForIA32) { pref = new org.jikesrvm.compilers.opt.regalloc.ia32.RegisterPreferences(); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); pref = new org.jikesrvm.compilers.opt.regalloc.ppc.RegisterPreferences(); } } GenericRegisterPreferences getPreferences() { return pref; } /** * Object holding register restrictions */ protected GenericRegisterRestrictions restrict; GenericRegisterRestrictions getRestrictions() { return restrict; } /** * Spill pointer (in bytes) relative to the beginning of the * stack frame (starts after the header). */ protected int spillPointer = StackFrameLayout.getStackFrameHeaderSize(); /** * Have we decided that a stack frame is required for this method? */ private boolean frameRequired; /** * Memory location (8 bytes) to be used for type conversions */ private int conversionOffset; /** * Memory location (4 bytes) to be used for caughtExceptions */ private int caughtExceptionOffset; /** * Is there a prologue yieldpoint in this method? */ private boolean prologueYieldpoint; /** * We will have to save and restore all non-volatile registers around * system calls, to protect ourselve from malicious native code that may * bash these registers. * * This field, when non-zero, holds the stack-frame offset reserved to * hold this data. */ private int sysCallOffset = 0; /** * For each physical register, holds a ScratchRegister which records * the current scratch assignment for the physical register. */ protected final ArrayList<ScratchRegister> scratchInUse = new ArrayList<ScratchRegister>(20); /** * An array which holds the spill location number used to stash nonvolatile * registers. */ protected final int[] nonVolatileGPRLocation = new int[ArchConstants.getNumberOfGPRs()]; protected final int[] nonVolatileFPRLocation = new int[ArchConstants.getNumberOfFPRs()]; /** * An array which holds the spill location number used to stash volatile * registers in the SaveVolatile protocol. */ protected final int[] saveVolatileGPRLocation = new int[ArchConstants.getNumberOfGPRs()]; protected final int[] saveVolatileFPRLocation = new int[ArchConstants.getNumberOfFPRs()]; /** * An object used to track adjustments to the GC maps induced by scratch * registers */ protected ScratchMap scratchMap; ScratchMap getScratchMap() { return scratchMap; } /** * Perform some architecture-specific initialization. * * @param ir the IR */ public abstract void initForArch(IR ir); /** * @param s the instruction to check * @return whether the instruction is a system call? */ public abstract boolean isSysCall(Instruction s); /** * Given symbolic register r in instruction s, do we need to ensure that * r is in a scratch register is s (as opposed to a memory operand) * * @param r the symbolic register * @param s the instruction that has an occurrence of the register * @return {@code true} if the symbolic register needs to be a scratch * register */ public abstract boolean needScratch(Register r, Instruction s); /** * Allocates a new spill location and grows the * frame size to reflect the new layout. * * @param type the type to spill * @return the spill location */ public abstract int allocateNewSpillLocation(int type); public abstract int getSpillSize(int type); /** * Cleans up some junk that's left in the IR after register allocation, * and adds epilogue code. */ public abstract void cleanUpAndInsertEpilogue(); /** * Returns the size of the fixed portion of the stack. * (in other words, the difference between the framepointer and * the stackpointer after the prologue of the method completes). * @return size in bytes of the fixed portion of the stackframe */ public abstract int getFrameFixedSize(); /** * Computes the number of stack words needed to hold nonvolatile * registers. * * Side effects: * <ul> * <li> updates the OptCompiler structure * <li> updates the <code>frameSize</code> field of this object * <li> updates the <code>frameRequired</code> field of this object * </ul> */ public abstract void computeNonVolatileArea(); /** * Inserts the prologue for a normal method. */ public abstract void insertNormalPrologue(); /** * Walk over the currently available scratch registers. * * <p>For any scratch register r which is def'ed by instruction s, * spill r before s and remove r from the pool of available scratch * registers. * * <p>For any scratch register r which is used by instruction s, * restore r before s and remove r from the pool of available scratch * registers. * * <p>For any scratch register r which has current contents symb, and * symb is spilled to location M, and s defs M: the old value of symb is * dead. Mark this. * * <p>Invalidate any scratch register assignments that are illegal in s. * * @param s the instruction to process */ public abstract void restoreScratchRegistersBefore(Instruction s); /** * In instruction s, replace all appearances of a symbolic register * operand with uses of the appropriate spill location, as cached by the * register allocator. * * @param s the instruction to mutate. * @param symb the symbolic register operand to replace */ public abstract void replaceOperandWithSpillLocation(Instruction s, RegisterOperand symb); /** * Should we use information from linear scan in choosing scratch * registers? */ private static boolean USE_LINEAR_SCAN = true; /** * We may rely on information from linear scan to choose scratch registers. * If so, the following holds a pointer to some information from linear * scan analysis. */ private ActiveSet activeSet = null; /** * Replaces all occurrences of register r1 in an instruction with register * r2. * * Also, for any register r3 that is spilled to the same location as * r1, replace r3 with r2. * * @param s instruction to process * @param r1 register to replace * @param r2 the replacement register */ private void replaceRegisterWithScratch(Instruction s, Register r1, Register r2) { int spill1 = regAllocState.getSpill(r1); for (Enumeration<Operand> e = s.getOperands(); e.hasMoreElements();) { Operand op = e.nextElement(); if (op != null) { if (op.isRegister()) { Register r3 = op.asRegister().getRegister(); if (r3 == r1) { op.asRegister().setRegister(r2); } else if (regAllocState.getSpill(r3) == spill1) { op.asRegister().setRegister(r2); } } } } } /** * We will have to save and restore all non-volatile registers around * system calls, to protect ourselves from malicious native code that may * bash these registers. Call this routine before register allocation * in order to allocate space on the stack frame to store these * registers. * * @param n the number of GPR registers to save and restore. * @return the offset into the stack where n*4 contiguous words are * reserved */ public int allocateSpaceForSysCall(int n) { int bytes = n * WORDSIZE; if (sysCallOffset == 0) { sysCallOffset = allocateOnStackFrame(bytes); } return sysCallOffset; } /** * We will have to save and restore all non-volatile registers around * system calls, to protect ourselves from malicious native code that may * bash these registers. Call this routine before register allocation * in order to get the stack-frame offset previously reserved for this * data. * * @return the offset into the stack where n*4 contiguous words are * reserved */ public int getOffsetForSysCall() { return sysCallOffset; } /** * Spills the contents of a scratch register to memory before * instruction s. * * @param scratch the scratch register to spill * @param s the instruction before which the spill needs to occur */ protected void unloadScratchRegisterBefore(Instruction s, ScratchRegister scratch) { // if the scratch register is not dirty, don't need to write anything, // since the stack holds the current value if (!scratch.isDirty()) return; // spill the contents of the scratch register Register scratchContents = scratch.getCurrentContents(); if (scratchContents != null) { int location = regAllocState.getSpill(scratchContents); insertSpillBefore(s, scratch.scratch, scratchContents, location); } } /** * Restores the contents of a scratch register before instruction s if * necessary. * * @param scratch the scratch register whose contents may need to be restored * @param s the instruction before which the restores needs to occur */ protected void reloadScratchRegisterBefore(Instruction s, ScratchRegister scratch) { if (scratch.hadToSpill()) { // Restore the live contents into the scratch register. int location = regAllocState.getSpill(scratch.scratch); insertUnspillBefore(s, scratch.scratch, scratch.scratch, location); } } /** * @param s the instruction whose reserved scratch registers are of interest * @return the scratch registers which are currently reserved * for use in the instruction (may be an empty list but will never be {@code null}) */ private ArrayList<Register> getReservedScratchRegisters(Instruction s) { ArrayList<Register> result = new ArrayList<Register>(3); for (ScratchRegister sr : scratchInUse) { if (sr.getCurrentContents() != null && appearsIn(sr.getCurrentContents(), s)) { result.add(sr.scratch); } } return result; } /** * If there is a scratch register available which currently holds the * value of symbolic register r, then return that scratch register.<p> * * Additionally, if there is a scratch register available which is * mapped to the same stack location as r, then return that scratch * register.<p> * * Else return {@code null}. * * @param r the symbolic register to hold * @param s the instruction for which we need r in a register * @return a register as described above or {@code null} */ private ScratchRegister getCurrentScratchRegister(Register r, Instruction s) { for (ScratchRegister sr : scratchInUse) { if (sr.getCurrentContents() == r) { return sr; } int location = regAllocState.getSpill(sr.getCurrentContents()); int location2 = regAllocState.getSpill(r); if (location == location2) { // OK. We're currently holding a different symbolic register r2 in // a scratch register, and r2 is mapped to the same spill location // as r. So, coopt the scratch register for r, instead. Register r2 = sr.getCurrentContents(); sr.setCurrentContents(r); scratchMap.endScratchInterval(sr.scratch, s); scratchMap.endSymbolicInterval(r2, s); scratchMap.beginScratchInterval(sr.scratch, s); scratchMap.beginSymbolicInterval(r, sr.scratch, s); return sr; } } return null; } /** * @param r the register to check * @return the scratch register for r if it's currently in use as a scratch register, * {@code null} otherwise */ private ScratchRegister getPhysicalScratchRegister(Register r) { for (ScratchRegister sr : scratchInUse) { if (sr.scratch == r) { return sr; } } return null; } /** * Walk over the currently available scratch registers.<p> * * For any register which is dirty, note this in the scratch map for * instruction s. * * @param s the instruction which needs an update in the scratch map */ private void markDirtyScratchRegisters(Instruction s) { for (ScratchRegister scratch : scratchInUse) { if (scratch.isDirty()) { scratchMap.markDirty(s, scratch.getCurrentContents()); } } } /** * Walk over the currently available scratch registers, and spill their * contents to memory before instruction s. Also restore the correct live * value for each scratch register. Normally, s should end a * basic block.<p> * * SPECIAL CASE: If s is a return instruction, only restore the scratch * registers that are used by s. The others are dead. * * @param s the instruction before which the scratch registers need to be * restored */ private void restoreAllScratchRegistersBefore(Instruction s) { for (Iterator<ScratchRegister> i = scratchInUse.iterator(); i.hasNext();) { ScratchRegister scratch = i.next(); // SPECIAL CASE: If s is a return instruction, only restore the // scratch // registers that are used by s. The others are dead. if (!s.isReturn() || usedIn(scratch.scratch, s)) { unloadScratchRegisterBefore(s, scratch); reloadScratchRegisterBefore(s, scratch); } // update the scratch maps, even if the scratch registers are now // dead. if (VERBOSE_DEBUG) { System.out.println("RALL: End scratch interval " + scratch.scratch + " " + s); } i.remove(); scratchMap.endScratchInterval(scratch.scratch, s); Register scratchContents = scratch.getCurrentContents(); if (scratchContents != null) { if (VERBOSE_DEBUG) { System.out.println("RALL: End symbolic interval " + scratchContents + " " + s); } scratchMap.endSymbolicInterval(scratchContents, s); } } } /** * @param r the register * @param s the instruction * @return {@code true} if the register is dead immediately before * the instruction */ public boolean isDeadBefore(Register r, Instruction s) { BasicInterval bi = activeSet.getBasicInterval(r, s); // If there is no basic interval containing s, then r is dead before // s. if (bi == null) { return true; } else { // If the basic interval begins at s, then r is dead before // s. return bi.getBegin() == regAllocState.getDFN(s); } } /** * Inserts code as needed so that after instruction s, the value of * a symbolic register will be held in a particular scratch physical * register. * * @param s the instruction after which the value will be held in scratch * @param symb the register whose value needs to be held in scratch * @param beCheap don't expend much effort optimizing scratch * assignments * @return the physical scratch register that holds the value * after instruction s */ private ScratchRegister holdInScratchAfter(Instruction s, Register symb, boolean beCheap) { // Get a scratch register. ScratchRegister sr = getScratchRegister(symb, s, beCheap); // make the scratch register available to hold the new // symbolic register Register current = sr.getCurrentContents(); if (current != null && current != symb) { int location = regAllocState.getSpill(current); int location2 = regAllocState.getSpill(symb); if (location != location2) { insertSpillBefore(s, sr.scratch, current, location); } } // Record the new contents of the scratch register sr.setCurrentContents(symb); return sr; } /** * @param symb the symbolic register that we want to assign * @param phys the scratch register * @param s the instruction where the assignment would take place * @return whether it's legal to assign the symbolic register to the scratch register * in the given instruction */ protected boolean isLegal(Register symb, Register phys, Instruction s) { // If the physical scratch register already appears in s, so we can't // use it as a scratch register for another value. if (appearsIn(phys, s)) return false; // Check register restrictions for symb. if (getRestrictions().isForbidden(symb, phys, s)) return false; // Further assure legality for all other symbolic registers in symb // which are mapped to the same spill location as symb. int location = regAllocState.getSpill(symb); for (Enumeration<Operand> e = s.getOperands(); e.hasMoreElements();) { Operand op = e.nextElement(); if (op.isRegister()) { Register r = op.asRegister().getRegister(); if (r.isSymbolic()) { if (location == regAllocState.getSpill(r)) { if (getRestrictions().isForbidden(r, phys, s)) { return false; } } } } } // Otherwise, all is kosher. return true; } /** * Gets a scratch register to hold symbolic register symb in instruction * s. * * @param symb the symbolic register to hold * @param s the instruction where the scratch register is needed * @param beCheap don't expend too much effort * @return a scratch register, never {@code null} */ private ScratchRegister getScratchRegister(Register symb, Instruction s, boolean beCheap) { ScratchRegister r = getCurrentScratchRegister(symb, s); if (r != null) { // symb is currently assigned to scratch register r if (isLegal(symb, r.scratch, s)) { if (r.getCurrentContents() != symb) { // we're reusing a scratch register based on the fact that symb // shares a spill location with r.currentContents. However, // update the mapping information. if (r.getCurrentContents() != null) { if (VERBOSE_DEBUG) { System.out.println("GSR: End symbolic interval " + r.getCurrentContents() + " " + s); } scratchMap.endSymbolicInterval(r.getCurrentContents(), s); } if (VERBOSE_DEBUG) { System.out.println("GSR: Begin symbolic interval " + symb + " " + r.scratch + " " + s); } scratchMap.beginSymbolicInterval(symb, r.scratch, s); } return r; } } // if we get here, either there is no current scratch assignment, or // the current assignment is illegal. Find a new scratch register. ScratchRegister result = null; if (beCheap || activeSet == null) { result = getFirstAvailableScratchRegister(symb, s); } else { result = getScratchRegisterUsingIntervals(symb, s); } // Record that we will touch the scratch register. result.scratch.touchRegister(); return result; } /** * Finds a register which can serve as a scratch * register for symbolic register r in instruction s. * * <p> Inserts spills if necessary to ensure that the returned scratch * register is free for use. * * @param r the symbolic register that needs a scratch * @param s the instruction where the scratch register is needed * @return a scratch register, never {@code null} */ private ScratchRegister getScratchRegisterUsingIntervals(Register r, Instruction s) { ArrayList<Register> reservedScratch = getReservedScratchRegisters(s); Register phys = null; if (r.isFloatingPoint()) { phys = getFirstDeadFPRNotUsedIn(r, s, reservedScratch); } else { phys = getFirstDeadGPRNotUsedIn(r, s, reservedScratch); } // if the version above failed, default to the dumber heuristics if (phys == null) { if (r.isFloatingPoint()) { phys = getFirstFPRNotUsedIn(r, s, reservedScratch); } else { phys = getFirstGPRNotUsedIn(r, s, reservedScratch); } } return createScratchBefore(regAllocState, s, phys, r); } /** * Finds the first available register which can serve as a scratch * register for symbolic register r in instruction s. * * <p> Inserts spills if necessary to ensure that the returned scratch * register is free for use. * * @param r the symbolic register that needs a scratch * @param s the instruction where the scratch register is needed * @return a scratch register, never {@code null} */ private ScratchRegister getFirstAvailableScratchRegister(Register r, Instruction s) { ArrayList<Register> reservedScratch = getReservedScratchRegisters(s); Register phys = null; if (r.isFloatingPoint()) { phys = getFirstFPRNotUsedIn(r, s, reservedScratch); } else { phys = getFirstGPRNotUsedIn(r, s, reservedScratch); } return createScratchBefore(regAllocState, s, phys, r); } /** * Assigns symbolic register symb to a physical register, and inserts code * before instruction s to load the register from the appropriate stack * location. * * @param s the instruction before which the register needs to be loaded * @param symb the symbolic register to be assigned to a scratch * @param beCheap don't expend to much effort to optimize scratch * assignments * @return the physical register used to hold the value when it is * loaded from the spill location */ private ScratchRegister moveToScratchBefore(Instruction s, Register symb, boolean beCheap) { ScratchRegister sr = getScratchRegister(symb, s, beCheap); Register scratchContents = sr.getCurrentContents(); if (scratchContents != symb) { if (scratchContents != null) { // the scratch register currently holds a different // symbolic register. // spill the contents of the scratch register to free it up. unloadScratchRegisterBefore(s, sr); } // Now load up the scratch register. // since symbReg must have been previously spilled, get the spill // location previous assigned to symbReg int location = regAllocState.getSpill(symb); insertUnspillBefore(s, sr.scratch, symb, location); // we have not yet written to sr, so mark it 'clean' sr.setDirty(false); } else { // In this case the scratch register already holds the desired // symbolic register. So: do nothing. } // Record the current contents of the scratch register sr.setCurrentContents(symb); return sr; } /** * Make physicals register r available to be used as a scratch register * before instruction s. In instruction s, r will hold the value of * register symb. * @param regAllocState TODO * @param s the instruction before which the scratch register will be created * @param r the physical register to be used as scratch * @param symb the symbolic register which needs a scratch register * * @return the scratch register that will hold the value */ private ScratchRegister createScratchBefore(RegisterAllocatorState regAllocState, Instruction s, Register r, Register symb) { int type = GenericPhysicalRegisterSet.getPhysicalRegisterType(r); int spillLocation = regAllocState.getSpill(r); if (spillLocation <= 0) { // no spillLocation yet assigned to the physical register. // allocate a new location and assign it for the physical register spillLocation = allocateNewSpillLocation(type); regAllocState.setSpill(r, spillLocation); } ScratchRegister sr = getPhysicalScratchRegister(r); if (sr == null) { sr = new ScratchRegister(r, null); scratchInUse.add(sr); // Since this is a new scratch register, spill the old contents of // r if necessary. if (activeSet == null) { insertSpillBefore(s, r, r, spillLocation); sr.setHadToSpill(true); } else { if (!isDeadBefore(r, s)) { insertSpillBefore(s, r, r, spillLocation); sr.setHadToSpill(true); } } } else { // update mapping information if (VERBOSE_DEBUG) { System.out.println("CSB: " + " End scratch interval " + sr.scratch + " " + s); } scratchMap.endScratchInterval(sr.scratch, s); Register scratchContents = sr.getCurrentContents(); if (scratchContents != null) { if (VERBOSE_DEBUG) { System.out.println("CSB: " + " End symbolic interval " + sr.getCurrentContents() + " " + s); } scratchMap.endSymbolicInterval(sr.getCurrentContents(), s); } } // update mapping information if (VERBOSE_DEBUG) { System.out.println("CSB: Begin scratch interval " + r + " " + s); } scratchMap.beginScratchInterval(r, s); if (VERBOSE_DEBUG) { System.out.println("CSB: Begin symbolic interval " + symb + " " + r + " " + s); } scratchMap.beginSymbolicInterval(symb, r, s); return sr; } private boolean usesSpillLocation(Register r, Instruction s) { int location = regAllocState.getSpill(r); return usesSpillLocation(location, s); } private Register spillLocationUse(Register r, Instruction s) { int location = regAllocState.getSpill(r); return spillLocationUse(location, s); } private boolean definesSpillLocation(Register r, Instruction s) { int location = regAllocState.getSpill(r); return definesSpillLocation(location, s); } private boolean definesSpillLocation(int loc, Instruction s) { for (Enumeration<Operand> e = s.getDefs(); e.hasMoreElements();) { Operand op = e.nextElement(); if (op != null && op.isRegister()) { Register r = op.asRegister().getRegister(); if (regAllocState.getSpill(r) == loc) { return true; } } } return false; } private boolean usesSpillLocation(int loc, Instruction s) { for (Enumeration<Operand> e = s.getUses(); e.hasMoreElements();) { Operand op = e.nextElement(); if (op != null && op.isRegister()) { Register r = op.asRegister().getRegister(); if (regAllocState.getSpill(r) == loc) { return true; } } } return false; } /** * Assuming instruction s uses the spill location loc, * return the symbolic register that embodies that use.<p> * * Note that at most one such register can be used, since at most one * live register can use a given spill location. * * @param s instruction to check * @param loc spill location * @return the symbolic register that belongs to the spill location */ private Register spillLocationUse(int loc, Instruction s) { for (Enumeration<Operand> e = s.getUses(); e.hasMoreElements();) { Operand op = e.nextElement(); if (op != null && op.isRegister()) { Register r = op.asRegister().getRegister(); if (regAllocState.getSpill(r) == loc) { return r; } } } OptimizingCompilerException.UNREACHABLE("NO Matching use"); return null; } /** * Returns a FPR that does not appear in instruction s, to be used as a * scratch register to hold register r. * Except, does NOT return any register that is a member of the reserved set. * <p> * @param r the register that needs a scratch register * @param s the instruction for which the scratch register is needed * @param reserved the registers that must not be used * @return a free FPR * @throws OptimizingCompilerException if no free FPR was found */ private Register getFirstFPRNotUsedIn(Register r, Instruction s, ArrayList<Register> reserved) { GenericPhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet(); // first try the volatiles for (Enumeration<Register> e = phys.enumerateVolatileFPRs(); e.hasMoreElements();) { Register p = e.nextElement(); if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p) && isLegal(r, p, s)) { return p; } } OptimizingCompilerException.TODO("Could not find a free FPR in spill situation"); return null; } /** * Return a FPR that does not appear in instruction s, and is dead * before instruction s, to hold symbolic register r. * Except, do NOT * return any register that is a member of the reserved set. * * @param r the register that needs a scratch register * @param s the instruction for which the scratch register is needed * @param reserved the registers that must not be used * @return {@code null} if no register found, a dead and unused FPR otherwise */ private Register getFirstDeadFPRNotUsedIn(Register r, Instruction s, ArrayList<Register> reserved) { GenericPhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet(); // first try the volatiles for (Enumeration<Register> e = phys.enumerateVolatileFPRs(); e.hasMoreElements();) { Register p = e.nextElement(); if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p)) { if (isDeadBefore(p, s) && isLegal(r, p, s)) return p; } } return null; } /** * Return a GPR that does not appear in instruction s, to hold symbolic * register r. * Except, do NOT * return any register that is a member of the reserved set. * @param r the register that needs a scratch register * @param s the instruction for which the scratch register is needed * @param reserved the registers that must not be used * @return a free GPR */ private Register getFirstGPRNotUsedIn(Register r, Instruction s, ArrayList<Register> reserved) { GenericPhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet(); // first try the volatiles for (Enumeration<Register> e = phys.enumerateVolatileGPRs(); e.hasMoreElements();) { Register p = e.nextElement(); if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p) && isLegal(r, p, s)) { return p; } } // next try the non-volatiles. We allocate the nonvolatiles backwards for (Enumeration<Register> e = phys.enumerateNonvolatileGPRsBackwards(); e.hasMoreElements();) { Register p = e.nextElement(); if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p) && isLegal(r, p, s)) { return p; } } OptimizingCompilerException.TODO("Could not find a free GPR in spill situation"); return null; } /** * Return a GPR that does not appear in instruction s, and is dead * before instruction s, to hold symbolic register r. * Except, do NOT * return any register that is a member of the reserved set. * @param r the register that needs a scratch register * @param s the instruction for which the scratch register is needed * @param reserved the registers that must not be used * @return {@code null} if no register found, a dead and unused GPR otherwise */ private Register getFirstDeadGPRNotUsedIn(Register r, Instruction s, ArrayList<Register> reserved) { GenericPhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet(); // first try the volatiles for (Enumeration<Register> e = phys.enumerateVolatileGPRs(); e.hasMoreElements();) { Register p = e.nextElement(); if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p)) { if (isDeadBefore(p, s) && isLegal(r, p, s)) return p; } } // next try the non-volatiles. We allocate the nonvolatiles backwards for (Enumeration<Register> e = phys.enumerateNonvolatileGPRsBackwards(); e.hasMoreElements();) { Register p = e.nextElement(); if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p)) { if (isDeadBefore(p, s) && isLegal(r, p, s)) return p; } } return null; } private boolean appearsIn(Register r, Instruction s) { for (Enumeration<Operand> e = s.getOperands(); e.hasMoreElements();) { Operand op = e.nextElement(); if (op != null && op.isRegister()) { if (op.asRegister().getRegister().number == r.number) { return true; } } } if (VM .BuildForIA32 && r.isFloatingPoint() && (s.operator().isFNInit() || s.operator().isFClear())) { return true; } // Assume that all volatile registers 'appear' in all call // instructions return s.isCall() && !s.operator().isCallSaveVolatile() && r.isVolatile(); } /** * @param s the instruction to check * @param instructionsBB the block that contains the instruction * @return whether the instruction is s a PEI (potentially excepting * instruction, i.e. it can throw an exception) with a reachable catch * block */ private boolean isPEIWithCatch(Instruction s, BasicBlock instructionsBB) { if (s.isPEI()) { // TODO: add a more efficient accessor on BasicBlock to // determine whether there's a catch block for a particular // instruction. if (instructionsBB.getApplicableExceptionalOut(s).hasMoreElements()) { return true; } } return false; } /** * @param n number of the non-volatile GPR * @return the offset from the frame pointer for the place to store the * nth nonvolatile GPR. */ protected int getNonvolatileGPROffset(int n) { return nonVolatileGPRLocation[n]; } /** * @param n number of the non-volatile FPR * @return the offset from the frame pointer for the place to store the * nth nonvolatile FPR. */ protected int getNonvolatileFPROffset(int n) { return nonVolatileFPRLocation[n]; } /** * PROLOGUE/EPILOGUE. Note: This must be done after register allocation! */ public final void insertPrologueAndEpilogue() { insertPrologue(); cleanUpAndInsertEpilogue(); } private void insertPrologue() { // compute the number of stack words needed to hold nonvolatile // registers computeNonVolatileArea(); if (frameIsRequired()) { insertNormalPrologue(); } else { Instruction inst = ir.firstInstructionInCodeOrder().nextInstructionInCodeOrder(); if (VM.VerifyAssertions) VM._assert(inst.getOpcode() == IR_PROLOGUE_opcode); inst.remove(); ir.MIRInfo.gcIRMap.delete(inst); } } /** * After register allocation, go back through the IR and insert * compensating code to deal with spills. */ public void insertSpillCode() { insertSpillCode(null); } /** * After register allocation, go back through the IR and insert * compensating code to deal with spills. * * @param set information from linear scan analysis (may be {@code null}) */ public void insertSpillCode(ActiveSet set) { if (USE_LINEAR_SCAN) { activeSet = set; } if (VERBOSE_DEBUG) { System.out.println("INSERT SPILL CODE:"); } // walk over each instruction in the IR for (Enumeration<BasicBlock> blocks = ir.getBasicBlocks(); blocks.hasMoreElements();) { BasicBlock bb = blocks.nextElement(); // If the following is true, don't expend effort trying to // optimize scratch assignements boolean beCheap = (ir.options.FREQ_FOCUS_EFFORT && bb.getInfrequent()); for (Enumeration<Instruction> e = bb.forwardInstrEnumerator(); e.hasMoreElements();) { Instruction s = e.nextElement(); if (VERBOSE_DEBUG) { System.out.println(s); } // If any scratch registers are currently in use, but use physical // registers that appear in s, then free the scratch register. restoreScratchRegistersBefore(s); // we must spill all scratch registers before leaving this basic block if (s.operator() == BBEND || isPEIWithCatch(s, bb) || s.isBranch() || s.isReturn()) { restoreAllScratchRegistersBefore(s); } // If s is a GC point, and scratch register r currently caches the // value of symbolic symb, and r is dirty: Then update the GC map to // account for the fact that symb's spill location does not // currently hold a valid reference. if (s.isGCPoint()) { // note that if we're being cheap, no scratch registers are // currently dirty, since we've restored them all. markDirtyScratchRegisters(s); } // Walk over each operand and insert the appropriate spill code. // for the operand. for (Enumeration<Operand> ops = s.getOperands(); ops.hasMoreElements();) { Operand op = ops.nextElement(); if (op != null && op.isRegister()) { Register r = op.asRegister().getRegister(); if (!r.isPhysical()) { // Is r currently assigned to a scratch register? // Note that if we're being cheap, the answer is always no (null) ScratchRegister scratch = getCurrentScratchRegister(r, s); if (VERBOSE_DEBUG) { System.out.println(r + " SCRATCH " + scratch); } if (scratch != null) { // r is currently assigned to a scratch register. Continue to // use the same scratch register. boolean defined = definedIn(r, s) || definesSpillLocation(r, s); if (defined) { scratch.setDirty(true); } replaceRegisterWithScratch(s, r, scratch.scratch); } else { // r is currently NOT assigned to a scratch register. // Do we need to create a new scratch register to hold r? // Note that we never need scratch floating point register // for FMOVs, since we already have a scratch stack location // reserved. // If we're being cheap, then always create a new scratch register. if (needScratch(r, s)) { // We must create a new scratch register. boolean used = usedIn(r, s) || usesSpillLocation(r, s); boolean defined = definedIn(r, s) || definesSpillLocation(r, s); if (used) { if (!usedIn(r, s)) { Register r2 = spillLocationUse(r, s); scratch = moveToScratchBefore(s, r2, beCheap); if (VERBOSE_DEBUG) { System.out.println("MOVED TO SCRATCH BEFORE " + r2 + " " + scratch); } } else { scratch = moveToScratchBefore(s, r, beCheap); if (VERBOSE_DEBUG) { System.out.println("MOVED TO SCRATCH BEFORE " + r + " " + scratch); } } } if (defined) { scratch = holdInScratchAfter(s, r, beCheap); scratch.setDirty(true); if (VERBOSE_DEBUG) { System.out.println("HELD IN SCRATCH AFTER" + r + " " + scratch); } } // replace the register in the target instruction. replaceRegisterWithScratch(s, r, scratch.scratch); } else { if (VM.BuildForIA32) { // No need to use a scratch register here. replaceOperandWithSpillLocation(s, op.asRegister()); } else { if (VM.VerifyAssertions) { if (s.operator() != YIELDPOINT_OSR) { VM._assert(NOT_REACHED); } } } } } } } } // deal with sys calls that may bash non-volatiles if (isSysCall(s)) { if (VM.BuildForIA32) { org.jikesrvm.compilers.opt.regalloc.ia32.CallingConvention.saveNonvolatilesAroundSysCall(s, ir); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); org.jikesrvm.compilers.opt.regalloc.ppc.CallingConvention.saveNonvolatilesAroundSysCall(s, ir); } } } } } /** * Insert a spill of a physical register before instruction s. * * @param s the instruction before which the spill should occur * @param r the register (should be physical) to spill * @param type the register that's contained in the physical register * @param location the spill location */ public abstract void insertSpillBefore(Instruction s, Register r, Register type, int location); /** * Insert a spill of a physical register after instruction s. * * @param s the instruction after which the spill should occur * @param r the register (should be physical) to spill * @param type the register that's contained in the physical register * @param location the spill location */ public final void insertSpillAfter(Instruction s, Register r, Register type, int location) { insertSpillBefore(s.nextInstructionInCodeOrder(), r, type, location); } /** * Insert a load of a physical register from a spill location before * instruction s. * * @param s the instruction before which the spill should occur * @param r the register (should be physical) to spill * @param type the register that's contained in the physical register * @param location the spill location */ public abstract void insertUnspillBefore(Instruction s, Register r, Register type, int location); /** * Insert a load of a physical register from a spill location before * instruction s. * * @param s the instruction before which the spill should occur * @param r the register (should be physical) to spill * @param type the register that's contained in the physical register * @param location the spill location */ public final void insertUnspillAfter(Instruction s, Register r, Register type, int location) { insertUnspillBefore(s.nextInstructionInCodeOrder(), r, type, location); } /** * @return {@code true} if and only if a stack frame * must be allocated for this method l */ protected boolean frameIsRequired() { return frameRequired; } /** * Records that we need a stack frame for this method. */ protected void setFrameRequired() { frameRequired = true; } /** * @return {@code true} if and only if this IR has a prologue yieldpoint */ protected boolean hasPrologueYieldpoint() { return prologueYieldpoint; } /** * Ensure that there's enough space for passing parameters. We need * {@code size - STACKFRAME_HEADER_SIZE} bytes. * * @param s space needed for parameters */ public void allocateParameterSpace(int s) { if (spillPointer < s) { spillPointer = s; frameRequired = true; } } /** * Allocates the specified number of bytes in the stackframe, * returning the offset to the start of the allocated space. * * @param size the number of bytes to allocate * @return offset to the start of the allocated space. */ public int allocateOnStackFrame(int size) { int free = spillPointer; spillPointer += size; frameRequired = true; return free; } /** * We encountered a magic (get/set framepointer) that is going to force * us to actually create the stack frame. */ public void forceFrameAllocation() { frameRequired = true; } /** * We encountered a float/int conversion that uses * the stack as temporary storage. * * @return offset to the start of the allocated space */ public int allocateSpaceForConversion() { if (conversionOffset == 0) { conversionOffset = allocateOnStackFrame(8); } return conversionOffset; } /** * We encountered a catch block that actually uses its caught * exception object; allocate a stack slot for the exception delivery * code to use to pass the exception object to us. * * @return offset to the start of the allocated space */ public int allocateSpaceForCaughtException() { if (caughtExceptionOffset == 0) { caughtExceptionOffset = allocateOnStackFrame(BYTES_IN_ADDRESS); } return caughtExceptionOffset; } /** * Called as part of the register allocator startup. * <ol> * <li>examine the IR to determine whether or not we need to * allocate a stack frame</li> * <li>given that decison, determine whether or not we need to have * prologue/epilogue yieldpoints. If we don't need them, remove them. * Set up register preferences.</li> * <li>initialization code for the old RegisterManager</li> * <li>save caughtExceptionOffset where the exception deliverer can find it</li> * <li>initialize the restrictions object</li> * </ol> * @param ir the IR */ public void prepare(IR ir) { // (1) if we haven't yet committed to a stack frame we // will look for operators that would require a stack frame // - LOWTABLESWITCH // - a GC Point, except for YieldPoints or IR_PROLOGUE boolean preventYieldPointRemoval = false; if (!frameRequired) { for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) { if (s.operator() == LOWTABLESWITCH) { // uses BL to get pc relative addressing. frameRequired = true; preventYieldPointRemoval = true; break; } else if (s.isGCPoint() && !s.isYieldPoint() && s.operator() != IR_PROLOGUE) { // frame required for GCpoints that are not yield points // or IR_PROLOGUE, which is the stack overflow check frameRequired = true; preventYieldPointRemoval = true; break; } } } // (2) // In non-adaptive configurations we can omit the yieldpoint if // the method contains exactly one basic block whose only successor // is the exit node. (The method may contain calls, but we believe that // in any program that isn't going to overflow its stack there must be // some invoked method that contains more than 1 basic block, and // we'll insert a yieldpoint in its prologue.) // In adaptive configurations the only methods we eliminate yieldpoints // from are those in which the yieldpoints are the only reason we would // have to allocate a stack frame for the method. Having more yieldpoints // gets us better sampling behavior. Thus, in the adaptive configuration // we only omit the yieldpoint in leaf methods with no PEIs that contain // exactly one basic block whose only successor is the exit node. // TODO: We may want to force yieldpoints in "large" PEI-free // single-block leaf methods (if any exist). // TODO: This is a kludge. Removing the yieldpoint removes // the adaptive system's ability to accurately sample program // behavior. Even if the method is absolutely trivial // eg boolean foo() { return false; }, we may still want to // sample it for the purposes of adaptive inlining. // On the other hand, the ability to do this inlining in some cases // may not be able to buy back having to create a stackframe // for all methods. // // Future feature: always insert a pseudo yield point that when taken will // create the stack frame on demand. BasicBlock firstBB = ir.cfg.entry(); boolean isSingleBlock = firstBB.hasZeroIn() && firstBB.hasOneOut() && firstBB.pointsOut(ir.cfg.exit()); boolean removeYieldpoints = isSingleBlock && !preventYieldPointRemoval; // In adaptive systems if we require a frame, we don't remove // any yield points if (VM.BuildForAdaptiveSystem && frameRequired) { removeYieldpoints = false; } if (removeYieldpoints) { for (Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) { if (s.isYieldPoint()) { Instruction save = s; // get previous instruction, so we can continue // after we remove this instruction s = s.prevInstructionInCodeOrder(); save.remove(); ir.MIRInfo.gcIRMap.delete(save); } } prologueYieldpoint = false; } else { prologueYieldpoint = ir.method.isInterruptible(); frameRequired = true; } // (3) initialization this.ir = ir; this.regAllocState = ir.MIRInfo.regAllocState; this.scratchMap = new ScratchMap(regAllocState); pref.initialize(ir); frameSize = spillPointer; initForArch(ir); // (4) save caughtExceptionOffset where the exception deliverer can find it ir.compiledMethod.setUnsignedExceptionOffset(caughtExceptionOffset); // (5) initialize the restrictions object if (VM.BuildForIA32) { restrict = new org.jikesrvm.compilers.opt.regalloc.ia32.RegisterRestrictions(ir.regpool.getPhysicalRegisterSet()); } else { if (VM.VerifyAssertions) VM._assert(VM.BuildForPowerPC); restrict = new org.jikesrvm.compilers.opt.regalloc.ppc.RegisterRestrictions(ir.regpool.getPhysicalRegisterSet()); } } /** * Sets up register restrictions. * * @param ir the IR which will get the restrictions */ public final void computeRestrictions(IR ir) { restrict.init(ir); } /** * Find an volatile register to allocate starting at the reg corresponding * to the symbolic register passed * @param symbReg the place to start the search * @return the allocated register or null */ public final Register allocateVolatileRegister(Register symbReg) { GenericPhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet(); int physType = GenericPhysicalRegisterSet.getPhysicalRegisterType(symbReg); for (Enumeration<Register> e = phys.enumerateVolatiles(physType); e.hasMoreElements();) { Register realReg = e.nextElement(); if (realReg.isAvailable()) { realReg.allocateToRegister(symbReg); if (DEBUG) VM.sysWriteln(" volat." + realReg + " to symb " + symbReg); return realReg; } } return null; } protected static int align(int number, int alignment) { alignment--; return (number + alignment) & ~alignment; } /** * Find a nonvolatile register to allocate starting at the reg corresponding * to the symbolic register passed. * <p> * TODO: Clean up this interface. * * @param symbReg the place to start the search * @return the allocated register or null */ public final Register allocateNonVolatileRegister(Register symbReg) { GenericPhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet(); int physType = GenericPhysicalRegisterSet.getPhysicalRegisterType(symbReg); for (Enumeration<Register> e = phys.enumerateNonvolatilesBackwards(physType); e.hasMoreElements();) { Register realReg = e.nextElement(); if (realReg.isAvailable()) { realReg.allocateToRegister(symbReg); return realReg; } } return null; } /** * Class to represent a physical register currently allocated as a * scratch register. A scratch register is a register that is reserved * for use in spills and unspills. It is not available as a normal register * for the register allocation. */ protected static final class ScratchRegister { /** * The physical register used as scratch. */ public final Register scratch; /** * The current contents of scratch */ private Register currentContents; /** * Is this physical register currently dirty? (Must be written back to * memory?) */ private boolean dirty = false; public boolean isDirty() { return dirty; } public void setDirty(boolean b) { dirty = b; } /** * Did we spill a value in order to free up this scratch register? */ private boolean spilledIt = false; public boolean hadToSpill() { return spilledIt; } public void setHadToSpill(boolean b) { spilledIt = b; } public ScratchRegister(Register scratch, Register currentContents) { this.scratch = scratch; this.currentContents = currentContents; } public Register getCurrentContents() { return currentContents; } public void setCurrentContents(Register currentContents) { this.currentContents = currentContents; } @Override public String toString() { String dirtyString = dirty ? "D" : "C"; return "SCRATCH<" + scratch + "," + getCurrentContents() + "," + dirtyString + ">"; } } }