/*
* 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 java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import org.jikesrvm.ArchitectureSpecific;
import org.jikesrvm.ArchitectureSpecific.OPT_CallingConvention;
import static org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterConstants.CONDITION_VALUE;
import static org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterConstants.DOUBLE_VALUE;
import static org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterConstants.FLOAT_VALUE;
import static org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterConstants.INT_VALUE;
import static org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterConstants.NUM_FPRS;
import static org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterConstants.NUM_GPRS;
import org.jikesrvm.ArchitectureSpecific.OPT_PhysicalRegisterSet;
import org.jikesrvm.ArchitectureSpecific.OPT_RegisterPreferences;
import org.jikesrvm.ArchitectureSpecific.OPT_RegisterRestrictions;
import org.jikesrvm.VM;
import static org.jikesrvm.VM_Constants.BYTES_IN_ADDRESS;
import static org.jikesrvm.VM_Constants.NOT_REACHED;
import org.jikesrvm.compilers.opt.ir.OPT_BasicBlock;
import org.jikesrvm.compilers.opt.ir.OPT_IR;
import org.jikesrvm.compilers.opt.ir.OPT_IRTools;
import org.jikesrvm.compilers.opt.ir.OPT_Instruction;
import org.jikesrvm.compilers.opt.ir.OPT_Operand;
import org.jikesrvm.compilers.opt.ir.OPT_Operators;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.BBEND;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.CALL_SAVE_VOLATILE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.IR_PROLOGUE;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.IR_PROLOGUE_opcode;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.LOWTABLESWITCH;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.YIELDPOINT_OSR;
import static org.jikesrvm.compilers.opt.ir.OPT_Operators.YIELDPOINT_PROLOGUE;
import org.jikesrvm.compilers.opt.ir.OPT_Register;
import org.jikesrvm.compilers.opt.ir.OPT_RegisterOperand;
/**
* Class to manage the allocation of the "compiler-independent" portion of
* the stackframe.
* <p>
*/
public abstract class OPT_GenericStackManager extends OPT_IRTools {
/**
* Size of a word, in bytes
*/
protected static final int WORDSIZE = BYTES_IN_ADDRESS;
/**
* 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[NUM_GPRS];
protected final int[] nonVolatileFPRLocation = new int[NUM_FPRS];
/**
* An array which holds the spill location number used to stash volatile
* registers in the SaveVolatile protocol.
*/
protected final int[] saveVolatileGPRLocation = new int[NUM_GPRS];
protected final int[] saveVolatileFPRLocation = new int[NUM_FPRS];
protected static final boolean debug = false;
protected static final boolean verbose = false;
protected static final boolean verboseDebug = false;
/**
* Perform some architecture-specific initialization.
*/
public abstract void initForArch(OPT_IR ir);
/**
* Is a particular instruction a system call?
*/
public abstract boolean isSysCall(OPT_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)
*/
public abstract boolean needScratch(OPT_Register r, OPT_Instruction s);
/**
* Allocate a new spill location and grow the
* frame size to reflect the new layout.
*
* @param type the type to spill
* @return the spill location
*/
public abstract int allocateNewSpillLocation(int type);
/**
* Clean up some junk that's left in the IR after register allocation,
* and add epilogue code.
*/
public abstract void cleanUpAndInsertEpilogue();
/**
* Return 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();
/**
* Compute the number of stack words needed to hold nonvolatile
* registers.
*
* Side effects:
* <ul>
* <li> updates the VM_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();
/**
* Insert 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.
*/
public abstract void restoreScratchRegistersBefore(OPT_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(OPT_Instruction s, OPT_RegisterOperand symb);
// Get the spill location previously assigned to the symbolic
/**
* 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 OPT_LinearScan.ActiveSet activeSet = null;
/**
* Replace all occurences 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.
*/
private void replaceRegisterWithScratch(OPT_Instruction s, OPT_Register r1, OPT_Register r2) {
int spill1 = OPT_RegisterAllocatorState.getSpill(r1);
for (Enumeration<OPT_Operand> e = s.getOperands(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null) {
if (op.isRegister()) {
OPT_Register r3 = op.asRegister().getRegister();
if (r3 == r1) {
op.asRegister().setRegister(r2);
} else if (OPT_RegisterAllocatorState.getSpill(r3) == spill1) {
op.asRegister().setRegister(r2);
}
}
}
}
}
/**
* 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. 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 ourselve 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;
}
/**
* Spill the contents of a scratch register to memory before
* instruction s.
*/
protected void unloadScratchRegisterBefore(OPT_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
OPT_Register scratchContents = scratch.currentContents;
if (scratchContents != null) {
int location = OPT_RegisterAllocatorState.getSpill(scratchContents);
insertSpillBefore(s, scratch.scratch, getValueType(scratchContents), location);
}
}
/**
* Restore the contents of a scratch register before instruction s.
*/
protected void reloadScratchRegisterBefore(OPT_Instruction s, ScratchRegister scratch) {
if (scratch.hadToSpill()) {
// Restore the live contents into the scratch register.
int location = OPT_RegisterAllocatorState.getSpill(scratch.scratch);
insertUnspillBefore(s, scratch.scratch, getValueType(scratch.scratch), location);
}
}
/**
* Return the set of scratch registers which are currently reserved
* for use in instruction s.
*/
private ArrayList<OPT_Register> getReservedScratchRegisters(OPT_Instruction s) {
ArrayList<OPT_Register> result = new ArrayList<OPT_Register>(3);
for (ScratchRegister sr : scratchInUse) {
if (sr.currentContents != null && appearsIn(sr.currentContents, 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.
*
* Additionally, if there is a scratch register available which is
* mapped to the same stack location as r, then return that scratch
* register.
*
* Else return null.
*
* @param r the symbolic register to hold
* @param s the instruction for which we need r in a register
*/
private ScratchRegister getCurrentScratchRegister(OPT_Register r, OPT_Instruction s) {
for (ScratchRegister sr : scratchInUse) {
if (sr.currentContents == r) {
return sr;
}
int location = OPT_RegisterAllocatorState.getSpill(sr.currentContents);
int location2 = OPT_RegisterAllocatorState.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.
OPT_Register r2 = sr.currentContents;
sr.currentContents = 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;
}
/**
* If register r is currently in use as a scratch register,
* then return that scratch register.
* Else return null.
*/
private ScratchRegister getPhysicalScratchRegister(OPT_Register r) {
for (ScratchRegister sr : scratchInUse) {
if (sr.scratch == r) {
return sr;
}
}
return null;
}
/**
* Walk over the currently available scratch registers.
*
* For any register which is dirty, note this in the scratch map for
* instruction s.
*/
private void markDirtyScratchRegisters(OPT_Instruction s) {
for (ScratchRegister scratch : scratchInUse) {
if (scratch.isDirty()) {
scratchMap.markDirty(s, scratch.currentContents);
}
}
}
/**
* 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.
*
* SPECIAL CASE: If s is a return instruction, only restore the scratch
* registers that are used by s. The others are dead.
*/
private void restoreAllScratchRegistersBefore(OPT_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 (verboseDebug) {
System.out.println("RALL: End scratch interval " + scratch.scratch + " " + s);
}
i.remove();
scratchMap.endScratchInterval(scratch.scratch, s);
OPT_Register scratchContents = scratch.currentContents;
if (scratchContents != null) {
if (verboseDebug) {
System.out.println("RALL: End symbolic interval " + scratchContents + " " + s);
}
scratchMap.endSymbolicInterval(scratchContents, s);
}
}
}
/**
* Is a particular register dead immediately before instruction s.
*/
public boolean isDeadBefore(OPT_Register r, OPT_Instruction s) {
OPT_LinearScan.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() == OPT_LinearScan.getDFN(s);
}
}
/**
* Insert code as needed so that after instruction s, the value of
* a symbolic register will be held in a particular scratch physical
* register.
*
* @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(OPT_Instruction s, OPT_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
OPT_Register current = sr.currentContents;
if (current != null && current != symb) {
int location = OPT_RegisterAllocatorState.getSpill(current);
int location2 = OPT_RegisterAllocatorState.getSpill(symb);
if (location != location2) {
insertSpillBefore(s, sr.scratch, getValueType(current), location);
}
}
// Record the new contents of the scratch register
sr.currentContents = symb;
return sr;
}
/**
* Is it legal to assign symbolic register symb to scratch register phys
* in instruction s?
*/
protected boolean isLegal(OPT_Register symb, OPT_Register phys, OPT_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 = OPT_RegisterAllocatorState.getSpill(symb);
for (Enumeration<OPT_Operand> e = s.getOperands(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op.isRegister()) {
OPT_Register r = op.asRegister().getRegister();
if (r.isSymbolic()) {
if (location == OPT_RegisterAllocatorState.getSpill(r)) {
if (getRestrictions().isForbidden(r, phys, s)) {
return false;
}
}
}
}
}
// Otherwise, all is kosher.
return true;
}
/**
* Get a scratch register to hold symbolic register symb in instruction
* s.
*
* @param beCheap don't expend too much effort
*/
private ScratchRegister getScratchRegister(OPT_Register symb, OPT_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.currentContents != 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.currentContents != null) {
if (verboseDebug) {
System.out.println("GSR: End symbolic interval " + r.currentContents + " " + s);
}
scratchMap.endSymbolicInterval(r.currentContents, s);
}
if (verboseDebug) {
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;
}
/**
* Find a register which can serve as a scratch
* register for symbolic register r in instruction s.
*
* <p> Insert spills if necessary to ensure that the returned scratch
* register is free for use.
*/
private ScratchRegister getScratchRegisterUsingIntervals(OPT_Register r, OPT_Instruction s) {
ArrayList<OPT_Register> reservedScratch = getReservedScratchRegisters(s);
OPT_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(s, phys, r);
}
/**
* Find the first available register which can serve as a scratch
* register for symbolic register r in instruction s.
*
* <p> Insert spills if necessary to ensure that the returned scratch
* register is free for use.
*/
private ScratchRegister getFirstAvailableScratchRegister(OPT_Register r, OPT_Instruction s) {
ArrayList<OPT_Register> reservedScratch = getReservedScratchRegisters(s);
OPT_Register phys = null;
if (r.isFloatingPoint()) {
phys = getFirstFPRNotUsedIn(r, s, reservedScratch);
} else {
phys = getFirstGPRNotUsedIn(r, s, reservedScratch);
}
return createScratchBefore(s, phys, r);
}
/**
* Assign symbolic register symb to a physical register, and insert code
* before instruction s to load the register from the appropriate stack
* location.
*
* @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(OPT_Instruction s, OPT_Register symb, boolean beCheap) {
ScratchRegister sr = getScratchRegister(symb, s, beCheap);
OPT_Register scratchContents = sr.currentContents;
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 = OPT_RegisterAllocatorState.getSpill(symb);
insertUnspillBefore(s, sr.scratch, getValueType(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.currentContents = symb;
return sr;
}
/**
* Make physical register r available to be used as a scratch register
* before instruction s. In instruction s, r will hold the value of
* register symb.
*/
private ScratchRegister createScratchBefore(OPT_Instruction s, OPT_Register r, OPT_Register symb) {
int type = OPT_PhysicalRegisterSet.getPhysicalRegisterType(r);
int spillLocation = OPT_RegisterAllocatorState.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);
OPT_RegisterAllocatorState.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, (byte) type, spillLocation);
sr.setHadToSpill(true);
} else {
if (!isDeadBefore(r, s)) {
insertSpillBefore(s, r, (byte) type, spillLocation);
sr.setHadToSpill(true);
}
}
} else {
// update mapping information
if (verboseDebug) {
System.out.println("CSB: " + " End scratch interval " + sr.scratch + " " + s);
}
scratchMap.endScratchInterval(sr.scratch, s);
OPT_Register scratchContents = sr.currentContents;
if (scratchContents != null) {
if (verboseDebug) {
System.out.println("CSB: " + " End symbolic interval " + sr.currentContents + " " + s);
}
scratchMap.endSymbolicInterval(sr.currentContents, s);
}
}
// update mapping information
if (verboseDebug) {
System.out.println("CSB: Begin scratch interval " + r + " " + s);
}
scratchMap.beginScratchInterval(r, s);
if (verboseDebug) {
System.out.println("CSB: Begin symbolic interval " + symb + " " + r + " " + s);
}
scratchMap.beginSymbolicInterval(symb, r, s);
return sr;
}
/**
* Does instruction s use the spill location for a given register?
*/
private boolean usesSpillLocation(OPT_Register r, OPT_Instruction s) {
int location = OPT_RegisterAllocatorState.getSpill(r);
return usesSpillLocation(location, s);
}
/**
* Assuming instruction s uses the spill location for a given register,
* return the symbolic register that embodies that use.
*/
private OPT_Register spillLocationUse(OPT_Register r, OPT_Instruction s) {
int location = OPT_RegisterAllocatorState.getSpill(r);
return spillLocationUse(location, s);
}
/**
* Does instruction s define the spill location for a given register?
*/
private boolean definesSpillLocation(OPT_Register r, OPT_Instruction s) {
int location = OPT_RegisterAllocatorState.getSpill(r);
return definesSpillLocation(location, s);
}
/**
* Does instruction s define spill location loc?
*/
private boolean definesSpillLocation(int loc, OPT_Instruction s) {
for (Enumeration<OPT_Operand> e = s.getDefs(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null && op.isRegister()) {
OPT_Register r = op.asRegister().getRegister();
if (OPT_RegisterAllocatorState.getSpill(r) == loc) {
return true;
}
}
}
return false;
}
/**
* Does instruction s use spill location loc?
*/
private boolean usesSpillLocation(int loc, OPT_Instruction s) {
for (Enumeration<OPT_Operand> e = s.getUses(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null && op.isRegister()) {
OPT_Register r = op.asRegister().getRegister();
if (OPT_RegisterAllocatorState.getSpill(r) == loc) {
return true;
}
}
}
return false;
}
/**
* Assuming instruction s uses the spill location loc,
* return the symbolic register that embodies that use.
* Note that at most one such register can be used, since at most one
* live register can use a given spill location.
*/
private OPT_Register spillLocationUse(int loc, OPT_Instruction s) {
for (Enumeration<OPT_Operand> e = s.getUses(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null && op.isRegister()) {
OPT_Register r = op.asRegister().getRegister();
if (OPT_RegisterAllocatorState.getSpill(r) == loc) {
return r;
}
}
}
OPT_OptimizingCompilerException.UNREACHABLE("NO Matching use");
return null;
}
/**
* Return a FPR that does not appear in instruction s, to be used as a
* scratch register to hold register r
* Except, do NOT return any register that is a member of the reserved set.
*
* Throw an exception if none found.
*/
private OPT_Register getFirstFPRNotUsedIn(OPT_Register r, OPT_Instruction s, ArrayList<OPT_Register> reserved) {
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
// first try the volatiles
for (Enumeration<OPT_Register> e = phys.enumerateVolatileFPRs(); e.hasMoreElements();) {
OPT_Register p = e.nextElement();
if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p) && isLegal(r, p, s)) {
return p;
}
}
OPT_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.
*
* Return null if none found
*/
private OPT_Register getFirstDeadFPRNotUsedIn(OPT_Register r, OPT_Instruction s, ArrayList<OPT_Register> reserved) {
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
// first try the volatiles
for (Enumeration<OPT_Register> e = phys.enumerateVolatileFPRs(); e.hasMoreElements();) {
OPT_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.
*
* Throw an exception if none found.
*/
private OPT_Register getFirstGPRNotUsedIn(OPT_Register r, OPT_Instruction s, ArrayList<OPT_Register> reserved) {
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
// first try the volatiles
for (Enumeration<OPT_Register> e = phys.enumerateVolatileGPRs(); e.hasMoreElements();) {
OPT_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<OPT_Register> e = phys.enumerateNonvolatileGPRsBackwards(); e.hasMoreElements();) {
OPT_Register p = e.nextElement();
if (!appearsIn(p, s) && !p.isPinned() && !reserved.contains(p) && isLegal(r, p, s)) {
return p;
}
}
OPT_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.
*
* return null if none found
*/
private OPT_Register getFirstDeadGPRNotUsedIn(OPT_Register r, OPT_Instruction s, ArrayList<OPT_Register> reserved) {
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
// first try the volatiles
for (Enumeration<OPT_Register> e = phys.enumerateVolatileGPRs(); e.hasMoreElements();) {
OPT_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<OPT_Register> e = phys.enumerateNonvolatileGPRsBackwards(); e.hasMoreElements();) {
OPT_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;
}
/**
* Does register r appear in instruction s?
*/
private boolean appearsIn(OPT_Register r, OPT_Instruction s) {
for (Enumeration<OPT_Operand> e = s.getOperands(); e.hasMoreElements();) {
OPT_Operand op = e.nextElement();
if (op != null && op.isRegister()) {
if (op.asRegister().getRegister().number == r.number) {
return true;
}
}
}
if (VM
.BuildForIA32 &&
r.isFloatingPoint() &&
(OPT_Operators.helper.isFNInit(s.operator) || OPT_Operators.helper.isFClear(s.operator))) {
return true;
}
// Assume that all volatile registers 'appear' in all call
// instructions
return s.isCall() && s.operator != CALL_SAVE_VOLATILE && r.isVolatile();
}
/**
* Is s a PEI with a reachable catch block?
*/
private boolean isPEIWithCatch(OPT_Instruction s) {
if (s.isPEI()) {
// TODO: optimize this away by passing the basic block in.
OPT_BasicBlock b = s.getBasicBlock();
// TODO: add a more efficient accessor on OPT_BasicBlock to
// determine whether there's a catch block for a particular
// instruction.
if (b.getApplicableExceptionalOut(s).hasMoreElements()) {
return true;
}
}
return false;
}
/**
* Return the offset from the frame pointer for the place to store the
* nth nonvolatile GPR.
*/
protected int getNonvolatileGPROffset(int n) {
return nonVolatileGPRLocation[n];
}
/**
* 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. must be done after register allocation
*/
public final void insertPrologueAndEpilogue() {
insertPrologue();
cleanUpAndInsertEpilogue();
}
/**
* Insert the prologue.
*/
private void insertPrologue() {
// compute the number of stack words needed to hold nonvolatile
// registers
computeNonVolatileArea();
if (frameIsRequired()) {
insertNormalPrologue();
} else {
OPT_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
*/
public void insertSpillCode(OPT_LinearScan.ActiveSet set) {
if (USE_LINEAR_SCAN) {
activeSet = set;
}
if (verboseDebug) {
System.out.println("INSERT SPILL CODE:");
}
// walk over each instruction in the IR
for (Enumeration<OPT_BasicBlock> blocks = ir.getBasicBlocks(); blocks.hasMoreElements();) {
OPT_BasicBlock bb = blocks.nextElement();
for (Enumeration<OPT_Instruction> e = bb.forwardInstrEnumerator(); e.hasMoreElements();) {
// If the following is true, don't expend effort trying to
// optimize scratch assignements
boolean beCheap = (ir.options.FREQ_FOCUS_EFFORT && bb.getInfrequent());
OPT_Instruction s = e.nextElement();
if (verboseDebug) {
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) || s.isBranch() || s.isReturn()) {
restoreAllScratchRegistersBefore(s);
}
// SJF: This is a bad hack which avoids bug 2642. For some reason,
// if we cache a reference value in ECX across the prologue_yieldpoint in
// method java.Hashtable.put(), bad things happen and the program
// non-deterministically crashes with apparent bad GC Maps.
// I have not figured out what's really going on, despite great
// effort. I'm giving up for now, and instead resorting to this
// woeful hack to avoid the problem.
// To reproduce the bug, comment out the following and run SPECjbb
// using the night-sanity parameters on OptOptSemispace; the program
// should crash with a bad GC map about half the time.
if (s.operator == YIELDPOINT_PROLOGUE) {
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<OPT_Operand> ops = s.getOperands(); ops.hasMoreElements();) {
OPT_Operand op = ops.nextElement();
if (op != null && op.isRegister()) {
OPT_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 (verboseDebug) {
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)) {
OPT_Register r2 = spillLocationUse(r, s);
scratch = moveToScratchBefore(s, r2, beCheap);
if (verboseDebug) {
System.out.println("MOVED TO SCRATCH BEFORE " + r2 + " " + scratch);
}
} else {
scratch = moveToScratchBefore(s, r, beCheap);
if (verboseDebug) {
System.out.println("MOVED TO SCRATCH BEFORE " + r + " " + scratch);
}
}
}
if (defined) {
scratch = holdInScratchAfter(s, r, beCheap);
scratch.setDirty(true);
if (verboseDebug) {
System.out.println("HELD IN SCRATCH AFTER" + r + " " + scratch);
}
}
// replace the register in the target instruction.
replaceRegisterWithScratch(s, r, scratch.scratch);
} else {
if (s.operator != YIELDPOINT_OSR) {
if (VM.BuildForIA32) {
// No need to use a scratch register here.
replaceOperandWithSpillLocation(s, op.asRegister());
} else {
if (VM.VerifyAssertions) VM._assert(NOT_REACHED);
}
}
}
}
}
}
}
// deal with sys calls that may bash non-volatiles
if (isSysCall(s)) {
OPT_CallingConvention.saveNonvolatilesAroundSysCall(s, ir);
}
}
}
}
/**
* An object used to track adjustments to the GC maps induced by scrach
* registers
*/
protected OPT_ScratchMap scratchMap = new OPT_ScratchMap();
OPT_ScratchMap getScratchMap() { return scratchMap; }
/**
* 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 one of INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, or
* CONDITION_VALUE
* @param location the spill location
*/
public abstract void insertSpillBefore(OPT_Instruction s, OPT_Register r, byte 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 one of INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, or
* CONDITION_VALUE
* @param location the spill location
*/
public final void insertSpillAfter(OPT_Instruction s, OPT_Register r, byte 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 one of INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, or
* CONDITION_VALUE
* @param location the spill location
*/
public abstract void insertUnspillBefore(OPT_Instruction s, OPT_Register r, byte 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 one of INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, or
* CONDITION_VALUE
* @param location the spill location
*/
public final void insertUnspillAfter(OPT_Instruction s, OPT_Register r, byte type, int location) {
insertUnspillBefore(s.nextInstructionInCodeOrder(), r, type, location);
}
/**
* Object holding register preferences
*/
protected OPT_RegisterPreferences pref = new OPT_RegisterPreferences();
OPT_RegisterPreferences getPreferences() { return pref; }
/**
* Object holding register restrictions
*/
protected OPT_RegisterRestrictions restrict;
OPT_RegisterRestrictions getRestrictions() { return restrict; }
/**
* Spill pointer (in bytes) relative to the beginning of the
* stack frame (starts after the header).
*/
protected int spillPointer = ArchitectureSpecific.VM_ArchConstants.STACKFRAME_HEADER_SIZE;
/**
* 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;
/**
* Are we required to allocate a stack frame for this method?
*/
protected boolean frameIsRequired() { return frameRequired; }
/**
* Record that we need a stack frame for this method.
*/
protected void setFrameRequired() {
frameRequired = true;
}
/**
* Does this IR have a prologue yieldpoint?
*/
protected boolean hasPrologueYieldpoint() { return prologueYieldpoint; }
/**
* Ensure param passing area of size - STACKFRAME_HEADER_SIZE bytes
*/
public void allocateParameterSpace(int s) {
if (spillPointer < s) {
spillPointer = s;
frameRequired = true;
}
}
/**
* Allocate 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 acutally create the stack frame.
*/
public void forceFrameAllocation() { frameRequired = true; }
/**
* We encountered a float/int conversion that uses
* the stack as temporary storage.
*/
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.
*/
public int allocateSpaceForCaughtException() {
if (caughtExceptionOffset == 0) {
caughtExceptionOffset = allocateOnStackFrame(BYTES_IN_ADDRESS);
}
return caughtExceptionOffset;
}
/**
* Called as part of the register allocator startup.
* (1) examine the IR to determine whether or not we need to
* allocate a stack frame
* (2) 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.
* (3) initialization code for the old OPT_RegisterManager.
* (4) save caughtExceptionOffset where the exception deliverer can find it
* (5) initialize the restrictions object
* @param ir the IR
*/
public void prepare(OPT_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 (OPT_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.
OPT_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 poits
if (VM.BuildForAdaptiveSystem && frameRequired) {
removeYieldpoints = false;
}
if (removeYieldpoints) {
for (OPT_Instruction s = ir.firstInstructionInCodeOrder(); s != null; s = s.nextInstructionInCodeOrder()) {
if (s.isYieldPoint()) {
OPT_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;
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
restrict = new OPT_RegisterRestrictions(ir.regpool.getPhysicalRegisterSet());
}
protected OPT_IR ir;
protected int frameSize; // = 0; (by default)
protected boolean allocFrame; // = false; (by default)
/**
* Set up register restrictions
*/
public final void computeRestrictions(OPT_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 OPT_Register allocateVolatileRegister(OPT_Register symbReg) {
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
int physType = OPT_PhysicalRegisterSet.getPhysicalRegisterType(symbReg);
for (Enumeration<OPT_Register> e = phys.enumerateVolatiles(physType); e.hasMoreElements();) {
OPT_Register realReg = e.nextElement();
if (realReg.isAvailable()) {
realReg.allocateToRegister(symbReg);
if (debug) VM.sysWrite(" volat." + realReg + " to symb " + symbReg + '\n');
return realReg;
}
}
return null;
}
/**
* Given a symbolic register, return a code that indicates the type
* of the value stored in the register.
* Note: This routine returns INT_VALUE for longs
*
* @return one of INT_VALUE, FLOAT_VALUE, DOUBLE_VALUE, CONDITION_VALUE
*/
public final byte getValueType(OPT_Register r) {
if (r.isInteger() || r.isLong() || r.isAddress()) {
return INT_VALUE;
} else if (r.isCondition()) {
return CONDITION_VALUE;
} else if (r.isDouble()) {
return DOUBLE_VALUE;
} else if (r.isFloat()) {
return FLOAT_VALUE;
} else {
throw new OPT_OptimizingCompilerException("getValueType: unsupported " + r);
}
}
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
*
* TODO: Clean up this interface.
*
* @param symbReg the place to start the search
* @return the allocated register or null
*/
public final OPT_Register allocateNonVolatileRegister(OPT_Register symbReg) {
OPT_PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
int physType = OPT_PhysicalRegisterSet.getPhysicalRegisterType(symbReg);
for (Enumeration<OPT_Register> e = phys.enumerateNonvolatilesBackwards(physType); e.hasMoreElements();) {
OPT_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.
*/
protected static class ScratchRegister {
/**
* The physical register used as scratch.
*/
public final OPT_Register scratch;
/**
* The current contents of scratch
*/
public OPT_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(OPT_Register scratch, OPT_Register currentContents) {
this.scratch = scratch;
this.currentContents = currentContents;
}
public String toString() {
String dirtyString = dirty ? "D" : "C";
return "SCRATCH<" + scratch + "," + currentContents + "," + dirtyString + ">";
}
}
}