/*
* 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 java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet;
import org.jikesrvm.VM;
import org.jikesrvm.ArchitectureSpecificOpt.PhysicalRegisterConstants;
import org.jikesrvm.ArchitectureSpecificOpt.PhysicalRegisterSet;
import org.jikesrvm.ArchitectureSpecificOpt.RegisterRestrictions;
import org.jikesrvm.ArchitectureSpecificOpt.StackManager;
import org.jikesrvm.compilers.opt.OptOptions;
import org.jikesrvm.compilers.opt.OptimizingCompilerException;
import org.jikesrvm.compilers.opt.driver.CompilerPhase;
import org.jikesrvm.compilers.opt.driver.OptimizationPlanAtomicElement;
import org.jikesrvm.compilers.opt.driver.OptimizationPlanCompositeElement;
import org.jikesrvm.compilers.opt.driver.OptimizationPlanElement;
import org.jikesrvm.compilers.opt.ir.BasicBlock;
import org.jikesrvm.compilers.opt.ir.ControlFlowGraph;
import org.jikesrvm.compilers.opt.ir.GCIRMapElement;
import org.jikesrvm.compilers.opt.ir.IR;
import org.jikesrvm.compilers.opt.ir.Instruction;
import org.jikesrvm.compilers.opt.ir.InstructionEnumeration;
import org.jikesrvm.compilers.opt.ir.OperandEnumeration;
import org.jikesrvm.compilers.opt.ir.Operators;
import org.jikesrvm.compilers.opt.ir.RegSpillListElement;
import org.jikesrvm.compilers.opt.ir.Register;
import org.jikesrvm.compilers.opt.ir.operand.AddressConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.IntConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.LongConstantOperand;
import org.jikesrvm.compilers.opt.ir.operand.Operand;
import org.jikesrvm.compilers.opt.ir.operand.RegisterOperand;
import org.jikesrvm.compilers.opt.util.GraphEdge;
import org.jikesrvm.compilers.opt.util.SpaceEffGraphNode;
import org.jikesrvm.osr.OSRConstants;
import org.jikesrvm.osr.LocalRegPair;
import org.jikesrvm.osr.MethodVariables;
import org.jikesrvm.osr.VariableMapElement;
import org.vmmagic.unboxed.Word;
/**
* Main driver for linear scan register allocation.
*/
public final class LinearScan extends OptimizationPlanCompositeElement {
/**
* Build this phase as a composite of others.
*/
LinearScan() {
super("Linear Scan Composite Phase",
new OptimizationPlanElement[]{new OptimizationPlanAtomicElement(new IntervalAnalysis()),
new OptimizationPlanAtomicElement(new RegisterRestrictionsPhase()),
new OptimizationPlanAtomicElement(new LinearScanPhase()),
new OptimizationPlanAtomicElement(new UpdateGCMaps1()),
new OptimizationPlanAtomicElement(new SpillCode()),
new OptimizationPlanAtomicElement(new UpdateGCMaps2()),
new OptimizationPlanAtomicElement(new UpdateOSRMaps()),});
}
/**
* Mark FMOVs that end a live range?
*/
private static final boolean MUTATE_FMOV = VM.BuildForIA32;
/**
* debug flags
*/
private static final boolean DEBUG = false;
private static final boolean VERBOSE_DEBUG = false;
private static final boolean GC_DEBUG = false;
private static final boolean DEBUG_COALESCE = false;
/**
* Register allocation is required
*/
public boolean shouldPerform(OptOptions options) {
return true;
}
public String getName() {
return "Linear Scan Composite Phase";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* Associates the passed live interval with the passed register, using
* the scratchObject field of Register.
*
* @param reg the register
* @param interval the live interval
*/
static void setInterval(Register reg, CompoundInterval interval) {
reg.scratchObject = interval;
}
/**
* Returns the interval associated with the passed register.
* @param reg the register
* @return the live interval or null
*/
static CompoundInterval getInterval(Register reg) {
return (CompoundInterval) reg.scratchObject;
}
/**
* returns the dfn associated with the passed instruction
* @param inst the instruction
* @return the associated dfn
*/
static int getDFN(Instruction inst) {
return inst.scratch;
}
/**
* Associates the passed dfn number with the instruction
* @param inst the instruction
* @param dfn the dfn number
*/
static void setDFN(Instruction inst, int dfn) {
inst.scratch = dfn;
}
/**
* Print the DFN numbers associated with each instruction
*/
static void printDfns(IR ir) {
System.out.println("DFNS: **** " + ir.getMethod() + "****");
for (Instruction inst = ir.firstInstructionInCodeOrder(); inst != null; inst =
inst.nextInstructionInCodeOrder()) {
System.out.println(getDFN(inst) + " " + inst);
}
}
/**
* Return the Depth-first-number of the end of the live interval.
* Return the dfn for the end of the basic block if the interval is
* open-ended.
*/
static int getDfnEnd(LiveIntervalElement live, BasicBlock bb) {
Instruction end = live.getEnd();
int dfnEnd;
if (end != null) {
dfnEnd = getDFN(end);
} else {
dfnEnd = getDFN(bb.lastInstruction());
}
return dfnEnd;
}
/**
* Return the Depth-first-number of the beginning of the live interval.
* Return the dfn for the beginning of the basic block if the interval is
* open-ended.
*/
static int getDfnBegin(LiveIntervalElement live, BasicBlock bb) {
Instruction begin = live.getBegin();
int dfnBegin;
if (begin != null) {
dfnBegin = getDFN(begin);
} else {
dfnBegin = getDFN(bb.firstInstruction());
}
return dfnBegin;
}
public static final class LinearScanState {
/**
* The live interval information, a set of Basic Intervals
* sorted by increasing start point
*/
public final ArrayList<BasicInterval> intervals = new ArrayList<BasicInterval>();
/**
* Was any register spilled?
*/
public boolean spilledSomething = false;
/**
* Analysis information used by linear scan.
*/
public ActiveSet active;
}
/**
* A phase to compute register restrictions.
*/
static final class RegisterRestrictionsPhase extends CompilerPhase {
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public CompilerPhase newExecution(IR ir) {
return this;
}
public boolean shouldPerform(OptOptions options) {
return true;
}
public String getName() {
return "Register Restrictions";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* @param ir the IR
*/
public void perform(IR ir) {
// The registerManager has already been initialized
GenericStackManager sm = ir.stackManager;
// Set up register restrictions
sm.computeRestrictions(ir);
}
}
public static final class LinearScanPhase extends CompilerPhase
implements PhysicalRegisterConstants, Operators {
/**
* An object which manages spill location assignments.
*/
private SpillLocationManager spillManager;
/**
* The governing IR
* Also used by ClassWriter
*/
public IR ir;
/**
* Constructor for this compiler phase
*/
private static final Constructor<CompilerPhase> constructor = getCompilerPhaseConstructor(LinearScanPhase.class);
/**
* Get a constructor object for this compiler phase
* @return compiler phase constructor
*/
public Constructor<CompilerPhase> getClassConstructor() {
return constructor;
}
/**
* Register allocation is required
*/
public boolean shouldPerform(OptOptions options) {
return true;
}
public String getName() {
return "Linear Scan";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* Perform the linear scan register allocation algorithm
* See TOPLAS 21(5), Sept 1999, p 895-913
* @param ir the IR
*/
public void perform(IR ir) {
this.ir = ir;
// The registerManager has already been initialized
//GenericStackManager sm = ir.stackManager;
// Get register restrictions
// RegisterRestrictions restrict = sm.getRestrictions(); - unused
// Create the object that manages spill locations
spillManager = new SpillLocationManager(ir);
// Create an (empty) set of active intervals.
ActiveSet active = new ActiveSet(ir, spillManager);
ir.MIRInfo.linearScanState.active = active;
// Intervals sorted by increasing start point
for (BasicInterval b : ir.MIRInfo.linearScanState.intervals) {
MappedBasicInterval bi = (MappedBasicInterval) b;
CompoundInterval ci = bi.container;
active.expireOldIntervals(bi);
// If the interval does not correspond to a physical register
// then we process it.
if (!ci.getRegister().isPhysical()) {
// Update register allocation based on the new interval.
active.allocate(bi, ci);
} else {
// Mark the physical register as currently allocated.
ci.getRegister().allocateRegister();
}
active.add(bi);
}
// update the state.
if (active.spilledSomething()) {
ir.MIRInfo.linearScanState.spilledSomething = true;
}
}
}
/**
* Implements a basic live interval (no holes), which is a pair
* begin - the starting point of the interval
* end - the ending point of the interval
*
* Begin and end are numbers given to each instruction by a numbering pass
*/
static class BasicInterval {
/**
* DFN of the beginning instruction of this interval
*/
private final int begin;
/**
* DFN of the last instruction of this interval
*/
private int end;
/**
* Default constructor.
*/
BasicInterval(int begin, int end) {
this.begin = begin;
this.end = end;
}
/**
* @return the DFN signifying the beginning of this basic interval
*/
final int getBegin() {
return begin;
}
/**
* @return the DFN signifying the end of this basic interval
*/
final int getEnd() {
return end;
}
/**
* Extend a live interval to a new endpoint
*/
final void setEnd(int newEnd) {
end = newEnd;
}
/**
* Does this interval start after dfn?
* @param dfn the depth first numbering to compare to
*/
final boolean startsAfter(int dfn) {
return begin > dfn;
}
/**
* Does this interval start before dfn?
* @param dfn the depth first numbering to compare to
*/
final boolean startsBefore(int dfn) {
return begin < dfn;
}
/**
* Does this interval contain a dfn?
* @param dfn the depth first numbering to compare to
*/
final boolean contains(int dfn) {
return begin <= dfn && end >= dfn;
}
/**
* Does this interval start before another?
* @param i the interval to compare with
*/
final boolean startsBefore(BasicInterval i) {
return begin < i.begin;
}
/**
* Does this interval end after another?
* @param i the interval to compare with
*/
final boolean endsAfter(BasicInterval i) {
return end > i.end;
}
/**
* Does this interval represent the same range as another?
* @param i the interval to compare with
*/
final boolean sameRange(BasicInterval i) {
return begin == i.begin && end == i.end;
}
/**
* Redefine equals
*/
public boolean equals(Object o) {
if (!(o instanceof BasicInterval)) return false;
BasicInterval i = (BasicInterval) o;
return sameRange(i);
}
/**
* Does this interval end before dfn
* @param dfn the depth first numbering to compare to
*/
final boolean endsBefore(int dfn) {
return end < dfn;
}
/**
* Does this interval end after dfn
* @param dfn the depth first numbering to compare to
*/
final boolean endsAfter(int dfn) {
return end > dfn;
}
/**
* Does this interval intersect with another?
*/
final boolean intersects(BasicInterval i) {
int iBegin = i.getBegin();
int iEnd = i.getEnd();
return !(endsBefore(iBegin + 1) || startsAfter(iEnd - 1));
}
/**
* Return a String representation
*/
public String toString() {
String s = "[ " + begin + ", " + end + " ] ";
return s;
}
}
/**
* A basic interval contained in a CompoundInterval.
*/
static class MappedBasicInterval extends BasicInterval {
final CompoundInterval container;
MappedBasicInterval(BasicInterval b, CompoundInterval c) {
super(b.begin, b.end);
this.container = c;
}
MappedBasicInterval(int begin, int end, CompoundInterval c) {
super(begin, end);
this.container = c;
}
/**
* Redefine equals
*/
public boolean equals(Object o) {
if (super.equals(o)) {
MappedBasicInterval i = (MappedBasicInterval) o;
return container == i.container;
} else {
return false;
}
}
public String toString() {
return "<" + container.getRegister() + ">:" + super.toString();
}
}
/**
* Implements a live interval with holes; ie; a list of basic live
* intervals.
*/
static class CompoundInterval extends IncreasingStartIntervalSet {
/** Support for Set serialization */
static final long serialVersionUID = 7423553044815762071L;
/**
* Is this compound interval fully contained in infrequent code?
*/
private boolean _infrequent = true;
final void setFrequent() { _infrequent = false; }
final boolean isInfrequent() { return _infrequent; }
/**
* The register this compound interval represents
*/
private final Register reg;
/**
* A spill location assigned for this interval.
*/
private SpillLocationInterval spillInterval;
SpillLocationInterval getSpillInterval() { return spillInterval; }
/**
* Return the register this interval represents
*/
Register getRegister() {
return reg;
}
/**
* Create a new compound interval of a single Basic interval
*/
CompoundInterval(int dfnBegin, int dfnEnd, Register register) {
BasicInterval newInterval = new MappedBasicInterval(dfnBegin, dfnEnd, this);
add(newInterval);
reg = register;
}
/**
* Create a new compound interval of a single Basic interval
*/
CompoundInterval(BasicInterval i, Register register) {
BasicInterval newInterval = new MappedBasicInterval(i.getBegin(), i.getEnd(), this);
add(newInterval);
reg = register;
}
/**
* Dangerous constructor: use with care.
*/
CompoundInterval(Register r) {
reg = r;
}
/**
* Copy the ranges into a new interval associated with a register r.
*/
CompoundInterval copy(Register r) {
CompoundInterval result = new CompoundInterval(r);
for (Iterator<BasicInterval> i = iterator(); i.hasNext();) {
BasicInterval b = i.next();
result.add(b);
}
return result;
}
/**
* Copy the ranges into a new interval associated with a register r.
* Copy only the basic intervals up to and including stop.
*/
CompoundInterval copy(Register r, BasicInterval stop) {
CompoundInterval result = new CompoundInterval(r);
for (Iterator<BasicInterval> i = iterator(); i.hasNext();) {
BasicInterval b = i.next();
result.add(b);
if (b.sameRange(stop)) return result;
}
return result;
}
/**
* Add a new live range to this compound interval.
*
* @param live the new live range
* @param bb the basic block for live
* @return the new basic interval if one was created; null otherwise
*/
BasicInterval addRange(LiveIntervalElement live, BasicBlock bb) {
if (shouldConcatenate(live, bb)) {
// concatenate with the last basic interval
BasicInterval last = last();
last.setEnd(getDfnEnd(live, bb));
return null;
} else {
// create a new basic interval and append it to the list.
BasicInterval newInterval = new MappedBasicInterval(getDfnBegin(live, bb), getDfnEnd(live, bb), this);
add(newInterval);
return newInterval;
}
}
/**
* Should we simply merge the live interval <code>live</code> into a
* previous BasicInterval?
*
* @param live the live interval being queried
* @param bb the basic block in which live resides.
*/
private boolean shouldConcatenate(LiveIntervalElement live, BasicBlock bb) {
BasicInterval last = last();
// Make sure the new live range starts after the last basic interval
if (VM.VerifyAssertions) {
VM._assert(last.getEnd() <= getDfnBegin(live, bb));
}
int dfnBegin = getDfnBegin(live, bb);
if (live.getBegin() != null) {
if (live.getBegin() == bb.firstRealInstruction()) {
// live starts the basic block. Now make sure it is contiguous
// with the last interval.
return last.getEnd() + 1 >= dfnBegin;
} else {
// live does not start the basic block. Merge with last iff
// last and live share an instruction. This happens when a
// register is def'ed and use'd in the same instruction.
return last.getEnd() == dfnBegin;
}
} else {
// live.getBegin == null.
// Merge if it is contiguous with the last interval.
int dBegin = getDFN(bb.firstInstruction());
return last.getEnd() + 1 >= dBegin;
}
}
/**
* Assign this compound interval to a free spill location.
*
* @param spillManager governing spill location manager
*/
void spill(SpillLocationManager spillManager) {
spillInterval = spillManager.findOrCreateSpillLocation(this);
RegisterAllocatorState.setSpill(reg, spillInterval.getOffset());
RegisterAllocatorState.clearOneToOne(reg);
if (VERBOSE_DEBUG) {
System.out.println("Assigned " + reg + " to location " + spillInterval.getOffset());
}
}
/**
* Has this interval been spilled?
*/
boolean isSpilled() {
return (RegisterAllocatorState.getSpill(getRegister()) != 0);
}
/**
* Assign this compound interval to a physical register.
*/
void assign(Register r) {
getRegister().allocateToRegister(r);
}
/**
* Has this interval been assigned to a physical register?
*/
boolean isAssigned() {
return (RegisterAllocatorState.getMapping(getRegister()) != null);
}
/**
* Get the physical register this interval is assigned to. null if
* none assigned.
*/
Register getAssignment() {
return RegisterAllocatorState.getMapping(getRegister());
}
/**
* Merge this interval with another, non-intersecting interval.
* Precondition: BasicInterval stop is an interval in i. This version
* will only merge basic intervals up to and including stop into this.
*/
void addNonIntersectingInterval(CompoundInterval i, BasicInterval stop) {
SortedSet<BasicInterval> headSet = i.headSetInclusive(stop);
addAll(headSet);
}
/**
* Compute the headSet() [from java.util.SortedSet] but include all
* elements less than upperBound <em>inclusive</em>
*/
SortedSet<BasicInterval> headSetInclusive(BasicInterval upperBound) {
BasicInterval newUpperBound = new BasicInterval(upperBound.getBegin() + 1, upperBound.getEnd());
return headSet(newUpperBound);
}
/**
* Compute the headSet() [from java.util.SortedSet] but include all
* elements less than upperBound <em>inclusive</em>
*/
SortedSet<BasicInterval> headSetInclusive(int upperBound) {
BasicInterval newUpperBound = new BasicInterval(upperBound + 1, upperBound + 1);
return headSet(newUpperBound);
}
/**
* Compute the tailSet() [from java.util.SortedSet] but include all
* elements greater than lowerBound <em>inclusive</em>
*/
SortedSet<BasicInterval> tailSetInclusive(int lowerBound) {
BasicInterval newLowerBound = new BasicInterval(lowerBound - 1, lowerBound - 1);
return tailSet(newLowerBound);
}
/**
* Remove some basic intervals from this compound interval, and return
* the intervals actually removed.
*
* PRECONDITION: all basic intervals in i must appear in this compound
* interval, unless they end after the end of this interval
*/
CompoundInterval removeIntervalsAndCache(CompoundInterval i) {
CompoundInterval result = new CompoundInterval(i.getRegister());
Iterator<BasicInterval> myIterator = iterator();
Iterator<BasicInterval> otherIterator = i.iterator();
BasicInterval current = myIterator.hasNext() ? myIterator.next() : null;
BasicInterval currentI = otherIterator.hasNext() ? otherIterator.next() : null;
while (currentI != null && current != null) {
if (current.startsBefore(currentI)) {
current = myIterator.hasNext() ? myIterator.next() : null;
} else if (currentI.startsBefore(current)) {
currentI = otherIterator.hasNext() ? otherIterator.next() : null;
} else {
if (VM.VerifyAssertions) VM._assert(current.sameRange(currentI));
currentI = otherIterator.hasNext() ? otherIterator.next() : null;
BasicInterval next = myIterator.hasNext() ? myIterator.next() : null;
// add the interval to the cache
result.add(current);
current = next;
}
}
// SJF: the loop as written is slightly more efficient than calling
// removeAll(). However, for some reason, replacing the loop with
// the call to removeAll breaks the compiler on OptTestHarness
// -oc:O2 -method RVMMethod replaceCharWithString -. This is deeply
// disturbing. TODO: fix it. (Hope the problem goes away if/when
// we migrate to classpath libraries).
// removeAll(result);
for (BasicInterval b : result) {
remove(b);
}
return result;
}
/**
* SJF: Apparently our java.util implementation of removeAll()
* doesn't work. Perhaps I've somehow screwed up the comparator with
* the "consistent with equals" property?
* It breaks javalex on BaseOptMarkSweep on IA32
* Hopefully this problem will go away if/when we switch to classpath.
* Else, perhaps I'll ditch use of java.util Collections and write my
* own collection classes.
* In the meantime, here's an ugly hack to get around the problem.
*/
void removeAll(CompoundInterval c) {
for (BasicInterval b : c) {
remove(b);
}
}
/**
* Return the lowest DFN in this compound interval.
*/
int getLowerBound() {
BasicInterval b = first();
return b.getBegin();
}
/**
* Return the highest DFN in this compound interval.
*/
int getUpperBound() {
BasicInterval b = last();
return b.getEnd();
}
/**
* Does this interval intersect with i?
*/
boolean intersects(CompoundInterval i) {
if (isEmpty()) return false;
if (i.isEmpty()) return false;
// Walk over the basic intervals of this interval and i.
// Restrict the walking to intervals that might intersect.
int lower = Math.max(getLowerBound(), i.getLowerBound());
int upper = Math.min(getUpperBound(), i.getUpperBound());
// we may have to move one interval lower on each side.
BasicInterval b = getBasicInterval(lower);
int myLower = (b == null) ? lower : b.getBegin();
if (myLower > upper) return false;
b = i.getBasicInterval(lower);
int otherLower = (b == null) ? lower : b.getBegin();
if (otherLower > upper) return false;
SortedSet<BasicInterval> myTailSet = tailSetInclusive(myLower);
SortedSet<BasicInterval> otherTailSet = i.tailSetInclusive(otherLower);
Iterator<BasicInterval> myIterator = myTailSet.iterator();
Iterator<BasicInterval> otherIterator = otherTailSet.iterator();
BasicInterval current = myIterator.hasNext() ? myIterator.next() : null;
BasicInterval currentI = otherIterator.hasNext() ? otherIterator.next() : null;
while (current != null && currentI != null) {
if (current.getBegin() > upper) break;
if (currentI.getBegin() > upper) break;
if (current.intersects(currentI)) return true;
if (current.startsBefore(currentI)) {
current = myIterator.hasNext() ? myIterator.next() : null;
} else {
currentI = otherIterator.hasNext() ? otherIterator.next() : null;
}
}
return false;
}
/**
* Return the first basic interval that contains a given
* instruction.
*
* If there is no such interval, return null;
* @param s The instruction in question
*/
BasicInterval getBasicInterval(Instruction s) {
return getBasicInterval(getDFN(s));
}
/**
* Return the first basic interval that contains the given
* instruction.
*
* If there is no such interval, return null;
* @param n The DFN of the instruction in question
*/
BasicInterval getBasicInterval(int n) {
SortedSet<BasicInterval> headSet = headSetInclusive(n);
if (!headSet.isEmpty()) {
BasicInterval last = headSet.last();
return last.contains(n) ? last : null;
} else {
return null;
}
}
/**
* Make a String representation
*/
public String toString() {
String str = "[" + getRegister() + "]:";
for (Iterator<BasicInterval> i = iterator(); i.hasNext();) {
BasicInterval b = i.next();
str = str + b;
}
return str;
}
}
/**
* "Active set" for linear scan register allocation.
* This version is maintained sorted in order of increasing
* live interval end point.
*/
static final class ActiveSet extends IncreasingEndMappedIntervalSet {
/** Support for Set serialization */
static final long serialVersionUID = 2570397490575294294L;
/**
* Governing ir
*/
private final IR ir;
/**
* Manager of spill locations;
*/
private final SpillLocationManager spillManager;
/**
* An object to help estimate spill costs
*/
private final transient SpillCostEstimator spillCost;
/**
* Have we spilled anything?
*/
private boolean spilled;
/**
* Default constructor
*/
ActiveSet(IR ir, SpillLocationManager sm) {
super();
spilled = false;
this.ir = ir;
this.spillManager = sm;
switch (ir.options.REGALLOC_SPILL_COST_ESTIMATE) {
case OptOptions.REGALLOC_SIMPLE_SPILL_COST:
spillCost = new SimpleSpillCost(ir);
break;
case OptOptions.REGALLOC_BRAINDEAD_SPILL_COST:
spillCost = new BrainDeadSpillCost(ir);
break;
case OptOptions.REGALLOC_BLOCK_COUNT_SPILL_COST:
spillCost = new BlockCountSpillCost(ir);
break;
default:
OptimizingCompilerException.UNREACHABLE("unsupported spill cost");
spillCost = null;
}
}
/**
* Have we spilled anything?
*/
boolean spilledSomething() {
return spilled;
}
/**
* For each new basic interval, we scan the list of active basic
* intervals in order of increasing end point. We remove any "expired"
* intervals - those
* intervals that no longer overlap the new interval because their
* end point precedes the new interval's start point - and makes the
* corresponding register available for allocation
*
* @param newInterval the new interval
*/
void expireOldIntervals(BasicInterval newInterval) {
for (Iterator<BasicInterval> e = iterator(); e.hasNext();) {
MappedBasicInterval bi = (MappedBasicInterval) e.next();
// break out of the loop when we reach an interval that is still
// alive
int newStart = newInterval.getBegin();
if (bi.endsAfter(newStart)) break;
if (VERBOSE_DEBUG) System.out.println("Expire " + bi);
// note that the bi interval no longer is live
freeInterval(bi);
// remove bi from the active set
e.remove();
}
}
/**
* Take action when a basic interval becomes inactive
*/
void freeInterval(MappedBasicInterval bi) {
CompoundInterval container = bi.container;
Register r = container.getRegister();
if (r.isPhysical()) {
r.deallocateRegister();
return;
}
if (container.isSpilled()) {
// free the spill location iff this is the last interval in the
// compound interval.
BasicInterval last = container.last();
if (last.sameRange(bi)) {
spillManager.freeInterval(container.getSpillInterval());
}
} else {
// free the assigned register
if (VM.VerifyAssertions) VM._assert(container.getAssignment().isAllocated());
container.getAssignment().deallocateRegister();
}
}
/**
* Assign a basic interval to either a register or a spill location.
*/
void allocate(BasicInterval newInterval, CompoundInterval container) {
if (DEBUG) {
System.out.println("Allocate " + newInterval + " " + container.getRegister());
}
Register r = container.getRegister();
if (container.isSpilled()) {
// We previously decided to spill the compound interval. No further
// action is needed.
if (VERBOSE_DEBUG) System.out.println("Previously spilled " + container);
} else {
if (container.isAssigned()) {
// The compound interval was previously assigned to a physical
// register.
Register phys = container.getAssignment();
if (!currentlyActive(phys)) {
// The assignment of newInterval to phys is still OK.
// Update the live ranges of phys to include the new basic
// interval
if (VERBOSE_DEBUG) {
System.out.println("Previously assigned to " +
phys +
" " +
container +
" phys interval " +
getInterval(phys));
}
updatePhysicalInterval(phys, newInterval);
if (VERBOSE_DEBUG) {
System.out.println(" now phys interval " + getInterval(phys));
}
// Mark the physical register as currently allocated
phys.allocateRegister();
} else {
// The previous assignment is not OK, since the physical
// register is now in use elsewhere.
if (DEBUG) {
System.out.println("Previously assigned, " + phys + " " + container);
}
// first look and see if there's another free register for
// container.
if (VERBOSE_DEBUG) System.out.println("Looking for free register");
Register freeR = findAvailableRegister(container);
if (VERBOSE_DEBUG) System.out.println("Free register? " + freeR);
if (freeR == null) {
// Did not find a free register to assign. So, spill one of
// the two intervals concurrently allocated to phys.
CompoundInterval currentAssignment = getCurrentInterval(phys);
// choose which of the two intervals to spill
double costA = spillCost.getCost(container.getRegister());
double costB = spillCost.getCost(currentAssignment.getRegister());
if (VERBOSE_DEBUG) {
System.out.println("Current assignment " + currentAssignment + " cost " + costB);
}
if (VERBOSE_DEBUG) {
System.out.println("Cost of spilling" + container + " cost " + costA);
}
CompoundInterval toSpill = (costA < costB) ? container : currentAssignment;
// spill it.
Register p = toSpill.getAssignment();
toSpill.spill(spillManager);
spilled = true;
if (VERBOSE_DEBUG) {
System.out.println("Spilled " + toSpill + " from " + p);
}
CompoundInterval physInterval = getInterval(p);
physInterval.removeAll(toSpill);
if (VERBOSE_DEBUG) System.out.println(" after spill phys" + getInterval(p));
if (toSpill != container) updatePhysicalInterval(p, newInterval);
if (VERBOSE_DEBUG) {
System.out.println(" now phys interval " + getInterval(p));
}
} else {
// found a free register for container! use it!
if (DEBUG) {
System.out.println("Switch container " + container + "from " + phys + " to " + freeR);
}
CompoundInterval physInterval = getInterval(phys);
if (DEBUG) {
System.out.println("Before switch phys interval" + physInterval);
}
physInterval.removeAll(container);
if (DEBUG) {
System.out.println("Intervals of " + phys + " now " + physInterval);
}
container.assign(freeR);
updatePhysicalInterval(freeR, container, newInterval);
if (VERBOSE_DEBUG) {
System.out.println("Intervals of " + freeR + " now " + getInterval(freeR));
}
// mark the free register found as currently allocated.
freeR.allocateRegister();
}
}
} else {
// This is the first attempt to allocate the compound interval.
// Attempt to find a free physical register for this interval.
Register phys = findAvailableRegister(r);
if (phys != null) {
// Found a free register. Perform the register assignment.
container.assign(phys);
if (DEBUG) {
System.out.println("First allocation " + phys + " " + container);
}
updatePhysicalInterval(phys, newInterval);
if (VERBOSE_DEBUG) System.out.println(" now phys" + getInterval(phys));
// Mark the physical register as currently allocated.
phys.allocateRegister();
} else {
// Could not find a free physical register. Some member of the
// active set must be spilled. Choose a spill candidate.
CompoundInterval spillCandidate = getSpillCandidate(container);
if (VM.VerifyAssertions) {
VM._assert(!spillCandidate.isSpilled());
VM._assert((spillCandidate.getRegister().getType() == r.getType()) ||
(spillCandidate.getRegister().isNatural() && r.isNatural()));
VM._assert(!ir.stackManager.getRestrictions().mustNotSpill(spillCandidate.getRegister()));
if (spillCandidate.getAssignment() != null) {
VM._assert(!ir.stackManager.getRestrictions().
isForbidden(r, spillCandidate.getAssignment()));
}
}
if (spillCandidate != container) {
// spill a previously allocated interval.
phys = spillCandidate.getAssignment();
spillCandidate.spill(spillManager);
spilled = true;
if (VERBOSE_DEBUG) {
System.out.println("Spilled " + spillCandidate + " from " + phys);
}
CompoundInterval physInterval = getInterval(phys);
if (VERBOSE_DEBUG) {
System.out.println(" assigned " + phys + " to " + container);
}
physInterval.removeAll(spillCandidate);
if (VERBOSE_DEBUG) System.out.println(" after spill phys" + getInterval(phys));
updatePhysicalInterval(phys, newInterval);
if (VERBOSE_DEBUG) {
System.out.println(" now phys interval " + getInterval(phys));
}
container.assign(phys);
} else {
// spill the new interval.
if (VERBOSE_DEBUG) System.out.println("spilled " + container);
container.spill(spillManager);
spilled = true;
}
}
}
}
}
/**
* Update the interval representing the allocations of a physical
* register p to include a new interval i
*/
private void updatePhysicalInterval(Register p, BasicInterval i) {
// Get a representation of the intervals already assigned to p.
CompoundInterval physInterval = getInterval(p);
if (physInterval == null) {
// no interval yet. create one.
setInterval(p, new CompoundInterval(i, p));
} else {
// incorporate i into the set of intervals assigned to p
CompoundInterval ci = new CompoundInterval(i, p);
if (VM.VerifyAssertions) VM._assert(!ci.intersects(physInterval));
physInterval.addAll(ci);
}
}
/**
* Update the interval representing the allocations of a physical
* register p to include a new compound interval c. Include only
* those basic intervals in c up to and including basic interval stop.
*/
private void updatePhysicalInterval(Register p, CompoundInterval c, BasicInterval stop) {
// Get a representation of the intervals already assigned to p.
CompoundInterval physInterval = getInterval(p);
if (physInterval == null) {
// no interval yet. create one.
setInterval(p, c.copy(p, stop));
} else {
// incorporate c into the set of intervals assigned to p
if (VM.VerifyAssertions) VM._assert(!c.intersects(physInterval));
// copy to a new BasicInterval so "equals" will work as expected,
// since "stop" may be a MappedBasicInterval.
stop = new BasicInterval(stop.getBegin(), stop.getEnd());
physInterval.addNonIntersectingInterval(c, stop);
}
}
/**
* Is a particular physical register currently allocated to an
* interval in the active set?
*/
boolean currentlyActive(Register r) {
for (Iterator<BasicInterval> e = iterator(); e.hasNext();) {
MappedBasicInterval i = (MappedBasicInterval) e.next();
CompoundInterval container = i.container;
if (RegisterAllocatorState.getMapping(container.getRegister()) == r) {
return true;
}
}
return false;
}
/**
* Given that a physical register r is currently allocated to an
* interval in the active set, return the interval.
*/
CompoundInterval getCurrentInterval(Register r) {
for (Iterator<BasicInterval> e = iterator(); e.hasNext();) {
MappedBasicInterval i = (MappedBasicInterval) e.next();
CompoundInterval container = i.container;
if (RegisterAllocatorState.getMapping(container.getRegister()) == r) {
return container;
}
}
OptimizingCompilerException.UNREACHABLE("getCurrentInterval", "Not Currently Active ", r.toString());
return null;
}
/**
* try to find a free physical register to allocate to the compound
* interval. if no free physical register is found, return null;
*/
Register findAvailableRegister(CompoundInterval ci) {
if (ir.options.FREQ_FOCUS_EFFORT && ci.isInfrequent()) {
// don't bother trying to find an available register
return null;
}
Register r = ci.getRegister();
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
// first attempt to allocate to the preferred register
if (ir.options.REGALLOC_COALESCE_MOVES) {
Register p = getPhysicalPreference(ci);
if (p != null) {
if (DEBUG_COALESCE) {
System.out.println("REGISTER PREFERENCE " + ci + " " + p);
}
return p;
}
}
PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
int type = PhysicalRegisterSet.getPhysicalRegisterType(r);
// next attempt to allocate to a volatile
if (!restrict.allVolatilesForbidden(r)) {
for (Enumeration<Register> e = phys.enumerateVolatiles(type); e.hasMoreElements();) {
Register p = e.nextElement();
if (allocateToPhysical(ci, p)) {
return p;
}
}
}
// next attempt to allocate to a Nonvolatile. we allocate the
// novolatiles backwards.
for (Enumeration<Register> e = phys.enumerateNonvolatilesBackwards(type); e.hasMoreElements();) {
Register p = e.nextElement();
if (allocateToPhysical(ci, p)) {
return p;
}
}
// no allocation succeeded.
return null;
}
/**
* Try to find a free physical register to allocate to a symbolic
* register.
*/
Register findAvailableRegister(Register symb) {
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
// first attempt to allocate to the preferred register
if (ir.options.REGALLOC_COALESCE_MOVES) {
Register p = getPhysicalPreference(symb);
if (p != null) {
if (DEBUG_COALESCE) {
System.out.println("REGISTER PREFERENCE " + symb + " " + p);
}
return p;
}
}
PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
int type = PhysicalRegisterSet.getPhysicalRegisterType(symb);
// next attempt to allocate to a volatile
if (!restrict.allVolatilesForbidden(symb)) {
for (Enumeration<Register> e = phys.enumerateVolatiles(type); e.hasMoreElements();) {
Register p = e.nextElement();
if (allocateNewSymbolicToPhysical(symb, p)) {
return p;
}
}
}
// next attempt to allocate to a Nonvolatile. we allocate the
// novolatiles backwards.
for (Enumeration<Register> e = phys.enumerateNonvolatilesBackwards(type); e.hasMoreElements();) {
Register p = e.nextElement();
if (allocateNewSymbolicToPhysical(symb, p)) {
return p;
}
}
// no allocation succeeded.
return null;
}
/**
* Given the current state of the register allocator, compute the
* available physical register to which a symbolic register has the
* highest preference.
*
* @param r the symbolic register in question.
* @return the preferred register. null if no preference found.
*/
private Register getPhysicalPreference(Register r) {
// a mapping from Register to Integer
// (physical register to weight);
HashMap<Register, Integer> map = new HashMap<Register, Integer>();
CoalesceGraph graph = ir.stackManager.getPreferences().getGraph();
SpaceEffGraphNode node = graph.findNode(r);
// Return null if no affinities.
if (node == null) return null;
// walk through all in edges of the node, searching for affinity
for (Enumeration<GraphEdge> in = node.inEdges(); in.hasMoreElements();) {
CoalesceGraph.Edge edge = (CoalesceGraph.Edge) in.nextElement();
CoalesceGraph.Node src = (CoalesceGraph.Node) edge.from();
Register neighbor = src.getRegister();
if (neighbor.isSymbolic()) {
// if r is assigned to a physical register r2, treat the
// affinity as an affinity for r2
Register r2 = RegisterAllocatorState.getMapping(r);
if (r2 != null && r2.isPhysical()) {
neighbor = r2;
}
}
if (neighbor.isPhysical()) {
// if this is a candidate interval, update its weight
if (allocateNewSymbolicToPhysical(r, neighbor)) {
int w = edge.getWeight();
Integer oldW = map.get(neighbor);
if (oldW == null) {
map.put(neighbor, w);
} else {
map.put(neighbor, oldW + w);
}
break;
}
}
}
// walk through all out edges of the node, searching for affinity
for (Enumeration<GraphEdge> in = node.outEdges(); in.hasMoreElements();) {
CoalesceGraph.Edge edge = (CoalesceGraph.Edge) in.nextElement();
CoalesceGraph.Node dest = (CoalesceGraph.Node) edge.to();
Register neighbor = dest.getRegister();
if (neighbor.isSymbolic()) {
// if r is assigned to a physical register r2, treat the
// affinity as an affinity for r2
Register r2 = RegisterAllocatorState.getMapping(r);
if (r2 != null && r2.isPhysical()) {
neighbor = r2;
}
}
if (neighbor.isPhysical()) {
// if this is a candidate interval, update its weight
if (allocateNewSymbolicToPhysical(r, neighbor)) {
int w = edge.getWeight();
Integer oldW = map.get(neighbor);
if (oldW == null) {
map.put(neighbor, w);
} else {
map.put(neighbor, oldW + w);
}
break;
}
}
}
// OK, now find the highest preference.
Register result = null;
int weight = -1;
for (Map.Entry<Register, Integer> entry : map.entrySet()) {
int w = entry.getValue();
if (w > weight) {
weight = w;
result = entry.getKey();
}
}
return result;
}
/**
* Given the current state of the register allocator, compute the
* available physical register to which an interval has the highest
* preference.
*
* @return the preferred register. null if no preference found.
*/
private Register getPhysicalPreference(CompoundInterval ci) {
// a mapping from Register to Integer
// (physical register to weight);
HashMap<Register, Integer> map = new HashMap<Register, Integer>();
Register r = ci.getRegister();
CoalesceGraph graph = ir.stackManager.getPreferences().getGraph();
SpaceEffGraphNode node = graph.findNode(r);
// Return null if no affinities.
if (node == null) return null;
// walk through all in edges of the node, searching for affinity
for (Enumeration<GraphEdge> in = node.inEdges(); in.hasMoreElements();) {
CoalesceGraph.Edge edge = (CoalesceGraph.Edge) in.nextElement();
CoalesceGraph.Node src = (CoalesceGraph.Node) edge.from();
Register neighbor = src.getRegister();
if (neighbor.isSymbolic()) {
// if r is assigned to a physical register r2, treat the
// affinity as an affinity for r2
Register r2 = RegisterAllocatorState.getMapping(r);
if (r2 != null && r2.isPhysical()) {
neighbor = r2;
}
}
if (neighbor.isPhysical()) {
// if this is a candidate interval, update its weight
if (allocateToPhysical(ci, neighbor)) {
int w = edge.getWeight();
Integer oldW = map.get(neighbor);
if (oldW == null) {
map.put(neighbor, w);
} else {
map.put(neighbor, oldW + w);
}
break;
}
}
}
// walk through all out edges of the node, searching for affinity
for (Enumeration<GraphEdge> in = node.outEdges(); in.hasMoreElements();) {
CoalesceGraph.Edge edge = (CoalesceGraph.Edge) in.nextElement();
CoalesceGraph.Node dest = (CoalesceGraph.Node) edge.to();
Register neighbor = dest.getRegister();
if (neighbor.isSymbolic()) {
// if r is assigned to a physical register r2, treat the
// affinity as an affinity for r2
Register r2 = RegisterAllocatorState.getMapping(r);
if (r2 != null && r2.isPhysical()) {
neighbor = r2;
}
}
if (neighbor.isPhysical()) {
// if this is a candidate interval, update its weight
if (allocateToPhysical(ci, neighbor)) {
int w = edge.getWeight();
Integer oldW = map.get(neighbor);
if (oldW == null) {
map.put(neighbor, w);
} else {
map.put(neighbor, oldW + w);
}
break;
}
}
}
// OK, now find the highest preference.
Register result = null;
int weight = -1;
for (Map.Entry<Register, Integer> entry : map.entrySet()) {
int w = entry.getValue();
if (w > weight) {
weight = w;
result = entry.getKey();
}
}
return result;
}
/**
* Check whether it's ok to allocate an interval i to physical
* register p. If so, return true; If not, return false.
*/
private boolean allocateToPhysical(CompoundInterval i, Register p) {
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
Register r = i.getRegister();
PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
if (p != null && !phys.isAllocatable(p)) return false;
if (VERBOSE_DEBUG && p != null) {
if (!p.isAvailable()) System.out.println("unavailable " + i + p);
if (restrict.isForbidden(r, p)) System.out.println("forbidden" + i + p);
}
if ((p != null) && p.isAvailable() && !restrict.isForbidden(r, p)) {
CompoundInterval pInterval = getInterval(p);
if (pInterval == null) {
// no assignment to p yet
return true;
} else {
if (!i.intersects(pInterval)) {
return true;
}
}
}
return false;
}
/**
* Check whether it's ok to allocate symbolic register to a physical
* register p. If so, return true; If not, return false.
*
* NOTE: This routine assumes we're processing the first interval of
* register symb; so p.isAvailable() is the key information needed.
*/
private boolean allocateNewSymbolicToPhysical(Register symb, Register p) {
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
if (p != null && !phys.isAllocatable(p)) return false;
if (VERBOSE_DEBUG && p != null) {
if (!p.isAvailable()) System.out.println("unavailable " + symb + p);
if (restrict.isForbidden(symb, p)) System.out.println("forbidden" + symb + p);
}
return (p != null) && p.isAvailable() && !restrict.isForbidden(symb, p);
}
/**
* choose one of the active intervals or the newInterval to spill.
*/
private CompoundInterval getSpillCandidate(CompoundInterval newInterval) {
if (VERBOSE_DEBUG) System.out.println("GetSpillCandidate from " + this);
if (ir.options.FREQ_FOCUS_EFFORT && newInterval.isInfrequent()) {
// if it's legal to spill this infrequent interval, then just do so!
// don't spend any more effort.
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
if (!restrict.mustNotSpill(newInterval.getRegister())) {
return newInterval;
}
}
return spillMinUnitCost(newInterval);
}
/**
* Choose the interval with the min unit cost (defined as the number
* of defs and uses)
*/
private CompoundInterval spillMinUnitCost(CompoundInterval newInterval) {
if (VERBOSE_DEBUG) {
System.out.println(" interval caused a spill: " + newInterval);
}
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
Register r = newInterval.getRegister();
double minCost = spillCost.getCost(r);
if (VERBOSE_DEBUG) {
System.out.println(" spill cost: " + r + " " + minCost);
}
CompoundInterval result = newInterval;
if (restrict.mustNotSpill(result.getRegister())) {
if (VERBOSE_DEBUG) {
System.out.println(" must not spill: " + result.getRegister());
}
result = null;
minCost = Double.MAX_VALUE;
}
for (Iterator<BasicInterval> e = iterator(); e.hasNext();) {
MappedBasicInterval b = (MappedBasicInterval) e.next();
CompoundInterval i = b.container;
Register newR = i.getRegister();
if (VERBOSE_DEBUG) {
if (i.isSpilled()) {
System.out.println(" not candidate, already spilled: " + newR);
}
if ((r.getType() != newR.getType()) || (r.isNatural() && newR.isNatural())) {
System.out.println(" not candidate, type mismatch : " + r.getType() + " " + newR + " " + newR.getType());
}
if (restrict.mustNotSpill(newR)) {
System.out.println(" not candidate, must not spill: " + newR);
}
}
if (!newR.isPhysical() &&
!i.isSpilled() &&
(r.getType() == newR.getType() || (r.isNatural() && newR.isNatural())) &&
!restrict.mustNotSpill(newR)) {
// Found a potential spill interval. Check if the assignment
// works if we spill this interval.
if (checkAssignmentIfSpilled(newInterval, i)) {
double iCost = spillCost.getCost(newR);
if (VERBOSE_DEBUG) {
System.out.println(" potential candidate " + i + " cost " + iCost);
}
if (iCost < minCost) {
if (VERBOSE_DEBUG) System.out.println(" best candidate so far" + i);
minCost = iCost;
result = i;
}
} else {
if (VERBOSE_DEBUG) {
System.out.println(" not a candidate, insufficient range: " + i);
}
}
}
}
if (VM.VerifyAssertions) {
VM._assert(result != null);
}
return result;
}
/**
* Check whether, if we spilled interval spill, we could then assign
* interval i to physical register spill.getRegister().
*
* @return true if the allocation would fit. false otherwise
*/
private boolean checkAssignmentIfSpilled(CompoundInterval i, CompoundInterval spill) {
Register r = spill.getAssignment();
RegisterRestrictions restrict = ir.stackManager.getRestrictions();
if (restrict.isForbidden(i.getRegister(), r)) return false;
// 1. Speculatively simulate the spill.
CompoundInterval rI = getInterval(r);
CompoundInterval cache = rI.removeIntervalsAndCache(spill);
// 2. Check the fit.
boolean result = !rI.intersects(i);
// 3. Undo the simulated spill.
rI.addAll(cache);
return result;
}
/**
* Find the basic interval for register r containing instruction s.
* If there are two such intervals, return the 1st one.
* If there is none, return null.
*/
BasicInterval getBasicInterval(Register r, Instruction s) {
CompoundInterval c = getInterval(r);
if (c == null) return null;
return c.getBasicInterval(s);
}
}
/**
* phase to compute linear scan intervals.
*/
public static final class IntervalAnalysis extends CompilerPhase implements Operators {
/**
* the governing ir
*/
IR ir;
/**
* a list of basic blocks in topological order
*/
private BasicBlock listOfBlocks;
/**
* a reverse topological list of basic blocks
*/
private BasicBlock reverseTopFirst;
/**
* Constructor for this compiler phase
*/
private static final Constructor<CompilerPhase> constructor =
getCompilerPhaseConstructor(IntervalAnalysis.class);
/**
* Get a constructor object for this compiler phase
* @return compiler phase constructor
*/
public Constructor<CompilerPhase> getClassConstructor() {
return constructor;
}
/**
* should we perform this phase? yes.
*/
public boolean shouldPerform(OptOptions options) { return true; }
/**
* a name for this phase.
*/
public String getName() { return "Interval Analysis"; }
/**
* should we print the ir?
*/
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* compute live intervals for this ir
* the result is a sorted (by beginning point) set of compound
* intervals, stored in the private 'intervals' field.
*
* note: this implementation bashes the 'scratchobject' field on all
* registers and the 'scratch' field on instructions.
*
* @param ir the ir
*/
public void perform(IR ir) {
this.ir = ir;
ControlFlowGraph cfg = ir.cfg;
PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
LinearScanState state = new LinearScanState();
ir.MIRInfo.linearScanState = state;
// create topological list and a reverse topological list
// the results are on listOfBlocks and reverseTopFirst lists
createTopAndReverseList(cfg);
// give dfn values to each instruction
assignDepthFirstNumbers(cfg);
// initialize registers
initializeRegisters();
int lastBeginSeen = -1;
// visit each basic block in the listOfBlocks list
for (BasicBlock bb = listOfBlocks; bb != null; bb = (BasicBlock) bb.nextSorted) {
// visit each live interval for this basic block
for (LiveIntervalElement live = bb.getFirstLiveIntervalElement(); live != null; live = live.getNext()) {
// check that we process live intervals in order of increasing
// begin.
if (VM.VerifyAssertions) {
int begin = getDfnBegin(live, bb);
VM._assert(begin >= lastBeginSeen);
lastBeginSeen = begin;
}
// skip registers which are not allocated.
if (live.getRegister().isPhysical() && !phys.isAllocatable(live.getRegister())) {
continue;
}
CompoundInterval resultingInterval = processLiveInterval(live, bb);
if (!bb.getInfrequent() && resultingInterval != null) {
resultingInterval.setFrequent();
}
}
}
// debug support
if (VERBOSE_DEBUG) {
VM.sysWrite("**** start of interval dump " + ir.method + " ****\n");
VM.sysWrite(ir.MIRInfo.linearScanState.intervals.toString());
VM.sysWrite("**** end of interval dump ****\n");
}
}
/**
* create topological list and a reverse topological list
* the results are on listOfBlocks and reverseTopFirst lists
* @param cfg the control flow graph
*/
private void createTopAndReverseList(ControlFlowGraph cfg) {
// dfs: create a list of nodes (basic blocks) in a topological order
cfg.clearDFS();
listOfBlocks = cfg.entry();
listOfBlocks.sortDFS();
// this loop reverses the topological list by using the sortedPrev field
reverseTopFirst = null;
for (BasicBlock bb = listOfBlocks; bb != null; bb = (BasicBlock) bb.nextSorted) {
// put back pointers in the "prev" field
// set reverseTopFirst to be the more recent node we've seen,
// it will be the front of the list when we are done
bb.sortedPrev = reverseTopFirst;
reverseTopFirst = bb;
}
}
/**
* this method processes all basic blocks, do the following to each block
* 1) add it to the begining of the "listOfBlocks" list
* 2) number the instructions
* 3) process the instructions that restrict physical register
* assignment
* @param cfg the control flow graph
*/
void assignDepthFirstNumbers(ControlFlowGraph cfg) {
int curDfn = ir.numberInstructions() - 1;
listOfBlocks = null;
for (BasicBlock bb = reverseTopFirst; bb != null; bb = (BasicBlock) bb.sortedPrev) {
// insert bb at the front of the list
bb.nextSorted = listOfBlocks;
listOfBlocks = bb;
// number the instructions last to first
InstructionEnumeration e = bb.reverseInstrEnumerator();
while (e.hasMoreElements()) {
Instruction inst = e.next();
setDFN(inst, curDfn);
curDfn--;
}
}
if (DEBUG) { printDfns(ir); }
}
/**
* Initialize the interval for each register to null.
*/
private void initializeRegisters() {
for (Register reg = ir.regpool.getFirstSymbolicRegister(); reg != null; reg = reg.getNext()) {
setInterval(reg, null);
RegisterAllocatorState.setSpill(reg, 0);
// clear the 'long' type if it's persisted to here.
if (VM.BuildFor32Addr && reg.isLong()) {
reg.clearType();
reg.setInteger();
}
}
}
/**
* for each live interval associated with this block
* we either add a new interval, or extend a previous interval
* if it is contiguous
*
* @param live the liveintervalelement for a basic block/reg pair
* @param bb the basic block
* @return the resulting CompoundInterval. null if the live interval
* is not relevant to register allocation.
*/
private CompoundInterval processLiveInterval(LiveIntervalElement live, BasicBlock bb) {
// get the reg and (adjusted) begin, end pair for this interval
Register reg = live.getRegister();
int dfnend = getDfnEnd(live, bb);
int dfnbegin = getDfnBegin(live, bb);
if (MUTATE_FMOV && reg.isFloatingPoint()) {
Operators.helper.mutateFMOVs(live, reg, dfnbegin, dfnend);
}
// check for an existing live interval for this register
CompoundInterval existingInterval = getInterval(reg);
if (existingInterval == null) {
// create a new live interval
CompoundInterval newInterval = new CompoundInterval(dfnbegin, dfnend, reg);
if (VERBOSE_DEBUG) System.out.println("created a new interval " + newInterval);
// associate the interval with the register
setInterval(reg, newInterval);
// add the new interval to the sorted set of intervals.
BasicInterval b = newInterval.first();
ir.MIRInfo.linearScanState.intervals.add(b);
return newInterval;
} else {
// add the new live range to the existing interval
ArrayList<BasicInterval> intervals = ir.MIRInfo.linearScanState.intervals;
BasicInterval added = existingInterval.addRange(live, bb);
if (added != null) {
intervals.add(added);
}
if (VERBOSE_DEBUG) System.out.println("Extended old interval " + reg);
if (VERBOSE_DEBUG) System.out.println(existingInterval);
return existingInterval;
}
}
}
/**
* The following class manages allocation and reuse of spill locations.
*/
static class SpillLocationManager implements PhysicalRegisterConstants {
/**
* The governing IR
*/
private final IR ir;
/**
* Set of spill locations which were previously allocated, but may be
* free since the assigned register is no longer live.
*/
final HashSet<SpillLocationInterval> freeIntervals = new HashSet<SpillLocationInterval>();
/**
* Return a spill location that is valid to hold the contents of
* compound interval ci.
*/
SpillLocationInterval findOrCreateSpillLocation(CompoundInterval ci) {
SpillLocationInterval result = null;
Register r = ci.getRegister();
int type = PhysicalRegisterSet.getPhysicalRegisterType(r);
if (type == -1) {
type = DOUBLE_REG;
}
int spillSize = PhysicalRegisterSet.getSpillSize(type);
// Search the free intervals and try to find an interval to
// reuse. First look for the preferred interval.
if (ir.options.REGALLOC_COALESCE_SPILLS) {
result = getSpillPreference(ci, spillSize);
if (result != null) {
if (DEBUG_COALESCE) {
System.out.println("SPILL PREFERENCE " + ci + " " + result);
}
freeIntervals.remove(result);
}
}
// Now search for any free interval.
if (result == null) {
for (SpillLocationInterval s : freeIntervals) {
if (s.getSize() == spillSize && !s.intersects(ci)) {
result = s;
freeIntervals.remove(result);
break;
}
}
}
if (result == null) {
// Could not find an interval to reuse. Create a new interval.
int location = ir.stackManager.allocateNewSpillLocation(type);
result = new SpillLocationInterval(location, spillSize);
}
// Update the spill location interval to hold the new spill
result.addAll(ci);
return result;
}
/**
* Record that a particular interval is potentially available for
* reuse
*/
void freeInterval(SpillLocationInterval i) {
freeIntervals.add(i);
}
/**
* Constructor.
*/
SpillLocationManager(IR ir) {
this.ir = ir;
}
/**
* Given the current state of the register allocator, compute the
* available spill location to which ci has the highest preference.
*
* @param ci the interval to spill
* @param spillSize the size of spill location needed
* @return the interval to spill to. null if no preference found.
*/
SpillLocationInterval getSpillPreference(CompoundInterval ci, int spillSize) {
// a mapping from SpillLocationInterval to Integer
// (spill location to weight);
HashMap<SpillLocationInterval, Integer> map = new HashMap<SpillLocationInterval, Integer>();
Register r = ci.getRegister();
CoalesceGraph graph = ir.stackManager.getPreferences().getGraph();
SpaceEffGraphNode node = graph.findNode(r);
// Return null if no affinities.
if (node == null) return null;
// walk through all in edges of the node, searching for spill
// location affinity
for (Enumeration<GraphEdge> in = node.inEdges(); in.hasMoreElements();) {
CoalesceGraph.Edge edge = (CoalesceGraph.Edge) in.nextElement();
CoalesceGraph.Node src = (CoalesceGraph.Node) edge.from();
Register neighbor = src.getRegister();
if (neighbor.isSymbolic() && neighbor.isSpilled()) {
int spillOffset = RegisterAllocatorState.getSpill(neighbor);
// if this is a candidate interval, update its weight
for (SpillLocationInterval s : freeIntervals) {
if (s.getOffset() == spillOffset && s.getSize() == spillSize && !s.intersects(ci)) {
int w = edge.getWeight();
Integer oldW = map.get(s);
if (oldW == null) {
map.put(s, w);
} else {
map.put(s, oldW + w);
}
break;
}
}
}
}
// walk through all out edges of the node, searching for spill
// location affinity
for (Enumeration<GraphEdge> in = node.inEdges(); in.hasMoreElements();) {
CoalesceGraph.Edge edge = (CoalesceGraph.Edge) in.nextElement();
CoalesceGraph.Node dest = (CoalesceGraph.Node) edge.to();
Register neighbor = dest.getRegister();
if (neighbor.isSymbolic() && neighbor.isSpilled()) {
int spillOffset = RegisterAllocatorState.getSpill(neighbor);
// if this is a candidate interval, update its weight
for (SpillLocationInterval s : freeIntervals) {
if (s.getOffset() == spillOffset && s.getSize() == spillSize && !s.intersects(ci)) {
int w = edge.getWeight();
Integer oldW = map.get(s);
if (oldW == null) {
map.put(s, w);
} else {
map.put(s, oldW + w);
}
break;
}
}
}
}
// OK, now find the highest preference.
SpillLocationInterval result = null;
int weight = -1;
for (Map.Entry<SpillLocationInterval, Integer> entry : map.entrySet()) {
int w = entry.getValue();
if (w > weight) {
weight = w;
result = entry.getKey();
}
}
return result;
}
}
/**
* The following represents the intervals assigned to a particular spill
* location
*/
static class SpillLocationInterval extends CompoundInterval {
/** Support for Set serialization */
static final long serialVersionUID = 2854333172650538517L;
/**
* The spill location, an offset from the frame pointer
*/
private final int frameOffset;
int getOffset() {
return frameOffset;
}
/**
* The size of the spill location, in bytes.
*/
private final int size;
int getSize() {
return size;
}
SpillLocationInterval(int frameOffset, int size) {
super(null);
this.frameOffset = frameOffset;
this.size = size;
}
public String toString() {
return super.toString() + "<Offset:" + frameOffset + "," + size + ">";
}
/**
* Redefine hash code for reproducibility.
*/
public int hashCode() {
BasicInterval first = first();
BasicInterval last = last();
return frameOffset + (first.getBegin() << 4) + (last.getEnd() << 12);
}
}
/**
* Implements a set of Basic Intervals, sorted by start number.
* This version does NOT use container-mapping as a function in the comparator.
*/
static class IncreasingStartIntervalSet extends IntervalSet {
/** Support for Set serialization */
static final long serialVersionUID = -7086728932911844728L;
private static class StartComparator implements Comparator<BasicInterval> {
public int compare(BasicInterval b1, BasicInterval b2) {
int result = b1.getBegin() - b2.getBegin();
if (result == 0) {
result = b1.getEnd() - b2.getEnd();
}
return result;
}
}
static final StartComparator c = new StartComparator();
IncreasingStartIntervalSet() {
super(c /*,true*/);
}
}
/**
* Implements a set of Basic Intervals, sorted by start number.
* This version uses container-mapping as a function in the comparator.
*/
static class IncreasingStartMappedIntervalSet extends IntervalSet {
/** Support for Set serialization */
static final long serialVersionUID = -975667667343524421L;
private static class StartComparator implements Comparator<BasicInterval> {
public int compare(BasicInterval b1, BasicInterval b2) {
int result = b1.getBegin() - b2.getBegin();
if (result == 0) {
result = b1.getEnd() - b2.getEnd();
}
if (result == 0) {
if (b1 instanceof MappedBasicInterval) {
if (b2 instanceof MappedBasicInterval) {
MappedBasicInterval mb1 = (MappedBasicInterval) b1;
MappedBasicInterval mb2 = (MappedBasicInterval) b2;
return mb1.container.getRegister().number - mb2.container.getRegister().number;
}
}
}
return result;
}
}
static final StartComparator c = new StartComparator();
IncreasingStartMappedIntervalSet() {
super(c);
}
}
/**
* Implements a set of Basic Intervals, sorted by end number.
* This version uses container-mapping as a function in the comparator.
*/
static class IncreasingEndMappedIntervalSet extends IntervalSet {
/** Support for Set serialization */
static final long serialVersionUID = -3121737650157210290L;
private static class EndComparator implements Comparator<BasicInterval> {
public int compare(BasicInterval b1, BasicInterval b2) {
int result = b1.getEnd() - b2.getEnd();
if (result == 0) {
result = b1.getBegin() - b2.getBegin();
}
if (result == 0) {
if (b1 instanceof MappedBasicInterval) {
if (b2 instanceof MappedBasicInterval) {
MappedBasicInterval mb1 = (MappedBasicInterval) b1;
MappedBasicInterval mb2 = (MappedBasicInterval) b2;
return mb1.container.getRegister().number - mb2.container.getRegister().number;
}
}
}
return result;
}
}
static final EndComparator c = new EndComparator();
IncreasingEndMappedIntervalSet() {
super(c);
}
}
abstract static class IntervalSet extends TreeSet<BasicInterval> {
/**
* Create an interval set sorted by increasing start or end number
*/
IntervalSet(Comparator<BasicInterval> c) {
super(c);
}
/**
* Return a String representation
*/
public String toString() {
String result = "";
for (BasicInterval b : this) {
result = result + b + "\n";
}
return result;
}
}
/**
* Update GC maps after register allocation but before inserting spill
* code.
*/
static final class UpdateGCMaps1 extends CompilerPhase {
public boolean shouldPerform(OptOptions options) {
return true;
}
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public CompilerPhase newExecution(IR ir) {
return this;
}
public String getName() {
return "Update GCMaps 1";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* Iterate over the IR-based GC map collection and for each entry
* replace the symbolic reg with the real reg or spill it was allocated
* @param ir the IR
*/
public void perform(IR ir) {
for (GCIRMapElement GCelement : ir.MIRInfo.gcIRMap) {
if (GC_DEBUG) {
VM.sysWrite("GCelement " + GCelement);
}
for (RegSpillListElement elem : GCelement.regSpillList()) {
Register symbolic = elem.getSymbolicReg();
if (GC_DEBUG) {
VM.sysWrite("get location for " + symbolic + '\n');
}
if (symbolic.isAllocated()) {
Register ra = RegisterAllocatorState.getMapping(symbolic);
elem.setRealReg(ra);
if (GC_DEBUG) { VM.sysWrite(ra + "\n"); }
} else if (symbolic.isSpilled()) {
int spill = symbolic.getSpillAllocated();
elem.setSpill(spill);
if (GC_DEBUG) { VM.sysWrite(spill + "\n"); }
} else {
OptimizingCompilerException.UNREACHABLE("LinearScan", "register not alive:", symbolic.toString());
}
}
}
}
}
/**
* Update GC Maps again, to account for changes induced by spill code.
*/
static final class UpdateGCMaps2 extends CompilerPhase {
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public CompilerPhase newExecution(IR ir) {
return this;
}
public boolean shouldPerform(OptOptions options) {
return true;
}
public String getName() {
return "Update GCMaps 2";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* @param ir the IR
*/
public void perform(IR ir) {
PhysicalRegisterSet phys = ir.regpool.getPhysicalRegisterSet();
ScratchMap scratchMap = ir.stackManager.getScratchMap();
if (GC_DEBUG) {
System.out.println("SCRATCH MAP:\n" + scratchMap);
}
if (scratchMap.isEmpty()) return;
// Walk over each instruction that has a GC point.
for (GCIRMapElement GCelement : ir.MIRInfo.gcIRMap) {
// new elements to add to the gc map
HashSet<RegSpillListElement> newElements = new HashSet<RegSpillListElement>();
Instruction GCinst = GCelement.getInstruction();
// Get the linear-scan DFN for this instruction.
int dfn = GCinst.scratch;
if (GC_DEBUG) {
VM.sysWrite("GCelement at " + dfn + " , " + GCelement);
}
// a set of elements to delete from the GC Map
HashSet<RegSpillListElement> toDelete = new HashSet<RegSpillListElement>(3);
// For each element in the GC Map ...
for (RegSpillListElement elem : GCelement.regSpillList()) {
if (GC_DEBUG) {
VM.sysWrite("Update " + elem + "\n");
}
if (elem.isSpill()) {
// check if the spilled value currently is cached in a scratch
// register
Register r = elem.getSymbolicReg();
Register scratch = scratchMap.getScratch(r, dfn);
if (scratch != null) {
if (GC_DEBUG) {
VM.sysWrite("cached in scratch register " + scratch + "\n");
}
// we will add a new element noting that the scratch register
// also must be including in the GC map
RegSpillListElement newElem = new RegSpillListElement(r);
newElem.setRealReg(scratch);
newElements.add(newElem);
// if the scratch register is dirty, then delete the spill
// location from the map, since it doesn't currently hold a
// valid value
if (scratchMap.isDirty(GCinst, r)) {
toDelete.add(elem);
}
}
} else {
// check if the physical register is currently spilled.
int n = elem.getRealRegNumber();
Register r = phys.get(n);
if (scratchMap.isScratch(r, dfn)) {
// The regalloc state knows where the physical register r is
// spilled.
if (GC_DEBUG) {
VM.sysWrite("CHANGE to spill location " + RegisterAllocatorState.getSpill(r) + "\n");
}
elem.setSpill(RegisterAllocatorState.getSpill(r));
}
}
}
// delete all obsolete elements
for (RegSpillListElement deadElem : toDelete) {
GCelement.deleteRegSpillElement(deadElem);
}
// add each new Element to the gc map
for (RegSpillListElement newElem : newElements) {
GCelement.addRegSpillElement(newElem);
}
}
}
}
/**
* Insert Spill Code after register assignment.
*/
static final class SpillCode extends CompilerPhase implements Operators {
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public CompilerPhase newExecution(IR ir) {
return this;
}
public boolean shouldPerform(OptOptions options) {
return true;
}
public String getName() {
return "Spill Code";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* @param ir the IR
*/
public void perform(IR ir) {
replaceSymbolicRegisters(ir);
// Generate spill code if necessary
if (ir.hasSysCall() || ir.MIRInfo.linearScanState.spilledSomething) {
StackManager stackMan = (StackManager) ir.stackManager;
stackMan.insertSpillCode(ir.MIRInfo.linearScanState.active);
// stackMan.insertSpillCode();
}
if (VM.BuildForIA32 && !VM.BuildForSSE2Full) {
Operators.helper.rewriteFPStack(ir);
}
}
/**
* Iterate over the IR and replace each symbolic register with its
* allocated physical register.
* Also used by ClassWriter
*/
public static void replaceSymbolicRegisters(IR ir) {
for (InstructionEnumeration inst = ir.forwardInstrEnumerator(); inst.hasMoreElements();) {
Instruction s = inst.next();
for (OperandEnumeration ops = s.getOperands(); ops.hasMoreElements();) {
Operand op = ops.next();
if (op.isRegister()) {
RegisterOperand rop = op.asRegister();
Register r = rop.getRegister();
if (r.isSymbolic() && !r.isSpilled()) {
Register p = RegisterAllocatorState.getMapping(r);
if (VM.VerifyAssertions) VM._assert(p != null);
rop.setRegister(p);
}
}
}
}
}
}
/**
* Update GC maps after register allocation but before inserting spill
* code.
*/
public static final class UpdateOSRMaps extends CompilerPhase {
public boolean shouldPerform(OptOptions options) {
return true;
}
/**
* Return this instance of this phase. This phase contains no
* per-compilation instance fields.
* @param ir not used
* @return this
*/
public CompilerPhase newExecution(IR ir) {
return this;
}
public String getName() {
return "Update OSRMaps";
}
public boolean printingEnabled(OptOptions options, boolean before) {
return false;
}
/**
* Iterate over the IR-based OSR map, and update symbolic registers
* with real reg number or spill locations.
* Verify there are only two types of operands:
* ConstantOperand
* RegisterOperand
* for integer constant, we save the value of the integer
*
* The LONG register has another half part.
*
* CodeSpill replaces any allocated symbolic register by
* physical registers.
*/
public void perform(IR ir) throws OptimizingCompilerException {
// list of OsrVariableMapElement
//LinkedList<VariableMapElement> mapList = ir.MIRInfo.osrVarMap.list;
//for (int numOsrs=0, m=mapList.size(); numOsrs<m; numOsrs++) {
// VariableMapElement elm = mapList.get(numOsrs);
/* for each osr instruction */
for (VariableMapElement elm : ir.MIRInfo.osrVarMap.list) {
// for each inlined method
//LinkedList<MethodVariables> mvarsList = elm.mvars; XXX Remove once proven correct
//for (int numMvars=0, n=mvarsList.size(); numMvars<n; numMvars++) {
// MethodVariables mvar = mvarsList.get(numMvars);
for (MethodVariables mvar : elm.mvars) {
// for each tuple
//LinkedList<LocalRegPair> tupleList = mvar.tupleList;
//for (int numTuple=0, k=tupleList.size(); numTuple<k; numTuple++) {
//LocalRegPair tuple = tupleList.get(numTuple);
for (LocalRegPair tuple : mvar.tupleList) {
Operand op = tuple.operand;
if (op.isRegister()) {
Register sym_reg = ((RegisterOperand) op).getRegister();
setRealPosition(ir, tuple, sym_reg);
// get another half part of long register
if (VM.BuildFor32Addr && (tuple.typeCode == OSRConstants.LongTypeCode)) {
LocalRegPair other = tuple._otherHalf;
Operand other_op = other.operand;
if (VM.VerifyAssertions) VM._assert(other_op.isRegister());
Register other_reg = ((RegisterOperand) other_op).getRegister();
setRealPosition(ir, other, other_reg);
}
/* According to ConvertToLowLevelIR, StringConstant, LongConstant,
* NullConstant, FloatConstant, and DoubleConstant are all materialized
* The only thing left is the integer constants which could encode
* non-moveable objects.
* POTENTIAL DRAWBACKS: since any long, float, and double are moved
* to register and treated as use, it may consume more registers and
* add unnecessary MOVEs.
*
* Perhaps, ConvertToLowLevelIR can skip OsrPoint instruction.
*/
} else if (op.isIntConstant()) {
setTupleValue(tuple, OSRConstants.ICONST, ((IntConstantOperand) op).value);
if (VM.BuildFor32Addr && (tuple.typeCode == OSRConstants.LongTypeCode)) {
LocalRegPair other = tuple._otherHalf;
Operand other_op = other.operand;
if (VM.VerifyAssertions) VM._assert(other_op.isIntConstant());
setTupleValue(other, OSRConstants.ICONST, ((IntConstantOperand) other_op).value);
}
} else if (op.isAddressConstant()) {
setTupleValue(tuple, OSRConstants.ACONST, ((AddressConstantOperand) op).value.toWord());
} else if (VM.BuildFor64Addr && op.isLongConstant()) {
setTupleValue(tuple, OSRConstants.LCONST, Word.fromLong(((LongConstantOperand) op).value));
} else {
throw new OptimizingCompilerException("LinearScan", "Unexpected operand type at ", op.toString());
} // for the op type
} // for each tuple
} // for each inlined method
} // for each osr instruction
} // end of method
void setRealPosition(IR ir, LocalRegPair tuple, Register sym_reg) {
if (VM.VerifyAssertions) VM._assert(sym_reg != null);
int REG_MASK = 0x01F;
// now it is not symbolic register anymore.
// is is really confusing that sometimes a sym reg is a phy,
// and sometimes not.
if (sym_reg.isAllocated()) {
setTupleValue(tuple, OSRConstants.PHYREG, sym_reg.number & REG_MASK);
} else if (sym_reg.isPhysical()) {
setTupleValue(tuple, OSRConstants.PHYREG, sym_reg.number & REG_MASK);
} else if (sym_reg.isSpilled()) {
setTupleValue(tuple, OSRConstants.SPILL, sym_reg.getSpillAllocated());
} else {
dumpIR(ir, "PANIC");
throw new RuntimeException("LinearScan PANIC in OSRMAP, " + sym_reg + " is not alive");
}
} // end of setRealPosition
static void setTupleValue(LocalRegPair tuple, byte type, int value) {
tuple.valueType = type;
tuple.value = Word.fromIntSignExtend(value);
} // end of setTupleValue
static void setTupleValue(LocalRegPair tuple, byte type, Word value) {
tuple.valueType = type;
tuple.value = value;
} // end of setTupleValue
} // end of inner class
}