summaryrefslogtreecommitdiff
path: root/chapter20/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'chapter20/src/main/java')
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java177
1 files changed, 106 insertions, 71 deletions
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 335f4530..84575709 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -153,16 +153,34 @@ public class RegAlloc {
if( bb instanceof FunNode fun )
insertCalleeSave(fun);
+ // Optional diagnostic for values accidentally shared across functions.
+ //assert verifyFunctionLocalEdges();
+
// Top driver: repeated rounds of coloring and splitting.
byte round=0;
while( !graphColor(round) ) {
split(round);
- assert round < 7; // Really expect to be done soon
+ assert round < 7 : "Register allocation made no progress after eight rounds";
round++;
}
postColor(); // Remove no-op spills
}
+ boolean verifyFunctionLocalEdges() {
+ for( CFGNode bb : _code._cfg ) {
+ if( bb instanceof StartNode ) continue;
+ FunNode fun = bb.fun();
+ for( Node use : bb._outputs )
+ if( use instanceof MachNode mach && !(use instanceof ParmNode) )
+ for( int i=1; i<use.nIns(); i++ ) {
+ Node def = use.in(i);
+ if( def==null || mach.regmap(i)==null || def.cfg0()==null || def.cfg0() instanceof StartNode ) continue;
+ assert def.cfg0().fun()==fun : "Cross-function register edge: "+def.uniqueName()+" -> "+use.uniqueName()+" input "+i;
+ }
+ }
+ return true;
+ }
+
private boolean graphColor(byte round) {
_failed.clear();
_lrgs.clear();
@@ -201,8 +219,9 @@ public class RegAlloc {
// independently... which generally requires a full pass over the
// program for each failing live range. i.e., might be a lot of
// passes.
- for( LRG lrg : _failed.keySet() )
- split(round,lrg);
+ LRG[] splits = _failed.keySet().toArray(new LRG[0]);
+ Arrays.sort(splits, (x,y) -> x._lrg - y._lrg);
+ for( LRG lrg : splits ) split(round,lrg);
}
// Split this live range
@@ -217,10 +236,11 @@ public class RegAlloc {
if( lrg._mask.isEmpty() ) {
if( lrg._1regDefCnt <= 1 &&
lrg._1regUseCnt <= 1 &&
- (lrg._1regDefCnt + lrg._1regUseCnt) > 0 )
+ (lrg._1regDefCnt + lrg._1regUseCnt) > 0 &&
+ // A fixed-register clone with flexible uses must split at the uses.
+ !(lrg._1regDefCnt==1 && lrg._machDef.isClone() && lrg._1regUseCnt==0) )
return splitEmptyMaskSimple(round,lrg);
- // Default to splitByLoop
- //return splitEmptyMask(round,lrg);
+ // More complex conflicts use loop-boundary splitting.
}
// Generic split-by-loop depth.
@@ -236,49 +256,20 @@ public class RegAlloc {
// Split just after def
if( lrg._1regDefCnt==1 && !lrg._machDef.isClone() )
- // Force must-split, even if a prior split same block because register
- // conflicts. Example:
+ // An earlier copy is reusable only if no intervening clobber.
+ // Being in the same block alone is insufficient. Example:
// alloc
// V1/rax - forced by alloc
// alloc
// V2/rax - kills prior RAX
// st4 [V1],len - No good, must split around
- makeSplit("def/empty1",round,lrg).insertAfter((Node)lrg._machDef, false/*true*/);
+ insertAfterAndReplace(makeSplit("def/empty1",round,lrg),(Node)lrg._machDef,false);
// Split just before use
if( lrg._1regUseCnt==1 )
insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg);
return true;
}
- // Split live range with an empty mask. Specifically forces splits at
- // single-register defs or uses everywhere.
- boolean splitEmptyMask( byte round, LRG lrg ) {
- findAllLRG(lrg);
- // If no single-use or single-def, assume this is a complete register
- // kill and force spilling everywhere.
- boolean all = lrg._killed || (lrg._1regDefCnt + lrg._1regUseCnt)==0;
- for( Node n : _ns ) {
- if( !(n instanceof MachNode mach) ) continue;
- // Find def of spilling live range; spilling everywhere, OR
- // single-register DEF and not cloneable (since these will clone
- // before every use)
- if( lrg(n)==lrg && (all || (!mach.isClone() && mach.outregmap().size1() )) )
- makeSplit(n,"def/empty2",round,lrg).insertAfter(n,true);
- // Find all uses
- for( int i=1; i<n.nIns(); i++ ) {
- Node def = n.in(i);
- // Skip any new splits inserted just this pass
- while( def instanceof SplitNode && lrg(def)==null )
- def = def.in(1);
- // Main def (past splits) is of the spilling lrg, and spilling
- // single-register USE (or everywhere)
- if( lrg(def)==lrg && (all || mach.regmap(i).size1()) )
- insertBefore(n,i,"use/empty2",round,lrg);
- }
- }
- return true;
- }
-
// Self conflicts require Phis (or two-address).
// Insert a split after every def.
boolean splitSelfConflict( byte round, LRG lrg ) {
@@ -289,27 +280,28 @@ public class RegAlloc {
// For all conflicts
for( Node def : conflicts ) {
assert lrg(def)==lrg; // Might be conflict use-side
+ // Split before each use that extends the live range; i.e. is a
+ // Phi or two-address
+ for( int i=0; i<def._outputs._len; i++ ) {
+ Node use = def.out(i);
+ if( (use instanceof PhiNode phi &&
+ !(phi.region() instanceof LoopNode loop && phi.in(2)==def && def.cfg0().idepth() > loop.idepth() ) ) ||
+ (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
+ insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg );
+ }
// Split after the Phi which extends the LRG. Split also before
// Phi slot 1 (and not all inputs), because Phis extend the live range.
// TODO: split before all inputs (except the last; at least 1 split here must be extra)
if( def instanceof PhiNode phi && !(def instanceof ParmNode) ) {
SplitNode split = makeSplit("def/self",round,lrg);
- split.insertAfter(def,false);
+ insertAfterAndReplace(split,def,false);
if( split.nOuts()==0 )
- split.kill();
+ split.killOrdered();
insertBefore(phi,1,"use/self/phi",round,lrg);
}
// Split before two-address ops which extend the live range
if( def instanceof MachNode mach && mach.twoAddress()!= 0 )
insertBefore(def,mach.twoAddress(),"use/self/two",round,lrg);
- // Split before each use that extends the live range; i.e. is a
- // Phi or two-address
- for( int i=0; i<def._outputs._len; i++ ) {
- Node use = def.out(i);
- if( (use instanceof PhiNode phi && !(phi.region() instanceof LoopNode && phi.in(2)==def ) ) ||
- (use instanceof MachNode mach && mach.twoAddress()!=0 && use.in(mach.twoAddress())==def) )
- insertBefore( use, use._inputs.find(def), "use/self/use",round,lrg );
- }
}
return true;
}
@@ -356,10 +348,10 @@ public class RegAlloc {
(min==max || n.cfg0().loopDepth() <= min) ) {
// Cloneable constants will be cloned at uses, not after def
if( !(n instanceof MachNode mach && mach.isClone()) &&
- // Single user is already a split
- !(n.nOuts()==1 && n.out(0) instanceof SplitNode) )
+ // Single user is already a split, with no intervening clobber
+ !(n.nOuts()==1 && n.out(0) instanceof SplitNode split && sameBlockNoClobber(split)) )
// Split after def in min loop nest
- makeSplit("def/loop",round,lrg).insertAfter(n,false);
+ insertAfterAndReplace(makeSplit("def/loop",round,lrg),n,true);
}
// PhiNodes check all CFG inputs
@@ -370,20 +362,17 @@ public class RegAlloc {
// splitting in inner loop or at loop border
(min==max || phi.region().cfg(i).loopDepth() <= min) &&
// and not around the backedge of a loop (bad place to force a split, hard to remove)
- !(phi.region() instanceof LoopNode && i==2) )
+ !(phi.region() instanceof LoopNode && i==2 && phi.in(i) instanceof PhiNode pp && pp.region()==phi.region()) )
// Split before phi-use in prior block
insertBefore(phi,i, "use/loop/phi",round,lrg);
} else {
// Others check uses
for( int i=1; i<n.nIns(); i++ ) {
- if( lrgSame(n.in(i),lrg) && // This is a LRG use
- // splitting in inner loop or at loop border
- (min==max || n.cfg0().loopDepth() <= min) &&
- // Not a single-use split same block already
- !(n.in(i) instanceof SplitNode && n.in(i).nOuts()==1 && n.cfg0()==n.in(i).cfg0()) )
- // Split before in this block
- insertBefore( n, i, "use/loop/use", round,lrg );
+ if( lrgSame(n.in(i),lrg) &&
+ (min==max || n.in(i) instanceof MachNode mach && mach.isClone() || n.cfg0().loopDepth() <= min) )
+ // Preserve the new split; folding it through can undo progress.
+ insertBefore(n,i,"use/loop/use",round,lrg,false);
}
}
}
@@ -414,8 +403,8 @@ public class RegAlloc {
void findAllLRG( LRG lrg ) {
_ns.clear();
int wd = 0;
- _ns.push((Node)lrg._machDef);
- _ns.push((Node)lrg._machUse);
+ if( lrg._machDef!=null ) _ns.push((Node)lrg._machDef);
+ if( lrg._machUse!=null ) _ns.push((Node)lrg._machUse);
while( wd < _ns._len ) {
Node n = _ns.at(wd++);
if( lrg(n)!=lrg ) continue;
@@ -429,26 +418,47 @@ public class RegAlloc {
for( Node n : _ns ) assert !n.isDead();
}
- void insertBefore(Node n, int i, String kind, byte round, LRG lrg) {
+ void insertBefore(Node n, int i, String kind, byte round, LRG lrg) { insertBefore(n,i,kind,round,lrg,true); }
+ void insertBefore(Node n, int i, String kind, byte round, LRG lrg, boolean skip) {
Node def = n.in(i);
// Effective block for use
CFGNode cfg = n instanceof PhiNode phi ? phi.region().cfg(i) : n.cfg0();
// Def is a split ?
- if( def instanceof SplitNode ) {
- boolean singleReg = n instanceof MachNode mach && mach.regmap(i).size1();
+ if( skip && def instanceof SplitNode ) {
+ boolean singleReg = n instanceof MachNode mach && mach.regmap(i)!=null && mach.regmap(i).size1();
// Same block, multiple registers, split is only used by n,
// assume this is good enough and do not split again.
if( cfg==def.cfg0() && def.nOuts()==1 && !singleReg )
return;
}
- makeSplit(def,kind,round,lrg).insertBefore(n, i);
+ RegMask use = n instanceof MachNode mach ? mach.regmap(i) : null;
+ makeSplit(def,kind,round,lrg,use).insertBefore(n, i);
// Skip split-of-split same block
- if( def instanceof SplitNode && cfg==def.cfg0() )
+ if( skip && def instanceof SplitNode && cfg==def.cfg0() )
n.in(i).setDefOrdered(1,def.in(1));
}
- private Node makeSplit( Node def, String kind, byte round, LRG lrg ) {
- Node split = def instanceof MachNode mach && mach.isClone()
+ // Replace uses of `def` with `split`, and insert `split` immediately after
+ // `def` in the basic block.
+ public void insertAfterAndReplace( Node split, Node def, boolean must ) {
+ split.insertAfter(def);
+ if( split.nIns()>1 ) split.setDef(1,def);
+ for( int j=def.nOuts()-1; j>=0; j-- ) {
+ Node use = def.out(j);
+ if( use==split ) continue; // Skip self
+ // Can we avoid a split of a split? 'this' split is used by
+ // another split in the same block.
+ if( !must && use instanceof SplitNode split2 && sameBlockNoClobber(split2) )
+ continue;
+ int idx = use._inputs.find(def);
+ use.setDefOrdered(idx,split);
+ if( j < def.nOuts() ) j++;
+ }
+ }
+
+ private Node makeSplit( Node def, String kind, byte round, LRG lrg, RegMask use ) {
+ // A clone must be able to satisfy the use's register class.
+ Node split = def instanceof MachNode mach && mach.isClone() && (use==null || mach.outregmap().overlap(use))
? mach.copy()
: _code._mach.split(kind,round,lrg);
_lrgs.put(split,lrg);
@@ -498,13 +508,38 @@ public class RegAlloc {
break;
hi = hi.in(1);
}
- // Check no clobbers
- for( int idx = j-1; bb.out(idx) != hi; idx++) {
+ // Walk backward from the later copy, including instructions without results.
+ for( int idx = j-1; bb.out(idx) != hi; idx--) {
Node n = bb.out(idx);
- if( lrg(n)!=null && lrg(n)._reg == defreg )
+ if( clobbers(n,defreg) )
return false; // Clobbered
}
lo.setDefOrdered(1,hi.in(1));
return true;
}
+ private boolean clobbers(Node n, int reg) {
+ LRG lrg = lrg(n);
+ return lrg!=null && (lrg._reg==reg || lrg._reg==-1 && lrg._mask.size1() && lrg._mask.firstReg()==reg) ||
+ n instanceof MachNode mach && mach.killmap()!=null && mach.killmap().test(reg);
+ }
+
+ public boolean sameBlockNoClobber( SplitNode split ) {
+ Node def = split.in(1);
+ CFGNode cfg = def.cfg0();
+ if( cfg != split.cfg0() ) return false; // Not same block
+ // Get multinode head
+ Node def0 = def instanceof ProjNode ? def.in(0) : def;
+ int defreg = lrg(def)._reg;
+ if( defreg == -1 ) defreg = lrg(def)._mask.firstReg();
+ if( defreg == -1 ) return false; // no allowed registers -> clobbered
+ for( int idx = cfg._outputs.find(split) -1; idx >= 0; idx-- ) {
+ Node n = cfg.out(idx);
+ if( n==def0 ) return true; // No clobbers
+ if( lrg(n) == lrg(def) ) return false; // Self conflict
+ if( clobbers(n,defreg) )
+ return false; // Clobbered
+ }
+ throw Utils.TODO();
+ }
+
}