diff options
| author | Cliff Click <cliffc@acm.org> | 2026-10-17 17:38:12 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-10-17 17:38:12 -0700 |
| commit | 0be52f8dd82f70d4c557fe916d47dc425401f848 (patch) | |
| tree | ff2f2cc82744baed33e85971361a14abcdd2aa22 /chapter25/src/main | |
| parent | 9b9fadcfa74b087a695156573ea28b89a166b18b (diff) | |
| download | Simple-0be52f8dd82f70d4c557fe916d47dc425401f848.tar.gz Simple-0be52f8dd82f70d4c557fe916d47dc425401f848.zip | |
Phase cleanup heading into CLInit expansion
Diffstat (limited to 'chapter25/src/main')
25 files changed, 208 insertions, 108 deletions
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java index f0c4567f..eca2b9ce 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java @@ -290,6 +290,19 @@ public class Parser { _scope.define(ids[i], t, (i==0 && ids[0]=="self") || (finals!=null && finals.get(i)), new ParmNode(ids[i],i+2,t,fun,ConstantNode.seed(t).peephole()).peephole(), loc); } + // The initializer owns its run-once flag store. Parent initialization + // is a structural requirement, to be executed before this body when + // CLInit runtime lowering is added. + if( fun.isClz() ) { + Node self = fun.parm(2); + Field flag = ((TypeMemPtr)self._type)._obj.field("$init"); + Node prior = mem().keep(); + Node st = peep(new StoreNode(loc,"$init",flag._alias,flag._t,ctrl(),prior,self, + fldoff(self,"$init"),con(1),false)); + storeMem(st,flag._alias,prior); + prior.unkeep(); + } + // Parse the function body. Node last=_code.ZERO; // Last statement as the default result @@ -1236,9 +1249,12 @@ public class Parser { ? Field.make(initName,sig,_code.alias(_ref._cname),true) : oldFld.makeFrom(sig); Field ctorFld = ts==null ? null : ts.field("<ctor>"); + // A same-named struct shares its file's class object and flag. + Field flag = ts==null ? null : ts.field("$init"); ts = ctorFld==null ? TypeStruct.make(clzName,false,fld) : TypeStruct.make(clzName,false,fld,ctorFld); + if( flag!=null ) ts=ts.add(flag); TypeFunPtr alloc = makeAllocator((TypeStruct)TYPES.get(fullName),ret,ctorFld); ts = ts.add(Field.make("<new>",alloc,_code.alias(_ref._cname),true)); TYPES.put(clzName,ts); @@ -2315,7 +2331,7 @@ public class Parser { Kind.Func func = (Kind.Func)_scope._kinds.at(_scope.enclosingFuncOrDecl()); if( (ts==null || ts==TypeStruct.BOT || ts.isHigh() || startsClzPrefix(ts._name)) && !(ts!=null && (ts._name+".<clinit>").equals(func._name)) ) - ctrl(new CLInitNode(site,ctrl(),expr).peephole()); + classInit(site,expr); // With no field, we will update the bulk memory int alias = fld==null ? 1 : fld._alias; @@ -2578,6 +2594,22 @@ public class Parser { private ConstantNode parseLiteral() { return con(_lexer.parseNumber()); } ConstantNode con( long con ) { return _code.con(con); } ConstantNode con( Type t ) { return _code.con(t ); } + private void classInit(Lexer site, Node ptr) { + // Ordinary load machinery resolves the flag alias and offset when + // the receiver is still a forward reference. + Field flag = ptr._type instanceof TypeMemPtr tmp ? tmp._obj.field("$init") : null; + int alias = flag==null ? 1 : flag._alias; + Node ready = new LoadNode(site,"$init",alias,null,mem(),ptr,fldoff(ptr,"$init")).peephole(); + CLInitNode init = new CLInitNode(site,ctrl(),mem(),ptr,ready).peephole().keep(); + ctrl(new CProjNode(init,0,ScopeNode.CTRL).peephole()); + Node bulk = new ProjNode(init,1,ScopeNode.MEM0).peephole().keep(); + flag = init.flag(); + mem(flag==null ? bulk : mergeAlias(bulk,flag._alias, + new ProjNode(init,2,memName(flag._alias)).peephole())); + bulk.unkeep(); + init.unkeep(); + } + // Field offset for a known type (but unknown offset) public static ConFldOffNode off( TypeStruct ts, String fname ) { return (ConFldOffNode)(new ConFldOffNode(ts,fname).peephole()); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java index df3be2d0..6c70b71d 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java @@ -34,7 +34,7 @@ public class ClassInitDependencies { // Build once; tests can inspect the graph independently of cycle checking. public ClassInitDependencies( CodeGen code ) { _code = code; - assert code._phase.ordinal() >= CodeGen.Phase.Opto.ordinal(); + assert code._phase.ordinal() >= CodeGen.Phase.IterPostOpto.ordinal(); _pres = new int[code._linker._len]; _lows = new int[code._linker._len]; _stk = new int[code._linker._len]; diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index ff0be213..3909f881 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -28,7 +28,9 @@ public class CodeGen { public enum Phase { Parse, // Parse ASCII text into Sea-of-Nodes IR Iter, // Pessimistic peepholes after parse - Opto, // Run ideal optimizations + Opto, // SCCP, numeric modes, seed the peephole worklist + IterPostOpto, // Peepholes and inlining using SCCP results + FinalizeIR, // Field layout, offset cleanup, function/CU roots TypeCheck, // Last check for bad programs LoopTree, // Build a loop tree; break infinite loops Serialize, // Serialize public IR for future compiles to link @@ -97,8 +99,7 @@ public class CodeGen { return null; } - // True when fidx is published through a public field of a public class. - // TypeStruct.fidxs() already excludes private (underscore) fields. + // True when fidx is published through a public class or instance field. public boolean publicFIDX(int fidx) { if( fidx < GlobalBits.RESERVED ) return false; // A public class initializer is itself an external root; unlike @@ -106,19 +107,21 @@ public class CodeGen { FunNode fun = _linker.atX(fidx); if( fun != null && fun.isClz() && fun.isPublic() ) return true; - // Constructors and methods live on their class objects, including - // nested classes which do not have their own CompUnit. + // Include instance methods even while their constant function fields + // are stored in each instance rather than on the class object. for( Type t : Parser.TYPES.values() ) if( t instanceof TypeStruct clz && Parser.startsClzPrefix(clz._name) && Parser.isPublicClassName(clz._name) && - resolvedClass(clz) && - publicClassFIDX(clz,fidx) ) - return true; + resolvedClass(clz) ) { + if( publicFieldFIDX(clz,fidx) ) return true; + Type self = Parser.TYPES.get(clz._name.substring(Parser.CLZ.length())); + if( self instanceof TypeStruct ts && publicFieldFIDX(ts,fidx) ) return true; + } return false; } - private static boolean publicClassFIDX(TypeStruct clz, int fidx) { + private static boolean publicFieldFIDX(TypeStruct clz, int fidx) { boolean hasCtor = clz.field("<ctor>") != null; for( Field fld : clz._fields ) if( fld._fname.charAt(0)!='_' && fld._t instanceof TypeFunPtr tfp && @@ -182,7 +185,7 @@ public class CodeGen { public final XCtrlNode XCTRL; public Node constant( Type t ) { - assert _phase==null || _phase.ordinal() <= Phase.Opto.ordinal(); + assert _phase==null || _phase.ordinal() <= Phase.FinalizeIR.ordinal(); return ConstantNode.make(t).peephole(); } public ConstantNode con( Type t ) { return (ConstantNode)constant(t); } @@ -259,6 +262,8 @@ public class CodeGen { if( p2 < p1 && p2 < Phase.Parse .ordinal() ) { parse(); p2 = dump(dump); } if( p2 < p1 && p2 < Phase.Iter .ordinal() ) { iter(); p2 = dump(dump); } if( p2 < p1 && p2 < Phase.Opto .ordinal() ) { opto(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.IterPostOpto.ordinal() ) { iterPostOpto(); p2 = dump(dump); } + if( p2 < p1 && p2 < Phase.FinalizeIR.ordinal() ) { finalizeIR(); p2 = dump(dump); } if( p2 < p1 && p2 < Phase.TypeCheck .ordinal() ) { typeCheck(); p2 = dump(dump); } if( p2 < p1 && p2 < Phase.LoopTree .ordinal() ) { loopTree(); p2 = dump(dump); } if( p2 < p1 && p1 >= Phase.Encoding .ordinal() ) { unlinkImports(); p2 = dump(dump); } @@ -520,10 +525,8 @@ public class CodeGen { } - // Run ideal optimizations + // SCCP and numeric-mode resolution; leave cleanup work queued for Iter. public CodeGen opto() { - if( _phase == Phase.Parse ) - iter(); assert _phase == Phase.Iter; _phase = Phase.Opto; long t0 = System.currentTimeMillis(); @@ -534,6 +537,60 @@ public class CodeGen { if( _obs != null ) _obs.phase(_phase.name()); return this; } + + public CodeGen iterPostOpto() { + assert _phase == Phase.Opto; + _phase = Phase.IterPostOpto; + long t0 = System.currentTimeMillis(); + // Use SCCP types and the sharp call graph for peepholes and inlining. + _iter.iterate(this); + _times[Phase.IterPostOpto.ordinal()] = System.currentTimeMillis() - t0; + if( _obs != null ) _obs.phase(_phase.name()); + return this; + } + + public CodeGen finalizeIR() { + assert _phase == Phase.IterPostOpto; + _phase = Phase.FinalizeIR; + long t0 = System.currentTimeMillis(); + + // Freeze field sizes and replace symbolic field offsets by constants. + for( int i=0; i<_start.nOuts(); i++ ) { + Node use = _start.out(i); + if( use instanceof ConFldOffNode off ) { + Node con = off.asOffset(); + assert con != null : off._ts._name+"."+off._fname; + off.subsume(con); + i--; + } + } + _iter.iterate(this); // Propagate field offsets and clean up. + + // Retain surviving functions for emission without adding unknown callers. + for( FunNode fun : _linker ) + if( fun != null && !fun.isDead() && !fun._type.isHigh() ) + keepInCompUnit(fun); + for( CompUnit cu : _compunits.values() ) { + ProjNode mem = cu._start.proj(1); + if( mem != null ) mem.unkeep(); + } + // Stable print order: initializers first, then other Returns by NID. + for( Node stop : _stop._inputs ) + Arrays.sort(stop._inputs._es,0,stop.nIns(), + (x,y) -> ((((ReturnNode)x).fun().isClz() ? -1 : x._nid) - + (((ReturnNode)y).fun().isClz() ? -1 : y._nid) )); + + _times[Phase.FinalizeIR.ordinal()] = System.currentTimeMillis() - t0; + if( _obs != null ) _obs.phase(_phase.name()); + return this; + } + + // Root a surviving function without claiming that it has unknown callers. + void keepInCompUnit(FunNode fun) { + StopCUNode stop = fun._compunit._stop; + if( stop._inputs.find(fun.ret()) == -1 ) + add(stop).addDef(fun.ret()); + } public <N extends Node> N add( N n ) { return _iter.add(n); } public void addAll( Ary<Node> ary ) { _iter.addAll(ary); } public void addAll( Node n ) { _iter.add(n); _iter.addAll(n._inputs); } @@ -541,9 +598,7 @@ public class CodeGen { // --------------------------- // Last check for bad programs public CodeGen typeCheck() { - // Demand phase Opto for cleaning up dead control flow at least, - // required for the following GCM. - assert _phase.ordinal() <= Phase.Opto.ordinal(); + assert _phase == Phase.FinalizeIR; _phase = Phase.TypeCheck; long t0 = System.currentTimeMillis(); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index 8eebc15b..33d971e9 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -276,6 +276,10 @@ public abstract class GlobalCodeMotion { assert late[call._nid]!=null; lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call,anti); break; + case CLInitNode init: + assert late[init._nid]!=null; + lca = anti_dep(load,late[init._nid],init.cfg0(),lca,init,anti); + break; case PhiNode phi: // Repeat anti-dep for matching Phi inputs. // No anti-dep edges but may raise the LCA. diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java index efdcff6c..c41a29ac 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java @@ -3,9 +3,7 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Ary; -import com.seaofnodes.simple.util.Utils; -import java.util.Arrays; import java.util.BitSet; // Optimistic Algorithm @@ -45,36 +43,6 @@ abstract public class Opto { // After SCCP add everything changed to the worklist and see if // peepholes can make more progress. moveChangesToWorklist(code, oldTypes); - - // Progress with improved types. E.g. since we have a full Call Graph - // here, we might do better inlining. - code._iter.iterate(code); - - // TODO: - // loop unroll, peel, RCE, etc - - // Freeze field sizes; do struct layouts; convert field offsets into - // constants. - fieldSizes(code); - // Propagate field size constants - code._iter.iterate(code); - - // Retain surviving functions in their CompUnits for later code - // emission. Start inputs are semantic: they mean unknown callers. - for( FunNode fun : code._linker ) - if( fun != null && !fun.isDead() && !fun._type.isHigh() ) - keepInCompUnit(code,fun); - - for( CompUnit cu : code._compunits.values() ) { - ProjNode mem = cu._start.proj(1); - if( mem != null ) mem.unkeep(); - } - - // To help with testing, sort StopNode Returns by NID - for( Node stop : code._stop._inputs ) - Arrays.sort(stop._inputs._es,0,stop.nIns(), - (x,y) -> ((((ReturnNode)x).fun().isClz() ? -1 : x._nid) - - (((ReturnNode)y).fun().isClz() ? -1 : y._nid) )); } // Reset Types before running the worklist algorithm @@ -178,7 +146,7 @@ abstract public class Opto { if( fun.nIns() < 2 ) { assert fun.nIns() < 2 || fun.in(1) != start; assert !start.isDead(); - keepInCompUnit(code,fun); + code.keepInCompUnit(fun); // Function is reachable by any *remote* caller who gets the pointer! fun.insertDef(1,start); code._iter.add(fun); @@ -191,14 +159,6 @@ abstract public class Opto { } } - // Keep a surviving function rooted in its compilation unit without - // claiming that it has unknown callers. - private static void keepInCompUnit(CodeGen code, FunNode fun) { - StopCUNode stop = fun._compunit._stop; - if( stop._inputs.find(fun.ret()) == -1 ) - code.add(stop).addDef(fun.ret()); - } - // The core SCCP algorithm private static void sccp( CodeGen code, Ary<Type> oldTypes ) { @@ -353,19 +313,4 @@ abstract public class Opto { return evidence; } - - - // Freeze field sizes; do struct layouts; convert field offsets into - // constants. - private static void fieldSizes( CodeGen code ) { - for( int i=0; i<code._start.nOuts(); i++ ) { - Node use = code._start.out(i); - if( use instanceof ConFldOffNode off ) { - Node con = off.asOffset(); - assert con != null; // Can be null for trying to reference missing field - off.subsume( con ); - i--; - } - } - } } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java index bc47ebfc..92ebd587 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java @@ -55,6 +55,9 @@ public abstract class ArithNode extends Node implements ModeNode { if( n instanceof ModeNode mode && mode.mode()==0 ) { mode.setMode((byte)1); n.setType(n.compute()); + // Mode changes remove n from GVN even if the caller's remaining + // conditions prevent replacing the int-only operation by n. + CodeGen.CODE.add(n); } return true; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java index 82c4af10..850a5711 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java @@ -160,7 +160,7 @@ public class BulkMemPhiNode extends PhiNode { case MemPhiNode phi -> unsplit(phi._alias); case EscapeNode esc -> esc.pub()==this ? unsplit(esc.fld()._alias) : 0; default -> { - assert use instanceof ReturnNode || use instanceof CallNode + assert use instanceof ReturnNode || use instanceof CallNode || use instanceof CLInitNode : "Unexpected bulk-memory user "+use.getClass().getSimpleName()+"#"+use._nid+": "+use; yield 0; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java index fe3382a6..4f225fbe 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -109,6 +109,7 @@ public abstract class CFGNode extends Node { } private CFGNode _safeCFG() { return switch(this) { + case CLInitNode init when init.bypass() -> init.cfg0()._safeCFG(); case CallNode call when call.cend()._folding -> call.cfg0()._safeCFG(); case CallEndNode cend when cend._folding -> cend.cfg(1)._safeCFG(); case FunNode fun when fun._folding -> fun.cfg(1)._safeCFG(); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java index c9aeb874..9a41cc18 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java @@ -6,23 +6,24 @@ import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.codegen.CompUnit; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.BAOS; -import com.seaofnodes.simple.util.Utils; import java.util.*; // Track class initializers - the code just after this point expects the named // class to be initialized. If this code path dies, then the dependency dies // also - dead code does not force class init. // -public class CLInitNode extends CFGNode { +public class CLInitNode extends CFGNode implements MultiNode { public Parser.Lexer _loc; // For error reporting + // Inputs: control, whole memory, class pointer, loaded $init flag + // Results: control, whole memory, known-true flag alias memory. // For field references, the ptr might be the dst clz ptr or not. - public CLInitNode(Parser.Lexer loc, Node ctrl, Node ptr) { - super(ctrl,ptr); + public CLInitNode(Parser.Lexer loc, Node ctrl, Node mem, Node ptr, Node ready) { + super(ctrl,mem,ptr,ready); _loc = loc; } - CLInitNode(BAOS bais, String[] strs) { super(null,null); _loc=Parser.Lexer.unpack(bais,strs); } + CLInitNode(BAOS bais, String[] strs) { super(null,null,null,null); _loc=Parser.Lexer.unpack(bais,strs); } @Override public Tag serialTag() { return Tag.CLInit; } @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { Parser.Lexer.packed(_loc,baos,strs); @@ -34,7 +35,15 @@ public class CLInitNode extends CFGNode { _loc = init._loc; } - Node ptr() { return in(1); } + public Node mem() { return in(1); } + public Node ptr() { return in(2); } + public Node ready() { return in(3); } + + // The field/alias can be unresolved until forward references are loaded. + public Field flag() { + return ptr()._type instanceof TypeMemPtr tmp && Parser.startsClzPrefix(tmp._obj._name) + ? tmp._obj.field("$init") : null; + } @Override public String label() { return "CLInit"; } // Best effort dst compunit, based on ptr - or null @@ -55,14 +64,51 @@ public class CLInitNode extends CFGNode { // Block head is Start, Region, CProj, but not e.g. If, Return, Stop public boolean blockHead() { return true; } - @Override public Type compute() { return in(0)._type; } - @Override public Node idealize() { - // A receiver discovered to be an ordinary instance needs no class init. - if( ptr()._type instanceof TypeMemPtr ptr && ptr._obj!=TypeStruct.BOT && - !ptr._obj.isHigh() && !Parser.startsClzPrefix(ptr._obj._name) ) - return in(0); // Normal instance, no class init - if( ptr()._type == Type.NIL || ptr()._type.isHigh() ) - return in(0); - return null; + private boolean instance() { + return ptr()._type==Type.NIL || + (ptr()._type instanceof TypeMemPtr tmp && tmp._obj!=TypeStruct.BOT && + !tmp._obj.isHigh() && !tmp._obj._open && !tmp._obj._fref && + !Parser.startsClzPrefix(tmp._obj._name)); + } + + public boolean bypass() { return instance() || ready()._type==TypeInteger.TRUE; } + + @Override public Type compute() { + if( in(0)._type.isHigh() || ptr()._type.isHigh() ) + return TypeTuple.make(Type.XCONTROL,TypeMem.TOP,TypeMem.TOP); + if( instance() ) + return TypeTuple.make(in(0)._type,mem()._type,mem()._type); + Field flag = flag(); + Type slice = flag==null ? TypeMem.BOT : TypeMem.make(flag._alias,TypeInteger.TRUE); + // The initializer may write arbitrary public memory. Only its flag + // has a guaranteed value on the normal continuation. + Type bulk = ready()._type.isHigh() ? TypeMem.TOP : + ready()._type==TypeInteger.TRUE ? mem()._type : TypeMem.BOT; + return TypeTuple.make(in(0)._type,bulk,slice); + } + + @Override public Node pcopy(int idx, Node dep) { + dep.addDep(ptr()); + dep.addDep(ready()); + if( instance() ) return idx==0 ? in(0) : mem(); + if( ready()._type!=TypeInteger.TRUE ) return null; + if( idx==0 ) return in(0); + if( idx==1 ) return mem(); + // Already established on the incoming path; this is a memory fact, + // not a new store to the class object. + return CodeGen.CODE.con(((TypeTuple)_type)._types[2]); + } + + @Override public Node idealize() { return null; } + + // Resolve a precise flag slice even when the parser did not yet know the + // class/alias and could only install the whole-memory projection. + static Node slice(Node mem, int alias, Node dep) { + if( !(mem instanceof ProjNode proj && proj._idx==1 && proj.in(0) instanceof CLInitNode init) ) + return mem; + dep.addDep(init.ptr()); + Field flag = init.flag(); + if( flag==null || flag._alias!=alias ) return mem; + return new ProjNode(init,2,Parser.memName(alias)).peephole(); } } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java index 12fde6c0..073e4658 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java @@ -62,7 +62,7 @@ public class CProjNode extends CFGNode implements Proj { return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True"); // Copy of some other input - return ((MultiNode)ctrl()).pcopy(_idx); + return ((MultiNode)ctrl()).pcopy(_idx,this); } // Only called during basic-block layout, inverts a T/F CProj 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 13e7ac12..555d0c65 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -302,7 +302,7 @@ public class CallEndNode extends CFGNode implements MultiNode { } - @Override public Node pcopy(int idx) { + @Override public Node pcopy(int idx, Node dep) { return _folding ? in(1).in(idx) : null; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java index 47a7bbf6..4357570e 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java @@ -37,13 +37,13 @@ public class ConstantNode extends TypeNode { } static Node make( BAOS bais, Type[] types) { return raw(types[bais.packed2()]); } - // Semantic constant construction through Opto. A unique internal + // Semantic constant construction through FinalizeIR. A unique internal // function identity carries a lifetime edge to the function's Return. public static Node make( Type type ) { if( type==Type. CONTROL ) return new CtrlNode(); if( type==Type.XCONTROL ) return new XCtrlNode(); if( CodeGen.CODE._phase != null && - CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + CodeGen.CODE._phase.ordinal() > CodeGen.Phase.FinalizeIR.ordinal() ) return raw(type); if( type instanceof TypeFunPtr tfp && tfp.isConstant() ) { FunNode fun = CodeGen.CODE._link(tfp); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java index b6c3b0c9..cdc890a7 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java @@ -30,10 +30,10 @@ public class ConvertNode extends Node { } // The declaration fixes the result family during pessimistic parsing. - // Optimistic SCCP must retain stronger facts already inside that family. + // From SCCP onward, preserve dead inputs and stronger facts in that family. @Override public Type compute() { Type src = val()._type; - if( CodeGen.CODE._phase == CodeGen.Phase.Opto ) { + if( CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal() ) { if( src.isHigh() ) return Type.TOP; if( (src.isa(_dst) && src.accessISA(_dst)) ) return src; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java index a557f97c..0a997083 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -47,7 +47,8 @@ public class LoadNode extends MemOpNode { dep.addDepForwards(use); if( use instanceof StoreNode || use instanceof PhiNode || use instanceof MemMergeNode || - use instanceof NewNode || use instanceof EscapeNode || use instanceof CallNode ) return true; + use instanceof NewNode || use instanceof EscapeNode || use instanceof CallNode || + use instanceof CLInitNode ) return true; } return false; } @@ -134,6 +135,9 @@ public class LoadNode extends MemOpNode { if( _alias == 1 ) return null; + Node slice = CLInitNode.slice(mem,_alias,this); + if( slice!=mem ) { setDef(1,slice); return this; } + if( mem instanceof MemMergeNode merge ) { setDef(1,merge.alias(_alias)); // In support of uplifting control to a prior dominating load, if diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index 373b8825..b31e14d5 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -130,10 +130,10 @@ public abstract class MemOpNode extends TypeNode { if( !(ptr instanceof TypeMemPtr tmp && tmp.notNull()) ) return Parser.error( "Might be null accessing '" + _name + "'",_loc); if( tmp._obj.field(_name)==null && - CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + CodeGen.CODE._phase.ordinal() > CodeGen.Phase.FinalizeIR.ordinal() ) return Parser.error("Accessing unknown field '"+_name+"' from '*"+tmp._obj._name+"'",_loc); // Sane field - if( off() instanceof ConFldOffNode && CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) + if( off() instanceof ConFldOffNode && CodeGen.CODE._phase.ordinal() > CodeGen.Phase.FinalizeIR.ordinal() ) return Parser.error("Accessing unknown field '"+_name+"' from '*"+tmp._obj._name+"'",_loc); return null; } @@ -154,7 +154,7 @@ public abstract class MemOpNode extends TypeNode { Node slice = _alias<merge.nIns() ? merge.in(_alias) : null; if( (slice==mem) || (slice==null && merge.in(1)==mem) ) antiDeps(merge,deps,visit); - } else if( use instanceof CallNode || + } else if( use instanceof CallNode || use instanceof CLInitNode || (use instanceof MemOpNode st && !st._isLoad && st._alias==_alias) || (use instanceof EscapeNode esc && esc.fld()._alias==_alias) || (use instanceof MemPhiNode phi && phi._alias==_alias) || diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java index cdfaaf97..aaaedb89 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java @@ -56,11 +56,14 @@ public class MemPhiNode extends PhiNode { public Node idealize() { // A precise memory Phi can always bypass a MemMerge and select its // matching slice. - for( int i=1; i<nIns(); i++ ) + for( int i=1; i<nIns(); i++ ) { if( in(i) instanceof MemMergeNode mem ) { setDef(i,mem.alias(_alias)); return this; } + Node slice = CLInitNode.slice(in(i),_alias,this); + if( slice!=in(i) ) { setDef(i,slice); return this; } + } if( nIns()==3 && region() instanceof LoopNode loop && !loop.inProgress() && in(2) instanceof StoreNode st && st.mem()==this ) { Node sink = st.sink(this); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java index b4d6d78e..152d525a 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java @@ -25,5 +25,5 @@ public interface MultiNode { // Return not-null if this projection index is a ideal copy. // Called by ProjNode ideal and used to collapse Multis. - default Node pcopy(int idx) { return null; } + default Node pcopy(int idx, Node dep) { return null; } } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java index 4b29cc3c..fcc113a8 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java @@ -55,7 +55,7 @@ public class ProjNode extends Node implements Proj { return t instanceof TypeTuple tt ? tt._types[_idx] : t.oob(); } - @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx); } + @Override public Node idealize() { return ((MultiNode)in(0)).pcopy(_idx,this); } @Override public boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java index ab0f5da6..f2bd5841 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java @@ -40,7 +40,8 @@ public class StopCUNode extends StopNode { // can widen from a precise set to FULL; the public-only normalization // below must not forget private bits already discovered precisely from // that Return on an earlier SCCP step. - if( CodeGen.CODE._phase == CodeGen.Phase.Opto && + if( CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal() && + CodeGen.CODE._phase.ordinal() <= CodeGen.Phase.FinalizeIR.ordinal() && _type instanceof TypeTuple old && old.mem() instanceof TypeMem omem ) xmem = xmem.makeFrom(XInt.meet(xmem._escFs,omem._escFs), XInt.meet(xmem._escAs,omem._escAs)); @@ -100,7 +101,8 @@ public class StopCUNode extends StopNode { // precise finite sets. Replacing FULL early there would let a later // input refinement add bits, reversing Iter's monotonic direction. // This normalization is needed only for Opto's optimistic grand cycle. - if( CodeGen.CODE._phase != CodeGen.Phase.Opto ) return mem; + if( CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() || + CodeGen.CODE._phase.ordinal() > CodeGen.Phase.FinalizeIR.ordinal() ) return mem; int[] escFs = XInt.isHigh(mem._escFs) ? XInt.meet(prior._escFs,CodeGen.CODE.publicFIDXs()) : mem._escFs; diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 4917e890..418b1e92 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -304,7 +304,7 @@ public class StoreNode extends MemOpNode { public Parser.ParseException err() { Parser.ParseException err = super.err(); if( err != null ) return err; - assert _size>0 || CodeGen.CODE._phase.ordinal() <= CodeGen.Phase.Opto.ordinal(); + assert _size>0 || CodeGen.CODE._phase.ordinal() <= CodeGen.Phase.FinalizeIR.ordinal(); if( ptr()._type == Type.TOP ) // Dead store? return null; TypeMemPtr tmp = (TypeMemPtr)ptr()._type; diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index 660f1559..8c9bed95 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -305,6 +305,7 @@ public class arm extends Machine { case PtrToIntNode ptr -> new PtrToIntMach(ptr); case GuardNode guard -> new GuardMach(guard); case CallEndNode cend-> new CallEndMach(cend); + case CLInitNode init -> new CLInitNode(init); case CProjNode c -> new CProjNode(c); case ConstantNode con-> con(con); case DivNode div -> div.mode()==2 ? new DivFARM(div) : new DivARM(div); @@ -459,6 +460,7 @@ public class arm extends Machine { } private Node prj(ProjNode prj) { + if( prj.in(0) instanceof CLInitNode ) return new ProjNode(prj); TypeInteger ret = cReturn(prj); if( ret!=null ) return new ExtendARM(prj,ret); return prj.in(0) instanceof StartNode ? new ProjNode(prj) : new ProjARM(prj); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index 19b91866..0195542c 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -218,6 +218,7 @@ public class riscv extends Machine { case PtrToIntNode ptr -> new PtrToIntMach(ptr); case GuardNode guard -> new GuardMach(guard); case CallEndNode cend -> new CallEndMach(cend); + case CLInitNode init -> new CLInitNode(init); case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); case DivNode div -> div.mode()==2 ? new DivFRISC(div) : new DivRISC(div); @@ -412,6 +413,7 @@ public class riscv extends Machine { } private Node prj(ProjNode prj) { + if( prj.in(0) instanceof CLInitNode ) return new ProjNode(prj); TypeInteger ret = cReturn(prj); if( ret!=null ) return new ExtendRISC(prj,ret); return prj.in(0) instanceof StartNode ? new ProjNode(prj) : new ProjRISC(prj); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index 23c4d530..7b351533 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -491,6 +491,7 @@ public class x86_64_v2 extends Machine { } private Node prj( ProjNode prj ) { + if( prj.in(0) instanceof CLInitNode ) return new ProjNode(prj); TypeInteger ret = cReturn(prj); if( ret!=null ) return new ExtendX86(prj,ret); return prj.in(0) instanceof StartNode ? new ProjNode(prj) : new ProjX86(prj); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java index bf8403e1..e9231d9a 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java @@ -17,7 +17,7 @@ public class SimpleGraphObserver extends GraphCapture<Node> { @Override protected void compile(String src) { _code = new CodeGen(src); _code._obs = this; - _code.parse().opto().typeCheck(); + _code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); } @Override protected String phase() { return _code._phase.name(); } @Override protected int pos() { return _code._phase == CodeGen.Phase.Parse ? _code.P.pos() : -1; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java index 82c37a12..4c5ae6ea 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java @@ -597,7 +597,7 @@ public class TypeStruct extends Type { return _offs[idx]; } private int[] offsets() { // Field byte offsets - assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal(); + assert CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.FinalizeIR.ordinal(); // Compute a layout for a collection of fields assert _fields != null; // No forward refs |
