summaryrefslogtreecommitdiff
path: root/chapter28b/src/main/java
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-28 09:30:15 -0700
committerCliff Click <cliffc@acm.org>2026-10-28 09:30:15 -0700
commit696adef058d43695ac1c47a7223d9e7a8b8ab6ea (patch)
tree956dc21e7c2bdb1386da7b5451dd4f252d829456 /chapter28b/src/main/java
parent76a7ee6eaf6963b044cb5d149c916c55ae4d24f7 (diff)
downloadSimple-696adef058d43695ac1c47a7223d9e7a8b8ab6ea.tar.gz
Simple-696adef058d43695ac1c47a7223d9e7a8b8ab6ea.zip
Another round of extra _phase changes removed
Also removes loading more code to resolve cyclic class dependencies, and instead rejects some of the more exciting cases
Diffstat (limited to 'chapter28b/src/main/java')
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java10
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java21
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java163
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java8
-rw-r--r--chapter28b/src/main/java/com/seaofnodes/simple/node/StopNode.java3
5 files changed, 72 insertions, 133 deletions
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java
index d95f55f2..9a544685 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java
@@ -150,16 +150,6 @@ public class ClassInitDependencies {
return null;
}
- // A condition-producing helper outside the SCC can eliminate a cycle.
- // Before reporting one, restore the full-IR precision of the original check.
- // Called only after check() has found a cycle.
- Ary<CompUnit> cycleImports() {
- Ary<CompUnit> units = new Ary<>(CompUnit.class);
- for( FunNode fun : _code._linker )
- if( fun!=null && fun.summary()!=null && units.find(fun._compunit)==-1 ) units.add(fun._compunit);
- return units;
- }
-
// Every internal edge of an SCC belongs to a cycle. Print all of them:
// stack order is not a path, and enumerating all cycle paths can explode.
// This bookkeeping and formatting are only needed on an error.
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index ddcdba3c..e56068e7 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -523,28 +523,9 @@ public class CodeGen {
}
// TypeCheck saves its successful analysis for Serialize. Loading bodies
- // or entering another graph-changing phase discards it; no stale edges
- // survive a later Iter/FinalizeIR/TypeCheck sequence.
+ // or entering another graph-changing phase discards it.
ClassInitDependencies _classInitDependencies;
- ClassInitDependencies resolveInitCycles() {
- while( true ) {
- ClassInitDependencies deps = new ClassInitDependencies(this);
- String cycle = deps.check();
- if( cycle==null ) return deps;
- Ary<CompUnit> units = deps.cycleImports();
- if( units.isEmpty() ) throw Parser.error(cycle,null);
- for( CompUnit cu : units ) ParseAll.loadCompUnit(this,cu);
- Phase prior = _phase;
- try {
- _phase=Phase.IterPostOpto;
- _iter.iterate(this);
- _phase=Phase.FinalizeIR;
- FinalizeIR.run(this);
- } finally { _phase=prior; }
- }
- }
-
public CodeGen finalizeIR() { return phase(Phase.FinalizeIR, _phase==Phase.IterPostOpto, () -> FinalizeIR.run(this)); }
// Cross-phase `keep()`s belong here! We have a few nodes which we can
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
index 3e4adf5f..32027cb5 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
@@ -450,113 +450,76 @@ public class Serialize {
String[] strs = tread.strs();
GlobalBits fileAliases = tread.fileAliases();
- CodeGen.Phase prior = code._phase;
- code._phase=CodeGen.Phase.Parse; // Constructors must tolerate an incomplete graph.
- try {
- // D - Read the nodes
-
- // Number of nodes
- int num = bais.packed4();
- // Packed array of nodes
- Ary<Node> nodes = new Ary<>(Node.class);
- CompUnit cu = null;
- for( int i = 0; i<num; i++ ) {
- Node.Tag tag = Node.Tag.VALS[bais.packed1()];
- Type t = types[bais.packed2()];
- Node n = tag.make(bais,strs,types,fileAliases,aliases);
- n._type = t;
- nodes.push(n);
- // A little extra for FunNodes
- if( n instanceof StartCUNode startcu ) {
- cu = code._compunits.get(startcu._fname==null ? (code._srcName==null ? "Test" : code._srcName) : startcu._fname);
- assert cu != null;
- }
- if( n instanceof StopCUNode stopcu ) {
- cu = null;
- }
- if( n instanceof FunNode fun ) {
- assert cu != null;
- fun._compunit = cu;
- if( fun._name==null || fun._name.isEmpty() ) fun._name=fidxs.symbol(fun.sig().fidx());
- }
- if( n instanceof ExternNode ext && ext._con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
- String old = code.externFunc(tfp.fidx());
- if( old == null )
- code.externFunc(tfp.fidx(), ext._extern);
- else
- assert old.equals(ext._extern);
- }
+ // D - Read the nodes
+
+ // Number of nodes
+ int num = bais.packed4();
+ // Packed array of nodes
+ Ary<Node> nodes = new Ary<>(Node.class);
+ CompUnit cu = null;
+ for( int i = 0; i<num; i++ ) {
+ Node.Tag tag = Node.Tag.VALS[bais.packed1()];
+ Type t = types[bais.packed2()];
+ Node n = tag.make(bais,strs,types,fileAliases,aliases);
+ n._type = t;
+ nodes.push(n);
+ // A little extra for FunNodes
+ if( n instanceof StartCUNode startcu ) {
+ cu = code._compunits.get(startcu._fname==null ? (code._srcName==null ? "Test" : code._srcName) : startcu._fname);
+ assert cu != null;
+ }
+ if( n instanceof StopCUNode stopcu ) {
+ cu = null;
+ }
+ if( n instanceof FunNode fun ) {
+ assert cu != null;
+ fun._compunit = cu;
+ if( fun._name==null || fun._name.isEmpty() ) fun._name=fidxs.symbol(fun.sig().fidx());
+ }
+ if( n instanceof ExternNode ext && ext._con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
+ String old = code.externFunc(tfp.fidx());
+ if( old == null )
+ code.externFunc(tfp.fidx(), ext._extern);
+ else
+ assert old.equals(ext._extern);
}
+ }
- // Node edges
- for( Node n : nodes ) {
- int len = n.nIns();
- for( int i = 0; i<len; i++ ) {
- int idx = bais.packed2();
- Node def = idx==0 ? null : nodes.at(idx-1);
- // MemMerge precise aliases are encoded by their input slot.
- // Remap populated file-local slots into this compilation's
- // local alias numbering; null holes carry no identity.
- int local = i;
- if( n instanceof MemMergeNode && i >= GlobalBits.RESERVED ) {
- if( def == null ) continue;
- local = aliases.map(fileAliases,i);
- }
- n.setDefX(local,def);
- // Call's output[0] is always the CallEnd
- if( n instanceof CallEndNode cend && i==0 ) {
- var outs = cend.call()._outputs;
- outs.swap(0,outs._len-1);
- }
+ // Node edges
+ for( Node n : nodes ) {
+ int len = n.nIns();
+ for( int i = 0; i<len; i++ ) {
+ int idx = bais.packed2();
+ Node def = idx==0 ? null : nodes.at(idx-1);
+ // MemMerge precise aliases are encoded by their input slot.
+ // Remap populated file-local slots into this compilation's
+ // local alias numbering; null holes carry no identity.
+ int local = i;
+ if( n instanceof MemMergeNode && i >= GlobalBits.RESERVED ) {
+ if( def == null ) continue;
+ local = aliases.map(fileAliases,i);
}
- // A little extra for FunNodes
- if( n instanceof FunNode fun ) {
- // Return is hooked like an edge
- ReturnNode ret = (ReturnNode)nodes.at(bais.packed2()-1);
- fun.setRet(ret);
- ret._fun = fun;
+ n.setDefX(local,def);
+ // Call's output[0] is always the CallEnd
+ if( n instanceof CallEndNode cend && i==0 ) {
+ var outs = cend.call()._outputs;
+ outs.swap(0,outs._len-1);
}
}
- // Add edge from Start to Stop
- StartNode start = (StartNode)nodes.at(0);
- StopNode stop = (StopNode)nodes.last();
- start.setDef(1,stop);
-
- // A body materialized after SCCP joins a different analysis context.
- // Its saved types are reset and checked by the ensuing fixed-point solve.
- assert prior!=CodeGen.Phase.Parse || check(code,nodes);
- elf._nodes = nodes;
- } finally { code._phase=prior; }
- }
-
- // Check loaded types and nodes are consistent
- private static boolean check( CodeGen code, Ary<Node> nodes ) {
- // Loaded nodes are complete, unlike nodes still being built by the
- // parser. Recompute them with post-parse semantics. The compilation-
- // unit Stop is the exception: its escape type depends on the complete
- // program's public interface, which ParseAll freezes only after every
- // compilation unit has been loaded.
- boolean good = true;
- CodeGen.Phase phase = code._phase;
- code._phase = CodeGen.Phase.Opto;
- try {
- for( Node n : nodes ) {
- if( n instanceof StopCUNode && !code.publicInterfaceFrozen() )
- continue;
- // Cross-object calls are relinked only after every body has
- // loaded. Their saved return types cannot be recomputed yet.
- if( n instanceof CallEndNode end && end.call().fptr()._type instanceof TypeFunPtr fp &&
- end.nIns()<=fp.nfcns() ) continue;
- Type nval = n.compute();
- if( nval != n._type ) {
- System.err.println("Node "+n+" read in as "+n._type+" but computes as "+nval);
- good = false;
- }
+ // A little extra for FunNodes
+ if( n instanceof FunNode fun ) {
+ // Return is hooked like an edge
+ ReturnNode ret = (ReturnNode)nodes.at(bais.packed2()-1);
+ fun.setRet(ret);
+ ret._fun = fun;
}
- } finally {
- code._phase = phase;
}
- return good;
+ // Add edge from Start to Stop
+ StartNode start = (StartNode)nodes.at(0);
+ StopNode stop = (StopNode)nodes.last();
+ start.setDef(1,stop);
+
+ elf._nodes = nodes;
}
// --------------------------------------------------
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
index 78794096..a5eaa5ca 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -10,8 +10,12 @@ import com.seaofnodes.simple.util.Ary;
abstract class TypeCheck {
static void run(CodeGen code) {
// Check for class-init cycles first; such a cycle can prevent other
- // things from resolving types. Serialize can reuse the final graph.
- code._classInitDependencies = code.resolveInitCycles();
+ // things from resolving types. Use the graph and summaries left by
+ // optimization; do not load more bodies to disprove a possible cycle.
+ ClassInitDependencies deps = new ClassInitDependencies(code);
+ String cycle = deps.check();
+ if( cycle!=null ) throw Parser.error(cycle,null);
+ code._classInitDependencies = deps; // Serialize reuses this checked graph.
Ary<Node> errs = new Ary<>(Node.class);
code._stop.walk( n -> {
diff --git a/chapter28b/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter28b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index a7e52563..6d453324 100644
--- a/chapter28b/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter28b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -15,7 +15,8 @@ public class StopNode extends CFGNode {
public StopNode() {
super();
- _type = compute();
+ // Edges may still be missing, including when loading a StopCU.
+ _type = TypeTuple.STATE;
}
public StopNode(StopNode stop) { super(stop); }
@Override public Tag serialTag() { return Tag.Stop; }