summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter25/initialization.md115
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Parser.java34
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/ClassInitDependencies.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java87
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java57
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CFGNode.java1
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java76
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CProjNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConvertNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/LoadNode.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/MemPhiNode.java5
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/MultiNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ProjNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java1
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeStruct.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java22
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java46
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java34
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java26
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java94
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java40
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java27
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java18
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java36
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java73
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Simple.java6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--docs/chapter-backports.md16
-rw-r--r--skills/chapter25-codex-notes.md29
57 files changed, 761 insertions, 395 deletions
diff --git a/chapter25/initialization.md b/chapter25/initialization.md
index 9d2c7250..9996a3a3 100644
--- a/chapter25/initialization.md
+++ b/chapter25/initialization.md
@@ -171,6 +171,119 @@ initialize every possible dependency, or a prefix of the topological list.
## Ideal IR and runtime protocol
+### Compiler phase boundaries
+
+The Chapter 25 driver runs `Parse -> Iter -> Opto -> IterPostOpto -> FinalizeIR
+-> TypeCheck -> LoopTree`, followed by the existing serialization and backend
+phases. `Opto` performs SCCP, resolves numeric modes, and seeds the worklist;
+it does not invoke either Iter pass. `IterPostOpto` performs peepholes and
+inlining using the SCCP results. `FinalizeIR` resolves field layouts and offsets,
+runs the resulting cleanup, roots surviving functions, and releases temporary
+CU memory keeps. Numeric resolution remains in Opto because it can require
+another SCCP propagation before the types are ready for Iter.
+
+Direct callers explicitly run `parse().iter().opto().iterPostOpto().finalizeIR()`
+before `typeCheck()`. The driver handles that sequence when a later phase is
+requested. CLInit expansion after successful TypeCheck remains unimplemented;
+its phase and the serialization boundary are separate decisions.
+
+### Memory IR checkpoint (2026-10-17)
+
+This checkpoint supersedes the older representation details below. Keep CLInit
+opaque through Iter/Opto and the class-cycle check; introduce neither its
+conditional call CFG nor frequency estimates in those phases.
+
+Use the existing mutable `$init` field and alias. The implemented node shape is:
+
+```text
+ready = Load(C.$init, incoming memory)
+CLInit(ctrl, wholeMemory, classPtr, ready)
+ -> (ctrl, wholeMemory, initAliasMemory)
+```
+
+The third result is an alias slice containing true on normal continuation.
+Merge it over the whole-memory result using MemMerge. The whole-memory result
+conservatively accounts for arbitrary initializer effects; changing just the
+flag slice would be incorrect. The flag result is a fact about the operation's
+result, not an extra runtime store at each touch. Keep the class pointer and
+source location on the operation; resolve the flag alias from the class type,
+including after forward-reference resolution and object loading.
+
+The parser creates the ready load up front, using the existing receiver node.
+For an unresolved receiver, ordinary Load/FldOff machinery resolves the alias
+and offset later; no additional named FRef is needed. If the receiver becomes
+an ordinary instance, the speculative CLInit and flag load disappear together.
+If the parser cannot yet identify the flag alias, precise Loads and MemPhis
+recover that slice from the whole-memory projection once the type resolves.
+
+Known-true input bypasses the operation, returning incoming control/memory;
+false or unknown retains it. Parser memory uses the result, so subsequent flag
+loads, MemPhis, and ordinary peepholes can establish redundant checks without
+a separate parser set of initialized classes. A merge establishes true only
+when every live incoming path does. Initially an ordinary unknown call may
+lose a known-true fact; that is conservative, and preserving monotone flag
+facts across arbitrary calls can follow later.
+
+The static flag starts false. Its true store belongs inside `<clinit>`, not at
+each CLInit site. It is installed at body entry so the body knows its own flag
+is true. The parent requirement remains structural; runtime lowering must
+execute it before entering the body. This implements the accepted same-class
+reentry rule; it does not exempt cross-class cycles from validation. Retain
+initializer boundaries and their dependency edges until the post-Opto check;
+do not propagate an in-progress caller's flag into another initializer to
+erase that check. Ordinary field-initialization checking remains separate.
+
+Memory/projection wiring, parser updates, the initializer-owned flag store,
+and copying/serialization are implemented. Rebuild serialized objects for the
+new four-input node shape. Runtime lowering, entry adaptation, and executable
+run-once behavior remain a subsequent step.
+
+Public function fields on instances of public classes must retain their Start
+caller, just like public class-object fields. `CodeGen.publicFIDX` now checks
+both, preserving private-field/class and hidden-constructor exclusions. This
+fixes the `sys.scan.match` widening exposed by conservative CLInit memory
+effects; neither imported-type resets nor SCCP assertions needed changes.
+
+Validation: 395 raw0 tests, 37 raw1 tests, 9 standalone tests, the fuzzer
+regression wrapper, 25 Chapter 25 tests, and 38 additional tests all pass.
+The additional group includes 16 class-init tests
+covering cycles, serialization, repeated touches, branch merges, flag stores,
+and removal of a speculative flag load when a forward call returns an instance.
+
+### Deferred constant instance fields
+
+A final instance field proven to have the same constant value in every instance
+can eventually be stored on the class object, or folded directly to a
+ConstantNode, and removed from instance layout. Instance-field access semantics
+remain unchanged. Public function reachability must not depend on that storage
+choice: an externally accessible function field is callable by anyone whether
+stored in the instance or class. This optimization is not needed for the current
+initialization checkpoint; instance layout is unchanged.
+
+### Deferred CFG frequencies
+
+After Opto and cycle validation, lower surviving CLInit operations to flag
+tests, conditional calls, and control/memory merges. Mark the needs-init edge
+rare. Once the resulting CFG and loop tree are stable, estimate frequencies
+before GCM and reuse them in Encoding block layout. Frequencies are derived
+backend data, not ideal-IR types or inputs to Iter/Opto.
+
+Start each function at 1.0; use 50/50 for unknown tests, 90/10 for non-null/null,
+and a small positive minimum probability for initialization and known-failure
+edges. Use an estimated loop trip count of 10 unless a proven count is known;
+distinguish body iterations from header tests and account for early exits.
+Compute cyclic flow using loop-aware propagation, not an unbounded walk around
+backedges. Regions sum incoming edge flows, and branch outputs sum to their
+input. Bound probabilities and loop amplification and normalize outgoing
+probabilities; do not independently clamp successor counts and break those
+relationships. Unreachable blocks remain zero. Bound the resulting estimates
+against overflow. Later profiling can supply probabilities/trip estimates
+through the same interface; switch distributions are deferred.
+
+GCM chooses the lowest-frequency legal placement, with deterministic tie breaks.
+Encoding prefers common fall-through paths, forward cold branches, and backward
+loop edges. Recompute/invalidate estimates if later CFG changes require it.
+
Introduce an effectful `CLInitNode` with a stable target CompUnit identity:
```text
@@ -664,7 +777,7 @@ unfinished dynamic parent. Check that early flag folding cannot hide a cycle.
Files: `CodeGen.java`, `ParseAll.java`, `Serialize.java`, `ElfReader.java`,
`Main.java`, and the native linking path in `TestC.java` as appropriate.
-The current driver runs parse, Iter, Opto, TypeCheck, LoopTree,
+The current driver runs Parse, Iter, Opto, IterPostOpto, FinalizeIR, TypeCheck, LoopTree,
`unlinkImports`, serialization, then selection and scheduling. Add explicit
initialization-analysis boundaries without relying on a particular peephole
worklist order:
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
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 79af6ae7..5b57e701 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -20,7 +20,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print());
}
@@ -36,7 +36,7 @@ if (arg == 1) {
}
return c;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -64,7 +64,7 @@ if( arg==1 )
else
b=a+1;
return a+b;""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print());
}
@@ -79,7 +79,7 @@ if( arg==1 )
else
a=b+1;
return a+b;""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print());
}
@@ -99,7 +99,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,3,5,2,4);", code.print());
}
@@ -115,7 +115,7 @@ if( arg==0 )
b=2;
return arg+a+b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
String ir = code.print();
assertTrue(ir,
ir.equals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));") ||
@@ -133,7 +133,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -148,7 +148,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index fdf6963f..c503b56c 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -15,7 +15,7 @@ public class Chapter06Test {
if( true ) return 2;
return 1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -29,7 +29,7 @@ if (arg)
a = 2;
return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -65,7 +65,7 @@ else
b=5;
return b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,42,5);", code.print());
}
@@ -84,7 +84,7 @@ if( arg==2 )
else
b=5;
return b;""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,2,5);", code.print());
}
@@ -104,7 +104,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2)).parse().opto();
+""", TypeInteger.constant(2)).parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1)).parse().opto();
+""", TypeInteger.constant(1)).parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,3,5,4);", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1));
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3));
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1));
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0));
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,6,1,5);", code.print());
}
@@ -278,7 +278,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1));
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2));
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 5;", code.print());
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 2ca6f8dd..498038c1 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -17,7 +17,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -34,7 +34,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print());
}
@@ -54,7 +54,7 @@ while(i < arg) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print());
}
@@ -89,7 +89,7 @@ while(a < 10) {
}
return b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -106,7 +106,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -122,7 +122,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -148,7 +148,7 @@ int a = 1;
while(arg) a = 2;
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -164,7 +164,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -180,7 +180,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -197,7 +197,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -214,7 +214,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 09669cc7..61815840 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -22,7 +22,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
Assert.assertEquals( "5", Eval2.eval(code, 1));
@@ -45,7 +45,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -64,7 +64,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -80,7 +80,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -99,7 +99,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -116,7 +116,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -132,14 +132,14 @@ while( arg < 10 ) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return arg;", code.print());
}
@Test
public void testRegress2() {
CodeGen code = new CodeGen("if(1) return 0; else while(arg>- -arg) arg=arg+1; return 0;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -200,7 +200,7 @@ while(1) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index d36cdf7f..839b834d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,(arg<<1),(Shl+1));", code.print());
Assert.assertEquals( "2", Eval2.eval(code, 1));
Assert.assertEquals("23", Eval2.eval(code, 11));
@@ -49,7 +49,7 @@ return x;
@Test
public void testGVN2() {
CodeGen code = new CodeGen("return arg*arg-arg*arg;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
Assert.assertEquals("0", Eval2.eval(code, 1, 1));
}
@@ -63,7 +63,7 @@ while (arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,arg,(Phi_arg+2));", code.print());
Assert.assertEquals("11", Eval2.eval(code, 1));
}
@@ -80,7 +80,7 @@ while (arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,arg,(Phi_arg+4));", code.print());
Assert.assertEquals("13", Eval2.eval(code, 1));
}
@@ -109,7 +109,7 @@ while (arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -128,14 +128,14 @@ while(v1+arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@Test
public void testWhile0() {
CodeGen code = new CodeGen("while(0) continue; if(0) arg=0; return arg;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -148,7 +148,7 @@ if(0) while(0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -156,14 +156,14 @@ return 0;
@Test
public void testPrecedence() {
CodeGen code = new CodeGen("return 3-1+2;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -181,7 +181,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
}
@@ -195,7 +195,7 @@ arg=0;
int v0=0!=0<-0;
return -0+0+0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -203,14 +203,14 @@ return -0+0+0;
@Test
public void testFuzz2() {
CodeGen code = new CodeGen("return 0+-0;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@Test
public void testFuzz3() {
CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0; return 1;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -222,7 +222,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -244,7 +244,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -257,7 +257,7 @@ while(0==1) while(v0)
v0=1+v0;
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -273,14 +273,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Top;", code.print());
}
@Test
public void testFuzz8() {
CodeGen code = new CodeGen("while(arg) arg = arg - 1; return arg;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index cfffeb83..1b542d63 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -12,7 +12,7 @@ public class Chapter10Test {
"if(1) return new S; else return 0;"
} ) {
String src = "struct S { int x; }; " + body;
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
// This chapter also has constructor/class-init Returns in the CompUnit.
var entry = (com.seaofnodes.simple.node.FunNode)code._start.uctrl();
org.junit.Assert.assertTrue(body,
@@ -102,7 +102,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -122,7 +122,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 2;", code.print());
}
@@ -138,7 +138,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return Test.Vector2D; return MEM[ 2:___ 3:.x=0; 4:.y=0;]; return Test.Vector2D; ]", code.print());
}
@@ -151,7 +151,7 @@ struct s0 {
s0? v1=null;
int v3=v1.zAicm;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'zAicm'",e.getMessage()); }
}
@@ -176,7 +176,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return Phi(Loop,0,(Phi_a+2)); return MEM[ 2:___ 3:.a=0;]; return Test.Bar; ]", code.print());
}
@@ -189,7 +189,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar",e.getMessage()); }
}
@@ -202,7 +202,7 @@ if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
}
@@ -215,7 +215,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar", e.getMessage()); }
}
@@ -228,7 +228,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1,.a);", code.print());
}
@@ -244,7 +244,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -261,7 +261,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) {
assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@@ -298,7 +298,7 @@ while( i.x < i.len ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum)); return MEM[ 2:___ 3:.x=0; 4:.len=0;]; return Test.Iter; ]", code.print());
}
@@ -317,7 +317,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return Phi(Loop,Test.s0,Phi(Region,Test.s0,Phi_ret)); return MEM[ 2:___ 3:.v0=0;]; return Test.s0; ]", code.print());
}
@@ -334,7 +334,7 @@ while(arg) {
}
return ret.v0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return .v0;", code.print());
}
@@ -351,7 +351,7 @@ while(arg < 10) {
}
return ret.v0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return .v0;", code.print());
}
@@ -370,7 +370,7 @@ s0 b = new s0();
// a.x==1, b.x==1, cnt==2
return a.x * 100 + b.x * 10 + cnt;
""");
- code.parse().iter().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return 112; return MEM[ 2:___ 3:___ 4:.x=1;]; return Test.s0; ]", code.print());
}
@@ -399,7 +399,7 @@ if(0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -413,7 +413,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().iter().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("Stop[ return Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print());
}
@@ -429,7 +429,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
}
@Test
@@ -441,7 +441,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return (const)Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print());
}
@@ -474,7 +474,7 @@ while(0) {}
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -485,7 +485,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 40db6c4b..19c531f8 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -14,7 +14,7 @@ public class Chapter12Test {
"""
return 3;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -34,7 +34,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); }
}
@@ -56,7 +56,7 @@ _LLI? next = head.next;
if( next==null ) return 1;
return next.q;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return Phi(Region,1,.q,0);", code.print());
assertEquals("2", Eval2.eval(code, 3));
}
@@ -75,7 +75,7 @@ i0.f = f0;
f0.i = i0;
return f0.i.f.i.i;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 17;", code.print());
}
@@ -87,7 +87,7 @@ struct _N { !_N? !next; int i; };
_N n = new _N;
return n.next;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return null;", code.print());
}
@@ -100,7 +100,7 @@ struct N { !M !next; int i; new N = { !M m -> next = m; }; };
N n = new N(new M);
return n.next;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return (const)Test.M; return MEM[ 2:___ 3:.m=0;]; return Test.M; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:.next=Parm_m(Test.N.N,*Test.M {i64 !m; }); 8:.i=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:___ 7:___ 8:.i=0;]; return Test.N; ]", code.print());
}
@@ -113,7 +113,7 @@ struct N { !M !next; int i; new N = { !M m -> next = m; }; };
N n = new N(null);
return n.next;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Argument #0 isa null, but must be a *Test.M {i64 !m; }",e.getMessage()); }
}
@@ -126,20 +126,20 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("3",Eval2.eval(code,0));
}
@Test
public void testNullRef4() {
CodeGen code = new CodeGen("return -null-5/null-5;");
- try { code.parse().opto().typeCheck(); fail(); }
- catch( Exception e ) { assertEquals("Cannot 5 // null",e.getMessage()); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot null + 5",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); }
}
@@ -150,7 +150,7 @@ return n.i;
struct _S{};
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -162,7 +162,7 @@ return 0;
struct S1 { S2? s; };
return new S2;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
}
@@ -174,7 +174,7 @@ struct _S1 { !_S2? !s; };
struct _S2 { int x; };
return (new _S1).s=new _S2;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Test._S2;", code.print());
}
@@ -191,7 +191,7 @@ p2.pi.i = 2;
if (arg) p1 = new P;
return p1.pi.i + 1;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -210,7 +210,7 @@ c.f0=a; c.f1=b;
return a.f0.f1.f0.f1.f0;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM().localSched();
assertEquals("return Test._B;", code.print());
assertEquals("Test._B{f0=Test._C{f0=Test._A{f0=$cyclic,f1=$cyclic},f1=$cyclic},f1=$cyclic}", Eval2.eval(code, 0));
}
@@ -276,7 +276,7 @@ z.a=k; z.b=s; z.c=e;
return a.b.c.a.b.c.a.b.c.a.b.c.a.b.c.a.b.c;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM().localSched();
assertEquals("return Test._R;", code.print());
assertEquals("Test._R{a=Test._C{a=Test._N{a=Test._Y{a=Test._J{a=Test._U{a=Test._F{a=Test._Q{a=Test._B{a=Test._M{a=Test._X{a=Test._I{a=Test._T{a=Test._E{a=Test._P{a=Test._A{a=Test._L{a=Test._W{a=Test._H{a=Test._S{a=Test._D{a=Test._O{a=Test._Z{a=Test._K{a=Test._V{a=Test._G{a=$cyclic,b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic},b=$cyclic,c=$cyclic}", Eval2.eval(code, 0));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 7cfa5817..9786006c 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -34,7 +34,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print());
assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
assertEquals("1", Eval2.eval(code, 2)); // 2
@@ -73,7 +73,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -96,7 +96,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -115,7 +115,7 @@ if (v1) {
}
return v0.f;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,.f,2);", code.print());
}
@@ -133,7 +133,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -150,7 +150,7 @@ while(1) {
}
return v.f;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,2);", code.print());
for( int arg : new int[]{-1,0,1,3,100} )
assertEquals("0",Eval2.eval(code,arg));
@@ -167,7 +167,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -185,7 +185,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -205,7 +205,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -223,7 +223,7 @@ if (arg) {
}
return v.f;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -242,7 +242,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
//assertEquals("return new _S;", code.print());
}
@@ -259,7 +259,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -277,7 +277,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index c1354e34..ccca2e2b 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -18,7 +18,7 @@ public class Chapter14FloatTest {
{"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
};
for( String[] test : tests ) {
- var code = new CodeGen(test[0]).parse().opto().typeCheck();
+ var code = new CodeGen(test[0]).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -26,7 +26,7 @@ public class Chapter14FloatTest {
@Test public void testSubZeroFloat() {
- var code = new CodeGen("flt x = arg; return 0-x;").parse().opto().typeCheck();
+ var code = new CodeGen("flt x = arg; return 0-x;").parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
// Compare strings so +0.0 and -0.0 remain distinct.
assertEquals("0.0", Eval2.eval(code,0));
assertEquals("-1.0", Eval2.eval(code,1));
@@ -40,7 +40,7 @@ public class Chapter14FloatTest {
{"return 2.5; return 7;", "2.5"}
} ) {
String src = test[0];
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -53,7 +53,7 @@ public class Chapter14FloatTest {
"""
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -65,7 +65,7 @@ return 0;
flt x = 3.14;
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -84,7 +84,7 @@ return guess;
""");
// TODO: One of the problems with <clinit> returning an OS error code,
// is it chops all results to int.
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,(flt)arg,((Phi_guess+(ToFloat/Phi_guess))*0.5f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -97,7 +97,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index 086bb824..3ba03b48 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -32,7 +32,7 @@ if( b < 0 ) c = -1;
if( b > 2 ) c = 1;
return c;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 99;", code.print());
assertEquals("99", Eval2.eval(code, 0));
}
@@ -45,7 +45,7 @@ u8 b = 123;
b = b + 456;// Truncate
return b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 67;", code.print());
assertEquals("67", Eval2.eval(code, 0));
}
@@ -59,7 +59,7 @@ u8 b = 123;
while( b ) b = b + 456;// Truncate
return b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -72,7 +72,7 @@ b = b + 456;// Truncate
u1 c = b; // No more truncate needed
return c;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -85,7 +85,7 @@ int b = 123;
b = b+456 & 31; // Precedence
return b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -99,7 +99,7 @@ struct _Foo { u1 b; };
f.b = 123;
return f.b;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -111,7 +111,7 @@ return f.b;
i8 b = 255; // Chopped
return b; // Sign extend
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return -1;", code.print());
assertEquals("-1", Eval2.eval(code, 0));
}
@@ -124,7 +124,7 @@ i8 b = arg;
b = b + 1;// Truncate
return b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (((((arg<<56)>>56)+1)<<56)>>56);", code.print());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("-128", Eval2.eval(code, 127));
@@ -138,7 +138,7 @@ u16 mask = (1<<16)-1; // AND mask
int c = 123456789 & mask;
return c; //
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 52501;", code.print());
assertEquals("52501", Eval2.eval(code, 0));
}
@@ -149,7 +149,7 @@ return c; //
"""
return (arg | 123 ^ 456) >>> 1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (((arg|123)^456)>>>1);", code.print());
assertEquals("217", Eval2.eval(code, 0));
}
@@ -162,7 +162,7 @@ flt f = arg;
arg = f & 1;
return arg;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot '&' flt",e.getMessage()); }
}
@@ -175,7 +175,7 @@ while(arg) v1=-v0;
while(v1) break;
return v1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -194,7 +194,7 @@ if(0&0>>>0) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -220,7 +220,7 @@ f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5;
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index f363847a..3c1254d2 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -17,7 +17,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -31,7 +31,7 @@ struct _C { !_C? !l; };
c.l = c;
return c;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Test._C;", code.print());
assertEquals("Test._C{l=$cyclic}", Eval2.eval(code, 0));
}
@@ -50,7 +50,7 @@ while (i < old#) {
output[i] = 1;
return output;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return []u8;", code.print());
assertEquals("\u0001", Eval2.eval(code, 0));
}
@@ -62,7 +62,7 @@ return output;
int[] is = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -75,7 +75,7 @@ int[] is = new int[2];
int[] is2 = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -89,7 +89,7 @@ a[0] = 1;
a[1] = 2;
return a[0];
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -102,7 +102,7 @@ struct _A { int i; };
!_A?[] !a = new !_A?[2];
return a;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return []*Test._A?;", code.print());
assertEquals("*Test._A {i64 !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -131,7 +131,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1.2,.y,2.3);", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
assertEquals("1.2", Eval2.eval(code, 1));
@@ -156,7 +156,7 @@ if( iss[arg] )
if( iss[arg][2] )
rez = iss[arg][2].y;
return rez;""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return Phi(Region,1.2,1.2,.y);", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks
@@ -174,7 +174,7 @@ root._kids = new !_Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
root._kids[0] = new _Tree;
return root;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return Test._Tree;", code.print());
assertEquals("Test._Tree{_kids=*Test._Tree {*[]*Test._Tree?[mutable]? !_kids; }?[ Test._Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -186,7 +186,7 @@ return root;
struct _S { int a; int[] !b; new _S = { int[] ary -> b=ary; }; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -209,7 +209,7 @@ while( i < ary# - 1 ) {
}
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return (.[]+(.[]*1000));", code.print());
assertEquals("1006", Eval2.eval(code, 4));
}
@@ -218,7 +218,7 @@ return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
public void sieveOEratosthenes() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp"));
CodeGen code = new CodeGen(src+"return sieve(arg);");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return []u32;", code.print());
assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -235,7 +235,7 @@ s2.f = 2.0;
if (arg) s1 = new _S;
return s1.i + s1.f;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return ((flt).i+.f);", code.print());
assertEquals("0.0", Eval2.eval(code, 0));
}
@@ -247,7 +247,7 @@ return s1.i + s1.f;
"""
return new flt;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@@ -258,7 +258,7 @@ return new flt;
flt is = new int[2];
return is;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Type *[]i64 is not of declared type flt",e.getMessage()); }
}
@@ -269,7 +269,7 @@ return is;
int[] is = new int[3.14];
return is[1];
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot '<<' 3.14",e.getMessage()); }
}
@@ -279,7 +279,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 4));
try { Eval2.eval(code,-1); fail(); }
@@ -312,7 +312,7 @@ while(v5+(0&0)) {
}
return v1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -328,7 +328,7 @@ int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -353,7 +353,7 @@ else {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 91e5cd38..ad9b99bc 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -15,7 +15,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -28,7 +28,7 @@ return 3.14;
int x, y;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -39,7 +39,7 @@ return x+y;
int x=2, y=x+1;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -52,7 +52,7 @@ struct _X { int x=3; };
_X z = new _X;
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -64,7 +64,7 @@ struct _X { int x=3; new _X = { int xx -> x=xx; }; };
_X z = new _X(4);
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@@ -85,7 +85,7 @@ _LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1,.i,0);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -115,7 +115,7 @@ _LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1,.i,0);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -140,7 +140,7 @@ struct _Square {
};
return new _Square;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Test._Square;", code.print());
assertEquals("Test._Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Test._Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 52acff57..95a0fc37 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -15,7 +15,7 @@ int x=2, y=3;
if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x
return y;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,9,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -28,7 +28,7 @@ struct _X { int ~x; new _X = { int xx -> x=xx; }; };
_X z = new _X(3);
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -40,7 +40,7 @@ struct _Point { int ~x, ~y; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
_Point p = new _Point(3,4);
return p;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (const)Test._Point;", code.print());
assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -53,7 +53,7 @@ struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -65,7 +65,7 @@ struct _Point { int x=3, y=4; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -77,7 +77,7 @@ _Point p = new _Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("'Test._Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
@@ -89,7 +89,7 @@ _Point p = new _Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -101,7 +101,7 @@ struct _Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Test._Point;", code.print());
assertEquals("Test._Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index b6768d6f..6137c166 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -15,7 +15,7 @@ public class Chapter17bTest {
"""
return 1;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -27,7 +27,7 @@ return 1;
CodeGen code = new CodeGen("""
return arg++;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return arg;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -37,7 +37,7 @@ return arg++;
CodeGen code = new CodeGen("""
return arg+++arg++;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return ((arg<<1)+1);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -48,7 +48,7 @@ return arg+++arg++;
// -(arg--)-(arg--)
return -arg---arg--;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (-((arg<<1)+-1));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -61,7 +61,7 @@ xs[0]++;
xs[1]++;
return xs[0];
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -73,7 +73,7 @@ u8[] !xs = new u8[1];
xs[0]--;
return xs[0];
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 255;", code.print());
assertEquals("255", Eval2.eval(code, 2));
}
@@ -86,7 +86,7 @@ struct _S { u16 x; };
s.x--;
return s.x;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 65535;", code.print());
assertEquals("65535", Eval2.eval(code, 2));
}
@@ -94,7 +94,7 @@ return s.x;
@Test
public void testInc6() {
CodeGen code = new CodeGen("return --arg;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return (arg+-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
}
@@ -102,7 +102,7 @@ return s.x;
@Test
public void testInc7() {
CodeGen code = new CodeGen("u8 x=0; return --x;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 255;", code.print());
assertEquals("255", Eval2.eval(code, 0));
}
@@ -110,7 +110,7 @@ return s.x;
@Test
public void testInc8() {
CodeGen code = new CodeGen("int x; x+=2; return x+=3;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -118,14 +118,14 @@ return s.x;
@Test
public void testInc9() {
CodeGen code = new CodeGen("flt x = 1.5; x++; return x;");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 2.5f;", code.print());
assertEquals("2.5", Eval2.eval(code, 0));
}
@Test public void testInt10() {
CodeGen code = new CodeGen("flt x = 1.5; x++; int i = x; return i;");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("2",Eval2.eval(code,0));
}
@@ -133,73 +133,73 @@ return s.x;
// ---------------------------------------------------------------
@Test public void testVar0() {
CodeGen code = new CodeGen("var d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); }
}
@Test public void testVar2() {
CodeGen code = new CodeGen("int x; x=3; x++; return x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@Test public void testVar3() {
CodeGen code = new CodeGen("int x=3; x++; return x; // Ok, primitive so x is mutable despite initializer ");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@Test public void testVar4() {
CodeGen code = new CodeGen("struct _S{int x;}; !_S? !s; s=new _S; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct _S{int x; new _S={ int xx -> x=xx; };}; !_S? !s; s=new _S(3); s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@Test public void testVar6() {
CodeGen code = new CodeGen("struct S{int x;}; ~S s = new S; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@Test public void testVar7() {
CodeGen code = new CodeGen("struct S{int x; new S={ int xx -> x=xx; };}; ~S s = new S(3); s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@Test public void testVar8() {
CodeGen code = new CodeGen("struct _S{int x;}; !_S !s=new _S; s.x++; return s.x; // Ok, has '!' so s.x is mutable ");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar9() {
CodeGen code = new CodeGen("struct _S{int x;}; var s=new _S; s.x++; return s.x; // Ok, has var so s.x is mutable ");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar10() {
CodeGen code = new CodeGen("struct S{int x;}; ~S s=new S; s.x++; return s.x; // Error, has val so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -218,7 +218,7 @@ foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
xfoo.bar.x++; // Error, cannot mutate through xfoo
return xfoo.bar.x;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -239,7 +239,7 @@ foo.bar.x++; // Bumps to 5
int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5
return x4*10+x5;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 45;", code.print());
assertEquals("45", Eval2.eval(code, 0));
}
@@ -249,7 +249,7 @@ return x4*10+x5;
CodeGen code = new CodeGen("""
int i,i++;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -264,7 +264,7 @@ struct A { !B !b; new A = { ->
}; };
A x = new A;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Field 'b' might not be initialized",e.getMessage()); }
}
@@ -296,7 +296,7 @@ A x = new A;
"!B !b; new A = { -> if (arg) { b = new B; return 0; } b = new B; };",
} ) {
CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return new A.b != null;");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("1",Eval2.eval(code,0));
assertEquals("1",Eval2.eval(code,1));
}
@@ -314,7 +314,7 @@ val x = 1;
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'x'",e.getMessage()); }
}
@@ -343,7 +343,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -354,7 +354,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -368,7 +368,7 @@ val get = { !S s -> s.x; };
if( !s || !arg ) return 0;
return get(s);
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
}
@Test
@@ -378,7 +378,7 @@ struct _Bar { int x; };
var b = arg ? new _Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
@@ -392,7 +392,7 @@ struct _Bar { int x; };
var b = arg ? new _Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -406,7 +406,7 @@ struct _Bar { !_Bar? !next; int x; new _Bar = { !_Bar? !n, int xx -> next=n; x=x
var b = arg ? new _Bar((arg==2) ? new _Bar(null,2), 1);
return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0"
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,.x,0,.x);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -419,7 +419,7 @@ return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0
flt f=arg?1:1.2;
return f; // missing widening
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -431,7 +431,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot return generic scalar",e.getMessage()); }
}
@@ -444,7 +444,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print());
assertEquals("3", Eval2.eval(code, 3));
assertEquals("45", Eval2.eval(code, 10));
@@ -457,7 +457,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print());
assertEquals("3", Eval2.eval(code, 3));
assertEquals("45", Eval2.eval(code, 10));
@@ -472,7 +472,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print());
assertEquals("3", Eval2.eval(code, 3));
assertEquals("45", Eval2.eval(code, 10));
@@ -485,7 +485,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+Phi(Loop,0,(Phi_i+1))));", code.print());
assertEquals("3", Eval2.eval(code, 3));
assertEquals("45", Eval2.eval(code, 10));
@@ -498,7 +498,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -506,7 +506,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected `Unexpected code after expression` but found `;`",e.getMessage()); }
}
@@ -522,7 +522,7 @@ struct _A {
};
return new _A;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Test._A;", code.print());
assertEquals("Test._A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -537,7 +537,7 @@ struct _A {
};
return new _A(new !B?[0]).array_of_b;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return []*B?;", code.print());
assertEquals("*B {}?[]", Eval2.eval(code,0));
}
@@ -556,7 +556,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -587,7 +587,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM();
assertEquals("return []i64;", code.print());
assertEquals("i64[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 82f99fc0..d88d8e9d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -91,7 +91,7 @@ public class Chapter18Test {
"No common type amongst int and reference"}
} ) {
try {
- new CodeGen(test[0]).parse().opto().typeCheck();
+ new CodeGen(test[0]).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals("Cannot return generic scalar",e.getMessage());
@@ -140,7 +140,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code.print());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -157,7 +157,7 @@ return a;
{int -> int}? x2 = null; // null function ptr
return x2;
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return null;", code.print());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -170,7 +170,7 @@ return x2;
x*x;
};
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return { sq}; return (Parm_x(sq,i64)*x); ]", code.print());
assertEquals("{ ptr i64 -> i64 #[ 3]}", Eval2.eval(code, 3));
}
@@ -197,7 +197,7 @@ return _sq(arg)+_sq(3);
int cnt=1; // Global scope; exactly one of these
return { -> cnt++; }; // Escape global-scope variable is OK
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return { ptr -> i64 #[ 3]}; return .cnt; ]", code.print());
assertEquals("{ ptr -> i64 #[ 3]}", Eval2.eval(code, 0));
}
@@ -216,7 +216,7 @@ val B = ctr(); // Make a counter
A(); // Increment counter A
return A()*10 + B(); // Incr A and B; result: 2*10+1 == 21
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -228,7 +228,7 @@ return A()*10 + B(); // Incr A and B; result: 2*10+1 == 21
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -241,7 +241,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("Stop[ return #2; return (Parm_x($fun3,i64,3)*x); return (Parm_x($fun4,i64,3)<<1); ]", code.print());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -251,7 +251,7 @@ return fcn(3);
@Test
public void testFcn5() {
CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1 : x*_fact(x-1); }; return _fact(arg);");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
String ir = code.print();
assertTrue(ir.equals("Stop[ return #2; return Phi(Region,1,(Parm_x(_fact,arg,(x-1))*#2)); ]") ||
ir.equals("Stop[ return Phi(Region,1,(arg*#2)); return Phi(Region,1,(Parm_x(_fact,(arg-1),(x-1))*#2)); ]") ||
@@ -269,7 +269,7 @@ return fcn(3);
@Test
public void testFcn5Float() {
CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1.0 : x*_fact(x-1); }; return _fact(arg);");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("1.0", Eval2.eval(code, 0));
assertEquals("1.0", Eval2.eval(code, 1));
assertEquals("2.0", Eval2.eval(code, 2));
@@ -280,7 +280,7 @@ return fcn(3);
@Test
public void testFcn5Minus() {
CodeGen code = new CodeGen("val _neg = { int x -> x ? -_neg(x-1) : 1; }; return _neg(arg);");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals( "1", Eval2.eval(code, 0));
assertEquals("-1", Eval2.eval(code, 1));
assertEquals( "1", Eval2.eval(code, 2));
@@ -295,7 +295,7 @@ struct _S { int i; new _S = { int x -> i=x; }; };
val _newS = { int x -> return new _S(x); };
return _newS(1).i;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -310,7 +310,7 @@ val _g = {->2;};
if( arg ? _f : _g ) return 1;
return 2;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -388,7 +388,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -404,7 +404,7 @@ for(;;) {
}
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { ptr i64 -> i64 #[ 3]}?",e.getMessage()); }
}
@@ -416,7 +416,7 @@ return 0;
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Expecting 3 arguments, but found 1",e.getMessage()); }
}
@@ -432,7 +432,7 @@ val x = { -> return 1; };
S? s = new S;
return s.x;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*Test.S'",e.getMessage()); }
}
@@ -445,7 +445,7 @@ val g = { ->
};
return g();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -459,7 +459,7 @@ val is_even = { int x -> x ? is_odd (x-1) : true ; };
val is_odd = { int x -> x ? is_even(x-1) : false; };
return is_even(arg);
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("Stop[ return #2; return Phi(Region,0,1,#2); ]", code.print());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -481,7 +481,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 2419dc2d..2763e53d 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -37,7 +37,7 @@ public class Chapter19Test {
CodeGen code = new CodeGen("""
return 0;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 2));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 5acfbc8f..9f537fe7 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -69,7 +69,7 @@ return f(s);
// The entry of g can disappear before its Return is folded into f.
// Exercise both worklist orders; deleting the entry does not kill 123.
for( int seed=0; seed<64; seed++ ) {
- CodeGen code = new CodeGen(src,seed,true).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,seed,true).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -92,7 +92,7 @@ return f(s);
@Test
public void testJig() {
CodeGen code = new CodeGen("return 0;");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -122,7 +122,7 @@ return f(s);
@Test public void testBasic1() {
String src = "return arg | 2;";
- testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));");
+ testCPU(src,"x86_64_v2", "SystemV",1,"return mov((ori,arg));");
testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );");
testCPU(src,"arm" , "SystemV",0,"return (ori,arg);");
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 1f4913f0..83342ab8 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -48,7 +48,7 @@ public class Chapter21Test {
else if(i==4) b.x=40;
}
return sum;
- """).parse().opto();
+ """).parse().iter().opto().iterPostOpto().finalizeIR();
// The i==2 read still sees 10: three reads of 10, then three of 30.
assertEquals("120",Eval2.eval(code,0));
assertEquals("60",Eval2.eval(code,1));
@@ -298,7 +298,7 @@ return new _s0.v1;
@Test public void testBasic1() {
String src = "return arg | 2;";
- testCPU(src,"x86_64_v2", "SystemV",1,"return (ori,mov(arg));");
+ testCPU(src,"x86_64_v2", "SystemV",1,"return mov((ori,arg));");
testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );");
testCPU(src,"arm" , "SystemV",0,"return (ori,arg);");
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index 7484a6ae..cbb08438 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -88,7 +88,7 @@ public class Chapter22Test {
@Test public void testSubZeroTypeError() {
try {
- new CodeGen("return null-0;").parse().opto().typeCheck();
+ new CodeGen("return null-0;").parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
fail("Subtraction must reject null even when the other operand is zero");
} catch( Parser.ParseException e ) {
assertEquals("Cannot null - 0",e.getMessage());
@@ -135,6 +135,7 @@ return new s0.v1;
static int testCPUSize( String src, String cpu, String os, int spills ) {
+ // Includes the file initializer's hidden $init=true store.
return testCPU(src,cpu,os,spills, "return 0;" )._encoding._bits.size();
}
@@ -146,9 +147,9 @@ struct _Person { i32 age;};
p.age = (arg<<17)>>17;
return 0;
""";
- assertEquals(65, testCPUSize(src, "x86_64_v2","win64", 3 ));
- assertEquals(76, testCPUSize(src, "riscv", "SystemV", 4 ));
- assertEquals(76, testCPUSize(src, "arm" , "SystemV", 4 ));
+ assertEquals(76, testCPUSize(src, "x86_64_v2","win64", 3 ));
+ assertEquals(92, testCPUSize(src, "riscv", "SystemV", 4 ));
+ assertEquals(92, testCPUSize(src, "arm" , "SystemV", 4 ));
// do assertEquals here
EvalRisc5 R5 = TestRisc5.build( src, "sext_str_not_fold_away", 0, 4, false);
@@ -178,9 +179,9 @@ return 0;
int trap_arm = A5.step(100);
assertEquals(0,trap_arm);
- assertEquals(65, testCPUSize(src, "x86_64_v2","win64",3 ));
- assertEquals(76, testCPUSize(src, "riscv", "SystemV",4 ));
- assertEquals(76, testCPUSize(src, "arm", "SystemV",4 ));
+ assertEquals(76, testCPUSize(src, "x86_64_v2","win64",3 ));
+ assertEquals(92, testCPUSize(src, "riscv", "SystemV",4 ));
+ assertEquals(92, testCPUSize(src, "arm", "SystemV",4 ));
}
@@ -202,9 +203,9 @@ return 0;
int trap_arm = A5.step(100);
assertEquals(0,trap_arm);
- assertEquals(55, testCPUSize(src, "x86_64_v2","win64",3 ));
- assertEquals(72, testCPUSize(src, "riscv", "SystemV",5 ));
- assertEquals(72, testCPUSize(src, "arm", "SystemV",5));
+ assertEquals(66, testCPUSize(src, "x86_64_v2","win64",3 ));
+ assertEquals(84, testCPUSize(src, "riscv", "SystemV",5 ));
+ assertEquals(84, testCPUSize(src, "arm", "SystemV",5));
// do assertEquals here
}
@@ -282,7 +283,7 @@ aa.c = cc;
bb.c = cc;
return cc.cz;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 25;", code.print());
assertEquals("25", Eval2.eval(code, 0));
}
@@ -321,7 +322,7 @@ return 0;
@Test
public void testStrLoad() {
CodeGen code = new CodeGen("return \"abc\"[1];");
- code.parse().opto();
+ code.parse().iter().opto().iterPostOpto().finalizeIR();
assertEquals("return .[];", code.print());
}
@@ -341,7 +342,7 @@ val _sum = { i32[~] is -> // final array
};
return _sum(is);
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code.print());
assertEquals("14", Eval2.eval(code, 0));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
index 053621bd..b5da4d91 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -120,10 +120,10 @@ return a && b ? 0 : 1; // Expected answer 0
@Test
public void testLogicalValueSemantics() {
- assertEquals("42", Eval2.eval(new CodeGen("return 7 && 42;").parse().opto(), 0));
- assertEquals("0", Eval2.eval(new CodeGen("return 0 && 42;").parse().opto(), 0));
- assertEquals("7", Eval2.eval(new CodeGen("return 7 || 42;").parse().opto(), 0));
- assertEquals("42", Eval2.eval(new CodeGen("return 0 || 42;").parse().opto(), 0));
+ assertEquals("42", Eval2.eval(new CodeGen("return 7 && 42;").parse().iter().opto().iterPostOpto().finalizeIR(), 0));
+ assertEquals("0", Eval2.eval(new CodeGen("return 0 && 42;").parse().iter().opto().iterPostOpto().finalizeIR(), 0));
+ assertEquals("7", Eval2.eval(new CodeGen("return 7 || 42;").parse().iter().opto().iterPostOpto().finalizeIR(), 0));
+ assertEquals("42", Eval2.eval(new CodeGen("return 0 || 42;").parse().iter().opto().iterPostOpto().finalizeIR(), 0));
}
@Test
@@ -133,7 +133,7 @@ struct S {};
S? s = arg ? new S;
return !(!s || !arg) ? 1 : 0;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
}
@@ -146,7 +146,7 @@ struct S { !S? !fld; new S = { !S? f -> fld=f; }; };
val ptr = arg == 1 ? null : new S(arg==1 ? null : new S(null));
return ptr && ptr.fld ? "true" : "false";
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("true", Eval2.eval(code, 0));
// Evaluate on RISC5 emulator
@@ -276,7 +276,7 @@ struct _Scan {
return _s.peek('q');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return Phi(Region,0,1);", code.print());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "win64",23,null);
@@ -301,7 +301,7 @@ struct _Scan {
return s.skip().x;
""";
- try { new CodeGen(src).parse().opto().typeCheck(); fail(); }
+ try { new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
catch( Exception e ) {
// Bad error message, but basically requires a mutable 'x' field.
org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa"));
@@ -331,7 +331,7 @@ struct _Scan {
return _s.require('[');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return (.[]==91);", code.print());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "win64",4, null);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
index 3e21c73e..4bfb0d30 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
@@ -257,7 +257,7 @@ while (cnt) {
}
return x;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 1;", code.print());
}
@@ -272,7 +272,7 @@ while( cnt-- )
x = 2-x;
return x;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals("return 1;", code.print());
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
index 30e1ed79..4e8effbe 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -9,6 +9,7 @@ import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
import com.seaofnodes.simple.codegen.ElfReader;
import com.seaofnodes.simple.codegen.ParseAll;
import com.seaofnodes.simple.node.CallNode;
+import com.seaofnodes.simple.node.FunNode;
import com.seaofnodes.simple.node.LoopNode;
import com.seaofnodes.simple.type.TypeInteger;
import com.seaofnodes.simple.util.Ary;
@@ -27,6 +28,41 @@ import static org.junit.Assert.*;
public class Chapter25Test {
+ @Test public void testPublicInstanceMethod() {
+ String src = """
+ struct Scan {
+ val visible = { u8 c -> c==255; };
+ val _hidden = { u8 c -> c==1; };
+ };
+ struct _Private {
+ val hiddenClassMethod = { u8 c -> c==2; };
+ };
+ return new Scan.visible(44);
+ """;
+ for( long seed : new long[]{1,123,987654321} ) {
+ CodeGen code = new CodeGen(src,seed,true).driver(CodeGen.Phase.Parse);
+ FunNode visible = null;
+ int checked = 0;
+ for( FunNode fun : code._linker ) {
+ if( fun==null || fun._name==null ) continue;
+ if( fun._name.equals("visible") ) {
+ assertTrue(code.publicFIDX(fun.sig().fidx()));
+ visible = fun;
+ checked++;
+ }
+ if( fun._name.equals("_hidden") || fun._name.equals("hiddenClassMethod") ) {
+ assertFalse(code.publicFIDX(fun.sig().fidx()));
+ checked++;
+ }
+ }
+ assertEquals(3,checked);
+ code.driver(CodeGen.Phase.TypeCheck);
+ assertTrue(visible.unknownCallers());
+ assertEquals(TypeInteger.U8,visible.parm(3)._type);
+ assertEquals("0",Eval2.eval(code,0));
+ }
+ }
+
@Ignore
@Test
public void testJig() {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java b/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
index e3af8cce..cc010668 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
@@ -1,11 +1,13 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.FunNode;
+import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
import com.seaofnodes.simple.type.Field;
import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.type.TypeTuple;
import com.seaofnodes.simple.util.Ary;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -362,6 +364,75 @@ public class ClassInitDependenciesTest {
compile("A",Map.of("A","int value=arg; val "+name+"={ -> A.value; }; int result="+name+"();"),seed);
}
+ @Test public void testInitializationMemory() throws Exception {
+ String[] sources={
+ "int value=B.value+B.value;",
+ "int value; if(arg) value=B.value; else value=B.value+1; value=value+B.value;",
+ "int value; if(arg) value=B.value; value=value+B.value;"
+ };
+ int[] counts={1,2,2};
+ for( long seed : new long[]{1,123,987654321} )
+ for( int i=0; i<sources.length; i++ ) {
+ CodeGen code=compile("A",Map.of("A",sources[i],"B","int value=arg;"),seed);
+ FunNode fun=code.link(code._compunits.get("Root/A"));
+ CompUnit b=code._compunits.get("Root/B");
+ int[] touches={0};
+ code._stop.walk(n -> {
+ if( n instanceof CLInitNode init && init.fun()==fun && init.cu()==b ) {
+ touches[0]++;
+ TypeTuple state=(TypeTuple)init._type;
+ assertSame(TypeMem.BOT,state._types[1]);
+ TypeMem flag=(TypeMem)state._types[2];
+ assertEquals(b._clz.field("$init")._alias,flag._alias);
+ assertSame(TypeInteger.TRUE,flag._t);
+ assertNotNull(init.ready());
+ }
+ return null;
+ });
+ assertEquals(sources[i],counts[i],touches[0]);
+ }
+ }
+
+ @Test public void testForwardInstanceTouch() {
+ String src="""
+ struct S { int x=7; };
+ val _get={ -> _make().x; };
+ val _make={ -> new S; };
+ return _get();
+ """;
+ for( long seed : new long[]{1,123,987654321} ) {
+ CodeGen code=new CodeGen(src,seed,true).driver(CodeGen.Phase.Parse);
+ CLInitNode init=code._stop.walk(n -> n instanceof CLInitNode c ? c : null);
+ assertNotNull("The unresolved receiver needs a speculative check",init);
+ assertNotNull("The readiness load is present from parsing",init.ready());
+ code.driver(CodeGen.Phase.TypeCheck);
+ assertNull("An instance needs neither CLInit nor its speculative $init load",
+ code._stop.walk(n -> n instanceof CLInitNode ||
+ (n instanceof LoadNode ld && ld._name.equals("$init")) ? n : null));
+ assertEquals("7",Eval2.eval(code,0));
+ }
+ }
+
+ @Test public void testInitializerFlagStore() throws Exception {
+ // A same-named struct must retain the file class's hidden flag.
+ CodeGen code=compile("A",Map.of("A","struct A { int value; }; int value=arg;"),123);
+ CompUnit a=code._compunits.get("Root/A");
+ Field flag=a._clz.field("$init");
+ assertNotNull(flag);
+ assertFalse(flag._final);
+ FunNode fun=code.link(a);
+ int[] stores={0};
+ code._stop.walk(n -> {
+ if( n instanceof StoreNode st && st._name.equals("$init") && st._alias==flag._alias ) {
+ stores[0]++;
+ assertSame(TypeInteger.TRUE,st.val()._type);
+ assertSame(fun,CFGNode.earlyCFG(st).fun());
+ }
+ return null;
+ });
+ assertEquals(1,stores[0]);
+ }
+
@Test public void testOverlappingFunctionAndClassCycles() throws Exception {
// Fixed function graph: Opto/inlining must not erase the overlap that
// exercises SCC traversal. One clinit is legal, two in the SCC are not.
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 48f739cf..db41363a 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
@@ -88,7 +88,7 @@ public class CompoundAssignmentTest {
}
private static void check(String src, long arg, long expected) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Main.java b/chapter25/src/test/java/com/seaofnodes/simple/Main.java
index 589c043b..97129f5b 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Main.java
@@ -11,7 +11,7 @@ public class Main {
String src = Files.readString(Path.of(args[0]));
int arg = Integer.valueOf(args[1]);
CodeGen code = new CodeGen(src);
- code.parse().opto().typeCheck().GCM().localSched();
+ code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM().localSched();
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index 9406f480..4934c91f 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -58,7 +58,7 @@ public class MemoryEncodingTest {
// Exercise both unresolved Store aliases and nested value-Phi cleanup.
for( long seed : new long[]{0,9,126} )
for( int p=0; p<sources.length; p++ ) {
- CodeGen code=new CodeGen(sources[p],seed,true).parse().opto();
+ CodeGen code=new CodeGen(sources[p],seed,true).parse().iter().opto().iterPostOpto().finalizeIR();
if( p==0 ) assertNull(code._stop.walk(n ->
n instanceof LoadNode ld && ld.ptr() instanceof ProjNode ptr &&
ptr.in(0) instanceof NewNode nn &&
@@ -86,7 +86,7 @@ public class MemoryEncodingTest {
if (arg>1) { v=p.x; p.x=41; p.y=5; }
else { v=q.x; q.x=42; }
return v;
- """).parse().opto();
+ """).parse().iter().opto().iterPostOpto().finalizeIR();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 60e4c5aa..9727857e 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
@@ -6,12 +6,12 @@ import com.seaofnodes.simple.codegen.CodeGen;
public class MutabilityTest {
private static void ok(String src, String result) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
assertEquals(result,Eval2.eval(code,0));
}
private static void bad(String src) {
try {
- new CodeGen(src).parse().opto().typeCheck();
+ new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
index b7e10122..9078a33a 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
@@ -45,7 +45,9 @@ Options:
--dump-time - print compilation and execution times
--dump-after-parse - dump intermediate representation after parse
--dump-after-iter - dump intermediate representation after pessimistic iter pass
- --dump-after-opto - dump intermediate representation after opto pass
+ --dump-after-opto - dump intermediate representation after SCCP
+ --dump-after-iter-post-opto - dump after SCCP-driven peepholes and inlining
+ --dump-after-finalize-ir - dump after field layout and IR finalization
--dump-after-type-check - dump intermediate representation after type check pass
--dump-after-select - dump intermediate representation after instruction selection pass
--dump-after-gcm - dump intermediate representation after GCM pass
@@ -114,6 +116,8 @@ Options:
case "--dump-after-parse": dump |= 1<<Phase.Parse .ordinal() ; break;
case "--dump-after-iter": dump |= 1<<Phase.Iter .ordinal() ; break;
case "--dump-after-opto": dump |= 1<<Phase.Opto .ordinal() ; break;
+ case "--dump-after-iter-post-opto": dump |= 1<<Phase.IterPostOpto.ordinal(); break;
+ case "--dump-after-finalize-ir": dump |= 1<<Phase.FinalizeIR.ordinal() ; break;
case "--dump-after-type-check": dump |= 1<<Phase.TypeCheck .ordinal() ; break;
case "--dump-after-loop-tree": dump |= 1<<Phase.LoopTree .ordinal() ; break;
case "--dump-after-select": dump |= 1<<Phase.Select .ordinal() ; break;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index ec9e3910..55c2c040 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -133,7 +133,7 @@ public class Fuzzer {
double parse_nop_ratio = (double)parse_nops/parse_peeps;
double parse_peeps_per_node = (double)parse_peeps/nids;
- code.opto();
+ code.iter().opto().iterPostOpto().finalizeIR();
int iter_peeps= code._iter_cnt;
int iter_nops = code._iter_nop_cnt;
double iter_nop_ratio = (double)iter_nops/iter_peeps;
diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md
index 6b4462eb..821c7b2e 100644
--- a/docs/chapter-backports.md
+++ b/docs/chapter-backports.md
@@ -467,8 +467,20 @@ lines beyond the original extraction.
Allocator tuning is good enough: the residual Win64 NewtonExport ranking issue
is closed without a fix. Additional emulator-driver/measurement coverage is also
-closed without expansion (Cliff, 2026-10-08). The remaining active work is AOT
-class initialization below; the other items stay deferred for the chapter split.
+closed without expansion (Cliff, 2026-10-08). Active work is AOT class
+initialization below. Phase cleanup also needs backporting; the other items
+stay deferred for the chapter split.
+
+- **Compiler phase cleanup: needs backport (2026-10-17).** Chapter 25 separates
+ `Opto` (SCCP, numeric resolution, worklist seeding), `IterPostOpto` (peepholes
+ and inlining), and `FinalizeIR` (field layout/offsets, cleanup, function/CU
+ roots). `opto()` no longer silently runs pre-Iter; callers explicitly name
+ the required phases. Backport the applicable boundaries to earlier chapters,
+ updating tests, viewer/CLI callers, phase guards, timings, and dump options.
+ Numeric resolution stays in Opto where present. Keep CLInit expansion and
+ serialization changes separate. Chapter 25's full Make suite passes (505 tests),
+ including rebuilt sys.o and import/native execution. Earlier chapter compilers
+ are not yet changed or validated for this cleanup.
- **Chapter 25 deterministic inlining (deferred until after the Chapter 25 split).**
As of 2026-10-07, ordinary functions reject `_approxUIDs >= 100` before checking
diff --git a/skills/chapter25-codex-notes.md b/skills/chapter25-codex-notes.md
index f76c6fd6..878f231e 100644
--- a/skills/chapter25-codex-notes.md
+++ b/skills/chapter25-codex-notes.md
@@ -74,6 +74,35 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m
## Mutability and chapter boundaries
+- Chapter 25 phase split (2026-10-17): Opto now only runs SCCP, numeric-mode
+ resolution and worklist seeding. IterPostOpto does peepholes/inlining;
+ FinalizeIR does field layout/offset cleanup and function/CU finalization.
+ opto() requires an explicit prior iter(). Direct calls needing the old complete
+ optimization sequence also name iterPostOpto().finalizeIR(); driver() handles
+ the sequence for later targets. The full 505-test Make suite passes. Earlier
+ chapter backports are pending in docs/chapter-backports.md. CLInit expansion
+ and serialization ordering are not part of this refactor.
+- Class-init memory checkpoint (2026-10-17) is implemented: keep CLInit
+ opaque through Iter/Opto, carry whole-memory effects plus the known-true
+ `$init` alias result, and use normal parser memory for redundant checks.
+ The flag store belongs inside the initializer. Parser creates the ready load
+ up front; ordinary Load/FldOff nodes resolve forward receivers. The fourth
+ CLInit input is always present in a constructed graph. If the receiver resolves
+ to an instance, CLInit and its speculative flag load disappear together.
+ Precise alias recovery can still be deferred. See `chapter25/initialization.md`.
+ Runtime lowering and startup adaptation remain deferred. Public function fields
+ on instances of public classes must retain Start, just like class-object
+ fields. publicFIDX now checks both; this fixes sys.scan.match losing Start
+ while building sys.o and subsequently widening after import. All Chapter 25
+ Make suites pass (505 tests). Do not weaken assertions or reset imported types
+ to conceal incorrect public classification.
+ Future optimization: move final, always-constant instance fields to the class
+ or fold them to constants and omit their instance storage. Access semantics
+ and public reachability remain unchanged; do not implement layout changes yet.
+ Frequency estimates belong
+ after Opto and lowering: function entry 1, unknown branches 50/50, estimated
+ loop trips 10, rare init/failure edges, non-null/null 90/10; bounded,
+ flow-consistent estimates shared by GCM and Encoding.
- Class-init cycle validation (2026-10-14): reject an SCC only when it contains
more than one `<clinit>`. A self-edge or helper recursion with one initializer
is legal, regardless of inlining. Error reports list every internal edge of