From 0e94cdc0c481e9312d1b5033b3597c7c262ff878 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 12 Oct 2026 16:46:53 -0700 Subject: Check for cyclic class init before we emit code --- .../com/seaofnodes/simple/node/CLInitNode.java | 58 ++++++++++++++++++---- .../java/com/seaofnodes/simple/node/CallNode.java | 12 +++-- .../com/seaofnodes/simple/node/ConFldOffNode.java | 4 ++ .../com/seaofnodes/simple/node/FldOffNode.java | 6 +++ .../java/com/seaofnodes/simple/node/FunNode.java | 2 + .../java/com/seaofnodes/simple/node/MemOpNode.java | 1 - .../java/com/seaofnodes/simple/node/NewNode.java | 9 +++- .../main/java/com/seaofnodes/simple/node/Node.java | 23 +++++---- .../java/com/seaofnodes/simple/node/ScopeNode.java | 3 +- .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 1 + 10 files changed, 89 insertions(+), 30 deletions(-) (limited to 'chapter25/src/main/java/com/seaofnodes/simple/node') 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 f00d8076..5131049e 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java @@ -2,29 +2,65 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; import com.seaofnodes.simple.Parser; +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.*; -/** An active class touch, retained in control until initialization validation. - * This checkpoint records the obligation only; flag/call lowering comes later. - */ +// 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 final Parser.Lexer _loc; - public final String _field; + public Parser.Lexer _loc; // For error reporting - public CLInitNode(Parser.Lexer loc, String field, Node ctrl, Node ptr) { + // 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); - _loc=loc; - _field=field; + _loc = loc; } - public Node ptr() { return in(1); } + CLInitNode(BAOS bais) { super(null,null); } + @Override public Tag serialTag() { return Tag.CLInit; } + @Override public void packed( BAOS baos, HashMap strs, HashMap types, IdentityHashMap anodes ) { + } + + public CLInitNode(CLInitNode init) { + super(init); + _loc = init._loc; + } + + Node ptr() { return in(1); } @Override public String label() { return "CLInit"; } - @Override protected ExprPrinter _print1(ExprPrinter p) { return p.p("CLInit ").p(ptr()); } + + // Best effort dst compunit, based on ptr - or null + public CompUnit cu() { + if( !(ptr()._type instanceof TypeMemPtr ptr && Parser.startsClzPrefix(ptr._obj._name)) ) + return null; + // Convert "class:sys.ary" into "sys/ary" and lookup CompUnit + String fname = ptr._obj._name.substring(6).replace(".","/"); + return CodeGen.CODE._compunits.get(fname); + } + + @Override protected ExprPrinter _print1(ExprPrinter p) { + FunNode fun = fun(); + CompUnit cu = cu(); + return p.p("CLInit ").p(fun._name).p(" -> ").p(cu==null ? "---" : cu._cname); + } + + // 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); + !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; } } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java index 4372e2d5..38117f23 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java @@ -22,11 +22,17 @@ public class CallNode extends CFGNode { // Source location for late reported errors public final Parser.Lexer _loc; - public CallNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } + public CallNode(Parser.Lexer loc, Node... nodes) { this(loc,loc==null ? 0 : loc.lineColumn(),nodes); } + private CallNode(Parser.Lexer loc, long position, Node... nodes) { super(nodes); _loc=loc; } public CallNode(CallNode call) { super(call); _loc = call._loc; } @Override public Tag serialTag() { return Tag.Call; } - public void packed( BAOS baos, HashMap strs, HashMap types, IdentityHashMap anodes ) { baos.packed1(nIns()); } - static Node make( BAOS bais ) { return new CallNode(null,new Node[bais.packed1()]); } + public void packed( BAOS baos, HashMap strs, HashMap types, IdentityHashMap anodes ) { + baos.packed1(nIns()); + } + static Node make( BAOS bais ) { + int nIns = bais.packed1(); + return new CallNode(null,new Node[nIns]); + } @Override protected ExprPrinter _print1(ExprPrinter p) { String fname = name(); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java index f94fa484..284220ea 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConFldOffNode.java @@ -71,4 +71,8 @@ public class ConFldOffNode extends ConstantNode { return _ts._name==off._ts._name && _fname.equals(off._fname) && super.eq(n); } @Override int hash() { return super.hash() * _ts._name.hashCode() * _fname.hashCode(); } + + @Override public Parser.ParseException err() { + return _ts._fref ? Parser.error("Unknown struct type '"+_ts._name+"'",null) : null; + } } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java index ef894929..a9f8d692 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FldOffNode.java @@ -45,4 +45,10 @@ public class FldOffNode extends TypeNode { return _fname.equals(off._fname) && super.eq(n); } @Override int hash() { return super.hash() * _fname.hashCode(); } + + @Override public Parser.ParseException err() { + if( in(0)._type instanceof TypeMemPtr tmp && tmp._obj._fref ) + return Parser.error("Unknown struct type '"+tmp._obj._name+"'",null); + return null; + } } 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 1e2ee10a..7f9a2819 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -49,6 +49,7 @@ public class FunNode extends RegionNode { baos.packed2(types.get(_sig)); // NPE if fails lookup baos.packed2(_name==null ? 0 : strs.get(_name)); baos.packed2(_approxUIDs); + // _compunit set from context on loading, do not write } static Node make( BAOS bais, String[] strs, Type[] types) { Node[] ins = new Node[bais.packed1()]; @@ -56,6 +57,7 @@ public class FunNode extends RegionNode { String name = strs[bais.packed2()]; FunNode fun = new FunNode(null,sig,name,null,ins); fun._approxUIDs = bais.packed2(); + // _compunit set from context on loading, do not read return fun; } 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 365157d2..373b8825 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -136,7 +136,6 @@ public abstract class MemOpNode extends TypeNode { if( off() instanceof ConFldOffNode && CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() ) return Parser.error("Accessing unknown field '"+_name+"' from '*"+tmp._obj._name+"'",_loc); return null; - } // Memory writers which must follow this read. A packaging node is not a diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java index 5eedd0cd..fe80f690 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -1,12 +1,11 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; - +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.BAOS; import com.seaofnodes.simple.util.SB; - import java.util.HashMap; import java.util.IdentityHashMap; @@ -64,6 +63,12 @@ public class NewNode extends Node implements MultiNode { @Override int hash() { return _ts.hashCode(); } + + @Override public Parser.ParseException err() { + return _ts._fref ? Parser.error("Unknown struct type '"+_ts._name+"'",null) : null; + } + + // ------------ // MachNode specifics, shared across all CPUs public int _arg2Reg, _xslot; diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java index 23a7e2c4..796965b5 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/Node.java @@ -70,20 +70,21 @@ public abstract class Node extends BaseNode implements Cloneable { // Disk/serialized opcode tags public enum Tag { - Add,And,EQ,NE,LT,LE,ULT, - CallEnd,Call,ConFldOff,Con,CProj, - Div,Escape,Extern,Fun,FunPtr,If,Load,Loop, - MemMerge,Minus,Mul, - New,Never,Not,Or,Parm,Phi,Proj, + Add,And,BulkMemPhi,EQ,NE,LT,LE,ULT, + CallEnd,Call,ConFldOff,Con,CLInit,CProj, + Div,Escape,Extern,ExternOffset,Fun,FunPtr,Guard, + If,Load,Loop,MemMerge,MemPhi,Minus,Mul, + New,Never,Not,Or,Parm,Phi,Proj,PtrToInt, ReadOnly,Return,Region,RoundF32, - Sar,Shl,Shr,Start,Stop,Store,Sub,ToFloat, - XCtrl,Xor,StartCU,StopCU,Guard,MemPhi,BulkMemPhi,PtrToInt,ExternOffset,ToInteger; + Sar,Shl,Shr,Start,StartCU,StopCU,Stop,Store,Sub, + ToInteger,ToFloat,XCtrl,Xor; public static final Tag[] VALS = values(); public Node make( BAOS bais, String[] strs, Type[] types, GlobalBits fileAliases, GlobalBits aliases ) { return switch(this) { case Add -> new AddNode(null,null,(byte)bais.packed1()); case And -> { int mode=bais.packed1(); assert mode==1; yield new AndNode(null,null,null); } case Div -> new DivNode(null,null,(byte)bais.packed1()); + case Guard -> GuardNode.make(bais); case If -> new IfNode(null,null); case Loop -> new LoopNode(null,null,null); case Minus -> new MinusNode(null,(byte)bais.packed1()); @@ -104,7 +105,6 @@ public abstract class Node extends BaseNode implements Cloneable { case ToInteger-> new ToIntegerNode(null); case XCtrl -> new XCtrlNode(); case Xor -> { int mode=bais.packed1(); assert mode==1; yield new XorNode(null,null,null); } - case Guard -> GuardNode.make(bais); case EQ -> new BoolNode.EQ (null,null,(byte)bais.packed1()); case NE -> new BoolNode.NE (null,null,(byte)bais.packed1()); @@ -114,23 +114,24 @@ public abstract class Node extends BaseNode implements Cloneable { case Call -> CallNode.make(bais); case CallEnd-> CallEndNode.make(bais ,types); + case CLInit -> new CLInitNode(bais); case CProj -> CProjNode.make(bais,strs); case Con -> ConstantNode.make(bais ,types); case Escape-> EscapeNode.make(bais ,types); case Extern-> ExternNode.make(bais,strs,types); case ExternOffset-> new ExternOffsetNode(strs[bais.packed2()]); - case FunPtr-> FunPtrNode.make(bais, types); + case FunPtr-> FunPtrNode.make(bais, types); case Fun -> FunNode.make(bais,strs,types); case Load -> new LoadNode(bais,strs,types,fileAliases,aliases); case MemMerge->MemMergeNode.make(bais); - case MemPhi-> MemPhiNode.make(bais,strs,types,fileAliases,aliases); + case MemPhi-> MemPhiNode.make(bais,strs,types,fileAliases,aliases); case BulkMemPhi-> BulkMemPhiNode.make(bais,strs,types,fileAliases,aliases); case New -> NewNode.make(bais ,types); case Parm -> ParmNode.make(bais,strs,types); case Phi -> PhiNode.make(bais,strs,types); case Proj -> ProjNode.make(bais,strs); case Store -> new StoreNode(bais,strs,types,fileAliases,aliases); - case Stop -> StopNode.make(bais); + case Stop -> StopNode.make(bais); case StartCU-> StartCUNode.make(bais,strs,types); case StopCU -> StopCUNode.make(bais); case Region-> RegionNode.make(bais); diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index 92cad745..21d60d53 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -1,12 +1,11 @@ package com.seaofnodes.simple.node; import com.seaofnodes.print.ExprPrinter; - import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Var; import com.seaofnodes.simple.codegen.CodeGen; -import com.seaofnodes.simple.codegen.ParseAll; import com.seaofnodes.simple.codegen.CompUnit; +import com.seaofnodes.simple.codegen.ParseAll; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.Ary; import com.seaofnodes.simple.util.Utils; 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 ff15fdad..23c4d530 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 @@ -282,6 +282,7 @@ public class x86_64_v2 extends Machine { case AddNode add -> add(add); case AndNode and -> and(and); case BoolNode bool -> cmp(bool); + case CLInitNode init -> new CLInitNode(init); case CProjNode c -> new CProjNode(c); case CallEndNode cend -> new CallEndMach(cend); case CallNode call -> call(call); -- cgit v1.2.3