diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-07 19:10:51 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-07 19:10:51 -0700 |
| commit | 2eba63966bdc3d9cfdf41ee085f35cbb6d529bbc (patch) | |
| tree | c297b19a448bc3e0780437122646183240437a30 /chapter25/src/main/java/com | |
| parent | aaec6f8136df6f7020bfc05f79ff5273adf73de1 (diff) | |
| download | Simple-2eba63966bdc3d9cfdf41ee085f35cbb6d529bbc.tar.gz Simple-2eba63966bdc3d9cfdf41ee085f35cbb6d529bbc.zip | |
Towrds getting RA spill counts comparable
Ignore JIGs. Get test suites more conical, so reg alloc spill counts are comparable
Diffstat (limited to 'chapter25/src/main/java/com')
3 files changed, 52 insertions, 5 deletions
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java index 6145910c..bcdb14bf 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/IFG.java @@ -457,6 +457,13 @@ abstract public class IFG { int area = cfg != use.cfg0() ? functionArea(lrg._fun,alloc) : cfg._outputs.find(use)-cfg._outputs.find(def)-1; + // A single-use function address can move next to its use without + // adding an instruction. Prefer that to saving a callee-save + // register just to hold the address. Stay in the same block and + // require a gap so the clone makes progress. + if( def instanceof FunPtrNode && !lrg._multiUse && + cfg == use.cfg0() && area > 0 ) + return 1000000; if( area > 0 ) return areaScore(area,loopCost(use)); } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index 162f9c24..35180fea 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -179,10 +179,12 @@ public class CallEndNode extends CFGNode implements MultiNode { { addDep(fptr); return -1; } // Encouraged inlining because small size and constructor. - int maxSize = fun._name!=null && fun.isInit() && !fun.isClz() ? 200 : 100; - if( fun._approxUIDs >= maxSize ) - return -2; // Try later (if shrinking helps) - if( fun.body().cardinality() >= maxSize ) + int maxSize = fun.inlineLimit(); + // A large parse can leave only a tiny initializer. Recognize that + // without repeatedly walking the whole body while it shrinks. + if( fun._approxUIDs >= maxSize ) { + if( !smallBody(fun) ) return -1; + } else if( fun.body().cardinality() >= maxSize ) return -1; // Try later (if cleanup makes the real body small enough) // Can be cloned but not trivial @@ -192,6 +194,39 @@ public class CallEndNode extends CFGNode implements MultiNode { return 1; } + // Bounded, conservative recognition of straight-line initializer boilerplate. + // Walk uses from the entry and stop at Return, never following callers or + // constant inputs. This also avoids scanning sparse MemMerge input arrays. + // Watch only this small prefix, including an unsupported node that may fold. + private static final Node[] TINYBODY = new Node[15]; // Tiny worklist + private boolean smallBody(FunNode fun) { + ReturnNode ret = fun.ret(); + for( Node in : ret._inputs ) + if( in!=null ) addDep(in); + if( ret.ctrl()!=fun ) return false; + TINYBODY[0] = fun; + int len=1; + for( int i=0; i<len; i++ ) { + Node n = TINYBODY[i]; + addDep(n); + if( n==ret ) continue; + if( n instanceof ParmNode parm && parm.region()!=fun ) return false; + if( n!=fun && !(n instanceof ParmNode) && + !(n instanceof StoreNode) && !(n instanceof MemMergeNode) ) + return false; // Something unexpected + if( n.nOuts()>TINYBODY.length ) return false; // Too big + for( Node use : n._outputs ) { + if( use==null ) continue; + int j=0; + while( j<len && TINYBODY[j]!=use ) j++; + if( j<len ) continue; // Already in TINYBODY + if( len==TINYBODY.length ) { addDep(use); return false; } // Too big + TINYBODY[len++] = use; // Add to worklist + } + } + return true; + } + public void doInline() { assert _inline > 0; // Only called when possible to inline diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java index 29e9900a..b74f9e6b 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -221,6 +221,11 @@ public class FunNode extends RegionNode { public static boolean isInit(String name ) { return name!=null && name.endsWith("init>"); } public static boolean isInstance(String name ) { return name!=null && name.endsWith(".<init>"); } + // Allocation factories and instance initializers carry extra memory boilerplate. + public int inlineLimit() { + return isInstance() || (_name!=null && _name.endsWith(".<new>")) ? 200 : 100; + } + // Function is public (callable from Start directly). public boolean isPublic( ) { // Never true for anonymous functions @@ -316,7 +321,7 @@ public class FunNode extends RegionNode { FunNode copyBody() { // Build the function body BitSet BitSet body = body(); - assert body.cardinality() < 100; + assert body.cardinality() < inlineLimit(); // Walk the body, cloning IdentityHashMap<Node,Node> map = new IdentityHashMap<>(); BitSet visit = CodeGen.CODE.visit(); |
