summaryrefslogtreecommitdiff
path: root/chapter21/src/main/java/com
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-03-29 10:48:05 -0700
committerCliff Click <cliffc@acm.org>2025-03-29 10:48:05 -0700
commitb09370af9db2be3b763eb72a61a34aecb743dcd9 (patch)
tree071b89926ff2e42dfb00828b2f2a908f4e1a831d /chapter21/src/main/java/com
parent7c0a57d1693b2d714f5871f4615b17c29d15ad46 (diff)
downloadSimple-b09370af9db2be3b763eb72a61a34aecb743dcd9.tar.gz
Simple-b09370af9db2be3b763eb72a61a34aecb743dcd9.zip
Split popular constants
Diffstat (limited to 'chapter21/src/main/java/com')
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java94
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/Node.java12
3 files changed, 74 insertions, 33 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 32fca074..6ffb42c9 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -4,6 +4,7 @@ import com.seaofnodes.simple.Ary;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.node.*;
import java.util.Arrays;
+import java.util.BitSet;
import java.util.IdentityHashMap;
/**
@@ -206,7 +207,7 @@ public class RegAlloc {
split(round,lrg);
}
- // Split this live range
+ // Split this live range, top level heuristic
boolean split( byte round, LRG lrg ) {
assert lrg.leader(); // Already rolled up
@@ -220,8 +221,11 @@ public class RegAlloc {
lrg._1regUseCnt <= 1 &&
(lrg._1regDefCnt + lrg._1regUseCnt) > 0 )
return splitEmptyMaskSimple(round,lrg);
- // Default to splitByLoop
- //return splitEmptyMask(round,lrg);
+ // Repeated single-reg uses from a single def. Special for archs
+ // with more fixed regs.
+ if( !lrg._multiDef && lrg._1regDefCnt <= 1 && lrg._1regUseCnt > 2 )
+ if( splitEmptyMaskByUse(round,lrg) )
+ return true;
}
// Generic split-by-loop depth.
@@ -244,39 +248,69 @@ public class RegAlloc {
// 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*/);
+ makeSplit("def/empty1",round,lrg).insertAfterAndReplace((Node)lrg._machDef, false/*true*/);
// Split just before use
if( lrg._1regUseCnt==1 || (lrg._1regDefCnt==1 && ((Node)lrg._machDef).nOuts()==1) )
insertBefore((Node)lrg._machUse,lrg._uidx,"use/empty1",round,lrg,true);
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);
+ // Single-def live range with an empty mask. There are many single-reg
+ // uses. Theory is there's many repeats if the same reg amongst the uses.
+ // In of splitting once per use, start by splitting into groups based on
+ // required input register.
+ boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
+ Node def = (Node)lrg._machDef;
+
+ // Look at each use, and break into non-overlapping register classes.
+ Ary<RegMask> rclass = new Ary<>(RegMask.class);
+ boolean done=false;
+ while( !done ) {
+ done = true;
+ for( Node use : def._outputs )
+ if( use instanceof MachNode mach )
+ for( int i=1; i<use.nIns(); i++ )
+ if( use.in(i)==def )
+ done = putIntoRegClass( rclass, mach.regmap(i) );
+ }
+
+ // See how many register classes we split into
+ if( rclass._len <= 1 ) return false;
+
+ // Split by class
+ for( RegMask rmask : rclass ) {
+ Node split = makeSplit(def,"popular",round,lrg);
+ split.insertAfter( def );
+ // all uses by class to split
+ for( int j=0; j < def._outputs._len; j++ ) {
+ Node use = def._outputs.at(j);
+ if( use instanceof MachNode mach ) {
+ // Check all use inputs for n, in case there's several
+ for( int i = 1; i < use.nIns(); i++ )
+ // Find a def input, and check register class
+ if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) )
+ // Modify use to use the split version specialized to this rclass
+ { use.setDef( i, split ); j--; break; }
+ }
+ }
+ }
+ return true;
+ }
+
+
+ // Put use into a register class, perhaps adding a class or perhaps
+ // narrowing a class (and causing a repeat)
+ private static boolean putIntoRegClass( Ary<RegMask> rclass, RegMask rmask ) {
+ for( int i=0; i<rclass._len; i++ ) {
+ RegMask omask = rclass.at(i);
+ if( omask.and(rmask) == omask ) return true; // Within the same register class
+ if( omask.overlap(rmask) ) {
+ rclass.set(i,new RegMask(omask.copy().and(rmask)));
+ return false; // Need go again
}
}
+ // Add a new class, no need to go again
+ rclass.push(rmask);
return true;
}
@@ -303,7 +337,7 @@ public class RegAlloc {
// 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);
+ split.insertAfterAndReplace(def,false);
if( split.nOuts()==0 )
split.kill();
insertBefore(phi,1,"use/self/phi",round,lrg);
@@ -360,7 +394,7 @@ public class RegAlloc {
// Single user is already a split
!(n.nOuts()==1 && n.out(0) instanceof SplitNode) )
// Split after def in min loop nest
- makeSplit("def/loop",round,lrg).insertAfter(n,false);
+ makeSplit("def/loop",round,lrg).insertAfterAndReplace(n,false);
}
// PhiNodes check all CFG inputs
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
index 2dbb23a9..df53c7ca 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
@@ -32,6 +32,7 @@ public class RegMask {
}
public RegMask(long bits ) { _bits0 = bits; }
public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
+ public RegMask(RegMask r) { _bits0 = r._bits0; _bits1 = r._bits1; }
private RegMask() { _bits0 = _bits1 = 0; }
// AND, with copy-on-write if changing
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter21/src/main/java/com/seaofnodes/simple/node/Node.java
index 3201ff3b..39af5a32 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -300,9 +300,8 @@ public abstract class Node {
kill();
}
- // Replace uses of `def` with `this`, and insert `this` immediately after
- // `def` in the basic block.
- public void insertAfter( Node def, boolean must ) {
+ // insert `this` immediately after `def` in the same basic block.
+ public CFGNode insertAfter( Node def ) {
CFGNode cfg = def.cfg0();
int i = cfg._outputs.find(def)+1;
if( cfg instanceof CallEndNode ) {
@@ -315,6 +314,13 @@ public abstract class Node {
while( cfg.out(i) instanceof PhiNode || cfg.out(i) instanceof CalleeSaveNode ) i++;
cfg._outputs.insert(this,i);
_inputs.set(0,cfg);
+ return cfg;
+ }
+
+ // Replace uses of `def` with `this`, and insert `this` immediately after
+ // `def` in the basic block.
+ public void insertAfterAndReplace( Node def, boolean must ) {
+ CFGNode cfg = insertAfter(def);
for( int j=def.nOuts()-1; j>=0; j-- ) {
// Can we avoid a split of a split? 'this' split is used by
// another split in the same block.