summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-18 22:09:58 -0700
committerCliff Click <cliffc@acm.org>2026-10-18 22:09:58 -0700
commit300d2a9c3672acacc8bdd8d5370a424651a00571 (patch)
treef33d895f6ec4e08692efa4c090c681257553097d
parent0be52f8dd82f70d4c557fe916d47dc425401f848 (diff)
downloadSimple-300d2a9c3672acacc8bdd8d5370a424651a00571.tar.gz
Simple-300d2a9c3672acacc8bdd8d5370a424651a00571.zip
Cleanup & backportmainclinit
The phase rework, made new phases explicit and add them to the CodeGen driver. Fix the flag-spilling on X86 and ARM. Fix the GCM conservative anti-deps and backport.
-rw-r--r--chapter10/README.md8
-rw-r--r--chapter13/README.md8
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/SchedulerTest.java21
-rw-r--r--chapter14/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter14/src/test/java/com/seaofnodes/simple/SchedulerTest.java21
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/SchedulerTest.java21
-rw-r--r--chapter16/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter16/src/test/java/com/seaofnodes/simple/SchedulerTest.java21
-rw-r--r--chapter17a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter17a/src/test/java/com/seaofnodes/simple/SchedulerTest.java21
-rw-r--r--chapter17b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter17b/src/test/java/com/seaofnodes/simple/SchedulerTest.java21
-rw-r--r--chapter18/README.ja.md2
-rw-r--r--chapter18/README.md32
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java112
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/TypeCheck.java15
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/BrainFuckTest.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter10Test.java56
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java30
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter13Test.java28
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java94
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java68
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Eval2.java4
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/MergeSortTest.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/PhaseTest.java33
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java2
-rw-r--r--chapter19/README.md10
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/CodeGen.java189
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/InstSelect.java74
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/TypeCheck.java15
-rw-r--r--chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/BrainFuckTest.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter10Test.java56
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java30
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter13Test.java28
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java94
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter18Test.java60
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Chapter19Test.java88
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Eval2.java4
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/MergeSortTest.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/PhaseTest.java41
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter19/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java2
-rw-r--r--chapter20/README.md5
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java233
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java11
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java69
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java2
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java6
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java2
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java15
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java13
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java11
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java13
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java12
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java8
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java2
-rw-r--r--chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter10Test.java56
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java30
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter13Test.java28
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java94
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter18Test.java60
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter19Test.java82
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Chapter20Test.java15
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Eval2.java4
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/PhaseTest.java41
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java57
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java11
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter20/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java2
-rw-r--r--chapter21/README.md12
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java289
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java11
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java90
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java6
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java15
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java21
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java11
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java11
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java25
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java6
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java6
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java56
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java30
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java31
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java94
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java40
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java12
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java13
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Compile.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Eval2.java5
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java4
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/PhaseTest.java42
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Simple.java143
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java145
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java15
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter22/README.ja.md2
-rw-r--r--chapter22/README.md19
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java311
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java30
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java11
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java90
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/LRG.java2
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java6
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java15
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java21
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java11
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java11
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java25
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java6
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java6
-rw-r--r--chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter10Test.java56
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java30
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter13Test.java31
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java94
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter18Test.java40
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter19Test.java12
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter20Test.java11
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java2
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java8
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Compile.java2
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/LiteralTest.java4
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java4
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/PhaseTest.java42
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/Simple.java149
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java145
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java15
-rw-r--r--chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter23/README.md8
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java330
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java48
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java11
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java90
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/LRG.java2
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java6
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java15
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java21
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java11
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java11
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java25
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java6
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java6
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter23/src/main/java/com/seaofnodes/simple/type/TypeStruct.java2
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java58
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java32
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter13Test.java31
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java90
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter18Test.java40
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter19Test.java12
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter20Test.java11
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter21Test.java2
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter22Test.java8
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java8
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Compile.java2
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/LiteralTest.java4
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java4
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/PhaseTest.java42
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/Simple.java149
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java145
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java15
-rw-r--r--chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter24/#README.md#338
-rw-r--r--chapter24/.#README.md1
-rw-r--r--chapter24/README.md239
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java293
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java76
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java11
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java90
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/LRG.java2
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/Opto.java85
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java6
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java15
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java21
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java11
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java11
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java25
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java6
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java6
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/type/TypeStruct.java2
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter05Test.java16
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter06Test.java32
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter07Test.java14
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter08Test.java22
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter09Test.java40
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java58
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java32
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter13Test.java26
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java14
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter14Test.java30
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter15Test.java42
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter16Test.java18
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter17aTest.java16
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java90
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter18Test.java40
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter19Test.java12
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter20Test.java11
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java2
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java8
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java8
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter24Test.java12
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Compile.java2
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/LiteralTest.java4
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Main.java2
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java4
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/MutabilityTest.java4
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/PhaseTest.java56
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/SchedulerTest.java26
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Simple.java155
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java145
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java15
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java2
-rw-r--r--chapter25/README.ja.md4
-rw-r--r--chapter25/README.md75
-rw-r--r--chapter25/ROADMAP.md39
-rw-r--r--chapter25/initialization.md842
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/Parser.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java604
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java44
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java60
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java126
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Iter.java60
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java12
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java52
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/StaticData.java18
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java57
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Unlink.java112
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java14
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java60
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java5
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java27
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java3
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java11
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java21
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java11
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java11
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java25
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java40
-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.java32
-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.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java8
-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.java39
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java82
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Eval2.java18
-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/PhaseTest.java64
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Simple.java2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java20
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java141
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java16
-rw-r--r--docs/chapter-backports.md1377
-rw-r--r--isa/src/main/java/com/seaofnodes/isa/Arm64.java6
-rw-r--r--isa/src/main/java/com/seaofnodes/isa/X86.java7
-rw-r--r--isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java16
-rw-r--r--isa/src/test/java/com/seaofnodes/isa/RiscTest.java16
-rw-r--r--isa/src/test/java/com/seaofnodes/isa/X86Test.java4
-rw-r--r--skills/chapter25-codex-notes.md107
-rw-r--r--skills/graph-viewer-codex-notes.md3
403 files changed, 7560 insertions, 7114 deletions
diff --git a/chapter10/README.md b/chapter10/README.md
index e55d1057..db72c4cb 100644
--- a/chapter10/README.md
+++ b/chapter10/README.md
@@ -385,6 +385,14 @@ The single memory chain is correct but conservative. A store to `y` can
prevent forwarding an earlier store to `x`, even when the objects are
identical. This is a missed optimization and it motivates the next chapter.
+## Checking the optimized graph
+
+Memory accesses can expose type errors only after their pointer types settle.
+The post-parse pipeline therefore adds `TypeCheck` after iterative peepholes.
+It walks the surviving graph and reports node errors; dead control has already
+been removed. Here `StopNode.iterate()` runs both steps. Chapter 18 introduces
+the `CodeGen` driver and explicit phase entry points.
+
## Executing the graph
The evaluator includes a small late scheduler. A load must execute before a
diff --git a/chapter13/README.md b/chapter13/README.md
index 7df00642..7794c2eb 100644
--- a/chapter13/README.md
+++ b/chapter13/README.md
@@ -352,6 +352,14 @@ stores, we do the same - until/unless we find stores with the SAME block as the
Load's LCA. Then we add the anti-dependency edge to force ordering within the
same block.
+For a memory Phi, constrain the paths whose inputs differ from the Load's
+memory; paths carrying that memory unchanged do not overwrite it. A different
+input can come from a sibling branch, so its dominator chain need not reach
+the memory definition's block. Stop the walk when it rises above that block's
+depth, or reaches its immediate dominator. Include the boundary depth so
+same-block ordering is preserved. The immediate-dominator sentinel also handles
+a root with no dominator.
+
See [`antiUse()`](src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java#L224)
for the alias filter, and
[`find_anti_dep()`](src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java#L233)
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 78a0dcde..b431f82c 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -244,10 +244,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
default: throw Utils.TODO();
@@ -260,7 +261,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// Preserve the full store range for the earlier evaluator scheduler.
// It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter13/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index ad595822..55aa5d2b 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,27 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; S a=new S; S b=new S;
+ a.x=10; b.x=20; S p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ StopNode stop = new Parser(src).parse().iterate();
+ var eval = new Evaluator(stop);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(expected[arg],eval.evaluate(arg,100));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
diff --git a/chapter14/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter14/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 78a0dcde..b431f82c 100644
--- a/chapter14/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter14/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -244,10 +244,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
default: throw Utils.TODO();
@@ -260,7 +261,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// Preserve the full store range for the earlier evaluator scheduler.
// It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter14/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter14/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index ad595822..55aa5d2b 100644
--- a/chapter14/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter14/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,27 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; S a=new S; S b=new S;
+ a.x=10; b.x=20; S p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ StopNode stop = new Parser(src).parse().iterate();
+ var eval = new Evaluator(stop);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(expected[arg],eval.evaluate(arg,100));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index d17ec3b6..4c9fc779 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -246,10 +246,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
default: throw Utils.TODO();
@@ -262,7 +263,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// Preserve the full store range for the earlier evaluator scheduler.
// It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter15/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index ad595822..55aa5d2b 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,27 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; S a=new S; S b=new S;
+ a.x=10; b.x=20; S p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ StopNode stop = new Parser(src).parse().iterate();
+ var eval = new Evaluator(stop);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(expected[arg],eval.evaluate(arg,100));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
diff --git a/chapter16/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter16/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index bb109fc9..e153fcc7 100644
--- a/chapter16/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter16/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -246,10 +246,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
default: throw Utils.TODO();
@@ -262,7 +263,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// Preserve the full store range for the earlier evaluator scheduler.
// It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk; stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( !(stblk instanceof IfNode) && anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter16/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter16/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 4b692f51..2ca03fcf 100644
--- a/chapter16/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter16/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,27 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; S a=new S; S b=new S;
+ a.x=10; b.x=20; S p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ StopNode stop = new Parser(src).parse().iterate();
+ var eval = new Evaluator(stop);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(expected[arg],eval.evaluate(arg,100));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
diff --git a/chapter17a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter17a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index eba99128..c6d02b27 100644
--- a/chapter17a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter17a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -207,10 +207,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
default: throw Utils.TODO();
@@ -223,7 +224,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// Preserve the full store range for the earlier evaluator scheduler.
// It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter17a/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter17a/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index d8a22fd3..7f566f04 100644
--- a/chapter17a/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter17a/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,27 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ StopNode stop = new Parser(src).parse().iterate();
+ var eval = new Evaluator(stop);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(expected[arg],eval.evaluate(arg,100));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
diff --git a/chapter17b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter17b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index eba99128..c6d02b27 100644
--- a/chapter17b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter17b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -207,10 +207,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
default: throw Utils.TODO();
@@ -223,7 +224,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// Preserve the full store range for the earlier evaluator scheduler.
// It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter17b/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter17b/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index d8a22fd3..7f566f04 100644
--- a/chapter17b/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter17b/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,27 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ StopNode stop = new Parser(src).parse().iterate();
+ var eval = new Evaluator(stop);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(expected[arg],eval.evaluate(arg,100));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
diff --git a/chapter18/README.ja.md b/chapter18/README.ja.md
index 6c0396b4..ad078163 100644
--- a/chapter18/README.ja.md
+++ b/chapter18/README.ja.md
@@ -214,7 +214,7 @@ Add は残りますが、今度は `arg` を直接消費します。引数が `3
```java
CodeGen code = new CodeGen("val sq = { int x -> x*x; }; return sq(arg);");
-code.parse().opto().typeCheck().GCM().localSched();
+code.driver(CodeGen.Phase.LocalSched);
```
大域的コード移動は、[第13章](../chapter13/README.ja.md)の処理を複数の関数に拡張します。関数本体を別々に保ち、共有される定数式には、それを使う各関数内に局所的なコピーを与え、呼び出しをまたぐメモリへの作用を尊重します。
diff --git a/chapter18/README.md b/chapter18/README.md
index 9b13e277..578ed7d4 100644
--- a/chapter18/README.md
+++ b/chapter18/README.md
@@ -81,8 +81,7 @@ es[1] = 9;
return find(es,9); // 1
```
-All reachable results of one function must have a common type. This is checked
-after optimization, so an unreachable return does not cause a type error.
+Return types are generally inferred, so all returns must have a common type.
There is no `void` type although a function can return `null` with no syntax,
and callers can ignore a result by using the call as a statement.
@@ -161,10 +160,10 @@ body contains branches, loops, and several return statements.
### Calls and return points
[CallNode](src/main/java/com/seaofnodes/simple/node/CallNode.java) takes inputs
-`{control, memory, arguments..., function pointer}`. The final input determines
-the target; it can be a constant function or the result of a function-valued
-expression. Argument expressions are evaluated before the Call takes its
-control and memory inputs.
+`{control, memory, arguments..., function pointer}`. The final input
+determines the target; it can be a constant function or the result of a
+function-valued expression. Argument expressions are evaluated in order before the Call
+takes its control and memory inputs.
Each Call has a [CallEndNode](src/main/java/com/seaofnodes/simple/node/CallEndNode.java),
the point where its caller continues. CallEnd takes the Call and the Returns
@@ -323,8 +322,9 @@ compilation process and its function lookup table. It enforces the phase order:
| Phase | Purpose |
|---|---|
| Parse | Build the functions and calls directly in the IR. |
-| Opto | Iterate peepholes, including call linking and trivial inlining. |
+| Iter | Iterate peepholes, including call linking and trivial inlining. |
| TypeCheck | Check the surviving graph after types and dead control have settled. |
+| LoopTree | Discover loop nesting and connect nonreturning loops with synthetic exits. |
| Schedule | Use global code motion to place operations in basic blocks. |
| LocalSched | Order operations within each block for execution. |
@@ -332,9 +332,25 @@ For example:
```java
CodeGen code = new CodeGen("val sq = { int x -> x*x; }; return sq(arg);");
-code.parse().opto().typeCheck().GCM().localSched();
+code.driver(CodeGen.Phase.TypeCheck);
```
+With six stages, writing the whole call chain at every use obscures what the
+caller needs. `driver(target)` runs the missing phases in order and stops at
+the requested stage. A later call resumes from there; asking for an already
+completed stage does not rerun it:
+
+```java
+code.driver(CodeGen.Phase.LoopTree); // Ready to inspect loops.
+code.driver(CodeGen.Phase.LocalSched); // Finish scheduling.
+```
+
+Short tests can still say `code.parse().iter()` to show exactly what they test.
+`Iter` is the worklist solve introduced in Chapter 9; an optimistic `Opto`
+phase arrives with SCCP in Chapter 24. Phase entry points check ordering and
+report their completion to the graph viewer. Loop-tree construction is now
+an explicit entry point instead of work hidden inside GCM.
+
Global code motion extends the work of [Chapter 13](../chapter13/README.md) to
multiple functions. It keeps their bodies separate, gives shared constant
expressions a local copy in each function that uses them, and respects memory
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java
index 864b3a1a..e5ac8530 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java
@@ -1,4 +1,4 @@
- package com.seaofnodes.simple;
+package com.seaofnodes.simple;
import com.seaofnodes.graph.GraphObserver;
@@ -20,10 +20,45 @@ public class CodeGen {
public enum Phase {
- Parse, Opto, TypeCheck, Schedule, LocalSched, RegAlloc;
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ Schedule, // Global code motion
+ LocalSched; // Order instructions within blocks
+
}
public Phase _phase;
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) {
+ step(target, Phase.Parse, this::parse);
+ step(target, Phase.Iter, this::iter);
+ step(target, Phase.TypeCheck, this::typeCheck);
+ step(target, Phase.LoopTree, this::loopTree);
+ step(target, Phase.Schedule, this::GCM);
+ step(target, Phase.LocalSched, this::localSched);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ work.run();
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
+
public final String _src;
// Compile-time known initial argument type
public final TypeInteger _arg;
@@ -46,71 +81,38 @@ public class CodeGen {
public CodeGen( String src ) { this(src,TypeInteger.BOT); }
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- _linker.clear();
-
- P = new Parser(_src,_arg);
- _stop = P.parse();
- _start = Parser.START; // Capture global start
- if( _obs != null ) _obs.phase("Parse");
- P = null; // No longer parsing
- return this;
- }
+ return phase(Phase.Parse, _phase==null, () -> {
+ _linker.clear();
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
-
- // Optimization
- IterPeeps.iterate(_stop);
-
- // TODO:
- // Optimistic
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
-
- public CodeGen typeCheck() {
- // Demand phase Opto for cleaning up dead control flow at least,
- // required for the following GCM.
- assert _phase == Phase.Opto;
- _phase = Phase.TypeCheck;
-
- // Type check
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
+ P = new Parser(_src,_arg);
+ _stop = P.parse();
+ _start = Parser.START; // Capture global start
+ P = null; // No longer parsing
});
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
}
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> IterPeeps.iterate(_stop)); }
- public CodeGen GCM() {
- assert _phase == Phase.TypeCheck;
- _phase = Phase.Schedule;
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.Iter, () -> TypeCheck.run(this)); }
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_stop);
+ public CodeGen loopTree() {
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> _start.buildLoopTree(_stop));
+ }
- // TODO:
- // loop unroll, peel, RCE, etc
- GlobalCodeMotion.buildCFG(_start,_stop);
- return this;
+ public CodeGen GCM() {
+ return phase(Phase.Schedule, _phase==Phase.LoopTree, () -> {
+ // TODO:
+ // loop unroll, peel, RCE, etc
+ GlobalCodeMotion.buildCFG(_start,_stop);
+ });
}
// Local (basic block) scheduler phase, a classic list scheduler
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- ListScheduler.sched(this);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// Reverse from a constant function pointer to the IR function being called
public FunNode link( TypeFunPtr tfp ) {
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 8221ede1..2019b714 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -260,10 +260,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -278,7 +279,9 @@ public abstract class GlobalCodeMotion {
//
private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/TypeCheck.java b/chapter18/src/main/java/com/seaofnodes/simple/TypeCheck.java
new file mode 100644
index 00000000..a41e4161
--- /dev/null
+++ b/chapter18/src/main/java/com/seaofnodes/simple/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index b2dd3107..4db81c70 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter18/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.driver(CodeGen.Phase.TypeCheck);
}
@Override protected String phase() { return _code._phase.name(); }
@Override protected int pos() { return _code.P != null ? _code.P.pos() : -1; }
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter18/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index 37ecde1c..43314312 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -63,7 +63,7 @@ for( int pc = 0; pc < program#; pc++ ) {
}
return output;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000));
}
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index ff60c695..e88474a4 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -19,7 +19,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print());
}
@@ -35,7 +35,7 @@ if (arg == 1) {
}
return c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -63,7 +63,7 @@ if( arg==1 )
else
b=a+1;
return a+b;""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg*2)+Phi(Region,2,3));", code.print());
}
@@ -78,7 +78,7 @@ if( arg==1 )
else
a=b+1;
return a+b;""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((Phi(Region,(arg*2),arg)+arg)+Phi(Region,4,5));", code.print());
}
@@ -98,7 +98,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", code.print());
}
@@ -114,7 +114,7 @@ if( arg==0 )
b=2;
return arg+a+b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -129,7 +129,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -144,7 +144,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 2107cdb0..ed51ebe3 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -14,7 +14,7 @@ public class Chapter06Test {
if( true ) return 2;
return 1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -28,7 +28,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();
assertEquals("return 1;", code.print());
}
@@ -43,7 +43,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -64,7 +64,7 @@ else
b=5;
return b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,42,5);", code.print());
}
@@ -83,7 +83,7 @@ if( arg==2 )
else
b=5;
return b;""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,2,5);", code.print());
}
@@ -104,7 +104,7 @@ else
a=5;
return a;
""", TypeInteger.constant(2));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
}
@@ -125,7 +125,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -145,7 +145,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,3,Phi(Region,4,5));", code.print());
}
@@ -166,7 +166,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -187,7 +187,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -203,7 +203,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 4;", code.print());
}
@@ -238,7 +238,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
}
@@ -256,7 +256,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", code.print());
}
@@ -276,7 +276,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 6;", code.print());
}
@@ -295,7 +295,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2));
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 6d6a3ce7..a5c6ef14 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -33,7 +33,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print());
}
@@ -53,7 +53,7 @@ while(i < arg) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print());
}
@@ -88,7 +88,7 @@ while(a < 10) {
}
return b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -121,7 +121,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -147,7 +147,7 @@ int a = 1;
while(arg) a = 2;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -179,7 +179,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -213,7 +213,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 03a2c50a..379bb55b 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -23,7 +23,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
Assert.assertEquals( "5", Eval2.eval(code, 1));
@@ -46,7 +46,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -65,7 +65,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -81,7 +81,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -100,7 +100,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -117,7 +117,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -133,14 +133,14 @@ while( arg < 10 ) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
}
@@ -168,7 +168,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -185,7 +185,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -201,7 +201,7 @@ while(1) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index b210f7e9..410e32cd 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,(arg*2),(Mul+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();
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();
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();
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();
assertEquals("return 0;", code.print());
}
@@ -127,14 +127,14 @@ while(v1+arg) {
v1=v2;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testWhile0() {
CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -146,7 +146,7 @@ if(0) while(0) {
while(0) {}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -154,14 +154,14 @@ if(0) while(0) {
@Test
public void testPrecedence() {
CodeGen code = new CodeGen("return 3-1+2;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -179,7 +179,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
}
@@ -193,7 +193,7 @@ arg=0;
int v0=0!=0<-0;
return -0+0+0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -201,14 +201,14 @@ return -0+0+0;
@Test
public void testFuzz2() {
CodeGen code = new CodeGen("return 0+-0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testFuzz3() {
CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -220,7 +220,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -242,7 +242,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -254,7 +254,7 @@ int v0=0;
while(0==1) while(v0)
v0=1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ~int;", code.print());
}
@@ -270,14 +270,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index bcae373b..2681e878 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -13,7 +13,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().typeCheck();
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -38,7 +38,7 @@ public class Chapter10Test {
"if (!!!p) return -1; return p.x;",
"int b = !!p; if (b) return p.x + b - 1; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -53,7 +53,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -85,7 +85,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -105,7 +105,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
}
@@ -121,7 +121,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Vector2D;", code.print());
}
@@ -159,7 +159,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -172,7 +172,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); }
}
@@ -185,7 +185,7 @@ if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
}
@@ -211,7 +211,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -227,7 +227,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -244,7 +244,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) {
assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@@ -281,7 +281,7 @@ while( i.x < i.len ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -300,7 +300,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -318,7 +318,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -335,7 +335,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -362,7 +362,7 @@ if(0) {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -376,7 +376,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return s0;", code.print());
}
@@ -392,7 +392,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -404,7 +404,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)s0;", code.print());
}
@@ -436,7 +436,7 @@ while(0) {}
} }
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -447,7 +447,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -461,7 +461,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -487,7 +487,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -503,7 +503,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -520,7 +520,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -535,7 +535,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -553,7 +553,7 @@ return 0;
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();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 9fd392d2..b21d9e0e 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -12,7 +12,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -31,7 +31,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -59,7 +59,7 @@ public class Chapter11Test {
field = work.getClass().getDeclaredField("_R");
field.setAccessible(true);
((java.util.Random)field.get(work)).setSeed(seed);
- code.opto();
+ code.iter();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index a15132d4..aa4f1ae5 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -13,7 +13,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -33,7 +33,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -55,7 +55,7 @@ LLI? next = head.next;
if( next==null ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("2", Eval2.eval(code, 3));
}
@@ -74,7 +74,7 @@ i0.f = f0;
f0.i = i0;
return f0.i.f.i.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 17;", code.print());
}
@@ -86,7 +86,7 @@ struct N { !N? !next; int i; };
N n = new N;
return n.next;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return null;", code.print());
}
@@ -99,7 +99,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)M;", code.print());
}
@@ -112,7 +112,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; }
return n.next;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); }
}
@@ -125,20 +125,20 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("3",Eval2.eval(code,0));
}
@Test
public void testNullRef4() {
CodeGen code = new CodeGen("-null-5/null-5;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@@ -148,7 +148,7 @@ return n.i;
"""
struct S{};
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -160,7 +160,7 @@ struct S{};
struct S1 { !S2? !s; };
return new S2;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
}
@@ -172,7 +172,7 @@ struct S1 { !S2? !s; };
struct S2 { int x; };
return new S1.s=new S2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S2;", code.print());
}
@@ -189,7 +189,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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -254,7 +254,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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=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/chapter18/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 584377e4..498796ea 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -290,7 +290,7 @@ else {
}
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
}
@@ -322,7 +322,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))));", code.print());
assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
assertEquals("1", Eval2.eval(code, 2)); // 2
@@ -344,7 +344,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -367,7 +367,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -386,7 +386,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -404,7 +404,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -421,7 +421,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -436,7 +436,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -454,7 +454,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -474,7 +474,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -492,7 +492,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -511,7 +511,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
//assertEquals("return new S;", code.print());
}
@@ -528,7 +528,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -546,7 +546,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index a40b4f04..473a06fa 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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().typeCheck();
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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().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));
@@ -42,7 +42,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().typeCheck();
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)/2.0f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index 789a2113..c00549e2 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
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();
+ code.parse().iter();
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();
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();
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();
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();
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().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();
assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -194,7 +194,7 @@ if(0&0>>>0) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 97f695bf..5b8e0fea 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -13,7 +13,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -27,7 +27,7 @@ struct C { !C? !l; };
c.l = c;
return c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return C;", code.print());
assertEquals("C{l=$cyclic}", Eval2.eval(code, 0));
}
@@ -46,7 +46,7 @@ while (i < old#) {
output[i] = 1;
return output;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [u8];", code.print());
assertEquals("\u0001", Eval2.eval(code, 0));
}
@@ -58,7 +58,7 @@ return output;
int[] is = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -71,7 +71,7 @@ int[] is = new int[2];
int[] is2 = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -85,7 +85,7 @@ a[0] = 1;
a[1] = 2;
return a[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -98,7 +98,7 @@ struct A { int i; };
!A?[] !a = new !A?[2];
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*A?];", code.print());
assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -127,7 +127,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
assertEquals("1.2", Eval2.eval(code, 1));
@@ -153,7 +153,7 @@ if( iss[arg] )
rez = iss[arg][2].y;
return rez;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing '[]'",e.getMessage()); }
}
@@ -168,7 +168,7 @@ root._kids = new !Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
root._kids[0] = new Tree;
return root;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -180,7 +180,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -203,7 +203,7 @@ while( i < ary# - 1 ) {
}
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (.[]+(.[]*1000));", code.print());
assertEquals("1006", Eval2.eval(code, 4));
}
@@ -244,7 +244,7 @@ while( j < nprimes ) {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -261,7 +261,7 @@ s2.f = 2.0;
if (arg) s1 = new S;
return s1.i + s1.f;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt).i+.f);", code.print());
assertEquals("0.0", Eval2.eval(code, 0));
}
@@ -273,7 +273,7 @@ return s1.i + s1.f;
"""
return new flt;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@@ -283,7 +283,7 @@ return new flt;
"""
int is = new int[2];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); }
}
@@ -294,7 +294,7 @@ int is = new int[2];
int[] is = new int[3.14];
return is[1];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); }
}
@@ -305,7 +305,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 4));
try { Eval2.eval(code,0); }
@@ -335,7 +335,7 @@ while(v5+(0&0)) {
}
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -350,7 +350,7 @@ u32 v7=0;
int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -374,7 +374,7 @@ else {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index fd028d57..d2ef9357 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -16,7 +16,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -29,7 +29,7 @@ return 3.14;
int x, y;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -40,7 +40,7 @@ return x+y;
int x=2, y=x+1;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -53,7 +53,7 @@ struct X { int x=3; };
X z = new X;
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -65,7 +65,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@@ -86,7 +86,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -117,7 +117,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -142,7 +142,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -166,7 +166,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter();
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index ab92d439..52f20ace 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -16,7 +16,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();
assertEquals("return Phi(Region,9,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -29,7 +29,7 @@ struct X { int ~x; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -41,7 +41,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -54,7 +54,7 @@ Point p = new Point { x=5; y=6; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -66,7 +66,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -74,7 +74,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).parse().iter(); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -87,7 +87,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -99,7 +99,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e83b43af..a5ce8f49 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -16,7 +16,7 @@ int i = 0;
i=i=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -28,7 +28,7 @@ return i;
CodeGen code = new CodeGen("""
return arg++;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -38,7 +38,7 @@ return arg++;
CodeGen code = new CodeGen("""
return arg+++arg++;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg*2)+1);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -49,7 +49,7 @@ return arg+++arg++;
// -(arg--)-(arg--)
return -arg---arg--;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (-((arg*2)+-1));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -62,7 +62,7 @@ xs[0]++;
xs[1]++;
return xs[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -74,7 +74,7 @@ u8[] !xs = new u8[1];
xs[0]--;
return xs[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 255;", code.print());
assertEquals("255", Eval2.eval(code, 2));
}
@@ -87,7 +87,7 @@ struct S { u16 x; };
s.x--;
return s.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 65535;", code.print());
assertEquals("65535", Eval2.eval(code, 2));
}
@@ -95,7 +95,7 @@ return s.x;
@Test
public void testInc6() {
CodeGen code = new CodeGen("return --arg;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (arg+-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
}
@@ -103,7 +103,7 @@ return s.x;
@Test
public void testInc7() {
CodeGen code = new CodeGen("u8 x=0; return --x;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 255;", code.print());
assertEquals("255", Eval2.eval(code, 0));
}
@@ -111,7 +111,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();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -119,14 +119,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();
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();
+ code.parse().iter();
assertEquals("2",Eval2.eval(code,0));
}
@@ -134,73 +134,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(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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();
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();
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();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter();
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().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;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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();
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();
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().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
~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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();
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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -264,7 +264,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -277,7 +277,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
@@ -291,7 +291,7 @@ val s = new S{x = 2;};
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -310,7 +310,7 @@ focus(me);
return me;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@@ -320,7 +320,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -331,7 +331,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -343,7 +343,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -356,7 +356,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x*2)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -370,7 +370,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=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();
assertEquals("return Phi(Region,Phi(Region,.x,.x),0);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -383,7 +383,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();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -395,7 +395,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -408,7 +408,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -421,7 +421,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -436,7 +436,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -449,7 +449,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -462,7 +462,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -470,7 +470,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -486,7 +486,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -500,7 +500,7 @@ struct A{
};
return new A.not_array_of_b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*B?];", code.print());
assertEquals("*B?[]", Eval2.eval(code,0));
}
@@ -519,7 +519,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -550,7 +550,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 7f2b92ac..87e703b4 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -12,24 +12,24 @@ public class Chapter18Test {
@Test public void testFloatArrays() {
for( String type : new String[]{"flt","f64","f32"} ) {
CodeGen zero = new CodeGen("var a = new "+type+"[arg]; return a[arg-1];")
- .parse().opto().typeCheck();
+ .parse().iter().typeCheck();
assertEquals("0.0",Eval2.eval(zero,3));
new CodeGen(type+" x = arg; var a = new "+type+"[arg]; a[0] = x; return a[0];")
- .parse().opto().typeCheck();
+ .parse().iter().typeCheck();
CodeGen loop = new CodeGen("var a = new "+type+"[arg]; "+
"for(int i=0; i<arg; i++) a[i] += i+0.25; return a[0]+a[arg-1];")
- .parse().opto().typeCheck();
+ .parse().iter().typeCheck();
assertEquals("0.5",Eval2.eval(loop,1));
assertEquals("3.5",Eval2.eval(loop,4));
CodeGen rounded = new CodeGen("var a = new "+type+"[arg]; "+
"for(int i=0; i<arg; i++) a[i] = 16777216.0+i; return a[arg-1];")
- .parse().opto().typeCheck();
+ .parse().iter().typeCheck();
assertEquals(type.equals("f32") ? "1.6777216E7" : "1.6777217E7",Eval2.eval(rounded,2));
}
CodeGen rounded = new CodeGen("var a = new f32[arg]; "+
"for(int i=0; i<arg; i++) a[i] = 16777217.0; return a[arg-1];")
- .parse().opto().typeCheck();
+ .parse().iter().typeCheck();
assertEquals("1.6777216E7",Eval2.eval(rounded,2));
}
@@ -41,7 +41,7 @@ public class Chapter18Test {
"int x=0; while(arg) { if(arg==2) while(1) { x++; } x++; arg--; } return x;"
};
for( String src : sources ) {
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().typeCheck();
code._start.buildLoopTree(code._stop);
int[] nevers = {0};
code._stop.walk(n -> {
@@ -74,7 +74,7 @@ public class Chapter18Test {
"val g = { int x -> x ? g(x-1)/305420988+x*305420988 : 1; }; "+
"return f(arg)+g(arg);";
CodeGen code = new CodeGen(src);
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
Node con = code._stop.walk(n -> n instanceof ConstantNode &&
n._type==TypeInteger.constant(305420988) ? n : null);
assertNotNull(con);
@@ -85,7 +85,7 @@ public class Chapter18Test {
for( Node use : uses )
for( int i=1; i<use.nIns(); i++ )
if( use.in(i)==con ) use.setDef(i,cast);
- code.GCM();
+ code.driver(CodeGen.Phase.Schedule);
var owners = new java.util.IdentityHashMap<CFGNode,Boolean>();
code._stop.walk(n -> {
if( !(n instanceof CastNode) || !(n.in(1) instanceof CastNode) ) return null;
@@ -122,7 +122,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().typeCheck();
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -153,7 +153,7 @@ public class Chapter18Test {
"""
return 0;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -171,7 +171,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -188,7 +188,7 @@ return a;
{int -> int}? x2 = null; // null function ptr
return x2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -202,7 +202,7 @@ return x2;
};
return sq;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
assertEquals("{ sq}", Eval2.eval(code, 3));
}
@@ -216,7 +216,7 @@ var sq = { int x ->
};
return sq(arg)+sq(3);
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -229,7 +229,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -241,7 +241,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -254,7 +254,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)*2); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -264,7 +264,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();
+ code.parse().iter();
assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -281,7 +281,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -296,7 +296,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -314,7 +314,7 @@ for(;;) {
i2i = id(x);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return x( Phi(Loop,arg,x( 3))); return Parm_i(x,int,3,Phi_arg); ]", code._stop.toString());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -331,7 +331,7 @@ for(;;) {
arg = x(3);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return Top;", code._stop.toString());
assertEquals(null, Eval2.eval(code, 0));
}
@@ -350,7 +350,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -365,7 +365,7 @@ for(;;) {
i2i = null;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
}
@@ -377,7 +377,7 @@ for(;;) {
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -395,7 +395,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -408,7 +408,7 @@ val g = { ->
};
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -422,7 +422,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();
+ code.parse().iter();
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -444,7 +444,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -461,7 +461,7 @@ if (arg) i2i = null;
if (i2i) return i2i(arg);
return f2f(o)(1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,o( arg),o( 1)); return Parm_i(o,int,arg,1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -477,7 +477,7 @@ struct Person {
p.coffee_count += 1;
return p.coffee_count;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -496,7 +496,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).parse().iter();
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -525,7 +525,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).parse().iter();
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -543,7 +543,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).parse().iter();
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter18/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 860827d7..2c1a138d 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter18/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().typeCheck();
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java
index 3f337e86..e62b63a6 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -114,9 +114,7 @@ public abstract class Eval2 {
public static String eval( CodeGen code, long arg, int timeout ) {
SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
// Force local scheduling phase
- if( code._phase.ordinal() < CodeGen.Phase.TypeCheck .ordinal() ) code.typeCheck();
- if( code._phase.ordinal() < CodeGen.Phase.Schedule .ordinal() ) code.GCM();
- if( code._phase.ordinal() < CodeGen.Phase.LocalSched.ordinal() ) code.localSched();
+ code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
CODE = code;
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter18/src/test/java/com/seaofnodes/simple/MergeSortTest.java
index a352530f..08b50d2c 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/MergeSortTest.java
@@ -57,7 +57,7 @@ merge_sort(a, b, a#);
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return [int]; return 0; ]", code._stop.toString());
assertEquals("int[ 1,2,3,4,5,6,7,8,9,10,11]", Eval2.eval(code, 11));
}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter18/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 0861424f..79d68843 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/MutabilityTest.java
@@ -5,12 +5,12 @@ import static org.junit.Assert.*;
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().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().typeCheck();
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter18/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..d0cb173a
--- /dev/null
+++ b/chapter18/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,33 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+}
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter18/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index d29166a7..95c062b5 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -30,7 +50,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -45,7 +65,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -64,7 +84,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index a5634e82..31274b58 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
@@ -134,7 +134,7 @@ public class Fuzzer {
double parse_peeps_per_node = (double)parse_peeps/nids;
- code.opto();
+ code.iter();
int iter_peeps= Node.ITER_CNT;
int iter_nops = Node.ITER_NOP_CNT;
double iter_nop_ratio = (double)iter_nops/iter_peeps;
diff --git a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index 4509c6fc..254f9653 100644
--- a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -130,7 +130,7 @@ class FuzzerUtils {
var code = new CodeGen(script);
code.parse();
WorkRandom.setSeed(workListSeed);
- return code.opto().typeCheck().GCM().localSched();
+ return code.iter().driver(CodeGen.Phase.Schedule).localSched();
} finally {
NodeWalkVisit.clear();
System.setErr(err);
diff --git a/chapter19/README.md b/chapter19/README.md
index d8f7c9e1..c7bd1b9f 100644
--- a/chapter19/README.md
+++ b/chapter19/README.md
@@ -43,6 +43,16 @@ the porting code is looked up and used then. This allows the Java
implementation at least the ability to add CPU ports lazily.
+The driver from Chapter 18 accepts a CPU and calling convention when machine
+selection is needed:
+
+```java
+code.driver(CodeGen.Phase.LocalSched, "x86_64_v2", "SystemV");
+```
+
+This runs through loop discovery, instruction selection, and both schedulers.
+Omitting the CPU keeps the ideal graph for evaluation.
+
## Machine Specific Nodes
We add a notion of "machine specific" Nodes, which implement the `MachNode`
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/CodeGen.java b/chapter19/src/main/java/com/seaofnodes/simple/CodeGen.java
index 0a3a60e8..920e0289 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/CodeGen.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/CodeGen.java
@@ -1,13 +1,11 @@
- package com.seaofnodes.simple;
+package com.seaofnodes.simple;
import com.seaofnodes.graph.GraphObserver;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.MemOpX86;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
import java.util.HashMap;
-import java.util.IdentityHashMap;
public class CodeGen {
public GraphObserver<Node> _obs;
@@ -26,13 +24,14 @@ public class CodeGen {
public static CodeGen CODE;
public enum Phase {
- Parse, // Parse ASCII text into Sea-of-Nodes IR
- Opto, // Run ideal optimizations
- TypeCheck, // Last check for bad programs
- InstructionSelection, // Convert to target hardware nodes
- Schedule, // Global schedule (code motion) nodes
- LocalSched, // Local schedule
- RegAlloc; // Register allocation
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ InstructionSelection, // Select machine instructions
+ Schedule, // Global code motion
+ LocalSched; // Order instructions within blocks
+
}
public enum CallingConv {
Win64,
@@ -40,6 +39,42 @@ public class CodeGen {
}
public Phase _phase;
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) { return driver(target,null,null); }
+ public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); }
+ public CodeGen driver(Phase target, String cpu, String callingConv, int dump) {
+ step(target, Phase.Parse, this::parse, dump);
+ step(target, Phase.Iter, this::iter, dump);
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ step(target, Phase.InstructionSelection, () -> instSelect(cpu,callingConv), dump);
+ step(target, Phase.Schedule, this::GCM, dump);
+ step(target, Phase.LocalSched, this::localSched, dump);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) {
+ if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":");
+ System.err.println(this);
+ }
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
// ---------------------------
// Compilation source code
public final String _src;
@@ -67,7 +102,7 @@ public class CodeGen {
* A counter, for unique node id generation. Starting with value 1, to
* avoid bugs confusing node ID 0 with uninitialized values.
*/
- private int _uid = 1;
+ int _uid = 1;
public int UID() { return _uid; }
public int getUID() { return _uid++; }
@@ -122,12 +157,9 @@ public class CodeGen {
// Parse ASCII text into Sea-of-Nodes IR
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
-
- P.parse();
- if( _obs != null ) _obs.phase("Parse");
- return this;
+ return phase(Phase.Parse, _phase==null, () -> {
+ P.parse();
+ });
}
@@ -142,43 +174,15 @@ public class CodeGen {
public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
- // Run ideal optimizations
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
-
- // Pessimistic peephole optimization on a worklist
- _iter.iterate(this);
-
- // TODO:
- // Optimistic
- // TODO:
- // loop unroll, peel, RCE, etc
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); }
public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
// ---------------------------
// 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();
- _phase = Phase.TypeCheck;
-
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
- });
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.Iter, () -> TypeCheck.run(this)); }
// ---------------------------
@@ -187,70 +191,9 @@ public class CodeGen {
// Convert to target hardware nodes
public CodeGen instSelect( String cpu, String callingConv ) {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.InstructionSelection;
-
- _callingConv = switch(callingConv) {
- case "SystemV" -> CallingConv.SystemV;
- case "Win64" -> CallingConv.Win64;
- default -> throw Utils.TODO(); // Unknown calling convention
- };
-
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = "com.seaofnodes.simple.node.cpus."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- // Convert to machine ops
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = ( StopNode)map.get(_stop );
- _start = (StartNode)map.get(_start);
- _instOuts(_stop,visit());
- _visit.clear();
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachConcrete, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachConcreteNode || n instanceof MemOpX86) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- map.put(n, x=_mach.instSelect(n) );
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- if( x instanceof MachNode mach )
- mach.postSelect(); // Post selection action
-
- // Updates forward edges only.
- n._outputs.clear();
- return x;
- }
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- _instOuts(in,visit);
- }
+ if( cpu==null ) return this;
+ return phase(Phase.InstructionSelection, _phase==Phase.LoopTree,
+ () -> new InstSelect(this).run(cpu,callingConv,"com.seaofnodes.simple.node.cpus"));
}
@@ -259,30 +202,26 @@ public class CodeGen {
Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
- public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.InstructionSelection.ordinal();
- _phase = Phase.Schedule;
+ public CodeGen loopTree() {
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> _start.buildLoopTree(_stop));
+ }
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_stop);
- GlobalCodeMotion.buildCFG(this);
+ public CodeGen GCM() {
+ return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.InstructionSelection), () -> {
+ GlobalCodeMotion.buildCFG(this);
- return this;
+ });
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- ListScheduler.sched(this);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// ---------------------------
public String reg(Node n) {
- if( _phase == Phase.RegAlloc ) throw Utils.TODO();
return "N"+ n._nid;
}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter19/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 95ada83d..9454e968 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter19/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -269,10 +269,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -287,7 +288,9 @@ public abstract class GlobalCodeMotion {
//
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/InstSelect.java b/chapter19/src/main/java/com/seaofnodes/simple/InstSelect.java
new file mode 100644
index 00000000..967a553c
--- /dev/null
+++ b/chapter19/src/main/java/com/seaofnodes/simple/InstSelect.java
@@ -0,0 +1,74 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.node.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.MemOpX86;
+
+// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ InstSelect(CodeGen code) { _code=code; }
+
+ @SuppressWarnings("unchecked")
+ void run(String cpu, String callingConv, String base) {
+ _code._callingConv = switch(callingConv) {
+ case "SystemV" -> CodeGen.CallingConv.SystemV;
+ case "Win64" -> CodeGen.CallingConv.Win64;
+ default -> throw Utils.TODO(); // Unknown calling convention
+ };
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = "com.seaofnodes.simple.node.cpus."+cpu+"."+cpu;
+ try { _code._mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = ( StopNode)map.get(_code._stop );
+ _code._start = (StartNode)map.get(_code._start);
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachConcrete, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachConcreteNode || n instanceof MemOpX86) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(); // Post selection action
+
+ // Updates forward edges only.
+ n._outputs.clear();
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/TypeCheck.java b/chapter19/src/main/java/com/seaofnodes/simple/TypeCheck.java
new file mode 100644
index 00000000..a41e4161
--- /dev/null
+++ b/chapter19/src/main/java/com/seaofnodes/simple/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index 4ec55f4d..b22a5067 100644
--- a/chapter19/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter19/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.driver(CodeGen.Phase.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/chapter19/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter19/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index 8f09cb3d..52e762d4 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -63,7 +63,7 @@ for( int pc = 0; pc < program#; pc++ ) {
}
return output;
""");
- code.parse().opto().typeCheck()/*.instSelect("x86_64_v2", "SystemV")*/.GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000));
}
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 7a9414a2..09e7f7c7 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -19,7 +19,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print());
}
@@ -35,7 +35,7 @@ if (arg == 1) {
}
return c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -63,7 +63,7 @@ if( arg==1 )
else
b=a+1;
return a+b;""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print());
}
@@ -78,7 +78,7 @@ if( arg==1 )
else
a=b+1;
return a+b;""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print());
}
@@ -98,7 +98,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", code.print());
}
@@ -114,7 +114,7 @@ if( arg==0 )
b=2;
return arg+a+b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -129,7 +129,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -144,7 +144,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 117531f0..b19b726b 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -14,7 +14,7 @@ public class Chapter06Test {
if( true ) return 2;
return 1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -28,7 +28,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();
assertEquals("return 1;", code.print());
}
@@ -43,7 +43,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -64,7 +64,7 @@ else
b=5;
return b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,42,5);", code.print());
}
@@ -83,7 +83,7 @@ if( arg==2 )
else
b=5;
return b;""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,2,5);", code.print());
}
@@ -103,7 +103,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2), 123L).parse().opto();
+""", TypeInteger.constant(2), 123L).parse().iter();
assertEquals("return 5;", code.print());
}
@@ -123,7 +123,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L).parse().opto();
+""", TypeInteger.constant(1), 123L).parse().iter();
assertEquals("return 3;", code.print());
}
@@ -143,7 +143,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,3,Phi(Region,4,5));", code.print());
}
@@ -164,7 +164,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -185,7 +185,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -201,7 +201,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -220,7 +220,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -238,7 +238,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
}
@@ -256,7 +256,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", code.print());
}
@@ -277,7 +277,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 6;", code.print());
}
@@ -297,7 +297,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 6d6a3ce7..a5c6ef14 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -33,7 +33,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print());
}
@@ -53,7 +53,7 @@ while(i < arg) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print());
}
@@ -88,7 +88,7 @@ while(a < 10) {
}
return b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -121,7 +121,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -147,7 +147,7 @@ int a = 1;
while(arg) a = 2;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -179,7 +179,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -213,7 +213,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 350df18b..2a284648 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -21,7 +21,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
Assert.assertEquals( "5", Eval2.eval(code, 1));
@@ -44,7 +44,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -63,7 +63,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -79,7 +79,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -98,7 +98,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -115,7 +115,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -131,14 +131,14 @@ while( arg < 10 ) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
}
@@ -166,7 +166,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -183,7 +183,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print());
assertTrue(code.ctrl() instanceof RegionNode);
}
@@ -199,7 +199,7 @@ while(1) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 10f4a78b..cde27ed8 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
assertEquals("return 0;", code.print());
}
@@ -127,14 +127,14 @@ while(v1+arg) {
v1=v2;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testWhile0() {
CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -146,7 +146,7 @@ if(0) while(0) {
while(0) {}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -154,14 +154,14 @@ if(0) while(0) {
@Test
public void testPrecedence() {
CodeGen code = new CodeGen("return 3-1+2;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -179,7 +179,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
}
@@ -193,7 +193,7 @@ arg=0;
int v0=0!=0<-0;
return -0+0+0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -201,14 +201,14 @@ return -0+0+0;
@Test
public void testFuzz2() {
CodeGen code = new CodeGen("return 0+-0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testFuzz3() {
CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -220,7 +220,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -242,7 +242,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -254,7 +254,7 @@ int v0=0;
while(0==1) while(v0)
v0=1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -270,14 +270,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index bcae373b..2681e878 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -13,7 +13,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().typeCheck();
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -38,7 +38,7 @@ public class Chapter10Test {
"if (!!!p) return -1; return p.x;",
"int b = !!p; if (b) return p.x + b - 1; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -53,7 +53,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -85,7 +85,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -105,7 +105,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
}
@@ -121,7 +121,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Vector2D;", code.print());
}
@@ -159,7 +159,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -172,7 +172,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); }
}
@@ -185,7 +185,7 @@ if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
}
@@ -211,7 +211,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -227,7 +227,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -244,7 +244,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) {
assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@@ -281,7 +281,7 @@ while( i.x < i.len ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -300,7 +300,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -318,7 +318,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -335,7 +335,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -362,7 +362,7 @@ if(0) {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -376,7 +376,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return s0;", code.print());
}
@@ -392,7 +392,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -404,7 +404,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)s0;", code.print());
}
@@ -436,7 +436,7 @@ while(0) {}
} }
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -447,7 +447,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -461,7 +461,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -487,7 +487,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -503,7 +503,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -520,7 +520,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -535,7 +535,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -553,7 +553,7 @@ return 0;
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();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 14819869..41860347 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -12,7 +12,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -31,7 +31,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -53,7 +53,7 @@ public class Chapter11Test {
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
- code.opto();
+ code.iter();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 90aaf631..4fbede32 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -13,7 +13,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -33,7 +33,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -55,7 +55,7 @@ LLI? next = head.next;
if( next==null ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("2", Eval2.eval(code, 3));
}
@@ -74,7 +74,7 @@ i0.f = f0;
f0.i = i0;
return f0.i.f.i.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 17;", code.print());
}
@@ -86,7 +86,7 @@ struct N { !N? !next; int i; };
N n = new N;
return n.next;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return null;", code.print());
}
@@ -99,7 +99,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)M;", code.print());
}
@@ -112,7 +112,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; }
return n.next;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); }
}
@@ -125,20 +125,20 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("3",Eval2.eval(code,0));
}
@Test
public void testNullRef4() {
CodeGen code = new CodeGen("-null-5/null-5;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@@ -148,7 +148,7 @@ return n.i;
"""
struct S{};
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -160,7 +160,7 @@ struct S{};
struct S1 { !S2? !s; };
return new S2;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
}
@@ -172,7 +172,7 @@ struct S1 { !S2? !s; };
struct S2 { int x; };
return new S1.s=new S2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S2;", code.print());
}
@@ -189,7 +189,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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -254,7 +254,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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=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/chapter19/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 584377e4..498796ea 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -290,7 +290,7 @@ else {
}
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
}
@@ -322,7 +322,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))));", code.print());
assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
assertEquals("1", Eval2.eval(code, 2)); // 2
@@ -344,7 +344,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -367,7 +367,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -386,7 +386,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -404,7 +404,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -421,7 +421,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -436,7 +436,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -454,7 +454,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -474,7 +474,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -492,7 +492,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -511,7 +511,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
//assertEquals("return new S;", code.print());
}
@@ -528,7 +528,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -546,7 +546,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index a40b4f04..473a06fa 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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().typeCheck();
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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().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));
@@ -42,7 +42,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().typeCheck();
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)/2.0f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index 789a2113..c00549e2 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
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();
+ code.parse().iter();
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();
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();
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();
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();
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().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();
assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -194,7 +194,7 @@ if(0&0>>>0) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index c38dfb00..ec6bce5d 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -13,7 +13,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -27,7 +27,7 @@ struct C { !C? !l; };
c.l = c;
return c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return C;", code.print());
assertEquals("C{l=$cyclic}", Eval2.eval(code, 0));
}
@@ -46,7 +46,7 @@ while (i < old#) {
output[i] = 1;
return output;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [u8];", code.print());
assertEquals("\u0001", Eval2.eval(code, 0));
}
@@ -58,7 +58,7 @@ return output;
int[] is = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -71,7 +71,7 @@ int[] is = new int[2];
int[] is2 = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -85,7 +85,7 @@ a[0] = 1;
a[1] = 2;
return a[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -98,7 +98,7 @@ struct A { int i; };
!A?[] !a = new !A?[2];
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*A?];", code.print());
assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -127,7 +127,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
assertEquals("1.2", Eval2.eval(code, 1));
@@ -152,7 +152,7 @@ if( iss[arg] )
if( iss[arg][2] )
rez = iss[arg][2].y;
return rez;""");
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks
@@ -170,7 +170,7 @@ root._kids = new !Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
root._kids[0] = new Tree;
return root;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -182,7 +182,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -205,7 +205,7 @@ while( i < ary# - 1 ) {
}
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (.[]+(.[]*1000));", code.print());
assertEquals("1006", Eval2.eval(code, 4));
}
@@ -246,7 +246,7 @@ while( j < nprimes ) {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -263,7 +263,7 @@ s2.f = 2.0;
if (arg) s1 = new S;
return s1.i + s1.f;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt).i+.f);", code.print());
assertEquals("0.0", Eval2.eval(code, 0));
}
@@ -275,7 +275,7 @@ return s1.i + s1.f;
"""
return new flt;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@@ -285,7 +285,7 @@ return new flt;
"""
int is = new int[2];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); }
}
@@ -296,7 +296,7 @@ int is = new int[2];
int[] is = new int[3.14];
return is[1];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); }
}
@@ -307,7 +307,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 4));
try { Eval2.eval(code,0); }
@@ -337,7 +337,7 @@ while(v5+(0&0)) {
}
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -352,7 +352,7 @@ u32 v7=0;
int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -376,7 +376,7 @@ else {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index fd028d57..d2ef9357 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -16,7 +16,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -29,7 +29,7 @@ return 3.14;
int x, y;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -40,7 +40,7 @@ return x+y;
int x=2, y=x+1;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -53,7 +53,7 @@ struct X { int x=3; };
X z = new X;
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -65,7 +65,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@@ -86,7 +86,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -117,7 +117,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -142,7 +142,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -166,7 +166,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter();
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index ab92d439..52f20ace 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -16,7 +16,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();
assertEquals("return Phi(Region,9,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -29,7 +29,7 @@ struct X { int ~x; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -41,7 +41,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -54,7 +54,7 @@ Point p = new Point { x=5; y=6; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -66,7 +66,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -74,7 +74,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).parse().iter(); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -87,7 +87,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -99,7 +99,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 19cc25d9..97b5d7d8 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -16,7 +16,7 @@ int i = 0;
i=i=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -28,7 +28,7 @@ return i;
CodeGen code = new CodeGen("""
return arg++;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -38,7 +38,7 @@ return arg++;
CodeGen code = new CodeGen("""
return arg+++arg++;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg<<1)+1);", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -49,7 +49,7 @@ return arg+++arg++;
// -(arg--)-(arg--)
return -arg---arg--;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (-((arg<<1)+-1));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -62,7 +62,7 @@ xs[0]++;
xs[1]++;
return xs[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -74,7 +74,7 @@ u8[] !xs = new u8[1];
xs[0]--;
return xs[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 255;", code.print());
assertEquals("255", Eval2.eval(code, 2));
}
@@ -87,7 +87,7 @@ struct S { u16 x; };
s.x--;
return s.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 65535;", code.print());
assertEquals("65535", Eval2.eval(code, 2));
}
@@ -95,7 +95,7 @@ return s.x;
@Test
public void testInc6() {
CodeGen code = new CodeGen("return --arg;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (arg+-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
}
@@ -103,7 +103,7 @@ return s.x;
@Test
public void testInc7() {
CodeGen code = new CodeGen("u8 x=0; return --x;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 255;", code.print());
assertEquals("255", Eval2.eval(code, 0));
}
@@ -111,7 +111,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();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -119,14 +119,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();
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();
+ code.parse().iter();
assertEquals("2",Eval2.eval(code,0));
}
@@ -134,73 +134,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(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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();
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();
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();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter();
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().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;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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();
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();
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().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
~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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();
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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -264,7 +264,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -277,7 +277,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
@@ -291,7 +291,7 @@ val s = new S{x = 2;};
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -310,7 +310,7 @@ focus(me);
return me;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@@ -320,7 +320,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -331,7 +331,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -343,7 +343,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -356,7 +356,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -370,7 +370,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=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();
assertEquals("return Phi(Region,Phi(Region,.x,.x),0);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -383,7 +383,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();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -395,7 +395,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -408,7 +408,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -421,7 +421,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -436,7 +436,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -449,7 +449,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -462,7 +462,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -470,7 +470,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -486,7 +486,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -500,7 +500,7 @@ struct A{
};
return new A.not_array_of_b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*B?];", code.print());
assertEquals("*B?[]", Eval2.eval(code,0));
}
@@ -519,7 +519,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -550,7 +550,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index f965edd4..2b1a254c 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -17,7 +17,7 @@ public class Chapter18Test {
"int x=0; while(arg) { if(arg==2) while(1) { x++; } x++; arg--; } return x;"
};
for( String src : sources ) {
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().typeCheck();
code._start.buildLoopTree(code._stop);
int[] nevers = {0};
code._stop.walk(n -> {
@@ -50,7 +50,7 @@ public class Chapter18Test {
"val g = { int x -> x ? g(x-1)/305420988+x*305420988 : 1; }; "+
"return f(arg)+g(arg);";
CodeGen code = new CodeGen(src);
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
Node con = code._stop.walk(n -> n instanceof ConstantNode &&
n._type==TypeInteger.constant(305420988) ? n : null);
assertNotNull(con);
@@ -61,7 +61,7 @@ public class Chapter18Test {
for( Node use : uses )
for( int i=1; i<use.nIns(); i++ )
if( use.in(i)==con ) use.setDef(i,cast);
- code.GCM();
+ code.driver(CodeGen.Phase.Schedule);
var owners = new java.util.IdentityHashMap<CFGNode,Boolean>();
code._stop.walk(n -> {
if( !(n instanceof CastNode) || !(n.in(1) instanceof CastNode) ) return null;
@@ -98,7 +98,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().typeCheck();
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -129,7 +129,7 @@ public class Chapter18Test {
"""
return 0;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -147,7 +147,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -164,7 +164,7 @@ return a;
{int -> int}? x2 = null; // null function ptr
return x2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -178,7 +178,7 @@ return x2;
};
return sq;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -192,7 +192,7 @@ var sq = { int x ->
};
return sq(arg)+sq(3);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return (sq( 3)+sq( arg)); return (Parm_x(sq,int,3,arg)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -205,7 +205,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -217,7 +217,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -230,7 +230,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return Phi(Region,{ int -> int #1},{ int -> int #2})( 3); return (Parm_x($fun,int,3)*x); return (Parm_x($fun,int,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -240,7 +240,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().typeCheck();
assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -257,7 +257,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -272,7 +272,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -290,7 +290,7 @@ for(;;) {
i2i = id(x);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return x( Phi(Loop,arg,x( 3))); return Parm_i(x,int,3,Phi_arg); ]", code._stop.toString());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -306,7 +306,7 @@ for(;;) {
arg = x(3);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return Top;", code._stop.toString());
assertEquals(null, Eval2.eval(code, 0));
}
@@ -331,7 +331,7 @@ ps[0] = new Person;
ps[1] = new Person;
fcn(ps,1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -350,7 +350,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -365,7 +365,7 @@ for(;;) {
i2i = null;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
}
@@ -377,7 +377,7 @@ for(;;) {
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -395,7 +395,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -408,7 +408,7 @@ val g = { ->
};
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -422,7 +422,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();
+ code.parse().iter();
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -444,7 +444,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -461,7 +461,7 @@ if (arg) i2i = null;
if (i2i) return i2i(arg);
return f2f(o)(1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,o( arg),o( 1)); return Parm_i(o,int,arg,1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -477,7 +477,7 @@ struct Person {
p.coffee_count += 1;
return p.coffee_count;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -496,7 +496,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).parse().iter();
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -525,7 +525,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).parse().iter();
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -543,7 +543,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).parse().iter();
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter19/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 1abda81f..38e9923d 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -35,7 +35,7 @@ public class Chapter19Test {
CodeGen code = new CodeGen("""
return 0;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -79,105 +79,105 @@ val _hashCodeString = { !String self ->
s.cs[0] = 67; // C
s.cs[1] = 108; // l
hashCode(s);""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
assertEquals("-2449306563677080489", Eval2.eval(code, 2));
}
@Test
public void testBasic0() {
- CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return 0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return 0;", code._stop.toString());
}
@Test
public void testBasic1() {
- CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg+1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (inc,arg);", code._stop.toString());
}
@Test
public void testBasic2() {
- CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return -17;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return -17;", code._stop.toString());
}
@Test
public void testBasic3() {
- CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg==1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (set==,(cmp,arg));", code._stop.toString());
}
@Test
public void testBasic4() {
- CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg<<1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (shli,arg);", code._stop.toString());
}
@Test
public void testBasic5() {
- CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg >> 1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (sari,arg);", code._stop.toString());
}
@Test
public void testBasic6() {
- CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg >>> 1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (shri,arg);", code._stop.toString());
}
@Test
public void testBasic7() {
- CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg / 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (div,arg,2);", code._stop.toString());
}
@Test
public void testBasic8() {
- CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg * 6;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (muli,arg);", code._stop.toString());
}
@Test
public void testBasic9() {
- CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg & 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (andi,arg);", code._stop.toString());
}
@Test
public void testBasic10() {
- CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg | 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (ori,arg);", code._stop.toString());
}
@Test
public void testBasic11() {
- CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg ^ 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (xori,arg);", code._stop.toString());
}
@Test
public void testBasic12() {
- CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg + 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic13() {
- CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg - 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic14() {
- CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg * 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic15() {
- CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg / 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (divf,(i2f8,arg),2.0f);", code._stop.toString());
}
@@ -187,7 +187,7 @@ hashCode(s);""");
"""
int arg1 = arg + 1;
return arg1 / arg;""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (div,(inc,arg),arg);", code._stop.toString());
}
@@ -198,7 +198,7 @@ return arg1 / arg;""");
int arg1 = arg + 1;
return arg1 * arg;
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (mul,(inc,arg),arg);", code._stop.toString());
}
@@ -209,7 +209,7 @@ return arg1 * arg;
int arg1 = arg + 1;
return arg1 << arg;
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (shl,(inc,arg),arg);", code._stop.toString());
}
@@ -220,7 +220,7 @@ return arg1 * arg;
int arg1 = arg + 1;
return arg1 >> arg;
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (sar,(inc,arg),arg);", code._stop.toString());
}
@@ -231,7 +231,7 @@ return arg1 * arg;
int arg1 = arg + 1;
return arg1 >>> arg;
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (shr,(inc,arg),arg);", code._stop.toString());
}
@@ -241,7 +241,7 @@ return arg1 * arg;
int a = arg;
return a + 2.0;
"""
- ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ ).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
}
@@ -256,7 +256,7 @@ else {
a = arg-3;
}
return a;""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print());
}
@@ -271,7 +271,7 @@ if( arg==1 )
else
a=b+1;
return a+b;""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print());
}
@@ -283,7 +283,7 @@ int sum=0;
for( int i=0; i<arg; i++ )
sum += i;
return sum;""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print());
}
@@ -293,42 +293,42 @@ return sum;""");
"""
struct S { int a; !S? !c; };
return new S;""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return S;", code.print());
}
@Test
public void testLea1() {
CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testLea2() {
CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testLea3() {
CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testAlloc2() {
CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@Test
public void testAlloc3() {
CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@@ -345,7 +345,7 @@ for( int i=0; i<ary#-1; i++ )
ary[i+1] += ary[i];
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@@ -360,7 +360,7 @@ for( int i=0; i<A#; i++ )
for( int i=0; i<A#; i++ )
B[i] += A[i];
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return 0;", code.print());
}
@@ -373,7 +373,7 @@ for( int i=0; i<A#; i++ )
A[i]++;
return A[1];
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@@ -393,7 +393,7 @@ val sqrt = { flt x ->
flt farg = arg;
return sqrt(farg);
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return Phi(Loop,(i2f8,arg),(divf,(addf,(divf,i2f8,Phi_guess),Phi_guess),2.0f));", code.print());
};
@@ -424,7 +424,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return [int];", code.print());
//assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -437,7 +437,7 @@ return rez;
val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(2)*10 + fcn(3);
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("Stop[ return (add,Phi(Region,{ int -> int #1},{ int -> int #2})( 3),(muli,Phi_( 2))); return (mul,Parm_x($fun,int,3,2),x); return (shli,Parm_x($fun,int,3,2)); ]", code.print());
}
@@ -448,7 +448,7 @@ return fcn(2)*10 + fcn(3);
val sq = { int x -> x*x; };
return sq(arg) + sq(3);
""");
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("Stop[ return (add,sq(),sq()); return (mul,Parm_x(sq,int,3,arg),x); ]", code.print());
}
@@ -456,11 +456,11 @@ return sq(arg) + sq(3);
for( String cpu : new String[]{"x86_64_v2","riscv"} )
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().typeCheck();
var aliases = memoryPhis(code._stop);
- code.instSelect(cpu,"SystemV");
+ code.driver(CodeGen.Phase.InstructionSelection,cpu,"SystemV");
assertEquals(aliases,memoryPhis(code._stop));
- code.GCM().localSched();
+ code.driver(CodeGen.Phase.Schedule).localSched();
code._stop.walk(n -> {
if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
assertTrue(n.in(0) instanceof CFGNode);
@@ -511,7 +511,7 @@ return sq(arg) + sq(3);
int before = p.x + arg;
a.x=33; b.x=44;
return before;
- """).parse().opto().typeCheck().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ """).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
var read = (MemOpNode)code._stop.walk(n ->
n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
assertNotNull(read);
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter19/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 860827d7..2c1a138d 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter19/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().typeCheck();
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java
index 46251cef..2900de7a 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -114,9 +114,7 @@ public abstract class Eval2 {
public static String eval( CodeGen code, long arg, int timeout ) {
SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
// Force local scheduling phase
- if( code._phase.ordinal() < CodeGen.Phase.TypeCheck .ordinal() ) code.typeCheck();
- if( code._phase.ordinal() < CodeGen.Phase.Schedule .ordinal() ) code.GCM();
- if( code._phase.ordinal() < CodeGen.Phase.LocalSched.ordinal() ) code.localSched();
+ code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
CODE = code;
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/Main.java b/chapter19/src/test/java/com/seaofnodes/simple/Main.java
index 523bd306..9e1c38b5 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/Main.java
@@ -10,7 +10,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.driver(CodeGen.Phase.LocalSched);
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter19/src/test/java/com/seaofnodes/simple/MergeSortTest.java
index a352530f..08b50d2c 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/MergeSortTest.java
@@ -57,7 +57,7 @@ merge_sort(a, b, a#);
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return [int]; return 0; ]", code._stop.toString());
assertEquals("int[ 1,2,3,4,5,6,7,8,9,10,11]", Eval2.eval(code, 11));
}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter19/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 0861424f..79d68843 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/MutabilityTest.java
@@ -5,12 +5,12 @@ import static org.junit.Assert.*;
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().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().typeCheck();
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter19/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..90049145
--- /dev/null
+++ b/chapter19/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.CodeGen;
+import com.seaofnodes.simple.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testResumeMachineCompilation() {
+ CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck);
+ staged.driver(Phase.InstructionSelection,"x86_64_v2","SystemV");
+ staged.driver(Phase.LocalSched,"x86_64_v2","SystemV");
+ assertEquals(Phase.LocalSched,staged._phase);
+ assertNotNull(staged._mach);
+ }
+}
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter19/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index d29166a7..95c062b5 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -10,6 +10,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -30,7 +50,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -45,7 +65,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -64,7 +84,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 851380cf..345aa2a6 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter19/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();
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/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index 5c89a485..f7f76411 100644
--- a/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/chapter19/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -101,7 +101,7 @@ class FuzzerUtils {
System.setErr(NULL_PRINT_STREAM);
System.setOut(NULL_PRINT_STREAM);
var code = new CodeGen(script, TypeInteger.BOT, workListSeed);
- return code.parse().opto().typeCheck().GCM().localSched();
+ return code.driver(CodeGen.Phase.LocalSched);
} finally {
System.setErr(err);
System.setOut(out);
diff --git a/chapter20/README.md b/chapter20/README.md
index 96803e51..34824650 100644
--- a/chapter20/README.md
+++ b/chapter20/README.md
@@ -7,6 +7,11 @@ English | [日本語](README.ja.md)
The grammar is unchanged from [Chapter 18](../chapter18/docs/18-grammar.md).
+`RegAlloc` follows local scheduling and assigns physical registers or stack
+slots to live ranges. Request the complete pipeline through allocation with
+`code.driver(CodeGen.Phase.RegAlloc, "x86_64_v2", "SystemV")`. Instruction
+encoding is introduced in Chapter 21; this chapter stops with allocated nodes.
+
## Some Reading Material
This is a Briggs-Chaitin-Click allocator, and is very similar to the one used
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 78cf4c06..09ac516b 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -8,7 +8,6 @@ import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.type.*;
import java.util.BitSet;
import java.util.HashMap;
-import java.util.IdentityHashMap;
public class CodeGen {
public GraphObserver<Node> _obs;
@@ -27,17 +26,59 @@ public class CodeGen {
public static CodeGen CODE;
public enum Phase {
- Parse, // Parse ASCII text into Sea-of-Nodes IR
- Opto, // Run ideal optimizations
- TypeCheck, // Last check for bad programs
- InstSelect, // Convert to target hardware nodes
- Schedule, // Global schedule (code motion) nodes
- LocalSched, // Local schedule
- RegAlloc, // Register allocation
- Encoding, // Encoding
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ InstSelect, // Select machine instructions
+ Schedule, // Global code motion
+ LocalSched, // Order instructions within blocks
+ RegAlloc; // Assign registers and stack slots
+
}
public Phase _phase;
+ public static final String PORTS = "com.seaofnodes.simple.node.cpus";
+
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) { return driver(target,null,null); }
+ public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); }
+ public CodeGen driver(Phase target, String cpu, String callingConv, int dump) {
+ if( target.ordinal()>=Phase.RegAlloc.ordinal() && cpu==null && _mach==null )
+ throw new IllegalArgumentException("Machine code requires a CPU target");
+ step(target, Phase.Parse, this::parse, dump);
+ step(target, Phase.Iter, this::iter, dump);
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ step(target, Phase.InstSelect, () -> instSelect(PORTS,cpu,callingConv), dump);
+ step(target, Phase.Schedule, this::GCM, dump);
+ step(target, Phase.LocalSched, this::localSched, dump);
+ step(target, Phase.RegAlloc, this::regAlloc, dump);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) {
+ if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":");
+ System.err.println(this);
+ }
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
// ---------------------------
// Compilation source code
public final String _src;
@@ -120,16 +161,10 @@ public class CodeGen {
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
- public int _tParse;
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- long t0 = System.currentTimeMillis();
-
- P.parse();
- _tParse = (int)(System.currentTimeMillis() - t0);
- if( _obs != null ) _obs.phase("Parse");
- return this;
+ return phase(Phase.Parse, _phase==null, () -> {
+ P.parse();
+ });
}
@@ -144,49 +179,15 @@ public class CodeGen {
public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
- // Run ideal optimizations
- public int _tOpto;
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
- long t0 = System.currentTimeMillis();
-
- // Pessimistic peephole optimization on a worklist
- _iter.iterate(this);
- _tOpto = (int)(System.currentTimeMillis() - t0);
-
- // TODO:
- // Optimistic
- // TODO:
- // loop unroll, peel, RCE, etc
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); }
public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
// ---------------------------
// Last check for bad programs
- int _tTypeCheck;
- 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();
- _phase = Phase.TypeCheck;
- long t0 = System.currentTimeMillis();
-
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
- });
- _tTypeCheck = (int)(System.currentTimeMillis() - t0);
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.Iter, () -> TypeCheck.run(this)); }
// ---------------------------
@@ -196,118 +197,44 @@ public class CodeGen {
public String _callingConv;
// Convert to target hardware nodes
- public int _tInsSel;
public CodeGen instSelect( String base, String cpu, String callingConv ) {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.InstSelect;
-
- _callingConv = callingConv;
-
- // Not timing the class load...
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = ( StopNode)map.get(_stop );
- _start = (StartNode)map.get(_start);
- _instOuts(_stop,visit());
- _visit.clear();
- _tInsSel = (int)(System.currentTimeMillis() - t0);
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachNode already, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachNode ) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- map.put(n, x=_mach.instSelect(n) );
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- if( x instanceof MachNode mach )
- mach.postSelect(); // Post selection action
-
- // Updates forward edges only.
- n._outputs.clear();
- return x;
- }
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- _instOuts(in,visit);
- }
+ if( cpu==null ) return this;
+ return phase(Phase.InstSelect, _phase==Phase.LoopTree,
+ () -> new InstSelect(this).run(cpu,callingConv,base));
}
// ---------------------------
// Control Flow Graph in RPO order.
- int _tGCM;
public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
- public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.InstSelect.ordinal();
- _phase = Phase.Schedule;
- long t0 = System.currentTimeMillis();
+ public CodeGen loopTree() {
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> _start.buildLoopTree(_stop));
+ }
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_stop);
- GlobalCodeMotion.buildCFG(this);
- _tGCM = (int)(System.currentTimeMillis() - t0);
+ public CodeGen GCM() {
+ return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.InstSelect), () -> {
+ GlobalCodeMotion.buildCFG(this);
- return this;
+ });
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- int _tLocal;
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- long t0 = System.currentTimeMillis();
- ListScheduler.sched(this);
- _tLocal = (int)(System.currentTimeMillis() - t0);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// ---------------------------
// Register Allocation
- int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
- assert _phase == Phase.LocalSched;
- _phase = Phase.RegAlloc;
- long t0 = System.currentTimeMillis();
- _regAlloc = new RegAlloc(this);
- _regAlloc.regAlloc();
- _tRegAlloc = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> {
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ });
}
public String reg(Node n) {
if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) {
@@ -318,22 +245,6 @@ public class CodeGen {
}
// ---------------------------
- // Encoding
- int _tEncode;
- public CodeGen encode() {
- assert _phase == Phase.RegAlloc;
- _phase = Phase.Encoding;
- long t0 = System.currentTimeMillis();
-
-
- //_tEncode = (int)(System.currentTimeMillis() - t0);
- //throw Utils.TODO();
- return this;
- }
- // Encoded binary, no relocation info
- public byte[] binary() { throw Utils.TODO(); }
-
- // ---------------------------
SB asm(SB sb) { return ASMPrinter.print(sb,this); }
public String asm() { return asm(new SB()).toString(); }
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 52906afb..225e5fb8 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -273,10 +273,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -291,7 +292,9 @@ public abstract class GlobalCodeMotion {
//
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
new file mode 100644
index 00000000..074349e6
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -0,0 +1,69 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ InstSelect(CodeGen code) { _code=code; }
+
+ @SuppressWarnings("unchecked")
+ void run(String cpu, String callingConv, String base) {
+ _code._callingConv = callingConv;
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = base+"."+cpu+"."+cpu;
+ try { _code._mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = ( StopNode)map.get(_code._stop );
+ _code._start = (StartNode)map.get(_code._start);
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachNode already, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachNode ) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(); // Post selection action
+
+ // Updates forward edges only.
+ n._outputs.clear();
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index 5803cad8..8ccb5b34 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -54,6 +54,8 @@ public class LRG {
// Adjacent Live Range neighbors. Only valid during coloring
Ary<LRG> _adj;
+ public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index af28cbb3..ddf7b697 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -346,7 +346,7 @@ public class RegAlloc {
// If the minLoopDepth is less than the maxLoopDepth: for-all defs and
// uses, if at minLoopDepth or lower, split after def and before use.
for( Node n : _ns ) {
- if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) continue; // Ordinary splits already reach stack storage.
if( n.isDead() ) continue; // Some Clonable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
@@ -397,8 +397,8 @@ public class RegAlloc {
}
private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
+ // Ordinary splits already reach stack storage; register-only transfers do not.
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld;
// Collect min/max loop depth
int min = (int)ld;
int max = (int)(ld>>32);
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
index 5227c11e..80dcde61 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
@@ -81,7 +81,7 @@ public class RegMask {
boolean isEmpty() { return _bits0==0 && _bits1==0; }
- boolean test( int reg ) {
+ public boolean test( int reg ) {
return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1) != 0;
}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
new file mode 100644
index 00000000..0997fd2f
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index 07166a7c..74639df5 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -10,6 +10,8 @@ public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
public final byte _round;
public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
+ // A register-only transfer may need another split to reach stack storage.
+ public boolean isNonStackSplit() { return false; }
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
new file mode 100644
index 00000000..6783d126
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
@@ -0,0 +1,13 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// NZCV transfers require a GPR. Further splits can spill that GPR to the stack.
+public class SplitFlagsARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,1L);
+ SplitFlagsARM(String kind,byte round) { super(kind,round); }
+ @Override public boolean isNonStackSplit() { return true; }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
new file mode 100644
index 00000000..46eff4ca
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
@@ -0,0 +1,11 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Keep a saved NZCV value in GPRs and stack slots through subsequent spills.
+public class SplitGPRARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L);
+ SplitGPRARM(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 72254310..543e6636 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -55,7 +55,7 @@ public class arm extends Machine {
// Load/store mask; both GPR and FPR
static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -1L);
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/);
static final RegMask FLAGS_MASK = new RegMask(FLAGS);
// x30 (LR): Procedure link register, used to return from subroutines.
@@ -169,7 +169,16 @@ public class arm extends Machine {
}
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask();
+ if( (def!=null && def.size1() && def.test(FLAGS)) ||
+ (use!=null && use.size1() && use.test(FLAGS)) )
+ return new SplitFlagsARM(kind,round);
+ // Spilling the saved flags requires a GPR between NZCV and the stack.
+ if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM )
+ return new SplitGPRARM(kind,round);
+ return new SplitARM(kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
new file mode 100644
index 00000000..3785bf3f
--- /dev/null
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
@@ -0,0 +1,12 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Flag snapshots can use GPRs or stack slots, but never XMM registers.
+public class SplitFlagsX86 extends SplitX86 {
+ private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L<<x86_64_v2.FLAGS) | x86_64_v2.SPILLS,-1L);
+ SplitFlagsX86(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index c66aed9f..06e3e578 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -34,7 +34,7 @@ public class x86_64_v2 extends Machine {
static final RegMask RPC_MASK = new RegMask(RPC);
static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | (1L<<FLAGS) | SPILLS, -1L );
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | SPILLS, -1L );
// Load/store mask; both GPR and FPR
static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -200,7 +200,11 @@ public class x86_64_v2 extends Machine {
}
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitX86(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ return lrg._machDef!=null && lrg._machDef.outregmap().test(FLAGS)
+ ? new SplitFlagsX86(kind,round)
+ : new SplitX86(kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/chapter20/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index a4f768c5..1ee88285 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/chapter20/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -49,5 +49,5 @@ public final class ASMPrinter extends AssemblyAdapter<Node> {
if( n instanceof MemMergeNode ) return true;
return n.getClass()==ConstantNode.class;
}
- @Override public boolean postAlloc() { return _code._phase.ordinal()>CodeGen.Phase.RegAlloc.ordinal(); }
+ @Override public boolean postAlloc() { return _code._phase.ordinal()>=CodeGen.Phase.RegAlloc.ordinal(); }
}
diff --git a/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index 9dce0b75..a6418589 100644
--- a/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter20/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.driver(CodeGen.Phase.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/chapter20/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 70cc6121..44493d87 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -20,7 +20,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", code.print());
}
@@ -115,7 +115,7 @@ if( arg==0 )
b=2;
return arg+a+b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -145,7 +145,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 24d12921..11caf1f5 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter20/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();
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();
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
assertEquals("return Phi(Region,2,5);", code.print());
}
@@ -104,7 +104,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2), 123L).parse().opto();
+""", TypeInteger.constant(2), 123L).parse().iter();
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L).parse().opto();
+""", TypeInteger.constant(1), 123L).parse().iter();
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,3,Phi(Region,4,5));", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", code.print());
}
@@ -278,7 +278,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 88554323..b0526eb1 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -34,7 +34,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -122,7 +122,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -180,7 +180,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 7e90d484..af378682 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -22,7 +22,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", 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();
assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -64,7 +64,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 401f346f..0a3049e6 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
assertEquals("return 0;", code.print());
}
@@ -127,14 +127,14 @@ while(v1+arg) {
v1=v2;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testWhile0() {
CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -146,7 +146,7 @@ if(0) while(0) {
while(0) {}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -154,14 +154,14 @@ if(0) while(0) {
@Test
public void testPrecedence() {
CodeGen code = new CodeGen("return 3-1+2;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -179,7 +179,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
}
@@ -193,7 +193,7 @@ arg=0;
int v0=0!=0<-0;
return -0+0+0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -201,14 +201,14 @@ return -0+0+0;
@Test
public void testFuzz2() {
CodeGen code = new CodeGen("return 0+-0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testFuzz3() {
CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -220,7 +220,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -242,7 +242,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -254,7 +254,7 @@ int v0=0;
while(0==1) while(v0)
v0=1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -270,14 +270,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 91505f41..8af1c71d 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -14,7 +14,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().typeCheck();
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -39,7 +39,7 @@ public class Chapter10Test {
"if (!!!p) return -1; return p.x;",
"int b = !!p; if (b) return p.x + b - 1; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -54,7 +54,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -86,7 +86,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -106,7 +106,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
}
@@ -122,7 +122,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Vector2D;", code.print());
}
@@ -160,7 +160,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -173,7 +173,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); }
}
@@ -186,7 +186,7 @@ if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
}
@@ -212,7 +212,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -228,7 +228,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -245,7 +245,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) {
assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@@ -282,7 +282,7 @@ while( i.x < i.len ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -301,7 +301,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -319,7 +319,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -336,7 +336,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -363,7 +363,7 @@ if(0) {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -377,7 +377,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return s0;", code.print());
}
@@ -393,7 +393,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -405,7 +405,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)s0;", code.print());
}
@@ -437,7 +437,7 @@ while(0) {}
} }
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -448,7 +448,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -462,7 +462,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -488,7 +488,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -504,7 +504,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -521,7 +521,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -536,7 +536,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -554,7 +554,7 @@ return 0;
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();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 439333ca..a74f8540 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -13,7 +13,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -32,7 +32,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -54,7 +54,7 @@ public class Chapter11Test {
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
- code.opto();
+ code.iter();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 4abc9117..7f340776 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -14,7 +14,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -34,7 +34,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -56,7 +56,7 @@ LLI? next = head.next;
if( next==null ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", 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();
+ code.parse().iter();
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();
assertEquals("return null;", code.print());
}
@@ -100,7 +100,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)M;", code.print());
}
@@ -113,7 +113,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; }
return n.next;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); }
}
@@ -126,20 +126,20 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("3",Eval2.eval(code,0));
}
@Test
public void testNullRef4() {
CodeGen code = new CodeGen("-null-5/null-5;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@@ -149,7 +149,7 @@ return n.i;
"""
struct S{};
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -161,7 +161,7 @@ struct S{};
struct S1 { !S2? !s; };
return new S2;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
}
@@ -173,7 +173,7 @@ struct S1 { !S2? !s; };
struct S2 { int x; };
return new S1.s=new S2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S2;", code.print());
}
@@ -190,7 +190,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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -255,7 +255,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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=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/chapter20/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 975946da..3dbc9b41 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -290,7 +290,7 @@ else {
}
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
}
@@ -322,7 +322,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))));", code.print());
assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
assertEquals("1", Eval2.eval(code, 2)); // 2
@@ -344,7 +344,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -367,7 +367,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -386,7 +386,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -404,7 +404,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -421,7 +421,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -436,7 +436,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -454,7 +454,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -474,7 +474,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -492,7 +492,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -511,7 +511,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
//assertEquals("return new S;", code.print());
}
@@ -528,7 +528,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -546,7 +546,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 6377bc14..cf7d3481 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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().typeCheck();
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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().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));
@@ -42,7 +42,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().typeCheck();
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index f0fe7dcd..ebc94d21 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
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();
+ code.parse().iter();
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();
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();
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();
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();
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().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();
assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -194,7 +194,7 @@ if(0&0>>>0) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 6a22ff97..7da8fe0f 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -14,7 +14,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -28,7 +28,7 @@ struct C { !C? !l; };
c.l = c;
return c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return C;", code.print());
assertEquals("C{l=$cyclic}", Eval2.eval(code, 0));
}
@@ -47,7 +47,7 @@ while (i < old#) {
output[i] = 1;
return output;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [u8];", code.print());
assertEquals("\u0001", Eval2.eval(code, 0));
}
@@ -59,7 +59,7 @@ return output;
int[] is = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -72,7 +72,7 @@ int[] is = new int[2];
int[] is2 = new int[2];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -86,7 +86,7 @@ a[0] = 1;
a[1] = 2;
return a[0];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -99,7 +99,7 @@ struct A { int i; };
!A?[] !a = new !A?[2];
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*A?];", code.print());
assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -128,7 +128,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
assertEquals("1.2", Eval2.eval(code, 1));
@@ -153,7 +153,7 @@ if( iss[arg] )
if( iss[arg][2] )
rez = iss[arg][2].y;
return rez;""");
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks
@@ -171,7 +171,7 @@ root._kids = new !Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
root._kids[0] = new Tree;
return root;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -183,7 +183,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -206,7 +206,7 @@ while( i < ary# - 1 ) {
}
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (.[]+(.[]*1000));", code.print());
assertEquals("1006", Eval2.eval(code, 4));
}
@@ -247,7 +247,7 @@ while( j < nprimes ) {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -264,7 +264,7 @@ s2.f = 2.0;
if (arg) s1 = new S;
return s1.i + s1.f;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt).i+.f);", code.print());
assertEquals("0.0", Eval2.eval(code, 0));
}
@@ -276,7 +276,7 @@ return s1.i + s1.f;
"""
return new flt;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@@ -286,7 +286,7 @@ return new flt;
"""
int is = new int[2];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); }
}
@@ -297,7 +297,7 @@ int is = new int[2];
int[] is = new int[3.14];
return is[1];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); }
}
@@ -307,7 +307,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 4));
try { Eval2.eval(code,-1); fail(); }
@@ -340,7 +340,7 @@ while(v5+(0&0)) {
}
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -355,7 +355,7 @@ u32 v7=0;
int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -379,7 +379,7 @@ else {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 362388d6..78e468cd 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -17,7 +17,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -30,7 +30,7 @@ return 3.14;
int x, y;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -41,7 +41,7 @@ return x+y;
int x=2, y=x+1;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -54,7 +54,7 @@ struct X { int x=3; };
X z = new X;
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@@ -87,7 +87,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -118,7 +118,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -143,7 +143,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -167,7 +167,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter();
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 16ffe967..4a69c33d 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -17,7 +17,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();
assertEquals("return Phi(Region,9,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -30,7 +30,7 @@ struct X { int ~x; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -42,7 +42,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -55,7 +55,7 @@ Point p = new Point { x=5; y=6; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -67,7 +67,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -75,7 +75,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).parse().iter(); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -88,7 +88,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -100,7 +100,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index da619ed3..f6eb0190 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -15,7 +15,7 @@ public class Chapter17bTest {
"""
return 1;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
+ code.parse().iter();
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(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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();
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();
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();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter();
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().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;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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();
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();
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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -217,7 +217,7 @@ foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -238,7 +238,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();
assertEquals("return 45;", code.print());
assertEquals("45", Eval2.eval(code, 0));
}
@@ -248,7 +248,7 @@ return x4*10+x5;
CodeGen code = new CodeGen("""
int i,i++;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -263,7 +263,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -276,7 +276,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
@@ -290,7 +290,7 @@ val s = new S{x = 2;};
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -309,7 +309,7 @@ focus(me);
return me;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@@ -319,7 +319,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -330,7 +330,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -355,7 +355,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -369,7 +369,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=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();
assertEquals("return Phi(Region,Phi(Region,.x,.x),0);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -382,7 +382,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();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -394,7 +394,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -407,7 +407,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -420,7 +420,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -435,7 +435,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -448,7 +448,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -461,7 +461,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -469,7 +469,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -485,7 +485,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -499,7 +499,7 @@ struct A{
};
return new A.not_array_of_b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*B?];", code.print());
assertEquals("*B?[]", Eval2.eval(code,0));
}
@@ -518,7 +518,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -549,7 +549,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index dafd2f3a..26ae7bf2 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -18,7 +18,7 @@ public class Chapter18Test {
"int x=0; while(arg) { if(arg==2) while(1) { x++; } x++; arg--; } return x;"
};
for( String src : sources ) {
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).parse().iter().typeCheck();
code._start.buildLoopTree(code._stop);
int[] nevers = {0};
code._stop.walk(n -> {
@@ -51,7 +51,7 @@ public class Chapter18Test {
"val g = { int x -> x ? g(x-1)/305420988+x*305420988 : 1; }; "+
"return f(arg)+g(arg);";
CodeGen code = new CodeGen(src);
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
Node con = code._stop.walk(n -> n instanceof ConstantNode &&
n._type==TypeInteger.constant(305420988) ? n : null);
assertNotNull(con);
@@ -62,7 +62,7 @@ public class Chapter18Test {
for( Node use : uses )
for( int i=1; i<use.nIns(); i++ )
if( use.in(i)==con ) use.setDef(i,cast);
- code.GCM();
+ code.driver(CodeGen.Phase.Schedule);
var owners = new java.util.IdentityHashMap<CFGNode,Boolean>();
code._stop.walk(n -> {
if( !(n instanceof CastNode) || !(n.in(1) instanceof CastNode) ) return null;
@@ -99,7 +99,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().typeCheck();
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -130,7 +130,7 @@ public class Chapter18Test {
"""
return 0;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -148,7 +148,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -165,7 +165,7 @@ return a;
{int -> int}? x2 = null; // null function ptr
return x2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -179,7 +179,7 @@ return x2;
};
return sq;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return { sq}; return (Parm_x(sq,int)*x); ]", code._stop.toString());
assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -193,7 +193,7 @@ var sq = { int x ->
};
return sq(arg)+sq(3);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString());
assertEquals("13", Eval2.eval(code, 2));
}
@@ -206,7 +206,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -218,7 +218,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -231,7 +231,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return #2; return (Parm_x($fun1,int,3)*x); return (Parm_x($fun2,int,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -241,7 +241,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().typeCheck();
assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -258,7 +258,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -273,7 +273,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -291,7 +291,7 @@ for(;;) {
i2i = id(x);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -307,7 +307,7 @@ for(;;) {
arg = x(3);
}
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return Top;", code._stop.toString());
assertEquals(null, Eval2.eval(code, 0));
}
@@ -332,7 +332,7 @@ ps[0] = new Person;
ps[1] = new Person;
fcn(ps,1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -351,7 +351,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -366,7 +366,7 @@ for(;;) {
i2i = null;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
}
@@ -378,7 +378,7 @@ for(;;) {
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -396,7 +396,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -409,7 +409,7 @@ val g = { ->
};
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -423,7 +423,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();
+ code.parse().iter();
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -445,7 +445,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -462,7 +462,7 @@ if (arg) i2i = null;
if (i2i) return i2i(arg);
return f2f(o)(1);
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,#2,1); return Parm_i(o,int); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -478,7 +478,7 @@ struct Person {
p.coffee_count += 1;
return p.coffee_count;
""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 2));
}
@@ -497,7 +497,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).parse().iter();
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -526,7 +526,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).parse().iter();
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -544,7 +544,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).parse().iter();
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 9bd75207..f93e79a9 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter20/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().typeCheck();
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -81,105 +81,105 @@ val _hashCodeString = { !String self ->
s.cs[0] = 67; // C
s.cs[1] = 108; // l
hashCode(s)+hashCode(s);""");
- code.parse().opto().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return (#2+#2); return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString());
assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@Test
public void testBasic0() {
- CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return 0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return 0;", code._stop.toString());
}
@Test
public void testBasic1() {
- CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg+1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (inc,arg);", code._stop.toString());
}
@Test
public void testBasic2() {
- CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return -17;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return -17;", code._stop.toString());
}
@Test
public void testBasic3() {
- CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg==1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (set==,(cmp,arg));", code._stop.toString());
}
@Test
public void testBasic4() {
- CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg<<1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (shli,arg);", code._stop.toString());
}
@Test
public void testBasic5() {
- CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg >> 1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (sari,arg);", code._stop.toString());
}
@Test
public void testBasic6() {
- CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg >>> 1;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (shri,arg);", code._stop.toString());
}
@Test
public void testBasic7() {
- CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg / 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (div,arg,2);", code._stop.toString());
}
@Test
public void testBasic8() {
- CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg * 6;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (muli,arg);", code._stop.toString());
}
@Test
public void testBasic9() {
- CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg & 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (andi,arg);", code._stop.toString());
}
@Test
public void testBasic10() {
- CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg | 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (ori,arg);", code._stop.toString());
}
@Test
public void testBasic11() {
- CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg ^ 2;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (xori,arg);", code._stop.toString());
}
@Test
public void testBasic12() {
- CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg + 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic13() {
- CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg - 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (subf,(i2f8,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic14() {
- CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg * 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (mulf,(i2f8,arg),2.0f);", code._stop.toString());
}
@Test
public void testBasic15() {
- CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ CodeGen code = new CodeGen("return arg / 2.0;").driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (mulf,(i2f8,arg),0.5f);", code._stop.toString());
}
@@ -189,7 +189,7 @@ hashCode(s)+hashCode(s);""");
"""
int arg1 = arg + 1;
return arg1 / arg;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (div,(inc,arg),arg);", code._stop.toString());
}
@@ -200,7 +200,7 @@ return arg1 / arg;""");
int arg1 = arg + 1;
return arg1 * arg;
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (mul,(inc,arg),arg);", code._stop.toString());
}
@@ -210,7 +210,7 @@ return arg1 * arg;
int a = arg;
return a + 2.0;
"""
- ).parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ ).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (addf,(i2f8,arg),2.0f);", code._stop.toString());
}
@@ -225,7 +225,7 @@ else {
a = arg-3;
}
return a;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print());
}
@@ -240,7 +240,7 @@ if( arg==1 )
else
a=b+1;
return a+b;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print());
}
@@ -252,7 +252,7 @@ int sum=0;
for( int i=0; i<arg; i++ )
sum += i;
return sum;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print());
}
@@ -262,42 +262,42 @@ return sum;""");
"""
struct S { int a; !S? !c; };
return new S;""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return S;", code.print());
}
@Test
public void testLea1() {
CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testLea2() {
CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testLea3() {
CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return (lea,arg,(div,arg,3));", code.print());
}
@Test
public void testAlloc2() {
CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@Test
public void testAlloc3() {
CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@@ -314,7 +314,7 @@ for( int i=0; i<ary#-1; i++ )
ary[i+1] += ary[i];
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@@ -329,7 +329,7 @@ for( int i=0; i<A#; i++ )
for( int i=0; i<A#; i++ )
B[i] += A[i];
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return 0;", code.print());
}
@@ -342,7 +342,7 @@ for( int i=0; i<A#; i++ )
A[i]++;
return A[1];
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return .[];", code.print());
}
@@ -362,7 +362,7 @@ val sqrt = { flt x ->
flt farg = arg;
return sqrt(farg);
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return Phi(Loop,(i2f8,arg),(mulf,(addf,(divf,i2f8,Phi_guess),Phi_guess),0.5f));", code.print());
};
@@ -393,7 +393,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("return [int];", code.print());
//assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
@@ -406,7 +406,7 @@ return rez;
val fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(2)*10 + fcn(3);
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print());
}
@@ -417,7 +417,7 @@ return fcn(2)*10 + fcn(3);
val sq = { int x -> x*x; };
return sq(arg) + sq(3);
""");
- code.parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2", "SystemV").GCM().localSched();
+ code.driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print());
}
@@ -425,11 +425,11 @@ return sq(arg) + sq(3);
for( String cpu : new String[]{"x86_64_v2","riscv","arm"} )
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().typeCheck();
var aliases = memoryPhis(code._stop);
- code.instSelect(PORTS,cpu,"SystemV");
+ code.driver(CodeGen.Phase.InstSelect,cpu,"SystemV");
assertEquals(aliases,memoryPhis(code._stop));
- code.GCM().localSched();
+ code.driver(CodeGen.Phase.Schedule).localSched();
code._stop.walk(n -> {
if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
assertTrue(n.in(0) instanceof CFGNode);
@@ -480,7 +480,7 @@ return sq(arg) + sq(3);
int before = p.x + arg;
a.x=33; b.x=44;
return before;
- """).parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2","SystemV").GCM().localSched();
+ """).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
var read = (MemOpNode)code._stop.walk(n ->
n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
assertNotNull(read);
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter20/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 104be01e..e6a01826 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -10,6 +10,10 @@ import static com.seaofnodes.simple.Main.PORTS;
import static org.junit.Assert.*;
public class Chapter20Test {
+ @Test public void testFlagSpills() throws Exception {
+ com.seaofnodes.simple.codegen.FlagSpillTestSupport.run();
+ }
+
@Test public void testAllocatorTopPhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.topPhi(); }
@Test public void testAllocatorFixedNeighbor() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.uncoloredFixedNeighbor(); }
@@ -17,7 +21,7 @@ public class Chapter20Test {
@Test public void testNarrowStoreMasks() {
for( String type : new String[]{"i8","u8","i16","u16"} ) {
CodeGen code = new CodeGen("struct S { "+type+" x; }; !S !s=new S; s.x=arg; return 0;")
- .parse().opto().typeCheck().instSelect(PORTS,"x86_64_v2","SystemV").GCM().localSched().regAlloc();
+ .driver(CodeGen.Phase.RegAlloc,"x86_64_v2","SystemV");
int stores=0;
for( var bb : code._cfg )
for( var node : bb.outs() )
@@ -40,7 +44,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,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().iter().typeCheck();
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -64,7 +68,7 @@ return f(s);
@Test
public void testJig() {
CodeGen code = new CodeGen("return 0;");
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -76,7 +80,7 @@ return f(s);
static void testCPU(String src, String cpu, String os, int spills, String stop) {
CodeGen code = new CodeGen(src);
- code.parse().opto().typeCheck().instSelect(PORTS,cpu,os).GCM().localSched().regAlloc().encode();
+ code.driver(CodeGen.Phase.RegAlloc,cpu,os);
com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
SpillStats.record(code,"Chapter20",cpu,os);
assertEquals("Expect spills: "+cpu,spills,code._regAlloc._spillScaled,Math.max(1,spills>>3));
@@ -254,8 +258,7 @@ return arg;
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY,
"int[] !a = new int[3]; a[arg]=arg; return a[0]+1;"} ) {
- var code = new CodeGen(src).parse().opto().typeCheck()
- .instSelect(PORTS,cpu,"SystemV").GCM().localSched().regAlloc();
+ var code = new CodeGen(src).driver(CodeGen.Phase.RegAlloc,cpu,"SystemV");
com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
code._stop.walk(n -> {
// Whole memory, precise Phis, and memory projections need no register.
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter20/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 48f739cf..8131c95f 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter20/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().typeCheck();
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java
index 3312ec6b..83827319 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -116,9 +116,7 @@ public abstract class Eval2 {
public static String eval( CodeGen code, long arg, int timeout ) {
SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
// Force local scheduling phase
- if( code._phase.ordinal() < CodeGen.Phase.TypeCheck .ordinal() ) code.typeCheck();
- if( code._phase.ordinal() < CodeGen.Phase.Schedule .ordinal() ) code.GCM();
- if( code._phase.ordinal() < CodeGen.Phase.LocalSched.ordinal() ) code.localSched();
+ code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
CODE = code;
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/Main.java b/chapter20/src/test/java/com/seaofnodes/simple/Main.java
index f3a6784c..5f58542b 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/Main.java
@@ -12,7 +12,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.driver(CodeGen.Phase.LocalSched);
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter20/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 7ae80314..baf4e252 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter20/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().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().typeCheck();
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter20/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..1214ffeb
--- /dev/null
+++ b/chapter20/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,41 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testResumeMachineCompilation() {
+ CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck);
+ staged.driver(Phase.InstSelect,"x86_64_v2","SystemV");
+ staged.driver(Phase.RegAlloc,"x86_64_v2","SystemV");
+ assertEquals(Phase.RegAlloc,staged._phase);
+ assertNotNull(staged._mach);
+ }
+}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter20/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 3b19857a..a388d8c3 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -11,6 +11,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -31,7 +51,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -46,7 +66,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -65,7 +85,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter20/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
new file mode 100644
index 00000000..f5bd9510
--- /dev/null
+++ b/chapter20/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
@@ -0,0 +1,57 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import static org.junit.Assert.*;
+
+// Force overlapping flags, then force every GPR unavailable across the gap.
+public class FlagSpillTestSupport {
+ private static class Op extends MachConcreteNode {
+ final RegMask _def, _use, _kill;
+ Op(RegMask def,RegMask use,RegMask kill,Node... ins) {
+ super(ins); _def=def; _use=use; _kill=kill; _type=TypeInteger.BOT;
+ }
+ @Override public String op() { return "flagsProbe"; }
+ @Override public RegMask outregmap() { return _def; }
+ @Override public RegMask regmap(int i) { return _use; }
+ @Override public RegMask killmap() { return _kill; }
+ @Override public int encoding(java.io.ByteArrayOutputStream enc) { throw new AssertionError(); }
+ }
+
+ public static void run() throws Exception {
+ for( boolean isArm : new boolean[]{false,true} )
+ for( boolean stack : new boolean[]{false,true} ) {
+ CodeGen code = new CodeGen("");
+ code._callingConv="SystemV";
+ code._mach=isArm ? new arm() : new x86_64_v2();
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ code._cfg.add(block);
+ int flagsReg=isArm ? 64 : 32, firstStack=flagsReg+1;
+ RegMask flags = new RegMask(flagsReg);
+ Op def = new Op(flags,null,null,block);
+ RegMask killMask = stack ? (isArm ? new RegMask(0x7fffffffL,1L) : new RegMask(0x1ffffffefL)) : flags;
+ Op kill = new Op(null,null,killMask,block);
+ new Op(null,flags,null,block,def);
+ RegAlloc alloc = new RegAlloc(code);
+ alloc.regAlloc();
+
+ int splits=0, slots=0;
+ for( Node n : block._outputs ) if( n instanceof SplitNode split ) {
+ int dst=alloc.regnum(n), src=alloc.regnum(n.in(1));
+ assertTrue(split.outregmap().test(dst));
+ assertTrue(split.regmap(1).test(src));
+ for( int fp=isArm ? 32 : 16; fp<flagsReg; fp++ ) {
+ assertFalse("FP output allowed",split.outregmap().test(fp));
+ assertFalse("FP input allowed",split.regmap(1).test(fp));
+ }
+ if( dst>=firstStack || src>=firstStack ) slots++;
+ splits++;
+ }
+ assertTrue(splits>=2);
+ assertEquals(stack,slots!=0);
+
+ }
+ }
+}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter20/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 2966cb00..75f8f1fd 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -23,6 +23,11 @@ public class RegAllocTestSupport {
@Override public Node copy() { return new Op(_def,_use,_kill,_clone,(Node)null); }
@Override public int encoding(ByteArrayOutputStream bytes) { throw new AssertionError(); }
}
+ private static SplitNode testSplit(CodeGen code, MachNode def) {
+ LRG lrg = new LRG((short)0);
+ lrg._machDef = def;
+ return code._mach.split("test",(byte)0,lrg);
+ }
private static CodeGen graph() {
CodeGen code = new CodeGen("");
code._mach = new x86_64_v2();
@@ -169,11 +174,11 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode hi = code._mach.split("test",(byte)0,null);
+ SplitNode hi = testSplit(code,def);
hi.setDef(0,code._start); hi.setDef(1,def);
alloc.newLRG(hi)._reg=1;
new Op(null,null,killed ? A : null,false,code._start);
- SplitNode lo = code._mach.split("test",(byte)0,null);
+ SplitNode lo = testSplit(code,hi);
lo.setDef(0,code._start); lo.setDef(1,hi);
alloc.newLRG(lo)._reg=0;
Op use = new Op(null,A,null,false,code._start,lo);
@@ -187,7 +192,7 @@ public class RegAllocTestSupport {
uncolored.newLRG(def0)._mask=A;
Op fixed = new Op(A,null,null,false,pre._start);
uncolored.newLRG(fixed)._mask=A;
- SplitNode copy = pre._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(pre,def0);
copy.setDef(0,pre._start); copy.setDef(1,def0);
assertFalse("A fixed definition clobbers even before coloring",uncolored.sameBlockNoClobber(copy));
}
diff --git a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 586924b2..7e8bdbf1 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter20/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();
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/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
index a2137314..b2be5b5f 100644
--- a/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ b/chapter20/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
@@ -101,7 +101,7 @@ class FuzzerUtils {
System.setErr(NULL_PRINT_STREAM);
System.setOut(NULL_PRINT_STREAM);
var code = new CodeGen(script, TypeInteger.BOT, workListSeed);
- return code.parse().opto().typeCheck().GCM().localSched();
+ return code.driver(CodeGen.Phase.LocalSched);
} finally {
System.setErr(err);
System.setOut(out);
diff --git a/chapter21/README.md b/chapter21/README.md
index 80304c1a..a3ae6cb8 100644
--- a/chapter21/README.md
+++ b/chapter21/README.md
@@ -17,6 +17,18 @@ in the linear revision history and
Here is the [complete language grammar](docs/21-grammar.md) for this chapter.
+Two phases extend the pipeline: `Encoding` turns allocated nodes into bytes,
+and `Export` resolves relocations in memory or writes an ELF object. For example:
+
+```java
+code.driver(CodeGen.Phase.Encoding, "x86_64_v2", "SystemV");
+code.exportELF("program.o");
+```
+
+The CLI uses the same driver. `--dump-after-iter`, the other phase dump options,
+and `--dump-time` expose the stages without duplicating their ordering in the
+command-line frontend.
+
## From machine nodes to bytes
Instruction selection chooses the operation and register constraints. Register
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 2c44cb05..95e30d12 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -9,7 +9,6 @@ import com.seaofnodes.simple.type.*;
import java.io.IOException;
import java.util.BitSet;
import java.util.HashMap;
-import java.util.IdentityHashMap;
@SuppressWarnings("unchecked")
public class CodeGen {
@@ -20,19 +19,61 @@ public class CodeGen {
public static CodeGen CODE;
public enum Phase {
- Parse, // Parse ASCII text into Sea-of-Nodes IR
- Opto, // Run ideal optimizations
- TypeCheck, // Last check for bad programs
- LoopTree, // Build a loop tree; break infinite loops
- InstSelect, // Convert to target hardware nodes
- Schedule, // Global schedule (code motion) nodes
- LocalSched, // Local schedule
- RegAlloc, // Register allocation
- Encoding, // Encoding
- Export, // Export
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ InstSelect, // Select machine instructions
+ Schedule, // Global code motion
+ LocalSched, // Order instructions within blocks
+ RegAlloc, // Assign registers and stack slots
+ Encoding, // Encode instructions
+ Export; // Link in memory or write ELF
+
}
public Phase _phase;
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) { return driver(target,null,null); }
+ public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); }
+ public CodeGen driver(Phase target, String cpu, String callingConv, int dump) {
+ if( target.ordinal()>=Phase.RegAlloc.ordinal() && cpu==null && _mach==null )
+ throw new IllegalArgumentException("Machine code requires a CPU target");
+ if( target==Phase.Export ) throw new IllegalArgumentException("Use exportELF with an output file or null for memory linking");
+ step(target, Phase.Parse, this::parse, dump);
+ step(target, Phase.Iter, this::iter, dump);
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ step(target, Phase.InstSelect, () -> instSelect(cpu,callingConv), dump);
+ step(target, Phase.Schedule, this::GCM, dump);
+ step(target, Phase.LocalSched, this::localSched, dump);
+ step(target, Phase.RegAlloc, this::regAlloc, dump);
+ step(target, Phase.Encoding, this::encode, dump);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) {
+ if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":");
+ System.err.println(this);
+ }
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
// ---------------------------
// Compilation source code
public final String _src;
@@ -53,21 +94,6 @@ public class CodeGen {
}
- // All passes up to Phase, except ELF
- public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
- public CodeGen driver( Phase phase, String cpu, String callingConv ) {
- if( _phase==null ) parse();
- if( _phase.ordinal() < phase.ordinal() ) opto();
- if( _phase.ordinal() < phase.ordinal() ) typeCheck();
- if( _phase.ordinal() < phase.ordinal() ) loopTree();
- if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv);
- if( _phase.ordinal() < phase.ordinal() ) GCM();
- if( _phase.ordinal() < phase.ordinal() ) localSched();
- if( _phase.ordinal() < phase.ordinal() ) regAlloc();
- if( _phase.ordinal() < phase.ordinal() ) encode();
- return this;
- }
-
// Run all the phases through final ELF emission
public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException {
return driver(Phase.Encoding,cpu,callingConv).exportELF(obj);
@@ -143,16 +169,10 @@ public class CodeGen {
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
- public int _tParse;
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- long t0 = System.currentTimeMillis();
-
- P.parse();
- _tParse = (int)(System.currentTimeMillis() - t0);
- if( _obs != null ) _obs.phase("Parse");
- return this;
+ return phase(Phase.Parse, _phase==null, () -> {
+ P.parse();
+ });
}
@@ -167,61 +187,23 @@ public class CodeGen {
public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
- // Run ideal optimizations
- public int _tOpto;
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
- long t0 = System.currentTimeMillis();
-
- // Pessimistic peephole optimization on a worklist
- _iter.iterate(this);
- _tOpto = (int)(System.currentTimeMillis() - t0);
-
- // TODO:
- // Optimistic
- // TODO:
- // loop unroll, peel, RCE, etc
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); }
public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
// ---------------------------
// Last check for bad programs
- public int _tTypeCheck;
- 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();
- _phase = Phase.TypeCheck;
- long t0 = System.currentTimeMillis();
-
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
- });
- _tTypeCheck = (int)(System.currentTimeMillis() - t0);
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.Iter, () -> TypeCheck.run(this)); }
// ---------------------------
// Build the loop tree; break never-exit loops
- public int _tLoopTree;
public CodeGen loopTree() {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.LoopTree;
- long t0 = System.currentTimeMillis();
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_stop);
- _tLoopTree = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> {
+ // Build the loop tree, fix never-exit loops
+ _start.buildLoopTree(_stop);
+ });
}
// ---------------------------
@@ -243,138 +225,41 @@ public class CodeGen {
public RegMask _rpcMask;
// Convert to target hardware nodes
- public int _tInsSel;
public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
public CodeGen instSelect( String cpu, String callingConv, String base ) {
- assert _phase.ordinal() == Phase.LoopTree.ordinal();
- _phase = Phase.InstSelect;
-
- _callingConv = callingConv;
-
- // Not timing the class load...
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- // Build global copies of common register masks.
- long callerSave = _mach.callerSave();
- long neverSave = _mach. neverSave();
- int maxReg = Math.min(64,_mach.regs().length);
- assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
- _callerSave = new RegMask(callerSave);
-
- // Build a Return RegMask array. All returns have the following inputs:
- // 0 - ctrl
- // 1 - memory
- // 2 - varies with returning GPR,FPR
- // 3 - RPC
- // 4+- Caller save registers
- _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
- for( int reg=0, i=4; reg<maxReg; reg++ )
- if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
- _retMasks[i++] = new RegMask(reg);
- _rpcMask = new RegMask(_mach.rpc());
- _retMasks[3] = _rpcMask;
-
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = ( StopNode)map.get(_stop );
- _start = (StartNode)map.get(_start);
- _instOuts(_stop,visit());
- _visit.clear();
- _tInsSel = (int)(System.currentTimeMillis() - t0);
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachNode already, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachNode ) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- map.put(n, x=_mach.instSelect(n) );
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- if( x instanceof MachNode mach )
- mach.postSelect(this); // Post selection action
-
- // Updates forward edges only.
- n._outputs.clear();
- return x;
- }
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- _instOuts(in,visit);
- }
+ if( cpu==null ) return this;
+ return phase(Phase.InstSelect, _phase==Phase.LoopTree,
+ () -> new InstSelect(this).run(cpu,callingConv,base));
}
// ---------------------------
// Control Flow Graph in Reverse Post Order.
- public int _tGCM;
public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.InstSelect.ordinal();
- _phase = Phase.Schedule;
- long t0 = System.currentTimeMillis();
-
- GlobalCodeMotion.buildCFG(this);
- _tGCM = (int)(System.currentTimeMillis() - t0);
+ return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.InstSelect), () -> {
+ GlobalCodeMotion.buildCFG(this);
- return this;
+ });
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- public int _tLocal;
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- long t0 = System.currentTimeMillis();
- ListScheduler.sched(this);
- _tLocal = (int)(System.currentTimeMillis() - t0);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// ---------------------------
// Register Allocation
- public int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
- assert _phase == Phase.LocalSched;
- _phase = Phase.RegAlloc;
- long t0 = System.currentTimeMillis();
- _regAlloc = new RegAlloc(this);
- _regAlloc.regAlloc();
- _tRegAlloc = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> {
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ });
}
// Human readable register name
@@ -390,27 +275,27 @@ public class CodeGen {
// ---------------------------
// Encoding
- public int _tEncode;
public Encoding _encoding; // Encoding object
public void preEncode() { } // overridden by alternative ports
public CodeGen encode() {
- assert _phase == Phase.RegAlloc;
- _phase = Phase.Encoding;
- long t0 = System.currentTimeMillis();
- _encoding = new Encoding(this);
- preEncode();
- _encoding.encode();
- _tEncode = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.Encoding, _phase==Phase.RegAlloc, () -> {
+ _encoding = new Encoding(this);
+ preEncode();
+ _encoding.encode();
+ });
}
// ---------------------------
// Exporting to external formats
public CodeGen exportELF(String fname) throws IOException {
- assert _phase == Phase.Encoding;
- _phase = Phase.Export;
- if( fname == null ) new LinkMem(this).link(); // In memory patching
- else new ElfFile(this).export(fname); // External ELF file
+ assert _phase==Phase.Encoding;
+ _phase=Phase.Export;
+ long t0=System.currentTimeMillis();
+ try {
+ if( fname == null ) new LinkMem(this).link(); // In memory patching
+ else new ElfFile(this).export(fname); // External ELF file
+ } finally { _times[Phase.Export.ordinal()]=System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(Phase.Export.name());
return this;
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 447a6df1..d4db558a 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -274,10 +274,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -294,7 +295,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// A conditional writer can precede the load without dominating its late block.
// Walk back to the first overlap with the load's placement range.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
new file mode 100644
index 00000000..4d67be4b
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ InstSelect(CodeGen code) { _code=code; }
+
+ @SuppressWarnings("unchecked")
+ void run(String cpu, String callingConv, String base) {
+ _code._callingConv = callingConv;
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = base+"."+cpu+"."+cpu;
+ try { _code._mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(_code); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Build global copies of common register masks.
+ long callerSave = _code._mach.callerSave();
+ long neverSave = _code._mach. neverSave();
+ int maxReg = Math.min(64,_code._mach.regs().length);
+ assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+ _code._callerSave = new RegMask(callerSave);
+
+ // Build a Return RegMask array. All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ _code._retMasks = new RegMask[(maxReg-_code._callerSave.size()-Long.bitCount( neverSave ))+4];
+ for( int reg=0, i=4; reg<maxReg; reg++ )
+ if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+ _code._retMasks[i++] = new RegMask(reg);
+ _code._rpcMask = new RegMask(_code._mach.rpc());
+ _code._retMasks[3] = _code._rpcMask;
+
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = ( StopNode)map.get(_code._stop );
+ _code._start = (StartNode)map.get(_code._start);
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachNode already, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachNode ) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(_code); // Post selection action
+
+ // Updates forward edges only.
+ n._outputs.clear();
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index ec254a2e..6ece27a2 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -59,6 +59,8 @@ public class LRG {
public int nadj() { return _adj==null ? 0 : _adj._len; }
+ public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index dd3d2acb..85b3d283 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -361,7 +361,7 @@ public class RegAlloc {
// If the minLoopDepth is less than the maxLoopDepth: for-all defs and
// uses, if at minLoopDepth or lower, split after def and before use.
for( Node n : _ns ) {
- if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) continue; // Ordinary splits already reach stack storage.
if( n.isDead() ) continue; // Some Clonable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
@@ -414,8 +414,8 @@ public class RegAlloc {
}
private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
+ // Ordinary splits already reach stack storage; register-only transfers do not.
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld;
// Collect min/max loop depth
int min = (int)ld;
int max = (int)(ld>>32);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
new file mode 100644
index 00000000..0997fd2f
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index ff18b292..437a700a 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -10,6 +10,8 @@ public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
public final byte _round;
public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
+ // A register-only transfer may need another split to reach stack storage.
+ public boolean isNonStackSplit() { return false; }
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
new file mode 100644
index 00000000..3e045a6f
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+
+// NZCV transfers require a GPR. Further splits can spill that GPR to the stack.
+public class SplitFlagsARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,1L);
+ SplitFlagsARM(String kind,byte round) { super(kind,round); }
+ @Override public boolean isNonStackSplit() { return true; }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( src==arm.FLAGS ) enc.add4(Arm64.flags(dst,false));
+ else if( dst==arm.FLAGS ) enc.add4(Arm64.flags(src,true));
+ else super.encoding(enc);
+ }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
new file mode 100644
index 00000000..46eff4ca
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
@@ -0,0 +1,11 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Keep a saved NZCV value in GPRs and stack slots through subsequent spills.
+public class SplitGPRARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L);
+ SplitGPRARM(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 66a59fde..9910795d 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -267,7 +267,16 @@ public class arm extends Machine {
@Override public int rpc() { return X30; }
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask();
+ if( (def!=null && def.size1() && def.test(FLAGS)) ||
+ (use!=null && use.size1() && use.test(FLAGS)) )
+ return new SplitFlagsARM(kind,round);
+ // Spilling the saved flags requires a GPR between NZCV and the stack.
+ if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM )
+ return new SplitGPRARM(kind,round);
+ return new SplitARM(kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
new file mode 100644
index 00000000..4650b535
--- /dev/null
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+
+// Flag snapshots can use GPRs or stack slots, but never XMM registers.
+public class SplitFlagsX86 extends SplitX86 {
+ private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L<<x86_64_v2.FLAGS) | x86_64_v2.SPILLS,-1L);
+ SplitFlagsX86(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( dst==x86_64_v2.FLAGS || src==x86_64_v2.FLAGS ) {
+ boolean toFlags = dst==x86_64_v2.FLAGS;
+ int reg = toFlags ? src : dst;
+ if( reg>=x86_64_v2.MAX_REG )
+ X86.flagsStack(enc,enc._fun.computeStackOffset(enc._code,reg),toFlags);
+ else
+ X86.flags(enc,reg,toFlags);
+ } else super.encoding(enc);
+ }
+}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
index 5cf668d1..3c4dde17 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -17,12 +17,10 @@ public class SplitX86 extends SplitNode {
@Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
- // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
- // flags->flags.
+ // Ordinary GPR, XMM, and stack moves. Flags use SplitFlagsX86.
@Override public void encoding( Encoding enc ) {
short dst=enc.reg(this), src=enc.reg(in(1));
- if( dst==x86_64_v2.FLAGS ) { X86.flags(enc,src,true); return; }
- if( src==x86_64_v2.FLAGS ) { X86.flags(enc,dst,false); return; }
+ assert dst!=x86_64_v2.FLAGS && src!=x86_64_v2.FLAGS;
boolean dstX=dst>=x86_64_v2.XMM_OFFSET, srcX=src>=x86_64_v2.XMM_OFFSET;
// Stack spills
if( dst >= x86_64_v2.MAX_REG ) {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 1b7cf775..1dc47188 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -45,7 +45,7 @@ public class x86_64_v2 extends Machine {
static RegMask RPC_MASK = new RegMask(RPC);
static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | SPILLS, -1L );
// Load/store mask; both GPR and FPR
static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -235,7 +235,9 @@ public class x86_64_v2 extends Machine {
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(String kind, byte round, LRG lrg) {
- return new SplitX86(kind, round);
+ return lrg._machDef!=null && lrg._machDef.outregmap().test(FLAGS)
+ ? new SplitFlagsX86(kind,round)
+ : new SplitX86(kind,round);
}
// Return a MachNode unconditional branch
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index d8d0e216..99b3d190 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter21/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.driver(CodeGen.Phase.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/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index aa6fada0..97d33b70 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -20,7 +20,7 @@ public class BrainFuckTest {
String brain_fuck = "Hello World!\n";
TestC.run("brain_fuck", brain_fuck, 40);
- EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 146, false);
+ EvalRisc5 R5 = TestRisc5.build("brain_fuck", 0, 42, false);
int trap = R5.step(100000);
assertEquals(0,trap);
int ptr = (int)R5.regs[riscv.A0];
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 6ff4ac95..c809f72f 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -20,7 +20,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -145,7 +145,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index b6bc2bf3..2c97b9c2 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter21/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();
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();
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
assertEquals("return Phi(Region,2,5);", code.print());
}
@@ -104,7 +104,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(2), 123L).parse().opto();
+""", TypeInteger.constant(2), 123L).parse().iter();
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L).parse().opto();
+""", TypeInteger.constant(1), 123L).parse().iter();
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,3,5,4);", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,6,1,5);", code.print());
}
@@ -278,7 +278,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2), 123L);
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 88554323..b0526eb1 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -34,7 +34,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -122,7 +122,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -180,7 +180,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 09669cc7..487a2c7b 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -22,7 +22,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
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();
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();
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 401f346f..0a3049e6 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
assertEquals("return 0;", code.print());
}
@@ -127,14 +127,14 @@ while(v1+arg) {
v1=v2;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testWhile0() {
CodeGen code = new CodeGen("while(0) continue; if(0) arg=0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -146,7 +146,7 @@ if(0) while(0) {
while(0) {}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -154,14 +154,14 @@ if(0) while(0) {
@Test
public void testPrecedence() {
CodeGen code = new CodeGen("return 3-1+2;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
}
@@ -179,7 +179,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
}
@@ -193,7 +193,7 @@ arg=0;
int v0=0!=0<-0;
return -0+0+0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -201,14 +201,14 @@ return -0+0+0;
@Test
public void testFuzz2() {
CodeGen code = new CodeGen("return 0+-0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@Test
public void testFuzz3() {
CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -220,7 +220,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -242,7 +242,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -254,7 +254,7 @@ int v0=0;
while(0==1) while(v0)
v0=1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -270,14 +270,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 91505f41..8af1c71d 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -14,7 +14,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().typeCheck();
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -39,7 +39,7 @@ public class Chapter10Test {
"if (!!!p) return -1; return p.x;",
"int b = !!p; if (b) return p.x + b - 1; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -54,7 +54,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).parse().iter().typeCheck();
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -86,7 +86,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -106,7 +106,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
}
@@ -122,7 +122,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Vector2D;", code.print());
}
@@ -160,7 +160,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -173,7 +173,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); }
}
@@ -186,7 +186,7 @@ if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
}
@@ -212,7 +212,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -228,7 +228,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -245,7 +245,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) {
assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@@ -282,7 +282,7 @@ while( i.x < i.len ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -301,7 +301,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -319,7 +319,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -336,7 +336,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -363,7 +363,7 @@ if(0) {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -377,7 +377,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return s0;", code.print());
}
@@ -393,7 +393,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
}
@Test
@@ -405,7 +405,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)s0;", code.print());
}
@@ -437,7 +437,7 @@ while(0) {}
} }
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -448,7 +448,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -462,7 +462,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -488,7 +488,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -504,7 +504,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -521,7 +521,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -536,7 +536,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).parse().iter();
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -554,7 +554,7 @@ return 0;
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();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 439333ca..a74f8540 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -13,7 +13,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -32,7 +32,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).parse().iter();
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -54,7 +54,7 @@ public class Chapter11Test {
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
- code.opto();
+ code.iter();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 0b4ad8ec..324b2a12 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -14,7 +14,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -34,7 +34,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", 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();
+ code.parse().iter();
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();
assertEquals("return null;", code.print());
}
@@ -100,7 +100,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)M;", code.print());
}
@@ -113,7 +113,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; }
return n.next;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); }
}
@@ -126,20 +126,20 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("3",Eval2.eval(code,0));
}
@Test
public void testNullRef4() {
CodeGen code = new CodeGen("-null-5/null-5;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot 'Add' null",e.getMessage()); }
}
@@ -149,7 +149,7 @@ return n.i;
"""
struct S{};
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -161,7 +161,7 @@ struct S{};
struct S1 { !S2? !s; };
return new S2;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
}
@@ -173,7 +173,7 @@ struct S1 { !S2? !s; };
struct S2 { int x; };
return new S1.s=new S2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S2;", code.print());
}
@@ -190,7 +190,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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -255,7 +255,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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=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/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 66e12e42..4e56b9de 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -290,8 +290,11 @@ else {
}
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 9;", code.print());
+ code.driver(CodeGen.Phase.Schedule);
+ // LoopTree connects the nonreturning paths using synthetic Never exits.
+ assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
}
@@ -322,7 +325,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter();
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
@@ -344,7 +347,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -367,7 +370,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -386,7 +389,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -404,7 +407,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return arg;", code.print());
}
@@ -421,7 +424,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -436,7 +439,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -454,7 +457,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -474,7 +477,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -492,7 +495,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return S;", code.print());
}
@@ -511,7 +514,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter();
//assertEquals("return new S;", code.print());
}
@@ -528,7 +531,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Top;", code.print());
}
@@ -546,7 +549,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 6377bc14..cf7d3481 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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().typeCheck();
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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().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));
@@ -42,7 +42,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().typeCheck();
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index f0fe7dcd..ebc94d21 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
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();
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();
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();
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();
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();
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();
+ code.parse().iter();
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();
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();
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();
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();
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().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();
assertEquals("return (Phi(Loop,((1<<arg)&1),0)&Phi(Loop,Shl,4294967295));", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -194,7 +194,7 @@ if(0&0>>>0) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index cb92277f..fbbb6c9d 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -17,7 +17,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
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();
assertEquals("return C;", code.print());
assertEquals("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();
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();
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();
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();
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();
assertEquals("return [*A?];", code.print());
assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -131,7 +131,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter();
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().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();
+ code.parse().iter();
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -186,7 +186,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter();
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();
+ code.parse().iter();
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();
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();
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(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@@ -257,7 +257,7 @@ return new flt;
"""
int is = new int[2];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Type *[int] is not of declared type int",e.getMessage()); }
}
@@ -268,7 +268,7 @@ int is = new int[2];
int[] is = new int[3.14];
return is[1];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); }
}
@@ -278,7 +278,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 4));
try { Eval2.eval(code,-1); fail(); }
@@ -311,7 +311,7 @@ while(v5+(0&0)) {
}
return v1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -326,7 +326,7 @@ u32 v7=0;
int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
@@ -350,7 +350,7 @@ else {
}
}
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 362388d6..78e468cd 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -17,7 +17,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -30,7 +30,7 @@ return 3.14;
int x, y;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -41,7 +41,7 @@ return x+y;
int x=2, y=x+1;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -54,7 +54,7 @@ struct X { int x=3; };
X z = new X;
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@@ -87,7 +87,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -118,7 +118,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -143,7 +143,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -167,7 +167,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter();
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 16ffe967..4a69c33d 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -17,7 +17,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();
assertEquals("return Phi(Region,9,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -30,7 +30,7 @@ struct X { int ~x; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -42,7 +42,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -55,7 +55,7 @@ Point p = new Point { x=5; y=6; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -67,7 +67,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -75,7 +75,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).parse().iter(); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -88,7 +88,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -100,7 +100,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e8f6e0bd..8ea72c21 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -15,7 +15,7 @@ public class Chapter17bTest {
"""
return 1;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
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();
+ code.parse().iter();
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(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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();
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();
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();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter();
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().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;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().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();
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();
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().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -217,7 +217,7 @@ foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -238,7 +238,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();
assertEquals("return 45;", code.print());
assertEquals("45", Eval2.eval(code, 0));
}
@@ -248,7 +248,7 @@ return x4*10+x5;
CodeGen code = new CodeGen("""
int i,i++;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -263,7 +263,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -276,7 +276,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
@@ -290,7 +290,7 @@ val s = new S{x = 2;};
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -309,7 +309,7 @@ focus(me);
return me;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@@ -319,7 +319,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -330,7 +330,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -355,7 +355,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -369,7 +369,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=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();
assertEquals("return Phi(Region,.x,0,.x);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -382,7 +382,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();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -394,7 +394,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -407,7 +407,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -420,7 +420,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -435,7 +435,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -448,7 +448,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
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));
@@ -461,7 +461,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -469,7 +469,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -485,7 +485,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -499,7 +499,7 @@ struct A{
};
return new A.not_array_of_b;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return [*B?];", code.print());
assertEquals("*B?[]", Eval2.eval(code,0));
}
@@ -518,7 +518,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -549,7 +549,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 7c0af028..6282f26c 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter21/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().typeCheck();
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -140,7 +140,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
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();
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -170,7 +170,7 @@ return x2;
x*x;
};
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString());
//assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -197,7 +197,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -209,7 +209,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -222,7 +222,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter();
assertEquals("Stop[ return #2; return (Parm_x($fun1,int,3)*x); return (Parm_x($fun2,int,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -232,7 +232,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().typeCheck();
assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -249,7 +249,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -264,7 +264,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -357,7 +357,7 @@ for(;;) {
i2i = null;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
}
@@ -369,7 +369,7 @@ for(;;) {
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -387,7 +387,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -400,7 +400,7 @@ val g = { ->
};
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.parse().iter().typeCheck(); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -414,7 +414,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();
+ code.parse().iter();
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -436,7 +436,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -488,7 +488,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).parse().iter();
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -517,7 +517,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).parse().iter();
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -535,7 +535,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).parse().iter();
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index c53a8e11..2027dbd6 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -39,7 +39,7 @@ public class Chapter19Test {
CodeGen code = new CodeGen("""
return 0;
""");
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -48,7 +48,7 @@ return 0;
@Test
public void testString() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
- CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
//assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@@ -357,11 +357,11 @@ return sq(arg) + sq(3);
for( String cpu : new String[]{"x86_64_v2","riscv","arm"} )
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).parse().iter().typeCheck();
var aliases = memoryPhis(code._stop);
- code.loopTree().instSelect(cpu,"SystemV");
+ code.loopTree().driver(CodeGen.Phase.InstSelect,cpu,"SystemV");
assertEquals(aliases,memoryPhis(code._stop));
- code.GCM().localSched();
+ code.driver(CodeGen.Phase.Schedule).localSched();
code._stop.walk(n -> {
if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
assertTrue(n.in(0) instanceof CFGNode);
@@ -413,7 +413,7 @@ return sq(arg) + sq(3);
int before = p.x + arg;
a.x=33; b.x=44;
return before;
- """).parse().opto().typeCheck().loopTree().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ """).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
var read = (MemOpNode)code._stop.walk(n ->
n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
assertNotNull(read);
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index e63fb32e..93d8caae 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -9,6 +9,10 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter20Test {
+ @Test public void testFlagSpills() throws Exception {
+ com.seaofnodes.simple.codegen.FlagSpillTestSupport.run();
+ }
+
@Test public void testAllocatorTopPhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.topPhi(); }
@Test public void testNeverReturnAllocation() {
@@ -69,7 +73,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,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().iter().typeCheck();
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -93,7 +97,7 @@ return f(s);
@Test
public void testJig() {
CodeGen code = new CodeGen("return 0;");
- code.parse().opto().typeCheck();
+ code.parse().iter().typeCheck();
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -339,7 +343,7 @@ for( int pc = 0; pc < program#; pc++ ) {
return output;
""";
testTarget(src,"x86_64_v2", "SystemV",40,null);
- testTarget(src,"riscv" , "SystemV",146,null);
+ testTarget(src,"riscv" , "SystemV",42,null);
testTarget(src,"arm" , "SystemV",34,null);
//assertEquals("Hello World!\n", Eval2.eval(code, 0, 10000));
}
@@ -407,8 +411,7 @@ return a;
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY,
"int[] !a = new int[3]; a[arg]=arg; return a[0]+1;"} ) {
- var code = new CodeGen(src).parse().opto().typeCheck().loopTree()
- .instSelect(cpu,"SystemV").GCM().localSched().regAlloc();
+ var code = new CodeGen(src).driver(CodeGen.Phase.RegAlloc,cpu,"SystemV");
com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
code._stop.walk(n -> {
// Whole memory, precise Phis, and memory projections need no register.
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index a51ae3bc..b19d1bb5 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -27,7 +27,7 @@ public class Chapter21Test {
else if(i==4) b.x=40;
}
return sum;
- """).parse().opto();
+ """).parse().iter();
// 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));
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Compile.java b/chapter21/src/test/java/com/seaofnodes/simple/Compile.java
index 088055f9..f3ca1905 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Compile.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Compile.java
@@ -16,7 +16,7 @@ public class Compile {
private static Process run(String simple, String c) throws IOException, InterruptedException {
boolean USE_WSL = System.getProperty("os.name").startsWith("Windows");
CodeGen code = new CodeGen(simple);
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode();
+ code.driver(CodeGen.Phase.Encoding,"x86_64_v2","SystemV");
if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main";
code.exportELF("test.o");
var params = new ArrayList<String>();
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter21/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 48f739cf..8131c95f 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter21/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().typeCheck();
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java
index a855350e..f39750c2 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -116,10 +116,7 @@ public abstract class Eval2 {
public static String eval( CodeGen code, long arg, int timeout ) {
SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
// Force local scheduling phase
- if( code._phase.ordinal() < CodeGen.Phase.TypeCheck .ordinal() ) code.typeCheck();
- if( code._phase.ordinal() < CodeGen.Phase.LoopTree .ordinal() ) code.loopTree();
- if( code._phase.ordinal() < CodeGen.Phase.Schedule .ordinal() ) code.GCM();
- if( code._phase.ordinal() < CodeGen.Phase.LocalSched.ordinal() ) code.localSched();
+ code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
CODE = code;
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Main.java b/chapter21/src/test/java/com/seaofnodes/simple/Main.java
index 589c043b..ca1d0b5b 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter21/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.driver(CodeGen.Phase.LocalSched);
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter21/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index 1ce2cd34..7cdf217b 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -65,7 +65,7 @@ public class MemoryEncodingTest {
"""
};
for( int p=0; p<sources.length; p++ ) {
- CodeGen code=new CodeGen(sources[p]).parse().opto();
+ CodeGen code=new CodeGen(sources[p]).parse().iter();
if( p==0 ) assertNull(code._stop.walk(n ->
n instanceof LoadNode ld && ld.ptr() instanceof ProjNode ptr &&
ptr.in(0) instanceof NewNode nn &&
@@ -158,7 +158,7 @@ public class MemoryEncodingTest {
}
return null;
}));
- code.instSelect(target,"SystemV").GCM().localSched().regAlloc();
+ code.driver(CodeGen.Phase.RegAlloc,target,"SystemV");
RegAllocTestSupport.checkRegisters(code);
code.encode();
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter21/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 7ae80314..baf4e252 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter21/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().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().typeCheck();
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter21/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..d890a537
--- /dev/null
+++ b/chapter21/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testResumeMachineCompilation() {
+ CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck);
+ staged.driver(Phase.InstSelect,"x86_64_v2","SystemV");
+ staged.driver(Phase.Encoding,"x86_64_v2","SystemV");
+ byte[] bits = staged._encoding._bits.toByteArray();
+ CodeGen direct = new CodeGen(SRC).driver(Phase.Encoding,"x86_64_v2","SystemV");
+ assertArrayEquals(bits,direct._encoding._bits.toByteArray());
+ }
+}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 3b19857a..a388d8c3 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -11,6 +11,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -31,7 +51,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -46,7 +66,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -65,7 +85,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Simple.java b/chapter21/src/test/java/com/seaofnodes/simple/Simple.java
index 705b086a..c496ea90 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Simple.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
import com.seaofnodes.simple.print.*;
import java.nio.file.Files;
@@ -13,20 +14,20 @@ import java.util.stream.Collectors;
public class Simple {
public static final String PORTS = "com.seaofnodes.simple.node.cpus";
- static final int DUMP_AFTER_PARSE = 1<<0;
- static final int DUMP_AFTER_OPTO = 1<<1;
- static final int DUMP_AFTER_TYPE_CHECK = 1<<2;
- static final int DUMP_AFTER_LOOP_TREE = 1<<3;
- static final int DUMP_AFTER_INSTR_SELECT = 1<<4;
- static final int DUMP_AFTER_GCM = 1<<5;
- static final int DUMP_AFTER_LOCAL_SCHED = 1<<6;
- static final int DUMP_AFTER_REG_ALLOC = 1<<7;
- static final int DUMP_AFTER_ENCODE = 1<<8;
+ static final int DUMP_AFTER_PARSE = 1<<Phase.Parse.ordinal();
+ static final int DUMP_AFTER_ITER = 1<<Phase.Iter.ordinal();
+ static final int DUMP_AFTER_TYPE_CHECK = 1<<Phase.TypeCheck.ordinal();
+ static final int DUMP_AFTER_LOOP_TREE = 1<<Phase.LoopTree.ordinal();
+ static final int DUMP_AFTER_INSTR_SELECT = 1<<Phase.InstSelect.ordinal();
+ static final int DUMP_AFTER_GCM = 1<<Phase.Schedule.ordinal();
+ static final int DUMP_AFTER_LOCAL_SCHED = 1<<Phase.LocalSched.ordinal();
+ static final int DUMP_AFTER_REG_ALLOC = 1<<Phase.RegAlloc.ordinal();
+ static final int DUMP_AFTER_ENCODE = 1<<Phase.Encoding.ordinal();
static final int DUMP_FINAL = 1<<16;
static final int DUMP_PASS_NAME = 1<<30;
- static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME;
+ static final int DUMP_AFTER_ALL = ((1<<Phase.values().length)-1) | DUMP_PASS_NAME;
static final int DUMP_DISASSEMBLE = 1<<31;
@@ -39,15 +40,16 @@ public class Simple {
simple <input-file> [options] [--eval|--run ...]
Options:
--dump - dump final intermediate representation
- --dump-after-parse - dump intermediate representation after parse
- --dump-after-opto - dump intermediate representation after opto pass
- --dump-after-type-check - dump intermediate representation after type check pass
- --dump-after-instr-select - dump intermediate representation after instrution selection pass
- --dump-after-gcm - dump intermediate representation after GCM pass
- --dump-after-local-sched - dump intermediate representation after local scheduling pass
- --dump-after-reg-alloc - dump intermediate representation after register allocation pass
- --dump-after-encode - dump intermediate representation after encoding pass
- --dump-after-all - dump intermediate representation after all passes
+ --dump-after-parse - dump intermediate representation after parsing
+ --dump-after-iter - dump intermediate representation after Iter
+ --dump-after-type-check - dump intermediate representation after type checking
+ --dump-after-loop-tree - dump intermediate representation after loop-tree construction
+ --dump-after-instr-select - dump intermediate representation after instruction selection
+ --dump-after-gcm - dump intermediate representation after GCM
+ --dump-after-local-sched - dump intermediate representation after local scheduling
+ --dump-after-reg-alloc - dump intermediate representation after register allocation
+ --dump-after-encode - dump intermediate representation after encoding
+ --dump-after-all - dump intermediate representation after all passes
-S - dump generated assembler code
--eval ... - evaluate the compiled code in emulator
--run ... - run the compiled code natively
@@ -80,50 +82,19 @@ Options:
}
static void dump(CodeGen code, int dump, int pass) {
- if ((dump & pass) != 0) {
- if ((dump & DUMP_PASS_NAME) != 0) {
- System.err.println(switch (pass) {
- case DUMP_AFTER_PARSE -> "After Parse:";
- case DUMP_AFTER_OPTO -> "After OPTO:";
- case DUMP_AFTER_TYPE_CHECK -> "After Type Check:";
- case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:";
- case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:";
- case DUMP_AFTER_GCM -> "After GCM:";
- case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:";
- case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:";
- case DUMP_AFTER_ENCODE -> "After Instruction Encoding:";
- case DUMP_FINAL -> "Final:";
- default -> throw Utils.TODO();
- });
- }
-
- System.err.println(IRPrinter.prettyPrint(code._stop, 9999));
-
- }
+ if( (dump & pass)==0 ) return;
+ if( (dump & DUMP_PASS_NAME)!=0 ) System.err.println("Final:");
+ System.err.println(code);
}
static void print_compilation_times(CodeGen code) {
- double t, total = 0;
-
- total += t = code._tParse / 1e3;
- System.out.println(String.format("Parsing Time: %.3f sec", t));
- total += t = code._tOpto / 1e3;
- System.out.println(String.format("Optimization Time: %.3f sec", t));
- total += t = code._tTypeCheck / 1e3;
- System.out.println(String.format("Type Checking Time: %.3f sec", t));
- total += t = code._tLoopTree / 1e3;
- System.out.println(String.format("Loop Tree Time: %.3f sec", t));
- total += t = code._tInsSel / 1e3;
- System.out.println(String.format("Instruction Selection Time: %.3f sec", t));
- total += t = code._tGCM / 1e3;
- System.out.println(String.format("GCM Time: %.3f sec", t));
- total += t = code._tLocal / 1e3;
- System.out.println(String.format("Local Scheduling Time: %.3f sec", t));
- total += t = code._tRegAlloc / 1e3;
- System.out.println(String.format("Register Allocation Time: %.3f sec", t));
- total += t = code._tEncode / 1e3;
- System.out.println(String.format("Instruction Encoding Time: %.3f sec", t));
- System.out.println(String.format("TOTAL COMPILATION TIME: %.3f sec", total));
+ long total=0;
+ for( Phase phase : Phase.values() ) {
+ long time=code._times[phase.ordinal()];
+ total+=time;
+ System.out.printf("%-24s %.3f sec%n",phase+" Time:",time/1e3);
+ }
+ System.out.printf("TOTAL COMPILATION TIME: %.3f sec%n",total/1e3);
}
static String getInput() throws IOException {
@@ -157,16 +128,16 @@ loop: for (int i = 0; i < args.length; i++) {
switch (arg) {
case "-": use_stdin = true; break;
case "--dump": dump |= DUMP_FINAL; break;
- case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
- case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
- case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
- case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
- case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
- case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
- case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
- case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
- case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
- case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
+ case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
+ case "--dump-after-iter": dump |= DUMP_AFTER_ITER; break;
+ case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
+ case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
+ case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break;
+ case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
+ case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
+ case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
+ case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
+ case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
case "-S": dump |= DUMP_DISASSEMBLE; break;
case "--eval": do_eval = true; first_arg = i + 1; break loop;
case "--run": do_run = true; first_arg = i + 1; break loop;
@@ -221,36 +192,8 @@ loop: for (int i = 0; i < args.length; i++) {
// Compilation pipeline
CodeGen code = new CodeGen(src);
- code.parse();
- dump(code, dump, DUMP_AFTER_PARSE);
-
- code.opto();
- dump(code, dump, DUMP_AFTER_OPTO);
-
- code.typeCheck();
- dump(code, dump, DUMP_AFTER_TYPE_CHECK);
-
- code.loopTree();
- dump(code, dump, DUMP_AFTER_LOOP_TREE);
-
- if (do_codegen) {
- code.instSelect(PORTS, cpu, abi);
- dump(code, dump, DUMP_AFTER_INSTR_SELECT);
- }
-
- code.GCM();
- dump(code, dump, DUMP_AFTER_GCM);
-
- code.localSched();
- dump(code, dump, DUMP_AFTER_LOCAL_SCHED);
-
- if (do_codegen) {
- code.regAlloc();
- dump(code, dump, DUMP_AFTER_REG_ALLOC);
-
- code.encode();
- dump(code, dump, DUMP_AFTER_ENCODE);
- }
+ code.driver(do_codegen ? Phase.Encoding : Phase.LocalSched,
+ do_codegen ? cpu : null, abi, dump);
dump(code, dump, DUMP_FINAL);
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter21/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
new file mode 100644
index 00000000..7054ade4
--- /dev/null
+++ b/chapter21/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
@@ -0,0 +1,145 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.simple.TestC;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+// Force overlapping flags, then force every GPR unavailable across the gap.
+public class FlagSpillTestSupport {
+ private static class Op extends MachConcreteNode {
+ final RegMask _def, _use, _kill;
+ Op(RegMask def,RegMask use,RegMask kill,Node... ins) {
+ super(ins); _def=def; _use=use; _kill=kill; _type=TypeInteger.BOT;
+ }
+ @Override public String op() { return "flagsProbe"; }
+ @Override public RegMask outregmap() { return _def; }
+ @Override public RegMask regmap(int i) { return _use; }
+ @Override public RegMask killmap() { return _kill; }
+ @Override public void encoding(Encoding enc) { throw new AssertionError(); }
+ }
+
+ public static void run() throws Exception {
+ StringBuilder asm = new StringBuilder(".text\n");
+ for( boolean isArm : new boolean[]{false,true} )
+ for( boolean stack : new boolean[]{false,true} ) {
+ CodeGen code = new CodeGen("");
+ code._callingConv="SystemV";
+ code._mach=isArm ? new arm(code) : new x86_64_v2(code);
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ code._cfg.add(block);
+ int flagsReg=isArm ? 64 : 32, firstStack=flagsReg+1;
+ RegMask flags = new RegMask(flagsReg);
+ Op def = new Op(flags,null,null,block);
+ RegMask killMask = stack ? (isArm ? new RegMask(0x7fffffffL,1L) : new RegMask(0x1ffffffefL)) : flags;
+ Op kill = new Op(null,null,killMask,block);
+ new Op(null,flags,null,block,def);
+ RegAlloc alloc = new RegAlloc(code);
+ alloc.regAlloc();
+
+ int splits=0, slots=0;
+ for( Node n : block._outputs ) if( n instanceof SplitNode split ) {
+ int dst=alloc.regnum(n), src=alloc.regnum(n.in(1));
+ assertTrue(split.outregmap().test(dst));
+ assertTrue(split.regmap(1).test(src));
+ for( int fp=isArm ? 32 : 16; fp<flagsReg; fp++ ) {
+ assertFalse("FP output allowed",split.outregmap().test(fp));
+ assertFalse("FP input allowed",split.regmap(1).test(fp));
+ }
+ if( dst>=firstStack || src>=firstStack ) slots++;
+ splits++;
+ }
+ assertTrue(splits>=2);
+ assertEquals(stack,slots!=0);
+
+ code._regAlloc=alloc;
+ FunNode frame = new FunNode(null,null,(Node)null);
+ Encoding save = new Encoding(code), restore = new Encoding(code);
+ save._fun=restore._fun=frame;
+ Encoding enc = save;
+ for( Node n : block._outputs ) {
+ if( n==kill ) enc=restore;
+ if( n instanceof SplitNode split ) split.encoding(enc);
+ }
+ if( isArm ) {
+ for( int bits=0; bits<16; bits++ ) {
+ EvalArm64 cpu = new EvalArm64(new byte[1024],512);
+ System.arraycopy(save._bits.buf(),0,cpu._buf,0,save._bits.size());
+ System.arraycopy(restore._bits.buf(),0,cpu._buf,save._bits.size(),restore._bits.size());
+ cpu.N=(bits&8)!=0; cpu.Z=(bits&4)!=0; cpu.C=(bits&2)!=0; cpu.V=(bits&1)!=0;
+ assertEquals(0,cpu.step(save._bits.size()/4));
+ cpu.N=cpu.Z=cpu.C=cpu.V=false;
+ if( stack ) for( int r=0; r<31; r++ ) cpu.regs[r]=0;
+ assertEquals(0,cpu.step(restore._bits.size()/4));
+ assertEquals((bits&8)!=0,cpu.N); assertEquals((bits&4)!=0,cpu.Z);
+ assertEquals((bits&2)!=0,cpu.C); assertEquals((bits&1)!=0,cpu.V);
+ assertEquals(512,cpu.regs[31]);
+ }
+ } else {
+ Encoding body = new Encoding(code);
+ int[] preserved={3,5,6,7,12,13,14,15};
+ for( int r : preserved ) pushPop(body,r,false);
+ X86.imm(body,0x81,5,4,16); // Reserve spill storage.
+ X86.flags(body,TestC.OS.startsWith("Windows") ? 1 : 7,true);
+ bytes(body,save);
+ if( stack ) {
+ for( int r=0; r<16; r++ ) if( r!=4 ) X86.constant(body,r,0);
+ } else X86.reg(body,0x39,0,0); // CMP rax,rax clobbers only flags.
+ bytes(body,restore);
+ X86.flags(body,0,false); // Return the restored flags.
+ X86.imm(body,0x81,0,4,16);
+ for( int i=preserved.length-1; i>=0; i-- ) pushPop(body,preserved[i],true);
+ X86.ret(body);
+ String name=stack ? "flags_stack" : "flags_reg";
+ asm.append(".globl ").append(name).append('\n').append(name).append(":\n.byte ");
+ for( int i=0; i<body._bits.size(); i++ ) {
+ if( i!=0 ) asm.append(',');
+ asm.append(body._bits.buf()[i]&255);
+ }
+ asm.append('\n');
+ }
+ }
+ Path dir=Files.createTempDirectory(Path.of("build/objs"),"flag_spills_");
+ Files.writeString(dir.resolve("flags.s"),asm);
+ Files.writeString(dir.resolve("flags.c"),"""
+ typedef unsigned long long u64;
+ u64 __attribute__((CALL_CONV)) flags_reg(u64);
+ u64 __attribute__((CALL_CONV)) flags_stack(u64);
+ int main(void) {
+ const int positions[]={0,2,4,6,7,11};
+ for(int bits=0; bits<64; bits++) {
+ u64 flags=0x202;
+ for(int i=0;i<6;i++) if(bits&(1<<i)) flags|=1ULL<<positions[i];
+ if((flags_reg(flags)&0x8d5)!=(flags&0x8d5)) return 1;
+ if((flags_stack(flags)&0x8d5)!=(flags&0x8d5)) return 2;
+ }
+ return 0;
+ }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "flags.exe" : "flags").toString();
+ exec("gcc","-DCALL_CONV="+(TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"),
+ "-o",exe,dir.resolve("flags.c").toString(),dir.resolve("flags.s").toString());
+ exec(exe);
+ }
+
+ private static void exec(String... command) throws Exception {
+ Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
+ String output = new String(process.getInputStream().readAllBytes(),java.nio.charset.StandardCharsets.UTF_8);
+ assertEquals(String.join(" ",command)+"\n"+output,0,process.waitFor());
+ }
+
+ private static void bytes(Encoding dst,Encoding src) {
+ for( int i=0; i<src._bits.size(); i++ ) dst.add1(src._bits.buf()[i]&255);
+ }
+ private static void pushPop(Encoding enc,int reg,boolean pop) {
+ if( reg>=8 ) enc.add1(0x41);
+ enc.add1((pop ? 0x58 : 0x50)+(reg&7));
+ }
+}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter21/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 739eba3b..06ed40d9 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -22,6 +22,11 @@ public class RegAllocTestSupport {
@Override public Node copy() { return new Op(_def,_use,_kill,_clone,(Node)null); }
@Override public void encoding(Encoding enc) { throw new AssertionError(); }
}
+ private static SplitNode testSplit(CodeGen code, MachNode def) {
+ LRG lrg = new LRG((short)0);
+ lrg._machDef = def;
+ return code._mach.split("test",(byte)0,lrg);
+ }
private static CodeGen graph() {
CodeGen code = new CodeGen("");
code._mach = new x86_64_v2(code);
@@ -168,11 +173,11 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode hi = code._mach.split("test",(byte)0,null);
+ SplitNode hi = testSplit(code,def);
hi.setDef(0,code._start); hi.setDef(1,def);
alloc.newLRG(hi)._reg=1;
new Op(null,null,killed ? A : null,false,code._start);
- SplitNode lo = code._mach.split("test",(byte)0,null);
+ SplitNode lo = testSplit(code,hi);
lo.setDef(0,code._start); lo.setDef(1,hi);
alloc.newLRG(lo)._reg=0;
Op use = new Op(null,A,null,false,code._start,lo);
@@ -186,7 +191,7 @@ public class RegAllocTestSupport {
uncolored.newLRG(def0)._mask=A;
Op fixed = new Op(A,null,null,false,pre._start);
uncolored.newLRG(fixed)._mask=A;
- SplitNode copy = pre._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(pre,def0);
copy.setDef(0,pre._start); copy.setDef(1,def0);
assertFalse("A fixed definition clobbers even before coloring",uncolored.sameBlockNoClobber(copy));
}
@@ -199,7 +204,7 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
alloc.newLRG(copy)._reg=1;
if( kind==3 ) new Op(null,null,A,false,code._start);
@@ -222,7 +227,7 @@ public class RegAllocTestSupport {
CodeGen code = graph();
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(new RegMask(3L),null,null,false,code._start);
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
Op use = new Op(null,new RegMask(3L),null,false,code._start,copy);
LRG from = alloc.newLRG(def), to = alloc.newLRG(copy);
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 586924b2..7e8bdbf1 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter21/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();
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/chapter22/README.ja.md b/chapter22/README.ja.md
index 91c74dcf..d6edaf83 100644
--- a/chapter22/README.ja.md
+++ b/chapter22/README.ja.md
@@ -159,5 +159,3 @@ return sum(is);
後のヒューリスティックをいくつか混在させた元の第22章スナップショットは、同じ115コンパイルで854移動、1,736重み付き移動でした。修正して段階化した版は240重み付き移動(13.8%)を減らします。どちらの比較も、コンパイラの章全体どうしの比較と混同してはいけません。下位表現への変換処理も変わるためです。たとえば固定した第20章の String 入力には明示的な return がなく、以前の監査では第22章の既定戻り値処理がその処理対象を除去しました。遅延メモリを使うと、一部の対象コンパイルは呼び出し可能な本体を残し、現在の表にはそれらの移動が含まれます。
完全なスイートと統計実行器は成功します。通常のバックエンド比較では最適化器のシードを固定します。最適化器のワークリストをシャッフルしても本質的に同じグラフへ正規化するはずなので、それらのグラフで割り当てを繰り返しても、検証範囲はあまり増えません。最適化器の正規化やワークリスト順序の失敗を調べるときに、シードを変えてください。
-
-[バックポート記録](../docs/chapter-backports.md) には、解決された String の fall-through と、返される関数の保持/再配置の失敗が、再現方法と検証履歴を含めて記録されています。
diff --git a/chapter22/README.md b/chapter22/README.md
index 00ae6183..f5560aa1 100644
--- a/chapter22/README.md
+++ b/chapter22/README.md
@@ -34,17 +34,24 @@ Let's break it down.
pool*, and from there in an ELF file's RODATA section.
-Chapter 11's lazy memory partitioning continues here: the parser carries one
-`$mem`, and BulkMemPhi/MemPhi discover field aliases during optimization.
-Allocations consume a partial MemMerge and produce `{ptr, $mem}`; calls carry
-whole memory. The loop Load search follows every arm of a memory merge, so
-BrainFuck's invariant program length folds out of its main loop.
-
You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter22) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter21...linear-chapter22) it to the previous chapter.
Here is the [complete language grammar](docs/22-grammar.md) for this chapter.
+## Finalizing the ideal graph
+
+Library functions introduce a distinction between an externally callable entry
+and an unused internal function. After `Iter` settles function pointers,
+`FinalizeIR` removes unknown-caller hooks from internal functions with no callers
+or exposed function address, then runs the resulting peephole cleanup. Public
+entries retain their calling assumptions. Type checking examines the surviving
+graph afterward.
+
+The ideal pipeline is `Parse -> Iter -> FinalizeIR -> TypeCheck`; use
+`code.driver(CodeGen.Phase.TypeCheck)` when all of it is needed.
+
+
## Nested Types and Static Fields
Simple now supports nested types - this is a name-space only change, so no new
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 8a3d6322..e5ad596d 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -9,7 +9,6 @@ import com.seaofnodes.simple.type.*;
import java.io.IOException;
import java.util.BitSet;
import java.util.HashMap;
-import java.util.IdentityHashMap;
@SuppressWarnings("unchecked")
public class CodeGen {
@@ -20,19 +19,63 @@ public class CodeGen {
public static CodeGen CODE;
public enum Phase {
- Parse, // Parse ASCII text into Sea-of-Nodes IR
- Opto, // Run ideal optimizations
- TypeCheck, // Last check for bad programs
- LoopTree, // Build a loop tree; break infinite loops
- Select, // Convert to target hardware nodes
- Schedule, // Global schedule (code motion) nodes
- LocalSched, // Local schedule
- RegAlloc, // Register allocation
- Encoding, // Encoding
- Export, // Export
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ FinalizeIR, // Remove unused internal function entries
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ Select, // Select machine instructions
+ Schedule, // Global code motion
+ LocalSched, // Order instructions within blocks
+ RegAlloc, // Assign registers and stack slots
+ Encoding, // Encode instructions
+ Export; // Link in memory or write ELF
+
}
public Phase _phase;
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) { return driver(target,null,null); }
+ public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); }
+ public CodeGen driver(Phase target, String cpu, String callingConv, int dump) {
+ if( target.ordinal()>=Phase.RegAlloc.ordinal() && cpu==null && _mach==null )
+ throw new IllegalArgumentException("Machine code requires a CPU target");
+ if( target==Phase.Export ) throw new IllegalArgumentException("Use exportELF with an output file or null for memory linking");
+ step(target, Phase.Parse, this::parse, dump);
+ step(target, Phase.Iter, this::iter, dump);
+ step(target, Phase.FinalizeIR, this::finalizeIR, dump);
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ step(target, Phase.Select, () -> instSelect(cpu,callingConv), dump);
+ step(target, Phase.Schedule, this::GCM, dump);
+ step(target, Phase.LocalSched, this::localSched, dump);
+ step(target, Phase.RegAlloc, this::regAlloc, dump);
+ step(target, Phase.Encoding, this::encode, dump);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) {
+ if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":");
+ System.err.println(this);
+ }
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
// ---------------------------
// Compilation source code
public final String _src;
@@ -54,22 +97,6 @@ public class CodeGen {
}
- // All passes up to Phase, except ELF
- public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
- public CodeGen driver( Phase phase, String cpu, String callingConv ) {
- if( _phase==null ) parse();
- int p1 = phase.ordinal();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Opto .ordinal() ) opto();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.TypeCheck .ordinal() ) typeCheck();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LoopTree .ordinal() ) loopTree();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Select .ordinal() && cpu != null ) instSelect(cpu,callingConv);
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Schedule .ordinal() ) GCM();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LocalSched.ordinal() ) localSched();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.RegAlloc .ordinal() ) regAlloc();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Encoding .ordinal() ) encode();
- return this;
- }
-
// Run all the phases through final ELF emission
public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException {
return driver(Phase.Encoding,cpu,callingConv).exportELF(obj);
@@ -145,16 +172,10 @@ public class CodeGen {
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
- public int _tParse;
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- long t0 = System.currentTimeMillis();
-
- P.parse();
- _tParse = (int)(System.currentTimeMillis() - t0);
- if( _obs != null ) _obs.phase("Parse");
- return this;
+ return phase(Phase.Parse, _phase==null, () -> {
+ P.parse();
+ });
}
@@ -169,80 +190,25 @@ public class CodeGen {
public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
- // Run ideal optimizations
- public int _tOpto;
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
- long t0 = System.currentTimeMillis();
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); }
- // Pessimistic peephole optimization on a worklist
- _iter.iterate(this);
-
- // Not really a true optimistic pass, but look for unlinked functions.
- // This can be removed, which may trigger another round of pessimistic.
- // This is the point where we flip from a virtual Call Graph (any call
- // can call any function) to having a correct (but conservative) CG.
-
- FunNode main = link(_main);
- for( int i=0; i<_start.nOuts(); i++ ) {
- Node use = _start.out(i);
- if( use instanceof FunNode fun &&
- fun.nIns()==2 && fun.in(1)==_start && fun != main && !fun.hasPtr() &&
- (fun._name==null || fun._name.startsWith("sys.")) ) {
- add(fun).setDef(1,Parser.XCTRL);
- addAll(fun._outputs);
- i--;
- }
- }
- _iter.iterate(this);
-
- _tOpto = (int)(System.currentTimeMillis() - t0);
-
- // TODO:
- // Optimistic
- // TODO:
- // loop unroll, peel, RCE, etc
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
+ public CodeGen finalizeIR() { return phase(Phase.FinalizeIR, _phase==Phase.Iter, () -> FinalizeIR.run(this)); }
public <N extends Node> N add( N n ) { return (N)_iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
// ---------------------------
// Last check for bad programs
- public int _tTypeCheck;
- 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();
- _phase = Phase.TypeCheck;
- long t0 = System.currentTimeMillis();
-
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
- });
- _tTypeCheck = (int)(System.currentTimeMillis() - t0);
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.FinalizeIR, () -> TypeCheck.run(this)); }
// ---------------------------
// Build the loop tree; break never-exit loops
- public int _tLoopTree;
public CodeGen loopTree() {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.LoopTree;
- long t0 = System.currentTimeMillis();
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_start,_stop);
- _tLoopTree = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> {
+ // Build the loop tree, fix never-exit loops
+ _start.buildLoopTree(_start,_stop);
+ });
}
// ---------------------------
@@ -264,138 +230,41 @@ public class CodeGen {
public RegMask _rpcMask;
// Convert to target hardware nodes
- public int _tInsSel;
public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
public CodeGen instSelect( String cpu, String callingConv, String base ) {
- assert _phase.ordinal() == Phase.LoopTree.ordinal();
- _phase = Phase.Select;
-
- _callingConv = callingConv;
-
- // Not timing the class load...
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- // Build global copies of common register masks.
- long callerSave = _mach.callerSave();
- long neverSave = _mach. neverSave();
- int maxReg = Math.min(64,_mach.regs().length);
- assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
- _callerSave = new RegMask(callerSave);
-
- // Build a Return RegMask array. All returns have the following inputs:
- // 0 - ctrl
- // 1 - memory
- // 2 - varies with returning GPR,FPR
- // 3 - RPC
- // 4+- Caller save registers
- _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
- for( int reg=0, i=4; reg<maxReg; reg++ )
- if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
- _retMasks[i++] = new RegMask(reg);
- _rpcMask = new RegMask(_mach.rpc());
- _retMasks[3] = _rpcMask;
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = ( StopNode)map.get(_stop );
- StartNode start = (StartNode)map.get(_start);
- _start = start==null ? new StartNode(_start) : start;
- _instOuts(_stop,visit());
- _visit.clear();
- _tInsSel = (int)(System.currentTimeMillis() - t0);
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachNode already, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachNode ) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- map.put(n, x=_mach.instSelect(n) );
- // Stop retains the callable body. Its lifetime edge is not a machine operand.
- if( n instanceof FunPtrNode ) x._inputs.pop();
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- if( x instanceof MachNode mach )
- mach.postSelect(this); // Post selection action
-
- return x;
- }
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- _instOuts(in,visit);
- }
+ if( cpu==null ) return this;
+ return phase(Phase.Select, _phase==Phase.LoopTree,
+ () -> new InstSelect(this).run(cpu,callingConv,base));
}
// ---------------------------
// Control Flow Graph in Reverse Post Order.
- public int _tGCM;
public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.Select.ordinal();
- _phase = Phase.Schedule;
- long t0 = System.currentTimeMillis();
-
- GlobalCodeMotion.buildCFG(this);
- _tGCM = (int)(System.currentTimeMillis() - t0);
+ return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.Select), () -> {
+ GlobalCodeMotion.buildCFG(this);
- return this;
+ });
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- public int _tLocal;
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- long t0 = System.currentTimeMillis();
- ListScheduler.sched(this);
- _tLocal = (int)(System.currentTimeMillis() - t0);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// ---------------------------
// Register Allocation
- public int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
- assert _phase == Phase.LocalSched;
- _phase = Phase.RegAlloc;
- long t0 = System.currentTimeMillis();
- _regAlloc = new RegAlloc(this);
- _regAlloc.regAlloc();
- _tRegAlloc = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> {
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ });
}
// Human readable register name
@@ -411,27 +280,27 @@ public class CodeGen {
// ---------------------------
// Encoding
- public int _tEncode;
public Encoding _encoding; // Encoding object
public void preEncode() { } // overridden by alternative ports
public CodeGen encode() {
- assert _phase == Phase.RegAlloc;
- _phase = Phase.Encoding;
- long t0 = System.currentTimeMillis();
- _encoding = new Encoding(this);
- preEncode();
- _encoding.encode();
- _tEncode = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.Encoding, _phase==Phase.RegAlloc, () -> {
+ _encoding = new Encoding(this);
+ preEncode();
+ _encoding.encode();
+ });
}
// ---------------------------
// Exporting to external formats
public CodeGen exportELF(String fname) throws IOException {
- assert _phase == Phase.Encoding;
- _phase = Phase.Export;
- if( fname == null ) new LinkMem(this).link(); // In memory patching
- else new ElfFile(this).export(fname); // External ELF file
+ assert _phase==Phase.Encoding;
+ _phase=Phase.Export;
+ long t0=System.currentTimeMillis();
+ try {
+ if( fname == null ) new LinkMem(this).link(); // In memory patching
+ else new ElfFile(this).export(fname); // External ELF file
+ } finally { _times[Phase.Export.ordinal()]=System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(Phase.Export.name());
return this;
}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
new file mode 100644
index 00000000..0e7f3060
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
@@ -0,0 +1,30 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.util.Arrays;
+
+// Finish the ideal graph before type checking and scheduling.
+abstract class FinalizeIR {
+ static void run(CodeGen code) {
+ // Not really a true optimistic pass, but look for unlinked functions.
+ // This can be removed, which may trigger another round of pessimistic.
+ // This is the point where we flip from a virtual Call Graph (any call
+ // can call any function) to having a correct (but conservative) CG.
+
+ FunNode main = code.link(code._main);
+ for( int i=0; i<code._start.nOuts(); i++ ) {
+ Node use = code._start.out(i);
+ if( use instanceof FunNode fun &&
+ fun.nIns()==2 && fun.in(1)==code._start && fun != main && !fun.hasPtr() &&
+ (fun._name==null || fun._name.startsWith("sys.")) ) {
+ code.add(fun).setDef(1,Parser.XCTRL);
+ code.addAll(fun._outputs);
+ i--;
+ }
+ }
+ code._iter.iterate(code);
+
+ }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 530336d6..1818be73 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -273,10 +273,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -293,7 +294,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// A conditional writer can precede the load without dominating its late block.
// Walk back to the first overlap with the load's placement range.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
new file mode 100644
index 00000000..e8e87785
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ InstSelect(CodeGen code) { _code=code; }
+
+ @SuppressWarnings("unchecked")
+ void run(String cpu, String callingConv, String base) {
+ _code._callingConv = callingConv;
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = base+"."+cpu+"."+cpu;
+ try { _code._mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(_code); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Build global copies of common register masks.
+ long callerSave = _code._mach.callerSave();
+ long neverSave = _code._mach. neverSave();
+ int maxReg = Math.min(64,_code._mach.regs().length);
+ assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+ _code._callerSave = new RegMask(callerSave);
+
+ // Build a Return RegMask array. All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ _code._retMasks = new RegMask[(maxReg-_code._callerSave.size()-Long.bitCount( neverSave ))+4];
+ for( int reg=0, i=4; reg<maxReg; reg++ )
+ if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+ _code._retMasks[i++] = new RegMask(reg);
+ _code._rpcMask = new RegMask(_code._mach.rpc());
+ _code._retMasks[3] = _code._rpcMask;
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = ( StopNode)map.get(_code._stop );
+ StartNode start = (StartNode)map.get(_code._start);
+ _code._start = start==null ? new StartNode(_code._start) : start;
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachNode already, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachNode ) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Stop retains the callable body. Its lifetime edge is not a machine operand.
+ if( n instanceof FunPtrNode ) x._inputs.pop();
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(_code); // Post selection action
+
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index ec254a2e..6ece27a2 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -59,6 +59,8 @@ public class LRG {
public int nadj() { return _adj==null ? 0 : _adj._len; }
+ public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 5a641490..39c6dd88 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -361,7 +361,7 @@ public class RegAlloc {
// If the minLoopDepth is less than the maxLoopDepth: for-all defs and
// uses, if at minLoopDepth or lower, split after def and before use.
for( Node n : _ns ) {
- if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) continue; // Ordinary splits already reach stack storage.
if( n.isDead() ) continue; // Some Clonable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
@@ -414,8 +414,8 @@ public class RegAlloc {
}
private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
+ // Ordinary splits already reach stack storage; register-only transfers do not.
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld;
// Collect min/max loop depth
int min = (int)ld;
int max = (int)(ld>>32);
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
new file mode 100644
index 00000000..0997fd2f
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index ff18b292..437a700a 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -10,6 +10,8 @@ public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
public final byte _round;
public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
+ // A register-only transfer may need another split to reach stack storage.
+ public boolean isNonStackSplit() { return false; }
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
new file mode 100644
index 00000000..3e045a6f
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+
+// NZCV transfers require a GPR. Further splits can spill that GPR to the stack.
+public class SplitFlagsARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,1L);
+ SplitFlagsARM(String kind,byte round) { super(kind,round); }
+ @Override public boolean isNonStackSplit() { return true; }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( src==arm.FLAGS ) enc.add4(Arm64.flags(dst,false));
+ else if( dst==arm.FLAGS ) enc.add4(Arm64.flags(src,true));
+ else super.encoding(enc);
+ }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
new file mode 100644
index 00000000..46eff4ca
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
@@ -0,0 +1,11 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Keep a saved NZCV value in GPRs and stack slots through subsequent spills.
+public class SplitGPRARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L);
+ SplitGPRARM(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index ddd9797b..be77eba9 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -269,7 +269,16 @@ public class arm extends Machine {
@Override public int rpc() { return X30; }
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask();
+ if( (def!=null && def.size1() && def.test(FLAGS)) ||
+ (use!=null && use.size1() && use.test(FLAGS)) )
+ return new SplitFlagsARM(kind,round);
+ // Spilling the saved flags requires a GPR between NZCV and the stack.
+ if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM )
+ return new SplitGPRARM(kind,round);
+ return new SplitARM(kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
new file mode 100644
index 00000000..4650b535
--- /dev/null
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+
+// Flag snapshots can use GPRs or stack slots, but never XMM registers.
+public class SplitFlagsX86 extends SplitX86 {
+ private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L<<x86_64_v2.FLAGS) | x86_64_v2.SPILLS,-1L);
+ SplitFlagsX86(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( dst==x86_64_v2.FLAGS || src==x86_64_v2.FLAGS ) {
+ boolean toFlags = dst==x86_64_v2.FLAGS;
+ int reg = toFlags ? src : dst;
+ if( reg>=x86_64_v2.MAX_REG )
+ X86.flagsStack(enc,enc._fun.computeStackOffset(enc._code,reg),toFlags);
+ else
+ X86.flags(enc,reg,toFlags);
+ } else super.encoding(enc);
+ }
+}
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
index 5cf668d1..3c4dde17 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -17,12 +17,10 @@ public class SplitX86 extends SplitNode {
@Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
- // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
- // flags->flags.
+ // Ordinary GPR, XMM, and stack moves. Flags use SplitFlagsX86.
@Override public void encoding( Encoding enc ) {
short dst=enc.reg(this), src=enc.reg(in(1));
- if( dst==x86_64_v2.FLAGS ) { X86.flags(enc,src,true); return; }
- if( src==x86_64_v2.FLAGS ) { X86.flags(enc,dst,false); return; }
+ assert dst!=x86_64_v2.FLAGS && src!=x86_64_v2.FLAGS;
boolean dstX=dst>=x86_64_v2.XMM_OFFSET, srcX=src>=x86_64_v2.XMM_OFFSET;
// Stack spills
if( dst >= x86_64_v2.MAX_REG ) {
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index 7ddd17d4..dc3f813e 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -47,7 +47,7 @@ public class x86_64_v2 extends Machine {
static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<<RAX) & ~(1L<<RDX));
static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | SPILLS, -1L );
// Load/store mask; both GPR and FPR
static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -238,7 +238,9 @@ public class x86_64_v2 extends Machine {
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(String kind, byte round, LRG lrg) {
- return new SplitX86(kind, round);
+ return lrg._machDef!=null && lrg._machDef.outregmap().test(FLAGS)
+ ? new SplitFlagsX86(kind,round)
+ : new SplitX86(kind,round);
}
// Return a MachNode unconditional branch
diff --git a/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index 22b6beba..b6c09d6b 100644
--- a/chapter22/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter22/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.driver(CodeGen.Phase.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/chapter22/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 6ff4ac95..8168594a 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -20,7 +20,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().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().finalizeIR();
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -145,7 +145,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index b6bc2bf3..ad3597f2 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter22/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().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().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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), 123L).parse().opto();
+""", TypeInteger.constant(2), 123L).parse().iter().finalizeIR();
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L).parse().opto();
+""", TypeInteger.constant(1), 123L).parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,3,5,4);", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3), 123L);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0), 123L);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,6,1,5);", code.print());
}
@@ -278,7 +278,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1), 123L);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2), 123L);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 5;", code.print());
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 88554323..0ebc427c 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -34,7 +34,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().finalizeIR();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -122,7 +122,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -180,7 +180,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 09669cc7..543b91d1 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -22,7 +22,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 551880b1..df590448 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -127,14 +127,14 @@ while(v1+arg) {
v1=v2;
}
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ ]", code.print()); // No explicit return: omit default main.
}
@Test
public void testWhile0() {
CodeGen code = new CodeGen("while(0) continue; if(0) arg=0; return arg;");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -147,7 +147,7 @@ if(0) while(0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -155,14 +155,14 @@ return 0;
@Test
public void testPrecedence() {
CodeGen code = new CodeGen("return 3-1+2;");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -180,7 +180,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
}
@@ -194,7 +194,7 @@ arg=0;
int v0=0!=0<-0;
return -0+0+0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -202,14 +202,14 @@ return -0+0+0;
@Test
public void testFuzz2() {
CodeGen code = new CodeGen("return 0+-0;");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@Test
public void testFuzz3() {
CodeGen code = new CodeGen("int v0=0; while(0==69) while(v0) return 0;");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ ]", code.print()); // The only return is unreachable.
}
@@ -221,7 +221,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -243,7 +243,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -255,7 +255,7 @@ int v0=0;
while(0==1) while(v0)
v0=1+v0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ ]", code.print());
}
@@ -271,14 +271,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index bcb73eb9..68971e79 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -14,7 +14,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).driver(CodeGen.Phase.TypeCheck);
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -39,7 +39,7 @@ public class Chapter10Test {
"if (!!!p) return -1; return p.x;",
"int b = !!p; if (b) return p.x + b - 1; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -54,7 +54,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -86,7 +86,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -106,7 +106,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 2;", code.print());
}
@@ -122,7 +122,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Vector2D;", code.print());
}
@@ -160,7 +160,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -173,7 +173,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar",e.getMessage()); }
}
@@ -186,7 +186,7 @@ if (arg) bar = new Bar;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
}
@@ -212,7 +212,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -228,7 +228,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -245,7 +245,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) {
assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@@ -282,7 +282,7 @@ while( i.x < i.len ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -301,7 +301,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -319,7 +319,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -336,7 +336,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -364,7 +364,7 @@ if(0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -378,7 +378,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return s0;", code.print());
}
@@ -394,7 +394,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
}
@Test
@@ -406,7 +406,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (const)s0;", code.print());
}
@@ -439,7 +439,7 @@ while(0) {}
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -450,7 +450,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -464,7 +464,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -490,7 +490,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).parse().iter().finalizeIR();
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -506,7 +506,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -523,7 +523,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -538,7 +538,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -556,7 +556,7 @@ return 0;
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().finalizeIR();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 439333ca..a2a6aab4 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -13,7 +13,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -32,7 +32,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -54,7 +54,7 @@ public class Chapter11Test {
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse();
- code.opto();
+ code.iter().finalizeIR();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index d8b072a2..e680af8c 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -14,7 +14,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -34,7 +34,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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.i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,0,1,.i);", 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();
+ code.parse().iter().finalizeIR();
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().finalizeIR();
assertEquals("return null;", code.print());
}
@@ -100,7 +100,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (const)M;", code.print());
}
@@ -113,7 +113,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; }
return n.next;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *M",e.getMessage()); }
}
@@ -126,20 +126,20 @@ struct N { !N? !next; int i; };
n.i = 3.14;
return n.i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
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(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
}
@@ -150,7 +150,7 @@ return n.i;
struct S{};
return 0;
""");
- code.parse().opto();
+ code.parse().iter().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(); fail(); }
+ try { code.parse().iter().finalizeIR(); 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().finalizeIR();
assertEquals("return 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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
}
@@ -256,7 +256,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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N{a=Y{a=J{a=U{a=F{a=Q{a=B{a=M{a=X{a=I{a=T{a=E{a=P{a=A{a=L{a=W{a=H{a=S{a=D{a=O{a=Z{a=K{a=V{a=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/chapter22/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 66e12e42..4f699a3f 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -290,8 +290,11 @@ else {
}
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 9;", code.print());
+ code.driver(CodeGen.Phase.Schedule);
+ // LoopTree connects the nonreturning paths using synthetic Never exits.
+ assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
}
@@ -322,7 +325,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter().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
@@ -344,7 +347,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -367,7 +370,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -386,7 +389,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return S;", code.print());
}
@@ -404,7 +407,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -421,7 +424,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return S;", code.print());
}
@@ -436,7 +439,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -454,7 +457,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -474,7 +477,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -492,7 +495,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return S;", code.print());
}
@@ -511,7 +514,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
//assertEquals("return new S;", code.print());
}
@@ -528,7 +531,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -546,7 +549,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 6377bc14..51397e77 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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]).driver(CodeGen.Phase.TypeCheck);
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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;").driver(CodeGen.Phase.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));
@@ -42,7 +42,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).driver(CodeGen.Phase.TypeCheck);
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index c342eeed..95a5ee81 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().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().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();
+ code.parse().iter().finalizeIR();
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().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().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().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().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.driver(CodeGen.Phase.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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 074cd8ba..1f47ee10 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -17,7 +17,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return C;", code.print());
assertEquals("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().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().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().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().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().finalizeIR();
assertEquals("return [*A?];", code.print());
assertEquals("*A {int !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -131,7 +131,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter().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.driver(CodeGen.Phase.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();
+ code.parse().iter().finalizeIR();
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -186,7 +186,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter().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();
+ code.parse().iter().finalizeIR();
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().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().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().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
}
@@ -257,7 +257,7 @@ return new flt;
"""
flt is = new int[2];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Type *[int] is not of declared type flt",e.getMessage()); }
}
@@ -268,7 +268,7 @@ flt is = new int[2];
int[] is = new int[3.14];
return is[1];
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); }
}
@@ -278,7 +278,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 4));
try { Eval2.eval(code,-1); fail(); }
@@ -311,7 +311,7 @@ while(v5+(0&0)) {
}
return v1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -327,7 +327,7 @@ int v8=0;
while(0<--1>>>---(v7*0==v8)) {}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -352,7 +352,7 @@ else {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index 362388d6..27766f20 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -17,7 +17,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -30,7 +30,7 @@ return 3.14;
int x, y;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -41,7 +41,7 @@ return x+y;
int x=2, y=x+1;
return x+y;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 5;", code.print());
assertEquals("5", Eval2.eval(code, 0));
}
@@ -54,7 +54,7 @@ struct X { int x=3; };
X z = new X;
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 4;", code.print());
assertEquals("4", Eval2.eval(code, 0));
}
@@ -87,7 +87,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -118,7 +118,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -143,7 +143,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -167,7 +167,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter().finalizeIR();
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 16ffe967..b8a25016 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
@@ -17,7 +17,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().finalizeIR();
assertEquals("return Phi(Region,9,3);", code.print());
assertEquals("3", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -30,7 +30,7 @@ struct X { int ~x; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -42,7 +42,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -55,7 +55,7 @@ Point p = new Point { x=5; y=6; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -67,7 +67,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -75,7 +75,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).parse().iter().finalizeIR(); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -88,7 +88,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -100,7 +100,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index e8f6e0bd..3467f35c 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -15,7 +15,7 @@ public class Chapter17bTest {
"""
return 1;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.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().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().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().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().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().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().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().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().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().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().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();
+ code.parse().iter().finalizeIR();
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().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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().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().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().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter().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.driver(CodeGen.Phase.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;}; S s=new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().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().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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -217,7 +217,7 @@ foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
~Foo xfoo = foo; // Throw away mutability
xfoo.bar.x++; // Error, cannot mutate through xfoo
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -238,7 +238,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().finalizeIR();
assertEquals("return 45;", code.print());
assertEquals("45", Eval2.eval(code, 0));
}
@@ -248,7 +248,7 @@ return x4*10+x5;
CodeGen code = new CodeGen("""
int i,i++;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -263,7 +263,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -276,7 +276,7 @@ return new A {
if (arg) b = new B; // Constructor ends with partial init of b
}.b;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("'A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
}
@@ -290,7 +290,7 @@ val s = new S{x = 2;};
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -309,7 +309,7 @@ focus(me);
return me;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@@ -319,7 +319,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -330,7 +330,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -355,7 +355,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -369,7 +369,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=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().finalizeIR();
assertEquals("return Phi(Region,.x,0,.x);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -382,7 +382,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().finalizeIR();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -394,7 +394,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -407,7 +407,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -420,7 +420,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -435,7 +435,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -448,7 +448,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -461,7 +461,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -469,7 +469,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -485,7 +485,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -499,7 +499,7 @@ struct A{
};
return new A.not_array_of_b;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return [*B?];", code.print());
assertEquals("*B?[]", Eval2.eval(code,0));
}
@@ -518,7 +518,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -549,7 +549,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return [int];", code.print());
assertEquals("int[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 6eb4d6f4..e310366d 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter22/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]).driver(CodeGen.Phase.TypeCheck);
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -140,7 +140,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
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().finalizeIR();
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -170,7 +170,7 @@ return x2;
x*x;
};
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (Parm_x(sq,int)*x);", code._stop.toString());
//assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -197,7 +197,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -209,7 +209,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -222,7 +222,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ return (Parm_x($fun2,int,3)<<1); return (Parm_x($fun1,int,3)*x); return #2; ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -232,7 +232,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.driver(CodeGen.Phase.TypeCheck);
assertEquals("Stop[ return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*#2)); return #2; ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -249,7 +249,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -264,7 +264,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Stop[ return 2; return 1; return 1; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -358,7 +358,7 @@ for(;;) {
}
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { int -> int #1}?",e.getMessage()); }
}
@@ -370,7 +370,7 @@ return 0;
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -388,7 +388,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -401,7 +401,7 @@ val g = { ->
};
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -415,7 +415,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();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -437,7 +437,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -489,7 +489,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).parse().iter().finalizeIR();
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -518,7 +518,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).parse().iter().finalizeIR();
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -536,7 +536,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).parse().iter().finalizeIR();
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 4564e513..ac141247 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -39,7 +39,7 @@ public class Chapter19Test {
CodeGen code = new CodeGen("""
return 0;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -48,7 +48,7 @@ return 0;
@Test
public void testString() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
- CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); return Phi(Region,1,0,0,1); ]", code._stop.toString());
//assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@@ -358,11 +358,11 @@ return sq(arg) + sq(3);
for( String cpu : new String[]{"x86_64_v2","riscv","arm"} )
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
var aliases = memoryPhis(code._stop);
- code.loopTree().instSelect(cpu,"SystemV");
+ code.loopTree().driver(CodeGen.Phase.Select,cpu,"SystemV");
assertEquals(aliases,memoryPhis(code._stop));
- code.GCM().localSched();
+ code.driver(CodeGen.Phase.Schedule).localSched();
code._stop.walk(n -> {
if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
assertTrue(n.in(0) instanceof CFGNode);
@@ -414,7 +414,7 @@ return sq(arg) + sq(3);
int before = p.x + arg;
a.x=33; b.x=44;
return before;
- """).parse().opto().typeCheck().loopTree().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ """).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
var read = (MemOpNode)code._stop.walk(n ->
n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
assertNotNull(read);
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 3575e461..3b1ca4ff 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -11,6 +11,10 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter20Test {
+ @Test public void testFlagSpills() throws Exception {
+ com.seaofnodes.simple.codegen.FlagSpillTestSupport.run();
+ }
+
@Test public void testAllocatorTopPhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.topPhi(); }
@Test public void testNeverReturnAllocation() {
@@ -71,7 +75,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,com.seaofnodes.simple.type.TypeInteger.BOT,seed).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed).driver(CodeGen.Phase.TypeCheck);
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -96,7 +100,7 @@ return f(s);
@Test
public void testJig() {
CodeGen code = new CodeGen("return 0;");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -414,8 +418,7 @@ return a;
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY,
"int[] !a = new int[3]; a[arg]=arg; return a[0]+1;"} ) {
- var code = new CodeGen(src).parse().opto().typeCheck().loopTree()
- .instSelect(cpu,"SystemV").GCM().localSched().regAlloc();
+ var code = new CodeGen(src).driver(CodeGen.Phase.RegAlloc,cpu,"SystemV");
com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
code._stop.walk(n -> {
// Whole memory, precise Phis, and memory projections need no register.
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 2c91de0d..4bce96ab 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -28,7 +28,7 @@ public class Chapter21Test {
else if(i==4) b.x=40;
}
return sum;
- """).parse().opto();
+ """).parse().iter().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));
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index 2d94b8f0..a506004e 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -126,7 +126,7 @@ public class Chapter22Test {
else assertEquals(42,cpu.regs[0]);
}
}
- CodeGen unused = new CodeGen("({->42;}); return 0;").parse().opto();
+ CodeGen unused = new CodeGen("({->42;}); return 0;").parse().iter().finalizeIR();
assertEquals(1,unused._stop.nIns());
}
@@ -171,7 +171,7 @@ public class Chapter22Test {
@Test public void testSubZeroTypeError() {
try {
- new CodeGen("return null-0;").parse().opto().typeCheck();
+ new CodeGen("return null-0;").driver(CodeGen.Phase.TypeCheck);
fail("Subtraction must reject null even when the other operand is zero");
} catch( Parser.ParseException e ) {
assertEquals("Cannot '-' null",e.getMessage());
@@ -343,7 +343,7 @@ bb.c = cc;
val x = 5; // aa.az; // Error to self-define forward ref
return cc.cz;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 25;", code._stop.toString());
assertEquals("25", Eval2.eval(code, 0));
}
@@ -383,7 +383,7 @@ val sum = { i32[~] is -> // final array
};
return sum(is);
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code._stop.toString());
assertEquals("14", Eval2.eval(code, 0));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Compile.java b/chapter22/src/test/java/com/seaofnodes/simple/Compile.java
index 088055f9..f3ca1905 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Compile.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Compile.java
@@ -16,7 +16,7 @@ public class Compile {
private static Process run(String simple, String c) throws IOException, InterruptedException {
boolean USE_WSL = System.getProperty("os.name").startsWith("Windows");
CodeGen code = new CodeGen(simple);
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode();
+ code.driver(CodeGen.Phase.Encoding,"x86_64_v2","SystemV");
if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main";
code.exportELF("test.o");
var params = new ArrayList<String>();
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter22/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 48f739cf..e2d822d0 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter22/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).driver(CodeGen.Phase.TypeCheck);
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/LiteralTest.java b/chapter22/src/test/java/com/seaofnodes/simple/LiteralTest.java
index 2375f213..0f6e43a4 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/LiteralTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/LiteralTest.java
@@ -20,7 +20,7 @@ public class LiteralTest {
}
private static void assertString(String text, byte[] expected) {
- var code = new CodeGen("return \"" + text + "\";").parse().opto().typeCheck();
+ var code = new CodeGen("return \"" + text + "\";").driver(CodeGen.Phase.TypeCheck);
TypeMemPtr ptr = (TypeMemPtr)code.expr()._type;
TypeConAryB bytes = (TypeConAryB)ptr._obj._con;
assertArrayEquals(text, expected, bytes._ary);
@@ -33,7 +33,7 @@ public class LiteralTest {
}
private static void assertCharacter(String text, int expected) {
- var code = new CodeGen("return '" + text + "';").parse().opto().typeCheck();
+ var code = new CodeGen("return '" + text + "';").driver(CodeGen.Phase.TypeCheck);
assertEquals(text, expected, ((TypeInteger)code.expr()._type).value());
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Main.java b/chapter22/src/test/java/com/seaofnodes/simple/Main.java
index 589c043b..ca1d0b5b 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter22/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.driver(CodeGen.Phase.LocalSched);
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index 4bd36256..700e5344 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -66,7 +66,7 @@ public class MemoryEncodingTest {
"""
};
for( int p=0; p<sources.length; p++ ) {
- CodeGen code=new CodeGen(sources[p]).parse().opto();
+ CodeGen code=new CodeGen(sources[p]).parse().iter().finalizeIR();
if( p==0 ) assertNull(code._stop.walk(n ->
n instanceof LoadNode ld && ld.ptr() instanceof ProjNode ptr &&
ptr.in(0) instanceof NewNode nn &&
@@ -160,7 +160,7 @@ public class MemoryEncodingTest {
}
return null;
}));
- code.instSelect(target,"SystemV").GCM().localSched().regAlloc();
+ code.driver(CodeGen.Phase.RegAlloc,target,"SystemV");
RegAllocTestSupport.checkRegisters(code);
code.encode();
}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter22/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 7ae80314..51e31a17 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter22/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).driver(CodeGen.Phase.TypeCheck);
assertEquals(result,Eval2.eval(code,0));
}
private static void bad(String src) {
try {
- new CodeGen(src).parse().opto().typeCheck();
+ new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter22/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..885889a8
--- /dev/null
+++ b/chapter22/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter FinalizeIR TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testResumeMachineCompilation() {
+ CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck);
+ staged.driver(Phase.Select,"x86_64_v2","SystemV");
+ staged.driver(Phase.Encoding,"x86_64_v2","SystemV");
+ byte[] bits = staged._encoding._bits.toByteArray();
+ CodeGen direct = new CodeGen(SRC).driver(Phase.Encoding,"x86_64_v2","SystemV");
+ assertArrayEquals(bits,direct._encoding._bits.toByteArray());
+ }
+}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter22/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 3b19857a..ca211c60 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -11,6 +11,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -31,7 +51,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter().finalizeIR();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -46,7 +66,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -65,7 +85,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Simple.java b/chapter22/src/test/java/com/seaofnodes/simple/Simple.java
index cd795fae..69fbabc9 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/Simple.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
import com.seaofnodes.simple.print.*;
import java.nio.file.Files;
@@ -13,20 +14,21 @@ import java.util.stream.Collectors;
public class Simple {
public static final String PORTS = "com.seaofnodes.simple.node.cpus";
- static final int DUMP_AFTER_PARSE = 1<<0;
- static final int DUMP_AFTER_OPTO = 1<<1;
- static final int DUMP_AFTER_TYPE_CHECK = 1<<2;
- static final int DUMP_AFTER_LOOP_TREE = 1<<3;
- static final int DUMP_AFTER_SELECT = 1<<4;
- static final int DUMP_AFTER_GCM = 1<<5;
- static final int DUMP_AFTER_LOCAL_SCHED = 1<<6;
- static final int DUMP_AFTER_REG_ALLOC = 1<<7;
- static final int DUMP_AFTER_ENCODE = 1<<8;
+ static final int DUMP_AFTER_PARSE = 1<<Phase.Parse.ordinal();
+ static final int DUMP_AFTER_ITER = 1<<Phase.Iter.ordinal();
+ static final int DUMP_AFTER_FINALIZE_IR = 1<<Phase.FinalizeIR.ordinal();
+ static final int DUMP_AFTER_TYPE_CHECK = 1<<Phase.TypeCheck.ordinal();
+ static final int DUMP_AFTER_LOOP_TREE = 1<<Phase.LoopTree.ordinal();
+ static final int DUMP_AFTER_SELECT = 1<<Phase.Select.ordinal();
+ static final int DUMP_AFTER_GCM = 1<<Phase.Schedule.ordinal();
+ static final int DUMP_AFTER_LOCAL_SCHED = 1<<Phase.LocalSched.ordinal();
+ static final int DUMP_AFTER_REG_ALLOC = 1<<Phase.RegAlloc.ordinal();
+ static final int DUMP_AFTER_ENCODE = 1<<Phase.Encoding.ordinal();
static final int DUMP_FINAL = 1<<16;
static final int DUMP_PASS_NAME = 1<<30;
- static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME;
+ static final int DUMP_AFTER_ALL = ((1<<Phase.values().length)-1) | DUMP_PASS_NAME;
static final int DUMP_DISASSEMBLE = 1<<31;
@@ -36,15 +38,17 @@ public class Simple {
simple [options] <input-file> ...args...
Options:
--dump - dump final intermediate representation
- --dump-after-parse - dump intermediate representation after parse
- --dump-after-opto - dump intermediate representation after opto pass
- --dump-after-type-check - dump intermediate representation after type check pass
- --dump-after-select - dump intermediate representation after instrution selection pass
- --dump-after-gcm - dump intermediate representation after GCM pass
- --dump-after-local-sched - dump intermediate representation after local scheduling pass
- --dump-after-reg-alloc - dump intermediate representation after register allocation pass
- --dump-after-encode - dump intermediate representation after encoding pass
- --dump-after-all - dump intermediate representation after all passes
+ --dump-after-parse - dump intermediate representation after parsing
+ --dump-after-iter - dump intermediate representation after Iter
+ --dump-after-finalize-ir - dump intermediate representation after FinalizeIR
+ --dump-after-type-check - dump intermediate representation after type checking
+ --dump-after-loop-tree - dump intermediate representation after loop-tree construction
+ --dump-after-select - dump intermediate representation after instruction selection
+ --dump-after-gcm - dump intermediate representation after GCM
+ --dump-after-local-sched - dump intermediate representation after local scheduling
+ --dump-after-reg-alloc - dump intermediate representation after register allocation
+ --dump-after-encode - dump intermediate representation after encoding
+ --dump-after-all - dump intermediate representation after all passes
-S - dump generated assembler code
--eval - evaluate the compiled code in emulator
--run - run the compiled code natively; this is the default
@@ -74,50 +78,19 @@ Options:
}
static void dump(CodeGen code, int dump, int pass) {
- if ((dump & pass) != 0) {
- if ((dump & DUMP_PASS_NAME) != 0) {
- System.err.println(switch (pass) {
- case DUMP_AFTER_PARSE -> "After Parse:";
- case DUMP_AFTER_OPTO -> "After OPTO:";
- case DUMP_AFTER_TYPE_CHECK -> "After Type Check:";
- case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:";
- case DUMP_AFTER_SELECT -> "After Code Selection:";
- case DUMP_AFTER_GCM -> "After GCM:";
- case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:";
- case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:";
- case DUMP_AFTER_ENCODE -> "After Encoding:";
- case DUMP_FINAL -> "Final:";
- default -> throw Utils.TODO();
- });
- }
-
- System.err.println(IRPrinter.prettyPrint(code._stop, 9999));
-
- }
+ if( (dump & pass)==0 ) return;
+ if( (dump & DUMP_PASS_NAME)!=0 ) System.err.println("Final:");
+ System.err.println(code);
}
static void print_compilation_times(CodeGen code) {
- double t, total = 0;
-
- total += t = code._tParse / 1e3;
- System.out.printf( "Parsing Time: %.3f sec%n", t);
- total += t = code._tOpto / 1e3;
- System.out.printf( "Optimization Time: %.3f sec%n", t);
- total += t = code._tTypeCheck / 1e3;
- System.out.printf( "Type Checking Time: %.3f sec%n", t);
- total += t = code._tLoopTree / 1e3;
- System.out.printf( "Loop Tree Time: %.3f sec%n", t);
- total += t = code._tInsSel / 1e3;
- System.out.printf( "Code Selection Time: %.3f sec%n", t);
- total += t = code._tGCM / 1e3;
- System.out.printf( "GCM Time: %.3f sec%n", t);
- total += t = code._tLocal / 1e3;
- System.out.printf( "Local Scheduling Time: %.3f sec%n", t);
- total += t = code._tRegAlloc / 1e3;
- System.out.printf( "Register Allocation Time: %.3f sec%n", t);
- total += t = code._tEncode / 1e3;
- System.out.printf( "Encoding Time: %.3f sec%n", t);
- System.out.printf( "TOTAL COMPILATION TIME: %.3f sec%n", total);
+ long total=0;
+ for( Phase phase : Phase.values() ) {
+ long time=code._times[phase.ordinal()];
+ total+=time;
+ System.out.printf("%-24s %.3f sec%n",phase+" Time:",time/1e3);
+ }
+ System.out.printf("TOTAL COMPILATION TIME: %.3f sec%n",total/1e3);
}
static String getInput() throws IOException {
@@ -150,16 +123,17 @@ Options:
switch (arg) {
case "-": input_filename=""; break;
case "--dump": dump |= DUMP_FINAL; break;
- case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
- case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
- case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
- case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
- case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
- case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
- case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
- case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
- case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
- case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
+ case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
+ case "--dump-after-finalize-ir": dump |= DUMP_AFTER_FINALIZE_IR; break;
+ case "--dump-after-iter": dump |= DUMP_AFTER_ITER; break;
+ case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
+ case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
+ case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
+ case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
+ case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
+ case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
+ case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
+ case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
case "-S": dump |= DUMP_DISASSEMBLE; break;
case "--eval": do_eval = true ; do_run = false; break;
case "--run": do_run = true ; break;
@@ -198,38 +172,9 @@ Options:
// Compilation pipeline
CodeGen code = new CodeGen(src);
- code.parse();
- dump(code, dump, DUMP_AFTER_PARSE);
-
- code.opto();
- dump(code, dump, DUMP_AFTER_OPTO);
-
- code.typeCheck();
- dump(code, dump, DUMP_AFTER_TYPE_CHECK);
-
- code.loopTree();
- dump(code, dump, DUMP_AFTER_LOOP_TREE);
-
- if (do_codegen) {
- code.instSelect(cpu, abi, PORTS);
- dump(code, dump, DUMP_AFTER_SELECT);
- }
-
- code.GCM();
- dump(code, dump, DUMP_AFTER_GCM);
-
- code.localSched();
- dump(code, dump, DUMP_AFTER_LOCAL_SCHED);
-
- if (do_codegen) {
- code.regAlloc();
- dump(code, dump, DUMP_AFTER_REG_ALLOC);
-
- code.encode();
- dump(code, dump, DUMP_AFTER_ENCODE);
-
- code.exportELF(base+".o");
- }
+ code.driver(do_codegen ? Phase.Encoding : Phase.LocalSched,
+ do_codegen ? cpu : null, abi, dump);
+ if( do_codegen ) code.exportELF(base+".o");
dump(code, dump, DUMP_FINAL);
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter22/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
new file mode 100644
index 00000000..00d86c63
--- /dev/null
+++ b/chapter22/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
@@ -0,0 +1,145 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.simple.TestC;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+// Force overlapping flags, then force every GPR unavailable across the gap.
+public class FlagSpillTestSupport {
+ private static class Op extends MachConcreteNode {
+ final RegMask _def, _use, _kill;
+ Op(RegMask def,RegMask use,RegMask kill,Node... ins) {
+ super(ins); _def=def; _use=use; _kill=kill; _type=TypeInteger.BOT;
+ }
+ @Override public String op() { return "flagsProbe"; }
+ @Override public RegMask outregmap() { return _def; }
+ @Override public RegMask regmap(int i) { return _use; }
+ @Override public RegMask killmap() { return _kill; }
+ @Override public void encoding(Encoding enc) { throw new AssertionError(); }
+ }
+
+ public static void run() throws Exception {
+ StringBuilder asm = new StringBuilder(".text\n");
+ for( boolean isArm : new boolean[]{false,true} )
+ for( boolean stack : new boolean[]{false,true} ) {
+ CodeGen code = new CodeGen("");
+ code._callingConv="SystemV";
+ code._mach=isArm ? new arm(code) : new x86_64_v2(code);
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ code._cfg.add(block);
+ int flagsReg=isArm ? 64 : 32, firstStack=flagsReg+1;
+ RegMask flags = new RegMask(flagsReg);
+ Op def = new Op(flags,null,null,block);
+ RegMask killMask = stack ? (isArm ? new RegMask(0x7fffffffL,1L) : new RegMask(0x1ffffffefL)) : flags;
+ Op kill = new Op(null,null,killMask,block);
+ new Op(null,flags,null,block,def);
+ RegAlloc alloc = new RegAlloc(code);
+ alloc.regAlloc();
+
+ int splits=0, slots=0;
+ for( Node n : block._outputs ) if( n instanceof SplitNode split ) {
+ int dst=alloc.regnum(n), src=alloc.regnum(n.in(1));
+ assertTrue(split.outregmap().test(dst));
+ assertTrue(split.regmap(1).test(src));
+ for( int fp=isArm ? 32 : 16; fp<flagsReg; fp++ ) {
+ assertFalse("FP output allowed",split.outregmap().test(fp));
+ assertFalse("FP input allowed",split.regmap(1).test(fp));
+ }
+ if( dst>=firstStack || src>=firstStack ) slots++;
+ splits++;
+ }
+ assertTrue(splits>=2);
+ assertEquals(stack,slots!=0);
+
+ code._regAlloc=alloc;
+ FunNode frame = new FunNode(null,null,"flags",(Node)null);
+ Encoding save = new Encoding(code), restore = new Encoding(code);
+ save._fun=restore._fun=frame;
+ Encoding enc = save;
+ for( Node n : block._outputs ) {
+ if( n==kill ) enc=restore;
+ if( n instanceof SplitNode split ) split.encoding(enc);
+ }
+ if( isArm ) {
+ for( int bits=0; bits<16; bits++ ) {
+ EvalArm64 cpu = new EvalArm64(new byte[1024],512);
+ System.arraycopy(save._bits.buf(),0,cpu._buf,0,save._bits.size());
+ System.arraycopy(restore._bits.buf(),0,cpu._buf,save._bits.size(),restore._bits.size());
+ cpu.N=(bits&8)!=0; cpu.Z=(bits&4)!=0; cpu.C=(bits&2)!=0; cpu.V=(bits&1)!=0;
+ assertEquals(0,cpu.step(save._bits.size()/4));
+ cpu.N=cpu.Z=cpu.C=cpu.V=false;
+ if( stack ) for( int r=0; r<31; r++ ) cpu.regs[r]=0;
+ assertEquals(0,cpu.step(restore._bits.size()/4));
+ assertEquals((bits&8)!=0,cpu.N); assertEquals((bits&4)!=0,cpu.Z);
+ assertEquals((bits&2)!=0,cpu.C); assertEquals((bits&1)!=0,cpu.V);
+ assertEquals(512,cpu.regs[31]);
+ }
+ } else {
+ Encoding body = new Encoding(code);
+ int[] preserved={3,5,6,7,12,13,14,15};
+ for( int r : preserved ) pushPop(body,r,false);
+ X86.imm(body,0x81,5,4,16); // Reserve spill storage.
+ X86.flags(body,TestC.OS.startsWith("Windows") ? 1 : 7,true);
+ bytes(body,save);
+ if( stack ) {
+ for( int r=0; r<16; r++ ) if( r!=4 ) X86.constant(body,r,0);
+ } else X86.reg(body,0x39,0,0); // CMP rax,rax clobbers only flags.
+ bytes(body,restore);
+ X86.flags(body,0,false); // Return the restored flags.
+ X86.imm(body,0x81,0,4,16);
+ for( int i=preserved.length-1; i>=0; i-- ) pushPop(body,preserved[i],true);
+ X86.ret(body);
+ String name=stack ? "flags_stack" : "flags_reg";
+ asm.append(".globl ").append(name).append('\n').append(name).append(":\n.byte ");
+ for( int i=0; i<body._bits.size(); i++ ) {
+ if( i!=0 ) asm.append(',');
+ asm.append(body._bits.buf()[i]&255);
+ }
+ asm.append('\n');
+ }
+ }
+ Path dir=Files.createTempDirectory(Path.of("build/objs"),"flag_spills_");
+ Files.writeString(dir.resolve("flags.s"),asm);
+ Files.writeString(dir.resolve("flags.c"),"""
+ typedef unsigned long long u64;
+ u64 __attribute__((CALL_CONV)) flags_reg(u64);
+ u64 __attribute__((CALL_CONV)) flags_stack(u64);
+ int main(void) {
+ const int positions[]={0,2,4,6,7,11};
+ for(int bits=0; bits<64; bits++) {
+ u64 flags=0x202;
+ for(int i=0;i<6;i++) if(bits&(1<<i)) flags|=1ULL<<positions[i];
+ if((flags_reg(flags)&0x8d5)!=(flags&0x8d5)) return 1;
+ if((flags_stack(flags)&0x8d5)!=(flags&0x8d5)) return 2;
+ }
+ return 0;
+ }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "flags.exe" : "flags").toString();
+ exec("gcc","-DCALL_CONV="+(TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"),
+ "-o",exe,dir.resolve("flags.c").toString(),dir.resolve("flags.s").toString());
+ exec(exe);
+ }
+
+ private static void exec(String... command) throws Exception {
+ Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
+ String output = new String(process.getInputStream().readAllBytes(),java.nio.charset.StandardCharsets.UTF_8);
+ assertEquals(String.join(" ",command)+"\n"+output,0,process.waitFor());
+ }
+
+ private static void bytes(Encoding dst,Encoding src) {
+ for( int i=0; i<src._bits.size(); i++ ) dst.add1(src._bits.buf()[i]&255);
+ }
+ private static void pushPop(Encoding enc,int reg,boolean pop) {
+ if( reg>=8 ) enc.add1(0x41);
+ enc.add1((pop ? 0x58 : 0x50)+(reg&7));
+ }
+}
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter22/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 2d9860a6..0895a4fc 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter22/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -22,6 +22,11 @@ public class RegAllocTestSupport {
@Override public Node copy() { return new Op(_def,_use,_kill,_clone,(Node)null); }
@Override public void encoding(Encoding enc) { throw new AssertionError(); }
}
+ private static SplitNode testSplit(CodeGen code, MachNode def) {
+ LRG lrg = new LRG((short)0);
+ lrg._machDef = def;
+ return code._mach.split("test",(byte)0,lrg);
+ }
private static CodeGen graph() {
CodeGen code = new CodeGen("");
code._mach = new x86_64_v2(code);
@@ -178,11 +183,11 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode hi = code._mach.split("test",(byte)0,null);
+ SplitNode hi = testSplit(code,def);
hi.setDef(0,code._start); hi.setDef(1,def);
alloc.newLRG(hi)._reg=1;
new Op(null,null,killed ? A : null,false,code._start);
- SplitNode lo = code._mach.split("test",(byte)0,null);
+ SplitNode lo = testSplit(code,hi);
lo.setDef(0,code._start); lo.setDef(1,hi);
alloc.newLRG(lo)._reg=0;
Op use = new Op(null,A,null,false,code._start,lo);
@@ -196,7 +201,7 @@ public class RegAllocTestSupport {
uncolored.newLRG(def0)._mask=A;
Op fixed = new Op(A,null,null,false,pre._start);
uncolored.newLRG(fixed)._mask=A;
- SplitNode copy = pre._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(pre,def0);
copy.setDef(0,pre._start); copy.setDef(1,def0);
assertFalse("A fixed definition clobbers even before coloring",uncolored.sameBlockNoClobber(copy));
}
@@ -209,7 +214,7 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
alloc.newLRG(copy)._reg=1;
if( kind==3 ) new Op(null,null,A,false,code._start);
@@ -232,7 +237,7 @@ public class RegAllocTestSupport {
CodeGen code = graph();
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(new RegMask(3L),null,null,false,code._start);
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
Op use = new Op(null,new RegMask(3L),null,false,code._start,copy);
LRG from = alloc.newLRG(def), to = alloc.newLRG(copy);
diff --git a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index 586924b2..08e19fd4 100644
--- a/chapter22/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter22/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().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/chapter23/README.md b/chapter23/README.md
index 85a5b201..c99d2441 100644
--- a/chapter23/README.md
+++ b/chapter23/README.md
@@ -376,6 +376,14 @@ will be cycle-equals (all other things being equals).
+## Finalizing field layouts
+
+`FinalizeIR` now also settles field sizes and layouts, replaces symbolic field
+offsets by constants, and runs peepholes exposed by those constants. Recursive
+type construction must finish before layout is queried. The phase follows
+`Iter` and precedes `TypeCheck`; the driver includes it when compiling through
+any later stage.
+
## Methods and Final Fields and Classes
Methods are simply function values (final fields) declared inside a struct that
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index 9bf1aa61..e332146f 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -21,19 +21,63 @@ public class CodeGen {
public static CodeGen CODE;
public enum Phase {
- Parse, // Parse ASCII text into Sea-of-Nodes IR
- Opto, // Run ideal optimizations
- TypeCheck, // Last check for bad programs
- LoopTree, // Build a loop tree; break infinite loops
- Select, // Convert to target hardware nodes
- Schedule, // Global schedule (code motion) nodes
- LocalSched, // Local schedule
- RegAlloc, // Register allocation
- Encoding, // Encoding
- Export, // Export
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ FinalizeIR, // Finish function cleanup and layouts
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ Select, // Select machine instructions
+ Schedule, // Global code motion
+ LocalSched, // Order instructions within blocks
+ RegAlloc, // Assign registers and stack slots
+ Encoding, // Encode instructions
+ Export; // Link in memory or write ELF
+
}
public Phase _phase;
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) { return driver(target,null,null); }
+ public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); }
+ public CodeGen driver(Phase target, String cpu, String callingConv, int dump) {
+ if( target.ordinal()>=Phase.RegAlloc.ordinal() && cpu==null && _mach==null )
+ throw new IllegalArgumentException("Machine code requires a CPU target");
+ if( target==Phase.Export ) throw new IllegalArgumentException("Use exportELF with an output file or null for memory linking");
+ step(target, Phase.Parse, this::parse, dump);
+ step(target, Phase.Iter, this::iter, dump);
+ step(target, Phase.FinalizeIR, this::finalizeIR, dump);
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ step(target, Phase.Select, () -> instSelect(cpu,callingConv), dump);
+ step(target, Phase.Schedule, this::GCM, dump);
+ step(target, Phase.LocalSched, this::localSched, dump);
+ step(target, Phase.RegAlloc, this::regAlloc, dump);
+ step(target, Phase.Encoding, this::encode, dump);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) {
+ if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":");
+ System.err.println(this);
+ }
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
// ---------------------------
// Compilation source code
public final String _src;
@@ -59,22 +103,6 @@ public class CodeGen {
}
- // All passes up to Phase, except ELF
- public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
- public CodeGen driver( Phase phase, String cpu, String callingConv ) {
- if( _phase==null ) parse();
- int p1 = phase.ordinal();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Opto .ordinal() ) opto();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.TypeCheck .ordinal() ) typeCheck();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LoopTree .ordinal() ) loopTree();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Select .ordinal() && cpu != null ) instSelect(cpu,callingConv);
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Schedule .ordinal() ) GCM();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LocalSched.ordinal() ) localSched();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.RegAlloc .ordinal() ) regAlloc();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Encoding .ordinal() ) encode();
- return this;
- }
-
// Run all the phases through final ELF emission
public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException {
return driver(Phase.Encoding,cpu,callingConv).exportELF(obj);
@@ -147,16 +175,10 @@ public class CodeGen {
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
- public int _tParse;
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- long t0 = System.currentTimeMillis();
-
- P.parse();
- _tParse = (int)(System.currentTimeMillis() - t0);
- if( _obs != null ) _obs.phase("Parse");
- return this;
+ return phase(Phase.Parse, _phase==null, () -> {
+ P.parse();
+ });
}
@@ -171,100 +193,25 @@ public class CodeGen {
public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
- // Run ideal optimizations
- public int _tOpto;
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
- long t0 = System.currentTimeMillis();
-
- // Pessimistic peephole optimization on a worklist
- _iter.iterate(this);
-
- // OPTIMISTIC PASS GOES HERE
-
- // Not really a true optimistic pass, but look for unlinked functions.
- // This can be removed, which may trigger another round of pessimistic.
- // This is the point where we flip from a virtual Call Graph (any call
- // can call any function) to having a correct (but conservative) CG.
- int progress = 0;
- while( progress != _start.nOuts() ) {
- progress = _start.nOuts();
- FunNode main = link(_main);
- for( int i=0; i<_start.nOuts(); i++ ) {
- Node use = _start.out(i);
- if( use instanceof FunNode fun &&
- fun.nIns()==2 && fun.in(1)==_start && fun != main && !fun.hasPtr() &&
- (fun._name==null || fun._name.startsWith("sys.")) ) {
- add(fun).setDef(1,Parser.XCTRL);
- addAll(fun._outputs);
- i--;
- }
- }
- _iter.iterate(this);
- }
-
- // Freeze field sizes; do struct layouts; convert field offsets into
- // constants.
- for( int i=0; i<_start.nOuts(); i++ ) {
- Node use = _start.out(i);
- if( use instanceof ConFldOffNode off ) {
- TypeMemPtr tmp = (TypeMemPtr) Parser.TYPES.get(off._name);
- off.subsume( off.asOffset(tmp._obj) );
- i--;
- }
- }
- _iter.iterate(this);
-
- // To help with testing, sort StopNode returns by NID
- Arrays.sort(_stop._inputs._es,0,_stop.nIns(),(x,y) -> x._nid - y._nid );
-
- _tOpto = (int)(System.currentTimeMillis() - t0);
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); }
- // TODO:
- // Optimistic
- // TODO:
- // loop unroll, peel, RCE, etc
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
+ public CodeGen finalizeIR() { return phase(Phase.FinalizeIR, _phase==Phase.Iter, () -> FinalizeIR.run(this)); }
public <N extends Node> N add( N n ) { return _iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
// ---------------------------
// Last check for bad programs
- public int _tTypeCheck;
- 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();
- _phase = Phase.TypeCheck;
- long t0 = System.currentTimeMillis();
-
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
- });
- _tTypeCheck = (int)(System.currentTimeMillis() - t0);
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.FinalizeIR, () -> TypeCheck.run(this)); }
// ---------------------------
// Build the loop tree; break never-exit loops
- public int _tLoopTree;
public CodeGen loopTree() {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.LoopTree;
- long t0 = System.currentTimeMillis();
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_start,_stop);
- _tLoopTree = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> {
+ // Build the loop tree, fix never-exit loops
+ _start.buildLoopTree(_start,_stop);
+ });
}
// ---------------------------
@@ -286,138 +233,41 @@ public class CodeGen {
public RegMask _rpcMask;
// Convert to target hardware nodes
- public int _tInsSel;
public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
public CodeGen instSelect( String cpu, String callingConv, String base ) {
- assert _phase.ordinal() == Phase.LoopTree.ordinal();
- _phase = Phase.Select;
-
- _callingConv = callingConv;
-
- // Not timing the class load...
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- // Build global copies of common register masks.
- long callerSave = _mach.callerSave();
- long neverSave = _mach. neverSave();
- int maxReg = Math.min(64,_mach.regs().length);
- assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
- _callerSave = new RegMask(callerSave);
-
- // Build a Return RegMask array. All returns have the following inputs:
- // 0 - ctrl
- // 1 - memory
- // 2 - varies with returning GPR,FPR
- // 3 - RPC
- // 4+- Caller save registers
- _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
- for( int reg=0, i=4; reg<maxReg; reg++ )
- if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
- _retMasks[i++] = new RegMask(reg);
- _rpcMask = new RegMask(_mach.rpc());
- _retMasks[3] = _rpcMask;
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = ( StopNode)map.get(_stop );
- StartNode start = (StartNode)map.get(_start);
- _start = start==null ? new StartNode(_start) : start;
- _instOuts(_stop,visit());
- _visit.clear();
- _tInsSel = (int)(System.currentTimeMillis() - t0);
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachNode already, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachNode ) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- map.put(n, x=_mach.instSelect(n) );
- // Stop retains the callable body. Its lifetime edge is not a machine operand.
- if( n instanceof FunPtrNode ) x._inputs.pop();
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- if( x instanceof MachNode mach )
- mach.postSelect(this); // Post selection action
-
- return x;
- }
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- _instOuts(in,visit);
- }
+ if( cpu==null ) return this;
+ return phase(Phase.Select, _phase==Phase.LoopTree,
+ () -> new InstSelect(this).run(cpu,callingConv,base));
}
// ---------------------------
// Control Flow Graph in Reverse Post Order.
- public int _tGCM;
public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.Select.ordinal();
- _phase = Phase.Schedule;
- long t0 = System.currentTimeMillis();
+ return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.Select), () -> {
+ GlobalCodeMotion.buildCFG(this);
- GlobalCodeMotion.buildCFG(this);
- _tGCM = (int)(System.currentTimeMillis() - t0);
-
- return this;
+ });
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- public int _tLocal;
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- long t0 = System.currentTimeMillis();
- ListScheduler.sched(this);
- _tLocal = (int)(System.currentTimeMillis() - t0);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// ---------------------------
// Register Allocation
- public int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
- assert _phase == Phase.LocalSched;
- _phase = Phase.RegAlloc;
- long t0 = System.currentTimeMillis();
- _regAlloc = new RegAlloc(this);
- _regAlloc.regAlloc();
- _tRegAlloc = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> {
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ });
}
// Human readable register name
@@ -433,27 +283,27 @@ public class CodeGen {
// ---------------------------
// Encoding
- public int _tEncode;
public Encoding _encoding; // Encoding object
public void preEncode() { } // overridden by alternative ports
public CodeGen encode() {
- assert _phase == Phase.RegAlloc;
- _phase = Phase.Encoding;
- long t0 = System.currentTimeMillis();
- _encoding = new Encoding(this);
- preEncode();
- _encoding.encode();
- _tEncode = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.Encoding, _phase==Phase.RegAlloc, () -> {
+ _encoding = new Encoding(this);
+ preEncode();
+ _encoding.encode();
+ });
}
// ---------------------------
// Exporting to external formats
public CodeGen exportELF(String fname) throws IOException {
- assert _phase == Phase.Encoding;
- _phase = Phase.Export;
- if( fname == null ) new LinkMem(this).link(); // In memory patching
- else new ElfFile(this).export(fname); // External ELF file
+ assert _phase==Phase.Encoding;
+ _phase=Phase.Export;
+ long t0=System.currentTimeMillis();
+ try {
+ if( fname == null ) new LinkMem(this).link(); // In memory patching
+ else new ElfFile(this).export(fname); // External ELF file
+ } finally { _times[Phase.Export.ordinal()]=System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(Phase.Export.name());
return this;
}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
new file mode 100644
index 00000000..55553393
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
@@ -0,0 +1,48 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.util.Arrays;
+
+// Finish the ideal graph before type checking and scheduling.
+abstract class FinalizeIR {
+ static void run(CodeGen code) {
+ // Not really a true optimistic pass, but look for unlinked functions.
+ // This can be removed, which may trigger another round of pessimistic.
+ // This is the point where we flip from a virtual Call Graph (any call
+ // can call any function) to having a correct (but conservative) CG.
+ int progress = 0;
+ while( progress != code._start.nOuts() ) {
+ progress = code._start.nOuts();
+ FunNode main = code.link(code._main);
+ for( int i=0; i<code._start.nOuts(); i++ ) {
+ Node use = code._start.out(i);
+ if( use instanceof FunNode fun &&
+ fun.nIns()==2 && fun.in(1)==code._start && fun != main && !fun.hasPtr() &&
+ (fun._name==null || fun._name.startsWith("sys.")) ) {
+ code.add(fun).setDef(1,Parser.XCTRL);
+ code.addAll(fun._outputs);
+ i--;
+ }
+ }
+ code._iter.iterate(code);
+ }
+
+ // Freeze field sizes; do struct layouts; convert field offsets into
+ // constants.
+ for( int i=0; i<code._start.nOuts(); i++ ) {
+ Node use = code._start.out(i);
+ if( use instanceof ConFldOffNode off ) {
+ TypeMemPtr tmp = (TypeMemPtr) Parser.TYPES.get(off._name);
+ off.subsume( off.asOffset(tmp._obj) );
+ i--;
+ }
+ }
+ code._iter.iterate(code);
+
+ // To help with testing, sort StopNode returns by NID
+ Arrays.sort(code._stop._inputs._es,0,code._stop.nIns(),(x,y) -> x._nid - y._nid );
+
+ }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 80d5d9bb..da45db4f 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -273,10 +273,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -293,7 +294,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// A conditional writer can precede the load without dominating its late block.
// Walk back to the first overlap with the load's placement range.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
new file mode 100644
index 00000000..e8e87785
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ InstSelect(CodeGen code) { _code=code; }
+
+ @SuppressWarnings("unchecked")
+ void run(String cpu, String callingConv, String base) {
+ _code._callingConv = callingConv;
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = base+"."+cpu+"."+cpu;
+ try { _code._mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(_code); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Build global copies of common register masks.
+ long callerSave = _code._mach.callerSave();
+ long neverSave = _code._mach. neverSave();
+ int maxReg = Math.min(64,_code._mach.regs().length);
+ assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+ _code._callerSave = new RegMask(callerSave);
+
+ // Build a Return RegMask array. All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ _code._retMasks = new RegMask[(maxReg-_code._callerSave.size()-Long.bitCount( neverSave ))+4];
+ for( int reg=0, i=4; reg<maxReg; reg++ )
+ if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+ _code._retMasks[i++] = new RegMask(reg);
+ _code._rpcMask = new RegMask(_code._mach.rpc());
+ _code._retMasks[3] = _code._rpcMask;
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = ( StopNode)map.get(_code._stop );
+ StartNode start = (StartNode)map.get(_code._start);
+ _code._start = start==null ? new StartNode(_code._start) : start;
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachNode already, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachNode ) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Stop retains the callable body. Its lifetime edge is not a machine operand.
+ if( n instanceof FunPtrNode ) x._inputs.pop();
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(_code); // Post selection action
+
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index 009ddcdb..0c36ae2b 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -58,6 +58,8 @@ public class LRG {
public int nadj() { return _adj==null ? 0 : _adj._len; }
+ public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 6518f3b3..51ca3a1a 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -416,7 +416,7 @@ public class RegAlloc {
// If the minLoopDepth is less than the maxLoopDepth: for-all defs and
// uses, if at minLoopDepth or lower, split after def and before use.
for( Node n : _ns ) {
- if( n instanceof SplitNode && min!=max ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n instanceof SplitNode split && !split.isNonStackSplit() && min!=max ) continue; // Ordinary splits already reach stack storage.
if( n.isDead() ) continue; // Some Cloneable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
@@ -469,8 +469,8 @@ public class RegAlloc {
}
private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
+ // Ordinary splits already reach stack storage; register-only transfers do not.
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld;
// Collect min/max loop depth
int min = (int)ld;
int max = (int)(ld>>32);
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter23/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
new file mode 100644
index 00000000..0997fd2f
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter23/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index 211489fb..a7fe6c5f 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -10,6 +10,8 @@ public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
public final byte _round;
public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
+ // A register-only transfer may need another split to reach stack storage.
+ public boolean isNonStackSplit() { return false; }
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
new file mode 100644
index 00000000..3e045a6f
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+
+// NZCV transfers require a GPR. Further splits can spill that GPR to the stack.
+public class SplitFlagsARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,1L);
+ SplitFlagsARM(String kind,byte round) { super(kind,round); }
+ @Override public boolean isNonStackSplit() { return true; }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( src==arm.FLAGS ) enc.add4(Arm64.flags(dst,false));
+ else if( dst==arm.FLAGS ) enc.add4(Arm64.flags(src,true));
+ else super.encoding(enc);
+ }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
new file mode 100644
index 00000000..46eff4ca
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
@@ -0,0 +1,11 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Keep a saved NZCV value in GPRs and stack slots through subsequent spills.
+public class SplitGPRARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L);
+ SplitGPRARM(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 9faf1d40..b4609419 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -269,7 +269,16 @@ public class arm extends Machine {
@Override public int rpc() { return X30; }
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask();
+ if( (def!=null && def.size1() && def.test(FLAGS)) ||
+ (use!=null && use.size1() && use.test(FLAGS)) )
+ return new SplitFlagsARM(kind,round);
+ // Spilling the saved flags requires a GPR between NZCV and the stack.
+ if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM )
+ return new SplitGPRARM(kind,round);
+ return new SplitARM(kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
new file mode 100644
index 00000000..4650b535
--- /dev/null
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+
+// Flag snapshots can use GPRs or stack slots, but never XMM registers.
+public class SplitFlagsX86 extends SplitX86 {
+ private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L<<x86_64_v2.FLAGS) | x86_64_v2.SPILLS,-1L);
+ SplitFlagsX86(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( dst==x86_64_v2.FLAGS || src==x86_64_v2.FLAGS ) {
+ boolean toFlags = dst==x86_64_v2.FLAGS;
+ int reg = toFlags ? src : dst;
+ if( reg>=x86_64_v2.MAX_REG )
+ X86.flagsStack(enc,enc._fun.computeStackOffset(enc._code,reg),toFlags);
+ else
+ X86.flags(enc,reg,toFlags);
+ } else super.encoding(enc);
+ }
+}
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
index e8281975..4e9eda7d 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -17,12 +17,10 @@ public class SplitX86 extends SplitNode {
@Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
- // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
- // flags->flags.
+ // Ordinary GPR, XMM, and stack moves. Flags use SplitFlagsX86.
@Override public void encoding( Encoding enc ) {
short dst=enc.reg(this), src=enc.reg(in(1));
- if( dst==x86_64_v2.FLAGS ) { X86.flags(enc,src,true); return; }
- if( src==x86_64_v2.FLAGS ) { X86.flags(enc,dst,false); return; }
+ assert dst!=x86_64_v2.FLAGS && src!=x86_64_v2.FLAGS;
boolean dstX=dst>=x86_64_v2.XMM_OFFSET, srcX=src>=x86_64_v2.XMM_OFFSET;
// Stack spills
if( dst >= x86_64_v2.MAX_REG ) {
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index e283bc8f..fcae17f8 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -47,7 +47,7 @@ public class x86_64_v2 extends Machine {
static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<<RAX) & ~(1L<<RDX));
static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | SPILLS, -1L );
// Load/store mask; both GPR and FPR
static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -238,7 +238,9 @@ public class x86_64_v2 extends Machine {
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(String kind, byte round, LRG lrg) {
- return new SplitX86(kind, round);
+ return lrg._machDef!=null && lrg._machDef.outregmap().test(FLAGS)
+ ? new SplitFlagsX86(kind,round)
+ : new SplitX86(kind,round);
}
// Return a MachNode unconditional branch
diff --git a/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index 8877b310..fb87ec75 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter23/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.driver(CodeGen.Phase.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/chapter23/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter23/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index f065126a..0077ee31 100644
--- a/chapter23/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter23/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -429,7 +429,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/chapter23/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 6ff4ac95..8168594a 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -20,7 +20,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().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().finalizeIR();
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -145,7 +145,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 1ff875b4..d817a015 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter23/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().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().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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), 123L, true).parse().opto();
+""", TypeInteger.constant(2), 123L, true).parse().iter().finalizeIR();
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L, true).parse().opto();
+""", TypeInteger.constant(1), 123L, true).parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,3,5,4);", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1), 123L, true);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3), 123L, true);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1), 123L, true);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0), 123L, true);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,6,1,5);", code.print());
}
@@ -278,7 +278,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1), 123L, true);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2), 123L, true);
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 5;", code.print());
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 88554323..0ebc427c 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -34,7 +34,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().finalizeIR();
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -122,7 +122,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -180,7 +180,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 09669cc7..543b91d1 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -22,7 +22,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 54a14a25..3a4415d9 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -128,14 +128,14 @@ while(v1+arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -148,7 +148,7 @@ if(0) while(0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
}
@@ -181,7 +181,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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 0;");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -222,7 +222,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -244,7 +244,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -273,14 +273,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 0f4ca169..78265bb2 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -14,7 +14,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).driver(CodeGen.Phase.TypeCheck);
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -40,7 +40,7 @@ public class Chapter10Test {
"int b = !!p; if (b) return p.x + b - 1; return -1;",
"if (!(p == null || arg == 0)) return p.x; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -70,7 +70,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -102,7 +102,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return 2;", code.print());
}
@@ -138,7 +138,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Vector2D;", code.print());
}
@@ -176,7 +176,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -189,7 +189,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar", e.getMessage()); }
}
@@ -228,7 +228,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -244,7 +244,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.parse().iter().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.driver(CodeGen.Phase.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().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -317,7 +317,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -335,7 +335,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -352,7 +352,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -380,7 +380,7 @@ if(0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -394,7 +394,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return s0;", code.print());
}
@@ -410,7 +410,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
}
@Test
@@ -422,7 +422,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (const)s0;", code.print());
}
@@ -455,7 +455,7 @@ while(0) {}
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -466,7 +466,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -480,7 +480,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -506,7 +506,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).parse().iter().finalizeIR();
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -522,7 +522,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -539,7 +539,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -554,7 +554,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -572,7 +572,7 @@ return 0;
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().finalizeIR();
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 3d5d2686..43a780ec 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -13,7 +13,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -32,7 +32,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).parse().iter().finalizeIR();
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -54,7 +54,7 @@ public class Chapter11Test {
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed,false).parse();
- code.opto();
+ code.iter().finalizeIR();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index ac3b81ec..4026ea8b 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -14,7 +14,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -34,7 +34,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Region,0,1,.q);", 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();
+ code.parse().iter().finalizeIR();
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().finalizeIR();
assertEquals("return null;", code.print());
}
@@ -100,7 +100,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (const)M;", code.print());
}
@@ -113,7 +113,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; };
return n.next;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *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.driver(CodeGen.Phase.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(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot '//' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
}
@@ -150,7 +150,7 @@ return n.i;
struct S{};
return 0;
""");
- code.parse().opto();
+ code.parse().iter().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(); fail(); }
+ try { code.parse().iter().finalizeIR(); 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().finalizeIR();
assertEquals("return 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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.LocalSched);
assertEquals("return B;", code._stop.toString());
assertEquals("B{f0=C},f1=A{f0=$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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N},b=V},c=H}},b=K{a=$cyclic,b=D{a=O},b=W},c=I}},c=P}},c=$cyclic}", Eval2.eval(code, 0));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 63f1d12c..6451ad1d 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -290,8 +290,11 @@ else {
}
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 9;", code.print());
+ code.driver(CodeGen.Phase.Schedule);
+ // LoopTree connects the nonreturning paths using synthetic Never exits.
+ assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print());
}
@@ -322,7 +325,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.parse().iter().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
@@ -344,7 +347,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -367,7 +370,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -386,7 +389,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return S;", code.print());
}
@@ -404,7 +407,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return arg;", code.print());
}
@@ -421,7 +424,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return S;", code.print());
}
@@ -436,7 +439,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -454,7 +457,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -474,7 +477,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -492,7 +495,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return S;", code.print());
}
@@ -511,7 +514,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
//assertEquals("return new S;", code.print());
}
@@ -528,7 +531,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Top;", code.print());
}
@@ -546,7 +549,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 6377bc14..51397e77 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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]).driver(CodeGen.Phase.TypeCheck);
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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;").driver(CodeGen.Phase.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));
@@ -42,7 +42,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).driver(CodeGen.Phase.TypeCheck);
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index c342eeed..95a5ee81 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().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().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().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();
+ code.parse().iter().finalizeIR();
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().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().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().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().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.driver(CodeGen.Phase.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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index d0a42ccf..5331809f 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -17,7 +17,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return C;", code.print());
assertEquals("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().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().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().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().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().finalizeIR();
assertEquals("return []*A?;", code.print());
assertEquals("*A {i64 !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -131,7 +131,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.parse().iter().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.driver(CodeGen.Phase.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();
+ code.parse().iter().finalizeIR();
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree {*[]*Tree?? !_kids; }?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -186,7 +186,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.parse().iter().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();
+ code.parse().iter().finalizeIR();
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().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().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().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.driver(CodeGen.Phase.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(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 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().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().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().finalizeIR();
assertEquals("return 0;", code.print());
}
@@ -353,7 +353,7 @@ else {
}
return 0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index f73cbf57..67962acd 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -15,7 +15,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.parse().iter().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().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().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().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -64,7 +64,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().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().finalizeIR();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -116,7 +116,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -141,7 +141,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -165,7 +165,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().iter().finalizeIR();
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 8597b799..ba5bb985 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter23/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().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; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -40,7 +40,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -53,7 +53,7 @@ struct Point { int x=3, y=4; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -73,7 +73,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).parse().iter().finalizeIR(); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -86,7 +86,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -98,7 +98,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 24dc0229..28a10b05 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -15,7 +15,7 @@ public class Chapter17bTest {
"""
return 1;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.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().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().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().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().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().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().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().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().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().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().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.driver(CodeGen.Phase.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().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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().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().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().finalizeIR();
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.parse().iter().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.driver(CodeGen.Phase.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;}; ~S s = new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().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().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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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().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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -264,7 +264,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -291,7 +291,7 @@ val x = 1;
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -320,7 +320,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -331,7 +331,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -343,7 +343,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -356,7 +356,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -370,7 +370,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=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().finalizeIR();
assertEquals("return Phi(Region,.x,0,.x);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -383,7 +383,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().finalizeIR();
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -395,7 +395,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -408,7 +408,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -421,7 +421,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -436,7 +436,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -449,7 +449,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().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));
@@ -462,7 +462,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -470,7 +470,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -486,7 +486,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -500,7 +500,7 @@ struct A {
};
return new A{array_of_b = new !B?[0]; }.array_of_b;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return []*B?;", code.print());
assertEquals("*B {... }?[]", Eval2.eval(code,0));
}
@@ -519,7 +519,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -550,7 +550,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return []i64;", code.print());
assertEquals("i64[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 902344ed..11bdfb9a 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter23/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]).driver(CodeGen.Phase.TypeCheck);
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -140,7 +140,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
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().finalizeIR();
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -170,7 +170,7 @@ return x2;
x*x;
};
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return (Parm_x(sq,i64)*x);", code._stop.toString());
//assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -197,7 +197,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -209,7 +209,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -222,7 +222,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ return #2; return (Parm_x($fun21,i64,3)*x); return (Parm_x($fun22,i64,3)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -232,7 +232,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.driver(CodeGen.Phase.TypeCheck);
assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,i64,arg,(x-1))*#2)); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -249,7 +249,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -264,7 +264,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("Stop[ return 1; return 1; return 2; ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.parse().iter().finalizeIR(); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -358,7 +358,7 @@ for(;;) {
}
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { i64 -> i64 #21}?",e.getMessage()); }
}
@@ -370,7 +370,7 @@ return 0;
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -388,7 +388,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -401,7 +401,7 @@ val g = { ->
};
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -415,7 +415,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();
+ code.parse().iter().finalizeIR();
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -437,7 +437,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -489,7 +489,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).parse().iter().finalizeIR();
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -518,7 +518,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).parse().iter().finalizeIR();
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -536,7 +536,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).parse().iter().finalizeIR();
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 23b45e46..70fd3ade 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -39,7 +39,7 @@ public class Chapter19Test {
CodeGen code = new CodeGen("""
return 0;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -48,7 +48,7 @@ return 0;
@Test
public void testString() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
- CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString());
//assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@@ -358,11 +358,11 @@ return sq(arg) + sq(3);
for( String cpu : new String[]{"x86_64_v2","riscv","arm"} )
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
var aliases = memoryPhis(code._stop);
- code.loopTree().instSelect(cpu,"SystemV");
+ code.loopTree().driver(CodeGen.Phase.Select,cpu,"SystemV");
assertEquals(aliases,memoryPhis(code._stop));
- code.GCM().localSched();
+ code.driver(CodeGen.Phase.Schedule).localSched();
code._stop.walk(n -> {
if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
assertTrue(n.in(0) instanceof CFGNode);
@@ -414,7 +414,7 @@ return sq(arg) + sq(3);
int before = p.x + arg;
a.x=33; b.x=44;
return before;
- """).parse().opto().typeCheck().loopTree().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ """).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
var read = (MemOpNode)code._stop.walk(n ->
n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
assertNotNull(read);
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 08a23ef0..cf2b175d 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -11,6 +11,10 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter20Test {
+ @Test public void testFlagSpills() throws Exception {
+ com.seaofnodes.simple.codegen.FlagSpillTestSupport.run();
+ }
+
@Test public void testAllocatorTopPhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.topPhi(); }
@Test public void testNeverReturnAllocation() {
@@ -71,7 +75,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,com.seaofnodes.simple.type.TypeInteger.BOT,seed,true).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck);
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -96,7 +100,7 @@ return f(s);
@Test
public void testJig() {
CodeGen code = new CodeGen("return 0;");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -409,8 +413,7 @@ return a;
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY,
"int[] !a = new int[3]; a[arg]=arg; return a[0]+1;"} ) {
- var code = new CodeGen(src).parse().opto().typeCheck().loopTree()
- .instSelect(cpu,"SystemV").GCM().localSched().regAlloc();
+ var code = new CodeGen(src).driver(CodeGen.Phase.RegAlloc,cpu,"SystemV");
com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
code._stop.walk(n -> {
// Whole memory, precise Phis, and memory projections need no register.
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 30d22fc9..f25a881a 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -29,7 +29,7 @@ public class Chapter21Test {
else if(i==4) b.x=40;
}
return sum;
- """).parse().opto();
+ """).parse().iter().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));
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index ea210e3f..351fd571 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -126,7 +126,7 @@ public class Chapter22Test {
else assertEquals(42,cpu.regs[0]);
}
}
- CodeGen unused = new CodeGen("({->42;}); return 0;").parse().opto();
+ CodeGen unused = new CodeGen("({->42;}); return 0;").parse().iter().finalizeIR();
assertEquals(1,unused._stop.nIns());
}
@@ -172,7 +172,7 @@ public class Chapter22Test {
@Test public void testSubZeroTypeError() {
try {
- new CodeGen("return null-0;").parse().opto().typeCheck();
+ new CodeGen("return null-0;").driver(CodeGen.Phase.TypeCheck);
fail("Subtraction must reject null even when the other operand is zero");
} catch( Parser.ParseException e ) {
assertEquals("Cannot '-' null",e.getMessage());
@@ -344,7 +344,7 @@ bb.c = cc;
val x = 5; // aa.az; // Error to self-define forward ref
return cc.cz;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 25;", code._stop.toString());
assertEquals("25", Eval2.eval(code, 0));
}
@@ -384,7 +384,7 @@ val sum = { i32[~] is -> // final array
};
return sum(is);
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code._stop.toString());
assertEquals("14", Eval2.eval(code, 0));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java
index 0feaa5de..e27434c2 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -154,7 +154,7 @@ struct S { !S? !fld; };
val ptr = arg == 1 ? null : new S{fld = arg==1 ? null : new S{fld = null;};};
return ptr && ptr.fld ? "true" : "false";
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("[97-117][ 116,114,117,101]", Eval2.eval(code, 0));
// Evaluate on RISC5 emulator
@@ -257,7 +257,7 @@ struct Scan {
return Scan.peek(s,'q');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Region,0,1);", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "Win64" ,23,null);
@@ -282,7 +282,7 @@ struct Scan {
return s.skip().x;
""";
- try { new CodeGen(src).parse().opto().typeCheck(); fail(); }
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa")); }
};
@@ -309,7 +309,7 @@ struct Scan {
return s.require('[');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return (.[]==91);", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "Win64" ,4,null);
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Compile.java b/chapter23/src/test/java/com/seaofnodes/simple/Compile.java
index 088055f9..f3ca1905 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Compile.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Compile.java
@@ -16,7 +16,7 @@ public class Compile {
private static Process run(String simple, String c) throws IOException, InterruptedException {
boolean USE_WSL = System.getProperty("os.name").startsWith("Windows");
CodeGen code = new CodeGen(simple);
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode();
+ code.driver(CodeGen.Phase.Encoding,"x86_64_v2","SystemV");
if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main";
code.exportELF("test.o");
var params = new ArrayList<String>();
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter23/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 48f739cf..e2d822d0 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter23/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).driver(CodeGen.Phase.TypeCheck);
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/LiteralTest.java b/chapter23/src/test/java/com/seaofnodes/simple/LiteralTest.java
index 09ee30f5..447ef46e 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/LiteralTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/LiteralTest.java
@@ -20,7 +20,7 @@ public class LiteralTest {
}
private static void assertString(String text, byte[] expected) {
- var code = new CodeGen("return \"" + text + "\";").parse().opto().typeCheck();
+ var code = new CodeGen("return \"" + text + "\";").driver(CodeGen.Phase.TypeCheck);
TypeMemPtr ptr = (TypeMemPtr)code.expr()._type;
TypeConAryB bytes = (TypeConAryB)ptr._obj.field("[]")._t;
assertArrayEquals(text, expected, bytes._ary);
@@ -33,7 +33,7 @@ public class LiteralTest {
}
private static void assertCharacter(String text, int expected) {
- var code = new CodeGen("return '" + text + "';").parse().opto().typeCheck();
+ var code = new CodeGen("return '" + text + "';").driver(CodeGen.Phase.TypeCheck);
assertEquals(text, expected, ((TypeInteger)code.expr()._type).value());
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Main.java b/chapter23/src/test/java/com/seaofnodes/simple/Main.java
index 589c043b..ca1d0b5b 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter23/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.driver(CodeGen.Phase.LocalSched);
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter23/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index 52c0a573..3397a850 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -66,7 +66,7 @@ public class MemoryEncodingTest {
"""
};
for( int p=0; p<sources.length; p++ ) {
- CodeGen code=new CodeGen(sources[p]).parse().opto();
+ CodeGen code=new CodeGen(sources[p]).parse().iter().finalizeIR();
if( p==0 ) assertNull(code._stop.walk(n ->
n instanceof LoadNode ld && ld.ptr() instanceof ProjNode ptr &&
ptr.in(0) instanceof NewNode nn &&
@@ -160,7 +160,7 @@ public class MemoryEncodingTest {
}
return null;
}));
- code.instSelect(target,"SystemV").GCM().localSched().regAlloc();
+ code.driver(CodeGen.Phase.RegAlloc,target,"SystemV");
RegAllocTestSupport.checkRegisters(code);
code.encode();
}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter23/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 7ae80314..51e31a17 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter23/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).driver(CodeGen.Phase.TypeCheck);
assertEquals(result,Eval2.eval(code,0));
}
private static void bad(String src) {
try {
- new CodeGen(src).parse().opto().typeCheck();
+ new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter23/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..885889a8
--- /dev/null
+++ b/chapter23/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,42 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter FinalizeIR TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testResumeMachineCompilation() {
+ CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck);
+ staged.driver(Phase.Select,"x86_64_v2","SystemV");
+ staged.driver(Phase.Encoding,"x86_64_v2","SystemV");
+ byte[] bits = staged._encoding._bits.toByteArray();
+ CodeGen direct = new CodeGen(SRC).driver(Phase.Encoding,"x86_64_v2","SystemV");
+ assertArrayEquals(bits,direct._encoding._bits.toByteArray());
+ }
+}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter23/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 3b19857a..ca211c60 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -11,6 +11,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -31,7 +51,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter().finalizeIR();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -46,7 +66,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -65,7 +85,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.parse().iter().finalizeIR();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/Simple.java b/chapter23/src/test/java/com/seaofnodes/simple/Simple.java
index aef810d1..9da0d71b 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/Simple.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.util.Utils;
import java.io.BufferedReader;
@@ -13,20 +14,21 @@ import java.util.stream.Collectors;
public class Simple {
public static final String PORTS = "com.seaofnodes.simple.node.cpus";
- static final int DUMP_AFTER_PARSE = 1<<0;
- static final int DUMP_AFTER_OPTO = 1<<1;
- static final int DUMP_AFTER_TYPE_CHECK = 1<<2;
- static final int DUMP_AFTER_LOOP_TREE = 1<<3;
- static final int DUMP_AFTER_SELECT = 1<<4;
- static final int DUMP_AFTER_GCM = 1<<5;
- static final int DUMP_AFTER_LOCAL_SCHED = 1<<6;
- static final int DUMP_AFTER_REG_ALLOC = 1<<7;
- static final int DUMP_AFTER_ENCODE = 1<<8;
+ static final int DUMP_AFTER_PARSE = 1<<Phase.Parse.ordinal();
+ static final int DUMP_AFTER_ITER = 1<<Phase.Iter.ordinal();
+ static final int DUMP_AFTER_FINALIZE_IR = 1<<Phase.FinalizeIR.ordinal();
+ static final int DUMP_AFTER_TYPE_CHECK = 1<<Phase.TypeCheck.ordinal();
+ static final int DUMP_AFTER_LOOP_TREE = 1<<Phase.LoopTree.ordinal();
+ static final int DUMP_AFTER_SELECT = 1<<Phase.Select.ordinal();
+ static final int DUMP_AFTER_GCM = 1<<Phase.Schedule.ordinal();
+ static final int DUMP_AFTER_LOCAL_SCHED = 1<<Phase.LocalSched.ordinal();
+ static final int DUMP_AFTER_REG_ALLOC = 1<<Phase.RegAlloc.ordinal();
+ static final int DUMP_AFTER_ENCODE = 1<<Phase.Encoding.ordinal();
static final int DUMP_FINAL = 1<<16;
static final int DUMP_PASS_NAME = 1<<30;
- static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME;
+ static final int DUMP_AFTER_ALL = ((1<<Phase.values().length)-1) | DUMP_PASS_NAME;
static final int DUMP_DISASSEMBLE = 1<<31;
@@ -36,15 +38,17 @@ public class Simple {
simple [options] <input-file> ...args...
Options:
--dump - dump final intermediate representation
- --dump-after-parse - dump intermediate representation after parse
- --dump-after-opto - dump intermediate representation after opto pass
- --dump-after-type-check - dump intermediate representation after type check pass
- --dump-after-select - dump intermediate representation after instrution selection pass
- --dump-after-gcm - dump intermediate representation after GCM pass
- --dump-after-local-sched - dump intermediate representation after local scheduling pass
- --dump-after-reg-alloc - dump intermediate representation after register allocation pass
- --dump-after-encode - dump intermediate representation after encoding pass
- --dump-after-all - dump intermediate representation after all passes
+ --dump-after-parse - dump intermediate representation after parsing
+ --dump-after-iter - dump intermediate representation after Iter
+ --dump-after-finalize-ir - dump intermediate representation after FinalizeIR
+ --dump-after-type-check - dump intermediate representation after type checking
+ --dump-after-loop-tree - dump intermediate representation after loop-tree construction
+ --dump-after-select - dump intermediate representation after instruction selection
+ --dump-after-gcm - dump intermediate representation after GCM
+ --dump-after-local-sched - dump intermediate representation after local scheduling
+ --dump-after-reg-alloc - dump intermediate representation after register allocation
+ --dump-after-encode - dump intermediate representation after encoding
+ --dump-after-all - dump intermediate representation after all passes
-S - dump generated assembler code
--eval - evaluate the compiled code in emulator
--run - run the compiled code natively; this is the default
@@ -74,50 +78,19 @@ Options:
}
static void dump(CodeGen code, int dump, int pass) {
- if ((dump & pass) != 0) {
- if ((dump & DUMP_PASS_NAME) != 0) {
- System.err.println(switch (pass) {
- case DUMP_AFTER_PARSE -> "After Parse:";
- case DUMP_AFTER_OPTO -> "After OPTO:";
- case DUMP_AFTER_TYPE_CHECK -> "After Type Check:";
- case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:";
- case DUMP_AFTER_SELECT -> "After Code Selection:";
- case DUMP_AFTER_GCM -> "After GCM:";
- case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:";
- case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:";
- case DUMP_AFTER_ENCODE -> "After Encoding:";
- case DUMP_FINAL -> "Final:";
- default -> throw Utils.TODO();
- });
- }
-
- System.err.println(IRPrinter.prettyPrint(code._stop, 9999));
-
- }
+ if( (dump & pass)==0 ) return;
+ if( (dump & DUMP_PASS_NAME)!=0 ) System.err.println("Final:");
+ System.err.println(code);
}
static void print_compilation_times(CodeGen code) {
- double t, total = 0;
-
- total += t = code._tParse / 1e3;
- System.out.printf( "Parsing Time: %.3f sec%n", t);
- total += t = code._tOpto / 1e3;
- System.out.printf( "Optimization Time: %.3f sec%n", t);
- total += t = code._tTypeCheck / 1e3;
- System.out.printf( "Type Checking Time: %.3f sec%n", t);
- total += t = code._tLoopTree / 1e3;
- System.out.printf( "Loop Tree Time: %.3f sec%n", t);
- total += t = code._tInsSel / 1e3;
- System.out.printf( "Code Selection Time: %.3f sec%n", t);
- total += t = code._tGCM / 1e3;
- System.out.printf( "GCM Time: %.3f sec%n", t);
- total += t = code._tLocal / 1e3;
- System.out.printf( "Local Scheduling Time: %.3f sec%n", t);
- total += t = code._tRegAlloc / 1e3;
- System.out.printf( "Register Allocation Time: %.3f sec%n", t);
- total += t = code._tEncode / 1e3;
- System.out.printf( "Encoding Time: %.3f sec%n", t);
- System.out.printf( "TOTAL COMPILATION TIME: %.3f sec%n", total);
+ long total=0;
+ for( Phase phase : Phase.values() ) {
+ long time=code._times[phase.ordinal()];
+ total+=time;
+ System.out.printf("%-24s %.3f sec%n",phase+" Time:",time/1e3);
+ }
+ System.out.printf("TOTAL COMPILATION TIME: %.3f sec%n",total/1e3);
}
static String getInput() throws IOException {
@@ -150,16 +123,17 @@ Options:
switch (arg) {
case "-": input_filename=""; break;
case "--dump": dump |= DUMP_FINAL; break;
- case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
- case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
- case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
- case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
- case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
- case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
- case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
- case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
- case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
- case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
+ case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
+ case "--dump-after-finalize-ir": dump |= DUMP_AFTER_FINALIZE_IR; break;
+ case "--dump-after-iter": dump |= DUMP_AFTER_ITER; break;
+ case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
+ case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
+ case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
+ case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
+ case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
+ case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
+ case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
+ case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
case "-S": dump |= DUMP_DISASSEMBLE; break;
case "--eval": do_eval = true ; do_run = false; break;
case "--run": do_run = true ; break;
@@ -198,38 +172,9 @@ Options:
// Compilation pipeline
CodeGen code = new CodeGen(src);
- code.parse();
- dump(code, dump, DUMP_AFTER_PARSE);
-
- code.opto();
- dump(code, dump, DUMP_AFTER_OPTO);
-
- code.typeCheck();
- dump(code, dump, DUMP_AFTER_TYPE_CHECK);
-
- code.loopTree();
- dump(code, dump, DUMP_AFTER_LOOP_TREE);
-
- if (do_codegen) {
- code.instSelect(cpu, abi, PORTS);
- dump(code, dump, DUMP_AFTER_SELECT);
- }
-
- code.GCM();
- dump(code, dump, DUMP_AFTER_GCM);
-
- code.localSched();
- dump(code, dump, DUMP_AFTER_LOCAL_SCHED);
-
- if (do_codegen) {
- code.regAlloc();
- dump(code, dump, DUMP_AFTER_REG_ALLOC);
-
- code.encode();
- dump(code, dump, DUMP_AFTER_ENCODE);
-
- code.exportELF(base+".o");
- }
+ code.driver(do_codegen ? Phase.Encoding : Phase.LocalSched,
+ do_codegen ? cpu : null, abi, dump);
+ if( do_codegen ) code.exportELF(base+".o");
dump(code, dump, DUMP_FINAL);
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter23/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
new file mode 100644
index 00000000..00d86c63
--- /dev/null
+++ b/chapter23/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
@@ -0,0 +1,145 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.simple.TestC;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+// Force overlapping flags, then force every GPR unavailable across the gap.
+public class FlagSpillTestSupport {
+ private static class Op extends MachConcreteNode {
+ final RegMask _def, _use, _kill;
+ Op(RegMask def,RegMask use,RegMask kill,Node... ins) {
+ super(ins); _def=def; _use=use; _kill=kill; _type=TypeInteger.BOT;
+ }
+ @Override public String op() { return "flagsProbe"; }
+ @Override public RegMask outregmap() { return _def; }
+ @Override public RegMask regmap(int i) { return _use; }
+ @Override public RegMask killmap() { return _kill; }
+ @Override public void encoding(Encoding enc) { throw new AssertionError(); }
+ }
+
+ public static void run() throws Exception {
+ StringBuilder asm = new StringBuilder(".text\n");
+ for( boolean isArm : new boolean[]{false,true} )
+ for( boolean stack : new boolean[]{false,true} ) {
+ CodeGen code = new CodeGen("");
+ code._callingConv="SystemV";
+ code._mach=isArm ? new arm(code) : new x86_64_v2(code);
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ code._cfg.add(block);
+ int flagsReg=isArm ? 64 : 32, firstStack=flagsReg+1;
+ RegMask flags = new RegMask(flagsReg);
+ Op def = new Op(flags,null,null,block);
+ RegMask killMask = stack ? (isArm ? new RegMask(0x7fffffffL,1L) : new RegMask(0x1ffffffefL)) : flags;
+ Op kill = new Op(null,null,killMask,block);
+ new Op(null,flags,null,block,def);
+ RegAlloc alloc = new RegAlloc(code);
+ alloc.regAlloc();
+
+ int splits=0, slots=0;
+ for( Node n : block._outputs ) if( n instanceof SplitNode split ) {
+ int dst=alloc.regnum(n), src=alloc.regnum(n.in(1));
+ assertTrue(split.outregmap().test(dst));
+ assertTrue(split.regmap(1).test(src));
+ for( int fp=isArm ? 32 : 16; fp<flagsReg; fp++ ) {
+ assertFalse("FP output allowed",split.outregmap().test(fp));
+ assertFalse("FP input allowed",split.regmap(1).test(fp));
+ }
+ if( dst>=firstStack || src>=firstStack ) slots++;
+ splits++;
+ }
+ assertTrue(splits>=2);
+ assertEquals(stack,slots!=0);
+
+ code._regAlloc=alloc;
+ FunNode frame = new FunNode(null,null,"flags",(Node)null);
+ Encoding save = new Encoding(code), restore = new Encoding(code);
+ save._fun=restore._fun=frame;
+ Encoding enc = save;
+ for( Node n : block._outputs ) {
+ if( n==kill ) enc=restore;
+ if( n instanceof SplitNode split ) split.encoding(enc);
+ }
+ if( isArm ) {
+ for( int bits=0; bits<16; bits++ ) {
+ EvalArm64 cpu = new EvalArm64(new byte[1024],512);
+ System.arraycopy(save._bits.buf(),0,cpu._buf,0,save._bits.size());
+ System.arraycopy(restore._bits.buf(),0,cpu._buf,save._bits.size(),restore._bits.size());
+ cpu.N=(bits&8)!=0; cpu.Z=(bits&4)!=0; cpu.C=(bits&2)!=0; cpu.V=(bits&1)!=0;
+ assertEquals(0,cpu.step(save._bits.size()/4));
+ cpu.N=cpu.Z=cpu.C=cpu.V=false;
+ if( stack ) for( int r=0; r<31; r++ ) cpu.regs[r]=0;
+ assertEquals(0,cpu.step(restore._bits.size()/4));
+ assertEquals((bits&8)!=0,cpu.N); assertEquals((bits&4)!=0,cpu.Z);
+ assertEquals((bits&2)!=0,cpu.C); assertEquals((bits&1)!=0,cpu.V);
+ assertEquals(512,cpu.regs[31]);
+ }
+ } else {
+ Encoding body = new Encoding(code);
+ int[] preserved={3,5,6,7,12,13,14,15};
+ for( int r : preserved ) pushPop(body,r,false);
+ X86.imm(body,0x81,5,4,16); // Reserve spill storage.
+ X86.flags(body,TestC.OS.startsWith("Windows") ? 1 : 7,true);
+ bytes(body,save);
+ if( stack ) {
+ for( int r=0; r<16; r++ ) if( r!=4 ) X86.constant(body,r,0);
+ } else X86.reg(body,0x39,0,0); // CMP rax,rax clobbers only flags.
+ bytes(body,restore);
+ X86.flags(body,0,false); // Return the restored flags.
+ X86.imm(body,0x81,0,4,16);
+ for( int i=preserved.length-1; i>=0; i-- ) pushPop(body,preserved[i],true);
+ X86.ret(body);
+ String name=stack ? "flags_stack" : "flags_reg";
+ asm.append(".globl ").append(name).append('\n').append(name).append(":\n.byte ");
+ for( int i=0; i<body._bits.size(); i++ ) {
+ if( i!=0 ) asm.append(',');
+ asm.append(body._bits.buf()[i]&255);
+ }
+ asm.append('\n');
+ }
+ }
+ Path dir=Files.createTempDirectory(Path.of("build/objs"),"flag_spills_");
+ Files.writeString(dir.resolve("flags.s"),asm);
+ Files.writeString(dir.resolve("flags.c"),"""
+ typedef unsigned long long u64;
+ u64 __attribute__((CALL_CONV)) flags_reg(u64);
+ u64 __attribute__((CALL_CONV)) flags_stack(u64);
+ int main(void) {
+ const int positions[]={0,2,4,6,7,11};
+ for(int bits=0; bits<64; bits++) {
+ u64 flags=0x202;
+ for(int i=0;i<6;i++) if(bits&(1<<i)) flags|=1ULL<<positions[i];
+ if((flags_reg(flags)&0x8d5)!=(flags&0x8d5)) return 1;
+ if((flags_stack(flags)&0x8d5)!=(flags&0x8d5)) return 2;
+ }
+ return 0;
+ }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "flags.exe" : "flags").toString();
+ exec("gcc","-DCALL_CONV="+(TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"),
+ "-o",exe,dir.resolve("flags.c").toString(),dir.resolve("flags.s").toString());
+ exec(exe);
+ }
+
+ private static void exec(String... command) throws Exception {
+ Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
+ String output = new String(process.getInputStream().readAllBytes(),java.nio.charset.StandardCharsets.UTF_8);
+ assertEquals(String.join(" ",command)+"\n"+output,0,process.waitFor());
+ }
+
+ private static void bytes(Encoding dst,Encoding src) {
+ for( int i=0; i<src._bits.size(); i++ ) dst.add1(src._bits.buf()[i]&255);
+ }
+ private static void pushPop(Encoding enc,int reg,boolean pop) {
+ if( reg>=8 ) enc.add1(0x41);
+ enc.add1((pop ? 0x58 : 0x50)+(reg&7));
+ }
+}
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter23/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index ad898be3..b60adce7 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter23/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -22,6 +22,11 @@ public class RegAllocTestSupport {
@Override public Node copy() { return new Op(_def,_use,_kill,_clone,(Node)null); }
@Override public void encoding(Encoding enc) { throw new AssertionError(); }
}
+ private static SplitNode testSplit(CodeGen code, MachNode def) {
+ LRG lrg = new LRG((short)0);
+ lrg._machDef = def;
+ return code._mach.split("test",(byte)0,lrg);
+ }
private static CodeGen graph() {
CodeGen code = new CodeGen("");
code._mach = new x86_64_v2(code);
@@ -221,11 +226,11 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode hi = code._mach.split("test",(byte)0,null);
+ SplitNode hi = testSplit(code,def);
hi.setDef(0,code._start); hi.setDef(1,def);
alloc.newLRG(hi)._reg=1;
new Op(null,null,killed ? A : null,false,code._start);
- SplitNode lo = code._mach.split("test",(byte)0,null);
+ SplitNode lo = testSplit(code,hi);
lo.setDef(0,code._start); lo.setDef(1,hi);
alloc.newLRG(lo)._reg=0;
Op use = new Op(null,A,null,false,code._start,lo);
@@ -239,7 +244,7 @@ public class RegAllocTestSupport {
uncolored.newLRG(def0)._mask=A;
Op fixed = new Op(A,null,null,false,pre._start);
uncolored.newLRG(fixed)._mask=A;
- SplitNode copy = pre._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(pre,def0);
copy.setDef(0,pre._start); copy.setDef(1,def0);
assertFalse("A fixed definition clobbers even before coloring",uncolored.sameBlockNoClobber(copy));
}
@@ -252,7 +257,7 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
alloc.newLRG(copy)._reg=1;
if( kind==3 ) new Op(null,null,A,false,code._start);
@@ -275,7 +280,7 @@ public class RegAllocTestSupport {
CodeGen code = graph();
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(new RegMask(3L),null,null,false,code._start);
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
Op use = new Op(null,new RegMask(3L),null,false,code._start,copy);
LRG from = alloc.newLRG(def), to = alloc.newLRG(copy);
diff --git a/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index b29eb2c3..a3112d55 100644
--- a/chapter23/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter23/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().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/chapter24/#README.md# b/chapter24/#README.md#
new file mode 100644
index 00000000..9b973a09
--- /dev/null
+++ b/chapter24/#README.md#
@@ -0,0 +1,338 @@
+# Chapter 24: Chaining Relationals and Sparse Conditional Constant Propagation
+
+English | [日本語](README.ja.md)
+
+[Previous: Chapter 23](../chapter23/README.md) |
+[Next: Chapter 25](../chapter25/README.md)
+
+In this Chapter we allow *chaining* conditionals, e.g. `(60 <= score < 90)`
+where `score` in the middle does not have to be repeated.
+
+The main compiler change is Interprocedural Sparse Conditional Constant
+Propagation (SCCP): an optimistic type solve which also discovers call-graph
+edges. SCCP is otherwise the traditional one taught in many classes, except
+the lattice it operates on is very rich.
+
+A new syntax feature is chaining conditions, ala `lo <= x < min` is legal syntax.
+
+You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter24) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter23...linear-chapter24) it to the previous chapter.
+
+
+Here is the [complete language grammar](docs/24-grammar.md) for this chapter.
+
+
+## Sparse Conditional Constant Propagation
+
+[SCCP](https://en.wikipedia.org/wiki/Sparse_conditional_constant_propagation)
+is the top-down version of the same peephole/constant-folding work we have
+been doing all along. The rules for the algorithm are well known from the
+literature and commonly taught at the college level;
+[example slides](https://www.cse.psu.edu/~gxt29/teaching/cse522s23/slides/08monotoneFramework.pdf).
+
+- We have a lattice already
+- We have SSA form already (hence the algorithm is *sparse*)
+- We have a mapping from Nodes to Types (`Node._type`)
+- We have transfer functions (`Node.compute`) already.
+
+We initialize all `_type` fields to ⊤ (as opposed to ⊥), and place all Nodes on
+a worklist. We pull a Node from the worklist, run the transfer function
+`node.compute()`, and if the result changes from `node._type` we update
+`node._type` and put its output neighbors on the worklist. A simplified
+version with all asserts and debug helpers removed:
+
+```java
+while( (n = code._iter._work.pop()) != null ) {
+ Type nval = n.compute();
+ if( n._type != nval ) {
+ assert n._type.isa(nval); // Types start high and always fall
+ n._type = nval; // Update type in Node
+ code._iter.addAll(n._outputs);// Neighbors might change, need to inspect
+ }
+}
+```
+
+This algorithm always terminates (and generally fairly quickly) and always
+reaches a *fixed point*, as "best answer" subject to the limits of our lattice
+and transfer functions. The resulting types are monotonically better than
+those found by the peephole rules; we can then run the peephole rules on nodes
+with improved types, to see if unrelated peepholes can fire - such as newly
+discovered constants replacing computations with ConstantNodes.
+
+The following is SSA pseudocode:
+
+```text
+int x0 = 1; // x0 = 1
+while( rand ) {
+ x1 = phi(x0,x2); // x1 = BOT = phi(1,BOT)
+ x2 = 2 - x1; // x2 = BOT = 2 - BOT
+}
+return x1
+```
+
+Without SCCP, this small program cannot fold the computation of `x`.
+A glance at the program tells us `x` must always be a `1` but
+the bottom-up (pessimistic) approach decides that since x1 is BOT, x2 must be
+BOT so x1 must be BOT. We need the optimistic approach to break the
+stalemate, and just *assume* x1 is `1`, and then we can discover that x2 is
+also a `1`. Folding the value does not prove the loop terminates or make
+its condition and side effects removable.
+
+
+The algorithm progresses from left to right and stops when no more changes
+happen:
+
+|Var|init|x0=1|x1=phi(1,T)|x2=2-x1|x1=phi(1,1)|
+|---|----|----|-----------|-------|-----------|
+|x0 | T | 1 | 1 | 1 | 1 |
+|x1 | T | T | 1 | 1 | 1 |
+|x2 | T | T | T | 1 | 1 |
+
+
+
+### Conditional
+
+Since our control flow is in the same graph as our data flow (that's the major
+point of Sea-of-Nodes!), we need not do anything special and our algorithm is
+*conditional* already. Again an example is interesting:
+
+Again using SSA pseudocode:
+
+```text
+int x0 = 1; // x0 = 1
+while( rand ) {
+ x1 = phi(x0,x4); // x1 = BOT = phi(1,BOT)
+ if( x1 == 1 ) // Since x1 is BOT, both true and false arms are taken
+ x2 = 2 - x1; // x2 = BOT = 2 - BOT
+ else x3 = 99; // x3 = 99
+ x4 = phi(x2,x3); // x4 = BOT = phi(BOT,99)
+}
+return x1
+```
+
+Again the algorithm progresses from left to right, although this time not all
+paths are reachable. The `if` result is a pair of Control values, shown as
+either `C` if the path is alive or `X` if not. This is used when we compute
+`x4 = phi(x2,x3)`; the `PhiNode` ALSO checks the corresponding control input
+from the `if` and only merges alive paths.
+
+
+|Var|init |x0=1|x1=phi(1,T)| 1 == 1? | x2=2-x1 | x3=99 |x4=phi(C/1,X/99)|x1=phi(1,1)|
+|---|-----|----|-----------|---------|---------|---------|----------------|-----------|
+|x0 | T | 1 | 1 | 1 | 1 | 1 | 1 | 1 |
+|x1 | T | T | 1 | 1 | 1 | 1 | 1 | 1 |
+|if | T | T | T | [C,X] | [C,X] | [C,X] | [C,X] | [C,X] |
+|x2 | T | T | T | T | 1 | 1 | 1 | 1 |
+|x3 | T | T | T | T | T | 99 | 99 | 99 |
+|x4 | T | T | T | T | T | T | 1 | 1 |
+
+This kind of conditional constant propagation really helps if a condition can
+be proven true or false, as this removes a test and branch from the optimized
+program. A common way this happens is with null tests; here is the same
+program above, but using null pointers instead of integers:
+
+```java
+List head = notNullList();
+while( rand ) {
+ if( head ) head = new List(head,payload);
+ else { head = null; default_action(); }
+}
+```
+
+Here since `head` starts not-null, it can never be null and the
+`default_action` can never happen, and the loop can
+simplify:
+
+```java
+List head = notNullList();
+while( rand ) {
+ head = new List(head,payload);
+}
+```
+
+### Interprocedural
+
+In **Simple**, we let our SCCP run interprocedurally, again by (nearly) doing nothing:
+`CallNodes` call `FunNodes`, `ReturnNodes` return to `CallEndNodes` and they
+pass their type information along in exactly the normal way... except for one issue:
+there are no edges between `Calls` and `Funs` (nor `Returns` and `CallEnds`).
+Such edges make up a [Call Graph](https://en.wikipedia.org/wiki/Call_graph).
+
+We extend the normal SCCP algorithm to observe when a `Call`s function pointer
+input changes - when it picks up new function targets. There are only a finite
+number so we represent them exactly with *function indices* as described in an
+earlier chapter. (Separate compilation adds a special index for the infinite
+unknown functions that are called outside this compilation unit).
+
+Once we observe a new *fidx* at a `Call`, we *link* the `Call` with the `Fun`,
+and the `Return` with the `CallEnd`. Such linkage acts exactly as-if we added
+a CFG edge from the `Call` to the `Fun`, and the `Call`s arguments to the
+`Fun`s `ParmNodes`. `FunNodes` extend `RegionNodes` (and `ParmNodes` extend
+`PhiNodes`), so this linkage uses the same semantics. `Fun`s become reachable
+when called; `Parm`s merge (*meet*) all calling arguments, and so on.
+Similarly, `CallEnd`s merge results from all called functions, computing the
+*meet* over all the `Return`s.
+
+This *link* step adds a few checks to our algorithm's inner loop, and also
+builds a precise Call Graph which allows our SCCP to run interprocedurally.
+Other than discovering a few new edges as the algorithm proceeds, the core
+algorithm is unchanged. We are guaranteed to terminate with a fixed point
+solution, and may discover e.g. certain call parameters are constants (or
+e.g. not-null) on all calling paths.
+
+
+
+## Chaining Relational Tests
+
+Chaining relational tests offer a cleaner, more readable way to write chained
+comparisons without repeating the same variable.
+
+```java
+if (min <= x && x < max)
+```
+
+becomes
+
+```java
+if (min <= x < max)
+```
+
+This syntax saves space and makes the condition more intuitive, similar to how
+range checks are written in mathematics. E.g.
+
+```java
+int score = 75;
+if (60 <= score < 90) {
+ sys.io.p("Pass");
+}
+```
+This checks if score is between 60 (inclusive) and 90 (exclusive), without repeating `score`.
+
+Expressions that mix opposite directions of comparison, like using both
+`<=` and `>=`, are not allowed by Simple's direction rule.
+
+```java
+if (a <= b >= c)
+```
+
+
+### Operator direction rules
+Stacked comparisons are *only* valid if all the comparison operators
+"point" the same way:
+- `<` with `<`
+- `<` with `<=`
+- `>` with `>`
+- `>` with `>=`
+
+Equality operators (`==` and `!=`) have their normal, lower precedence; they
+are not links in a relational chain.
+
+It is not allowed to mix directions in a single chain:
+```java
+if (a <= b >= c) // Invalid
+```
+
+Comparisons can be forced into numeric context using booleans.
+This is valid but unusual:
+```java
+return (0 < arg) > 1;
+```
+Depending on the result of (0 < arg) this will turn into either
+```java
+return 0 > 1;
+```
+OR
+```java
+return 1 > 1;
+```
+The equality operator is valid in this form, but since it has lower precedence than comparisons,
+```java
+a < b == c
+```
+is parsed as:
+```java
+(a < b) == c
+```
+This might not be the intended chained comparison.
+A more explicit alternative is:
+```java
+a < b && b == c
+```
+Comparisons can be chained together in any length, and different comparison operators may be
+mixed freely as long as they point in the same direction.
+Each intermediate operand is evaluated once. Later operands are evaluated only
+if earlier comparisons succeed, so calls and updates in a chain short-circuit.
+
+E.g:
+```
+return 0 < arg < arg+1 < 4;
+```
+
+### Tricky edge cases
+```java
+return 0 != arg != 1;
+```
+
+This is parsed by standard precedence rules as:
+```java
+return ((0 != arg) != 1);
+```
+not as chained inequality.
+
+```java
+return a == b == c;
+```
+Similarly, this is parsed as:
+```java
+return ((a == b) == c);
+```
+
+
+### Optimization phases
+
+SCCP introduces a real `Opto` phase between two uses of the peephole worklist:
+
+```text
+Parse -> Iter -> Opto -> IterPostOpto -> FinalizeIR -> TypeCheck
+```
+
+`Iter` first reaches the pessimistic fixed point. `Opto` resets types to TOP,
+solves SCCP and the call graph, and queues nodes whose types changed.
+ `IterPostOpto` applies rewrites and inlining enabled by that solution.
+`FinalizeIR` settles layouts and detaches the analyzed call graph while keeping
+surviving functions available to the backend.
+
+
+## RegAlloc improvements: try cold loop splits first
+
+A loop Phi and its backedge can belong to one live range even though their
+values must coexist at some instruction. This self-conflict requires splitting
+the range. Chapter 23 split the Phi's definition and incoming values right away.
+This chapter first leaves the hot definition/backedge alone and tries the
+outside-loop copies. If coloring succeeds, it avoids moves on every iteration.
+Other conflicts, including two-address operations, keep their existing handling.
+
+The allocator remembers which Phis have had this chance. If the same Phi
+self-conflicts again, the next attempt uses the ordinary aggressive splitting.
+The record uses Phi identity because live ranges are rebuilt each round. It
+also covers a backedge-only conflict where the first attempt inserts no copy:
+that attempt must still consume the one permitted delay. A regression checks
+both the cheaper first attempt and the mandatory fallback.
+
+Rows are fixed test cohorts; columns are compiler chapters. Each cell gives
+**Ops / RA / X**: executed ARM+RISC-V instructions, the subset emitted for
+allocator copies/rematerializations, and x86's loop-weighted split-move estimate.
+**Cases D / X** counts measured dynamic program/target cases and x86 compilations;
+membership and inputs stay fixed across columns. Native-library work is excluded.
+Chapter 20 has no encoder, hence no dynamic counts. Lower is better within each
+metric; see the [measurement details](../docs/regalloc-spills.md) for coverage.
+
+<!-- spill-matrix:start -->
+| Test cohort | Cases D / X | Ch 20: Ops / RA / X | Ch 21: Ops / RA / X | Ch 22: Ops / RA / X | Ch 23: Ops / RA / X | Ch 24: Ops / RA / X |
+|---|---:|---:|---:|---:|---:|---:|
+| Ch 20 | 22 / 13 | — / — / 161 | 55,173 / 5,407 / 174 | 58,751 / 9,099 / 175 | 58,736 / 9,084 / 184 | 58,720 / 9,068 / 183 |
+| Ch 21 | 20 / 18 | | 6,207 / 261 / 461 | 6,324 / 379 / 475 | 6,326 / 381 / 475 | 6,325 / 380 / 470 |
+| Ch 22 | 14 / 8 | | | 198 / 60 / 7 | 194 / 56 / 7 | 194 / 56 / 7 |
+| Ch 23 | 14 / 8 | | | | 630 / 160 / 36 | 630 / 160 / 36 |
+| Ch 24 | 36 / 20 | | | | | 4,923 / 1,341 / 790 |
+<!-- spill-matrix:end -->
+
diff --git a/chapter24/.#README.md b/chapter24/.#README.md
new file mode 100644
index 00000000..c657b199
--- /dev/null
+++ b/chapter24/.#README.md
@@ -0,0 +1 @@
+cliffc@DESKTOP-DAD.21980:1790387027 \ No newline at end of file
diff --git a/chapter24/README.md b/chapter24/README.md
index ceb7e03e..e647bec3 100644
--- a/chapter24/README.md
+++ b/chapter24/README.md
@@ -10,125 +10,16 @@ where `score` in the middle does not have to be repeated.
The main compiler change is Interprocedural Sparse Conditional Constant
Propagation (SCCP): an optimistic type solve which also discovers call-graph
-edges. Memory follows Chapter 11's lazy partitioning: BulkMemPhi represents
-whole memory, MemPhi merges precise aliases, and allocations consume and produce
-partial memory for their instance fields. During SCCP, memory Phis ignore
-unreachable inputs and allocation results stay high until control reaches them.
-Chapter 25 retains the larger incomplete-types SSA redesign, private constructor
-memory, and separate compilation; see the [backport review queue](../docs/chapter-backports.md).
+edges. SCCP is otherwise the traditional one taught in many classes, except
+the lattice it operates on is very rich.
+A new syntax feature is chaining conditions, ala `lo <= x < min` is legal syntax.
You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter24) in a linear Git revision history on the [linear](https://github.com/SeaOfNodes/Simple/tree/linear) branch and [compare](https://github.com/SeaOfNodes/Simple/compare/linear-chapter23...linear-chapter24) it to the previous chapter.
Here is the [complete language grammar](docs/24-grammar.md) for this chapter.
-## Chaining Relational Tests
-
-Chaining relational tests offer a cleaner, more readable way to write chained
-comparisons without repeating the same variable.
-
-```java
-if (min <= x && x < max)
-```
-
-becomes
-
-```java
-if (min <= x < max)
-```
-
-This syntax saves space and makes the condition more intuitive, similar to how
-range checks are written in mathematics. E.g.
-
-```java
-int score = 75;
-if (60 <= score < 90) {
- sys.io.p("Pass");
-}
-```
-This checks if score is between 60 (inclusive) and 90 (exclusive), without repeating `score`.
-
-Expressions that mix opposite directions of comparison, like using both
-`<=` and `>=`, are not allowed by Simple's direction rule.
-
-```java
-if (a <= b >= c)
-```
-
-
-### Operator direction rules
-Stacked comparisons are *only* valid if all the comparison operators
-"point" the same way:
-- `<` with `<`
-- `<` with `<=`
-- `>` with `>`
-- `>` with `>=`
-
-Equality operators (`==` and `!=`) have their normal, lower precedence; they
-are not links in a relational chain.
-
-It is not allowed to mix directions in a single chain:
-```java
-if (a <= b >= c) // Invalid
-```
-
-Comparisons can be forced into numeric context using booleans.
-This is valid but unusual:
-```java
-return (0 < arg) > 1;
-```
-Depending on the result of (0 < arg) this will turn into either
-```java
-return 0 > 1;
-```
-OR
-```java
-return 1 > 1;
-```
-The equality operator is valid in this form, but since it has lower precedence than comparisons,
-```java
-a < b == c
-```
-is parsed as:
-```java
-(a < b) == c
-```
-This might not be the intended chained comparison.
-A more explicit alternative is:
-```java
-a < b && b == c
-```
-Comparisons can be chained together in any length, and different comparison operators may be
-mixed freely as long as they point in the same direction.
-Each intermediate operand is evaluated once. Later operands are evaluated only
-if earlier comparisons succeed, so calls and updates in a chain short-circuit.
-
-E.g:
-```
-return 0 < arg < arg+1 < 4;
-```
-
-### Tricky edge cases
-```java
-return 0 != arg != 1;
-```
-
-This is parsed by standard precedence rules as:
-```java
-return ((0 != arg) != 1);
-```
-not as chained inequality.
-
-```java
-return a == b == c;
-```
-Similarly, this is parsed as:
-```java
-return ((a == b) == c);
-```
-
-
## Sparse Conditional Constant Propagation
@@ -167,7 +58,7 @@ those found by the peephole rules; we can then run the peephole rules on nodes
with improved types, to see if unrelated peepholes can fire - such as newly
discovered constants replacing computations with ConstantNodes.
-The following is SSA pseudocode; `phi` denotes a merge, not Simple syntax.
+The following is SSA pseudocode:
```text
int x0 = 1; // x0 = 1
@@ -289,6 +180,128 @@ solution, and may discover e.g. certain call parameters are constants (or
e.g. not-null) on all calling paths.
+
+## Chaining Relational Tests
+
+Chaining relational tests offer a cleaner, more readable way to write chained
+comparisons without repeating the same variable.
+
+```java
+if (min <= x && x < max)
+```
+
+becomes
+
+```java
+if (min <= x < max)
+```
+
+This syntax saves space and makes the condition more intuitive, similar to how
+range checks are written in mathematics. E.g.
+
+```java
+int score = 75;
+if (60 <= score < 90) {
+ sys.io.p("Pass");
+}
+```
+This checks if score is between 60 (inclusive) and 90 (exclusive), without repeating `score`.
+
+Expressions that mix opposite directions of comparison, like using both
+`<=` and `>=`, are not allowed by Simple's direction rule.
+
+```java
+if (a <= b >= c)
+```
+
+
+### Operator direction rules
+Stacked comparisons are *only* valid if all the comparison operators
+"point" the same way:
+- `<` with `<`
+- `<` with `<=`
+- `>` with `>`
+- `>` with `>=`
+
+Equality operators (`==` and `!=`) have their normal, lower precedence; they
+are not links in a relational chain.
+
+It is not allowed to mix directions in a single chain:
+```java
+if (a <= b >= c) // Invalid
+```
+
+Comparisons can be forced into numeric context using booleans.
+This is valid but unusual:
+```java
+return (0 < arg) > 1;
+```
+Depending on the result of (0 < arg) this will turn into either
+```java
+return 0 > 1;
+```
+OR
+```java
+return 1 > 1;
+```
+The equality operator is valid in this form, but since it has lower precedence than comparisons,
+```java
+a < b == c
+```
+is parsed as:
+```java
+(a < b) == c
+```
+This might not be the intended chained comparison.
+A more explicit alternative is:
+```java
+a < b && b == c
+```
+Comparisons can be chained together in any length, and different comparison operators may be
+mixed freely as long as they point in the same direction.
+Each intermediate operand is evaluated once. Later operands are evaluated only
+if earlier comparisons succeed, so calls and updates in a chain short-circuit.
+
+E.g:
+```
+return 0 < arg < arg+1 < 4;
+```
+
+### Tricky edge cases
+```java
+return 0 != arg != 1;
+```
+
+This is parsed by standard precedence rules as:
+```java
+return ((0 != arg) != 1);
+```
+not as chained inequality.
+
+```java
+return a == b == c;
+```
+Similarly, this is parsed as:
+```java
+return ((a == b) == c);
+```
+
+
+### Optimization phases
+
+SCCP introduces a real `Opto` phase between two uses of the peephole worklist:
+
+```text
+Parse -> Iter -> Opto -> IterPostOpto -> FinalizeIR -> TypeCheck
+```
+
+`Iter` first reaches the pessimistic fixed point. `Opto` resets types to TOP,
+solves SCCP and the call graph, and queues nodes whose types changed.
+`IterPostOpto` applies rewrites and inlining enabled by that solution.
+`FinalizeIR` settles layouts and detaches the analyzed call graph while keeping
+surviving functions available to the backend.
+
+
## RegAlloc improvements: try cold loop splits first
A loop Phi and its backedge can belong to one live range even though their
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index f3d3dee3..ad48dd5d 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -22,18 +22,67 @@ public class CodeGen {
public static CodeGen CODE;
public enum Phase {
- Parse, // Parse ASCII text into Sea-of-Nodes IR
- Opto, // Run ideal optimizations
- TypeCheck, // Last check for bad programs
- LoopTree, // Build a loop tree; break infinite loops
- Select, // Convert to target hardware nodes
- Schedule, // Global schedule (code motion) nodes
- LocalSched, // Local schedule
- RegAlloc, // Register allocation
- Encoding, // Encoding
- Export, // Export
+ Parse, // Build ideal IR from source
+ Iter, // Pessimistic peepholes
+ Opto, // Optimistic interprocedural SCCP
+ IterPostOpto, // Peepholes and inlining using SCCP results
+ FinalizeIR, // Finish function cleanup and layouts
+ TypeCheck, // Check the surviving ideal graph
+ LoopTree, // Discover loops and connect nonreturning paths
+ Select, // Select machine instructions
+ Schedule, // Global code motion
+ LocalSched, // Order instructions within blocks
+ RegAlloc, // Assign registers and stack slots
+ Encoding, // Encode instructions
+ Export; // Link in memory or write ELF
+
}
public Phase _phase;
+
+ // Run through target, skipping phases already completed.
+ public CodeGen driver(Phase target) { return driver(target,null,null); }
+ public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); }
+ public CodeGen driver(Phase target, String cpu, String callingConv, int dump) {
+ if( target.ordinal()>=Phase.RegAlloc.ordinal() && cpu==null && _mach==null )
+ throw new IllegalArgumentException("Machine code requires a CPU target");
+ if( target==Phase.Export ) throw new IllegalArgumentException("Use exportELF with an output file or null for memory linking");
+ step(target, Phase.Parse, this::parse, dump);
+ step(target, Phase.Iter, this::iter, dump);
+ step(target, Phase.Opto, this::opto, dump);
+ step(target, Phase.IterPostOpto, this::iterPostOpto, dump);
+ step(target, Phase.FinalizeIR, this::finalizeIR, dump);
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ step(target, Phase.Select, () -> instSelect(cpu,callingConv), dump);
+ step(target, Phase.Schedule, this::GCM, dump);
+ step(target, Phase.LocalSched, this::localSched, dump);
+ step(target, Phase.RegAlloc, this::regAlloc, dump);
+ step(target, Phase.Encoding, this::encode, dump);
+ return this;
+ }
+
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) {
+ if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":");
+ System.err.println(this);
+ }
+ }
+
+ public final long[] _times = new long[Phase.values().length];
+ private CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ try { work.run(); }
+ finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(next.name());
+ return this;
+ }
+
+
// ---------------------------
// Compilation source code
public final String _src;
@@ -59,22 +108,6 @@ public class CodeGen {
}
- // All passes up to Phase, except ELF
- public CodeGen driver( Phase phase ) { return driver(phase,null,null); }
- public CodeGen driver( Phase phase, String cpu, String callingConv ) {
- if( _phase==null ) parse();
- int p1 = phase.ordinal();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Opto .ordinal() ) opto();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.TypeCheck .ordinal() ) typeCheck();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LoopTree .ordinal() ) loopTree();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Select .ordinal() && cpu != null ) instSelect(cpu,callingConv);
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Schedule .ordinal() ) GCM();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.LocalSched.ordinal() ) localSched();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.RegAlloc .ordinal() ) regAlloc();
- if( _phase.ordinal() < p1 && _phase.ordinal() < Phase.Encoding .ordinal() ) encode();
- return this;
- }
-
// Run all the phases through final ELF emission
public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException {
return driver(Phase.Encoding,cpu,callingConv).exportELF(obj);
@@ -160,16 +193,10 @@ public class CodeGen {
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
- public int _tParse;
public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- long t0 = System.currentTimeMillis();
-
- P.parse();
- _tParse = (int)(System.currentTimeMillis() - t0);
- if( _obs != null ) _obs.phase("Parse");
- return this;
+ return phase(Phase.Parse, _phase==null, () -> {
+ P.parse();
+ });
}
@@ -184,55 +211,28 @@ public class CodeGen {
public void iterCnt() { if( !_midAssert ) _iter_cnt++; }
public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; }
- // Run ideal optimizations
- public int _tOpto;
- public CodeGen opto() {
- assert _phase == Phase.Parse;
- _phase = Phase.Opto;
- long t0 = System.currentTimeMillis();
+ // Pessimistic peepholes reach a fixed point after parsing.
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); }
- Opto.opto(this);
+ // SCCP computes types and queues changed nodes; Iter applies their rewrites.
+ public CodeGen opto() { return phase(Phase.Opto, _phase==Phase.Iter, () -> Opto.opto(this)); }
+ public CodeGen iterPostOpto() { return phase(Phase.IterPostOpto, _phase==Phase.Opto, () -> _iter.iterate(this)); }
- _tOpto = (int)(System.currentTimeMillis() - t0);
- if( _obs != null ) _obs.phase("Opto");
- return this;
- }
+ public CodeGen finalizeIR() { return phase(Phase.FinalizeIR, _phase==Phase.IterPostOpto, () -> FinalizeIR.run(this)); }
public <N extends Node> N add( N n ) { return _iter.add(n); }
public void addAll( Ary<Node> ary ) { _iter.addAll(ary); }
// ---------------------------
// Last check for bad programs
- public int _tTypeCheck;
- 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();
- _phase = Phase.TypeCheck;
- long t0 = System.currentTimeMillis();
-
- Parser.ParseException err = _stop.walk(n -> {
- Parser.ParseException msg = n.err();
- if( msg != null && _obs != null ) _obs.error(n, msg.getMessage());
- return msg;
- });
- _tTypeCheck = (int)(System.currentTimeMillis() - t0);
- if( err != null )
- throw err;
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.FinalizeIR, () -> TypeCheck.run(this)); }
// ---------------------------
// Build the loop tree; break never-exit loops
- public int _tLoopTree;
public CodeGen loopTree() {
- assert _phase.ordinal() <= Phase.TypeCheck.ordinal();
- _phase = Phase.LoopTree;
- long t0 = System.currentTimeMillis();
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree(_start,_stop);
- _tLoopTree = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> {
+ // Build the loop tree, fix never-exit loops
+ _start.buildLoopTree(_start,_stop);
+ });
}
// ---------------------------
@@ -254,138 +254,41 @@ public class CodeGen {
public RegMask _rpcMask;
// Convert to target hardware nodes
- public int _tInsSel;
public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
public CodeGen instSelect( String cpu, String callingConv, String base ) {
- assert _phase.ordinal() == Phase.LoopTree.ordinal();
- _phase = Phase.Select;
-
- _callingConv = callingConv;
-
- // Not timing the class load...
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- // Build global copies of common register masks.
- long callerSave = _mach.callerSave();
- long neverSave = _mach. neverSave();
- int maxReg = Math.min(64,_mach.regs().length);
- assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
- _callerSave = new RegMask(callerSave);
-
- // Build a Return RegMask array. All returns have the following inputs:
- // 0 - ctrl
- // 1 - memory
- // 2 - varies with returning GPR,FPR
- // 3 - RPC
- // 4+- Caller save registers
- _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
- for( int reg=0, i=4; reg<maxReg; reg++ )
- if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
- _retMasks[i++] = new RegMask(reg);
- _rpcMask = new RegMask(_mach.rpc());
- _retMasks[3] = _rpcMask;
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = ( StopNode)map.get(_stop );
- StartNode start = (StartNode)map.get(_start);
- _start = start==null ? new StartNode(_start) : start;
- _instOuts(_stop,visit());
- _visit.clear();
- _tInsSel = (int)(System.currentTimeMillis() - t0);
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachNode already, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachNode ) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- map.put(n, x=_mach.instSelect(n) );
- // Stop retains the callable body. Its lifetime edge is not a machine operand.
- if( n instanceof FunPtrNode ) x._inputs.pop();
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- if( x instanceof MachNode mach )
- mach.postSelect(this); // Post selection action
-
- return x;
- }
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- _instOuts(in,visit);
- }
+ if( cpu==null ) return this;
+ return phase(Phase.Select, _phase==Phase.LoopTree,
+ () -> new InstSelect(this).run(cpu,callingConv,base));
}
// ---------------------------
// Control Flow Graph in Reverse Post Order.
- public int _tGCM;
public Ary<CFGNode> _cfg = new Ary<>(CFGNode.class);
// Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.Select.ordinal();
- _phase = Phase.Schedule;
- long t0 = System.currentTimeMillis();
-
- GlobalCodeMotion.buildCFG(this);
- _tGCM = (int)(System.currentTimeMillis() - t0);
+ return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.Select), () -> {
+ GlobalCodeMotion.buildCFG(this);
- return this;
+ });
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- public int _tLocal;
public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- long t0 = System.currentTimeMillis();
- ListScheduler.sched(this);
- _tLocal = (int)(System.currentTimeMillis() - t0);
- return this;
- }
+ return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this));
+ }
// ---------------------------
// Register Allocation
- public int _tRegAlloc;
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
- assert _phase == Phase.LocalSched;
- _phase = Phase.RegAlloc;
- long t0 = System.currentTimeMillis();
- _regAlloc = new RegAlloc(this);
- _regAlloc.regAlloc();
- _tRegAlloc = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> {
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ });
}
// Human readable register name
@@ -401,27 +304,27 @@ public class CodeGen {
// ---------------------------
// Encoding
- public int _tEncode;
public Encoding _encoding; // Encoding object
public void preEncode() { } // overridden by alternative ports
public CodeGen encode() {
- assert _phase == Phase.RegAlloc;
- _phase = Phase.Encoding;
- long t0 = System.currentTimeMillis();
- _encoding = new Encoding(this);
- preEncode();
- _encoding.encode();
- _tEncode = (int)(System.currentTimeMillis() - t0);
- return this;
+ return phase(Phase.Encoding, _phase==Phase.RegAlloc, () -> {
+ _encoding = new Encoding(this);
+ preEncode();
+ _encoding.encode();
+ });
}
// ---------------------------
// Exporting to external formats
public CodeGen exportELF(String fname) throws IOException {
- assert _phase == Phase.Encoding;
- _phase = Phase.Export;
- if( fname == null ) new LinkMem(this).link(); // In memory patching
- else new ElfFile(this).export(fname); // External ELF file
+ assert _phase==Phase.Encoding;
+ _phase=Phase.Export;
+ long t0=System.currentTimeMillis();
+ try {
+ if( fname == null ) new LinkMem(this).link(); // In memory patching
+ else new ElfFile(this).export(fname); // External ELF file
+ } finally { _times[Phase.Export.ordinal()]=System.currentTimeMillis()-t0; }
+ if( _obs!=null ) _obs.phase(Phase.Export.name());
return this;
}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
new file mode 100644
index 00000000..9e3b3e32
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
@@ -0,0 +1,76 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.util.Arrays;
+
+// Settle layouts and detach the analyzed call graph before the backend.
+abstract class FinalizeIR {
+ static void run(CodeGen code) {
+ fieldSizes(code);
+ code._iter.iterate(code);
+ removeCallGraph(code);
+ Arrays.sort(code._stop._inputs._es,0,code._stop.nIns(),(x,y) -> x._nid - y._nid);
+ }
+
+ // 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 ) {
+ TypeMemPtr tmp = (TypeMemPtr) Parser.TYPES.get(off._name);
+ off.subsume( off.asOffset(tmp._obj) );
+ i--;
+ }
+ }
+ }
+
+ // The remaining passes assume all calls are unlinked; i.e. we are throwing
+ // away the Call Graph here. Functions only reachable from internal calls
+ // need to be re-hooked to stop/start less they go dead.
+ private static void removeCallGraph(CodeGen code) {
+ code._start.walk( x -> {
+ // Unlink all existing (conservative) linkages.
+ if( x instanceof CallNode call ) {
+ for( Node y : call._outputs ) {
+ // Keep function alive and hooked to Start so it gets
+ // codegen'd. Function is callable by this call via
+ // TFP, although perhaps not by any other means.
+ if( y instanceof FunNode fun && !(fun.in(1) instanceof StartNode) ) {
+ ParmNode rpc = fun.rpc();
+ if( rpc==null ) // Ensure valid RPC
+ rpc = makeRPC(fun);
+ // Insert a hook to Start and Stop
+ fun.insertDef(1,code._start);
+ for( Node use : fun._outputs )
+ if( use instanceof ParmNode parm )
+ parm.insertDef(1,ConstantNode.make(parm._type).peephole());
+ code._stop.addDef(fun.ret());
+ }
+ }
+ call.unlink_all();
+ }
+ return null;
+ } );
+
+ }
+
+ // Make a valid Parm RPC.
+ // First make sure a valid RPC; single-call into multi-fun will have each
+ // function having a single call site, so the RPC becomes a constant and
+ // folds - but since multiple targets, the Call never inlines. Recreate a
+ // valid RPC so codegen (and Eval2) understands the calling convention.
+ private static ParmNode makeRPC( FunNode fun ) {
+ ParmNode rpc = new ParmNode("$rpc",0,fun.ret().rpc()._type,fun);
+ for( int i=1; i<fun.nIns(); i++ ) {
+ CallEndNode cend = ((CallNode)fun.in(i)).cend();
+ rpc.addDef(ConstantNode.make(cend._rpc).peephole());
+ }
+ fun.ret().setDef(3,rpc.init());
+ return rpc;
+ }
+
+
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 80d5d9bb..da45db4f 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -273,10 +273,11 @@ public abstract class GlobalCodeMotion {
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
break;
case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; unchanged memory does not
+ // clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
break;
case NewNode st:
@@ -293,7 +294,9 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// A conditional writer can precede the load without dominating its late block.
// Walk back to the first overlap with the load's placement range.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Another Phi input need not be dominated by this memory definition.
+ // Include its depth, and retain the sentinel for an entry's null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
new file mode 100644
index 00000000..e8e87785
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -0,0 +1,90 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ InstSelect(CodeGen code) { _code=code; }
+
+ @SuppressWarnings("unchecked")
+ void run(String cpu, String callingConv, String base) {
+ _code._callingConv = callingConv;
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = base+"."+cpu+"."+cpu;
+ try { _code._mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(_code); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ // Build global copies of common register masks.
+ long callerSave = _code._mach.callerSave();
+ long neverSave = _code._mach. neverSave();
+ int maxReg = Math.min(64,_code._mach.regs().length);
+ assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+ _code._callerSave = new RegMask(callerSave);
+
+ // Build a Return RegMask array. All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ _code._retMasks = new RegMask[(maxReg-_code._callerSave.size()-Long.bitCount( neverSave ))+4];
+ for( int reg=0, i=4; reg<maxReg; reg++ )
+ if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+ _code._retMasks[i++] = new RegMask(reg);
+ _code._rpcMask = new RegMask(_code._mach.rpc());
+ _code._retMasks[3] = _code._rpcMask;
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = ( StopNode)map.get(_code._stop );
+ StartNode start = (StartNode)map.get(_code._start);
+ _code._start = start==null ? new StartNode(_code._start) : start;
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachNode already, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachNode ) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Stop retains the callable body. Its lifetime edge is not a machine operand.
+ if( n instanceof FunPtrNode ) x._inputs.pop();
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ if( x instanceof MachNode mach )
+ mach.postSelect(_code); // Post selection action
+
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ _instOuts(in,visit);
+ }
+ }
+
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index 009ddcdb..0c36ae2b 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -58,6 +58,8 @@ public class LRG {
public int nadj() { return _adj==null ? 0 : _adj._len; }
+ public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/Opto.java
index cd52aadd..9f07c1d1 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/codegen/Opto.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/Opto.java
@@ -4,7 +4,6 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.Ary;
-import java.util.Arrays;
// Optimistic Algorithm
abstract public class Opto {
@@ -25,10 +24,6 @@ abstract public class Opto {
// FunNodes.
public static void opto(CodeGen code) {
- // Pessimistic peephole optimization on a worklist - this completes
- // whatever peepholes can run after parsing.
- code._iter.iterate(code);
-
// Reset Types before running the worklist algorithm
Ary<Type> oldTypes = new Ary<>(Type.class); // Used for asserts only
resetToTop(code,oldTypes);
@@ -60,28 +55,7 @@ abstract public class Opto {
// 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);
- // Throw away the Call Graph
-
- // The remaining passes assume all calls are unlinked; i.e. we are
- // throwing away the Call Graph here. Functions only reachable from
- // internal calls need to be re-hooked to stop/start less they go dead.
- removeCallGraph(code);
-
- // To help with testing, sort StopNode Returns by NID
- Arrays.sort(code._stop._inputs._es,0,code._stop.nIns(),(x,y) -> x._nid - y._nid );
}
// Reset Types before running the worklist algorithm
@@ -205,63 +179,4 @@ abstract public class Opto {
});
}
- // 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 ) {
- TypeMemPtr tmp = (TypeMemPtr) Parser.TYPES.get(off._name);
- off.subsume( off.asOffset(tmp._obj) );
- i--;
- }
- }
- }
-
- // The remaining passes assume all calls are unlinked; i.e. we are throwing
- // away the Call Graph here. Functions only reachable from internal calls
- // need to be re-hooked to stop/start less they go dead.
- private static void removeCallGraph(CodeGen code) {
- code._start.walk( x -> {
- // Unlink all existing (conservative) linkages.
- if( x instanceof CallNode call ) {
- for( Node y : call._outputs ) {
- // Keep function alive and hooked to Start so it gets
- // codegen'd. Function is callable by this call via
- // TFP, although perhaps not by any other means.
- if( y instanceof FunNode fun && !(fun.in(1) instanceof StartNode) ) {
- ParmNode rpc = fun.rpc();
- if( rpc==null ) // Ensure valid RPC
- rpc = makeRPC(fun);
- // Insert a hook to Start and Stop
- fun.insertDef(1,code._start);
- for( Node use : fun._outputs )
- if( use instanceof ParmNode parm )
- parm.insertDef(1,ConstantNode.make(parm._type).peephole());
- code._stop.addDef(fun.ret());
- }
- }
- call.unlink_all();
- }
- return null;
- } );
-
- }
-
- // Make a valid Parm RPC.
- // First make sure a valid RPC; single-call into multi-fun will have each
- // function having a single call site, so the RPC becomes a constant and
- // folds - but since multiple targets, the Call never inlines. Recreate a
- // valid RPC so codegen (and Eval2) understands the calling convention.
- private static ParmNode makeRPC( FunNode fun ) {
- ParmNode rpc = new ParmNode("$rpc",0,fun.ret().rpc()._type,fun);
- for( int i=1; i<fun.nIns(); i++ ) {
- CallEndNode cend = ((CallNode)fun.in(i)).cend();
- rpc.addDef(ConstantNode.make(cend._rpc).peephole());
- }
- fun.ret().setDef(3,rpc.init());
- return rpc;
- }
-
-
}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 7e826661..565fa6ba 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -443,7 +443,7 @@ public class RegAlloc {
// If the minLoopDepth is less than the maxLoopDepth: for-all defs and
// uses, if at minLoopDepth or lower, split after def and before use.
for( Node n : _ns ) {
- if( n instanceof SplitNode && min!=max ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n instanceof SplitNode split && !split.isNonStackSplit() && min!=max ) continue; // Ordinary splits already reach stack storage.
if( n.isDead() ) continue; // Some Cloneable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
@@ -496,8 +496,8 @@ public class RegAlloc {
}
private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
+ // Ordinary splits already reach stack storage; register-only transfers do not.
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld;
// Collect min/max loop depth
int min = (int)ld;
int max = (int)(ld>>32);
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter24/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
new file mode 100644
index 00000000..0997fd2f
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -0,0 +1,15 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ Parser.ParseException err = code._stop.walk(n -> {
+ Parser.ParseException msg = n.err();
+ if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage());
+ return msg;
+ });
+ if( err != null )
+ throw err;
+ }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter24/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index 211489fb..a7fe6c5f 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -10,6 +10,8 @@ public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
public final byte _round;
public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; }
+ // A register-only transfer may need another split to reach stack storage.
+ public boolean isNonStackSplit() { return false; }
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
new file mode 100644
index 00000000..3e045a6f
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+
+// NZCV transfers require a GPR. Further splits can spill that GPR to the stack.
+public class SplitFlagsARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,1L);
+ SplitFlagsARM(String kind,byte round) { super(kind,round); }
+ @Override public boolean isNonStackSplit() { return true; }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( src==arm.FLAGS ) enc.add4(Arm64.flags(dst,false));
+ else if( dst==arm.FLAGS ) enc.add4(Arm64.flags(src,true));
+ else super.encoding(enc);
+ }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
new file mode 100644
index 00000000..46eff4ca
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
@@ -0,0 +1,11 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Keep a saved NZCV value in GPRs and stack slots through subsequent spills.
+public class SplitGPRARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L);
+ SplitGPRARM(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
index 32c1e2fc..fae98a99 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java
@@ -271,7 +271,16 @@ public class arm extends Machine {
@Override public int rpc() { return X30; }
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); }
+ @Override public SplitNode split(String kind, byte round, LRG lrg) {
+ RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask();
+ if( (def!=null && def.size1() && def.test(FLAGS)) ||
+ (use!=null && use.size1() && use.test(FLAGS)) )
+ return new SplitFlagsARM(kind,round);
+ // Spilling the saved flags requires a GPR between NZCV and the stack.
+ if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM )
+ return new SplitGPRARM(kind,round);
+ return new SplitARM(kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
new file mode 100644
index 00000000..4650b535
--- /dev/null
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+
+// Flag snapshots can use GPRs or stack slots, but never XMM registers.
+public class SplitFlagsX86 extends SplitX86 {
+ private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L<<x86_64_v2.FLAGS) | x86_64_v2.SPILLS,-1L);
+ SplitFlagsX86(String kind,byte round) { super(kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( dst==x86_64_v2.FLAGS || src==x86_64_v2.FLAGS ) {
+ boolean toFlags = dst==x86_64_v2.FLAGS;
+ int reg = toFlags ? src : dst;
+ if( reg>=x86_64_v2.MAX_REG )
+ X86.flagsStack(enc,enc._fun.computeStackOffset(enc._code,reg),toFlags);
+ else
+ X86.flags(enc,reg,toFlags);
+ } else super.encoding(enc);
+ }
+}
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
index e8281975..4e9eda7d 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -17,12 +17,10 @@ public class SplitX86 extends SplitNode {
@Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
- // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
- // flags->flags.
+ // Ordinary GPR, XMM, and stack moves. Flags use SplitFlagsX86.
@Override public void encoding( Encoding enc ) {
short dst=enc.reg(this), src=enc.reg(in(1));
- if( dst==x86_64_v2.FLAGS ) { X86.flags(enc,src,true); return; }
- if( src==x86_64_v2.FLAGS ) { X86.flags(enc,dst,false); return; }
+ assert dst!=x86_64_v2.FLAGS && src!=x86_64_v2.FLAGS;
boolean dstX=dst>=x86_64_v2.XMM_OFFSET, srcX=src>=x86_64_v2.XMM_OFFSET;
// Stack spills
if( dst >= x86_64_v2.MAX_REG ) {
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
index e283bc8f..fcae17f8 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java
@@ -47,7 +47,7 @@ public class x86_64_v2 extends Machine {
static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<<RAX) & ~(1L<<RDX));
static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | SPILLS, -1L );
// Load/store mask; both GPR and FPR
static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -238,7 +238,9 @@ public class x86_64_v2 extends Machine {
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(String kind, byte round, LRG lrg) {
- return new SplitX86(kind, round);
+ return lrg._machDef!=null && lrg._machDef.outregmap().test(FLAGS)
+ ? new SplitFlagsX86(kind,round)
+ : new SplitX86(kind,round);
}
// Return a MachNode unconditional branch
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
index 3df1e0ea..b2c5492e 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java
+++ b/chapter24/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.driver(CodeGen.Phase.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/chapter24/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter24/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index bb6ce4cb..55942b0e 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -435,7 +435,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/chapter24/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 6ff4ac95..b234209f 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -20,7 +20,7 @@ else {
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print());
}
@@ -36,7 +36,7 @@ if (arg == 1) {
}
return c;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));", code.print());
}
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -145,7 +145,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 1ff875b4..53fcefed 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter24/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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -65,7 +65,7 @@ else
b=5;
return b;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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), 123L, true).parse().opto();
+""", TypeInteger.constant(2), 123L, true).driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1), 123L, true).parse().opto();
+""", TypeInteger.constant(1), 123L, true).driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,3,5,4);", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1), 123L, true);
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3), 123L, true);
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1), 123L, true);
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0), 123L, true);
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,6,1,5);", code.print());
}
@@ -278,7 +278,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(1), 123L, true);
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2), 123L, true);
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 5;", code.print());
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index 88554323..8db31bc1 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -34,7 +34,7 @@ if(arg){}else{
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -122,7 +122,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,1,2);", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
@@ -180,7 +180,7 @@ while(a < 10) {
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
assertTrue(code.ctrl() instanceof CProjNode);
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 09669cc7..376e472b 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -22,7 +22,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (Phi(Loop,1,Add)+1);", code.print());
assertTrue(code.ctrl() instanceof CProjNode prj && prj._idx==1 );
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 54a14a25..f24d96eb 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -24,7 +24,7 @@ while(v0) {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -128,14 +128,14 @@ while(v1+arg) {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return arg;", code.print());
}
@@ -148,7 +148,7 @@ if(0) while(0) {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
@@ -181,7 +181,7 @@ while(arg) {
}
return a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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 0;");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -222,7 +222,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -244,7 +244,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -257,7 +257,7 @@ while(0==1) while(v0)
v0=1+v0;
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -273,14 +273,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 0f4ca169..40c69a4f 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -14,7 +14,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).driver(CodeGen.Phase.TypeCheck);
org.junit.Assert.assertTrue(body,
code.expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
}
@@ -40,7 +40,7 @@ public class Chapter10Test {
"int b = !!p; if (b) return p.x + b - 1; return -1;",
"if (!(p == null || arg == 0)) return p.x; return -1;"
} ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
assertEquals(body,"-1",Eval2.eval(code,0));
assertEquals(body,"42",Eval2.eval(code,1));
}
@@ -70,7 +70,7 @@ public class Chapter10Test {
"if (!!p) { int x = p.x; } return p.x;"
} ) {
try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).parse().opto().typeCheck();
+ new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
fail(body);
} catch( RuntimeException e ) {
// Known null is rejected during parsing in these chapters.
@@ -102,7 +102,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -122,7 +122,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 2;", code.print());
}
@@ -138,7 +138,7 @@ else
v.y = 3;
return v;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Vector2D;", code.print());
}
@@ -176,7 +176,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,0,(Phi_a+2));", code.print());
}
@@ -189,7 +189,7 @@ if (arg) bar = null;
bar.a = 1;
return bar.a;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *Bar", e.getMessage()); }
}
@@ -228,7 +228,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,null,Bar);", code.print());
}
@@ -244,7 +244,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", code.print());
}
@@ -317,7 +317,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -335,7 +335,7 @@ while(arg) {
}
return ret;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -352,7 +352,7 @@ while(arg < 10) {
}
return ret;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,s0,Phi(Region,s0,Phi_ret));", code.print());
}
@@ -380,7 +380,7 @@ if(0) {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -394,7 +394,7 @@ if(0) return 0;
else return new s0;
if(new s0.f0) return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return s0;", code.print());
}
@@ -410,7 +410,7 @@ while(0<arg) {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
}
@Test
@@ -422,7 +422,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (const)s0;", code.print());
}
@@ -455,7 +455,7 @@ while(0) {}
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -466,7 +466,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -480,7 +480,7 @@ return 0;
a.y = 55; b.y = 66;
a.x = 33; b.x = 44;
return before*100 + p.x;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
assertEquals("1133",Eval2.eval(code,0));
assertEquals("2244",Eval2.eval(code,1));
@@ -506,7 +506,7 @@ return 0;
""";
@Test public void testMemoryAcrossNestedLoops() {
- var code = new CodeGen(NESTED_MEMORY).parse().opto();
+ var code = new CodeGen(NESTED_MEMORY).driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
for (int arg=0; arg<7; arg++) {
assertEquals("S{x="+(5L+arg)+",y="+(7L+arg*(arg+1)-(arg>=2 ? 2 : 0))+
@@ -522,7 +522,7 @@ return 0;
if (arg) { s.x = 11; s.z = 13; return s; }
s.y = 17;
return s;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
for (int arg=0; arg<2; arg++) {
assertEquals(arg==0 ? "S{x=3,y=17,z=7}" : "S{x=11,y=5,z=13}",Eval2.eval(code,arg));
@@ -539,7 +539,7 @@ return 0;
int v;
if (arg>1) v=p.x; else v=q.x;
return v;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
// The one-step safety check stops at the memory aggregate.
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
@@ -554,7 +554,7 @@ return 0;
!S !s = new S;
if (arg) s.x=arg+1; else s.x=arg+2;
return s;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
@@ -572,7 +572,7 @@ return 0;
if (arg>1) { v=p.x; p.x=41; p.y=5; }
else { v=q.x; q.x=42; }
return v;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
assertEquals("-1",Eval2.eval(code,-1));
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 3d5d2686..7c5e05f8 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -13,7 +13,7 @@ public class Chapter11Test {
s.x = 42;
while (arg > 0) { s.y = s.y + arg; arg = arg - 1; }
return s.x;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
var stop = code._stop;
assertEquals("return 42;",stop.toString());
assertEquals("42",Eval2.eval(code,5));
@@ -32,7 +32,7 @@ public class Chapter11Test {
arg = arg - 1;
}
return a;
- """).parse().opto();
+ """).driver(CodeGen.Phase.FinalizeIR);
var stop = code._stop;
int[] phis = {0};
stop.walk(n -> {
@@ -54,7 +54,7 @@ public class Chapter11Test {
// it. Keep both the worklist assertion and the final heap check.
for (int seed : new int[]{0,97,123,456}) {
var code = new CodeGen(Chapter10Test.NESTED_MEMORY,com.seaofnodes.simple.type.TypeInteger.BOT,seed,false).parse();
- code.opto();
+ code.iter().opto().iterPostOpto().finalizeIR();
assertEquals("S{x=9,y=25,z=17}",Eval2.eval(code,4));
}
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index ac3b81ec..2ef209c5 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -14,7 +14,7 @@ public class Chapter12Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -34,7 +34,7 @@ while( arg ) {
}
return head.next.i;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Region,0,1,.q);", 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();
+ code.driver(CodeGen.Phase.FinalizeIR);
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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return null;", code.print());
}
@@ -100,7 +100,7 @@ struct N { !M !next; int i; };
N n = new N { next = new M; };
return n.next;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (const)M;", code.print());
}
@@ -113,7 +113,7 @@ struct N { !M !next; int i; };
N n = new N { next = null; };
return n.next;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Type null is not of declared type *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.driver(CodeGen.Phase.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(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot '//' null",e.getMessage()); }
}
@Test public void testNullRef5() {
CodeGen code = new CodeGen("return null+42;");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot '+' null",e.getMessage()); }
}
@@ -150,7 +150,7 @@ return n.i;
struct S{};
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); 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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.LocalSched);
assertEquals("return B;", code._stop.toString());
assertEquals("B{f0=C},f1=A{f0=$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.driver(CodeGen.Phase.LocalSched);
assertEquals("return R;", code._stop.toString());
assertEquals("R{a=C{a=N},b=V},c=H}},b=K{a=$cyclic,b=D{a=O},b=W},c=I}},c=P}},c=$cyclic}", Eval2.eval(code, 0));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index 62bc1654..3301fa5e 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -35,7 +35,7 @@ while( prime <= arg ) {
}
return primeCount;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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
@@ -57,7 +57,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 2;", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("2", Eval2.eval(code, 1));
@@ -80,7 +80,7 @@ if (arg) {
}
return i;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -99,7 +99,7 @@ if (v1) {
}
return v0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return S;", code.print());
}
@@ -117,7 +117,7 @@ if (arg+1) arg= 0;
while (arg) v.f = 2;
return i;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return arg;", code.print());
}
@@ -134,7 +134,7 @@ while(1) {
}
return v;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return S;", code.print());
}
@@ -149,7 +149,7 @@ while( -arg )
while(1)
arg = arg+ptr.v;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -167,7 +167,7 @@ while (arg) {
}
return i;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -187,7 +187,7 @@ while(arg) {
}
return arg;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -205,7 +205,7 @@ if (arg) {
}
return v;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return S;", code.print());
}
@@ -224,7 +224,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
//assertEquals("return new S;", code.print());
}
@@ -241,7 +241,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -259,7 +259,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
index 6377bc14..b0cf0a91 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
@@ -20,7 +20,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]).driver(CodeGen.Phase.TypeCheck);
assertEquals(test[0],test[1],Eval2.eval(code,17));
assertEquals(test[0],test[2],Eval2.eval(code,-17));
}
@@ -28,7 +28,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;").driver(CodeGen.Phase.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));
@@ -42,7 +42,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).driver(CodeGen.Phase.TypeCheck);
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -55,7 +55,7 @@ public class Chapter14FloatTest {
"""
return 3.14;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -66,7 +66,7 @@ return 3.14;
"""
return 3.14;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3.14;", code.print());
assertEquals("3.14", Eval2.eval(code, 0));
}
@@ -83,7 +83,7 @@ while( 1 ) {
}
return guess;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,(flt)arg,(((ToFloat/Phi_guess)+Phi_guess)*0.5f));", code.print());
assertEquals("3.0", Eval2.eval(code, 9));
assertEquals("1.414213562373095", Eval2.eval(code, 2));
@@ -96,7 +96,7 @@ return guess;
flt x = arg;
return x+1==x;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return ((flt)arg==(ToFloat+1.0f));", code.print());
assertEquals("0", Eval2.eval(code, 1));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index c342eeed..b4a27c04 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -16,7 +16,7 @@ public class Chapter14Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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();
+ code.driver(CodeGen.Phase.FinalizeIR);
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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index d0a42ccf..d452fbfd 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -17,7 +17,7 @@ public class Chapter15Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return C;", code.print());
assertEquals("C{l=$cyclic}", Eval2.eval(code, 0));
}
@@ -50,7 +50,7 @@ while (i < old#) {
output[i] = 1;
return output;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return []*A?;", code.print());
assertEquals("*A {i64 !i; }?[ null,null]", Eval2.eval(code, 0));
}
@@ -131,7 +131,7 @@ else {
}
return rez;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Tree;", code.print());
assertEquals("Tree{_kids=*Tree {*[]*Tree?? !_kids; }?[ Tree{_kids=null},null]}", Eval2.eval(code, 0));
}
@@ -186,7 +186,7 @@ return root;
struct S { int a; int[] !b; };
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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();
+ code.driver(CodeGen.Phase.FinalizeIR);
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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Cannot allocate an array with length 3.14",e.getMessage()); }
}
@@ -279,7 +279,7 @@ return is[1];
int[] is = new int[arg];
return is[1];
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -353,7 +353,7 @@ else {
}
return 0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter16Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter16Test.java
index f73cbf57..d066357f 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter16Test.java
@@ -15,7 +15,7 @@ public class Chapter16Test {
"""
return 3.14;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -64,7 +64,7 @@ struct X { int x=3; };
X z = new X { x = 4; };
return z.x;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -116,7 +116,7 @@ LLI? next = head.next;
if( !next ) return 1;
return next.i;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,0,1,.i);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -141,7 +141,7 @@ struct Square {
};
return new Square;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Square;", code.print());
assertEquals("Square{side=3.0,diag=1.7320508075688772}", Eval2.eval(code, 3));
assertEquals("Square{side=4.0,diag=2.0}", Eval2.eval(code, 4));
@@ -165,7 +165,7 @@ return new Square;
""";
@Test public void testConstructorMemory() {
- var code = new CodeGen(CONSTRUCTOR_MEMORY).parse().opto();
+ var code = new CodeGen(CONSTRUCTOR_MEMORY).driver(CodeGen.Phase.FinalizeIR);
assertEquals("111249",Eval2.eval(code,0));
assertEquals("222352",Eval2.eval(code,1));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17aTest.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
index 8597b799..d03248c2 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ b/chapter24/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.driver(CodeGen.Phase.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; };
X z = new X { x=3; };
return z.x;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
assertEquals("3", Eval2.eval(code, 0));
}
@@ -40,7 +40,7 @@ struct Point { int ~x, ~y; };
Point p = new Point { x=3; y=4; };
return p;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (const)Point;", code.print());
assertEquals("Point{x=3,y=4}", Eval2.eval(code, 0));
}
@@ -53,7 +53,7 @@ struct Point { int x=3, y=4; };
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -73,7 +73,7 @@ return p;
public void testStructFinal3() {
for( String fields : new String[] { "var x; var y;", "int[] !x;", "int[] !x;" } ) {
String src = "struct Point { "+fields+" }; Point p = new Point; p.x++; return p;";
- try { new CodeGen(src).parse().opto(); fail(fields); }
+ try { new CodeGen(src).driver(CodeGen.Phase.FinalizeIR); fail(fields); }
catch( Exception e ) { assertEquals("'Point' is not fully initialized, field 'x' needs to be set in a constructor",e.getMessage()); }
}
}
@@ -86,7 +86,7 @@ Point p = new Point;
p.x++;
return p;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
}
@@ -98,7 +98,7 @@ struct Point { var x=3; var y=4; };
p.x++;
return p;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Point;", code.print());
assertEquals("Point{x=4,y=4}", Eval2.eval(code, 0));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 24dc0229..77e75449 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -15,7 +15,7 @@ public class Chapter17bTest {
"""
return 1;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",e.getMessage()); }
}
@Test public void testVar1() {
CodeGen code = new CodeGen("val d; return d;");
- try { code.parse().opto(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected =expression: ;",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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@Test public void testVar5() {
CodeGen code = new CodeGen("struct S{int x;}; !S? !s; s=new S{x=3;}; s.x++; return s.x; // Ok, no initializer so x is mutable ");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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;}; ~S s = new S{x=3;}; s.x++; return s.x; // Error initializer so x is immutable ");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.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.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
}
@@ -264,7 +264,7 @@ A x = new A {
b = new B;
};
""");
- try { code.parse().opto(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Cannot read uninitialized field 'b'",e.getMessage()); }
}
@@ -291,7 +291,7 @@ val x = 1;
return x;
"""
);
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot reassign final 'x'",e.getMessage()); }
}
@@ -320,7 +320,7 @@ return me;
CodeGen code = new CodeGen("""
return arg ? 1 : 2;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,1,2);", code.print());
assertEquals("2", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -331,7 +331,7 @@ return arg ? 1 : 2;
CodeGen code = new CodeGen("""
return arg ? 0 : arg;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -343,7 +343,7 @@ struct Bar { int x; };
var b = arg ? new Bar : null;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -356,7 +356,7 @@ struct Bar { int x; };
var b = arg ? new Bar;
return b ? b.x++ + b.x++ : -1;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,((.x<<1)+1),-1);", code.print());
assertEquals("-1", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -370,7 +370,7 @@ struct Bar { !Bar? !next; int x; };
var b = arg ? new Bar { next = (arg==2) ? new Bar{x=2;}; x=1; };
return b ? b.next ? b.next.x : b.x; // parses "b ? (b.next ? b.next.x : b.x) : 0"
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,.x,0,.x);", code.print());
assertEquals("0", Eval2.eval(code, 0));
assertEquals("1", Eval2.eval(code, 1));
@@ -383,7 +383,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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,1.0f,1.2);", code.print());
assertEquals("1.2", Eval2.eval(code, 0));
}
@@ -395,7 +395,7 @@ return f; // missing widening
struct S{};
return arg ? 7 : new S;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); }
}
@@ -408,7 +408,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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));
@@ -421,7 +421,7 @@ for( ; i<arg; i++ )
sum += i;
return sum;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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));
@@ -436,7 +436,7 @@ for( int i=0; ; i++ ) {
}
return sum;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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));
@@ -449,7 +449,7 @@ for( int i=0; i<arg; )
sum += i++;
return sum;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.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));
@@ -462,7 +462,7 @@ for( int i=0; i<arg; i++ )
sum += i;
return i;
""");
- try { code.parse().opto(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
}
@Test
@@ -470,7 +470,7 @@ return i;
CodeGen code = new CodeGen("""
for(;;arg++;) {}
""");
- try { code.parse().opto(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Syntax error, expected Unexpected code after expression: ;",e.getMessage()); }
}
@@ -486,7 +486,7 @@ struct A{
};
return new A;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return A;", code.print());
assertEquals("A{f1=null,f2=null}", Eval2.eval(code,0));
}
@@ -500,7 +500,7 @@ struct A {
};
return new A{array_of_b = new !B?[0]; }.array_of_b;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return []*B?;", code.print());
assertEquals("*B {... }?[]", Eval2.eval(code,0));
}
@@ -519,7 +519,7 @@ for( ; ptr; ptr = ptr.next )
sum += ptr.i;
return sum;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
assertEquals("45", Eval2.eval(code, 10));
}
@@ -550,7 +550,7 @@ for( int j=0; j<nprimes; j++ )
rez[j] = primes[j];
return rez;
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return []i64;", code.print());
assertEquals("i64[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 44c365dd..e26edb7e 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter24/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]).driver(CodeGen.Phase.TypeCheck);
fail("Expected incompatible return types: " + test[0]);
} catch( Parser.ParseException e ) {
assertEquals(test[1],e.getMessage());
@@ -140,7 +140,7 @@ while(arg--) {
}
return a;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Loop,1,Phi(Loop,2,Phi_a));", code._stop.toString());
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.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return null;", code._stop.toString());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -170,7 +170,7 @@ return x2;
x*x;
};
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (Parm_x(sq,i64)*x);", code._stop.toString());
//assertEquals("{ int -> int #1}", Eval2.eval(code, 3));
}
@@ -197,7 +197,7 @@ return sq(arg)+sq(3);
int cnt=1;
return { -> cnt; };
""");
- try { code.parse().opto(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
}
@@ -209,7 +209,7 @@ return { -> cnt; };
val cnt=2;
return { -> cnt; }();
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 2;", code._stop.toString());
assertEquals("2", Eval2.eval(code, 0));
}
@@ -222,7 +222,7 @@ return { -> cnt; }();
var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
return fcn(3);
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("Stop[ return #2; return (Parm_x($fun21,i64)*x); return (Parm_x($fun22,i64)<<1); ]", code._stop.toString());
assertEquals("6", Eval2.eval(code, 0));
assertEquals("9", Eval2.eval(code, 1));
@@ -232,7 +232,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.driver(CodeGen.Phase.TypeCheck);
assertEquals("Stop[ return #2; return Phi(Region,1,(Parm_x(fact,i64)*#2)); ]", code._stop.toString());
assertEquals( "1", Eval2.eval(code, 0));
assertEquals( "1", Eval2.eval(code, 1));
@@ -249,7 +249,7 @@ struct S { int i; };
val newS = { int x -> return new S { i=x; }; };
return newS(1).i;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -264,7 +264,7 @@ val f = {->1;};
val g = {->2;};
return 2;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 1;", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -342,7 +342,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().opto(); fail(); }
+ try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
}
@@ -358,7 +358,7 @@ for(;;) {
}
return 0;
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Might be null calling { i64 -> i64 #21}?",e.getMessage()); }
}
@@ -370,7 +370,7 @@ return 0;
val f = { int i, int j -> return i+j; };
return f();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Expecting 2 arguments, but found 0",e.getMessage()); }
}
@@ -388,7 +388,7 @@ for(;;) {
if (s) return s.x;
}
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*S'",e.getMessage()); }
}
@@ -401,7 +401,7 @@ val g = { ->
};
return g();
""");
- try { code.parse().opto().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
}
@@ -415,7 +415,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();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("Stop[ return is_even( arg); return Phi(Region,Phi(Region,is_even( ((Parm_x(is_even,int,arg,Sub)-1)-1)),0),1); ]", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
assertEquals("0", Eval2.eval(code, 1));
@@ -437,7 +437,7 @@ for(;;) {
if (s) return s.f;
}
""");
- code.parse().opto().typeCheck().GCM();
+ code.driver(CodeGen.Phase.Schedule);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 0));
}
@@ -489,7 +489,7 @@ return p.coffee_count;
""";
@Test public void testCallMemory() {
- var code = new CodeGen(CALL_MEMORY).parse().opto();
+ var code = new CodeGen(CALL_MEMORY).driver(CodeGen.Phase.FinalizeIR);
int[] calls={0};
code._stop.walk(n -> { if(n instanceof CallNode) calls[0]++; return null; });
assertEquals(2,calls[0]); // Keep real calls so this checks scheduling across them.
@@ -518,7 +518,7 @@ return p.coffee_count;
""";
@Test public void testRecursiveMemory() {
- var code = new CodeGen(RECURSIVE_MEMORY).parse().opto();
+ var code = new CodeGen(RECURSIVE_MEMORY).driver(CodeGen.Phase.FinalizeIR);
for(int n=0; n<7; n++)
assertEquals(Long.toString((10L*(n+1)+n*(n+1)/2)*100+10+n),Eval2.eval(code,n));
}
@@ -536,7 +536,7 @@ return p.coffee_count;
""";
@Test public void testInlineAllocationMemory() {
- var code = new CodeGen(INLINE_MEMORY).parse().opto();
+ var code = new CodeGen(INLINE_MEMORY).driver(CodeGen.Phase.FinalizeIR);
assertNull(code._stop.walk(n -> n instanceof CallNode ? n : null));
assertEquals("400944",Eval2.eval(code,0));
assertEquals("420946",Eval2.eval(code,2));
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 8a840787..303cfda2 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -39,7 +39,7 @@ public class Chapter19Test {
CodeGen code = new CodeGen("""
return 0;
""");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -79,7 +79,7 @@ val _hashCodeString = { !String self ->
return hash;
};
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck().GCM().localSched();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
assertEquals("Stop[ return Phi(Region,1,0,0,1); return Phi(Region,._hashCode,Phi(Region,123456789,Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code._stop.toString());
//assertEquals("-4898613127354160978", Eval2.eval(code, 2));
}
@@ -433,11 +433,11 @@ return sq(arg) + sq(3);
for( String cpu : new String[]{"x86_64_v2","riscv","arm"} )
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY} ) {
- var code = new CodeGen(src).parse().opto().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
var aliases = memoryPhis(code._stop);
- code.loopTree().instSelect(cpu,"SystemV");
+ code.loopTree().driver(CodeGen.Phase.Select,cpu,"SystemV");
assertEquals(aliases,memoryPhis(code._stop));
- code.GCM().localSched();
+ code.driver(CodeGen.Phase.Schedule).localSched();
code._stop.walk(n -> {
if( !(n instanceof CFGNode) && !(n instanceof ProjNode) )
assertTrue(n.in(0) instanceof CFGNode);
@@ -489,7 +489,7 @@ return sq(arg) + sq(3);
int before = p.x + arg;
a.x=33; b.x=44;
return before;
- """).parse().opto().typeCheck().loopTree().instSelect("x86_64_v2","SystemV").GCM().localSched();
+ """).driver(CodeGen.Phase.LocalSched,"x86_64_v2","SystemV");
var read = (MemOpNode)code._stop.walk(n ->
n instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.AddMemX86 ? n : null);
assertNotNull(read);
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index 4ca1dbda..ba93a440 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -11,6 +11,10 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter20Test {
+ @Test public void testFlagSpills() throws Exception {
+ com.seaofnodes.simple.codegen.FlagSpillTestSupport.run();
+ }
+
@Test public void testAllocatorTopPhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.topPhi(); }
@Test public void testNeverReturnAllocation() {
@@ -71,7 +75,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,com.seaofnodes.simple.type.TypeInteger.BOT,seed,true).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src,com.seaofnodes.simple.type.TypeInteger.BOT,seed,true).driver(CodeGen.Phase.TypeCheck);
assertEquals("seed "+seed, "123", Eval2.eval(code,0));
}
}
@@ -96,7 +100,7 @@ return f(s);
@Test
public void testJig() {
CodeGen code = new CodeGen("return 0;");
- code.parse().opto().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code._stop.toString());
assertEquals("0", Eval2.eval(code, 2));
}
@@ -409,8 +413,7 @@ return a;
for( String src : new String[]{Chapter10Test.NESTED_MEMORY,Chapter16Test.CONSTRUCTOR_MEMORY,
Chapter18Test.CALL_MEMORY,Chapter18Test.RECURSIVE_MEMORY,
"int[] !a = new int[3]; a[arg]=arg; return a[0]+1;"} ) {
- var code = new CodeGen(src).parse().opto().typeCheck().loopTree()
- .instSelect(cpu,"SystemV").GCM().localSched().regAlloc();
+ var code = new CodeGen(src).driver(CodeGen.Phase.RegAlloc,cpu,"SystemV");
com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
code._stop.walk(n -> {
// Whole memory, precise Phis, and memory projections need no register.
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 5b2c3a54..1bf65cfd 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -29,7 +29,7 @@ public class Chapter21Test {
else if(i==4) b.x=40;
}
return sum;
- """).parse().opto();
+ """).driver(CodeGen.Phase.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));
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index c77221bf..3ff8dc09 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter22Test.java
@@ -126,7 +126,7 @@ public class Chapter22Test {
else assertEquals(42,cpu.regs[0]);
}
}
- CodeGen unused = new CodeGen("({->42;}); return 0;").parse().opto();
+ CodeGen unused = new CodeGen("({->42;}); return 0;").driver(CodeGen.Phase.FinalizeIR);
assertEquals(1,unused._stop.nIns());
}
@@ -172,7 +172,7 @@ public class Chapter22Test {
@Test public void testSubZeroTypeError() {
try {
- new CodeGen("return null-0;").parse().opto().typeCheck();
+ new CodeGen("return null-0;").driver(CodeGen.Phase.TypeCheck);
fail("Subtraction must reject null even when the other operand is zero");
} catch( Parser.ParseException e ) {
assertEquals("Cannot '-' null",e.getMessage());
@@ -356,7 +356,7 @@ bb.c = cc;
val x = 5; // aa.az; // Error to self-define forward ref
return cc.cz;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 25;", code._stop.toString());
assertEquals("25", Eval2.eval(code, 0));
}
@@ -400,7 +400,7 @@ val sum = { i32[~] is -> // final array
};
return sum(is);
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code._stop.toString());
assertEquals("14", Eval2.eval(code, 0));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java
index 884e77e5..22be3af1 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -196,7 +196,7 @@ var apply = { i64 x, { i64 -> u32 } fcn ->
return 0;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("[97-117][ 116,114,117,101]", Eval2.eval(code, 0));
// Evaluate on RISC5 emulator
@@ -360,7 +360,7 @@ struct Scan {
return Scan.peek(s,'q');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Region,0,1);", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "Win64" ,23,null);
@@ -385,7 +385,7 @@ struct Scan {
return s.skip().x;
""";
- try { new CodeGen(src).parse().opto().typeCheck(); fail(); }
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa")); }
};
@@ -412,7 +412,7 @@ struct Scan {
return s.require('[');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return (.[]==91);", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "Win64" ,4,null);
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter24Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter24Test.java
index 9a657855..8cd300c2 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter24Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter24Test.java
@@ -142,7 +142,7 @@ if( ptr && ptr.fld ) {
return 0;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("[97-117][ 116,114,117,101]", Eval2.eval(code, 0));
// Evaluate on RISC5 emulator
@@ -627,7 +627,7 @@ struct Scan {
return Scan.peek(s,'q');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return Phi(Region,0,1);", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "Win64" ,23,null);
@@ -652,7 +652,7 @@ struct Scan {
return s.skip().x;
""";
- try { new CodeGen(src).parse().opto().typeCheck(); fail(); }
+ try { new CodeGen(src).driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { org.junit.Assert.assertTrue(e.getMessage().startsWith("Argument #0 isa")); }
};
@@ -679,7 +679,7 @@ struct Scan {
return s.require('[');
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return (.[]==91);", code._stop.toString());
assertEquals("1", Eval2.eval(code, 0));
testCPU(src,"x86_64_v2", "Win64" ,4,null);
@@ -699,7 +699,7 @@ while (cnt) {
}
return x;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 1;", code._stop.toString());
}
@@ -714,7 +714,7 @@ while( cnt-- )
x = 2-x;
return x;
""";
- CodeGen code = new CodeGen(src).parse().opto().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 1;", code._stop.toString());
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Compile.java b/chapter24/src/test/java/com/seaofnodes/simple/Compile.java
index 088055f9..f3ca1905 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Compile.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Compile.java
@@ -16,7 +16,7 @@ public class Compile {
private static Process run(String simple, String c) throws IOException, InterruptedException {
boolean USE_WSL = System.getProperty("os.name").startsWith("Windows");
CodeGen code = new CodeGen(simple);
- code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched().regAlloc().encode();
+ code.driver(CodeGen.Phase.Encoding,"x86_64_v2","SystemV");
if (c != null) for (var n:code._start._outputs) if (n instanceof FunNode f && f._name.equals("main")) f._name = "simple_main";
code.exportELF("test.o");
var params = new ArrayList<String>();
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter24/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
index 48f739cf..e2d822d0 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ b/chapter24/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).driver(CodeGen.Phase.TypeCheck);
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/LiteralTest.java b/chapter24/src/test/java/com/seaofnodes/simple/LiteralTest.java
index 09ee30f5..447ef46e 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/LiteralTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/LiteralTest.java
@@ -20,7 +20,7 @@ public class LiteralTest {
}
private static void assertString(String text, byte[] expected) {
- var code = new CodeGen("return \"" + text + "\";").parse().opto().typeCheck();
+ var code = new CodeGen("return \"" + text + "\";").driver(CodeGen.Phase.TypeCheck);
TypeMemPtr ptr = (TypeMemPtr)code.expr()._type;
TypeConAryB bytes = (TypeConAryB)ptr._obj.field("[]")._t;
assertArrayEquals(text, expected, bytes._ary);
@@ -33,7 +33,7 @@ public class LiteralTest {
}
private static void assertCharacter(String text, int expected) {
- var code = new CodeGen("return '" + text + "';").parse().opto().typeCheck();
+ var code = new CodeGen("return '" + text + "';").driver(CodeGen.Phase.TypeCheck);
assertEquals(text, expected, ((TypeInteger)code.expr()._type).value());
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Main.java b/chapter24/src/test/java/com/seaofnodes/simple/Main.java
index 589c043b..ca1d0b5b 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Main.java
+++ b/chapter24/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.driver(CodeGen.Phase.LocalSched);
System.out.println(code._stop);
System.out.println(Eval2.eval(code,arg,100000));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java b/chapter24/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
index e97194af..397d644c 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
@@ -66,7 +66,7 @@ public class MemoryEncodingTest {
"""
};
for( int p=0; p<sources.length; p++ ) {
- CodeGen code=new CodeGen(sources[p]).parse().opto();
+ CodeGen code=new CodeGen(sources[p]).driver(CodeGen.Phase.FinalizeIR);
if( p==0 ) assertNull(code._stop.walk(n ->
n instanceof LoadNode ld && ld.ptr() instanceof ProjNode ptr &&
ptr.in(0) instanceof NewNode nn &&
@@ -159,7 +159,7 @@ public class MemoryEncodingTest {
}
return null;
}));
- code.instSelect(target,"SystemV").GCM().localSched().regAlloc();
+ code.driver(CodeGen.Phase.RegAlloc,target,"SystemV");
RegAllocTestSupport.checkRegisters(code);
code.encode();
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter24/src/test/java/com/seaofnodes/simple/MutabilityTest.java
index 7ae80314..51e31a17 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ b/chapter24/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).driver(CodeGen.Phase.TypeCheck);
assertEquals(result,Eval2.eval(code,0));
}
private static void bad(String src) {
try {
- new CodeGen(src).parse().opto().typeCheck();
+ new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
} catch( RuntimeException expected ) {
String msg = expected.getMessage();
assertTrue("Expected a permission error, got: "+expected,
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter24/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..899f42fe
--- /dev/null
+++ b/chapter24/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,56 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testStopAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.Iter);
+ assertEquals(Phase.Iter,code._phase);
+ assertEquals("Parse Iter",String.join(" ",phases));
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // Repeated requests do not rerun phases.
+ code.driver(Phase.LoopTree);
+ assertEquals(Phase.LoopTree,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter Opto IterPostOpto FinalizeIR TypeCheck LoopTree Schedule LocalSched",String.join(" ",phases));
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testResumeMachineCompilation() {
+ CodeGen staged = new CodeGen(SRC).driver(Phase.TypeCheck);
+ staged.driver(Phase.Select,"x86_64_v2","SystemV");
+ staged.driver(Phase.Encoding,"x86_64_v2","SystemV");
+ byte[] bits = staged._encoding._bits.toByteArray();
+ CodeGen direct = new CodeGen(SRC).driver(Phase.Encoding,"x86_64_v2","SystemV");
+ assertArrayEquals(bits,direct._encoding._bits.toByteArray());
+ }
+
+ @Test public void testOptoRequiresIter() {
+ CodeGen code = new CodeGen(SRC).parse();
+ try {
+ code.opto();
+ fail("Opto must not implicitly run Iter");
+ } catch( AssertionError expected ) {
+ assertEquals("Cannot run Opto after Parse",expected.getMessage());
+ }
+ code.iter().opto();
+ assertEquals(Phase.Opto,code._phase);
+ code.driver(Phase.LocalSched);
+ assertEquals("45",Eval2.eval(code,10));
+ }
+}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/chapter24/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 3b19857a..6156700d 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -11,6 +11,26 @@ import static org.junit.Assert.assertEquals;
public class SchedulerTest {
+ @Test public void testMemoryPhiAcrossSiblingBranch() {
+ // The inner memory Phi defines the load's memory. The outer Phi's
+ // other input comes from a branch not dominated by that definition.
+ String src = """
+ struct S { int x; }; !S !a=new S; !S !b=new S;
+ a.x=10; b.x=20; !S !p=a; if(arg>3) p=b;
+ int result=0;
+ if(arg>0) {
+ if(arg>2) p.x=30; else p.x=40;
+ result=a.x;
+ }
+ return result;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ long[] expected = {0,40,40,30,10,10};
+ for( int arg=0; arg<expected.length; arg++ )
+ assertEquals(Long.toString(expected[arg]),Eval2.eval(code,arg));
+ }
+
+
private static void assertObj(Object obj, String name, Object... fields) {
assertEquals(Evaluator.Obj.class, obj.getClass());
var o = (Evaluator.Obj)obj;
@@ -31,7 +51,7 @@ int i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse();//.opto();
+ code.parse();//.iter().opto().iterPostOpto().finalizeIR();
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(1, 10));
}
@@ -46,7 +66,7 @@ struct S {
if(arg) v0.f=1;
return v0;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
var eval = new Evaluator(code._stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -65,7 +85,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- code.parse().opto();
+ code.driver(CodeGen.Phase.FinalizeIR);
var eval = new Evaluator(code._stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Simple.java b/chapter24/src/test/java/com/seaofnodes/simple/Simple.java
index 18804ebd..20d6fe71 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Simple.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.util.Utils;
import java.io.BufferedReader;
@@ -13,20 +14,23 @@ import java.util.stream.Collectors;
public class Simple {
public static final String PORTS = "com.seaofnodes.simple.node.cpus";
- static final int DUMP_AFTER_PARSE = 1<<0;
- static final int DUMP_AFTER_OPTO = 1<<1;
- static final int DUMP_AFTER_TYPE_CHECK = 1<<2;
- static final int DUMP_AFTER_LOOP_TREE = 1<<3;
- static final int DUMP_AFTER_SELECT = 1<<4;
- static final int DUMP_AFTER_GCM = 1<<5;
- static final int DUMP_AFTER_LOCAL_SCHED = 1<<6;
- static final int DUMP_AFTER_REG_ALLOC = 1<<7;
- static final int DUMP_AFTER_ENCODE = 1<<8;
+ static final int DUMP_AFTER_PARSE = 1<<Phase.Parse.ordinal();
+ static final int DUMP_AFTER_ITER = 1<<Phase.Iter.ordinal();
+ static final int DUMP_AFTER_OPTO = 1<<Phase.Opto.ordinal();
+ static final int DUMP_AFTER_ITER_POST_OPTO = 1<<Phase.IterPostOpto.ordinal();
+ static final int DUMP_AFTER_FINALIZE_IR = 1<<Phase.FinalizeIR.ordinal();
+ static final int DUMP_AFTER_TYPE_CHECK = 1<<Phase.TypeCheck.ordinal();
+ static final int DUMP_AFTER_LOOP_TREE = 1<<Phase.LoopTree.ordinal();
+ static final int DUMP_AFTER_SELECT = 1<<Phase.Select.ordinal();
+ static final int DUMP_AFTER_GCM = 1<<Phase.Schedule.ordinal();
+ static final int DUMP_AFTER_LOCAL_SCHED = 1<<Phase.LocalSched.ordinal();
+ static final int DUMP_AFTER_REG_ALLOC = 1<<Phase.RegAlloc.ordinal();
+ static final int DUMP_AFTER_ENCODE = 1<<Phase.Encoding.ordinal();
static final int DUMP_FINAL = 1<<16;
static final int DUMP_PASS_NAME = 1<<30;
- static final int DUMP_AFTER_ALL = 0xff | DUMP_PASS_NAME;
+ static final int DUMP_AFTER_ALL = ((1<<Phase.values().length)-1) | DUMP_PASS_NAME;
static final int DUMP_DISASSEMBLE = 1<<31;
@@ -36,15 +40,19 @@ public class Simple {
simple [options] <input-file> ...args...
Options:
--dump - dump final intermediate representation
- --dump-after-parse - dump intermediate representation after parse
- --dump-after-opto - dump intermediate representation after opto pass
- --dump-after-type-check - dump intermediate representation after type check pass
- --dump-after-select - dump intermediate representation after instrution selection pass
- --dump-after-gcm - dump intermediate representation after GCM pass
- --dump-after-local-sched - dump intermediate representation after local scheduling pass
- --dump-after-reg-alloc - dump intermediate representation after register allocation pass
- --dump-after-encode - dump intermediate representation after encoding pass
- --dump-after-all - dump intermediate representation after all passes
+ --dump-after-parse - dump intermediate representation after parsing
+ --dump-after-iter - dump intermediate representation after Iter
+ --dump-after-opto - dump intermediate representation after Opto
+ --dump-after-iter-post-opto - dump intermediate representation after IterPostOpto
+ --dump-after-finalize-ir - dump intermediate representation after FinalizeIR
+ --dump-after-type-check - dump intermediate representation after type checking
+ --dump-after-loop-tree - dump intermediate representation after loop-tree construction
+ --dump-after-select - dump intermediate representation after instruction selection
+ --dump-after-gcm - dump intermediate representation after GCM
+ --dump-after-local-sched - dump intermediate representation after local scheduling
+ --dump-after-reg-alloc - dump intermediate representation after register allocation
+ --dump-after-encode - dump intermediate representation after encoding
+ --dump-after-all - dump intermediate representation after all passes
-S - dump generated assembler code
--eval - evaluate the compiled code in emulator
--run - run the compiled code natively; this is the default
@@ -74,50 +82,19 @@ Options:
}
static void dump(CodeGen code, int dump, int pass) {
- if ((dump & pass) != 0) {
- if ((dump & DUMP_PASS_NAME) != 0) {
- System.err.println(switch (pass) {
- case DUMP_AFTER_PARSE -> "After Parse:";
- case DUMP_AFTER_OPTO -> "After OPTO:";
- case DUMP_AFTER_TYPE_CHECK -> "After Type Check:";
- case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:";
- case DUMP_AFTER_SELECT -> "After Code Selection:";
- case DUMP_AFTER_GCM -> "After GCM:";
- case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:";
- case DUMP_AFTER_REG_ALLOC -> "After Register Allocation:";
- case DUMP_AFTER_ENCODE -> "After Encoding:";
- case DUMP_FINAL -> "Final:";
- default -> throw Utils.TODO();
- });
- }
-
- System.err.println(IRPrinter.prettyPrint(code._stop, 9999));
-
- }
+ if( (dump & pass)==0 ) return;
+ if( (dump & DUMP_PASS_NAME)!=0 ) System.err.println("Final:");
+ System.err.println(code);
}
static void print_compilation_times(CodeGen code) {
- double t, total = 0;
-
- total += t = code._tParse / 1e3;
- System.out.printf( "Parsing Time: %.3f sec%n", t);
- total += t = code._tOpto / 1e3;
- System.out.printf( "Optimization Time: %.3f sec%n", t);
- total += t = code._tTypeCheck / 1e3;
- System.out.printf( "Type Checking Time: %.3f sec%n", t);
- total += t = code._tLoopTree / 1e3;
- System.out.printf( "Loop Tree Time: %.3f sec%n", t);
- total += t = code._tInsSel / 1e3;
- System.out.printf( "Code Selection Time: %.3f sec%n", t);
- total += t = code._tGCM / 1e3;
- System.out.printf( "GCM Time: %.3f sec%n", t);
- total += t = code._tLocal / 1e3;
- System.out.printf( "Local Scheduling Time: %.3f sec%n", t);
- total += t = code._tRegAlloc / 1e3;
- System.out.printf( "Register Allocation Time: %.3f sec%n", t);
- total += t = code._tEncode / 1e3;
- System.out.printf( "Encoding Time: %.3f sec%n", t);
- System.out.printf( "TOTAL COMPILATION TIME: %.3f sec%n", total);
+ long total=0;
+ for( Phase phase : Phase.values() ) {
+ long time=code._times[phase.ordinal()];
+ total+=time;
+ System.out.printf("%-24s %.3f sec%n",phase+" Time:",time/1e3);
+ }
+ System.out.printf("TOTAL COMPILATION TIME: %.3f sec%n",total/1e3);
}
static String getInput() throws IOException {
@@ -150,16 +127,19 @@ Options:
switch (arg) {
case "-": input_filename=""; break;
case "--dump": dump |= DUMP_FINAL; break;
- case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
- case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
- case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
- case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
- case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
- case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
- case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
- case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
- case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
- case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
+ case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break;
+ case "--dump-after-finalize-ir": dump |= DUMP_AFTER_FINALIZE_IR; break;
+ case "--dump-after-iter-post-opto": dump |= DUMP_AFTER_ITER_POST_OPTO; break;
+ case "--dump-after-iter": dump |= DUMP_AFTER_ITER; break;
+ case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break;
+ case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break;
+ case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break;
+ case "--dump-after-select": dump |= DUMP_AFTER_SELECT; break;
+ case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break;
+ case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break;
+ case "--dump-after-reg-alloc": dump |= DUMP_AFTER_REG_ALLOC; break;
+ case "--dump-after-encode": dump |= DUMP_AFTER_ENCODE; break;
+ case "--dump-after-all": dump |= DUMP_AFTER_ALL; break;
case "-S": dump |= DUMP_DISASSEMBLE; break;
case "--eval": do_eval = true ; do_run = false; break;
case "--run": do_run = true ; break;
@@ -198,38 +178,9 @@ Options:
// Compilation pipeline
CodeGen code = new CodeGen(src);
- code.parse();
- dump(code, dump, DUMP_AFTER_PARSE);
-
- code.opto();
- dump(code, dump, DUMP_AFTER_OPTO);
-
- code.typeCheck();
- dump(code, dump, DUMP_AFTER_TYPE_CHECK);
-
- code.loopTree();
- dump(code, dump, DUMP_AFTER_LOOP_TREE);
-
- if (do_codegen) {
- code.instSelect(cpu, abi, PORTS);
- dump(code, dump, DUMP_AFTER_SELECT);
- }
-
- code.GCM();
- dump(code, dump, DUMP_AFTER_GCM);
-
- code.localSched();
- dump(code, dump, DUMP_AFTER_LOCAL_SCHED);
-
- if (do_codegen) {
- code.regAlloc();
- dump(code, dump, DUMP_AFTER_REG_ALLOC);
-
- code.encode();
- dump(code, dump, DUMP_AFTER_ENCODE);
-
- code.exportELF(base+".o");
- }
+ code.driver(do_codegen ? Phase.Encoding : Phase.LocalSched,
+ do_codegen ? cpu : null, abi, dump);
+ if( do_codegen ) code.exportELF(base+".o");
dump(code, dump, DUMP_FINAL);
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter24/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
new file mode 100644
index 00000000..00d86c63
--- /dev/null
+++ b/chapter24/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
@@ -0,0 +1,145 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.simple.TestC;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+// Force overlapping flags, then force every GPR unavailable across the gap.
+public class FlagSpillTestSupport {
+ private static class Op extends MachConcreteNode {
+ final RegMask _def, _use, _kill;
+ Op(RegMask def,RegMask use,RegMask kill,Node... ins) {
+ super(ins); _def=def; _use=use; _kill=kill; _type=TypeInteger.BOT;
+ }
+ @Override public String op() { return "flagsProbe"; }
+ @Override public RegMask outregmap() { return _def; }
+ @Override public RegMask regmap(int i) { return _use; }
+ @Override public RegMask killmap() { return _kill; }
+ @Override public void encoding(Encoding enc) { throw new AssertionError(); }
+ }
+
+ public static void run() throws Exception {
+ StringBuilder asm = new StringBuilder(".text\n");
+ for( boolean isArm : new boolean[]{false,true} )
+ for( boolean stack : new boolean[]{false,true} ) {
+ CodeGen code = new CodeGen("");
+ code._callingConv="SystemV";
+ code._mach=isArm ? new arm(code) : new x86_64_v2(code);
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ code._cfg.add(block);
+ int flagsReg=isArm ? 64 : 32, firstStack=flagsReg+1;
+ RegMask flags = new RegMask(flagsReg);
+ Op def = new Op(flags,null,null,block);
+ RegMask killMask = stack ? (isArm ? new RegMask(0x7fffffffL,1L) : new RegMask(0x1ffffffefL)) : flags;
+ Op kill = new Op(null,null,killMask,block);
+ new Op(null,flags,null,block,def);
+ RegAlloc alloc = new RegAlloc(code);
+ alloc.regAlloc();
+
+ int splits=0, slots=0;
+ for( Node n : block._outputs ) if( n instanceof SplitNode split ) {
+ int dst=alloc.regnum(n), src=alloc.regnum(n.in(1));
+ assertTrue(split.outregmap().test(dst));
+ assertTrue(split.regmap(1).test(src));
+ for( int fp=isArm ? 32 : 16; fp<flagsReg; fp++ ) {
+ assertFalse("FP output allowed",split.outregmap().test(fp));
+ assertFalse("FP input allowed",split.regmap(1).test(fp));
+ }
+ if( dst>=firstStack || src>=firstStack ) slots++;
+ splits++;
+ }
+ assertTrue(splits>=2);
+ assertEquals(stack,slots!=0);
+
+ code._regAlloc=alloc;
+ FunNode frame = new FunNode(null,null,"flags",(Node)null);
+ Encoding save = new Encoding(code), restore = new Encoding(code);
+ save._fun=restore._fun=frame;
+ Encoding enc = save;
+ for( Node n : block._outputs ) {
+ if( n==kill ) enc=restore;
+ if( n instanceof SplitNode split ) split.encoding(enc);
+ }
+ if( isArm ) {
+ for( int bits=0; bits<16; bits++ ) {
+ EvalArm64 cpu = new EvalArm64(new byte[1024],512);
+ System.arraycopy(save._bits.buf(),0,cpu._buf,0,save._bits.size());
+ System.arraycopy(restore._bits.buf(),0,cpu._buf,save._bits.size(),restore._bits.size());
+ cpu.N=(bits&8)!=0; cpu.Z=(bits&4)!=0; cpu.C=(bits&2)!=0; cpu.V=(bits&1)!=0;
+ assertEquals(0,cpu.step(save._bits.size()/4));
+ cpu.N=cpu.Z=cpu.C=cpu.V=false;
+ if( stack ) for( int r=0; r<31; r++ ) cpu.regs[r]=0;
+ assertEquals(0,cpu.step(restore._bits.size()/4));
+ assertEquals((bits&8)!=0,cpu.N); assertEquals((bits&4)!=0,cpu.Z);
+ assertEquals((bits&2)!=0,cpu.C); assertEquals((bits&1)!=0,cpu.V);
+ assertEquals(512,cpu.regs[31]);
+ }
+ } else {
+ Encoding body = new Encoding(code);
+ int[] preserved={3,5,6,7,12,13,14,15};
+ for( int r : preserved ) pushPop(body,r,false);
+ X86.imm(body,0x81,5,4,16); // Reserve spill storage.
+ X86.flags(body,TestC.OS.startsWith("Windows") ? 1 : 7,true);
+ bytes(body,save);
+ if( stack ) {
+ for( int r=0; r<16; r++ ) if( r!=4 ) X86.constant(body,r,0);
+ } else X86.reg(body,0x39,0,0); // CMP rax,rax clobbers only flags.
+ bytes(body,restore);
+ X86.flags(body,0,false); // Return the restored flags.
+ X86.imm(body,0x81,0,4,16);
+ for( int i=preserved.length-1; i>=0; i-- ) pushPop(body,preserved[i],true);
+ X86.ret(body);
+ String name=stack ? "flags_stack" : "flags_reg";
+ asm.append(".globl ").append(name).append('\n').append(name).append(":\n.byte ");
+ for( int i=0; i<body._bits.size(); i++ ) {
+ if( i!=0 ) asm.append(',');
+ asm.append(body._bits.buf()[i]&255);
+ }
+ asm.append('\n');
+ }
+ }
+ Path dir=Files.createTempDirectory(Path.of("build/objs"),"flag_spills_");
+ Files.writeString(dir.resolve("flags.s"),asm);
+ Files.writeString(dir.resolve("flags.c"),"""
+ typedef unsigned long long u64;
+ u64 __attribute__((CALL_CONV)) flags_reg(u64);
+ u64 __attribute__((CALL_CONV)) flags_stack(u64);
+ int main(void) {
+ const int positions[]={0,2,4,6,7,11};
+ for(int bits=0; bits<64; bits++) {
+ u64 flags=0x202;
+ for(int i=0;i<6;i++) if(bits&(1<<i)) flags|=1ULL<<positions[i];
+ if((flags_reg(flags)&0x8d5)!=(flags&0x8d5)) return 1;
+ if((flags_stack(flags)&0x8d5)!=(flags&0x8d5)) return 2;
+ }
+ return 0;
+ }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "flags.exe" : "flags").toString();
+ exec("gcc","-DCALL_CONV="+(TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"),
+ "-o",exe,dir.resolve("flags.c").toString(),dir.resolve("flags.s").toString());
+ exec(exe);
+ }
+
+ private static void exec(String... command) throws Exception {
+ Process process = new ProcessBuilder(command).redirectErrorStream(true).start();
+ String output = new String(process.getInputStream().readAllBytes(),java.nio.charset.StandardCharsets.UTF_8);
+ assertEquals(String.join(" ",command)+"\n"+output,0,process.waitFor());
+ }
+
+ private static void bytes(Encoding dst,Encoding src) {
+ for( int i=0; i<src._bits.size(); i++ ) dst.add1(src._bits.buf()[i]&255);
+ }
+ private static void pushPop(Encoding enc,int reg,boolean pop) {
+ if( reg>=8 ) enc.add1(0x41);
+ enc.add1((pop ? 0x58 : 0x50)+(reg&7));
+ }
+}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter24/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 1ffdb3d0..0304c6a1 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -22,6 +22,11 @@ public class RegAllocTestSupport {
@Override public Node copy() { return new Op(_def,_use,_kill,_clone,(Node)null); }
@Override public void encoding(Encoding enc) { throw new AssertionError(); }
}
+ private static SplitNode testSplit(CodeGen code, MachNode def) {
+ LRG lrg = new LRG((short)0);
+ lrg._machDef = def;
+ return code._mach.split("test",(byte)0,lrg);
+ }
private static CodeGen graph() {
CodeGen code = new CodeGen("");
code._mach = new x86_64_v2(code);
@@ -221,11 +226,11 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode hi = code._mach.split("test",(byte)0,null);
+ SplitNode hi = testSplit(code,def);
hi.setDef(0,code._start); hi.setDef(1,def);
alloc.newLRG(hi)._reg=1;
new Op(null,null,killed ? A : null,false,code._start);
- SplitNode lo = code._mach.split("test",(byte)0,null);
+ SplitNode lo = testSplit(code,hi);
lo.setDef(0,code._start); lo.setDef(1,hi);
alloc.newLRG(lo)._reg=0;
Op use = new Op(null,A,null,false,code._start,lo);
@@ -239,7 +244,7 @@ public class RegAllocTestSupport {
uncolored.newLRG(def0)._mask=A;
Op fixed = new Op(A,null,null,false,pre._start);
uncolored.newLRG(fixed)._mask=A;
- SplitNode copy = pre._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(pre,def0);
copy.setDef(0,pre._start); copy.setDef(1,def0);
assertFalse("A fixed definition clobbers even before coloring",uncolored.sameBlockNoClobber(copy));
}
@@ -279,7 +284,7 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def)._reg=0;
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
alloc.newLRG(copy)._reg=1;
if( kind==3 ) new Op(null,null,A,false,code._start);
@@ -302,7 +307,7 @@ public class RegAllocTestSupport {
CodeGen code = graph();
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(new RegMask(3L),null,null,false,code._start);
- SplitNode copy = code._mach.split("test",(byte)0,null);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
Op use = new Op(null,new RegMask(3L),null,false,code._start,copy);
LRG from = alloc.newLRG(def), to = alloc.newLRG(copy);
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
index b29eb2c3..af4305c0 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ b/chapter24/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/chapter25/README.ja.md b/chapter25/README.ja.md
index 989161eb..b2ef41fb 100644
--- a/chapter25/README.ja.md
+++ b/chapter25/README.ja.md
@@ -74,7 +74,7 @@ return new Box(new i64[1]);
宣言で割り当て補助関数を生成します。これは割り当てを行い、隠れた `<init>` とユーザーコンストラクタを順に呼び、`EscapeNode` を通じて初期化済みの非公開メモリを公開します。パーサーの `Var` メタデータは、早すぎるフィールド読み取りやコンストラクタ出口での未初期化を検出します。Store の幅は保存する値ではなく、対象の宣言から得ます。
-ファイルレベルのコードは、インスタンス初期化とは異なるクラス初期化子(`<clinit>`)で表します。[遅延初期化の設計(英語)](initialization.md) は、一度だけ実行するためのチェック、モジュール間の非循環な依存関係、シリアライズの対応を提案しています。現在の実装すべてを記述する仕様ではありません。[ロードマップ](ROADMAP.ja.md) にも追加の提案があります。
+ファイルレベルのコードは、インスタンス初期化とは異なるクラス初期化子(`<clinit>`)で表します。遅延初期化と循環検査については[英語版の説明](README.md#lazy-class-initialization)を参照してください。今後の計画は[英語版ロードマップ](ROADMAP.md#class-initialization-and-compiler-follow-ups)にあります。
## システムライブラリと例
@@ -98,7 +98,7 @@ return new Box(new i64[1]);
第21章の隣接コピー転送も引き継ぎます。次の命令だけで使うコピーは、オペランドがコピー元レジスタを受け入れ、命令の結果に結び付けられていなければ除去します。
-以下は、必須の `cs` フィールドに明示的なコンストラクタを与えて保存済み String ソース3件を修正した後の、完全な2026-10-02再実行です。過去の212項目すべてが成功します。これはフィクスチャ調整と最近のコンパイラ修正後の新たな基準値を確立します。以前の監査との割り当て器だけの比較ではありません。[バックポート記録](../docs/chapter-backports.md) に古い部分比較を残しています。
+以下は、必須の `cs` フィールドに明示的なコンストラクタを与えて保存済み String ソース3件を修正した後の、完全な2026-10-02再実行です。過去の212項目すべてが成功します。これはフィクスチャ調整と最近のコンパイラ修正後の新たな基準値を確立します。以前の監査との割り当て器だけの比較ではありません。
`make spill-stats` を実行してください。各行は **第25章のコンパイラ** を使います。最初の5行は以前の212のプログラム/対象項目を、この章の構文とライブラリ構成に適応して保持しています。[フィクスチャノート](src/test/java/com/seaofnodes/simple/spill/README.ja.md) にその調整と測定の制限を記載しています。最適化器のシードは123です。最後の行には、既存シードで記録した `Chapter25Test` の13割り当てと、ドライバのシード456での新規システムライブラリコンパイルを含みます。`sys.o` を再利用して、この大きな処理対象を測定から消してはいけません。この結果は Windows x86-64 のものです。以前の群には RISC-V/ARM SystemV と x86 SystemV も含みます。報告器は対象ごとの合計も表示します。
diff --git a/chapter25/README.md b/chapter25/README.md
index 4029827f..8e51c557 100644
--- a/chapter25/README.md
+++ b/chapter25/README.md
@@ -10,20 +10,6 @@ their types and ideal IR into later compilations, and links native programs
against a separately built system library. It also contains a substantial
parser, type, and memory-SSA redesign to support incomplete information.
-These are the contents of the current snapshot. Independent correctness fixes
-will move to earlier chapters first; moving the larger SSA redesign backward,
-or splitting this chapter, is deferred. See the [backport queue](../docs/chapter-backports.md).
-
-The memory work from Chapters 21-24 carries forward here: Load search proves
-all arms of a merge before moving a read out of a loop, Phi factoring checks
-memory clobbers, and scheduling follows aliases through memory aggregates.
-The search follows `EscapeNode` to private or public memory and waits for
-unresolved Store aliases. Array-length reads have no element bounds check;
-array-element reads retain control. Integer value Phis use the existing typed
-conversion machinery to preserve the load's width across loop widening.
-New still produces private constructor memory, with publication handled by
-Escape; the earlier chapters' allocation-input design does not replace it.
-
You can also read [this chapter](https://github.com/SeaOfNodes/Simple/tree/linear-chapter25)
in the [linear history](https://github.com/SeaOfNodes/Simple/tree/linear) and
[compare it with Chapter 24](https://github.com/SeaOfNodes/Simple/compare/linear-chapter24...linear-chapter25).
@@ -107,9 +93,21 @@ optimize, and validate the graph.
- Guards carry branch-proven zero/nonzero facts before the input family is known.
`FunPtrNode` retains an explicit edge to its function's return graph.
-The driver runs parsing, pessimistic iteration, SCCP, and final type checking
-before serialization and machine selection. Nontrivial inlining is coordinated
-with iteration instead of always occurring as an immediate peephole.
+The driver separates optimization from lowering:
+
+```text
+Parse -> Iter -> Opto -> IterPostOpto -> FinalizeIR -> TypeCheck
+ -> Serialize (when producing an object) -> Lower -> LoopTree
+ -> Unlink -> Select -> Schedule -> LocalSched -> RegAlloc -> Encoding
+```
+
+`Opto` runs SCCP and resolves numeric modes. `IterPostOpto` applies peepholes
+and inlining using those results; `FinalizeIR` settles field layouts and offsets.
+Type checking sees the optimized ideal graph. Serialization saves that graph
+before lowering introduces implementation details such as initialization guards.
+For machine compilation, `Unlink` detaches imported function bodies before
+instruction selection; calls retain the symbols needed for linking. Ideal-IR
+evaluation keeps those bodies available and skips machine selection.
## Memory and construction
@@ -134,11 +132,46 @@ memory through `EscapeNode`s. Parser `Var` metadata detects early field reads
and missing initialization on constructor exits. Store widths come from target
declarations, not from the values being stored.
+## Lazy class initialization
+
File-level code is represented by a class initializer (`<clinit>`), distinct
-from instance initialization. The [lazy initialization design](initialization.md)
-specifies proposed run-once checks, acyclic dependencies across modules, and
-serialization support. It describes planned work, not all currently implemented
-behavior. The [roadmap](ROADMAP.md) contains additional proposals.
+from instance initialization. An active class-field access ensures that the
+class is initialized. Taking a class reference is passive: it identifies the
+class without running its body. Initialization runs once, on the path that
+first needs it, and a parent initializes before its child.
+
+For example, if `B.smp` computes `value`, then this code initializes B only
+when `take` is true; the second access reuses the initialized class:
+
+```java
+return take ? B.value + B.value : 0;
+```
+
+The compiler represents the obligation with an opaque `CLInit` node. It carries
+control, whole-memory effects, the class pointer, and a load of the hidden
+`$init` flag. Its results include a memory slice where that flag is known true.
+Ordinary memory SSA and load simplification can therefore remove repeated
+checks, including after a merge when every incoming path has initialized the
+class. Calls conservatively account for arbitrary memory effects.
+
+After optimization, the compiler builds a graph containing function calls,
+class touches, and parent requirements. A strongly connected component with
+more than one class initializer is an error. Ordinary recursion and helper
+cycles involving just one initializer are allowed. Checking after optimization
+means eliminated branches and call targets do not create false dependencies.
+Diagnostics show the cyclic edges and their source locations, including calls
+through helpers and imported code.
+
+Object files preserve opaque initialization nodes and their source locations.
+Imported ideal bodies participate in the combined cycle check before lowering.
+Lower then expands each remaining obligation into a flag test, a conditional
+initializer call, and control/memory merges. The initializer owns the flag store;
+the store is at body entry to support permitted same-class reentry. Cross-class
+cycles have already been rejected. This is a single-threaded protocol.
+
+The selected entry class receives the current integer `arg`; other initializers
+receive zero. Planned argument arrays and further initialization optimizations
+are described in the [roadmap](ROADMAP.md).
## System library and examples
diff --git a/chapter25/ROADMAP.md b/chapter25/ROADMAP.md
index 749a1614..966bf8e9 100644
--- a/chapter25/ROADMAP.md
+++ b/chapter25/ROADMAP.md
@@ -462,3 +462,42 @@ Here:
- Then it evaluates `z++`, which is false (0).
- Since `&&` requires all conditions to be true, the final result `g` is 0.
- All operands up to the false one are evaluated, so `x`, `y`, and `z` are all incremented.
+
+## Class initialization and compiler follow-ups
+
+### Entry arguments
+
+Replace the integer initializer argument with an argument array. The selected
+entry class receives a non-null array, empty when there are no arguments;
+other class initializers receive null. Coordinate the parser, entry signature,
+runtime driver, and separately compiled interfaces.
+
+### Pre-initialize classes from ELF data
+
+Prove when a class's entire initialization can be represented by ELF static
+data and relocations, with no remaining runtime effects. Emit its initial
+contents and start its `$init` flag as true, so no initializer call is needed.
+Carry that fact through separate compilation so importing compilers can remove
+checks too. A constant field alone is not proof that the whole class can be
+pre-initialized; I/O, argument-dependent work, and other observable effects
+still require execution. Preserve any dynamic parent requirement, and do not
+let cyclic dependencies justify their own pre-initialization.
+
+Mutable fields may have static initial contents without becoming constants.
+Choose read-only storage only when no runtime writes remain. Static reference
+cycles can use relocations without requiring a dynamic initialization cycle.
+
+### CFG frequency estimates
+
+After lowering and loop-tree construction, estimate block frequencies for GCM
+and code layout. Treat initialization and known-failure paths as rare; use
+bounded loop-trip estimates and branch probabilities when no profile is
+available. Keep estimates consistent with control flow and shared between
+scheduling and layout. These are backend estimates, not ideal-IR types.
+
+### Constant instance-field storage
+
+When a final instance field has the same constant value in every instance,
+fold accesses or store the value on the class object and omit the instance
+slot. Preserve field-access semantics and public function reachability:
+externally accessible function fields remain callable regardless of storage.
diff --git a/chapter25/initialization.md b/chapter25/initialization.md
deleted file mode 100644
index 9996a3a3..00000000
--- a/chapter25/initialization.md
+++ /dev/null
@@ -1,842 +0,0 @@
-# Lazy class initialization design
-
-Design and implementation plan, revised 2026-10-10. Checkpoint 1 implements
-post-Opto initialization dependencies and cycle checking for review. Declared
-constant final fields already have static-data lowering. General lazy run-once
-initialization, serialized dependency validation, and initialization guards
-remain to be implemented; the compiler does not yet enforce the full protocol.
-
-Initialization metadata belongs with class identity and compilation-unit
-ownership, not solely on the `<clinit>` FunNode. Represent active touches in
-ideal IR, validate an acyclic graph across source and imported objects, and
-lower remaining checks to a hidden flag in the class object and a conditional
-call. The initializer itself owns the store that sets the flag.
-Use one runtime bit in the first, single-threaded implementation.
-
-## Language rules
-
-- Dynamic class initialization runs once, on the first active touch. A class
- never actively touched executes no initialization code.
-- A dynamic parent completes before a dynamic child starts. There is no early
- publication point or special tail-call escape from this rule.
-- Outside the initializer's own checked initialization context, no code may
- observe fields before their required initialization completes.
-- Class identity exists before initialization. Taking a class reference, as
- in `sys`'s `val io = io`, is passive and does not execute the child's body.
- Storing a function identity is also distinct from invoking it.
-- Proven static-data accesses require no dynamic work. This does not authorize
- a called function to observe unfinished dynamic state in an ancestor or any
- other class.
-- After Opto, build the combined function-call and class-initialization graph,
- including module boundaries. Reject an SCC only when it contains more than
- one `<clinit>`. Ordinary recursion, initializer self-edges, and helper cycles
- involving only one initializer are legal. Unresolved conditional branches
- contribute edges; eliminated branches and call targets do not.
-- Initialization order follows actual execution and parent requirements, not
- parser discovery order, worklist seed, or filesystem enumeration order.
-
-For example, A conditionally reading `B.foo`, with B initializing `foo` from
-`A.bar`, is rejected if that read survives Opto. Neither an initial zero nor an early evaluation of
-`A.bar` resolves that cycle. If B is A's child, the parent requirement already
-provides the reverse dependency.
-
-An initializer may read its own previously initialized fields under the existing
-definite-initialization rules. That is not an ordinary external active touch.
-Same-class helper calls are allowed by the cycle checker, whether or not they
-inline. Ordinary field-initialization and type checks remain separate.
-
-The cycle diagnostic lists every internal edge of the first rejected SCC,
-including helper calls. Every listed edge participates in a cycle; this reports
-all cyclic connections in that component without enumerating individual paths.
-On failure, rescan the component's functions to choose one surviving touch or
-call site for each edge. `SourceError.format(CompUnit, byteOffset, message)` prints
-`file:line:character: error: message` starting in column one, followed by the
-source line and a separate caret line. Lexer snapshots retain their
-original CompUnit through inlining; Call and CLInit serialization retain the
-offset and source identity/path. Imported source is read only on error. Parent
-requirements are labeled explicitly rather than assigned a fabricated position.
-This is a focused diagnostic utility, not a migration of all parser errors.
-
-## Ownership and identity
-
-Keep `CompUnit._deps` for source/object discovery and rebuild dependencies.
-Do not reuse it as the initialization graph: passive references belong there,
-and its elements are files rather than necessarily individual classes.
-
-Use CompUnit directly for the current runtime initialization graph. The parser
-creates a file `<clinit>` in `parseStruct(true, ...)`; nested struct declarations
-use `parseStruct(false, ...)` for instance initialization. Do not introduce a
-separate ClassInit object or invent a dynamic class initializer for those
-instance constructors.
-
-Reuse `_cname` for stable identity, `_par` for the filesystem parent, `_clz` for
-the canonical class object and hidden field, and existing function identities
-for the body. Add only initialization-specific information:
-
-| Member | Meaning |
-|---|---|
-| `_classInitDeps` | Lazily allocated `Ary<CompUnit>` of direct initialization requirements |
-| `_initComplete` | All initializer touches and relevant call targets have resolved |
-| `_initStatic` | Proven that this unit's own initializer needs no execution |
-
-Keep diagnostic witnesses in the checker's temporary function summaries: source
-locations and indirect call witnesses where applicable. CompUnit retains only
-the edges and analysis flags after validation. Derive the flag alias from `_clz`'s
-`$init` field rather than maintaining a duplicate mapping. Locate the body by
-its stable function identity; a transient FunNode cache may be useful but does
-not define class identity. An unloaded or eliminated FunNode is not proof of
-static initialization.
-
-Static class objects for nested struct declarations keep their existing named
-TypeStruct identity and defining CompUnit. Their materialized data need no new
-runtime graph vertex; an active use that requires the enclosing file initialized
-maps to its CompUnit. Preserve initialization-independent static accesses. If a
-future language feature adds an independent dynamic initializer inside one file,
-then introduce the additional per-class graph identity that feature needs.
-
-`CompUnit.addClassInitDep(code, target)` allocates the collection lazily, deduplicates
-edges, and invalidates a compilation-wide validation stamp. Keep self-edges so
-they can be diagnosed. Explicit namespace-only parents have no dynamic body;
-a missing parent definition must not silently become an empty initializer.
-
-There are two different dataflows. Runtime `$init` values use ordinary memory
-aliases and support flag-load/CLInitNode optimization. Dependency discovery
-records which initializers may be requested, independently of incoming flag
-values and normal return. A dedicated analysis-only memory slice could encode
-that second forward dataflow, but the first implementation uses explicit
-CompUnit edges plus small function summaries. It does not add a second family
-of memory aliases, and it does not infer cycles from runtime `$init` types.
-
-## Discovering and validating dependencies
-
-Use the convention `A -> B` to mean that initializing A may require B completed.
-Topological output therefore places prerequisites before dependents. A dynamic
-child has a structural dependency on its parent. An active touch of another
-class during A's initializer contributes an edge; a passive reference does not.
-
-Insert active touches as CLInitNodes on executable control. After Opto finishes
-SCCP, inlining, and cleanup, collect surviving touches and calls. Use final
-function-pointer target sets and propagate initialization effects through the
-resulting call graph with a worklist. Recursive function SCCs reach a fixed
-point of finite dependency sets; they are not themselves initialization cycles.
-Indirect calls contribute the union of targets still possible after optimization.
-
-Do not accumulate historical targets or source touches eliminated by Opto.
-There is no side table watching Node replacements or type changes. An inlined
-helper's surviving CLInitNodes belong to its caller; an uninlined helper's
-summary propagates through its surviving calls. Walk executable control
-forwards so touches before nonreturning calls and infinite loops are included.
-Preserve initializer boundaries until validation; expanding/inlining CLInit
-operations early must not erase the class identity needed for checking.
-
-A field value becoming constant does not prove its initializer unnecessary.
-CLInitNode survives independently of the load. Dead control may remove both;
-a static-initialization proof may remove the obligation. Such proofs must not
-assume the very acyclicity being checked.
-
-Acceptance now depends on what Opto proves. Worklist determinism remains an
-implementation goal, but preserving every intermediate target set is not the
-language rule. Before declaring a graph complete, every surviving required
-initialization-time call must have a complete effect summary.
-
-An unknown function-pointer target or native callback is not an empty summary.
-Reject such a call during initialization unless an explicit trusted contract
-establishes its initialization effects, including that it cannot call back into
-unfinished Simple initialization. Ordinary native I/O can use such a contract;
-this is independent of whether the call has memory or I/O effects.
-
-Proving a class entirely static is a separate, deterministic classification
-step. Static object/reference cycles are legal and need no dynamic ordering.
-Dropping a dynamic requirement requires proof that the relevant operation needs
-no initialization, not merely that its guard or result optimized away. A real
-cycle must not bootstrap its own static-ready proof.
-
-Validate the union of parent edges and active initialization requirements after
-the source/import closure is resolved and again after loading or replacing
-summaries. Report a cycle with class names and the source/call witnesses for
-its edges. An incomplete library may retain explicitly unresolved obligations;
-it cannot be certified as a complete runnable program.
-
-Use a stable name order to break ties in diagnostic/topological output. Cache
-module-local results, but validate cross-module edges at final composition.
-One module's list is not necessarily a contiguous block: `X.A -> Y.B -> X.C`
-in prerequisite-first order requires interleaving X and Y. Retain edges as the
-authority rather than serializing a local order as a global proof.
-
-The topological order validates permitted completion order; it is not a startup
-schedule. If A conditionally uses independent B, B initializes only on the
-taken path. A may start before B and finish after it. That is safe precisely
-because B cannot directly or indirectly require unfinished A. Do not eagerly
-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
-CLInit(C, ctrl, memory) -> (ctrl, memory)
-```
-
-Control is input/output slot 0 and memory slot 1. Store the target class as
-semantic metadata or a separate constant input, never in the control/memory
-slots. Include target identity in equality, copying, printing, and serialization.
-Its normal return guarantees the required initialization of C and its ancestors.
-It can execute arbitrary initializer effects, so it is not a pure flag test.
-
-At dynamic class-field reads and writes, required instance creation, and calls
-requiring initialized class state, consult parser flow facts first and insert
-CLInitNode only if the required initialization is not already established.
-A check omitted by a valid dominating initialization proof needs no separate
-historical dependency record: its prerequisite is represented on the covering
-path. Passive Simple class references remain subject to checks at later field accesses.
-Native escape of class references is a separate boundary described below.
-Forward references must preserve the pending check until the target is known.
-
-For `ary.copy(args)`, check the class at the field access:
-
-```text
-CLInit(ary)
-f = load ary.copy
-call f(args)
-```
-
-The ordinary function header, calling convention, and static function-pointer
-field do not change. There is no per-function checked wrapper. Folding the
-field load to a constant function pointer does not remove the preceding
-initialization effect unless that effect is independently redundant. Once the
-checked load yields `f`, it can be passed along and called without repeating
-the check for `ary`. Materializing a function address in static data is not a
-source-level field access. An initializer's raw body remains private compiler
-machinery, not a callable reset operation.
-
-Keep CLInitNode in ideal IR so its peepholes can eliminate or simplify frequent
-checks. Lower a remaining node to ordinary Load/If/Call/Region/Phi IR before
-machine selection, using a shared class-initialization helper where an
-out-of-line guard is useful. The checking code contains no flag store:
-
-```text
-CLInit_C(ctrl, memory):
- (ctrl, memory) = CLInit_parent_if_required(ctrl, memory)
- if load(C.$init, memory):
- return (ctrl, memory)
- return call C.<clinit>(ctrl, memory)
-
-C.<clinit>(ctrl, memory):
- memory = store(C.$init, true, memory)
- ... original initializer body ...
- return (ctrl, memory)
-```
-
-Entry to `<clinit>` already means that its check took the not-initialized path.
-Place the flag store in the initializer, naturally at its entry. This covers
-all its normal exits without replicating a store in each CLInitNode expansion.
-Inlining the body may of course copy its store, just as it copies other body
-instructions. The body must not guard itself through ordinary method-entry
-instrumentation.
-
-There is no requirement to schedule the store last. With a validated acyclic
-initialization graph and one thread, no other user can observe this class's
-flag while its initializer is running. A nonreturning initializer can leave
-the bit set because no permitted observer can resume. The physical flag store
-is not an early publication point: the class's initialization still completes
-only when its body returns. Cycle validation and definite-initialization checks
-must remain independent of that store and of optimizations which expose it.
-
-The first runtime contract is single-threaded initialization, with no recovery
-and retry after initializer failure. The validated DAG rules out recursive
-entry to an unfinished initializer. Thus no runtime `initializing` state is
-needed. Concurrent first touches or recoverable initialization failures require
-an extended protocol; the flag is not a synchronization mechanism.
-
-## Hidden flag and known completion
-
-Add a hidden boolean field `$init` to the ordinary `<clinit>` class object.
-There are few such objects, one per class; no separate allocation, symbol, or
-special bit-packing scheme is needed. Use the existing small-integer field
-representation and normal struct layout/offset machinery. It is a logical bit,
-initially false for a dynamic body, stored true by that body, and inaccessible
-to user writes. Separate importers share it through the existing `C.$class`
-identity and its canonical field layout.
-
-Allocate/remap these aliases during class discovery, before the current
-`freezePublicInterface()` boundary. Hidden flags are not source-visible fields,
-but their memory effects must participate in call summaries and memory merging.
-Keep CLInitNode's complete public-memory effect until a narrower summary is
-proved; tracking only the flag would lose the initializer's field and I/O effects.
-
-There are two different facts:
-
-1. **Static body completion:** `_initStatic` proves that C's own initial contents
- are supplied by data and there are no remaining initializer effects. Its
- `$init` field starts true, and its redundant body/store can disappear.
-2. **Completion at a program point:** normal return from CLInitNode establishes
- readiness in that control/memory state. At a permitted external touch, a
- true flag branch proves completion of C's own body; its ancestors must also
- be known complete before concluding that an ordinary active use is safe.
-
-Inside C's own initializer, the flag can already be true while fields are still
-being initialized. Keep that lexical initialization context and its field-read
-checks; a folded true flag does not grant access to unfinished fields. In
-particular, it must not make a callback or cyclic initialization dependency
-legal. Record and validate those dependencies before suppressing their checks.
-
-Neither fact means mutable fields remain constant. Do not refine their declared
-types to their static initial values. Also, a static-only child with a dynamic
-ancestor is not globally ready for every active use: ordinary ensures still
-establish the ancestor. Passive references and proven initialization-independent
-static accesses need no such ensure, including during the parent's initializer.
-
-## Parser flow and ideal peepholes
-
-CLInitNode will occur frequently. Avoid constructing obvious duplicates in the
-parser, then use normal ideal optimization as more types, call effects, and
-control-flow facts become available. A separate late must-analysis is optional,
-not a prerequisite for removing straightforward redundant checks.
-
-Carry known-initialized class identities with parser control flow, for example
-as a BitSet in ScopeNode keyed by the existing remapped `$init` alias IDs. These
-are path facts, not mutable global properties of CompUnit or its declared
-TypeStruct:
-
-- On an active touch, consult the facts. If C and its required ancestors are
- known ready, omit the CLInitNode. Otherwise insert the obligation in the IR;
- post-Opto analysis collects surviving requirements.
-- Normal return from CLInit(C) adds C and its required ancestors. Do not add
- every possible dependency: a conditional initializer may never touch one.
-- Copy facts on a branch and intersect them at a reachable merge. An abrupt
- or nonreturning path contributes no completion facts to a reachable merge.
-- A loop's first iteration has only entry-path guarantees. Body/backedge facts
- alone cannot suppress its first check or establish readiness after a
- potentially zero-trip loop. Preserve pre-loop facts and refine only with a
- proof covering all relevant incoming paths.
-- A call cannot reset established facts. Add new ones only from a proven normal
- return summary; do not seed arbitrary exported function entries with facts
- learned at one caller. Checks at field-access sites establish their facts.
-
-Give CLInitNode ideal peepholes for these cases:
-
-| Proof | Rewrite |
-|---|---|
-| Fully static class / flag known true at a permitted touch | Remove the class's own check and call; retain any still-required ancestor initialization |
-| Flag known false at this memory/control point | Replace the test with an unconditional initializer call; retain the call's effects and results |
-| Earlier normally returning CLInit of the same class dominates | Forward control/memory through the redundant node |
-| Dominating true flag test covers this path | Remove the covered check, as with redundant if-test reasoning |
-| All reachable arms of a merge establish completion | Fold the repeated check after the merge |
-
-Known false must come from the memory reaching the node, not simply the false
-byte in the original object image. Known true on one branch is not known true
-after a merge with an unchecked branch. Match stable class identity and required
-ancestors, not just a pointer-shaped value or a similar field name.
-
-Use the existing dependency/wakeup mechanism when a node consults a flag value,
-memory state, or another node's proof, so later refinement revisits the check.
-Retain control and whole-memory ordering through the conditional call: removing
-a check must preserve preceding initialization effects, and hoisting a check
-onto an untaken path would violate laziness. In particular, CLInitNode is not
-freely speculatable or an ordinary pure expression suitable for unrestricted GVN.
-
-Parser omission must have an enduring IR justification: earlier effectful
-CLInit/control edges, a proven caller precondition, or static metadata.
-When bodies are inlined or deserialized, those proofs travel with the IR;
-transient parser facts alone cannot justify an unchecked exported body. A
-shared declared type must not be mutated to encode a path-local true flag.
-
-## Static data classification
-
-A class is fully static only when every required initial value is representable
-in data/relocations and no runtime effect remains: no I/O, observable allocation,
-potential failure, or required dynamic dependency. A missing/dead body alone
-does not prove this, and startup arguments are not static inputs.
-
-Ignore the compiler-inserted `$init = true` bookkeeping store when checking
-whether the original initializer is entirely static. Otherwise every inserted
-store would prevent this optimization. Once the proof succeeds, emit `$init`
-as true in the object image and eliminate the runtime store with the body.
-
-Mutable fields may have static initial values. Folding their initializing
-stores must still preserve reads and escapes during the initializer. Merely
-knowing the final value and that the body runs once is insufficient. Preserve
-object identity and canonical layout separately from initial contents, and do
-not collapse distinct class objects with equal bytes.
-
-This extends the current StaticData machinery; it does not change pointer
-widths or require class objects to become read-only. An object with a dynamic
-`$init` field stays writable even if its source fields are final. A fully static
-object whose source fields are also immutable can later use read-only storage,
-with its hidden field already true and no runtime writes to it.
-
-## Serialization and final composition
-
-Add a class-initialization directory to the `.simple` data that can be read
-without materializing every function body. Serialize:
-
-- Stable class and parent names, defining-unit identity, and initializer symbols.
-- Static/dynamic/unknown classification and summary-completeness information.
-- Direct initialization requirements and function-effect summaries, including
- unresolved obligations and useful cycle-diagnostic locations.
-- The hidden `$init` field's type, alias identity, initial image value, and
- canonical layout, alongside the other class fields. Remap aliases using file
- identity and local order rather than copying process-local numbers.
-- CLInitNode operations and the initializer's own flag Store in ideal IR,
- including all semantic fields needed to rebuild them.
-
-Do not serialize transient node references, a cached global topo index, or the
-runtime value of a dynamic flag as an unconditional type fact. A definition
-and its imported declarations must resolve to the same owning CompUnit and
-canonical class/flag identity.
-
-On load, register identities first, resolve parent/dependency edges second,
-then resolve function summaries and IR. Check conflicting owners, missing
-definitions, alias remapping, summary completeness, and combined cycles.
-Loaded bodies must agree with their summaries; report newly found requirements
-and revalidate rather than trusting an earlier module-local topo sort.
-
-Preserve these summaries before `unlinkImports()` removes imported bodies.
-Serialize ideal CLInitNode operations before lowering to machine code. Native
-objects retain checks at their class-field access sites even when a particular
-importing compiler inlines and removes its local checks. Ordinary function
-symbols and function-pointer constants are not replaced by checked wrappers.
-
-Do not serialize a parser's transient BitSet as unconditional entry knowledge.
-Its local alias IDs can change on reload; retained checks, control flow, and
-explicit entry contracts must support any omitted checks. Recompute flow facts
-for loaded graphs and verify hidden-field ownership/layout and that the flag
-store belongs to `<clinit>`, not to each checking caller.
-
-Final validation needs a Simple-aware executable/library composition step.
-An ordinary native linker cannot validate dependencies stored only in `.simple`.
-Enumerate the complete intended Simple object set, including exported entry
-points that native code could invoke; loading only the source-discovered
-references is insufficient. Validate that metadata set before handing objects
-to GCC/the native linker. Dynamically loading additional Simple modules is
-outside this first protocol and would require validation before activation.
-
-Keep the current unversioned `C0DE` convention and require rebuilding objects
-when the serialized layout changes. Retain/validate summaries for the exact
-objects being linked, not cached descriptions of older builds.
-
-## Program entry and other boundaries
-
-Program startup is another CLInitNode on the starting class. In a fresh runtime
-image with a dynamic starting class, its `$init` is known false, so the check
-folds to the initial `<clinit>` call. The starting class sets its own bit just
-like every other class; there is no adapter-specific flag store or protocol.
-A proven fully static starting class needs no dynamic initialization call.
-
-The current file `<clinit>` can receive `arg` and return a program/test result.
-Preserve those arguments and the call result on the known-first-call startup
-path. Ordinary class-use checks need only control and memory; they do not need
-to cache or invent a result for the skip path. For fully static startup, the
-program result must also be available as a constant or retained computation;
-static object contents alone do not prove the entry computation unnecessary.
-Existing tests executing file code with different arguments use independent
-runtime images, not a reset of an already initialized class. The startup fact
-must not be reused for an arbitrary native entry into a live image.
-
-Own-initializer helper calls and construction of a special instance during
-class initialization need an explicit definite-initialization proof. Do not
-carry forward the old module notes' unrestricted “golden instance” exception.
-Initially reject cases that would publish or inspect unfinished class state.
-
-## Native handoff
-
-Native callers are not required to understand or explicitly run Simple class
-initialization. The compiler/runtime must establish initialization before
-exposing a supported value, or reject an unsupported handoff.
-
-For the first implementation, reject passing a class object or raw `<clinit>`
-function to native code, including through a class-to-address conversion.
-Do not confuse these with an ordinary function pointer obtained by a checked
-field access: loading `ary.copy` already initializes `ary`, so that value does
-not need a new initialization wrapper when handed to native code, wherever the
-ordinary FFI supports its signature. Initialization-time callbacks still need
-the effect analysis described above; an uninitialized class cannot be hidden
-behind a callback to evade cycle validation.
-
-Later, support a selected class value at the handoff point:
-
-```text
-X = rand ? B : C // Passive class references
-CLInit(selected class X) // Initialize the actual value being passed
-libc.native(X)
-```
-
-Validate the initialization graph for all possible targets, B and C, including
-their parents and transitive requirements, regardless of the selected value.
-At runtime initialize only the selected target before the native call. This
-can lower to checks on the corresponding control-flow arms, or dispatch on a
-known target identity. The plan's initial CLInitNode has a fixed class identity;
-this dynamic selection is a later extension, not an assumed existing facility.
-
-Initializing every possible target before handoff is an alternative policy,
-but it runs effects for the unselected class. Prefer the selected-target form
-to retain the rule that untouched classes run no code. Unknown target sets or
-native traversal to further uninitialized class objects require additional
-analysis; initializing just X is not a blanket proof for everything reachable
-through X. Until supported, reject the handoff rather than transferring that
-responsibility to native code.
-
-A native call directly to an exported symbol, without a preceding Simple
-handoff, likewise needs compiler/runtime initialization support. Its eventual
-export/startup mechanism is deferred; it must not require the native caller to
-invoke `<clinit>`. Existing native-linking tests are not evidence that this
-future initialization guarantee is already implemented.
-
-## Implementation sequence
-
-Implement in Chapter 25 only, with the following reviewable checkpoints. Keep
-existing source/native entry conventions and pointer widths. Each checkpoint
-must build; do not claim the runtime guarantee until the executable protocol,
-serialization, and final-composition validation are all present.
-
-### 1. CompUnit dependencies and cycle checking
-
-Reviewed (2026-10-10). Files: `CompUnit.java`, `ParseAll.java`,
-`Parser.java`, `ClassInitDependencies.java`, `CLInitNode.java`, `CallEndNode.java`,
-`Opto.java`, and `CodeGen.java`. Generic `Node` needs no changes.
-
-- CompUnit holds direct `_classInitDeps`, `_initComplete`,
- and reserved `_initStatic`. Existing file-discovery `_deps` is unchanged.
- Diagnostic witnesses belong to the dependency checker and serialized summaries,
- not CompUnit.
-- Parser inserts a control-flow CLInitNode for an active class-field touch or
- a receiver whose class is not yet resolved. Ordinary instance receivers
- fold the obligation away when resolved. Passive references and direct
- own-initializer field accesses do not insert a check.
-- This checkpoint's ideal node has control at input 0 and receiver at input 1;
- it neither consumes nor produces memory yet. The full runtime form described
- above will add memory at slot 1 and move the receiver to slot 2. No hidden
- flag or runtime initialization call is generated in this checkpoint.
-- After Opto, the collector walks live control forwards in each surviving
- function, reads final call-target sets, and propagates sparse may-touch
- summaries. Ordinary helper inlining naturally relocates its checks into the
- caller. Class initializers are not inlined before validation.
-- Reachable nonreturning calls retain a conservative CallEnd during dataflow.
- Calls left out-of-line retain their continuations, so otherwise unreachable
- touches after them can contribute initialization dependencies. This extra
- ordering is accepted; users can remove such unreachable code.
- Inlining discovers bodies forwards from their entry, including non-exiting
- paths, and explicitly retains the Return node even when disconnected from
- executable control. Nonreturning functions can therefore inline normally.
-- TypeCheck builds fresh CompUnit requirements from those summaries and parent
- edges. It rejects cycles with source/call witnesses and computes a stable
- prerequisite-first order. No source-history or Node watcher table is kept.
-- Following validation and serialization, the temporary CLInit obligations are
- erased for the existing backend. Executable lowering remains a later
- checkpoint. The full lazy-initialization runtime guarantee is not implemented.
-
-Unknown native effects and unresolved targets leave `_initComplete`
-false; imported summaries are now available in checkpoint 2. The returned order
-covers known edges and is not a completeness
-certificate. `_initStatic` remains false until static classification is added.
-
-Validation covers direct, helper-mediated, forward, conditional, indirect,
-sibling, and parent/child cycles; legal ordinary recursion and passive
-references; unknown native/imported effects; dependencies eliminated by SCCP;
-unused helpers; discarded field values; and effects before versus after a
-nonreturning call. Cycle and optimized-target cases run with three worklist
-seeds. A bounded Eval2 run verifies that a retained nonreturning call executes
-rather than falling through; its x86, ARM, and RISC-V encoding checks also pass.
-All 492 tests in the parallel Chapter 25 Make suite pass. The encoding checks
-were added afterward and pass in the focused suite. Two existing RISC-V spill
-expectations change from 9 to 7; execution and allocation checks pass.
-Generated fixtures are under
-`chapter25/build/init-deps/`; full-suite logs are under `build/init-deps-opto*.log`.
-
-### 2. Serializable flags and dependency summaries
-
-Implemented for review (2026-10-10). Files: `Parser.java`, `Serialize.java`,
-`ElfReader.java`, `ParseAll.java`, `ClassInitDependencies.java`, `CodeGen.java`,
-and `CLInitNode.java`. Existing `GlobalBits` and `StaticData` mechanisms handle
-alias/function remapping and the zero flag image without special cases.
-
-- Add hidden `$init` fields with the normal field/alias/layout mechanism before
- `freezePublicInterface()`. Declared flag type is boolean; initial image value
- is false unless static initialization has been proved. Do not make a dynamic
- flag's declared type the constant false.
-- Serialize per-unit initialization metadata and function may-touch summaries
- in `.simple`, early enough for graph validation without loading every body.
- Preserve stable owner/alias/function identities and diagnostic witnesses.
-- Register imported units first, resolve/remap edges and hidden fields second,
- then validate the combined graph. Neither a module-local topological order
- nor a summary from an older object file is a final-link proof.
-- Keep metadata for fully static or body-eliminated units. Rebuild objects
- under the existing unversioned `C0DE` convention; add no compatibility layer.
-
-Checkpoint: a two-module cycle rejected from source is also rejected after
-serialization; an acyclic diamond resolves to one shared class and flag.
-Two local topo orders can be merged even when their modules must interleave.
-
-The object header carries owned class identities, initialization edges,
-completeness/static flags, and per-function may-touch summaries with diagnostic
-witnesses. Imports use those summaries even when their initializer bodies are
-not live in the importing compilation. Class layouts survive body elimination.
-Each `ClassCyclicError` witness holds existing owner/field name references, a
-numeric line/column, and a shared suffix for indirect calls; a null field denotes
-a call. Diagnostic text is formatted only when a cycle is rejected. Serialization
-stores shared witnesses once and preserves numeric locations without requiring
-the original source text.
-Ideal CLInitNodes also survive serialization, including source positions, so
-inlining an imported helper retains its active touches. Erasure now happens in
-Unlink, after serialization; executable checks still belong to checkpoint 3.
-
-Object lookup distinguishes class definitions from references to other objects.
-Objects serialize their own bodies; imported function pointers retain symbolic
-identities. Loading registers all classes supplied by each object, resolves
-prerequisites against existing source/imported units, and selects the requested
-nested class's type rather than always the object's root class. Cross-object
-calls are relinked after all bodies load, before Iter validates saved types.
-
-Validation covers separately compiled cyclic objects, a shared diamond across
-three worklist seeds, interleaving orders from two multi-class objects, imported
-helpers with and without inlining, type-only loading with stable flag aliases,
-and the emitted zero flag byte. The C0DE layout changed: rebuild existing objects.
-The runtime still does not test or set the flag, and static classification remains
-deferred. All 494 tests pass in the Chapter 25 Make suite. Logs:
-`build/init-checkpoint2-focused.log` and
-`build/init-checkpoint2-all.log`.
-
-### 3. CLInitNode and the executable protocol
-
-Files: new `node/CLInitNode.java`; `Node.java`, `Parser.java`, `CodeGen.java`,
-`Serialize.java`, `Eval2.java`, and the printer adapters as needed.
-
-- Add an effectful, serializable CLInitNode targeting the resolved CompUnit,
- with control/memory inputs and outputs in slots 0/1. Pending targets remain
- explicit until resolved. Its type computation is conservative about the
- initializer's whole memory effects; final normal output guarantees completion.
-- Integrate its possible call with existing Call/CallEnd linking, argument and
- return propagation, and body liveness. A reference to CompUnit metadata alone
- must not let SCCP discard the initializer as uncalled. Reuse the call machinery
- or a generated guard helper, rather than implementing a second independent
- calling convention in the node. Test an out-of-line imported initializer as
- well as an inlined one.
-- Insert checks at class-field accesses, including function-pointer loads;
- use the same operation for required instance creation and startup. Do not
- instrument ordinary function headers or rewrite their function-pointer data.
-- Insert `$init = true` once in each dynamic `<clinit>` body, naturally at entry.
- The checking node performs the test/conditional call and contains no store.
- A false startup test becomes a direct call preserving `arg` and the result.
-- Give Eval2 the same shared-object/flag behavior. Lower checks to existing
- control, memory, and call IR for machine compilation; use an out-of-line
- helper if necessary rather than adding target-specific machine operations.
-- Reject the presently unsupported native class/raw-initializer handoffs. Keep
- ordinary checked-field function pointers unchanged. Do not add an explicit
- initialization obligation to C drivers or other native callers. Direct native
- entry into a dynamic class remains outside the new guarantee until a
- compiler-managed startup/export mechanism exists.
-
-Checkpoint: sequential touches run the body once; parent effects precede child
-initialization; an untaken branch runs no child code. Verify result/argument
-preservation and flag sharing in Eval2, ARM/RISC emulation, and native execution
-through Simple startup. Existing direct native tests must not be mistaken for
-coverage of the deferred dynamic-export case.
-
-### 4. Parser flow and ideal simplification
-
-Files: `ScopeNode.java`, `Parser.java`, `CLInitNode.java`, and existing
-control/memory lookup helpers.
-
-- Add path-local known-initialized facts to ScopeNode. Copy in `dup`, intersect
- reachable predecessors in merges, and respect entry/backedge distinctions in
- loop completion, break, continue, and return handling. The established
- control/memory path must justify each check omitted by the parser.
-- Implement constant-true, constant-false, preceding dominating CLInit, and
- dominating true-test peepholes. Use dependencies to revisit proofs after SCCP
- or inlining sharpens values. Keep remaining parent requirements when only a
- child's own body is static.
-- Preserve initialization effects when folding field loads into function
- constants. Never hoist a check onto a path that previously did not touch the
- class. Do not confuse an early true flag inside `<clinit>` with permission
- to read that initializer's unfinished fields.
-- Add fully-static classification using the initializer body and its effects,
- excluding the generated flag store from the test. Emit a true initial flag
- only after proof; remove the now-redundant body/store/checks. Extend mutable
- constant-field lowering only where observations during initialization remain
- correct. Do not use classification to erase evidence of a genuine cycle.
-
-Checkpoint: distinguish a dominating check from a one-arm-only check, a
-zero-trip loop from a definitely executed touch, and a static child from an
-unfinished dynamic parent. Check that early flag folding cannot hide a cycle.
-
-### 5. Phase ordering and final composition
-
-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, IterPostOpto, FinalizeIR, TypeCheck, LoopTree,
-`unlinkImports`, serialization, then selection and scheduling. Add explicit
-initialization-analysis boundaries without relying on a particular peephole
-worklist order:
-
-1. During parse/load, register identities and represent active initialization
- obligations in ideal IR. Ordinary Opto resolves targets and removes dead
- control; do not collect a historical union of temporary types or calls.
-2. After Opto finishes, collect surviving CLInitNodes and calls, complete
- function summaries, classify provably static work without circular
- assumptions, and validate the graph. Any remaining unknown required
- initialization effects prevent certification.
-3. Before `unlinkImports`, retain imported summaries needed for validation and
- serialization. Serialize ideal CLInitNodes and their proof-bearing control
- and memory edges, plus the independent requirements.
-4. Lower remaining CLInitNodes before final loop-tree construction and machine
- selection. Because lowering introduces branches/calls, rebuild affected
- control information and run ordinary ideal/type validation on that graph.
- Move the final LoopTree step if needed rather than using a stale tree from
- before expansion. Keep the serialized representation ideal and reloadable.
- Ensure the generated call participates in call linking before the final
- unlink step; imported bodies may remain represented by symbolic targets.
-5. Before native executable composition, load metadata for the exact full
- Simple object set being linked, merge requirements, and validate again.
- A plain GCC/ELF link is not an initialization-graph checker. Do the same
- validation for a complete in-memory image.
-
-If optimization or loading discovers another target, mark summaries dirty and
-rerun validation before emission. No runtime `initializing` state is introduced;
-compile-time DFS bookkeeping is independent of the single runtime flag.
-
-Checkpoint: source, imported ideal IR, and final linked metadata agree on
-acceptance/rejection for equivalent optimized graphs. Exercise different
-worklist seeds and object-discovery orders to check determinism; eliminated
-branches and targets must not leave stale dependencies. Missing required
-metadata fails diagnostically.
-
-### Validation and scope
-
-Use a small set of focused fixtures covering several boundaries each, plus the
-existing full Chapter 25 Make suites. Include effects before a nonreturning
-call so dependency checking cannot accidentally depend on returned memory.
-Exercise duplicate touches, conditional cycles, native-handoff diagnostics,
-serialization alias remapping, class/flag ownership, and normal startup results.
-Run the fixed allocation cohorts only after runtime semantics stabilize; graph
-and spill changes are expected, and any new golden counts must follow execution
-and register-correctness checks. Keep Jig tests ignored.
-
-Do not backport this feature, change pointer widths, introduce concurrent or
-recoverable initialization, add dynamic module loading, or solve the general
-native-export policy in these checkpoints. The extra analysis-only alias slice
-remains an alternative representation if explicit summaries prove awkward;
-implementing both would add work without improving the initial guarantee.
-
-## Module goals retained from the old notes
-
-Keep one-line programs easy to compile/run, filesystem-based nested namespaces,
-leading-underscore privacy, separate compilation without source include files,
-and ordinary object-file/native-linker interoperability. Classes may be declared
-within a file or supplied by nested files. Source and object builds must have
-the same initialization semantics. The current CLI is documented in the
-[chapter README](README.md); historical command examples and conflicting
-initializer-order proposals are superseded by this design.
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
index eca2b9ce..5ceda6a9 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/Parser.java
@@ -291,8 +291,7 @@ public class Parser {
}
// 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.
+ // is a structural requirement, executed before this body by CLInit lowering.
if( fun.isClz() ) {
Node self = fun.parm(2);
Field flag = ((TypeMemPtr)self._type)._obj.field("$init");
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 3909f881..47ec0112 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -8,9 +8,6 @@ import com.seaofnodes.simple.print.*;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.*;
import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
import java.util.*;
@@ -32,8 +29,9 @@ public class CodeGen {
IterPostOpto, // Peepholes and inlining using SCCP results
FinalizeIR, // Field layout, offset cleanup, function/CU roots
TypeCheck, // Last check for bad programs
+ Serialize, // Save ideal IR, including opaque class initialization
+ Lower, // Expand checked class initialization to ordinary IR
LoopTree, // Build a loop tree; break infinite loops
- Serialize, // Serialize public IR for future compiles to link
Unlink, // Unlink call sites before machine code generation
Select, // Convert to target hardware nodes
Schedule, // Global schedule (code motion) nodes
@@ -185,7 +183,7 @@ public class CodeGen {
public final XCtrlNode XCTRL;
public Node constant( Type t ) {
- assert _phase==null || _phase.ordinal() <= Phase.FinalizeIR.ordinal();
+ assert _phase==null || _phase.ordinal() <= Phase.Lower.ordinal();
return ConstantNode.make(t).peephole();
}
public ConstantNode con( Type t ) { return (ConstantNode)constant(t); }
@@ -239,7 +237,9 @@ public class CodeGen {
}
- // Run requested phases.
+ // Run requested phases. File export must request Serialize before Lower;
+ // requesting Export up front does that automatically. Encoding alone does
+ // not retain reloadable ideal IR.
// No code emission, just IR generation
public CodeGen driver( Phase phase ) { return driver(phase,null,null,false,false,0); }
@@ -256,25 +256,64 @@ public class CodeGen {
// Write an object file for a specific cpu/os pair
public CodeGen driver( String cpu, String callingConv, boolean inMemory, boolean emitEntrySymbol ) { return driver(Phase.Export,cpu,callingConv,inMemory,emitEntrySymbol,0); }
- // Generic driver
- public CodeGen driver( Phase phase, String cpu, String callingConv, boolean inMemory, boolean emitEntrySymbol, int dump ) {
- int p1 = phase.ordinal(), p2 = _phase==null ? -1 : _phase.ordinal();
- 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); }
- if( p2 < p1 && p1 >= Phase.Encoding .ordinal() ) { serialize(); p2 = dump(dump); } // Include ideal graph in object file
- if( p2 < p1 && p2 < Phase.Unlink .ordinal() ) { unlink(); p2 = dump(dump); }
- if( p2 < p1 && p2 < Phase.Select .ordinal() && cpu != null ) { instSelect(cpu,callingConv); p2 = dump(dump); }
- if( p2 < p1 && p2 < Phase.Schedule .ordinal() ) { GCM(); p2 = dump(dump); }
- if( p2 < p1 && p2 < Phase.LocalSched.ordinal() ) { localSched();p2 = dump(dump); }
- if( p2 < p1 && p2 < Phase.RegAlloc .ordinal() ) { regAlloc(); p2 = dump(dump); }
- if( p2 < p1 && p2 < Phase.Encoding .ordinal() ) { encode(); p2 = dump(dump); }
- if( p2 < p1 && p2 < Phase.Export .ordinal() ) { exportELF(inMemory,emitEntrySymbol); p2 = dump(dump); }
+
+ // The Generic driver
+ public CodeGen driver( Phase target, String cpu, String callingConv, boolean inMemory, boolean emitEntrySymbol, int dump ) {
+ // Parse a Simple file, and for mentioned external symbols either load
+ // from .o or recursively parse
+ step(target, Phase.Parse, this::parse, dump);
+ // Run peepholes to a fixed point
+ step(target, Phase.Iter, this::iter, dump);
+ // Run SCCP (and resolve int/flt expressions)
+ step(target, Phase.Opto, this::opto, dump);
+ // Run peepholes to a fixed point again, on the better types.
+ step(target, Phase.IterPostOpto, this::iterPostOpto, dump);
+ // Field layouts; field symbols become constant offsets
+ step(target, Phase.FinalizeIR, this::finalizeIR, dump);
+ // Final error check - bad programs fail here
+ step(target, Phase.TypeCheck, this::typeCheck, dump);
+ // Save reloadable ideal IR for a later seperate compilation
+ step(target, Phase.Serialize, () -> Serialize.forTarget(this,target,inMemory), dump);
+ // Replace "fat" nodes with their exploded explicit IR; this can
+ // introduce CFG diamonds
+ step(target, Phase.Lower, this::lower, dump);
+ // Compute the loop-tree, used for approximating exec frequencies for
+ // scheduling and register allocation
+ step(target, Phase.LoopTree, this::loopTree, dump);
+ // Drop imports for machine selection, retain them for ideal evaluation.
+ step(target, Phase.Unlink, () -> unlink(cpu!=null), dump);
+ // Instruction selection
+ step(target, Phase.Select, () -> instSelect(cpu,callingConv), dump);
+ // Global Code Motion
+ step(target, Phase.Schedule, this::GCM, dump);
+ // Local (basic block only) scheduling
+ step(target, Phase.LocalSched, this::localSched, dump);
+ // Register allocation
+ step(target, Phase.RegAlloc, this::regAlloc, dump);
+ // Encode machine instructions into bits
+ step(target, Phase.Encoding, this::encode, dump);
+ // Export bits (and serialized IR) into ELF files
+ step(target, Phase.Export, () -> exportELF(inMemory,emitEntrySymbol), dump);
+ return this;
+ }
+
+ // The driver names the order; optional phases decide locally whether to run.
+ private void step(Phase target, Phase next, Runnable work, int dump) {
+ if( next.ordinal() > target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return;
+ Phase prior = _phase;
+ work.run();
+ if( _phase!=prior ) dump(dump);
+ }
+
+ // All phase entry points share ordering checks, timing, and viewer notification.
+ // Record elapsed time even on failure, but report completion only on success.
+ CodeGen phase(Phase next, boolean ordered, Runnable work) {
+ assert ordered : "Cannot run "+next+" after "+_phase;
+ _phase = next;
+ long t0 = System.currentTimeMillis();
+ work.run(); // Do The Phase
+ _times[next.ordinal()] = System.currentTimeMillis() - t0;
+ if( _obs!=null ) _obs.phase(next.name()); // Record for viewer
return this;
}
@@ -283,17 +322,14 @@ public class CodeGen {
}
// Verbose printing during compilation
- private int dump(int dump) {
+ private void dump(int dump) {
int p2 = _phase.ordinal();
- if( (dump & (1<<p2)) == 0 ) return p2;
-
-
+ if( (dump & (1<<p2)) == 0 ) return;
if( (dump & (1<<30)) != 0 )
System.err.println("After "+_phase+":");
System.err.println(_phase.ordinal() >= Phase.LocalSched.ordinal()
? asm(new SB()).toString()
: IRPrinter.prettyPrint(this));
- return p2;
}
@@ -430,25 +466,7 @@ public class CodeGen {
public final Parser P;
// Parse ASCII text into Sea-of-Nodes IR
- public CodeGen parse() {
- assert _phase == null;
- _phase = Phase.Parse;
- long t0 = System.currentTimeMillis();
-
- Parser.TYPES.clear();
- Parser.TYPES.putAll(Parser.INIT_TYPES);
- if( _src != null ) {
- // No source file, just the source itself.
- ParseAll.parseSource(this,_srcName==null ? "Test" : _srcName,_src);
- } else {
- // Path from module root to source file
- ParseAll.parsePath(this,_srcName);
- }
-
- _times[Phase.Parse.ordinal()] = System.currentTimeMillis() - t0;
- if( _obs != null ) _obs.phase(_phase.name());
- return this;
- }
+ public CodeGen parse() { return phase(Phase.Parse, _phase==null, () -> ParseAll.parse(this,_src)); }
// ---------------------------
@@ -464,126 +482,14 @@ public class CodeGen {
// Pessimistic peepholes after parsing. This lifts loaded and parsed
// types before the optimistic Opto pass resets types and lets them fall.
- public CodeGen iter() {
- assert _phase == Phase.Parse;
- _phase = Phase.Iter;
- long t0 = System.currentTimeMillis();
-
- // The phase shift closes all parser-in-progress states and freezes
- // unknown external inputs. Imported nodes carry post-Opto escape
- // precision, but Iter is a fresh pessimistic solve: memory escape
- // summaries must restart at FULL so newly discovered private escapes
- // can only be removed, never added. Seed the complete graph once.
- boolean hasImports = _externPaths != null;
- _start.walk(n -> {
- if( !(n instanceof ConstantNode) )
- n._type = hasImports ? iterImportReset(n._type) : iterEscapeReset(n._type);
- add(n);
- return null;
- });
- _iter.iterate(this);
- // Expensive assert.
- assert !expensiveAssert() || Opto.fixedPointCheck(this);
-
- _times[Phase.Iter.ordinal()] = System.currentTimeMillis() - t0;
- if( _obs != null ) _obs.phase(_phase.name());
- return this;
- }
-
- private static Type iterEscapeReset(Type t) {
- if( t instanceof TypeMem mem )
- return mem._one ? mem : mem.makeFrom(XInt.FULL,XInt.FULL);
- if( t instanceof TypeTuple tt ) {
- TypeTuple rez = tt;
- for( int i=0; i<tt._types.length; i++ ) {
- Type tx = iterEscapeReset(tt._types[i]);
- if( tx != tt._types[i] ) rez = rez.makeFrom(i,tx);
- }
- return rez;
- }
- return t;
- }
-
- private static Type iterImportReset(Type t) {
- if( t.isHigh() )
- return t.dual();
- if( t instanceof TypeMem mem )
- return TypeMem.make(mem._alias,iterImportReset(mem._t),mem._one,mem._clz,mem._final,XInt.FULL,XInt.FULL);
- if( t instanceof TypeMemPtr )
- return t.makeStorage();
- if( t instanceof TypeStruct )
- return t.makeStorage();
- if( t instanceof TypeTuple tt ) {
- TypeTuple rez = tt;
- for( int i=0; i<tt._types.length; i++ ) {
- Type tx = iterImportReset(tt._types[i]);
- if( tx != tt._types[i] ) rez = rez.makeFrom(i,tx);
- }
- return rez;
- }
- return t;
- }
-
+ public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> Iter.run(this)); }
// SCCP and numeric-mode resolution; leave cleanup work queued for Iter.
- public CodeGen opto() {
- assert _phase == Phase.Iter;
- _phase = Phase.Opto;
- long t0 = System.currentTimeMillis();
+ public CodeGen opto() { return phase(Phase.Opto, _phase==Phase.Iter, () -> Opto.opto(this)); }
- Opto.opto(this);
-
- _times[Phase.Opto.ordinal()] = System.currentTimeMillis() - t0;
- if( _obs != null ) _obs.phase(_phase.name());
- return this;
- }
+ public CodeGen iterPostOpto() { return phase(Phase.IterPostOpto, _phase==Phase.Opto, () -> _iter.iterate(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;
- }
+ public CodeGen finalizeIR() { return phase(Phase.FinalizeIR, _phase==Phase.IterPostOpto, () -> FinalizeIR.run(this)); }
// Root a surviving function without claiming that it has unknown callers.
void keepInCompUnit(FunNode fun) {
@@ -597,58 +503,7 @@ public class CodeGen {
// ---------------------------
// Last check for bad programs
- public CodeGen typeCheck() {
- assert _phase == Phase.FinalizeIR;
- _phase = Phase.TypeCheck;
- long t0 = System.currentTimeMillis();
-
- // Check for class-init cycles first; such a cycle can prevent other
- // things from resolving types. The dependency graph is temporary.
- String clzInitCycle = new ClassInitDependencies(this).check();
- if( clzInitCycle != null )
- throw Parser.error(clzInitCycle,null);
-
- Ary<Node> errs = new Ary<>(Node.class);
- _stop.walk( n -> {
- if( n.err() != null )
- errs.add(n);
- return null;
- });
- _times[Phase.TypeCheck.ordinal()] = System.currentTimeMillis() - t0;
-
- if( !errs.isEmpty() ) {
- // Prefer an originating error over a secondary error caused by a
- // BOTTOM input. If all errors are downstream-poisoned, retain
- // the old first-error behavior.
- Node min = null;
- for( Node n : errs )
- if( noBottomInputs(n) )
- { min = n; break; }
- if( min==null )
- min = errs.at(0);
- for( Node n : errs )
- if( n!=min &&
- noBottomInputs(n) &&
- ((n instanceof MemOpNode nmop &&
- min instanceof MemOpNode mmop &&
- nmop._loc.before(mmop._loc)) ||
- (n instanceof CFGNode ncfg &&
- min instanceof CFGNode mincfg &&
- ncfg.idepth() < mincfg.idepth())) )
- min = n;
- if( _obs != null ) _obs.error(min, min.err().getMessage());
- throw min.err();
- }
- if( _obs != null ) _obs.phase("TypeCheck");
- return this;
- }
-
- private static boolean noBottomInputs(Node n) {
- for( Node def : n._inputs )
- if( def!=null && !(def instanceof FldOffNode) && def._type==Type.BOTTOM )
- return false;
- return true;
- }
+ public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.FinalizeIR, () -> TypeCheck.run(this)); }
public static TypeStruct requiredStruct(Node n) {
// CNC: remove? cleanup?
@@ -659,62 +514,21 @@ public class CodeGen {
}
// ---------------------------
+ // TypeCheck queued surviving CLInits during its existing error walk.
+ public CodeGen lower() {
+ return phase(Phase.Lower, (_phase==Phase.TypeCheck || _phase==Phase.Serialize), () -> _iter.iterate(this));
+ }
+
+ // ---------------------------
// Build the loop tree; break never-exit loops
public CodeGen loopTree() {
- assert _phase.ordinal() <= Phase.Serialize.ordinal();
- _phase = Phase.LoopTree;
- long t0 = System.currentTimeMillis();
- // Build the loop tree, fix never-exit loops
- _start.buildLoopTree( _linker, _stop);
- // Invariant theory: No more type or code changes after Opto. The
- // LoopTree stuff is used to do layouts now, but not core opts. TODO:
- // at some future date we'll do loop opts (e.g. peeling, unrolling)
- // that will totally impact code shape, types and core opts - but this
- // will be done in tandem with Opto changes.
- //_iter.iterate(this);
- _times[Phase.LoopTree.ordinal()] = System.currentTimeMillis() - t0;
- return this;
+ return phase(Phase.LoopTree, _phase==Phase.Lower, () -> _start.buildLoopTree(_linker,_stop));
}
// ---------------------------
public BAOS _serial;
public void serialize() {
- assert _phase.ordinal() <= Phase.Serialize.ordinal();
- _phase = Phase.Serialize;
- long t0 = System.currentTimeMillis();
- // Does not change compiler phase; just records IR
- Serialize.serialize(this);
- _times[Phase.Serialize.ordinal()] = System.currentTimeMillis() - t0;
- }
-
- // Unlink imported function bodies before serialization. Calls to them
- // remain as normal calls through their function pointer, and codegen later
- // turns them into external relocations.
- private void unlinkImports() {
- assert _phase.ordinal() <= Phase.Serialize.ordinal();
- // Imported function constants keep their symbolic identity, not an
- // embedded copy of the other object's Return and body.
- for( Node n : _start._outputs.asAry() )
- if( n instanceof FunPtrNode ptr && ptr.ret()!=null && !owns(ptr.fun()) ) {
- ptr.liftType(ptr._type);
- ptr.setDef(1,null);
- }
- for( FunNode fun : _linker ) {
- if( fun==null || fun.isDead() || owns(fun) )
- continue;
- for( int i=1; i<fun.nIns(); i++ )
- if( fun.in(i) instanceof CallNode call )
- call.unlink(fun,i--);
- }
- for( int i=0; i<_stop.nIns(); i++ ) {
- StopCUNode stop=(StopCUNode)_stop.in(i);
- CompUnit cu=_compunits.get(stop.start()._fname);
- if( cu._src==null ) _stop.delDef(i--);
- }
- // The whole-object wrappers now summarize only the emitted units.
- _stop._type=_stop.compute();
- _start._type=_start.compute();
- //_iter.iterate(this);
+ phase(Phase.Serialize, _phase==Phase.TypeCheck, () -> Serialize.serialize(this));
}
// ---------------------------
@@ -740,208 +554,15 @@ public class CodeGen {
// Convert to target hardware nodes
public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); }
public CodeGen instSelect( String cpu, String callingConv, String base ) {
- assert _phase.ordinal() <= Phase.Unlink.ordinal();
- _phase = Phase.Select;
-
- _callingConv = callingConv;
-
- // Not timing the class load...
- // Look for CPU in fixed named place:
- // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
- String clzFile = base+"."+cpu+"."+cpu;
- try { _mach = ((Class<Machine>) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); }
- catch( Exception e ) { throw new RuntimeException(e); }
-
- _cCallingConv = _mach.cCallingConv(callingConv);
-
- // Build global copies of common register masks.
- long callerSave = _mach.callerSave();
- long cCallerSave= _mach.callerSave(_cCallingConv);
- long neverSave = _mach. neverSave();
- int maxReg = Math.min(64,_mach.regs().length);
- assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
- assert maxReg>=64 || (-1L << maxReg & cCallerSave)==0; // No stack slots in cCallerSave
- _callerSave = new RegMask(callerSave);
- _cCallerSave= new RegMask(cCallerSave);
-
- // Build a Return RegMask array. All returns have the following inputs:
- // 0 - ctrl
- // 1 - memory
- // 2 - varies with returning GPR,FPR
- // 3 - RPC
- // 4+- Caller save registers
- _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4];
- for( int reg=0, i=4; reg<maxReg; reg++ )
- if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
- _retMasks[i++] = new RegMask(reg);
- _rpcMask = new RegMask(_mach.rpc());
- _retMasks[3] = _rpcMask;
-
- // Convert to machine ops
- long t0 = System.currentTimeMillis();
- _uid = 1; // All new machine nodes reset numbering
- var map = new IdentityHashMap<Node,Node>();
- _instSelect( _stop, map );
- _stop = (StopNode)map.get(_stop );
- StartNode start = (StartNode)map.get(_start);
- _start = start==null ? new StartNode(_start) : start;
- _instOuts(_stop,visit());
- _visit.clear();
-
- // Replace the CompUnit stop (and list of functions)
- // with hardware-specific ones
- for( CompUnit cu : _compunits.values() ) {
- cu._start= (StartCUNode)map.get(cu._start);
- cu._stop = (StopCUNode )map.get(cu._stop );
- }
-
- _times[Phase.Select.ordinal()] = System.currentTimeMillis() - t0;
- return this;
- }
-
- // Walk all ideal nodes, recursively mapping ideal to machine nodes, then
- // make a machine node for "this".
- private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
- if( n==null ) return null;
- if( n instanceof StoreNode st && StaticData.preinitialized(this,st) )
- return _instSelect(st.mem(),map);
- Node x = map.get(n);
- if( x !=null ) return x; // Been there, done that
-
- // If n is a MachNode already, then its part of a multi-node expansion.
- // It does not need instruction selection (already selected!)
- // but it does need its inputs walked.
- if( n instanceof MachNode ) {
- for( int i=0; i < n.nIns(); i++ )
- n._inputs.set(i, _instSelect(n.in(i),map) );
- return n;
- }
-
- // Produce a machine node from n; map it to flag as done so stops cycles.
- if( n instanceof MemOpNode mem && mem.off() instanceof ExternOffsetNode off ) {
- // External fields replace base+offset with symbol+0. Keep the ideal
- // declaring type on the address for machine load/store width selection.
- TypeMemPtr ptr = (TypeMemPtr)mem.ptr()._type;
- n._inputs.set(2,new ExternNode(TypeMemPtr.make((byte)2,ptr._obj,true),off._extern));
- n._inputs.set(3,ConstantNode.raw(TypeInteger.ZERO));
- }
- map.put(n, x=_mach.instSelect(n) );
- // Carry loop-tree and pre-order info across the ideal->mach transition
- if( n instanceof CFGNode ncfg && x instanceof CFGNode xcfg ) {
- xcfg._ltree = ncfg._ltree;
- xcfg._pre = ncfg._pre;
- }
- // Walk machine op and replace inputs with mapped inputs
- for( int i=0; i < x.nIns(); i++ )
- x._inputs.set(i, _instSelect(x.in(i),map) );
- // Post selection action
- if( x instanceof MachNode mach ) {
- if( n instanceof ReturnNode ret )
- ((ReturnNode)mach)._fun = (FunNode)map.get(ret._fun);
- mach.postSelect(this); // Post selection action
- }
-
- return x;
- }
-
-
- // Walk all machine Nodes, and set their output edges
- private void _instOuts( Node n, BitSet visit ) {
- if( visit.get(n._nid) ) return;
- visit.set(n._nid);
- for( Node in : n._inputs )
- if( in!=null ) {
- in._outputs.push(n);
- // CallNode special: outputs are partially ordered; CallEnd in slot 0
- if( in instanceof CallNode call && n instanceof CallEndNode cend && call.out(0) != cend )
- call._outputs.swap(0,call.nOuts()-1);
- _instOuts(in,visit);
- }
+ return InstSelect.select(this,cpu,callingConv,base);
}
// ---------------------------
public void unlink() {
- assert _phase.ordinal() <= Phase.Select.ordinal();
- _phase = Phase.Unlink;
- long t0 = System.currentTimeMillis();
-
- // The remaining passes assume all calls are unlinked; i.e. we are throwing
- // away the Call Graph here. Functions only reachable from internal calls
- // need to be re-hooked to stop/start less they go dead.
- for( FunNode fun : _linker ) {
- // Already linked to start, not going dead
- if( fun==null || fun.isDead() )
- continue;
-
- // Imported functions are not emitted by this object. Unlink any
- // concrete calls so they become normal external relocations.
- if( !owns(fun) ) {
- while( fun.nIns() > 1 ) {
- if( fun.in(1) instanceof CallNode call )
- call.unlink(fun,1);
- else
- fun.removeDeadPath(1);
- }
- StartCUNode start = fun._compunit._start;
- StopCUNode stop = fun._compunit._stop;
- // Unhook the Stop->Return edge also
- ReturnNode ret = fun.ret();
- int idx = stop._inputs.find(ret);
- if( idx != -1 ) {
- stop._inputs.del(idx);
- ret.delUse(stop);
- }
- //
- if( stop.nIns()==0 ) {
- idx = _stop._inputs.find(stop);
- if( idx != -1 )
- _stop.delDef(idx);
- if( start.nIns() > 0 && start.in(0) != null )
- start.setDef(0,null);
- if( start.nIns() > 1 && start.in(1) != null )
- start.setDef(1,null);
- }
- continue;
- }
-
- // Insert the post-Opto code-generation hook. Unlike an Opto-time
- // Start input, this no longer carries unknown-caller semantics.
- ReturnNode ret = fun.ret();
- StartNode start = fun._compunit._start;
- assert start!=null;
- if( fun.nIns() <= 1 || fun.in(1) != start ) {
- fun.insertDef(1,start);
- for( Node use : fun._outputs )
- if( use instanceof ParmNode parm )
- parm.insertDef(1,ConstantNode.raw(parm._type));
- if( fun._compunit._stop._inputs.find(ret) == -1 )
- fun._compunit._stop.addDef(ret);
- }
- // Unlink from Call
- for( int i=2; i<fun.nIns(); i++ )
- ((CallNode)fun.in(i)).unlink(fun,i--);
- if( fun.rpc()==null ) { // Ensure valid RPC
- // First make sure a valid RPC; single-call into multi-fun will have each
- // function having a single call site, so the RPC becomes a constant and
- // folds - but since multiple targets, the Call never inlines. Recreate a
- // valid RPC so codegen (and Eval2) understands the calling convention.
- ParmNode rpc = new ParmNode( "$rpc", 0, ret.rpc()._type, fun );
- rpc.addDef(ret.rpc());
- ret.setDef( 3, rpc.init() );
- }
- }
-
- _stop.walk(n -> {
- if( n instanceof CallEndNode cend )
- for( int i=1; i<cend.nIns(); i++ )
- if( cend.in(i) instanceof ReturnNode )
- cend.delDef(i--);
- if( n instanceof CallNode call )
- call.unlink_all();
- return null;
- });
-
- _times[Phase.Unlink.ordinal()] = System.currentTimeMillis() - t0;
+ unlink(true);
+ }
+ private void unlink(boolean removeImports) {
+ phase(Phase.Unlink, _phase==Phase.LoopTree, () -> Unlink.run(this,removeImports));
}
// ---------------------------
@@ -950,39 +571,22 @@ public class CodeGen {
// Global schedule (code motion) nodes
public CodeGen GCM() {
- assert _phase.ordinal() <= Phase.Select.ordinal();
- _phase = Phase.Schedule;
- long t0 = System.currentTimeMillis();
-
- GlobalCodeMotion.buildCFG(this);
- _times[Phase.Schedule.ordinal()] = System.currentTimeMillis() - t0;
-
- return this;
+ return phase(Phase.Schedule, (_phase==Phase.Unlink || _phase==Phase.Select), () -> GlobalCodeMotion.buildCFG(this));
}
// ---------------------------
// Local (basic block) scheduler phase, a classic list scheduler
- public CodeGen localSched() {
- assert _phase == Phase.Schedule;
- _phase = Phase.LocalSched;
- long t0 = System.currentTimeMillis();
- ListScheduler.sched(this);
- _times[Phase.LocalSched.ordinal()] = System.currentTimeMillis() - t0;
- return this;
- }
+ public CodeGen localSched() { return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this)); }
// ---------------------------
// Register Allocation
public RegAlloc _regAlloc;
public CodeGen regAlloc() {
- assert _phase == Phase.LocalSched;
- _phase = Phase.RegAlloc;
- long t0 = System.currentTimeMillis();
- _regAlloc = new RegAlloc(this);
- _regAlloc.regAlloc();
- _times[Phase.RegAlloc.ordinal()] = System.currentTimeMillis() - t0;
- return this;
+ return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> {
+ _regAlloc = new RegAlloc(this);
+ _regAlloc.regAlloc();
+ });
}
// Human-readable register name
@@ -998,32 +602,16 @@ public class CodeGen {
// ---------------------------
// Encoding
public Encoding _encoding;
- public CodeGen encode() {
- assert _phase == Phase.RegAlloc;
- _phase = Phase.Encoding;
- long t0 = System.currentTimeMillis();
-
- _encoding = new Encoding(this).encode();
-
- _times[Phase.Encoding.ordinal()] = System.currentTimeMillis() - t0;
- return this;
- }
+ public CodeGen encode() { return phase(Phase.Encoding, _phase==Phase.RegAlloc, () -> _encoding = new Encoding(this).encode()); }
// ---------------------------
// Exporting to external formats
ElfWriter _elf;
public CodeGen exportELF( boolean inMemory, boolean emitEntrySymbol ) {
- assert _phase == Phase.Encoding;
- _phase = Phase.Export;
- long t0 = System.currentTimeMillis();
- if( _encoding!=null ) {
- if( inMemory )
- new LinkMem(this).link(_encoding); // In memory patching
- else
- _elf = new ElfWriter(this).export(emitEntrySymbol);
- }
- _times[Phase.Export.ordinal()] = System.currentTimeMillis() - t0;
- return this;
+ return phase(Phase.Export, _phase==Phase.Encoding, () -> {
+ if( inMemory ) new LinkMem(this).link(_encoding);
+ else _elf = new ElfWriter(this).export(emitEntrySymbol);
+ });
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
index 1b6281b8..b3eac583 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ElfWriter.java
@@ -282,6 +282,8 @@ public class ElfWriter {
// ------------------------------------------------------------------------
public ElfWriter export(boolean emitEntrySymbol) {
+ if( _code._serial==null )
+ throw new IllegalStateException("ELF export needs ideal IR: request Export from the driver, or Serialize before Lower");
// Sections are created in the order they are emitted.
_sections = new Ary<>(Section.class);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
new file mode 100644
index 00000000..923486c6
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/FinalizeIR.java
@@ -0,0 +1,44 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import java.util.Arrays;
+
+// Comnpute field layouts only after SCCP and its peepholes converge.
+// Keep surviving functions rooted without introducing unknown callers.
+abstract class FinalizeIR {
+ static void run(CodeGen code) {
+ // Compute field layouts and replace symbolic field offsets by constants.
+ 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 : off._ts._name+"."+off._fname;
+ off.subsume(con);
+ i--;
+ }
+ }
+ code._iter.iterate(code); // Propagate field offsets and clean up.
+
+ // Retain surviving functions for emission without adding unknown callers.
+ for( FunNode fun : code._linker )
+ if( fun != null ) {
+ // A folded static function field can leave a detached body
+ // with internal cycles. It has no callers; later phases must
+ // not revive it merely because its nodes are still allocated.
+ if( fun.isDead() || fun._type.isHigh() ) {
+ code._linker.set(fun.sig().fidx(),null);
+ continue;
+ }
+ code.keepInCompUnit(fun);
+ }
+ for( CompUnit cu : code._compunits.values() ) {
+ ProjNode mem = cu._start.proj(1);
+ if( mem != null ) code.add(mem.unkeep());
+ }
+ // Stable print order: initializers first, then other 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) ));
+ }
+}
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 33d971e9..09eb8d33 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -60,7 +60,7 @@ public abstract class GlobalCodeMotion {
if( use==null || globals.containsKey(use) ) continue;
FunNode fun = useFun(use);
if( fun==null ) continue; // Global metadata has no function owner.
- assert CodeGen.CODE.owns(fun);
+ assert CodeGen.CODE._mach==null || CodeGen.CODE.owns(fun); // Ideal evaluation includes imported bodies.
var local = copies.computeIfAbsent(fun,f -> new IdentityHashMap<>());
Node copy = cloneGlobal(con,fun,globals,local);
for( int i=0; i<use.nIns(); i++ )
@@ -142,8 +142,9 @@ public abstract class GlobalCodeMotion {
// Record Load NIDs at all their CFG block choices, then check against
// Store block choices to force a Load above an anti-dependent Store.
int[] anti = new int[code.UID()];
+ Ary<Node> antideps = new Ary<>(Node.class);
// Breadth-first scheduling
- breadth(code._stop,ns,late,anti);
+ breadth(code._stop,ns,late,anti, antideps);
// Copy the best placement choice into the control slot
for( int i=0; i<late.length; i++ )
@@ -151,7 +152,7 @@ public abstract class GlobalCodeMotion {
ns[i].setDef(0,late[i]);
}
- private static void breadth(Node stop, Node[] ns, CFGNode[] late, int[] anti) {
+ private static void breadth(Node stop, Node[] ns, CFGNode[] late, int[] anti, Ary<Node> antideps) {
// Things on the worklist have some (but perhaps not all) uses done.
WorkList<Node> work = new WorkList<>();
work.push(stop);
@@ -172,12 +173,12 @@ public abstract class GlobalCodeMotion {
// Loads need their memory inputs' uses also done
if( n instanceof MemOpNode ld && ld._isLoad )
- for( Node memuse : ld.antiDeps() )
+ for( Node memuse : ld.antiDeps(antideps) )
if( late[memuse._nid]==null )
continue outer;
// All uses done, schedule
- _doSchedLate(n,ns,late,anti);
+ _doSchedLate(n,ns,late,anti,antideps);
}
// A use just finished; reconsider its inputs and waiting loads,
@@ -194,7 +195,7 @@ public abstract class GlobalCodeMotion {
}
}
- private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late, int[] anti) {
+ private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late, int[] anti, Ary<Node> antideps) {
// Walk uses, gathering the LCA (Least Common Ancestor) of uses
CFGNode early = n.in(0) instanceof CFGNode cfg ? cfg : n.in(0).cfg0();
assert early != null;
@@ -205,7 +206,7 @@ public abstract class GlobalCodeMotion {
// Loads may need anti-dependencies, raising their LCA
if( n instanceof MemOpNode load && load._isLoad )
- lca = find_anti_dep(lca,load,early,late,anti);
+ lca = find_anti_dep(lca,load,early,late,anti, antideps);
// Walk up from the LCA to the early, looking for best place. This is
// the lowest execution frequency, approximated by least loop depth and
@@ -256,38 +257,26 @@ public abstract class GlobalCodeMotion {
if( def.in(i)!=null ) wakeLoads(def.in(i),late,work,visit);
}
- private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late, int[] anti) {
+ private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late, int[] anti, Ary<Node> antideps) {
// We could skip final-field loads here.
// Walk LCA->early, flagging Load's block location choices
for( CFGNode cfg=lca; cfg!=early.idom(); cfg = cfg.idom() )
anti[cfg._nid] = load._nid;
// Walk load->mem uses, looking for Stores causing an anti-dep
- for( Node mem : load.antiDeps() ) {
- switch( mem ) {
- case MemOpNode st:
- assert late[st._nid]!=null;
- lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
- break;
- case EscapeNode st:
- assert late[st._nid]!=null;
- lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
- break;
- case CallNode call:
- 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.
+ for( Node mem : load.antiDeps(antideps) ) {
+ if( mem instanceof PhiNode phi ) {
+ // Inputs carrying different memory might overwrite this alias.
+ // Raise the LCA for those paths; the unchanged-memory paths
+ // do not clobber the load. No scheduling edge to the Phi.
for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
+ if( phi.in(i)!=load.mem() )
lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
- break;
- default: throw Utils.TODO("Should not reach here");
+ } else {
+ // Hunt for clobbering stores
+ assert mem instanceof MemOpNode || mem instanceof EscapeNode ||
+ mem instanceof CallNode || mem instanceof CLInitNode;
+ assert late[mem._nid]!=null;
+ lca = anti_dep(load,late[mem._nid],mem.cfg0(),lca,mem,anti);
}
}
return lca;
@@ -297,12 +286,15 @@ public abstract class GlobalCodeMotion {
private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
// A conditional writer can precede the load without dominating its late block.
// Walk back to the first overlap with the load's placement range.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // A different Phi input need not be dominated by this memory's
+ // definition. Stop at its depth rather than looking for that ancestor.
+ // Retain the sentinel too: a function entry has a null idom.
+ for( ; stblk != defblk.idom() && stblk.idepth() >= defblk.idepth(); stblk = stblk.idom() ) {
// Store and Load overlap, need anti-dependence
if( anti[stblk._nid]==load._nid ) {
lca = stblk.domLCA(lca,null); // Raise Loads LCA
if( lca == stblk && st != null && !(st instanceof CallNode) && st._inputs.find(load) == -1 ) // And if something moved,
- st.addDef(load); // Add anti-dep as well; Calls already end their block.
+ st.addDef(load); // Add anti-dep as well (calls already end their block)
return lca; // Cap this stores' anti-dep to here
}
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
new file mode 100644
index 00000000..5fc002a1
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java
@@ -0,0 +1,126 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import java.util.BitSet;
+import java.util.IdentityHashMap;
+
+// Replace ideal nodes with machine nodes while preserving CFG/loop metadata.
+// Selection also owns target ABI masks and rebuilding the copied graph's uses.
+class InstSelect {
+ private final CodeGen _code;
+ private InstSelect(CodeGen code) { _code=code; }
+
+ static CodeGen select(CodeGen code, String cpu, String callingConv, String base) {
+ // Ideal evaluation schedules the ideal graph without a CPU port.
+ if( cpu==null ) return code;
+ return code.phase(CodeGen.Phase.Select, code._phase==CodeGen.Phase.Unlink,
+ () -> new InstSelect(code).run(cpu,callingConv,base));
+ }
+
+ private void run(String cpu, String callingConv, String base) {
+ _code._callingConv = callingConv;
+
+ // Look for CPU in fixed named place:
+ // com.seaofnodes.simple.node.cpus."cpu"."cpu.class"
+ String clzFile = base+"."+cpu+"."+cpu;
+ try { _code._mach = Class.forName(clzFile).asSubclass(Machine.class).getDeclaredConstructor(CodeGen.class).newInstance(_code); }
+ catch( Exception e ) { throw new RuntimeException(e); }
+
+ _code._cCallingConv = _code._mach.cCallingConv(callingConv);
+
+ // Build global copies of common register masks.
+ long callerSave = _code._mach.callerSave();
+ long cCallerSave= _code._mach.callerSave(_code._cCallingConv);
+ long neverSave = _code._mach. neverSave();
+ int maxReg = Math.min(64,_code._mach.regs().length);
+ assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave
+ assert maxReg>=64 || (-1L << maxReg & cCallerSave)==0; // No stack slots in cCallerSave
+ _code._callerSave = new RegMask(callerSave);
+ _code._cCallerSave= new RegMask(cCallerSave);
+
+ // Build a Return RegMask array. All returns have the following inputs:
+ // 0 - ctrl
+ // 1 - memory
+ // 2 - varies with returning GPR,FPR
+ // 3 - RPC
+ // 4+- Caller save registers
+ _code._retMasks = new RegMask[(maxReg-_code._callerSave.size()-Long.bitCount( neverSave ))+4];
+ for( int reg=0, i=4; reg<maxReg; reg++ )
+ if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 )
+ _code._retMasks[i++] = new RegMask(reg);
+ _code._rpcMask = new RegMask(_code._mach.rpc());
+ _code._retMasks[3] = _code._rpcMask;
+
+ // Convert to machine ops
+ _code._uid = 1; // All new machine nodes reset numbering
+ var map = new IdentityHashMap<Node,Node>();
+ _instSelect( _code._stop, map );
+ _code._stop = (StopNode)map.get(_code._stop );
+ StartNode start = (StartNode)map.get(_code._start);
+ _code._start = start==null ? new StartNode(_code._start) : start;
+ _instOuts(_code._stop,_code.visit());
+ _code._visit.clear();
+
+ // Replace the CompUnit stop (and list of functions)
+ // with hardware-specific ones
+ for( CompUnit cu : _code._compunits.values() ) {
+ cu._start= (StartCUNode)map.get(cu._start);
+ cu._stop = (StopCUNode )map.get(cu._stop );
+ }
+ }
+
+ private Node _instSelect( Node n, IdentityHashMap<Node,Node> map ) {
+ if( n==null ) return null;
+ Node x = map.get(n);
+ if( x !=null ) return x; // Been there, done that
+
+ // If n is a MachNode already, then its part of a multi-node expansion.
+ // It does not need instruction selection (already selected!)
+ // but it does need its inputs walked.
+ if( n instanceof MachNode ) {
+ for( int i=0; i < n.nIns(); i++ )
+ n._inputs.set(i, _instSelect(n.in(i),map) );
+ return n;
+ }
+
+ // Produce a machine node from n; map it to flag as done so stops cycles.
+ if( n instanceof MemOpNode mem && mem.off() instanceof ExternOffsetNode off ) {
+ // External fields replace base+offset with symbol+0. Keep the ideal
+ // declaring type on the address for machine load/store width selection.
+ TypeMemPtr ptr = (TypeMemPtr)mem.ptr()._type;
+ n._inputs.set(2,new ExternNode(TypeMemPtr.make((byte)2,ptr._obj,true),off._extern));
+ n._inputs.set(3,ConstantNode.raw(TypeInteger.ZERO));
+ }
+ map.put(n, x=_code._mach.instSelect(n) );
+ // Carry loop-tree and pre-order info across the ideal->mach transition
+ if( n instanceof CFGNode ncfg && x instanceof CFGNode xcfg ) {
+ xcfg._ltree = ncfg._ltree;
+ xcfg._pre = ncfg._pre;
+ }
+ // Walk machine op and replace inputs with mapped inputs
+ for( int i=0; i < x.nIns(); i++ )
+ x._inputs.set(i, _instSelect(x.in(i),map) );
+ // Post selection action
+ if( x instanceof MachNode mach ) {
+ if( n instanceof ReturnNode ret )
+ ((ReturnNode)mach)._fun = (FunNode)map.get(ret._fun);
+ mach.postSelect(_code); // Post selection action
+ }
+
+ return x;
+ }
+
+ private void _instOuts( Node n, BitSet visit ) {
+ if( visit.get(n._nid) ) return;
+ visit.set(n._nid);
+ for( Node in : n._inputs )
+ if( in!=null ) {
+ in._outputs.push(n);
+ // CallNode special: outputs are partially ordered; CallEnd in slot 0
+ if( in instanceof CallNode call && n instanceof CallEndNode cend && call.out(0) != cend )
+ call._outputs.swap(0,call.nOuts()-1);
+ _instOuts(in,visit);
+ }
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Iter.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Iter.java
new file mode 100644
index 00000000..c5120370
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Iter.java
@@ -0,0 +1,60 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
+// Establish a pessimistic fixed point after parsing/loading. Later Iter runs
+// reuse the worklist and types produced by the preceding phase.
+abstract class Iter {
+ static void run(CodeGen code) {
+ // The phase shift closes all parser-in-progress states and freezes
+ // unknown external inputs. Imported nodes carry post-Opto escape
+ // precision, but Iter is a fresh pessimistic solve: memory escape
+ // summaries must restart at FULL so newly discovered private escapes
+ // can only be removed, never added. Seed the complete graph once.
+ boolean hasImports = code._externPaths != null;
+ code._start.walk(n -> {
+ if( !(n instanceof ConstantNode) )
+ n._type = hasImports ? iterImportReset(n._type) : iterEscapeReset(n._type);
+ code.add(n);
+ return null;
+ });
+ code._iter.iterate(code);
+ // Expensive assert.
+ assert !CodeGen.expensiveAssert() || Opto.fixedPointCheck(code);
+ }
+
+ private static Type iterEscapeReset(Type t) {
+ if( t instanceof TypeMem mem )
+ return mem._one ? mem : mem.makeFrom(XInt.FULL,XInt.FULL);
+ if( t instanceof TypeTuple tt ) {
+ TypeTuple rez = tt;
+ for( int i=0; i<tt._types.length; i++ ) {
+ Type tx = iterEscapeReset(tt._types[i]);
+ if( tx != tt._types[i] ) rez = rez.makeFrom(i,tx);
+ }
+ return rez;
+ }
+ return t;
+ }
+
+ private static Type iterImportReset(Type t) {
+ if( t.isHigh() )
+ return t.dual();
+ if( t instanceof TypeMem mem )
+ return TypeMem.make(mem._alias,iterImportReset(mem._t),mem._one,mem._clz,mem._final,XInt.FULL,XInt.FULL);
+ if( t instanceof TypeMemPtr )
+ return t.makeStorage();
+ if( t instanceof TypeStruct )
+ return t.makeStorage();
+ if( t instanceof TypeTuple tt ) {
+ TypeTuple rez = tt;
+ for( int i=0; i<tt._types.length; i++ ) {
+ Type tx = iterImportReset(tt._types[i]);
+ if( tx != tt._types[i] ) rez = rez.makeFrom(i,tx);
+ }
+ return rez;
+ }
+ return t;
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java
index 3a4596fe..fdde8838 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/LRG.java
@@ -62,6 +62,8 @@ public class LRG {
public int nadj() { return _adj==null ? 0 : _adj._len; }
+ public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); }
+
void addNeighbor(LRG lrg) {
if( _adj==null ) _adj = new Ary<>(LRG.class);
_adj.push(lrg);
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 c41a29ac..ef222f60 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java
@@ -73,8 +73,10 @@ abstract public class Opto {
FunNode fun = ret.fun();
// Start is a semantic unknown-caller edge. Public functions
// keep it; closed-world functions must be reached by a
- // concrete Call edge discovered during SCCP.
- if( !(code.owns(fun) && fun.isClz()) &&
+ // concrete Call edge discovered during SCCP. Initializers,
+ // including private/imported ones, are called by opaque CLInit
+ // obligations whose Call edges are introduced only in Lower.
+ if( !fun.isClz() &&
!code.publicFIDX(fun.sig().fidx()) &&
fun.in(1) instanceof StartNode ) {
fun.removeDeadPath(1);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
index bb123634..7ce727fd 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/ParseAll.java
@@ -13,6 +13,18 @@ import java.util.*;
// Parse a given file, and all its dependencies
public abstract class ParseAll {
+ static void parse(CodeGen code, String src) {
+ Parser.TYPES.clear();
+ Parser.TYPES.putAll(Parser.INIT_TYPES);
+ if( src != null ) {
+ // No source file, just the source itself.
+ parseSource(code,code._srcName==null ? "Test" : code._srcName,src);
+ } else {
+ // Path from module root to source file
+ parsePath(code,code._srcName);
+ }
+ }
+
// Worklist of symbols to search and files to parse
private static final Ary<CompUnit> WORK = new Ary<>(CompUnit.class);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 1ce68080..ac792988 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -518,7 +518,7 @@ public class RegAlloc {
// fixed register uses. Ordinary pressure splits keep the old shallow
// boundary behavior.
for( Node n : _ns ) {
- if( n instanceof SplitNode && min!=max ) continue; // Ignoring splits; since spilling need to split in a deeper loop
+ if( n instanceof SplitNode split && !split.isNonStackSplit() && min!=max ) continue; // Ordinary splits already reach stack storage.
if( n.isDead() ) continue; // Some Cloneable went dead by other spill changes
// If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user,
// commute the inputs... which chops the LHS live ranges' upper bound to just the RHS.
@@ -572,8 +572,8 @@ public class RegAlloc {
}
private static long ldepth( long ld, Node n, CFGNode cfg ) {
- // Do not count splits
- if( n instanceof SplitNode ) return ld;
+ // Ordinary splits already reach stack storage; register-only transfers do not.
+ if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld;
// Collect min/max loop depth
int min = (int)ld;
int max = (int)(ld>>32);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
index 1806be24..6a23252d 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Serialize.java
@@ -8,6 +8,13 @@ import java.util.*;
abstract public class Serialize {
+ // Encoding/emulation need only machine code. File export also needs ideal
+ // IR for a later compiler to import; an explicit Serialize request stops here.
+ static void forTarget(CodeGen code, CodeGen.Phase target, boolean inMemory) {
+ if( target==CodeGen.Phase.Serialize || (target.ordinal()>=CodeGen.Phase.Export.ordinal() && !inMemory) )
+ code.serialize();
+ }
+
static void serialize( CodeGen code ) {
// Get all Nodes in a sane order
@@ -68,6 +75,15 @@ abstract public class Serialize {
// Initialize the mapping from bits/tags to types
Type.TAGOFFS();
+ IdentityHashMap<Node,Integer> anodes = new IdentityHashMap<>();
+ for( Node n : nodes ) anodes.put(n,anodes.size()+1); // 0 is null.
+ // The saved wrappers summarize this object's units only. Keep the
+ // live whole-program graph intact for subsequent lowering.
+ TypeTuple stopType = TypeTuple.STOP_HIGH;
+ for( Node n : nodes.last()._inputs )
+ if( anodes.containsKey(n) ) stopType = (TypeTuple)stopType.meet(n._type);
+ Type startType = TypeTuple.make(Type.CONTROL,stopType.mem(),((StartNode)nodes.at(0)).arg());
+
BAOS baos = new BAOS();
// A - Print the C0DE header. The format is not versioned yet.
baos.write('C').write('0').write('D').write('E');
@@ -91,6 +107,8 @@ abstract public class Serialize {
if( n instanceof ParmNode parm ) parm._declaredType.gather(types);
if( n instanceof TypeNode con ) con._con .gather(types);
}
+ stopType.gather(types);
+ startType.gather(types);
// A defined class remains importable even when its initializer body dies.
for( TypeStruct ts : pubTypes )
if( Parser.startsClzPrefix(ts._name) &&
@@ -190,21 +208,20 @@ abstract public class Serialize {
// How many nodes?
baos.packed4(nodes._len);
- // Radix sort by Node ID#, new compressed NIDs
- IdentityHashMap<Node,Integer> anodes = new IdentityHashMap<>();
- for( Node n : nodes )
- anodes.put(n,anodes.size()+1); // +1 bias, reserve 0 for null
// First cut: 1 byte opcode, optional per-node info, includes variable nIns
for( Node n : nodes ) {
baos.packed1(n.serialTag().ordinal());
- baos.packed2(types.get(n._type));
+ baos.packed2(types.get(n==nodes.at(0) ? startType : n==nodes.last() ? stopType : n._type));
n.packed(baos,strs,types,anodes);
}
// Write out the input indices packed.
for( Node n : nodes ) {
for( int j=0; j<n.nIns(); j++ ) {
+ if( !writeInput(n,j,anodes) ) continue;
Node def = n.in(j);
Integer idx = def==null ? Integer.valueOf(0) : anodes.get(def);
+ // Imported function constants retain their symbol, not a body.
+ if( idx==null && n instanceof FunPtrNode && j==1 ) idx=0;
assert idx != null : "Missing serialized input for "+n+" input "+j+" def "+def;
baos.packed2(idx);
}
@@ -214,6 +231,22 @@ abstract public class Serialize {
return baos;
}
+
+ // Omit cross-object call-graph links without unlinking the live IR.
+ // A parameter follows exactly the same retained callers as its function.
+ private static boolean writeInput(Node n, int i, IdentityHashMap<Node,Integer> nodes) {
+ if( n instanceof ParmNode parm ) n=parm.fun();
+ if( (n instanceof FunNode || n instanceof CallEndNode) && i>0 )
+ return nodes.containsKey(n.in(i));
+ if( n instanceof StopNode ) return nodes.containsKey(n.in(i));
+ return true;
+ }
+
+ public static int nIns(Node n, IdentityHashMap<Node,Integer> nodes) {
+ int len=0;
+ for( int i=0; i<n.nIns(); i++ ) if( writeInput(n,i,nodes) ) len++;
+ return len;
+ }
public static <T> void gather( HashMap<T,Integer> strs, T s) {
if( s!=null && !strs.containsKey(s) )
strs.put(s,strs.size());
@@ -450,7 +483,6 @@ abstract public class Serialize {
// --------------------------------------------------
public static Ary<Node> nodeOrder( CodeGen code, CompUnit cu ) {
- assert code._start._ltree != null; // Uses loop tree
BitSet visit = code.visit();
Ary<Node> nodes = new Ary<>(Node.class);
visit.set(code._stop._nid);
@@ -501,7 +533,6 @@ abstract public class Serialize {
// --------------------------------------------------
public static Ary<Node> nodeOrder( CodeGen code ) {
- assert code._start._ltree != null; // Uses loop tree
Ary<Node> nodes = new Ary<>(Node.class);
BitSet visit = code.visit();
nodes.add(code._start);
@@ -555,6 +586,9 @@ abstract public class Serialize {
private static void _funWalk( FunNode fun, Ary<Node> rpo, BitSet visit ) {
int start = rpo._len;
_funRPO(fun,rpo,visit);
+ // A nonreturning function still owns a Return, even before LoopTree
+ // inserts synthetic exits. No loop-depth ordering is needed on disk.
+ _funRPO(fun.ret(),rpo,visit);
int len = rpo._len - start;
for( int i=0; i< len>>1; i++ )
rpo.swap(start+i,start+len-1-i);
@@ -566,13 +600,9 @@ abstract public class Serialize {
visit.set(n._nid); // Stop recursion
// Walk outputs ordered
if( n instanceof CFGNode ncfg ) {
- // If nodes walk outer loops before inner, so they hit the
- // Return first, so Return is at the bottom of the RPO.
if( n instanceof IfNode iff ) {
CProjNode c0 = iff.cproj(0);
CProjNode c1 = iff.cproj(1);
- if( c0._ltree.depth() > c1._ltree.depth() )
- { c0 = c1; c1 = iff.cproj(0); }
_funRPO(c0,rpo,visit);
_funRPO(c1,rpo,visit);
} else if( n instanceof StopNode ) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/StaticData.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/StaticData.java
index f9f16008..616753d4 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/StaticData.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/StaticData.java
@@ -56,24 +56,6 @@ public final class StaticData {
return owner;
}
- // A final field whose declaration already denotes this exact constant is
- // available as that value to every ideal Load. Materialize it in the object
- // image instead of emitting the redundant initializer Store. This lowering
- // leaves serialized ideal IR intact, so imports retain their memory edges.
- static boolean preinitialized(CodeGen code,StoreNode st) {
- if( !(st.ptr()._type instanceof TypeMemPtr ptr) || !ptr._one || !isClass(ptr._obj) ) return false;
- CompUnit owner=owner(code,ptr._obj);
- if( owner==null || owner._src==null ) return false;
- if( !(Parser.TYPES.get(ptr._obj._name) instanceof TypeStruct ts) ) return false;
- Field fld=ts.field(st._name);
- if( fld==null || !fld._final || !fld._t.isConstant() || !st.val()._type.isConstant() ) return false;
- Type value=st.val()._type, declared=fld._t;
- if( declared instanceof TypeFunPtr fp && value instanceof TypeFunPtr vp ) return fp.fidx()==vp.fidx();
- if( declared instanceof TypeMemPtr ptr0 && value instanceof TypeMemPtr ptr1 )
- return pointerConstant(ptr0) && ptr0._obj==ptr1._obj;
- return declared==value;
- }
-
ObjectData object(Type t) {
Object key=isClass(t) ? ((TypeStruct)t)._name
: t instanceof TypeFunPtr fp ? functionSymbol(fp) : t;
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
new file mode 100644
index 00000000..9b4b0995
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java
@@ -0,0 +1,57 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.util.Ary;
+
+// Reject invalid optimized programs before lowering erases their source-level
+// obligations. Queue surviving CLInits during the same walk for Lower's Iter.
+abstract class TypeCheck {
+ static void run(CodeGen code) {
+ // Check for class-init cycles first; such a cycle can prevent other
+ // things from resolving types. The dependency graph is temporary.
+ String clzInitCycle = new ClassInitDependencies(code).check();
+ if( clzInitCycle != null )
+ throw Parser.error(clzInitCycle,null);
+
+ Ary<Node> errs = new Ary<>(Node.class);
+ code._stop.walk( n -> {
+ if( n.err() != null )
+ errs.add(n);
+ if( n instanceof CLInitNode ) code.add(n);
+ return null;
+ });
+
+ if( !errs.isEmpty() ) {
+ // Prefer an originating error over a secondary error caused by a
+ // BOTTOM input. If all errors are downstream-poisoned, retain
+ // the old first-error behavior.
+ Node min = null;
+ for( Node n : errs )
+ if( noBottomInputs(n) )
+ { min = n; break; }
+ if( min==null )
+ min = errs.at(0);
+ for( Node n : errs )
+ if( n!=min &&
+ noBottomInputs(n) &&
+ ((n instanceof MemOpNode nmop &&
+ min instanceof MemOpNode mmop &&
+ nmop._loc.before(mmop._loc)) ||
+ (n instanceof CFGNode ncfg &&
+ min instanceof CFGNode mincfg &&
+ ncfg.idepth() < mincfg.idepth())) )
+ min = n;
+ if( code._obs != null ) code._obs.error(min, min.err().getMessage());
+ throw min.err();
+ }
+ }
+
+ private static boolean noBottomInputs(Node n) {
+ for( Node def : n._inputs )
+ if( def!=null && !(def instanceof FldOffNode) && def._type==Type.BOTTOM )
+ return false;
+ return true;
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Unlink.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Unlink.java
new file mode 100644
index 00000000..fb1c4a6f
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Unlink.java
@@ -0,0 +1,112 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.simple.node.*;
+
+// Discard call-graph edges after LoopTree. Machine selection keeps only this
+// object's bodies; ideal evaluation keeps imports so their calls can execute.
+abstract class Unlink {
+ private static void unlinkImports(CodeGen code) {
+ // Imported function constants keep their symbolic identity, not an
+ // embedded copy of the other object's Return and body.
+ for( Node n : code._start._outputs.asAry() )
+ if( n instanceof FunPtrNode ptr && ptr.ret()!=null && !code.owns(ptr.fun()) ) {
+ ptr.liftType(ptr._type);
+ ptr.setDef(1,null);
+ }
+ for( FunNode fun : code._linker ) {
+ if( fun==null || fun.isDead() || code.owns(fun) )
+ continue;
+ for( int i=1; i<fun.nIns(); i++ )
+ if( fun.in(i) instanceof CallNode call )
+ call.unlink(fun,i--);
+ }
+ for( int i=0; i<code._stop.nIns(); i++ ) {
+ StopCUNode stop=(StopCUNode)code._stop.in(i);
+ CompUnit cu=code._compunits.get(stop.start()._fname);
+ if( cu._src==null ) code._stop.delDef(i--);
+ }
+ // The whole-object wrappers now summarize only the emitted units.
+ code._stop ._type=code._stop .compute();
+ code._start._type=code._start.compute();
+ }
+
+ static void run(CodeGen code, boolean removeImports) {
+ if( removeImports ) unlinkImports(code);
+
+ // The remaining passes assume all calls are unlinked; i.e. we are throwing
+ // away the Call Graph here. Functions only reachable from internal calls
+ // need to be re-hooked to stop/start less they go dead.
+ for( FunNode fun : code._linker ) {
+ // Already linked to start, not going dead
+ if( fun==null || fun.isDead() )
+ continue;
+
+ // Imported functions are not emitted by this object. Ideal
+ // evaluation retains them so their calls can actually execute.
+ if( removeImports && !code.owns(fun) ) {
+ while( fun.nIns() > 1 ) {
+ if( fun.in(1) instanceof CallNode call )
+ call.unlink(fun,1);
+ else
+ fun.removeDeadPath(1);
+ }
+ StartCUNode start = fun._compunit._start;
+ StopCUNode stop = fun._compunit._stop;
+ // Unhook the Stop->Return edge also
+ ReturnNode ret = fun.ret();
+ int idx = stop._inputs.find(ret);
+ if( idx != -1 ) {
+ stop._inputs.del(idx);
+ ret.delUse(stop);
+ }
+ //
+ if( stop.nIns()==0 ) {
+ idx = code._stop._inputs.find(stop);
+ if( idx != -1 )
+ code._stop.delDef(idx);
+ if( start.nIns() > 0 && start.in(0) != null )
+ start.setDef(0,null);
+ if( start.nIns() > 1 && start.in(1) != null )
+ start.setDef(1,null);
+ }
+ continue;
+ }
+
+ // Insert the post-Opto code-generation hook. Unlike an Opto-time
+ // Start input, this no longer carries unknown-caller semantics.
+ ReturnNode ret = fun.ret();
+ StartNode start = fun._compunit._start;
+ assert start!=null;
+ if( fun.nIns() <= 1 || fun.in(1) != start ) {
+ fun.insertDef(1,start);
+ for( Node use : fun._outputs )
+ if( use instanceof ParmNode parm )
+ parm.insertDef(1,ConstantNode.raw(parm._type));
+ if( fun._compunit._stop._inputs.find(ret) == -1 )
+ fun._compunit._stop.addDef(ret);
+ }
+ // Unlink from Call
+ for( int i=2; i<fun.nIns(); i++ )
+ ((CallNode)fun.in(i)).unlink(fun,i--);
+ if( fun.rpc()==null ) { // Ensure valid RPC
+ // First make sure a valid RPC; single-call into multi-fun will have each
+ // function having a single call site, so the RPC becomes a constant and
+ // folds - but since multiple targets, the Call never inlines. Recreate a
+ // valid RPC so codegen (and Eval2) understands the calling convention.
+ ParmNode rpc = new ParmNode( "$rpc", 0, ret.rpc()._type, fun );
+ rpc.addDef(ret.rpc());
+ ret.setDef( 3, rpc.init() );
+ }
+ }
+
+ code._stop.walk(n -> {
+ if( n instanceof CallEndNode cend )
+ for( int i=1; i<cend.nIns(); i++ )
+ if( cend.in(i) instanceof ReturnNode )
+ cend.delDef(i--);
+ if( n instanceof CallNode call )
+ call.unlink_all();
+ return null;
+ });
+ }
+}
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 850a5711..c6a6ff3b 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/BulkMemPhiNode.java
@@ -125,7 +125,7 @@ public class BulkMemPhiNode extends PhiNode {
// True if the MemMerge has no precise slice still covered by this Phi.
private boolean canPeek(MemMergeNode mmm) {
- return missingAlias(mmm,false)==0;
+ return missingAlias(mmm)==0;
}
// Return an alias required by an input but still covered by this Phi, or 0.
@@ -137,7 +137,7 @@ public class BulkMemPhiNode extends PhiNode {
assert !(con._con instanceof TypeMem tmem && tmem._alias != 1);
yield 0;
}
- case MemMergeNode mmm -> missingAlias(mmm,false);
+ case MemMergeNode mmm -> missingAlias(mmm);
case MemOpNode mem -> unsplit(mem._alias);
case BulkMemPhiNode bulk -> missingAlias(bulk);
default -> throw Utils.TODO("Should not reach here");
@@ -154,7 +154,7 @@ public class BulkMemPhiNode extends PhiNode {
return switch(use) {
case ScopeNode scope -> 0;
case ParmNode parm -> 0;
- case MemMergeNode mmm -> missingAlias(mmm,true);
+ case MemMergeNode mmm -> missingAlias(mmm);
case BulkMemPhiNode bulk -> missingAlias(bulk);
case MemOpNode mem -> unsplit(mem._alias);
case MemPhiNode phi -> unsplit(phi._alias);
@@ -183,14 +183,14 @@ public class BulkMemPhiNode extends PhiNode {
return 0;
}
- // First precise MemMerge slice still covered by this Phi. When inspecting
- // a user, ignore slots which point back to this Phi.
- private int missingAlias(MemMergeNode mmm, boolean user) {
+ // First explicit MemMerge slice still covered by this Phi. A user's slice
+ // may point to this Phi after a precise Phi folds; splitting is still valid.
+ private int missingAlias( MemMergeNode mmm ) {
for( int alias=2; alias<mmm.nIns(); alias++ )
if( mmm.in(alias)!=null &&
mmm.alias(alias)!=mmm.in(1) &&
!_aliases.get(alias) )
- { assert (!user || mmm.in(alias)!=this); return alias; }
+ return alias;
return 0;
}
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 9a41cc18..7bc17c75 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CLInitNode.java
@@ -99,7 +99,65 @@ public class CLInitNode extends CFGNode implements MultiNode {
return CodeGen.CODE.con(((TypeTuple)_type)._types[2]);
}
- @Override public Node idealize() { return null; }
+ @Override public Node idealize() {
+ if( CodeGen.CODE._phase!=CodeGen.Phase.Lower || bypass() || in(0)._type.isHigh() ) return null;
+ if( CodeGen.CODE._midAssert ) return this;
+ // Until entry arguments become arrays, ordinary initializers receive zero.
+ return expand(CodeGen.CODE.con(TypeInteger.ZERO));
+ }
+
+ // Called only after cycle/type checking. The initialized arm skips the
+ // call; both arms rejoin with whole memory and a known-true flag slice.
+ private Node expand(Node arg) {
+ FunNode target = CodeGen.CODE.link(cu());
+ assert target!=null && !target.isDead();
+ keep();
+ Node ctrl = in(0);
+ Node memory = mem();
+
+ // A child body may assume its parent has finished. Establish that
+ // requirement first, then test the child's flag in the resulting memory.
+ CompUnit parent = cu()._par;
+ if( parent!=null ) {
+ Node ptr = CodeGen.CODE.con(TypeMemPtr.make((byte)2,parent._clz,true));
+ Field flag = parent._clz.field("$init");
+ Node off = CodeGen.CODE.con(parent._clz.offset(parent._clz.find("$init")));
+ Node parentReady = CodeGen.CODE.add(new LoadNode(_loc,"$init",flag._alias,ctrl,memory,ptr,off).init());
+ CLInitNode init = CodeGen.CODE.add(new CLInitNode(_loc,ctrl,memory,ptr,parentReady).init());
+ ctrl = CodeGen.CODE.add(new CProjNode(init,0,ScopeNode.CTRL).init());
+ memory = CodeGen.CODE.add(new ProjNode(init,1,ScopeNode.MEM0).init());
+ }
+
+ Node childOff = CodeGen.CODE.con(cu()._clz.offset(cu()._clz.find("$init")));
+ // Read the child's flag after establishing its parent's memory effects.
+ Node ready = CodeGen.CODE.add(new LoadNode(_loc,"$init",flag()._alias,ctrl,memory,ptr(),childOff).init());
+
+ IfNode iff = CodeGen.CODE.add(new IfNode(ctrl,ready).init());
+ Node done = CodeGen.CODE.add(new CProjNode(iff,0,"True").init());
+ Node cold = CodeGen.CODE.add(new CProjNode(iff,1,"False").init());
+
+ Node fptr = new FunPtrNode(target.sig(),CodeGen.CODE._start,target.ret()).peephole();
+ CallNode call = CodeGen.CODE.add(new CallNode(_loc,cold,memory,ptr(),arg,fptr).init());
+ CallEndNode end = new CallEndNode(call,TypeRPC.constant(CodeGen.CODE.rpc(_loc._cu._cname)));
+ // This is a lowered call, after interprocedural analysis. Do not add
+ // new parameter flows to a body already optimized by SCCP.
+ CodeGen.CODE.add(end.init());
+ Node called = CodeGen.CODE.add(new CProjNode(end,0,ScopeNode.CTRL).init());
+ Node after = CodeGen.CODE.add(new ProjNode(end,1,ScopeNode.MEM0).init());
+ RegionNode region = CodeGen.CODE.add(new RegionNode(_loc,null,done,called).init());
+ Node merged = CodeGen.CODE.add(new BulkMemPhiNode(ScopeNode.MEM0,region,memory,after).init());
+
+ // Replace the individual outputs; no tuple-pack node or second
+ // representation of CLInit is needed while the projections collapse.
+ for( Node use : _outputs.asAry() ) {
+ if( use==null ) continue; // Our temporary keep.
+ assert use instanceof Proj;
+ int idx = ((Proj)use).idx();
+ use.subsume(idx==0 ? region : idx==1 ? merged : CodeGen.CODE.con(((TypeTuple)_type)._types[2]));
+ }
+ CodeGen.CODE.invalidateIDepthCaches();
+ return unkeep(); // Iter removes this now-unused node.
+ }
// Resolve a precise flag slice even when the parser did not yet know the
// class/alias and could only install the whole-memory projection.
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 555d0c65..cfc58ce9 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -40,7 +40,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
}
@Override public Tag serialTag() { return Tag.CallEnd; }
public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
- baos.packed1(nIns());
+ baos.packed1(Serialize.nIns(this,anodes));
// Linked CallEnds depend on Return types which depend on CallEnds;
// break the cycle
baos.packed2(types.get(_type));
@@ -108,7 +108,9 @@ public class CallEndNode extends CFGNode implements MultiNode {
// effects.
if( (nIns()-1)+externs < tfp.nfcns() ) {
// If during Opto, assume call won't be called and thus won't return anything.
- boolean opto = CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal();
+ // Lowered calls deliberately have no links into optimized bodies.
+ boolean opto = CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal() &&
+ CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Lower.ordinal();
Type tmem = opto ? TypeMem.TOP : TypeMem.BOT;
Type ret = opto ? Type.TOP : tfp.ret();
return TypeTuple.make(Type.CONTROL,tmem,ret);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
index b4bb5e72..71f6d146 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -101,6 +101,9 @@ public class CallNode extends CFGNode {
@Override
public Node idealize() {
+ // Lowering may introduce calls after the call graph has been analyzed.
+ // These use the callee interface without reopening its parameter flow.
+ if( CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Lower.ordinal() ) return null;
CallEndNode cend = cend();
if( cend==null ) return null; // Still building
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 4357570e..0569f352 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 FinalizeIR. A unique internal
+ // Semantic constant construction through Lower. 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.FinalizeIR.ordinal() )
+ CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Lower.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/FunNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java
index 7f9a2819..edde247a 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -45,7 +45,7 @@ public class FunNode extends RegionNode {
@Override public Tag serialTag() { return Tag.Fun; }
@Override public void packed(BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node,Integer> anodes ) {
assert !_folding;
- baos.packed1(nIns()); // Number of linked calls
+ baos.packed1(Serialize.nIns(this,anodes)); // Object-local linked calls
baos.packed2(types.get(_sig)); // NPE if fails lookup
baos.packed2(_name==null ? 0 : strs.get(_name));
baos.packed2(_approxUIDs);
@@ -171,7 +171,8 @@ public class FunNode extends RegionNode {
// through class-field loads and other initially imprecise values.
// - FIDX is not in the escape set
// - Not called anywhere else; post Opto called-not-escaped functions need to still be hooked to start
- if( in(1) instanceof StartCUNode start ) {
+ // Opaque CLInit obligations need their bodies even without Call edges.
+ if( !isClz() && in(1) instanceof StartCUNode start ) {
boolean preOpto = CodeGen.CODE._phase == null ||
CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal();
if( !preOpto && !start.escapedFIDX(_sig.fidx()) && nIns()<=2 && _ret!=null ) {
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 b31e14d5..bfbd46c3 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -6,13 +6,9 @@ import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.GlobalBits;
import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.util.BAOS;
-import com.seaofnodes.simple.util.Utils;
+import com.seaofnodes.simple.util.*;
import java.lang.StringBuilder;
-import java.util.BitSet;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.IdentityHashMap;
+import java.util.*;
/**
* Convenience common base for Load and Store.
@@ -140,27 +136,24 @@ public abstract class MemOpNode extends TypeNode {
// Memory writers which must follow this read. A packaging node is not a
// write; follow this alias through aggregates to its writers.
- public ArrayList<Node> antiDeps() {
- ArrayList<Node> deps = new ArrayList<>();
- antiDeps(mem(),deps,new BitSet());
- return deps;
+ public Ary<Node> antiDeps( Ary<Node> antideps ) {
+ antideps.clear();
+ return antiDeps(mem(),antideps);
}
- private void antiDeps(Node mem, ArrayList<Node> deps, BitSet visit) {
- if( visit.get(mem._nid) ) return;
- visit.set(mem._nid);
+ private Ary<Node> antiDeps( Node mem, Ary<Node> antideps ) {
for( Node use : mem._outputs ) {
if( use instanceof MemMergeNode merge ) {
- Node slice = _alias<merge.nIns() ? merge.in(_alias) : null;
- if( (slice==mem) || (slice==null && merge.in(1)==mem) )
- antiDeps(merge,deps,visit);
+ if( merge.alias(_alias)==mem )
+ antiDeps(merge,antideps);
} 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) ||
(use instanceof BulkMemPhiNode phi && !phi.isSplit(_alias)) ) {
- if( !deps.contains(use) ) deps.add(use);
+ if( !antideps.contains(use) ) antideps.add(use);
}
}
+ return antideps;
}
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index 899d7639..278c5308 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.util.BAOS;
import java.util.HashMap;
@@ -23,7 +24,7 @@ public class ParmNode extends PhiNode {
public ParmNode(ParmNode parm) { super(parm, parm._label ); _idx = parm._idx; _declaredType = parm._declaredType; }
@Override public Tag serialTag() { return Tag.Parm; }
@Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) {
- baos.packed1(nIns()); // Number of linked calls
+ baos.packed1(Serialize.nIns(this,anodes)); // Object-local linked calls
baos.packed2(_label==null ? 0 : strs.get(_label));
baos.packed2(types.get(_declaredType)); // NPE if fails lookup
baos.packed1(_idx);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java
index 0e104c15..3042f857 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/SplitNode.java
@@ -13,6 +13,8 @@ public abstract class SplitNode extends MachConcreteNode {
public final String _kind; // Kind of split
public final byte _round; // Debugging info for which RegAlloc round
public SplitNode(LRG lrg,String kind, byte round, Node[] nodes) { super(nodes); _lrg=lrg; _kind = kind; _round = round; }
+ // A register-only transfer may need another split to reach stack storage.
+ public boolean isNonStackSplit() { return false; }
@Override public String op() { return "mov"; }
@Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) {
p.p("mov(");
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 84c8a3a7..495be0a2 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -45,11 +45,11 @@ public class StartNode extends LoopNode implements MultiNode {
// Find module.<clinit>, it's the start.
CFGNode C = null;
for( Node use : _outputs )
- if( use instanceof FunNode fun && fun.isModInit() )
+ if( use instanceof FunNode fun && CodeGen.CODE.entryClinitName().equals(fun._name) )
{ assert C==null; C = fun; }
if( C==null )
for( FunNode fun : CodeGen.CODE._linker )
- if( fun!=null && !fun.isDead() && fun.isModInit() )
+ if( fun!=null && !fun.isDead() && CodeGen.CODE.entryClinitName().equals(fun._name) )
{ assert C==null; C = fun; }
return C;
}
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 f2bd5841..d3a356ed 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StopCUNode.java
@@ -41,7 +41,7 @@ public class StopCUNode extends StopNode {
// below must not forget private bits already discovered precisely from
// that Return on an earlier SCCP step.
if( CodeGen.CODE._phase.ordinal() >= CodeGen.Phase.Opto.ordinal() &&
- CodeGen.CODE._phase.ordinal() <= CodeGen.Phase.FinalizeIR.ordinal() &&
+ CodeGen.CODE._phase.ordinal() <= CodeGen.Phase.Lower.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));
@@ -102,7 +102,7 @@ public class StopCUNode extends StopNode {
// input refinement add bits, reversing Iter's monotonic direction.
// This normalization is needed only for Opto's optimistic grand cycle.
if( CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() ||
- CodeGen.CODE._phase.ordinal() > CodeGen.Phase.FinalizeIR.ordinal() ) return mem;
+ CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Lower.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/StopNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java
index e52a49a2..a7e52563 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -3,6 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.Serialize;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.BAOS;
@@ -18,7 +19,7 @@ public class StopNode extends CFGNode {
}
public StopNode(StopNode stop) { super(stop); }
@Override public Tag serialTag() { return Tag.Stop; }
- @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(nIns()); }
+ @Override public void packed( BAOS baos, HashMap<String,Integer> strs, HashMap<Type,Integer> types, IdentityHashMap<Node, Integer> anodes ) { baos.packed1(Serialize.nIns(this,anodes)); }
static Node make( BAOS bais ) {
StopNode stop = new StopNode();
stop.setDefX(bais.packed1()-1,null);
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 418b1e92..8a0bf8d4 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -89,6 +89,16 @@ public class StoreNode extends MemOpNode {
Type decl = declaredType();
if( decl != Type.BOTTOM )
{ assert decl != Type.TOP; val = val.join(decl); }
+ // A singleton class's final constant field lives in the static image.
+ // Its precise memory slice is a constant too; ordinary peepholes can
+ // replace this Store before scheduling or instruction selection.
+ // A constant runtime value alone is not enough: mutable fields (notably
+ // $init) still need their writes, and instance fields need allocation.
+ Field fld = ptr._obj.field(_name);
+ if( _alias!=1 && ptr._one && Parser.startsClzPrefix(ptr._obj._name) &&
+ fld!=null && fld._final && decl.isConstant() )
+ return TypeMem.make(_alias,val,true,true,true,mem._escFs,mem._escAs).escapesFrom(val);
+
// Allocation uses a private TypeMem and nothing else does. This
// memory is truly private; a temporary singleton until it escapes -
// which is never does in a constructor.
@@ -108,7 +118,6 @@ public class StoreNode extends MemOpNode {
Type t = val.meet(tfld);
// Bulk memory's final bit describes its conservative remainder, not
// this newly precise slice. Slice finality comes from the field.
- Field fld = ptr._obj.field(_name);
boolean xfinal = fld != null && fld._final;
return TypeMem.make(_alias,t,mem._one,mem._clz,xfinal,mem._escFs,mem._escAs).escapesFrom(val);
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
new file mode 100644
index 00000000..25e5d501
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java
@@ -0,0 +1,21 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.isa.Arm64;
+import com.seaofnodes.simple.codegen.*;
+
+// NZCV transfers require a GPR. Further splits can spill that GPR to the stack.
+public class SplitFlagsARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,1L);
+ SplitFlagsARM(LRG lrg,String kind,byte round) { super(lrg,kind,round); }
+ @Override public boolean isNonStackSplit() { return true; }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( src==arm.FLAGS ) enc.add4(Arm64.flags(dst,false));
+ else if( dst==arm.FLAGS ) enc.add4(Arm64.flags(src,true));
+ else super.encoding(enc);
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
new file mode 100644
index 00000000..c9b2127a
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java
@@ -0,0 +1,11 @@
+package com.seaofnodes.simple.node.cpus.arm;
+
+import com.seaofnodes.simple.codegen.*;
+
+// Keep a saved NZCV value in GPRs and stack slots through subsequent spills.
+public class SplitGPRARM extends SplitARM {
+ private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L);
+ SplitGPRARM(LRG lrg,String kind,byte round) { super(lrg,kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+}
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 8c9bed95..3c965da5 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
@@ -288,7 +288,16 @@ public class arm extends Machine {
@Override public int rpc() { return X30; }
// Create a split op; any register to any register, including stack slots
- @Override public SplitNode split(LRG lrg, String kind, byte round) { return new SplitARM(lrg,kind,round); }
+ @Override public SplitNode split(LRG lrg, String kind, byte round) {
+ RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask();
+ if( (def!=null && def.size1() && def.test(FLAGS)) ||
+ (use!=null && use.size1() && use.test(FLAGS)) )
+ return new SplitFlagsARM(lrg,kind,round);
+ // Spilling the saved flags requires a GPR between NZCV and the stack.
+ if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM )
+ return new SplitGPRARM(lrg,kind,round);
+ return new SplitARM(lrg,kind,round);
+ }
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
new file mode 100644
index 00000000..cb7694f9
--- /dev/null
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java
@@ -0,0 +1,25 @@
+package com.seaofnodes.simple.node.cpus.x86_64_v2;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.simple.codegen.*;
+
+// Flag snapshots can use GPRs or stack slots, but never XMM registers.
+public class SplitFlagsX86 extends SplitX86 {
+ private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L<<x86_64_v2.FLAGS) | x86_64_v2.SPILLS,-1L);
+ SplitFlagsX86(LRG lrg,String kind,byte round) { super(lrg,kind,round); }
+ @Override public RegMask regmap(int i) { return MASK; }
+ @Override public RegMask outregmap() { return MASK; }
+
+ @Override public void encoding(Encoding enc) {
+ short dst=enc.reg(this), src=enc.reg(in(1));
+ if( dst==src ) return;
+ if( dst==x86_64_v2.FLAGS || src==x86_64_v2.FLAGS ) {
+ boolean toFlags = dst==x86_64_v2.FLAGS;
+ int reg = toFlags ? src : dst;
+ if( reg>=x86_64_v2.MAX_REG )
+ X86.flagsStack(enc,fun(enc).computeStackOffset(enc._code,reg),toFlags);
+ else
+ X86.flags(enc,reg,toFlags);
+ } else super.encoding(enc);
+ }
+}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
index b2b643d3..46f2cb16 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java
@@ -16,12 +16,10 @@ public class SplitX86 extends SplitNode {
@Override public RegMask regmap(int i) { return x86_64_v2.SPLIT_MASK; }
@Override public RegMask outregmap() { return x86_64_v2.SPLIT_MASK; }
- // Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
- // flags->flags.
+ // Moves among GPRs, XMM registers, and stack slots.
@Override public void encoding( Encoding enc ) {
short dst=enc.reg(this), src=enc.reg(in(1));
- if( dst==x86_64_v2.FLAGS ) { X86.flags(enc,src,true); return; }
- if( src==x86_64_v2.FLAGS ) { X86.flags(enc,dst,false); return; }
+ assert dst!=x86_64_v2.FLAGS && src!=x86_64_v2.FLAGS;
boolean dstX=dst>=x86_64_v2.XMM_OFFSET, srcX=src>=x86_64_v2.XMM_OFFSET;
// Stack spills
if( dst >= x86_64_v2.MAX_REG ) {
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 7b351533..3dc93bf6 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
@@ -47,7 +47,7 @@ public class x86_64_v2 extends Machine {
static RegMask DIV_MASK = new RegMask(RD_BITS & ~(1L<<RAX) & ~(1L<<RDX));
static final long SPILLS = -(1L << MAX_REG);
- static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS /* | (1L<<FLAGS)*/ | SPILLS, -1L );
+ static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | SPILLS, -1L );
// Load/store mask; both GPR and FPR
static RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS);
@@ -267,7 +267,9 @@ public class x86_64_v2 extends Machine {
// Create a split op; any register to any register, including stack slots
@Override public SplitNode split(LRG lrg, String kind, byte round ) {
- return new SplitX86(lrg,kind, round);
+ return lrg._machDef!=null && lrg._machDef.outregmap().test(FLAGS)
+ ? new SplitFlagsX86(lrg,kind,round)
+ : new SplitX86(lrg,kind,round);
}
// Return a MachNode unconditional branch
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 e9231d9a..f2ed2f72 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ _code.driver(CodeGen.Phase.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/test/java/com/seaofnodes/simple/BulkMemPhiTest.java b/chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java
new file mode 100644
index 00000000..31c0302b
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java
@@ -0,0 +1,40 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.TypeMem;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class BulkMemPhiTest {
+ // Reduced from Capitalize's Lower-phase failure. A precise MemMerge slot
+ // may still be supplied by bulk memory which covers the requested alias.
+ @Test public void testBulkInPreciseSlot() {
+ CodeGen code=new CodeGen("");
+ code._phase=CodeGen.Phase.Lower;
+ Node m0=code.con(TypeMem.START), m1=code.con(TypeMem.BOT);
+ Node ctrl=new CProjNode(code._start,0,"Ctrl").init();
+ Node iff=new IfNode(ctrl,code.con(TypeInteger.BOOL)).init();
+ RegionNode region=new RegionNode(null,null,
+ new CProjNode(iff,0,"True").init(),
+ new CProjNode(iff,1,"False").init()).init();
+ BulkMemPhiNode bulk=new BulkMemPhiNode("$mem",region,m0,m1).init();
+ // A later init check proves its call arm dead. Its precise Phi can
+ // collapse before the surrounding aggregate gets another peephole.
+ RegionNode done=new RegionNode(null,null,region,code.XCTRL).init();
+ MemPhiNode precise=new MemPhiNode("$4",4,done,bulk,m1).init();
+ MemMergeNode merge=new MemMergeNode(null,m0);
+ merge.alias(4,precise);
+ merge.init();
+ Node folded=precise.peepholeOpt();
+ assertSame(bulk,folded);
+ precise.subsume(folded);
+
+ assertFalse(bulk.isSplit(4));
+ assertSame(bulk,merge.alias(4));
+ // Splitting this still-covered alias must tolerate the direct bulk use.
+ Node mem = bulk.idealize();
+ bulk.subsume(mem);
+ }
+}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 5b57e701..afdb1071 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print());
}
@@ -36,7 +36,7 @@ if (arg == 1) {
}
return c;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return (arg==Phi(Region,3,2));", code.print());
}
@@ -148,7 +148,7 @@ else
a=3;
return a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 c503b56c..d1af5d14 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 1;", code.print());
}
@@ -44,7 +44,7 @@ else
a=3;
return a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 2;", code.print());
assertTrue(code.ctrl() instanceof FunNode);
}
@@ -65,7 +65,7 @@ else
b=5;
return b;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,42,5);", code.print());
}
@@ -84,7 +84,7 @@ if( arg==2 )
else
b=5;
return b;""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+""", TypeInteger.constant(2)).driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 5;", code.print());
}
@@ -124,7 +124,7 @@ else if( arg==3 )
else
a=5;
return a;
-""", TypeInteger.constant(1)).parse().iter().opto().iterPostOpto().finalizeIR();
+""", TypeInteger.constant(1)).driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
@@ -144,7 +144,7 @@ else
a=5;
return a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,3,5,4);", code.print());
}
@@ -165,7 +165,7 @@ else
a=5;
return a;
""", TypeInteger.constant(1));
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
@@ -186,7 +186,7 @@ else
a=5;
return a;
""", TypeInteger.constant(3));
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 4;", code.print());
}
@@ -202,7 +202,7 @@ if( arg ) {
}
return a+b;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,4,1);", code.print());
}
@@ -221,7 +221,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(1));
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 4;", code.print());
}
@@ -239,7 +239,7 @@ if( arg ) {
}
return a+b;
""", TypeInteger.constant(0));
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 1;", code.print());
}
@@ -257,7 +257,7 @@ if( arg ) {
}
return a+b+c;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 6;", code.print());
}
@@ -298,7 +298,7 @@ if( arg ) {
}
return a+b+c;
""", TypeInteger.constant(2));
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 498038c1..7094c0d5 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 61815840..376e472b 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -167,7 +167,7 @@ while(arg < 10) {
}
return arg;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return arg;", code.print());
}
@@ -184,7 +184,7 @@ while(arg < 10) {
}
return a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 839b834d..7025b98a 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
}
@Test
@@ -40,7 +40,7 @@ else {
}
return x;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -128,14 +128,14 @@ while(v1+arg) {
}
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return arg;", code.print());
}
@@ -148,7 +148,7 @@ if(0) while(0) {
}
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 4;", code.print());
}
@Test
public void testSwap2() {
CodeGen code = new CodeGen("return 1+(1+1);");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 3;", code.print());
}
@@ -181,7 +181,7 @@ while(arg) {
}
return a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 1;", code.print());
}
@@ -222,7 +222,7 @@ while(1) {
if(1<0) while(arg==-0) arg=arg-arg;
}
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -244,7 +244,7 @@ while(1) {
}
return 0!=0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -257,7 +257,7 @@ while(0==1) while(v0)
v0=1+v0;
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -273,14 +273,14 @@ while(1)
v1=1;
return v1+v0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@Test
public void testFuzz8() {
CodeGen code = new CodeGen("while(arg) arg = arg - 1; return arg;");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 1b542d63..924884ac 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -122,7 +122,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 2;", code.print());
}
@@ -138,7 +138,7 @@ else
v.y = 3;
return v;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Might be null accessing 'zAicm'",e.getMessage()); }
}
@@ -176,7 +176,7 @@ while (arg) {
}
return bar.a;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,4,3);", code.print());
}
@@ -261,7 +261,7 @@ while(arg) {
}
return ret;
""");
- try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return .v0;", code.print());
}
@@ -351,7 +351,7 @@ while(arg < 10) {
}
return ret.v0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
}
@Test
@@ -441,7 +441,7 @@ while(v0.f0) {}
s0 v1 = v0;
return v1;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -485,7 +485,7 @@ int v0=arg==0;
while(v0) continue;
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 19c531f8..fae7611d 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("3",Eval2.eval(code,0));
}
@Test
public void testNullRef4() {
CodeGen code = new CodeGen("return -null-5/null-5;");
- try { code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); }
}
@@ -150,7 +150,7 @@ return n.i;
struct _S{};
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule).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().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule).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 9786006c..214bc69e 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Phi(Region,.f,0);", code.print());
}
@@ -115,7 +115,7 @@ if (v1) {
}
return v0.f;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return arg;", code.print());
}
@@ -150,7 +150,7 @@ while(1) {
}
return v.f;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -185,7 +185,7 @@ while (arg) {
}
return i;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -205,7 +205,7 @@ while(arg) {
}
return arg;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -223,7 +223,7 @@ if (arg) {
}
return v.f;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -242,7 +242,7 @@ while (arg > 0) {
}
return v;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
//assertEquals("return new _S;", code.print());
}
@@ -259,7 +259,7 @@ while(0>=0) {
}
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return Top;", code.print());
}
@@ -277,7 +277,7 @@ u32 v0=0;
return v1;
}
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 ccca2e2b..e058a822 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ var code = new CodeGen(test[0]).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ var code = new CodeGen("flt x = arg; return 0-x;").driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals(src,test[1],Eval2.eval(code,0));
}
}
@@ -53,7 +53,7 @@ public class Chapter14FloatTest {
"""
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 3ba03b48..bc2ad8d3 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 3c1254d2..25f00474 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return 0;", code.print());
}
@@ -353,7 +353,7 @@ else {
}
return 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 ad9b99bc..2942cfee 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 95a0fc37..ea7afe56 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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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 6137c166..e85ce31d 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM();
+ code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule);
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 d88d8e9d..ae1dca5f 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ new CodeGen(test[0]).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return null;", code.print());
assertEquals("null", Eval2.eval(code, 0 ) );
}
@@ -170,7 +170,7 @@ return x2;
x*x;
};
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 1;", code.print());
assertEquals("1", Eval2.eval(code, 0));
}
@@ -388,7 +388,7 @@ for(;;) {
}
return 1;
""");
- try { code.parse().iter().opto().iterPostOpto().finalizeIR(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM();
+ code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule);
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 2763e53d..6f290279 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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 9f537fe7..0458941e 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src,seed,true).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.TypeCheck);
assertEquals("return 0;", code.print());
assertEquals("0", Eval2.eval(code, 2));
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 83342ab8..07ae42eb 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().iter().opto().iterPostOpto().finalizeIR();
+ """).driver(CodeGen.Phase.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));
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
index cbb08438..00d77f65 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ new CodeGen("return null-0;").driver(CodeGen.Phase.TypeCheck);
fail("Subtraction must reject null even when the other operand is zero");
} catch( Parser.ParseException e ) {
assertEquals("Cannot null - 0",e.getMessage());
@@ -283,7 +283,7 @@ aa.c = cc;
bb.c = cc;
return cc.cz;
""";
- CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 25;", code.print());
assertEquals("25", Eval2.eval(code, 0));
}
@@ -322,7 +322,7 @@ return 0;
@Test
public void testStrLoad() {
CodeGen code = new CodeGen("return \"abc\"[1];");
- code.parse().iter().opto().iterPostOpto().finalizeIR();
+ code.driver(CodeGen.Phase.FinalizeIR);
assertEquals("return .[];", code.print());
}
@@ -342,7 +342,7 @@ val _sum = { i32[~] is -> // final array
};
return _sum(is);
""";
- CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.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 b5da4d91..df02d3b4 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().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));
+ assertEquals("42", Eval2.eval(new CodeGen("return 7 && 42;").driver(CodeGen.Phase.FinalizeIR), 0));
+ assertEquals("0", Eval2.eval(new CodeGen("return 0 && 42;").driver(CodeGen.Phase.FinalizeIR), 0));
+ assertEquals("7", Eval2.eval(new CodeGen("return 7 || 42;").driver(CodeGen.Phase.FinalizeIR), 0));
+ assertEquals("42", Eval2.eval(new CodeGen("return 0 || 42;").driver(CodeGen.Phase.FinalizeIR), 0));
}
@Test
@@ -133,7 +133,7 @@ struct S {};
S? s = arg ? new S;
return !(!s || !arg) ? 1 : 0;
""");
- code.parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ code.driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck(); fail(); }
+ try { new CodeGen(src).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.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 4bfb0d30..2f125ed0 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals("return 1;", code.print());
}
@@ -272,7 +272,7 @@ while( cnt-- )
x = 2-x;
return x;
""";
- CodeGen code = new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.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 4e8effbe..275be96a 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -28,6 +28,36 @@ import static org.junit.Assert.*;
public class Chapter25Test {
+ @Test public void testFlagSpills() throws IOException {
+ com.seaofnodes.simple.codegen.FlagSpillTestSupport.run();
+ }
+
+ @Ignore
+ @Test
+ public void testJig() {
+ String src = "{ i32 -> i32 } close = \"C\"; val main = { u8[] a -> if (a# > 1) return close(0); return a[0]; };";
+ CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,"riscv","SystemV");
+ assertEquals("return (arg+3);", code.print());
+ assertEquals("3",Eval2.eval(code,0));
+ }
+
+ @Test public void testConditionalClassInitX86() throws IOException {
+ Path build=Path.of("build/objs");
+ Files.createDirectories(build);
+ Path dir=Files.createTempDirectory(build,"init_flags_");
+ // Keep B separately compiled so its first-touch check survives.
+ new CodeGen(null,dir.toString(),null,"B","int value=17+arg;",123,true,TypeInteger.BOT)
+ .driver("x86_64_v2","win64",false,false);
+ // GCM can hoist the fused B.$init compare above the test of take.
+ // Its flags then cross another flag definition and need a legal split.
+ new CheckedCodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main","val run={int take -> return take ? B.value : 0; };",123,true,TypeInteger.BOT)
+ .driver(CodeGen.Phase.Encoding,"x86_64_v2","win64");
+ // Remove successful fixtures; preserve the object if compilation fails.
+ Files.delete(dir.resolve("B.o"));
+ Files.delete(dir);
+ }
+
@Test public void testPublicInstanceMethod() {
String src = """
struct Scan {
@@ -63,15 +93,6 @@ public class Chapter25Test {
}
}
- @Ignore
- @Test
- public void testJig() {
- String src = "int x = arg/17; if( arg == 0 ) return arg + x; return arg + x;";
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("return (arg+3);", code.print());
- assertEquals("3",Eval2.eval(code,0));
- }
-
@Test public void testNeverReturnPointerRiscV() {
new CodeGen("""
struct _S { int x; };
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java b/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
index cc010668..47e10f97 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
@@ -433,6 +433,88 @@ public class ClassInitDependenciesTest {
assertEquals(1,stores[0]);
}
+ @Test public void testInitializationExecution() throws Exception {
+ Path dir=tempDir("execution-");
+ object(dir,dir,"State","int count=0;");
+ object(dir,dir,"B","State.count++; int value=17+arg;");
+ String src="int value=arg ? B.value+B.value : 0; return value+100*State.count;";
+ for( long seed : new long[]{1,123,987654321} ) {
+ CodeGen code=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main",src,seed,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
+ assertNotNull(code._stop.walk(n -> n instanceof CLInitNode ? n : null));
+ assertEquals("0",Eval2.eval(code,0));
+ assertNull(code._stop.walk(n -> n instanceof CLInitNode ? n : null));
+ assertEquals("134",Eval2.eval(code,1));
+ }
+
+ object(dir,dir,"Main","val run={int take -> "+src.replace("arg ?","take ?")+" };");
+ Path cfile=dir.resolve("check.c");
+ Files.writeString(cfile,"""
+ #include <stdint.h>
+ __attribute__((CALL_CONV)) extern int64_t run(int64_t take);
+ int main(void) {
+ return run(0)!=0 || run(1)!=134 || run(1)!=134 || run(0)!=100;
+ }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "check.exe" : "check").toString();
+ TestC.linkExe(dir.resolve("Main.o").toString(),TestC.CALL_CONVENTION.equals("win64") ? "ms_abi" : "sysv_abi",
+ cfile.toString(),new Ary<>(new String[]{dir.resolve("B.o").toString(),dir.resolve("State.o").toString()}),exe);
+ assertEquals("",TestC.exec(exe));
+ for( String cpu : new String[]{"arm","riscv"} )
+ new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main",src,123,true,TypeInteger.BOT).driver(CodeGen.Phase.Encoding,cpu,"SystemV");
+ }
+
+ @Test public void testParentInitializationExecution() throws Exception {
+ Path dir=tempDir("execution-");
+ object(dir,dir,"State","int count=0;");
+ module(dir,dir,"P",Map.of("P","val C=C; State.count=State.count*10+1;",
+ "P/C","State.count=State.count*10+2; int value=arg;"));
+ for( long seed : new long[]{1,123,987654321} ) {
+ CodeGen code=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main","return P.C.value+State.count;",seed,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
+ assertEquals("12",Eval2.eval(code,99));
+ }
+ object(dir,dir,"Main","val run={ -> P.C.value+State.count; };");
+ Path cfile=dir.resolve("check.c");
+ Files.writeString(cfile,"""
+ #include <stdint.h>
+ __attribute__((CALL_CONV)) extern int64_t run(void);
+ int main(void) { return run()!=12 || run()!=12; }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "check.exe" : "check").toString();
+ TestC.linkExe(dir.resolve("Main.o").toString(),TestC.CALL_CONVENTION.equals("win64") ? "ms_abi" : "sysv_abi",
+ cfile.toString(),new Ary<>(new String[]{dir.resolve("P.o").toString(),dir.resolve("State.o").toString()}),exe);
+ assertEquals("",TestC.exec(exe));
+ }
+
+ @Test public void testNonreturningObject() throws Exception {
+ Path dir=tempDir("nonreturning-");
+ object(dir,dir,"Spin","val spin_noInline={ -> while(1) {} return 7; };");
+ CodeGen code=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main","Spin.spin_noInline(); return 7;",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
+ assertNull(Eval2.eval(code,0,10));
+ }
+
+ @Test public void testImportedPrivateInitializer() throws Exception {
+ Path dir=tempDir("private-");
+ module(dir,dir,"Library",Map.of("Library","val read={ -> _Hidden.value; };",
+ "Library/_Hidden","int value=17+arg;"));
+ CodeGen code=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main","return Library.read();",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
+ assertEquals("17",Eval2.eval(code,99));
+ // Even a request stopping at Select must drop imported bodies. They
+ // remain available only to the ideal evaluator above, not this object.
+ for( String cpu : new String[]{"x86_64_v2","arm","riscv"} ) {
+ CodeGen selected=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
+ "Main","return Library.read();",123,true,TypeInteger.BOT).driver(CodeGen.Phase.Select,cpu,"SystemV");
+ assertNull(selected._serial);
+ for( CompUnit cu : selected._compunits.values() )
+ if( cu._src==null ) assertNull(cu._stop);
+ selected.driver(CodeGen.Phase.Encoding,cpu,"SystemV");
+ }
+ }
+
@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 db41363a..e2d822d0 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals(src, Long.toString(expected), Eval2.eval(code, arg));
}
}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
index 21e677ce..366bd7f4 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
@@ -114,6 +114,9 @@ public abstract class Eval2 {
// Current active Frame, to avoid having to pass it everywhere
public static Frame F;
+ // Class pointers and initializer self parameters share one object per run.
+ private static final HashMap<String,Object[]> CLASSES = new HashMap<>();
+
public static String eval( CodeGen code, long arg ) { return eval(code,arg,1000); }
public static String eval( CodeGen code, long arg, int timeout ) {
if( code._start.uctrl()==null ) return ""; // The empty program
@@ -122,6 +125,7 @@ public abstract class Eval2 {
code.driver(CodeGen.Phase.LocalSched);
// Set global, so don't have to pass everywhere
CODE = code;
+ CLASSES.clear();
// Timer for when we run too long. Ticks once per backedge or function call
int loopCnt = 0;
@@ -140,7 +144,7 @@ public abstract class Eval2 {
TypeStruct clz = ((TypeMemPtr)fun.sig().arg(0))._obj;
// Map from the <clinit> self parameter to the
// singleton class structure.
- F.put(fun.parm(2),alloc(clz));
+ F.put(fun.parm(2),classObject(clz));
}
@@ -203,9 +207,9 @@ public abstract class Eval2 {
// Parms read from Call args -
// - except RPC is the actual prior frame
- // - except <clinit> takes the singleton class object and external args
+ // - except entry <clinit> takes the external program argument
Object val;
- if( isClz && phi instanceof ParmNode parm && parm._idx==3 )
+ if( isClz && prior instanceof StartNode && phi instanceof ParmNode parm && parm._idx==3 )
PHICACHE[i] = arg; // Reading the initial arguments to <clinit>
else {
Node n = phi instanceof ParmNode parm
@@ -349,6 +353,8 @@ public abstract class Eval2 {
yield new Object[0];
}
+ } else if( Parser.startsClzPrefix(tmp._obj._name) ) {
+ yield classObject(tmp._obj);
} else {
// Constant object with all zeros
yield alloc(tmp._obj);
@@ -417,6 +423,12 @@ public abstract class Eval2 {
return ptr;
}
+ private static Object[] classObject(TypeStruct type) {
+ Object[] ptr = CLASSES.get(type._name);
+ if( ptr==null ) CLASSES.put(type._name,ptr=alloc(type));
+ return ptr;
+ }
+
private static Object not( NotNode not ) {
Object n = val(not.in(1));
return ( n==null || (n instanceof Double D && D==0.0) || n instanceof Long L && L==0 )
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Main.java b/chapter25/src/test/java/com/seaofnodes/simple/Main.java
index 97129f5b..1248a6cf 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().iter().opto().iterPostOpto().finalizeIR().typeCheck().GCM().localSched();
+ code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule).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 4934c91f..adeba136 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().iter().opto().iterPostOpto().finalizeIR();
+ CodeGen code=new CodeGen(sources[p],seed,true).driver(CodeGen.Phase.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().iter().opto().iterPostOpto().finalizeIR();
+ """).driver(CodeGen.Phase.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 9727857e..8bd77c06 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().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
assertEquals(result,Eval2.eval(code,0));
}
private static void bad(String src) {
try {
- new CodeGen(src).parse().iter().opto().iterPostOpto().finalizeIR().typeCheck();
+ new CodeGen(src).driver(CodeGen.Phase.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/PhaseTest.java b/chapter25/src/test/java/com/seaofnodes/simple/PhaseTest.java
new file mode 100644
index 00000000..7252378a
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/PhaseTest.java
@@ -0,0 +1,64 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.graph.GraphObserver;
+import com.seaofnodes.simple.codegen.CodeGen;
+import com.seaofnodes.simple.codegen.CodeGen.Phase;
+import com.seaofnodes.simple.node.Node;
+import java.util.ArrayList;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class PhaseTest {
+ private static final String SRC = "int sum=0; for(int i=0; i<arg; i++) sum+=i; return sum;";
+
+ @Test public void testIdealPhasesAndResume() {
+ ArrayList<String> phases = new ArrayList<>();
+ CodeGen code = new CodeGen(SRC);
+ code._obs = new GraphObserver<Node>() {
+ @Override public void before(Node n) {}
+ @Override public void after(Node n, Node repl, boolean applied) {}
+ @Override public void phase(String phase) { phases.add(phase); }
+ };
+ code.driver(Phase.TypeCheck);
+ code.driver(Phase.TypeCheck); // A repeated request does not rerun anything.
+ code.driver(Phase.LocalSched);
+ assertEquals("Parse Iter Opto IterPostOpto FinalizeIR TypeCheck Lower LoopTree Unlink Schedule LocalSched",
+ String.join(" ",phases));
+ assertNull(code._serial);
+ assertNull(code._mach);
+ assertEquals("45",Eval2.eval(code,10));
+ }
+
+ @Test public void testEncodingWithoutSerialization() {
+ for( String cpu : new String[]{"x86_64_v2","arm","riscv"} ) {
+ CodeGen code = new CodeGen(SRC).driver(Phase.TypeCheck);
+ code.driver(Phase.Unlink,cpu,"SystemV");
+ code.driver(Phase.Select,cpu,"SystemV");
+ code.driver(Phase.Encoding,cpu,"SystemV");
+ assertNull(code._serial);
+ byte[] staged = code._encoding._bits.toByteArray();
+ CodeGen direct = new CodeGen(SRC).driver(Phase.Encoding,cpu,"SystemV");
+ assertArrayEquals(staged,direct._encoding._bits.toByteArray());
+ }
+ }
+
+ @Test public void testExplicitSerializationAndMemoryExport() {
+ CodeGen code = new CodeGen(SRC).driver(Phase.Serialize);
+ assertNotNull(code._serial);
+ assertEquals(Phase.Serialize,code._phase);
+ code.driver(Phase.Encoding,"riscv","SystemV");
+ assertNotNull(code._serial);
+
+ CodeGen memory = new CodeGen(SRC).driver("riscv","SystemV",true,false);
+ assertNull(memory._serial);
+ assertEquals(Phase.Export,memory._phase);
+
+ CodeGen late = new CodeGen(SRC).driver(Phase.Encoding,"riscv","SystemV");
+ try {
+ late.exportELF(false,false);
+ fail("Cannot recover ideal IR after lowering and instruction selection");
+ } catch( IllegalStateException expected ) {
+ assertTrue(expected.getMessage().contains("Serialize before Lower"));
+ }
+ }
+}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
index 9078a33a..2d688bbe 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
@@ -49,6 +49,7 @@ Options:
--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-lower - dump expanded class initialization before loop trees
--dump-after-select - dump intermediate representation after instruction selection pass
--dump-after-gcm - dump intermediate representation after GCM pass
--dump-after-local-sched - dump intermediate representation after local scheduling pass
@@ -119,6 +120,7 @@ Options:
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-lower": dump |= 1<<Phase.Lower .ordinal() ; break;
case "--dump-after-loop-tree": dump |= 1<<Phase.LoopTree .ordinal() ; break;
case "--dump-after-select": dump |= 1<<Phase.Select .ordinal() ; break;
case "--dump-after-gcm": dump |= 1<<Phase.Schedule .ordinal() ; break;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java b/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java
index 070b7a9e..03df05a7 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java
@@ -2,6 +2,9 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.codegen.StaticData;
+import com.seaofnodes.simple.node.ConstantNode;
+import com.seaofnodes.simple.node.StoreNode;
+import com.seaofnodes.simple.type.TypeMem;
import com.seaofnodes.simple.type.TypeStruct;
import com.seaofnodes.simple.type.TypeInteger;
import com.seaofnodes.simple.util.Ary;
@@ -16,14 +19,25 @@ public class StaticDataTest {
private static final String SRC="val n=42; val f={int x -> x+n;}; return 0;";
private static StaticData.ObjectData root(CodeGen code) {
- return code._encoding._data.entries().stream()
- .filter(obj -> obj.symbol.endsWith("Test.$class")).findFirst().orElseThrow();
+ for( var obj : code._encoding._data.entries() )
+ if( obj.symbol.endsWith("Test.$class") ) return obj;
+ throw new AssertionError("Missing Test class object");
}
// Read the image before <clinit>, then call the function through its data slot.
@Test public void testEmulatorImage() {
for( String cpu : new String[]{"arm","riscv"} ) {
- CodeGen code=new CodeGen(SRC).driver(cpu,"SystemV",true,false);
+ CodeGen code=new CodeGen(SRC).driver(CodeGen.Phase.TypeCheck);
+ assertNull("Constant fields fold before instruction selection",
+ code._stop.walk(n -> n instanceof StoreNode st &&
+ (st._name.equals("n") || st._name.equals("f")) ? n : null));
+ assertNotNull(code._stop.walk(n -> n instanceof ConstantNode &&
+ n._type instanceof TypeMem mem && mem.isConstant() &&
+ mem._t==TypeInteger.constant(42) ? n : null));
+ assertNotNull("The runtime initialization flag still needs its Store",
+ code._stop.walk(n -> n instanceof StoreNode st &&
+ st._name.equals("$init") ? n : null));
+ code.driver(cpu,"SystemV",true,false);
var e=code._encoding;
var obj=root(code);
TypeStruct ts=(TypeStruct)obj.type;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
new file mode 100644
index 00000000..445acb61
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
@@ -0,0 +1,141 @@
+package com.seaofnodes.simple.codegen;
+
+import com.seaofnodes.isa.X86;
+import com.seaofnodes.isa.eval.EvalArm64;
+import com.seaofnodes.simple.TestC;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.node.cpus.arm.arm;
+import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
+import com.seaofnodes.simple.type.TypeInteger;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import static org.junit.Assert.*;
+
+// Force overlapping flags, then force every GPR unavailable across the gap.
+public class FlagSpillTestSupport {
+ private static class Op extends MachConcreteNode {
+ final RegMask _def, _use, _kill;
+ Op(RegMask def,RegMask use,RegMask kill,Node... ins) {
+ super(ins); _def=def; _use=use; _kill=kill; _type=TypeInteger.BOT;
+ }
+ @Override public String op() { return "flagsProbe"; }
+ @Override public RegMask outregmap() { return _def; }
+ @Override public RegMask regmap(int i) { return _use; }
+ @Override public RegMask killmap() { return _kill; }
+ @Override public void encoding(Encoding enc) { throw new AssertionError(); }
+ }
+
+ public static void run() throws IOException {
+ StringBuilder asm = new StringBuilder(".text\n");
+ for( boolean isArm : new boolean[]{false,true} )
+ for( boolean stack : new boolean[]{false,true} ) {
+ CodeGen code = new CodeGen("");
+ code._callingConv="SystemV";
+ code._mach=isArm ? new arm(code) : new x86_64_v2(code);
+ RegionNode block = new RegionNode(null,code._start,code._start);
+ code._cfg.add(block);
+ int flagsReg=isArm ? 64 : 32, firstStack=flagsReg+1;
+ RegMask flags = new RegMask(flagsReg);
+ Op def = new Op(flags,null,null,block);
+ RegMask killMask = stack ? (isArm ? new RegMask(0x7fffffffL,1L) : new RegMask(0x1ffffffefL)) : flags;
+ Op kill = new Op(null,null,killMask,block);
+ new Op(null,flags,null,block,def);
+ RegAlloc alloc = new RegAlloc(code);
+ alloc.regAlloc();
+
+ int splits=0, slots=0;
+ for( Node n : block._outputs ) if( n instanceof SplitNode split ) {
+ int dst=alloc.regnum(n), src=alloc.regnum(n.in(1));
+ assertTrue(split.outregmap().test(dst));
+ assertTrue(split.regmap(1).test(src));
+ for( int fp=isArm ? 32 : 16; fp<flagsReg; fp++ ) {
+ assertFalse("FP output allowed",split.outregmap().test(fp));
+ assertFalse("FP input allowed",split.regmap(1).test(fp));
+ }
+ if( dst>=firstStack || src>=firstStack ) slots++;
+ splits++;
+ }
+ assertTrue(splits>=2);
+ assertEquals(stack,slots!=0);
+
+ // Keep the allocated registers, supplying a frame for encoding stack offsets.
+ FunNode frame = new FunNode(null,null,"flags",null,(Node)null);
+ code._regAlloc=new RegAlloc(code);
+ for( Node n : block._outputs )
+ if( alloc.regnum(n)>=0 ) code._regAlloc.newLRG(n,frame)._reg=alloc.regnum(n);
+ Encoding save = new Encoding(code), restore = new Encoding(code);
+ Encoding enc = save;
+ for( Node n : block._outputs ) {
+ if( n==kill ) enc=restore;
+ if( n instanceof SplitNode split ) split.encoding(enc);
+ }
+ if( isArm ) {
+ for( int bits=0; bits<16; bits++ ) {
+ EvalArm64 cpu = new EvalArm64(new byte[1024],512);
+ System.arraycopy(save._bits.buf(),0,cpu._buf,0,save._bits.size());
+ System.arraycopy(restore._bits.buf(),0,cpu._buf,save._bits.size(),restore._bits.size());
+ cpu.N=(bits&8)!=0; cpu.Z=(bits&4)!=0; cpu.C=(bits&2)!=0; cpu.V=(bits&1)!=0;
+ assertEquals(0,cpu.step(save._bits.size()/4));
+ cpu.N=cpu.Z=cpu.C=cpu.V=false;
+ if( stack ) for( int r=0; r<31; r++ ) cpu.regs[r]=0;
+ assertEquals(0,cpu.step(restore._bits.size()/4));
+ assertEquals((bits&8)!=0,cpu.N); assertEquals((bits&4)!=0,cpu.Z);
+ assertEquals((bits&2)!=0,cpu.C); assertEquals((bits&1)!=0,cpu.V);
+ assertEquals(512,cpu.regs[31]);
+ }
+ } else {
+ Encoding body = new Encoding(code);
+ int[] preserved={3,5,6,7,12,13,14,15};
+ for( int r : preserved ) pushPop(body,r,false);
+ X86.imm(body,0x81,5,4,16); // Reserve spill storage.
+ X86.flags(body,TestC.OS.startsWith("Windows") ? 1 : 7,true);
+ bytes(body,save);
+ if( stack ) {
+ for( int r=0; r<16; r++ ) if( r!=4 ) X86.constant(body,r,0);
+ } else X86.reg(body,0x39,0,0); // CMP rax,rax clobbers only flags.
+ bytes(body,restore);
+ X86.flags(body,0,false); // Return the restored flags.
+ X86.imm(body,0x81,0,4,16);
+ for( int i=preserved.length-1; i>=0; i-- ) pushPop(body,preserved[i],true);
+ X86.ret(body);
+ String name=stack ? "flags_stack" : "flags_reg";
+ asm.append(".globl ").append(name).append('\n').append(name).append(":\n.byte ");
+ for( int i=0; i<body._bits.size(); i++ ) {
+ if( i!=0 ) asm.append(',');
+ asm.append(body._bits.buf()[i]&255);
+ }
+ asm.append('\n');
+ }
+ }
+ Path dir=Files.createTempDirectory(Path.of("build/objs"),"flag_spills_");
+ Files.writeString(dir.resolve("flags.s"),asm);
+ Files.writeString(dir.resolve("flags.c"),"""
+ typedef unsigned long long u64;
+ u64 __attribute__((CALL_CONV)) flags_reg(u64);
+ u64 __attribute__((CALL_CONV)) flags_stack(u64);
+ int main(void) {
+ const int positions[]={0,2,4,6,7,11};
+ for(int bits=0; bits<64; bits++) {
+ u64 flags=0x202;
+ for(int i=0;i<6;i++) if(bits&(1<<i)) flags|=1ULL<<positions[i];
+ if((flags_reg(flags)&0x8d5)!=(flags&0x8d5)) return 1;
+ if((flags_stack(flags)&0x8d5)!=(flags&0x8d5)) return 2;
+ }
+ return 0;
+ }
+ """);
+ String exe=dir.resolve(TestC.OS.startsWith("Windows") ? "flags.exe" : "flags").toString();
+ TestC.exec("gcc","-DCALL_CONV="+(TestC.OS.startsWith("Windows") ? "ms_abi" : "sysv_abi"),
+ "-o",exe,dir.resolve("flags.c").toString(),dir.resolve("flags.s").toString());
+ TestC.exec(exe);
+ }
+
+ private static void bytes(Encoding dst,Encoding src) {
+ for( int i=0; i<src._bits.size(); i++ ) dst.add1(src._bits.buf()[i]&255);
+ }
+ private static void pushPop(Encoding enc,int reg,boolean pop) {
+ if( reg>=8 ) enc.add1(0x41);
+ enc.add1((pop ? 0x58 : 0x50)+(reg&7));
+ }
+}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 93380c87..292f1ae7 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -29,6 +29,12 @@ public class RegAllocTestSupport {
return code;
}
+ private static SplitNode testSplit(CodeGen code, MachNode def) {
+ LRG lrg = new LRG((short)0,null);
+ lrg._machDef = def;
+ return code._mach.split(lrg,"test",(byte)0);
+ }
+
public static void uncoloredFixedNeighbor() throws Exception {
CodeGen code = graph();
RegAlloc alloc = new RegAlloc(code);
@@ -225,11 +231,11 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def,null)._reg=0;
- SplitNode hi = code._mach.split(null,"test",(byte)0);
+ SplitNode hi = testSplit(code,def);
hi.setDef(0,code._start); hi.setDef(1,def);
alloc.newLRG(hi,null)._reg=1;
new Op(null,null,killed ? A : null,false,code._start);
- SplitNode lo = code._mach.split(null,"test",(byte)0);
+ SplitNode lo = testSplit(code,hi);
lo.setDef(0,code._start); lo.setDef(1,hi);
alloc.newLRG(lo,null)._reg=0;
Op use = new Op(null,A,null,false,code._start,lo);
@@ -243,7 +249,7 @@ public class RegAllocTestSupport {
uncolored.newLRG(def0,null)._mask=A;
Op fixed = new Op(A,null,null,false,pre._start);
uncolored.newLRG(fixed,null)._mask=A;
- SplitNode copy = pre._mach.split(null,"test",(byte)0);
+ SplitNode copy = testSplit(pre,def0);
copy.setDef(0,pre._start); copy.setDef(1,def0);
assertFalse("A fixed definition clobbers even before coloring",uncolored.sameBlockNoClobber(copy));
}
@@ -284,7 +290,7 @@ public class RegAllocTestSupport {
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(A,null,null,false,code._start);
alloc.newLRG(def,null)._reg=0;
- SplitNode copy = code._mach.split(null,"test",(byte)0);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
alloc.newLRG(copy,null)._reg=1;
if( kind==3 ) new Op(null,null,A,false,code._start);
@@ -307,7 +313,7 @@ public class RegAllocTestSupport {
CodeGen code = graph();
RegAlloc alloc = new RegAlloc(code);
Op def = new Op(new RegMask(3L),null,null,false,code._start);
- SplitNode copy = code._mach.split(null,"test",(byte)0);
+ SplitNode copy = testSplit(code,def);
copy.setDef(0,code._start); copy.setDef(1,def);
Op use = new Op(null,new RegMask(3L),null,false,code._start,copy);
LRG from = alloc.newLRG(def,null), to = alloc.newLRG(copy,null);
diff --git a/docs/chapter-backports.md b/docs/chapter-backports.md
index 821c7b2e..1f15afaa 100644
--- a/docs/chapter-backports.md
+++ b/docs/chapter-backports.md
@@ -1,1339 +1,82 @@
-# Chapter backport review
+# Chapter backport queue
-This is a queue of pending work. Completed corrections leave the queue; the
-[validation summary](#validation-record) records their scope. Detailed old logs
-are disposable build artifacts. Reusable rules live in
-[the AI notes](../skills/chapter25-codex-notes.md).
+This file tracks pending backports and deferred work. Completed work and old
+validation results belong in Git history. Reusable implementation constraints
+live in the [AI notes](../skills/chapter25-codex-notes.md).
-Introduce independent fixes in the earliest applicable chapter and propagate
-them through every affected snapshot. Testing Chapter 25's inherited tests does
-not validate the compilers in the earlier chapter directories. Chapters 10 and 11 introduce bulk memory and lazy memory partitioning;
-Chapter 12 adds references, Chapter 13 schedules the graph, and Chapter 14
-introduces floats and narrow numeric types. Keep the
-rest of SSA construction with incomplete types in Chapter 25 for now. Cliff wants to revisit splitting Chapter 25
-into smaller chapters; escape analysis remains in 25 until that larger review.
+## Deferred until the Chapter 25 split
-Reset checkpoint (2026-10-03): Cliff committed the chapter reorganization and
-mutability split as `22df8918` (`Chapter renumber, and split ch 17`). The compiler
-work and validation below are complete; review is ongoing. Chapter 18 README
-review and backup-file deletion followed in `3c13b271` (`Update README.md`).
-The tree was clean before this notes refresh. No build/test process is hung;
-check Git for subsequent changes. Splitting Chapter 25 remains deferred.
+### Deterministic inlining
-## Chapter numbering after the memory reorganization
+The parse-size cutoff can reject a function whose optimized body is small.
+After shrinking rewrites converge, choose one growing inline using current
+body sizes and a deterministic ranking. Cache sizes per convergence round;
+use stable call-site provenance to break ties, not worklist order or newly
+allocated node IDs. Avoid per-node dependencies and repeated whole-body scans
+while a large function gradually shrinks.
-| Previous chapter | Current chapter |
-|---|---|
-| 10a: whole memory | 10 |
-| 10b: lazy memory splitting | 11 |
-| 13: references, plus typed fields from 12 | 12 |
-| 11: GCM, retaining reference support | 13 |
-| 12: floats, and 14: narrow types | 14 |
-
-Chapters 15-16 and 18-25 retain their numbering; the subsequent mutability split
-replaces 17 with 17a and 17b. New Chapter 12 has no GCM or floating
-point nodes; Chapter 13 introduces GCM without floats. Chapter 14 adds both
-floating point and narrow numeric types. The inherited float test suite is
-now `Chapter14FloatTest`; reference and GCM suites are `Chapter12Test` and
-`Chapter13Test`. The README in Chapter 12 records the intentional `L0`/`L1`
-shallow-reference boundary and resolving field definitions by name.
-
-Historical validation entries below use the chapter numbers and test names
-in effect when the work was done. In particular, old 10a/10b records refer to
-current 10/11, and old `Chapter12Test` float regressions now live in
-`Chapter14FloatTest`. Float-to-int assignment conversion was subsequently
-added with the numeric material in Chapter 14 and propagated through 25.
-
-Validation of the reorganization (2026-10-03): Make suites pass in every
-chapter 10-25, including the shared printer/ISA tests and Chapter 25's native
-runtime tests. Chapters 10-14 produce release jars. Fresh standalone checkouts
-of 12, 13, and 14 pass their suites and package without parent chapter sources.
-Browser playback checks pass in 12 and 13. POM module order, portable IDEA
-XML, chapter navigation, local links, SVG XML, shell syntax, and UTF-8/LF checks
-pass. Logs are in `build/chapter-reorg/`.
-
-The new main-Java increments (added + removed lines) are 529 for 10 to 11,
-280 for 11 to 12, 867 for 12 to 13, and 1,185 for 13 to 14. These exclude tests,
-prose, assets, and generated files. The references chapter is now a small,
-separate language extension; the larger increments introduce scheduling and
-numeric types respectively.
-
-## Mutability syntax and chapter split (2026-10-03)
-
-Chapter 16 introduces constructors. Chapter 17a introduces independent binding
-and reference permissions; Chapter 17b adds inference, updates, conditional
-expressions, and `for`. POMs, portable IDEA descriptors, Make discovery, README
-navigation, and inherited test names follow the split. `Chapter17aTest` owns
-the former final-field constructor tests; `Chapter17bTest` owns syntax sugar.
-
-From 17a through 25, `!Point`/`~Point` control pointee access and `!p`/`~p`
-control reassignment. Primitive bindings default mutable; references default
-fixed and struct access defaults read-only. Arrays default writable, with
-`[~]` specifying a read-only layer. `var` and `val` preserve initializer access.
-Read-only views are deep but do not freeze other aliases or remove memory
-dependencies. Writable array slots require matching element permissions.
-
-`TypeMemPtr` carries the access bit independently of fields and preserves it
-through forward-reference resolution, cyclic interning, inference, loads,
-calls, and Chapter 25 serialization. Cyclic factories build the complete type
-before interning to avoid orphaning temporary types. Array aliases are shared
-across access views, while their element permissions remain distinct. The
-23/24 static-field distinction now requires an explicit initializer; a fixed
-nullable field's implicit null is still an instance field.
-
-Constructor permissions cover only allocation fields, including assignments in
-nested blocks. They do not permit rebinding surrounding or constructor-local
-fixed variables. Chapter 25's constructor-exit check also handles required
-primitive fields. The object layout changes under the unversioned `C0DE` header;
-rebuilding `sys.o` is required and exercised by its native tests.
-
-The changed graphs exposed two small existing assumptions: Chapter 24 must
-recognize a Phi whose own backedge is still under construction, and a memory
-query through New must record a dependency even when the queried loop MemPhi
-is a user of the Store. The memory-query dependency is propagated across the
-applicable bulk-memory implementations.
-
-Validation: Make suites pass in 16 (244), 17a (255), 17b (304), 18 (339),
-19 (383), 20 (405), 21 (444), 22 (465), 23 (487), and 24 (517); applicable
-chapter-local fuzz regression wrappers and the shared printer/ISA suites pass.
-Chapter 25's complete Make suite passes, including native/library execution and
-rebuilt serialized objects. Fresh standalone 17a/17b copies pass their tests
-and produce release jars; 16 also packages. POMs, IDEA XML, and navigation
-links are checked. Test-export tooling recognizes 17a/17b and its suite passes;
-unsupported Java test patterns are skipped per method rather than per class.
-Existing spill expectations changed in the String fixture:
-22's RISC-V count is 1 instead of 3; 24's x86 count is 0 instead of 9.
-
-## Completed integration notes
-
-These records describe completed work, not outstanding implementation tasks.
-Historical chapter numbers and measurements below refer to their original runs.
-
-### Loop store sinking: Chapters 17b-25 (2026-10-05)
-
-The Chapter 17b presentation peephole is forwarded through Chapter 25. A loop
-memory Phi whose backedge is a sole-use Store becomes a Store of a scalar Phi.
-An entry Load preserves zero-trip behavior. The address must be a constant
-field offset of an allocation dominating the loop; array elements are excluded.
-Wait for parallel bulk-memory splitting before replacing the precise Phi.
-Chapter 25 preserves the Store's frozen width and excludes unresolved aliases,
-undecided widths, and constructor initialization Stores.
+The bounded boilerplate-inlining heuristic is already in Chapters 20-24.
+This item is the larger Chapter 25 redesign.
-Existing suites exposed three small supporting fixes: Loads through a TOP
-pointer stay TOP in 17b-23; Chapter 25 keeps a MemMerge alive while flattening a
-dead memory cycle; the shared printer computes placement for constants even
-when they have no cached block. Existing graph assertions now count reachable
-definitions and allow scalar field Phis. The synthetic-exit fixture uses a
-varying pointer so it still exercises loop-carried memory after store sinking.
-No new test methods were added for the forward port. Spill expectations were
-updated for the changed graphs, including the infinite-loop, String, and Scan
-fixtures; runtime and register-correctness assertions remain enabled.
+### Conversion and BOTTOM-on-error contracts
-Validation: full Make suites pass in 17b (306 including its fuzzer wrapper),
-18 (339), 19 (383), 20 (405), 21 (444), 22 (465), 23 (487), and 24 (517), plus
-the separate fuzzer wrappers in 18-24. Chapter 25 passes all six suite groups
-(403, 39, 9, 23, 1, and 19), including rebuilt sys.o, native execution, and
-the existing fuzzer seeds. Shared printer and ISA checks pass. Logs are in
-`build/rebase-talk/loop-store-*.log`. The presentation example still has 333
-capture frames and 52 final nodes, with both Totals stores outside the loop.
+Coordinate these changes with the incomplete-type/conversion split:
-### Shared ISA encoders and evaluators: Chapters 21-25
+- Chapter 24 Loads supply a typed result even when they report an error.
+ Changing that result to BOTTOM alone breaks incomplete loop typing and can
+ obscure the intended diagnostic. Chapter 25's Convert nodes provide the
+ surrounding typed result. Relevant existing cases include
+ `testBrainFuck`, `testWhileWithNullInside`, `testNullGuardErrors`, and
+ `testLinkedList0`.
+- Chapter 24 arithmetic also needs its typed fallback while forward references
+ are unresolved. Returning BOTTOM confuses unresolved expressions with
+ uninitialized fields (`testCoRecur`) and loses arithmetic diagnostics.
-Issue #257's extraction is implemented in [`isa/`](../isa/README.md). `X86`
-accepts concrete registers, widths, immediates and addresses; `Arm64` and `RiscV`
-pack instruction words. Chapter `Encoding` classes implement `CodeSink` using
-their existing byte writers. ARM logical immediates and constant construction
-are shared too. None of these helpers imports compiler classes.
+Preserve the current contracts until the parser, conversions, loop bounds,
+and diagnostics can move together. Do not weaken narrowing assertions or
+expected errors to make an isolated backport pass.
-Instruction selection and masks, compiler-type interpretation, frame and block
-layout, branch relaxation, relocations, constants, and ELF remain chapter-local.
-Branch byte sizes and patching are shared; the chapter still runs relaxation
-and supplies targets. One shared `EvalArm64` and `EvalRisc5` replace the five
-copies of each. Image construction and test assertions stay in chapter harnesses.
-Evaluators compile as test support. Make continues to bundle tests in its release
-jars; Maven and IDEA retain separate main/test dependencies.
+### Other candidates requiring a concrete earlier-chapter failure
-Direct byte tests also cover corrections exposed by consolidation: x86 legacy
-prefix order for 16-bit memory operations, indirect register calls and their REX
-bits, imm8 shift counts, high-register PUSH/POP, low-byte SETcc, and five-byte
-unconditional near jumps. RISC-V JAL now takes its sign bit from displacement
-bit 20. ARM masks negative literal-load offsets, checks byte displacement ranges
-correctly, and its evaluator distinguishes MOVN from AND-immediate.
-A disposable GNU assembler comparison agrees on all 5,760 tested x86
-load/store/LEA instructions (`build/IsaOracle.java`). Shared tests add fixed ARM
-and RISC-V words and evaluator checks for multi-instruction ARM constants.
-Make, Maven source paths, IDEA wiring, and linearized checkouts include `isa/`
-starting at Chapter 21; Chapters 1-20 have no ISA dependency.
+These are investigation candidates, not promised wholesale ports:
-Validation: full Chapter 21-25 Make suites pass, including native execution and
-the existing allocator checks (`build/isa-final-tests.log`). The independent
-shared ISA tests pass; a fresh linear Chapter 21 release contains all three
-encoders and shared evaluators (`build/isa-linear21-complete-release.log`).
-Maven/IDE XML parses; the standalone POM gains main and test ISA source roots
-at Chapter 21 only. Shell syntax and LF checks pass.
-
-### Chapter 15 allocation design (implemented)
-
-Chapters 12-14 retain explicit initialization Stores. Chapter 15 replaces them
-with New's zeroing operation while keeping one parser-visible `$mem`.
-
-| Interface | Chapter 15 |
+| Candidate | Boundary to establish |
|---|---|
-| New inputs | `{ctrl, $mem, size}` |
-| New outputs | `{ptr, $mem}`; no control result or per-alias projections |
-| Coverage | field aliases from New's internal TypeStruct/TypeMemPtr |
-| Input memory | partial MemMerge, no default, entries for affected aliases |
-| Result integration | whole-memory MemMerge, prior default, affected entries all using the same New memory projection |
-| Memory types | existing alias plus stored-value type; no lattice extension |
-
-A null MemMerge default means absent aliases are uncovered. Empty structs
-therefore have an empty partial input; their allocation does not change the
-whole-memory aggregate. New's output also covers only its own aliases.
-
-An alias-specific contents query combines incoming values with the field's
-initializer from New's struct (integer zero, floating zero, or null). It records
-optimizer dependencies and stops at cached Phi types to break loops. MemPhi
-merges the scalar contents of its precise alias. Start's incoming heap is empty
-in this chapter; functions with heap arguments will require a different entry
-query in Chapter 18.
-
-Loads fold against their own New and bypass a proven distinct allocation via
-New's memory input. Different pointer nodes alone do not establish disjointness.
-Array length remains a subsequent Store; array access control inputs remain.
-Global code motion and the evaluator scheduler both follow an alias through
-MemMerge to discover the allocation's ordering constraint. MemMerge is packaging,
-not itself a clobber. The viewer and evaluator use the new slot conventions.
-
-### Chapter 16 constructor integration (reviewed)
-
-ScopeNode now owns the variable records and one `$mem` binding; the obsolete
-ScopeMin alias table is removed. New inputs are `{ctrl, $mem, size, fields...}`,
-with initializer inputs in struct-field order. Outputs remain `{ptr, $mem}`;
-partial memory coverage and whole-memory integration are unchanged from 15.
-Alias contents read initializer types with dependencies, rather than assuming
-zero. The evaluator reads these same inputs, including array length. Constructor
-body effects participate in ordinary lazy branch/loop memory SSA.
-
-The sole-use Store-to-New check is deliberately unchanged. A shared allocation
-memory result has more users than a per-alias result, so it permits fewer folds.
-`Chapter15Test.testBasic5` retains a `.y` Load instead of folding it to 3.14;
-both runtime results remain checked. Other changed printed expectations only
-adjust Region/Loop IDs after removing parser memory nodes.
-
-### Chapter 18 function memory (reviewed)
-
-Chapter 17 retains readonly casts, forward reference type updates, lexical
-guards, and increment/assignment semantics while replacing ScopeMin with one
-memory binding. Chapter 18 keeps its top-level Var class and makes MemMerge an
-ordinary optimizer node rather than a parser alias table. Memory Parm 1 stays
-opaque; local branches, loops, and merged returns use BulkMemPhi/MemPhi.
-Function entry and CallEnd memory have unknown contents, including heap arguments.
-Calls consume and return the complete memory state. There are no alias summaries
-or private constructor effects. Inlining can expose the existing alias rewrites.
-
-New retains `{ctrl, $mem, size, fields...}` inputs and `{ptr, $mem}` outputs.
-The evaluator, Eval2, graph viewer, and scheduling use those slots. Call is a
-clobber for every alias. GCM raises a Load before the Call's block terminator;
-it must not append the Load to the Call's inputs, which encode arguments and a
-last-slot function pointer. The one-step Phi Load guard also stops at Calls.
-
-### Chapter 19 instruction selection (reviewed)
-
-Both selectors preserve BulkMemPhi exclusions and MemPhi aliases. Selected
-memory instructions carry their alias through MemOpNode; GCM follows aggregates
-for ordinary and folded reads, waits for matching writers, and inserts ordering
-edges that require no register. Direct machine calls embed their target and must
-keep their argument lists intact, just like ideal Calls.
-
-Preserve this chapter's existing lowering of nonzero initializers into Stores.
-New now takes `{ctrl, $mem, size}` and produces `{ptr, $mem}`; there are no field
-value inputs at this stage. Partial coverage and whole-memory integration are
-unchanged. New's alias contents meet incoming memory with the field's zero type.
-Both CPU allocation register masks and both evaluators use the new slots.
-
-The nested-loop regression reaches x86's existing right-hand Load/Add TODO;
-that commutative case now uses AddMemX86 just like a left-hand Load. No other
-unfinished instruction patterns or encoding work is included.
-
-### Chapter 20 register allocation (reviewed)
-
-ARM joins x86-64 and RISC-V in preserving bulk/precise memory Phis, allocation
-slots, and selected memory effects. The allocator already excludes TypeMem Phis
-and gives memory/ordering edges no register constraints; no allocator heuristic
-changes are needed. Allocation regressions check that memory stays unallocated
-and the pointer projection receives a legal register on all three targets.
-
-Folded x86 Add must retain a value input for its two-address result even when
-that value is constant. The old immediate form omitted input 4 and caused a null
-live range in BuildLRG. Both left- and right-hand Load patterns now retain the
-other addend; the existing constant selector materializes it.
-
-Keep Chapter 20's inlining-safe Return typing and the dependency registrations
-on a rejected Phi factoring attempt. Those fixes predate this memory port.
-
-### Chapter 21 encoding (reviewed)
-
-All three targets retain the memory Phi subclasses and New's pointer slot 0,
-memory slot 1, and size input 2. Chapter 21 caches New's register masks in the
-shared node during register allocation; update that shared interface. Writers
-are placed before adding read-before-write constraints; the dominator walk
-must still find conditional writers above a Load's proposed merge placement.
-Calls receive no scheduling-only operands.
-
-Dependency registration reverses direction here: `consumer.addDep(producer)`.
-The forwarded memory queries and explicit forward dependencies follow that API.
-Synthetic no-exit loops collect MemPhi inputs by alias and use BulkMemPhi for
-the merged return memory. The one-step Load factoring guard is unchanged.
-
-Actual execution exposed a full-width integer Load allocated to XMM but encoded
-as a GPR load. LoadX86 now selects the encoding from the allocated register bank,
-preserving register masks. ARM's emulator now supports MOVK and MOVZ's complete
-16-bit immediate and halfword shift. Carry these narrow fixes with later ports.
-
-BrainFuck follow-up: both the original and ported compilers retain 12 ideal
-Loads and 114 Stores (106 Stores initialize the program). In the RISC-V Hello
-World run, both execute 3,261 heap loads and 646 heap stores; stack loads/stores
-change from 15/15 to 1,006/572, mostly spilling `d+4`. There are also pre-existing
-missed length optimizations: `program#` reloads the known 106 on all 907 loop
-tests, and `old#` is reloaded after allocating the replacement output array.
-Load forwarding stops at memory Phis; its loop-profit test does not see through
-the backedge's multiway Phi. The old-output pointer is itself a Phi, beyond the
-two-direct-allocations disjointness test. Field aliases distinguish array length
-from array data, but do not distinguish separate arrays of the same element type.
-A scratch source replacing only `program#` with 106 passes Hello World, removes
-907 heap loads, and returns stack traffic to 15/15. Its weighted split score
-nevertheless rises from 130 to 165 as allocation inserts many pointer copies
-before initialization stores; this score is not a dynamic memory-traffic measurement. No compiler
-optimization was changed in this investigation. Probe sources and before/after
-logs: `build/BrainMemory{Probe,Variants}.java`, `build/brain-memory-*.log`, and
-`build/brain-variants-*.log`.
-
-The subsequent Chapter 21 Load search now folds `program#` through the loop.
-The search returns a folding witness without creating nodes or rewiring edges;
-it retains optimizer dependencies. At a non-loop MemPhi it must prove every
-arm, with a BitSet remembering successful merges. Returning to the original
-loop memory is success only for an invariant pointer. Unknown clobbers, other
-loops, and incomplete merges fail. A separate builder creates value Phis after
-the proof; unchanged paths refer to the new loop value Phi. Enqueue that Phi:
-a self-edge can otherwise keep an obsolete Phi alive after local folding.
-Controlled array accesses are not hoisted across a merge. The one-step
-`Load.clobbered` factoring guard is unchanged. Carry this search with later ports.
-
-The unmodified BrainFuck source now has 11 Loads. RISC-V Hello World executes
-2,354 heap loads, 646 heap stores, and 15/15 stack loads/stores, compared with
-3,261 / 646 / 1,006 / 572 before this search. Emulator instruction count drops
-22,965 to 21,920 (not a hardware timing). Retained/weighted moves change
-46/130 to 144/165 as allocation inserts repeated initialization-store pointer copies.
-Four reduced cases cover unchanged N-way arms, a possibly aliasing arm,
-loop-carried pointers, and distinct values folding on all arms; they pass
-2,800 evaluations across 100 seeds. The existing 9,800 memory evaluations pass.
-At that checkpoint, the 432-test suite had only the user-edited BrainFuck golden failure
-(42 versus 165). A temporary test copy restoring native BrainFuck execution
-and using 165 passes all 91 spill-cohort entries and their runtime checks:
-943 / 1,594 moves / weighted moves. The user's test edits were preserved.
-Logs: `build/load-search21-tests5.log`, `build/load-search21-seeds.log`,
-`build/load-search21-loop-seeds.log`, `build/brain-variants-search.log`, and
-`build/load-search21-spills-complete.log`.
-
-### Chapters 22-24 lazy memory (implemented, review checkpoint)
-
-The forward port now reaches Chapter 24. Parameters, calls, and returns carry
-whole memory; BulkMemPhi/MemPhi discover aliases within functions. New consumes
-partial memory for its instance fields and produces `{ptr, $mem}`. The Chapter
-21 loop Load search, one-step Load factoring guard, dependency fixes, and
-alias-aware scheduling/evaluation carry through all three snapshots.
-
-Chapter 22 preserves C calls and its default-main handling. Chapter 23 keeps
-cyclic field types, deep-final Loads, and symbolic field offsets; class-wide
-fields do not participate in an allocation's memory coverage. Phi factoring
-rejects operand Phis mixing pointers and integers, even for boolean operations.
-A precise Phi waits for its bulk input to split instead of hiding that request
-behind an upcast. Eval2 keeps unknown-caller pointer placeholders distinct from
-actual heap constants.
-
-Chapter 24 retains its optimistic solver: MemPhi ignores TOP/XCONTROL arms,
-New returns high results while its control is high, and alias contents register
-a dependency on that control. The existing integer widening and call-graph
-solver remain intact. Register checks run before encoding adds untyped branches.
-Private constructor memory, escape tracking, and incomplete-type SSA stay in 25.
-Pretty-printer consolidation is implemented below.
-
-### Chapter 25 memory follow-through (implemented, same review batch)
-
-The memory representation was already present. The remaining port carries the
-pure loop Load search, generalized Phi factoring with the one-step Load guard,
-aggregate-aware scheduling, Call operand protection, and dependency wakeups.
-It also carries the x86 register-bank Load encoding and ARM MOVZ/MOVK emulator
-corrections. Bulk splitting queues new precise Phis instead of assuming an
-eager fold still returns a MemPhi.
-
-The search preserves private constructor memory and Escape publication. A Store
-whose alias is still 1 blocks the proof; external storage cannot be bypassed
-using allocation identity. Length reads can drop control, while indexed array
-reads retain it. Integer loop value Phis use Convert to preserve the declared
-load width, and every new nested value Phi is queued for optimization. The
-constructor, escape, serialization, and incomplete-type architecture stays intact.
-
-### Adjacent-copy forwarding: Chapters 21-25
-
-Chapter 21 now removes a post-color Split when its sole use is the immediately
-following non-Phi, non-CFG instruction, the operand accepts the source register,
-and the operand is not two-address tied. Adjacency proves no intervening clobber.
-The same rule and focused guard regression are present in 22-25; Chapter 20
-retains its baseline allocator. No spill-selection or CFG splitting rule changed.
-
-RISC-V BrainFuck keeps its original source and now has 35 / 42 retained / weighted
-moves instead of 144 / 165. Hello World executes 21,787 emulator instructions
-instead of 21,920; heap traffic remains 2,354 loads / 646 stores and stack traffic
-15 / 15. Full Make suites and fuzzer targets pass in 21-25. Chapters 21-24 also
-pass complete spill reports after refreshing reduced-move expectations:
-
-| Compiler | Allocations | Retained moves | Weighted moves |
-|---|---:|---:|---:|
-| 21 | 91 | 821 | 1,444 |
-| 22 | 117 | 826 | 1,470 |
-| 23 | 147 | 913 | 1,711 |
-| 24 | 214 | 1,310 | 2,969 |
-
-Cohort 22 includes the two zero-move C return ABI checks added since the older
-24-entry audit. Chapter 25's frozen 212-entry manifest remains unchanged.
-At that audit, its reporter failed on ten pre-existing String-constructor parse errors;
-the unmodified allocator reproduces all ten. The same 216 successful allocations
-(including 14 current cohort-25 allocations) improve from 2,563 / 5,496 to
-2,494 / 5,399, but these are partial totals. The subsequent historical String
-fixture repair below restores complete reporting. Fresh system-library encoding and
-native cohort-25 checks pass. Logs: `build/copy-forward21-spills-final.log`
-through `build/copy-forward24-spills-final.log`, `build/copy-forward25-spills.log`,
-`build/copy-forward25-baseline.log`, and `build/copy-forward*-tests*.log`.
-
-### Shared debug printers: Chapters 2-25 (implemented)
-
-The new [`print/`](../print/README.md) module owns expression recursion, IR
-row formatting and ordering, scheduled dumps, and assembly listing layout.
-Chapters retain their node spelling and small adapters for graph/machine facts.
-All chapter Nodes extend the shared `BaseNode<Node>`, providing one read-only
-node contract for printers and the viewer. Chapter 2 arithmetic needs only a
-one-line `format()` hook such as `"(%1+%2)"`; variable forms use protected hooks
-with `p.p(...)` and `p.n(...)`. Repeat tracking is universal, with repeated
-expansion for short constants. Common graph edge and snapshot construction has
-also moved out of chapter adapters.
-This also addresses issue #259's outside-package subclass use case. PR #260's
-public `_print1` change is superseded by the context API, rather than merged.
-
-The shared CFG-first RPO carries the Chapter 21 BrainFuck ordering correction
-through 25: loop headers precede their bodies, closing Regions follow them,
-and Phis stay with their headers. Function boundaries exclude caller arguments;
-shared floating expressions of constants print once with globals. All traversal
-and placement state is private; raw edges and captured encoding/layout metadata
-keep diagnostics read-only. `Node.toString()`/`print()` remain the expression
-entry points; `Node.p(depth)` starts structured dumps in Chapter 8, and
-`CodeGen.toString()` starts whole-program dumps in Chapter 18. The unused
-Chapter 7 IR adapter, `Node._printLine`, legacy LLVM-format wrappers, and
-`prettyPrintScheduled` aliases are removed. Evaluator/compiler `printLine`
-callers and the scheduled whole-program path in Chapters 20-24 remain.
-The wrapper cleanup passes all 26 chapter suites and the shared printer
-contracts (`build/prune-print-tests.log`, Chapters 1-9, and
-`build/prune-print-later-tests.log`, Chapters 10a-25).
-
-Make, Maven source paths, IDEA module dependencies, release packaging, and
-linearized checkout support include the shared sources. Shared contracts cover
-cycles, function boundaries, shared globals, Phi/projection grouping, multiline
-assembly, and captured pool metadata. Disposable real-graph checks cover
-BrainFuck and call/loop programs through Chapter 25, including repeatability,
-complete unique output, CFG order, and graph/cache identity. An outside-package
-Chapter 2 subclass compiles and prints nested operands using the protected hook.
-
-Validation: all 26 chapter Make suites and the shared contracts pass
-(`build/print-final-tests.log`). Chapters 21-24 each pass 18 real-graph snapshots;
-25 passes 12 (`build/print-probe-*.log`). Assembly checks cover all available
-CPUs in 19-25 before/after allocation and encoding (`build/asm-probe-*.log`).
-Fresh linearized Chapter 2 and 21 releases contain the shared printer classes;
-Chapter 2's fresh tests also pass (`build/print-linear{02,21}.log`). Maven/IDE XML
-parses, the linearization script passes its syntax check, and changes are LF-only.
-
-The subsequent BaseNode cleanup also passes all 26 suites
-(`build/base-early-final-tests.log`, `build/base-late-tests.log`) and the updated
-shared contracts, including multi-digit slots, escaped percent signs, missing
-inputs, cycles and matching-suffix trimming. Three Chapter 5-6 print expectations
-now use a short reference on a repeated Region. Nine Chapter 21 viewer snapshots
-match their pre-cleanup baseline except for canonical constant labels; both 21
-and 25 pass nine read-only, repeatable captures. Outside-package expression and
-format hooks compile. Fresh linearized Chapter 2/21 releases contain BaseNode and
-viewer classes (`build/base-linear{02,21}.log`). The graph IDEA module now depends
-on print. This cleanup removes about 2,800 node-source lines and 550 viewer-adapter
-lines beyond the original extraction.
-
-## Pending corrections
+| Conditional Store and array Load control | Reproduce under the destination chapter's alias model. |
+| SCCP dependencies, function revival, reachability | Separate corrections applicable to Chapter 24 from Chapter 25's guards, escape analysis, and external-caller machinery. |
+| Incomplete types, numeric modes, guards, symbolic fields, private constructor memory | Choose chapter boundaries and move the required invariants together. |
-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). Active work is AOT class
-initialization below. Phase cleanup also needs backporting; the other items
-stay deferred for the chapter split.
+Serialization, global identity remapping, and module escape summaries stay
+with separate compilation. Escape analysis stays in Chapter 25 pending the 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 follow-ups
-- **Chapter 25 deterministic inlining (deferred until after the Chapter 25 split).**
- As of 2026-10-07, ordinary functions reject `_approxUIDs >= 100` before checking
- the optimized body. `_approxUIDs` counts node IDs consumed during parsing and
- is not refreshed after optimization. In the now-private Chapter 20 String
- replay, `_hashCodeString` is rejected at 100 although its live body has 25
- nodes. MergeSort's `_merge` is rejected at 182 although its body has 54;
- both are below the ordinary live-body threshold of 100. A shadow build removing
- only that early rejection inlines both helpers and drops their out-of-line
- bodies; MergeSort retains its recursive `_split_merge` as expected. The
- ordinary-function policy is unchanged. Cliff deferred the larger repair until
- after splitting Chapter 25: converge shrinking rewrites, then choose one growing
- inline deterministically using current body sizes cached per convergence round.
- Stable call-site provenance must break ties rather than randomized worklist
- order or newly assigned node IDs. Avoid dependencies on every body node and
- repeated scans during each peephole. The bounded boilerplate check below is
- the small interim heuristic; the larger constructor parse allowance was replaced.
- Evidence:
- `build/ra-repair/inlining{,-ablation}.log`.
+Class-cycle checking and CLInit runtime lowering stay in Chapter 25. The
+[chapter README](../chapter25/README.md#lazy-class-initialization) explains the
+implementation; future work lives in the
+[roadmap](../chapter25/ROADMAP.md#class-initialization-and-compiler-follow-ups).
+Native code cannot receive classes; a native-startup/class-handoff policy is
+not planned. Current data pointers remain eight bytes.
-- **Chapter 24 Load BOTTOM-on-error backport, unwound/deferred (2026-09-28).**
- Cliff requested starting with 24 and analyzing failures before proceeding.
- Literal global BOTTOM memory/pointer inputs already propagate BOTTOM. The
- missing behavior is `LoadNode.compute()` returning BOTTOM instead of the
- declared field type when `err()!=null`; Cliff unwound that one-line experiment.
- Chapter 25's Convert nodes supply the surrounding expression/loop's typed
- result while retaining the erroneous BOTTOM-producing Load for TypeCheck.
- Chapter 24 relies on the Load itself supplying that typed result. Defer this
- backport with the conversion split rather than importing it alone.
- The experimental full Make suite had four failures out of 477
- tests (`build/load-bottom-24.log`); the fuzzer recipe was not reached.
- - `BrainFuckTest.testBrainFuck`: during parsing, the unfinished loop Phi for
- mutable `program` has its declared nullable type. The element load becomes
- BOTTOM, and reading the initialized `command` local triggers Parser's
- BOTTOM-means-uninitialized check before the loop can finish. Reduced valid
- source: `var !program="a"; for(int pc=0; pc<program#; pc++) { var command=program[pc]; return command; } return 0;`.
- - `Chapter10Test.testWhileWithNullInside`: the loop's result Phi starts at its
- declared `i64` while incomplete. Closing the loop exposes a BOTTOM Load on
- the backedge; Phi.compute explicitly propagates BOTTOM, violating the
- pessimistic `setType` narrowing assertion. Trace: Phi#1458 goes from i64 to
- BOTTOM with Load#1462 on its backedge. This occurs during parsing, before SCCP.
- - `Chapter10Test.testNullGuardErrors`: ReturnNode sees BOTTOM without any
- recorded scalar return kind, so its `No defined return type` error wins
- before the Load's `Might be null` diagnostic.
- - `Chapter13Test.testLinkedList0`: `head.next` becomes BOTTOM, and parsing the
- following `.i` reports `Expected reference but found Bot` first.
- Scratch instrumentation lives entirely under `build/load-bottom-probe`.
- Reduced sources pass or report the intended null error with the HEAD Load
- compiled separately, and reproduce the failures with the one-line change
- (`build/load-bottom-reduced-baseline.log`, `build/load-bottom-reduced.log`,
- `build/load-bottom-trace.log`). No earlier chapter was changed. Fixing these
- requires coordinating parse-time incomplete types, typed loop-Phi bounds,
- and diagnostics; the assertion and expected errors have not been weakened.
+Allocator tuning and additional emulator measurement coverage are closed.
+Current allocation measurements live in [regalloc-spills.md](regalloc-spills.md).
-- **Chapter 25 BOTTOM-on-error backport boundary (2026-09-28).** Cliff requested
- investigating the earlier arithmetic contracts. In 24, changing ArithNode's
- non-integer fallback to global BOTTOM breaks the valid `testCoRecur`: the
- declaration `val az = x*2` refers to `x` defined later, and the allocation-time
- check confuses its temporary BOTTOM with an unset field. Three arithmetic
- diagnostics also become generic parser errors (`testNullRef5`, `testMaskFloat`,
- `testSubZeroTypeError`). Full-suite evidence: `build/arithmetic-contract-24.log`.
- Restoring the integer fallback fixes all four in a 40-test focused run
- (`build/arithmetic-top-24.log`). The Java return signature itself has no typed
- caller dependency. Further BOTTOM work is paused for Cliff's decision about
- separating forward/unresolved expressions from initialization errors in the
- earlier parser. The independent Load experiment is recorded above. The
- known-null regression is already rejected
- during parsing in 10-24. Keep this separate from the constructor/memory fixes.
+## Backport validation
-## AOT class initialization
-
-Authorized on 2026-10-08 as the final cleanup before revisiting the Chapter 25
-split. The declared-constant final-field portion is implemented in Chapter 25:
-
-- StaticData owns object placement, canonical class identity, and relocations
- for function/object pointers in ELF and emulator data images. Imported class
- objects reference their defining compilation unit's `Name.$class` symbol.
-- Instruction selection omits a final-field Store only when its declared type
- already identifies exactly the stored constant. Serialized ideal IR retains
- its memory edges; no lattice or constructor-memory contract changed.
-- Runtime stores to mutable or insufficiently precise fields remain. Constant
- pools remain read-only and class objects remain writable. Function pointers
- remain four bytes, data pointers eight bytes. Native linking now explicitly
- selects low-address placement for the existing function-pointer width; ELF
- relocation overflow is checked by the linker.
-- The full parallel Chapter 25 Make suite passes (395, 37, 9, 23, 1, 21 tests).
- The final focused suite passes three static-data checks: native and ARM/RISC
- function-pointer calls before `<clinit>`, plus two serialized importers
- sharing one mutable class-object address. The third check was added after
- that full run. All 199 historical allocations and 108 dynamic cases pass;
- cohort membership, inputs and results remain fixed. Tables are refreshed.
- Logs live under `build/aot-init/` and `build/{spill,dynamic}-stats/`.
-
-**Initialization semantics decided:** dynamic `<clinit>` runs once on first
-active touch, after its parent completes. Passive class references do not
-trigger execution. Reject initialization cycles across modules; there is no
-early parent-ready point. Untouched classes execute no initialization code.
-The [lazy initialization design](../chapter25/initialization.md) covers the
-proposed initialization dependencies directly on CompUnit, CLInitNode checks with parser-flow
-and ideal simplification, a hidden `$init` field set inside `<clinit>`, static
-proofs, and serialization/composition validation. The constant-field lowering
-above does not implement the runtime initialization protocol.
-The [implementation checkpoints](../chapter25/initialization.md#implementation-sequence)
-choose CompUnit dependency edges and function may-touch summaries for cycle
-checking, separately from ordinary alias-memory facts for runtime `$init`.
-No separate ClassInit object or analysis-only memory slice is planned initially.
-
-**Checkpoint 1 reviewed (2026-10-10):** initialization cycles now
-count only requirements surviving Opto. Parser inserts ideal CLInit obligations
-on control; the collector walks optimized function bodies and propagates their
-final call-target effects into CompUnit dependencies. Removed branches/targets
-leave no historical edges. All generic Node watcher hooks are removed. Parent
-edges, cycle witnesses, and stable topological ordering remain. Nonreturning
-calls left out-of-line retain conservative continuations; touches after them
-can contribute extra initialization ordering. The ideal obligations are erased
-after validation for this checkpoint;
-runtime flags/calls and serialized summaries are still pending. Imported/native/
-unresolved effects remain explicitly incomplete and `_initStatic` remains false.
-All 492 Chapter 25 tests pass, plus focused x86/ARM/RISC-V encoding checks for
-the nonreturning-call fix added after the full run. Two existing RISC-V spill
-expectations improve from 9 to 7. Log: `build/init-deps-opto-final.log`.
-See the [checkpoint notes](../chapter25/initialization.md#1-compunit-dependencies-and-cycle-checking).
-
-**Non-exiting function inlining (2026-10-10):** general body cloning starts in
-Chapter 25. Chapters 20-24 clone only restricted straight-line boilerplate;
-their older `FunNode.body()` helpers have no callers. Chapter 25 now discovers
-body control forwards from Fun, including infinite-loop arms, and explicitly
-copies its Return even when that node has no executable incoming control.
-The nonreturning-inline guard is removed. Serialization also permits a unit
-whose unused memory projection disappeared. The regression covers a wholly
-non-exiting function and a two-call function with both returning and infinite
-arms, checking inlining and Eval2 behavior across three seeds and all three
-machine encoders with allocation checks. All 493 Chapter 25 tests pass;
-log: `build/inline-never-exit.log`.
-
-Changing data-pointer width and moving fully initialized class objects to
-read-only storage remain separate follow-ons.
-
-**Checkpoint 2 implemented for review (2026-10-10):** each class initializer's
-object has a normal mutable boolean `$init` field with a zero initial image.
-The C0DE header now carries owned units, initialization edges, completeness/static
-flags, and function effect summaries. Ideal CLInit markers survive serialization;
-backend erasure happens afterward. Loading remaps identities and validates the
-combined graph, including imported initializers without live bodies. Object
-discovery distinguishes definitions from referenced class names, and serialized
-IR excludes imported bodies. Focused fixtures cover cross-object cycles, shared
-diamond identities, interleaving module orders, imported helper inlining, and
-flag layout/remapping. Runtime check/call/store lowering remains checkpoint 3;
-rebuild old objects after the format change. All 494 Chapter 25 Make tests pass.
-Diagnostic witnesses use compact `ClassCyclicError` records with numeric locations
-and shared call-chain suffixes, including across serialization. Strings are
-formatted only on rejection. Validation includes source call-chain and imported
-field locations. Logs: `build/init-checkpoint2-*.log` and
-`build/class-cycle-witness-all.log`.
-
-The original broader design below remains a proposal, not a completion claim.
-
-The goal is to pre-allocate AOT class objects in ELF data and pre-fill their
-fields, letting ordinary graph optimization remove redundant initialization.
-Lazy-loaded classes retain runtime initialization for now.
-
-- Represent static object creation with a dedicated node, or a ConstantNode
- carrying a TypeStruct with the appropriate final field values. Preserve the
- class's identity and canonical layout independently of its current field
- values; equal contents must not merge distinct class objects.
-- Use normal StoreNode peepholes to remove same-value-over-same-value stores.
- If needed, add a peephole that folds initializing stores into the static
- creator's type. Create a replacement ConstantNode/type; never mutate a shared
- constant's type. Prove that folding preserves initialization ordering and
- does not expose a final value prematurely through another reference.
-- When `<clinit>` reduces to returning the static object, with no remaining
- I/O or public-memory effects, its runtime work can disappear. Initially it
- is sufficient to emit a trivial `<clinit>` and keep its calls. Omitting the
- function or calls is a later optimization; mixed initializers retain their
- remaining effects. Adapt this conceptual return to the existing class-memory
- representation rather than assuming the current `<clinit>` returns a pointer.
-- Extend encoding's relocation information to describe addresses stored inside
- data: functions, other class objects, and constant objects such as strings.
- Emit the required data-section relocations, including their symbol, field
- offset, width, and addend. Keep native linking and emulator linking consistent.
-- Give the function and data distinct stable symbols, e.g. `A.B.<clinit>` and
- `A.B.$class`. The defining CompUnit owns the class object and emits its one
- definition in its ELF. Other compilation units, including source currently
- being compiled, reference those symbols without emitting another copy of the
- class data. Unifying loaded Simple types/IR alone does not unify native data;
- preserve ownership and identity across partial compiles and serialization.
-- Target four-byte function **and data** pointers, with code and data addresses
- restricted to the low 4 GB. Function pointers stay four bytes; data pointers
- must be brought into agreement. Account for layout, loads/stores, relocations,
- native runtime/FFI boundaries, and actual placement; do not silently truncate
- an out-of-range address. This is a proposed representation constraint, not a
- claim that the current runtime already satisfies it.
-- Emit immutable, fully preinitialized objects in read-only storage. Objects
- with mutable fields or remaining runtime initialization writes stay writable.
- Retain the current writable-class-storage correction until those writes have
- actually been eliminated.
-
-Acceptance should cover scalar and pointer fields, shared/cyclic object
-references, preserved effects in mixed initializers, and serialization
-round-trips. A diamond of separately compiled users must share one class-object
-address and observe each other's mutations. Check ELF definitions/relocations,
-address-range enforcement, native execution, emulator execution, and the full
-Chapter 25 suite, including `make -j 4 tests`.
-
-## Larger or entangled changes: defer
-
-| Group | Eventual home | Why not in the small queue yet |
-|---|---|---|
-| Conditional Store and array Load control | Memory/arrays chapters | Reproduce under the earlier alias model before extracting fixes from the new memory implementation. |
-| SCCP dependencies, function revival, reachability | 24; some foundations may fit 18 | Separate old-IR corrections from new Guard/Escape/BulkMemPhi and external-caller machinery. |
-| TypeScalar, numeric modes, guards, symbolic fields, open/forward types | Revisit earlier homes later | A connected incomplete-types architecture, including phase ordering and errors. |
-| BulkMemPhi/MemPhi, private constructor memory, allocation helpers | Revisit memory / constructors / methods | Move invariants and regressions together. |
-| Serialization, global identity remapping, module escape summaries | Separate compilation | Remain with modules. |
-
-## Register allocation: completed chapter progression
-
-The allocator review is complete through Chapter 25; Cliff reviewed Chapter 23
-on 2026-10-07. Each README ends with the introduced technique, a triangular
-matrix of fixed cohorts across compiler chapters, and commentary. The current
-[spill-cost matrix](regalloc-spills.md) is regenerated by root `make spill-stats`,
-which checks matching test/CPU/ABI membership with assertions enabled.
-
-| Chapter | Additional technique | Final cohort sizes |
-|---|---|---|
-| 20 | Basic coloring, rematerialization and loop splitting, with shared legality/progress fixes | 39 |
-| 21 | Conservative copy coalescing and adjacent-copy forwarding | 39, 52 |
-| 22 | Stronger copy-chain/backedge bias and cheap-spill ordering | 39, 52, 26 |
-| 23 | Group popular single-def uses by compatible register classes | 39, 52, 26, 24 |
-| 24 | One cold-only attempt for loop-Phi self-conflicts, then mandatory fallback | 39, 52, 26, 24, 58 |
-| 25 | Existing area/cost ranking and later conflict strategies | 39, 52, 26, 24, 58, 14 |
-
-Shared corrections include RegMask/LRG bookkeeping, null use masks, kills without
-an output LRG, self-conflict splitting, compatible rematerialization, clobber-aware
-copy reuse/bypass, deterministic ordering, and side-effect-free diagnostics. The
-final audit adds the missing narrow x86 masks in 20-21 and RISC-V mask in 21;
-fixed-neighbor color bias in 20-25; and ARM memory register-bank selection,
-float opcodes, and matching emulator fixes in 21-25.
-
-Chapter 25's additional fixed-use/deep-side splitting thresholds remain there.
-The def-use-count snapshot and null-use guard support its extra split branch;
-20-24 do not have that branch. Earlier kill handling subtracts killed registers;
-25 can choose to split cloneables earlier. No independent earlier-chapter
-correctness failure was established for these strategy differences. They are
-not uncompleted promises to copy the complete allocator backwards. Compilation-
-unit ownership and void/metadata handling also stay with the later representation.
-
-The final ARM failure was substantive: the original Newton float programs reached
-the allocation round limit because a float store demanded a GPR; the deeper loop
-split kept cutting the wrong portion of the range. Permitting the supported FP
-registers removes that conflict. Encoding must then use the allocated register
-bank, with five-bit register numbers, for both immediate and indexed loads/stores.
-The emulator must preserve floating bits and scale double offsets by eight.
-
-Chapter 25 freezes 199 earlier compilation entries, after excluding Jig scratch
-tests, in
-[documented source fixtures](../chapter25/src/test/java/com/seaofnodes/simple/spill/README.md).
-Constructor/library adaptations change IR, so this is a program-cohort comparison,
-not identical machine graphs. Those rows replay allocation and legality checks;
-current Chapter25Test native checks and a fresh system-library encoding contribute
-14 more recorded allocations. The complete 2026-10-02 replay totals **2,568 moves /
-5,459 loop-weighted moves** over 226 compilations after the fixture repair below.
-Earlier audit total, before adjacent-copy forwarding: **2,597 moves / 5,579 loop-weighted moves** over 222
-compilations. Chapter 25's area/cost implementation is retained. Substituting the
-earlier ranking saves five moves on the clients but fails a fresh `sys` allocation
-at the eight-round limit; a failed library cannot be omitted from the comparison.
-
-The earlier tables are refreshed where the final mask fixes changed them:
-Chapter 20 was 238 / 357 before the subsequent lazy-memory port (now 234 / 360);
-Chapter 21 was 840 / 1,456 before lazy memory and adjacent-copy forwarding.
-The audit's coalescing on/off comparison saves
-230 / 433 moves in Chapter 21 with identical legality fixes. Current totals for 21-24 appear in the adjacent-copy record above. Full details and comparison limits belong in
-the individual READMEs rather than a second evolving set of tables here.
-
-## Review and test protocol
-
-1. Establish the destination baseline. Reproduce a real failure with a small
- source or constrained machine graph before classifying a change as a bug fix.
-2. Apply the smallest correction, propagate it to all affected snapshots, and
- run each snapshot's focused checks and full `make tests`. Respect the current
- user-authorized review boundary; do not push without explicit instructions.
-3. Force Java rebuilds when older Makefiles miss dependencies. Rebuild `sys.o`
- after compiler changes; include a fresh library compilation in allocator
- measurements rather than relying on an up-to-date object.
-4. Keep legality/progress/runtime checks separate from spill expectations.
- Eight-round exhaustion is a failure; neither increasing the cutoff nor
- changing a golden proves it fixed. Preserve test membership, targets/ABIs,
- and fixed seeds. Do not sweep optimizer seeds for routine backend validation.
-5. Report aggregate and local changes. Use same-compiler heuristic comparisons;
- changed frontend/lowering graphs are not allocator-only measurements.
-6. Keep edited text LF-only, preserve unrelated changes, and run `git diff --check`.
-
-The top-level runner accepts explicit chapter lists, e.g.
-`make -k tests CHAPTERS="chapter20 chapter21"`. Tests in 25 alone are insufficient.
-
-## Validation record
-
-- **Equality and ARM execution corrections, 2026-10-07.** Chapters 21-25
- retain RISC-V's subtraction when expanding equality and use unsigned SEQZ
- for logical-not (including negative inputs), exclude SP/XZR from
- narrow ARM load destinations, and encode ARM logical-not with CMP-to-XZR
- followed by CSET-to-result. ARM condition inversion is corrected in 21-23;
- FCMP operand order in 21-24. Chapter 20 has different selection/stub encoders
- and does not contain these faulty expansions. Shared evaluators now use
- unsigned SLT/SLTIU comparisons and discard ARM XZR destination writes.
- The existing Chapter 24 nested-equality test in 24/25 expected the wrong
- result for argument 1; it now checks -1, 0 and 1. Independent ISA checks and full
- Make suites pass in 21-25 (including native/system tests). All previously
- excluded measurement cases execute successfully; the reporter also checks
- nested equality at 0-3. Logs: `build/dynamic-stats/fix-tests{21..25}.log`
- and `fix-isa.log`. The expanded matrices and a measured breakdown of the
- Chapter 24-to-25 increase are in [the allocation report](regalloc-spills.md).
- Cohort 23's +476 Ops is chiefly class-field initialization; its +282 RA
- instructions comprise 280 rematerializations and only two extra splits.
-
-- **Dynamic allocation metrics, 2026-10-07.** Chapter 20-25 README matrices now
- report executed ARM/RISC-V instructions, the allocator-created subset (copies,
- spills/reloads and rematerializations), and x86-only weighted split estimates.
- Dynamic membership and input vectors are fixed per cohort, with results
- checked across compilers/targets. Native-library bodies are excluded. Chapter
- 20 has no encoder. The dynamic rows cover 22/20/14/14/36/2 program-target
- cases; compilation-only/API exclusions are explicit.
- The original full allocation cohorts are unchanged. Expanded shared evaluator
- support fixes ARM SDIV, ORR-immediate and false CSET values, and RISC-V floating
- stores and FEQ/FLT decoding. Independent instruction-word checks pass in
- `make -C isa tests`; full Make suites pass in 21-25, including Chapter 25's
- native/system groups. Logs: `build/dynamic-stats/tests-{21..25}.log` and
- `isa-tests.log`. The reporter validates cohort identities, inputs and results;
- generated per-case records are in `build/spill-stats/dynamic-counts.csv`.
-
-- **Remaining replay fixture cleanup, 2026-10-07.** Fourteen Chapter 25 replay
- sources make implementation helpers/classes private while retaining export
- APIs, explicitly return zero from three String API fixtures, and correct two
- guarded short-circuit adaptations to return their RHS value (not `!!value`).
- Disposable Eval2 checks at arguments 0-3 return 44, 1, 44, 1 for both loops.
- `make -C chapter25 spill-stats CTAGS=` passes all 199 historical allocations,
- fresh library encoding, 23 native tests and spill expectations. Fixed cohort
- membership remains unchanged. Cohort 21 changes 1,077 to 1,036; 23 changes
- 152 to 104; 24 changes 1,503 to 1,315. Other rows are unchanged. The matrix
- reporter validates membership against saved Chapter 20-24 reports and updates
- the documentation. No compiler changes or permanent tests were added.
- Logs and disposable allocation/assembly probes: `build/fixture-cleanup/`.
-
-- **Ignore debugging Jigs (2026-10-07).** All `testJig*` methods are scratch
- placeholders, not regression tests. Added missing `@Ignore` annotations in
- Chapters 14-25, preserving their bodies, and removed all 15 Jig entries and
- four unused frozen sources from Chapter 25's replay corpus. Cohorts 23/24 now
- have 24/58 allocations; the corpus has 199 entries. The debug Fibonacci in
- Chapters 22-24's `progs/jig.smp` uses private `_fib`; `fib.smp` and the genuine
- `testFibExport` fixtures retain their exports, as confirmed by Cliff.
- Full Make suites pass in all 13 affected snapshots, including 17a/17b and
- Chapter 25's native groups, plus shared printer/ISA checks. Fresh spill reports
- pass in 20-25, and every retained raw/scaled allocation record matches its
- prior measurement. Cohort 23 is now 103/103/152 across compilers 23/24/25;
- cohort 24 is 1,387/1,503 across 24/25. The historical heuristic comparison was
- recomputed from saved records with the same exclusions. All tables refreshed.
- A Chapter 25 allocation probe confirms the private debug Fibonacci leaves
- only `<clinit>`, with no separate Fibonacci body. Logs: `build/jig-cleanup/`.
-
-- **Tiny-body inlining backport, Chapters 20-24 (2026-10-07).** Preserved Cliff's
- reusable static 15-node worklist and Return-input/prefix dependencies. These
- chapters previously supported only single-caller inlining, so the port adds
- restricted copying of straight-line Store/MemMerge bodies, substituting call
- arguments and control directly. Growing inlines run from a separate worklist
- after ordinary peepholes settle; existing single-caller inlining is unchanged.
- Constructor defaults already expand directly in these chapters; they have no
- Chapter 25-style constructor calls or parse-size gate to relax. All full Make
- suites pass: 406/445/466/488/518 tests respectively, plus each fuzzer wrapper.
- The existing `Chapter18Test.testInline` graph now contains literal `1` on one
- branch; no new permanent tests were added. Disposable two-call store probes
- pass with seeds 123/456/789 in all five chapters, including an intervening store
- to another alias. All 39/91/117/147/214 allocation records, including raw and
- scaled costs, match the prior reports exactly. Logs: `build/tiny-inline/final*.log`.
- Chapter 25 code and the deferred general inliner redesign are unchanged.
-
-- **Bounded tiny-body inlining (2026-10-07).** Replaced the constructor-specific
- 1,000-node parse allowance with a conservative at-most-15-node check when the
- parse estimate exceeds the normal limit. It recognizes entry/parameter/store/
- memory-merge/Return boilerplate, follows uses to avoid sparse alias arrays,
- bounds fanout, and stops at Return. Dependencies watch Return inputs and only
- the inspected prefix/frontier; failed checks can retry after folding. Actual
- body limits remain 100, or 200 for `<new>` and instance `<init>`. All six replay
- constructor calls still fold, and all 228 raw/scaled allocation records match
- the preceding allowance trial. Full Chapter 25 Make tests pass, as do fresh
- Win64/SystemV library compilations. Disposable probes force parse estimates
- to 10,000 on Bubble Sort initializers and verify successful inlining/allocation
- on all three targets. A dependency probe verifies requeueing without a Return
- type change, rejection above the small-body bound, and acceptance after a
- memory-chain fold. Logs: `build/tiny-inline/`. The general inliner redesign
- remains deferred until after the chapter split.
-
-- **Chapter 25 constructor inlining allowance (2026-10-07).** `<new>` and
- instance `<init>` share a 200-node optimized-body limit, including the cloning
- assertion. A 1,000-node parse-time cutoff accommodates nested-method and
- memory construction overhead; ordinary functions stay at 100 for both gates.
- The initial 200/200 trial changed none of 228 allocation records. The larger
- parse allowance removes all six remaining constructor calls in the frozen
- replay sources (Bubble Sort), while public constructor definitions remain.
- Cohorts 20-23 are unchanged; cohort 24 improves 1,637 to 1,634, while cohort 25
- rises 1,829 to 1,862. Win64 sys improves 842 to 809, Dijkstra 584 to 582;
- FileIO rises 293 to 361. Fresh SystemV sys improves 803 to 777. These static
- move costs do not establish a runtime improvement. Full Chapter 25 Make tests
- pass (405 + 39 + 9 + 23 + 1 + 19), and the complete spill report passes its
- 214 replay allocations and native/system tests. No goldens, replay sources,
- seeds or memberships changed during this experiment. Matrices are refreshed.
- Earlier compiler snapshots are unchanged: 20-24 have single-use non-cloning
- inlining rather than this size-based cloning gate. Logs: `build/ctor-inline/`.
-
-- **Chapter 25 function constants and private helpers (2026-10-07).**
- Single-use, single-definition function addresses separated from their use in
- the same block get priority for rematerialization over spilling callee-save
- registers. Adjacent, multi-use and cross-block cases keep the existing ranking.
- On unchanged inputs this removes all 28 C-return initializer moves, plus 13
- weighted moves from cohort 24 and 22 from cohort 25, with no other cohort
- changes. Eleven replay fixtures separately mark local helpers/classes private;
- exported interfaces stay public. Final Ch25 weighted costs by cohort are
- 491 / 1,077 / 71 / 380 / 1,637 / 1,829, with unchanged membership and counts.
- The fixture README and matrix document the changed visibility baseline.
- Full `make -C chapter25 tests` passes (405 + 39 + 9 + 23 + 1 + 19 tests),
- as do both complete spill reports and fresh SystemV sys encoding on Windows
- (327 raw / 803 weighted moves). The reports each include 214 replay allocations,
- fresh Win64 library encoding and 23 native/system tests. No inlining-policy
- change or earlier-chapter allocator change was made. Logs: `build/ra-repair/`.
-
-- **Spill matrices and bounded CI processes (2026-10-07).** Reran Chapter 20-25
- statistics with fixed cohort sizes 39, 52, 26, 30, 67, and 14. Chapter 25 now
- includes the two Chapter 22 C-return ABI cases missing from its old manifest.
- The matrix generator checks the multiset of test/CPU/ABI identities and emits
- no totals row; each README shows columns through its chapter. Chapter 23's
- copied Java implementation snippets are replaced with direct source links.
- CI run 37675851863 timed out in `nm` after 10 seconds; run 37678408500 passed.
- The bounded GCC/nm/test-executable calls now allow 60 seconds and retain
- forced termination and captured stdout/stderr, with PID/CPU-time diagnostics.
- CLI subprocesses retain their existing unbounded behavior. Root `make
- spill-stats` passes and reproduces the matrix; the reporter rejects changed
- membership even when counts match, and rejects failed runs. All 23 Chapter 25
- system tests pass. An 11-second subprocess passes the new bound; an intentional
- hang is killed at a short test deadline with diagnostics and no live child.
- Logs: `build/spill-stats/`, `build/ci-timeout-60-tests.log`, and
- `build/ci-timeout-60-probe.log`.
-
-- **Numeric assignment conversions and evaluators (2026-10-07).** Chapters
- 14-25 accept float-to-integer assignment using ToIntegerNode: truncate toward
- zero, saturate to i64 on overflow, map NaN to zero, then apply the declared
- integer width. Locals, fields, and arrays use the conversion; Chapters 22-24
- also convert before their store-specific width handling. Chapter 25 lowers
- authoritative Convert nodes and serializes ToInteger in exported graphs.
- Both evaluators now execute ToInteger and RoundF32, including nonconstant
- f32 array stores. The Chapter 14 README documents the assignment syntax.
- Instruction selection starts in 19, ARM support in 20, and shared encoding
- in 21. x86 and RISC-V sequences normalize hardware overflow/NaN differences;
- ARM uses FCVTZS. Full Make suites pass in 14-25 and the shared ISA module.
- Tests cover constant and dynamic conversions, signed values, narrow integer
- results, limits, infinities, and NaN; Chapter 25 runs native x86 plus compiled
- ARM/RISC-V emulation. Disposable probes also check 19-20 selection/allocation,
- 21-24 encoding/emulation, and 25 importing an exported conversion function.
- Logs: `build/float-convert-<chapter>.log`, `build/float-convert-isa.log`.
-
-- **Floating arrays in Chapter 18 (2026-10-07).** Added TypeFloat to the
- `TypeStruct.makeAry` assertion, matching Chapter 19 onward; earlier array
- chapters already accept floating elements. The original `return new flt[1];`
- reproduction passes with assertions enabled. One regression covers flt/f64/f32
- zero initialization, flt/f64 looped loads and stores, and constant f32 rounding.
- It fails at the original assertion before the correction. The complete
- Chapter 18 suite passes (340 tests plus its fuzzer wrapper); no other compiler
- snapshot needed this assertion change. Nonconstant scalar and array narrowing
- passes type checking with the RoundF32 correction below. Subsequent evaluator
- support also exercises nonconstant f32 stores at runtime. Original logs:
- `build/float-arrays-18-before.log`, `build/float-arrays-18-after.log`.
-
-- **Nonconstant f32 narrowing (2026-10-07).** RoundF32Node now returns F32
- for an F64 input in Chapters 18-25, and the equivalent B32 for BOT in
- Chapters 14-17b. Constant rounding and other input types are unchanged.
- Chapter 18's floating-array regression also checks nonconstant scalar and
- array assignments. Complete Make suites pass in every affected chapter,
- including Chapter 25's native tests; no expected graph or allocation results
- changed. Logs: `build/round-f32-<chapter>.log`.
-
-- **Shared Phi inputs and deterministic inlining (2026-10-07).** Chapters
- 20-25 split a self-conflicting Phi's inputs when another Phi shares them;
- splitting only the entry and result could leave two loop Phis joined through
- their backedge forever. Chapters 24-25 keep their first cold-only attempt.
- Chapter 25 relinks cloned calls in node-ID order, removing identity-map
- iteration order from graph edges and optimizer worklists. The original
- SystemV sys-library allocation failure reproduced directly on Windows with
- `-XX:+UnlockExperimentalVMOptions -XX:hashCode=2`. With both fixes, complete
- phase dumps through allocation agree between Windows Java 21.0.4, Windows
- with constant identity hashes, and Linux Temurin 21.0.12.1. Windows suites
- pass in all six affected chapters (406, 445, 466, 488, 518, and 494 tests),
- including native tests and unchanged spill expectations. No tests were added.
- A fresh Linux checkout with Temurin 21 passes the exact CI command
- `make lib tests CTAGS=`, including all chapters, a fresh SystemV sys library,
- native tests, and shared printer/ISA checks.
- Logs: `build/ci-linux-all-tests.log`, `build/ci-windows-tests.log`,
- `build/ci-windows-ch25-final.log`, and
- `build/ci-fixed-{default,constant,linux}.log`.
-
-- **Unused parser memory aggregates, Chapters 11-24 (2026-10-03).** Release
- temporary prior-memory keeps with dead-node cleanup after publishing the new
- memory value. Flattening a MemMerge could remove its last real use while the
- parser kept it alive; plain `unkeep()` then left it unused and off the worklist.
- The Chapter 17b presentation alias example's MemMerge#18 now disappears.
- Its partial allocation input (#14) remains: null default means uncovered
- aliases, so it cannot be replaced with whole memory just because its covered
- slices coincide. Chapter 25's different construction sites were not changed.
- Make builds passed in all affected chapters; complete suites passed in 11
- (163 tests) and 12 (177). Existing Chapter10/11 tests passed in 13-24, plus
- Chapter15/16 tests where present and Chapter17a/17b tests in 17b. Updated
- earlier graph-shape expectations for the newly eliminated initializing Store
- and a different equivalent Phi name. Direct graph checks and evaluator runs
- passed for both presentation memory examples; their JSON/SVG captures were
- regenerated. Logs: `build/rebase-talk/memory-cleanup/`.
-
-- **Chapter 25 historical String fixtures (2026-10-02).** Added an explicit
- constructor initializing the required non-null `cs` field in
- `Chapter21Test-testStringExport.smp`, `Chapter21AllocTest-testString.smp`, and
- `Chapter23AllocTest-testString.smp`. Equality/hash bodies and the frozen
- 212-entry manifest are unchanged; all ten formerly failing entries now pass.
- These source adaptations establish a new comparison baseline, rather than
- demonstrating an allocator-only improvement. `make -C chapter25 spill-stats`
- passes all 212 historical entries, fresh system-library encoding, and all 23
- Chapter25Test tests, with zero spill-golden failures. The complete 226 recorded
- allocations total **2,568 retained moves / 5,459 weighted moves**; the README
- contains the cohort breakdown. Log: `build/string-fixtures-spill.log`.
-
-- **Function loop-tree boundaries, Chapters 18-25.** Stop the post-order loop
- walk at Return, seed each function's outer tree before visiting its body, and
- use that tree for synthetic exit projections, return Regions and the parent
- of otherwise exitless loops. In Chapter 25, walking through Return/StopCU/Stop
- into Start mistook the external-world cycle for another loop: the function
- entry had depth 1 while its return Region retained depth 0. Function bodies
- now consistently have depth 0, with real loops adding depth. Chapters 18-20
- also assign the tree to the newly created Never and both projections.
- `Chapter18Test.testNeverExitLoopDepth`, forwarded through 25, covers single,
- multiple, unconditional and nested synthetic exits. The RISC-V pointer-store
- reproducer `Chapter25Test.testNeverReturnPointerRiscV` now runs through
- Encoding without changing the spill heuristic or eight-round limit; its
- `@Ignore` is removed. Baseline failures are recorded in
- `build/loop-tree18-baseline.log` and `build/loop-tree25-baseline.log`.
- Full Make suites pass in `build/loop-tree-all.log`: 18 **333+1**, 19 **377+1**,
- 20 **399+1**, 21 **438+1**, 22 **459+1**, 23 **481+1**, 24 **511+1**, and
- 25 **403+39+9+23+1+14**, plus shared printer/ISA tests. Spill goldens remain
- unchanged.
-
-- **Synthetic TOP returns, Chapters 20-25.** TOP remains neutral in the type
- merge but has no runtime register value. BuildLRG excludes TOP definitions
- and Phi inputs; IFG does not propagate TOP arms into predecessor liveness;
- spilling ignores TOP for loop depth and inserts no copies for it. Chapter
- 20's register-bias walk also stops when a Phi arm has no LRG. Coalesce keeps
- its normal requirement that both ends of a real copy have live ranges.
- `Chapter20Test.testAllocatorTopPhi` checks either Phi slot in scheduled graphs
- from Chapter 20 onward. Source regressions in 21-25 (where loop repair precedes
- instruction selection) cover integer, non-null pointer and floating returns,
- multiple synthetic exits, no real exit, and the original two-field memory
- reproducer. Existing `testNeverMemory` tests now continue through Encoding.
- Full Make suites pass: 20 **398+1**, 21 **437+1**, 22 **458+1**, 23 **480+1**,
- 24 **510+1**, and 25 **402+39+9+22+1+14**, plus shared printer/ISA tests.
- Spill goldens were unchanged. The RISC-V pointer-store convergence test was
- ignored in that run; the subsequent loop-tree correction above enables it.
- Chapter 21 ARM/RISC-V execution
- of the original reproducer returns 0 for `arg=0` and stays in the loop for
- 10,000 instructions for `arg=1`. Logs: `build/top-ra-all.log` (20-24 and
- shared modules), `build/top-ra25-final.log`, `build/top-ra-execution.log`.
-
-- **GCM conditional-writer ordering, Chapters 21-25.** Restored the `idom()`
- walk from a writer's late block through its early bound, including matching
- memory-Phi predecessor paths. Chapters 11-20 already retained this walk.
- Exact-block checking missed a conditional Store that executes before a Load
- sunk below the merge. `Chapter21Test.testReadBeforeConditionalStore`, forwarded
- through 25, checks 120 for the aliasing case and 60 for the distinct-pointer
- case. Generated ARM/RISC-V execution agrees in Chapter 21. The walk was
- removed in `798bd959` (April 12, 2025, PR #201), before the Chapter 21 squash.
- Chapter 21's two RISC-V BrainFuck rows change from 35 / 42 to 48 / 146
- retained / weighted moves; the full 91-entry audit is now 847 / 1,652.
- Runtime checks pass and the two quality goldens are updated; no allocator
- heuristic changed. Validation logs: `build/gcm-walk-tests.log` (22-24),
- `build/gcm-walk21-final.log` (complete 21 rerun and spill audit), and
- `build/gcm-walk25-final.log` (complete 25 rerun).
-
-- **Chapter 25 memory follow-through.** Full Make validation passes **482 tests
- plus the 17-seed fuzzer wrapper**, and the release jar and system library build.
- The loop-search and read-before-write regressions fail against the original LoadNode and pass with the port. A
- scratch run across **100 optimizer seeds / 3,100 evaluations** checks the
- invariant length, a possibly aliasing arm, a changing loop pointer, differing
- stored values at a multiway merge, and reads before writes. Seeds 0 and 9 are
- retained in the regression for unresolved Store aliases and nested Phi queueing.
- At that audit, the historical spill replay retained exactly its **ten known String-constructor
- parse failures**, with no spill-golden or native-check failures. The same 216
- successful allocations total **2,488 moves / 5,379 weighted moves**, compared
- with 2,494 / 5,399 before this port; this is not a complete-suite total.
- The subsequent historical String fixture repair above restores a complete replay.
- Logs: `build/mem25-negative.log`, `build/mem25-seeds.log`,
- `build/mem25-final2.log` (spill replay), and `build/mem25-final4.log`
- (full tests and release).
-
-- **Chapters 22-24 lazy memory.** Full Make suites pass **454 / 476 / 506
- tests**, respectively, plus each chapter's fuzzer wrapper. Release jars build
- for all three chapters. Forwarded checks cover nested memory, constructors,
- calls and recursion, allocation registers, native x86 and emulated RISC-V/ARM
- execution, no-exit loop memory, and the loop Load search. Existing Chapter 23
- null-guard tests cover the mixed pointer/integer factoring rejection; Chapter
- 24's BubbleSort workload covers the precise-Phi/bulk-input collapse guard.
- Spill reports pass all **117 / 147 / 214** compilation entries, including
- runtime and allocation checks, with no golden failures. Current raw/weighted
- totals are **823 / 1,474**, **901 / 1,699**, and **1,307 / 3,008**; prior
- totals were 826 / 1,470, 913 / 1,711, and 1,310 / 2,969. These include graph
- changes outside allocation, so they are not allocator-only comparisons.
- The chapter README tables contain the current cohort breakdowns.
- Logs: `build/mem22-final.log`, `build/mem22-release.log`,
- `build/mem23-final2.log`, `build/mem23-release.log`, and
- `build/mem24-final3.log`.
-
-- **Chapter 21 lazy memory and encoding.** Baseline: **412 tests plus the
- fuzzer wrapper**. Final: **431 tests plus the fuzzer wrapper**. Make release
- and spill-stats pass. The 16 forwarded memory checks retain scheduling and
- allocation coverage; three execution/synthetic-exit tests add 42 native x86
- and 84 ARM/RISC-V evaluations with independent expected results, plus checks
- of no-exit loop memory through scheduling. **9,800 scheduled evaluations**
- pass across 100 optimizer seeds. Emulated multi-function programs enter at
- main's encoded offset, which need not be zero.
- The unchanged **91-entry** spill cohort changes **840 / 1,456** retained /
- weighted moves to **845 / 1,559**. RISC-V BrainFuck changes 35 / 42 to
- 37 / 58 in the Chapter 20 cohort and 35 / 42 to 46 / 130 in Chapter 21.
- An ablation omitting only read-before-New ordering returns those rows to
- 35 / 42 and 37 / 58; the ordering is retained, with no allocator tuning.
- All eleven changed rows are recorded in the chapter README.
- Logs: `build/memory21-baseline.log`, `build/memory21-final.log`,
- `build/memory21-seeds.log`, `build/memory21-spills-baseline.log`,
- `build/memory21-spills-final.log`, `build/memory21-spills-ablation.log`,
- and `build/memory21-release.log`. The later 22-24 port is recorded above.
-
-- **Chapter 20 lazy memory and register allocation.** Baseline: 380 tests plus
- the fuzzer wrapper. Final: **396 tests plus the fuzzer wrapper**, with the
- 15 forwarded regressions and one allocation regression spanning all three
- targets. **9,800 scheduled runtime evaluations** pass across 100 optimizer
- seeds. **42 selected/scheduled/allocated graphs** pass legality checks at fixed
- seed 123 on x86-64, RISC-V, and ARM. Make release and spill-stats pass.
- The unchanged 39-entry spill cohort changes from **238 / 357** retained /
- weighted moves to **234 / 360**. The x86 array prefix sum changes 5 / 5 to
- 6 / 13 because selected-read ordering adds a loop move; a scratch ablation
- omitting that ordering returns to 5 / 5. No allocator heuristics or benchmark
- membership changed. The chapter README records all six changed program rows.
- Logs: `build/memory20-baseline.log`, `build/memory20-final.log`,
- `build/memory20-seeds.log`, `build/memory20-machine.log`,
- `build/memory20-spills-baseline.log`, `build/memory20-spills-final.log`,
- `build/memory20-spills-ablation.log`, and `build/memory20-release.log`.
- This checkpoint was reviewed and committed as `f01ee052`.
-
-
-- **Chapter 19 lazy memory and instruction selection.** Baseline: 360 tests
- plus the fuzzer wrapper. Final: **375 tests plus the fuzzer wrapper**; release
- jar builds with Make alone. Forwarded 13 memory regressions and added two
- instruction-selection checks: alias/Phi preservation and allocation/call slots
- on both targets, and folded x86 reads ordered before clobbering Stores.
- **9,800 scheduled evaluations** and **2,800 selected/scheduled graphs** pass
- across 100 optimizer seeds, with x86_64_v2 and riscv selection exercised.
- The latter checks graph scheduling and shape; this chapter does not execute
- encoded machine code. Logs: `build/memory19-baseline.log`,
- `build/memory19-final.log`, `build/memory19-seeds.log`,
- `build/memory19-machine-seeds.log`, and `build/memory19-release.log`.
- Chapter 14's inherited And graph factors here without widening; its runtime
- assertion remains. This checkpoint was reviewed and committed as `612555ee`.
-
-
-- **Lazy memory through 17 and 18.** Baselines: 17 has 288 tests; 18 has 318
- plus its fuzzer wrapper. The port passes **298 tests in 17** and **331 tests
- plus the fuzzer wrapper in 18**. Both carry the nine memory regressions and
- constructor regression; 18 adds checks for non-inlined calls, recursive heap
- updates, and allocation inside an inlined function. The non-inlined check
- also verifies Call's input count and last-slot function pointer after GCM.
- **7,700 / 9,800 evaluations across 100 optimizer seeds** pass in 17 / 18,
- checking final memory values, complete scheduling, partial New inputs, and
- two-result New. Full suites and both release jars build with Make alone.
- Logs: `build/memory17-18-baseline.log`, `build/memory17-18-final.log`,
- `build/memory17-seeds.log`, and `build/memory18-seeds.log`.
- Chapter 17's And graph remains unfactored when the result would widen; the
- existing runtime assertion is unchanged. Shared allocation memory retains
- the `.y` Load in its inherited array example, as in 16. Nullable diagnostics
- select the earlier array dereference in 17/18. The Chapter 17 null test now
- makes `p2` mutable so its setup does not fail first on an unrelated final-field
- store. This checkpoint was reviewed and committed as `2d51b647`.
-
-- **Chapter 16 lazy memory and constructors.** Baseline: 237 tests. The port
- passes 247 tests, including nine carried memory regressions and one constructor
- case combining reads, writes, a loop, and an unrelated alias. Full Make tests
- and release builds for 10a-16 pass: **1,568 tests across eight snapshots**.
- An additional **7,700 evaluations across 100 optimizer seeds** check memory
- behavior, complete scheduling, two-result New, and partial allocation inputs.
- Logs: `build/memory16-baseline.log`, `build/memory16-all-tests.log`, and
- `build/memory16-seeds.log`.
- The changed graph order exposed a dependency notification gap in
- `Chapter14Test.testCloneAnd`: replacing Minus#25 rewired And#28 without waking
- its dependent Phi#26. `Node.subsume` now wakes each rewired user's recorded
- dependents in 10a-16. The existing test passes without weakening its assertions.
- Chapters 17+ are unchanged; constructor integration is the review boundary.
-
-- **Generalized Phi factoring, 10a through 15.** Replaced binary-only factoring
- and the blanket memory exclusion with operand-wise Phis and exact-class
- `copyEmpty`. Matching Loads and sole-use Stores are eligible when unbound to
- control; operation attributes, field/alias identity, operand types, and
- result-type monotonicity are preserved. Load safety examines memory users
- before explicit GCM anti-dependencies exist. The final guard checks only
- immediate users, stopping at Stores, Phis, MemMerges, and New in 15. Memory factoring waits for bulk
- partitioning at its Region; MemMerge and BulkMemPhi are never factored.
- Stores bound to control in 11-14 remain excluded. User removal now wakes
- recorded dependents, and empty-diamond folding observes projection rewiring.
- Three regressions live in each snapshot's `Chapter10Test`; the original 10a
- Phi fails both positive folding checks, and disabling the Load safety guard
- exposes an invalid read/write schedule in the negative probe.
- Full Make tests pass **159 / 162 / 177 / 181 / 196 / 216 / 230** and all seven
- release jars build. A 10a Phi label and one 14 Region ID were refreshed;
- expression/value expectations remain unchanged. Each snapshot also passes
- **3,500 evaluations across 100 optimizer seeds**, and 15 passes another
- **7,000 allocation/memory evaluations**. Logs: `build/phi-drop-baseline.log`,
- `build/phi-drop-tests.log`, `build/phi-drop-seeds*.log`,
- `build/phi-drop-negative.log`, `build/phi-drop-antidep-negative.log`, and
- `build/phi-drop-allocation-seeds15.log`. Chapters 16+ are unchanged.
- The subsequent guard refactor moves memory eligibility into virtual
- `MemOpNode.canDrop` overrides and clobber inspection into Load. Removed 10a's
- unused visited set. All 1,321 tests and seven release builds passed
- (`build/phi-guards-refactor.log`). At Cliff's request, the subsequent
- simplification also removes recursive aggregate traversal from 10b-15:
- a one-step check conservatively stops at possible indirect clobbers,
- keeping the teaching cost proportionate to saving one Load. The aggregate
- example now keeps two Loads in 10b-15; its runtime checks remain unchanged.
- All 1,321 tests and seven release builds pass with the simpler guard
- (`build/phi-guards-one-step.log`).
-
-- **Chapter 15 partial allocation memory (review checkpoint).** Implemented the
- approved two-result New and carried the lazy memory model through parser,
- scheduling, evaluator, viewer roles, and chapter documentation. Full Make
- suite passes **227 tests** and the release jar builds, up from baseline **218**: six carried memory
- regressions plus three allocation regressions. They cover unrelated aliases,
- mixed integer/float/pointer contents, reads across allocation, and allocation
- inside loops. A disposable probe also passed **7,000 evaluations across 100
- optimizer seeds**, including empty structs, arrays, both slot conventions,
- partial coverage, and complete scheduling of live definitions. Region/Loop
- golden changes only alter IDs; the nullable-access diagnostic matches 13-14.
- Logs: `build/memory15-baseline.log`, `build/memory-forward-15.log`, and
- `build/memory15-seeds.log`; final tests and release: `build/memory15-final.log`.
-
-- **Memory forward port through Chapters 12-14.** Preserved
- floating-point operations, typed reference initialization, and narrow stores
- while forwarding the reviewed parser, memory nodes, GCM, Return slots, and
- evaluator. Each chapter inherits all six memory regressions. Make tests pass
- **178 / 193 / 213**, up from baselines **172 / 187 / 207**; all three release
- jars build. Updated printed Region/Loop IDs without changing expression
- expectations. The invalid `head.next.i` test in 13 and 14 now reports the
- possibly-null `i` access first; both accesses remain invalid, and the test
- still requires rejection. Logs: `build/memory-forward-baselines.log` and
- `build/memory-forward-12-14.log`. The subsequent allocation port is recorded above.
-
-- **Control/memory slot convention (10a, 10b, 11).** Return now takes
- `{ctrl, $mem, result}` and describes its result tuple in that order, with
- whole memory typed as `TypeMem.BOT`. Evaluators use `expr()` rather than a
- literal result index. Updated viewer roles, existing projection checks,
- documentation, and numbered diagram edges. Make tests pass **156 / 159 / 174**
- respectively, and all three release jars build. Log:
- `build/return-memory-slots.log`. Preserve control slot 0 and memory slot 1
- when forwarding Call, CallEnd, and other nodes in later chapters.
-
-- **Chapter 11 memory forward port (2026-09-29, review checkpoint).** Parser,
- Start, Scope, and Return now use 10b's one-memory model and its three memory
- nodes. GCM schedules MemMerge as a dependency node, filters both Load
- readiness and anti-dependencies by alias, and retains the full Store placement
- range needed by the evaluator. Synthetic never-taken loop returns remain
- scope-free. Updated the evaluator, viewer Return edge roles, walkthrough
- diagrams, and scheduling code excerpts. The baseline passed **168 tests**;
- the port passes **174**, including all six forwarded memory regressions.
- Fifty existing printed expectations changed only Region/Loop node IDs.
- Five memory programs pass seven inputs across 100 optimizer seeds, with
- scheduling assertions and final heap/value checks (**3,500 evaluations**).
- Make also builds the release jar and regenerates all six updated walkthrough
- diagrams; `git diff --check` passes.
- Logs: `build/memory-forward-11-baseline.log`, `build/memory-forward-11.log`.
- Chapters 12-24 are untouched pending review.
-
-- **Chapter 10a/10b split (2026-09-29).** Replaced `chapter10` with two
- independently buildable snapshots: whole-memory SSA in 10a, then lazy
- `MemMerge`/`MemPhi`/`BulkMemPhi` partitioning in 10b. Updated Make discovery,
- Maven modules, IDEA descriptors/local module list, chapter navigation,
- linear-history link handling, and regenerated the memory diagrams.
- Fresh Make builds pass **156 tests in 10a and 159 in 10b**, including the
- chapter-local fuzzer regression wrapper. Both release jars and tags build.
- Offline Maven tests pass in both modules. Five memory programs each pass
- seven inputs across 100 optimizer seeds in each compiler (**3,500 executions
- per chapter**). Seed 97's missed bulk-Phi revisit is retained as a regression.
- Logs: `build/chapter10-clean-build.log`, `build/chapter10-maven.log`,
- `build/10a-seeds.log`, and `build/10b-seeds.log`. At this checkpoint Chapters
- 11-24 had not yet adopted the new memory representation; forward-port progress
- is recorded above.
-
-This is a condensed completion record, not a list of current suite counts for
-old revisions. Earlier detailed traces are in Git history; durable invariants
-have been consolidated into the [AI notes](../skills/chapter25-codex-notes.md).
-Unresolved reproductions have been promoted to the pending queue above.
-
-On 2026-09-28, the constructor/memory-check backport passed the full Make suites
-in every compiler snapshot from 10 through 24, including their default fuzzer
-wrappers (`build/error-backport-tests.log`). The destination baseline was also
-green (`build/error-backport-baseline.log`). The arithmetic contract experiment
-and its separate validation are recorded independently.
-
-| Completed work | Scope and evidence retained |
-|---|---|
-| Narrow C integer returns | On 2026-09-28, instruction selection in 22-25 sign/zero-extends direct C call results according to the declared 8/16/32-bit return type. x86 uses MOVSX/MOVZX/MOVSXD or a 32-bit MOV; ARM uses SBFM/UBFM; RISC-V uses shifts, including zero-extension of its sign-extended u32 ABI result. Chapter 25 consults external function identities because optimization can replace ExternNode with an ordinary constant. Native regressions run both x86 calling conventions with garbage upper return bits; ARM/RISC-V stubs exercise signed/unsigned boundaries and unchanged i64 results. Chapter 25's errno test now also requires `close(-1)==-1`. Original selectors reproduce failures in 22 and 25, including the libc case (`build/c-return-negative-{22,25}.log`). Full Make suites passed: 22 (433), 23 (455), 24 (485), 25 (479), plus each default fuzzer wrapper; 25 rebuilt sys.o (`build/c-return-final2.log`). |
-| ARM bitfield emulation | SBFM/UBFM decoding now handles both immediate fields, covering signed/unsigned extracts as well as ASR/LSR/LSL. The previous emulator treated all SBFM as ASR and all UBFM as LSL, hiding a logical-right-shift error and rejecting the new extension results. Introduced the correction and independent instruction-word regression in 21 and forwarded through 25. Full 21 suite passed (412 tests plus fuzzer; baseline 411); 22-25 validation is included above. |
-| FunPtr lifetime and callable entries | On 2026-09-28, introduced Return-linked FunPtrNode in 22-24, including semantic constant folding. Direct call targets retain existing call-graph treatment; all other address uses conservatively retain unknown callers, without escape analysis. Stop retains each callable Return through instruction selection; machine operands omit the lifetime edge. Anonymous/library returns failed relocation on all three targets before the change; 24 also failed named returns. New tests call returned anonymous/named/library pointers on ARM/RISC-V and from native C, and check discarded anonymous bodies disappear. Full Make suites passed: 21 (411), 22 (430), 23 (452), 24 (482), each plus its fuzzer wrapper. Baselines passed at 410/427/449/479. Chapter 24's Phi-selected functions retain general parameter types; graph expectations reflect that conservative rule. |
-| Function-address backend and worklist corrections | RISC-V TFPRISC now patches ADDI at opStart+4, rounds AUIPC for signed low bits, and reports its actual eight-byte size (21-24). ELF export emits anonymous bodies without exported names (21-24). Actual invocation exposed both bugs after retention was repaired; Chapter 21's reduced conditional-return probe trapped before the encoding fix. Chapter 22 gains the later inlining dominator null guard. In 24, killing Call#618 reduced FunPtr#540 to one use without waking dependent CallEnd#560; Node.kill now wakes the remaining definition's dependencies after removing a use (24-25). The no-return String workload loses unused hash bodies; its spill expectations become zero on all targets. Chapter 24 spill-stats passes all 212 original compilation entries at 1,351 moves / 3,115 weighted moves; its README separates this measurement from the original allocator ablation. Full Chapter 25 Make validation also passed: 476 ordinary tests plus its 17-seed fuzzer wrapper, with sys.o rebuilt. |
-| Chapter 25 direct C data bindings | On 2026-09-28, full `make tests` passed after the Field/ExternOffset refactor: 476 ordinary tests plus the fuzzer wrapper (17 seeds). Coverage checks zero layout space for external fields, native shared integer/float storage across C calls, compound updates, source/precompiled imports sharing aliases and preserving storage metadata, ARM/RISC-V emulated address relocation and signed loads, and the errno accessor after a failed native call. Direct bindings remain in 25: 22-24 have different singleton-pointer constant and instruction-selection contracts. Serialization retains magic C0DE; the changed layout requires rebuilding all objects. Only the independent RISC-V fixes below were selected for backport. |
-| RISC-V right shifts and pointer relocation | Backported from 25 to 22-24 on 2026-09-28. EvalRisc5 selects arithmetic/logical right shifts correctly; TMPRISC rounds the AUIPC high part to account for signed ADDI low bits. New regressions cover immediate/register shifts and forward/backward relocation boundaries. Both fail before the Chapter 22 fixes (arithmetic shift becomes logical; delta 0x800 lands 4 KB low). Full Make suites passed: 22 (427), 23 (449), 24 (479), each plus its fuzzer wrapper. Baseline suites also passed (425/447/477). |
-| Chapter 25 Load fuzzer recheck | At `8212a66f`, seed `-4628356252269023530` passes its direct fuzzPeepsRegression replay (`build/load-seed-recheck.log`). The complete `make fuzzer` wrapper also passes all 17 seeds on 2026-09-28 (one JUnit wrapper test). The seed remains a regression; OPEN_FAILING_SEEDS is empty. No new compiler fix was needed. The older Load TODO report is superseded. |
-| Arithmetic high results at the SCCP boundary | Chapter 24 integer and float arithmetic now return global TOP for high operands, matching 25. Widened ArithNode.compute's Java return signature to Type; no caller requires TypeInteger. Retained the typed integer fallback for unresolved/error operands, for the parser dependency in the pending queue. The TOP change starts with SCCP in 24; earlier snapshots retain their typed contracts. Full Chapter 24 Make suite passed: 477 tests plus its fuzzer wrapper (`build/arithmetic-top-full-24.log`). |
-| Required-field and memory-check backport | Chapters 16-24 retain allocation-time checks for inline `new S { ... }` initialization. Chapter 17 had its TOP/unset-field check commented out; restored it. Expanded the existing missing-initializer regression through 24 (introduced its array-field cases in 16), and forwarded the known-null load regression to 10-24 using their existing parse-time diagnostic. Added the fresh-allocation zero-fold monotonicity assertion in 19-24 and the TOP-pointer store-diagnostic guard in 18-23 (already present in 24). Before 18, TOP pointers already produce a diagnostic before the store's pointer cast. Chapter 25's constructor-exit snapshots and implicit-constructor declaration check stay with its separate allocation/constructor model. Its CallEnd return and MemMerge load-dependency corrections already have equivalent behavior in the applicable earlier implementations; storage-type nullability machinery stays with 25. Global error-result type changes are paused in the queue above. |
-| Chapter 25 required constructor fields | Parser records each explicit constructor's merged exit field types by Var identity and checks them against the complete field list when the struct closes. With no user constructor, declaration defaults are checked as an implicit empty constructor. Never-returning constructors have no completion obligation; nested blocks retain constructor context. Declaration-body assignments to required fields now emit their initializing stores. Load's fresh-allocation zero fold is restored with a monotonicity guard. Existing constructor tests cover partial/early returns, shadowing, later fields, valid defaults and non-returning constructors; older unused-struct/String fixtures now provide constructors, with measured spill expectations updated. Seed `6506797708065910879` is rejected during parsing and passes its regression. On 2026-09-28, all 472 non-fuzzer tests passed across `build/ctor-check-tests.log` and `build/ctor-remaining-tests.log`; the full Make run remains red solely on the independent Load TODO seed above. Allocation-time initialization checking was lost in the August 10 rewrite (`51f1f8f4`); this restores the obligation in the parser without depending on optimized private memory. |
-| Chapter 25 null-dereference diagnostic | Existing `Chapter10Test.testNullGuardErrors` includes `if (p == null) return p.x; return -1;`. The earlier recursive constant-fold error guard was removed by Cliff in favor of erroneous nodes computing BOTTOM, with related eager-folding and monotonicity corrections. The earlier full-suite pass in `build/null-deref-arith-top.log` applies to the superseded guard implementation, not the current edits. The exposed uninitialized-field failure is fixed by the constructor checks recorded above. |
-| Chapter 22 String without an explicit return | Backported 23's default-main teardown: when top-level control falls through, replace main's Start input with XCTRL and queue its users. Previously 22 omitted the return while retaining live control, leaving loop fragments with missing exits. Current seed 0 reproduced a missing-successor failure in Encoding; the earlier GCM changes had moved it past the originally recorded failure. The unchanged `Chapter20Test.testString` now also checks seed 0 through Encoding on x86/RISC-V/ARM, preserving its existing seed-123 allocator checks. Two Chapter09Test graph expectations now reflect removal of a default main with no live return; their sources are unchanged. Baseline and final full Chapter 22 suites passed (425 tests plus fuzzer), logged in `build/string-baseline.log` and `build/string-final.log`. Chapters 23-24 already contain this teardown; no later compiler changes were needed. |
-| Infinite-loop evaluation | Eval2 now runs the missing loop-tree phase in 21 and explicitly follows NeverNode projection 0 in 21-25. Existing `testFcn9` expects timeout in 21-24. Full 18-25 Make suites passed on 2026-09-26 (`build/never-exit-tests.log` and `build/never-exit-final.log`). On 2026-09-27, Cliff requested removal of the frozen-source fixture, replay tests, related reductions, and seed-list entry as unnecessary test overhead; the evaluator fixes remain. |
-| Always-on peepholes | Removed the global disable flag and branches in 2-18, the Chapter 18 parse overload, test toggles, and transpiler metadata/Rust output. Updated early graph expectations and READMEs; removed duplicate no-peephole scope tests. Chapter 8 fuzzing checks compilation/evaluation for failures; 9-18 compare worklist seeds 123/456 like later chapters. The Chapter 18 Load type workaround was unnecessary with normal forwarding and was removed. All 1-25 default Make suites passed across `build/always-peeps-tests.log` and `build/always-peeps-final.log`; later evaluator corrections are recorded above. The baseline was green in `build/always-peeps-baseline.log`. Direct checks exercised both worklist seeds in 9-18. The transpiler compiles and its focused grammar/Rust-output check passes; its existing repository-wide test stops at source-free chapter00 with `no source files`. |
-| B10 and integer/constant-array widening | Widening starts in 24. Both 24-25 now use levels 0-3, dual widening `3-widen`, retained singleton widening, and widening on TypeConAry. `nonZero` preserves the coordinate. The same TypeTest regression and integer gather ranges run in both snapshots; before the backport, Chapter 24 fails the widening regression and lattice associativity. Loop Phis widen only below-center nonconstant ranges: widening an above-center range to level 1 then falling to zero at level 0 violated SCCP monotonicity in 24's existing `testCloneAnd` and `testInfinite`; both now pass, with the guard also in 25. |
-| TypeConAry API backport and validation | 24-25 share the reduced raw `_make(any,widen)` API, integer meet dispatch through the distinct `TCONARY` tag, and gathered samples. Preserve 24's GLB hooks and ordinary byte stream; scalar envelopes and serialization stay in 25. Chapters 22-23 consolidate gather and remove redundant wrappers/arguments, retaining their pre-widening representations and needed sentinels. Full Make suites passed on 2026-09-26: 22 (428), 23 (450), 24 (480), 25 (474), each plus its fuzzer wrapper; all include TypeTest, and 25 rebuilt `sys.o`. Logs: `build/conary-backport-*` and `chapter25/build/conary-backport-25.log`. |
-| #254 subtraction zero identities | Require integer operands in 4-25 and resolved integer mode in 25. Preserve floating-point signed zero and subtraction diagnostics. `Chapter12Test.testSubZeroFloat` runs in 12-25; `Chapter22Test.testSubZeroTypeError` runs in 22-25. Against the original PR, the signed-zero test fails in 25 and the diagnostic test fails in 22-25. Full Make suites passed in every chapter 4-25 on 2026-09-25, including fresh `sys.o` compilation in 25. |
-| Short type extrema | Memory, struct and memory-pointer BOT/TOP names from 10; function-pointer names from 18. Exact types use shortcuts in ordinary, nested and HTML prints; precise types retain their details. Full Make suites in 10-20 and type/frontend suites in 21-25 passed; canonical/nested print checks passed in 10-25. |
-| Integer result types | Integer-op fallbacks in 4-13 and remaining unary cases through 17 retain integer types; 5's Phis meet their data-input types. Full Make suites in 4-17 passed, plus a chapter 5 snapshot check of Phi/Add types. Integer BOT starts in 4; later arithmetic already has typed fallbacks, with unresolved bimorphic modes preserved in 25. |
-| B14 cyclic leaf-kind equality | 23-24 backport; 25 already correct. Cyclic equality starts in 23. `TypeTest.testCyclicLeafKinds` fails before the fix; earlier interned-child equality already checks kinds. Full affected suites passed. |
-| B13 / #246 null guards | Nested Not/null guard discovery from 10; short-circuit Phi availability in 23-24. Regression and full suites in 10-25 passed. Separate Chapter 25 null-dereference diagnostic is recorded above. |
-| B09 and #251 debug printing | Side-effect-free print/accessor paths from their first applicable chapters; `_` diagnostic naming, type interning allowed, layouts forbidden. Dead nodes use `isDead()`, not null `_inputs`, from 7. Original-printer negative checks and full affected suites passed. |
-| #247 emulator stores | Full eight-byte writes in ARM/RISC-V emulators, 21-25. Independent byte-pattern/overwrite regressions failed before the fix; all affected suites passed. |
-| B12 x86 immediate multiply | Dedicated destination-register encoder in 21; 22-25 already had it. Low/extended-register encoding regressions and full 21-25 suites passed. |
-| B11 optimized return checking | Remove parse-time meet in 18-24; 18 checks the optimized result type. Earlier separate Returns already accept dead mixed-type exits. Regressions start in 10/12; full 10-25 suites passed. |
-| Dominator caches | Shared searches from 6; char depth in 6-17, separate char depth/version from 18, checked overflow and preserved clone fields. Inlining invalidation in 18-20. Full affected suites passed; dedicated bookkeeping helpers/tests were removed at Cliff's request. |
-| GCM, constant cloning, isPinned removal | Shared scheduling from 11, per-function cloning of stacked constants from 18; preserve chapter-specific anti-dependencies and linked Parm ownership. Full 11-25 suites passed. Keep clone-based copyEmpty and non-final graph fields per review. |
-| TypeFunPtr normalization | Trailing-default normalization in 23-24; 25 already correct. Only gather cases added; lattice laws and full 23-25 suites passed. Other `7f3b8856` representations remain in 25. |
-| Inlining Return types and ARM execution | Live return expressions outlast a deleted inline entry (20-25); ARM register SUB, call instructions, frame byte counts/alignment, and vector growth (21-25 as applicable). Reduced negative cases and full affected suites passed. The earlier standalone ARM String/hash failure is resolved. |
-| Native test reliability | Full process exit status, stdout plus successful execution, and distinct concurrent HelloWorld artifact names. Crashes no longer pass merely because output matches or an exit status narrows to zero. |
-| Allocator progression 20-25 | Fixed cohorts, legality checks, native/emulated execution from 21, and same-compiler comparisons. Final results below. |
-| Build/fuzzer harness baseline | All 25 chapter targets passed after build setup and explicit seed lists. Empty default lists were not exploratory fuzz coverage. |
+Introduce an independent fix in the earliest applicable chapter and propagate
+it through every affected snapshot. For correctness fixes, first reproduce the
+failure in the destination compiler. Preserve each chapter's representation
+and tutorial boundary.
-Final allocator audit, 2026-09-20, Windows/Cygwin with assertions:
+Run focused checks and the existing full Make suites for the affected compiler
+directories, for example:
-| Snapshot | Full suite | Spill report |
-|---|---|---|
-| 20 | 378 tests + fuzzer | 39 compilations, 238 / 357 |
-| 21 | 408 tests + fuzzer | 91 compilations, 840 / 1,456 |
-| 22 | 422 tests + fuzzer | 115 compilations, 838 / 1,496 |
-| 23 | 444 tests + fuzzer | 145 compilations, 933 / 1,745 |
-| 24 | 473 tests + fuzzer | 212 compilations, 1,332 / 2,956 |
-| 25 | 468 tests across parallel groups, including fuzzer | 222 compilations, 2,597 / 5,579 |
+```sh
+make -k tests CHAPTERS="chapter20 chapter21"
+```
-All full suites and final spill reports passed. The Chapter 25 run rebuilt
-`sys.o`. Negative checks against saved original classes reproduce fixed-neighbor
-bias, narrow-store masks, and ARM floating-memory encoding failures. The original
-Newton ARM allocation also failed before the fix. The Chapter 21 coalescing-off
-comparison passes all allocation/runtime checks but reports nine changed spill
-goldens, intentionally exiting nonzero. The Chapter 25 earlier-ranking ablation
-fails fresh library allocation, so its client-only totals are explicitly partial.
-Logs and saved original classes: `chapter{20..25}/build/regalloc-final/`.
+Chapter 25's inherited tests do not validate earlier compiler implementations.
+Force rebuilds when older Makefiles miss dependencies, and rebuild serialized
+libraries when their compiler or object format changes. Keep execution and
+register-legality checks independent of spill expectations. Preserve unrelated
+edits, keep text LF-only, and run `git diff --check`.
diff --git a/isa/src/main/java/com/seaofnodes/isa/Arm64.java b/isa/src/main/java/com/seaofnodes/isa/Arm64.java
index ce7f7b11..5edb2f3c 100644
--- a/isa/src/main/java/com/seaofnodes/isa/Arm64.java
+++ b/isa/src/main/java/com/seaofnodes/isa/Arm64.java
@@ -4,6 +4,12 @@ package com.seaofnodes.isa;
public final class Arm64 {
private Arm64() {}
+ // MRS Xt,NZCV / MSR NZCV,Xt. Register 31 denotes XZR.
+ public static int flags(int reg, boolean toFlags) {
+ assert 0<=reg && reg<32;
+ return (toFlags ? 0xD51B4200 : 0xD53B4200) | reg;
+ }
+
public static final int OP_ADD = 0b10_001_011;
public static final int OPF_ADD = 0b00_011_110;
public static final int OPF_OP_ADD = 0b00_101_0;
diff --git a/isa/src/main/java/com/seaofnodes/isa/X86.java b/isa/src/main/java/com/seaofnodes/isa/X86.java
index 78fa3da5..d5150a31 100644
--- a/isa/src/main/java/com/seaofnodes/isa/X86.java
+++ b/isa/src/main/java/com/seaofnodes/isa/X86.java
@@ -162,11 +162,18 @@ public final class X86 {
}
}
public static void flags(CodeSink out, int reg, boolean toFlags) {
+ assert 0<=reg && reg<16 && reg!=4;
if( !toFlags ) out.add1(0x9C); // PUSHF
rexF(0,reg,-1,false,out);
out.add1((toFlags ? 0x50 : 0x58)+(reg&7));
if( toFlags ) out.add1(0x9D); // POPF
}
+ public static void flagsStack(CodeSink out, int offset, boolean toFlags) {
+ if( !toFlags ) out.add1(0x9C); // PUSHFQ
+ // POP's destination uses the restored RSP, so offset needs no adjustment.
+ memory(out,0,toFlags ? 0xFF : 0x8F,false,toFlags ? 6 : 0,4,-1,offset,0);
+ if( toFlags ) out.add1(0x9D); // POPFQ
+ }
public static void stackCopy(CodeSink out, int srcOffset, int dstOffset) {
memory(out,0,0xFF,false,6,4,-1,srcOffset,0); // PUSH [rsp+srcOffset]
memory(out,0,0x8F,false,0,4,-1,dstOffset,0); // POP uses the restored rsp.
diff --git a/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java b/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java
index 1ff77f59..d4a54719 100644
--- a/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java
+++ b/isa/src/test-support/java/com/seaofnodes/isa/eval/EvalArm64.java
@@ -597,6 +597,22 @@ public class EvalArm64 {
break;
}
+ case 0xD5: {
+ if( (ir & ~31)==0xD53B4200 ) { // MRS Xt,NZCV
+ rval = (N ? 1L<<31 : 0) | (Z ? 1L<<30 : 0) |
+ (C ? 1L<<29 : 0) | (V ? 1L<<28 : 0);
+ if( rdid==31 ) rdid=-1; // XZR, not SP.
+ } else if( (ir & ~31)==0xD51B4200 ) { // MSR NZCV,Xt
+ long value = rdid==31 ? 0 : regs[rdid];
+ N = (value & (1L<<31))!=0;
+ Z = (value & (1L<<30))!=0;
+ C = (value & (1L<<29))!=0;
+ V = (value & (1L<<28))!=0;
+ rdid=-1;
+ } else throw new UnsupportedOperationException("Unknown system instruction");
+ break;
+ }
+
case 0xD6: {
int op = ir & 0xFFFFFC1F;
int rn = (ir >>> 5) & 31;
diff --git a/isa/src/test/java/com/seaofnodes/isa/RiscTest.java b/isa/src/test/java/com/seaofnodes/isa/RiscTest.java
index d10f97a4..5b354036 100644
--- a/isa/src/test/java/com/seaofnodes/isa/RiscTest.java
+++ b/isa/src/test/java/com/seaofnodes/isa/RiscTest.java
@@ -9,6 +9,22 @@ public class RiscTest {
assert expected==actual : String.format("Expected %08x, got %08x",expected,actual);
}
public static void main(String[] args) {
+ word(0xD53B4209,Arm64.flags(9,false)); // MRS x9,NZCV
+ word(0xD51B421A,Arm64.flags(26,true)); // MSR NZCV,x26
+ for( int flags=0; flags<16; flags++ ) {
+ var a=new EvalArm64(new byte[1024],512);
+ a.st4(4,0xD51B4209); // MSR NZCV,x9
+ a.st4(8,0xD53B421A); // MRS x26,NZCV
+ a.st4(12,0xD53B421F); // MRS xzr,NZCV
+ a.st4(16,0xD51B421F); // MSR NZCV,xzr
+ a._pc=4; a.regs[9]=((long)flags<<28) | 0xFFF;
+ assert a.step(3)==0;
+ assert a.regs[26]==(long)flags<<28 && a.regs[31]==512;
+ assert a.N==((flags&8)!=0) && a.Z==((flags&4)!=0);
+ assert a.C==((flags&2)!=0) && a.V==((flags&1)!=0);
+ assert a.step(1)==0;
+ assert !a.N && !a.Z && !a.C && !a.V && a.regs[31]==512;
+ }
// Instructions encountered by dynamic allocation measurements.
for( long[] pair : new long[][]{{17,3},{-17,3},{17,0},{Long.MIN_VALUE,-1}} ) {
var a=new EvalArm64(new byte[1024],512);
diff --git a/isa/src/test/java/com/seaofnodes/isa/X86Test.java b/isa/src/test/java/com/seaofnodes/isa/X86Test.java
index 8af84ae8..4d3f1433 100644
--- a/isa/src/test/java/com/seaofnodes/isa/X86Test.java
+++ b/isa/src/test/java/com/seaofnodes/isa/X86Test.java
@@ -102,6 +102,10 @@ public class X86Test {
check("41ffd2", b -> X86.callRegister(b,10));
check("41519d", b -> X86.flags(b,9,true));
check("9c4159", b -> X86.flags(b,9,false));
+ check("9c8f442418", b -> X86.flagsStack(b,24,false));
+ check("ff7424189d", b -> X86.flagsStack(b,24,true));
+ check("9c8f842480000000", b -> X86.flagsStack(b,128,false));
+ check("ffb424800000009d", b -> X86.flagsStack(b,128,true));
check("ff7424088f442410", b -> X86.stackCopy(b,8,16));
check("4d8bca", b -> X86.move(b,false,false,9,10));
check("4d0f28ca", b -> X86.move(b,true,true,9,10));
diff --git a/skills/chapter25-codex-notes.md b/skills/chapter25-codex-notes.md
index 878f231e..1801ffed 100644
--- a/skills/chapter25-codex-notes.md
+++ b/skills/chapter25-codex-notes.md
@@ -2,31 +2,13 @@
This file is intentionally AI-facing. It records durable invariants, debugging
habits, and project-owner preferences that are easy to miss when reading only
-the implementation. Use the current checkpoint below rather than old session
-handoffs or historical branch/status reports.
-For cross-chapter work, read [the backport queue and validation record](../docs/chapter-backports.md).
-It owns pending reproductions and a condensed completion record; these notes
-capture reusable lessons. Detailed superseded validation history remains in Git.
+the implementation. Check current code and Git state rather than treating old
+session handoffs as live status.
+For cross-chapter work, read [the backport queue](../docs/chapter-backports.md).
+It owns pending work; these notes capture reusable lessons. Completed work and
+superseded validation history remain in Git.
For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.md).
-## Reset checkpoint (2026-10-03)
-
-- Cliff committed the chapter reorganization and mutability split as `22df8918`
- (`Chapter renumber, and split ch 17`). Implementation and validation are done;
- there is no unfinished compiler port or hung validation process to resume.
- Check current Git state before treating this checkpoint as live status.
-- Cliff subsequently committed the Chapter 18 README review and backup-file
- deletion as `3c13b271` (`Update README.md`). The tree was clean before this
- memory refresh; only these two notes files are left modified by the refresh.
- Do not commit or push without a new instruction.
-- Read the mutability section and validation record in
- [chapter-backports.md](../docs/chapter-backports.md) for scope and results.
- Full affected chapter suites, Chapter 25 native tests, standalone 17a/17b
- builds, and exporter checks passed. Logs are disposable under
- `build/mutability/`; no need to repeat suites without a new change or concern.
-- Next work is Cliff's review, not another inferred cleanup task. The separate
- pending queue still includes float-to-int syntax and deferred BOTTOM contracts.
-
## Collaboration preferences
- For densely keyed integer maps, use indexed `Ary<T>` storage rather than
@@ -74,14 +56,62 @@ 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
+- Native boundary decision (2026-10-18): no classes for native code; do not
+ design a native-startup policy. Ordinary supported FFI calls are separate.
+ Chapter 25's README owns the tutorial explanation of class initialization;
+ ROADMAP owns future work. Do not recreate an initialization progress document.
+- GCM memory-Phi anti-dependencies constrain paths whose input differs from
+ the load's memory, not paths carrying that memory unchanged. Bound the idom
+ walk by the memory definition's depth: an alternate Phi input need not be
+ dominated by that definition. Also stop at `defblk.idom()`: when the memory
+ definition is the function entry, its null idom is the valid sentinel.
+ Include the boundary depth; a strict `>` skips required same-block edges
+ and can make a read observe a later store. Keep `testCapitalize`,
+ `testConditionalClassInitX86`, and
+ `testReadBeforeConditionalStore` passing; dropping Phi handling entirely
+ miscompiles the latter (140 instead of 120).
+ This rule applies from Chapter 13 onward. The nested-branch regression
+ `SchedulerTest.testMemoryPhiAcrossSiblingBranch` checks the disjoint walk;
+ removing the depth bound reproduces a null-stblk crash. Retain each early
+ chapter's evaluator scheduling rules when porting the boundary.
+- CLInit lowering (2026-10-17): TypeCheck queues surviving markers; optional
+ Serialize saves opaque ideal IR, then Lower runs Iter before LoopTree.
+ Serialization includes disconnected Returns and filters cross-object links
+ without mutating the live graph. Parent initialization precedes the child's
+ flag test/call; the true flag store remains inside the initializer. Runtime
+ flag reads follow ordinary Load idealization, including removing control.
+ Handle x86 condition-flag lifetime constraints in the backend, not by pinning
+ ideal Loads. Generated calls remain unlinked and
+ conservative; do not reopen parameter flow into SCCP-optimized bodies.
+ Private/imported initializer bodies retain Start because opaque CLInit has no
+ ordinary Call edge yet; this does not change public reachability.
+ The selected entry gets the external arg; other clinits get zero for now.
+ The planned array ABI uses non-null entry args and null elsewhere; not yet
+ implemented. Eval2 shares singleton class objects and retains imported bodies.
+ Full Chapter 25 Make validation: 509 tests, including native cold/hot calls,
+ parent order, ARM/RISC-V encoding and nonreturning object reload.
+- Chapter 25 phase structure: Opto 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.
+ the sequence for later targets. Iter setup, FinalizeIR, TypeCheck, Unlink,
+ and InstSelect own their algorithms in separate files. CodeGen's phase wrapper
+ handles assertions, timing (including failures), and successful viewer phase
+ notifications. Its driver lists the order and skips completed steps.
+ Serialize decides whether ideal IR is needed: explicit Serialize or file
+ Export, not Encoding or in-memory linking. A staged file export must request
+ Serialize before Lower; ideal IR cannot be recovered from machine nodes.
+ Unlink detaches imports for machine selection, including requests stopping at
+ Select, while ideal evaluation retains them. Schedule requires Unlink or Select;
+ use the driver instead of skipping Lower/LoopTree in hand-written test chains.
+ Earlier chapters expose only phases with real work. CodeGen and its resumable
+ driver start in Chapter 18, with explicit LoopTree and LocalSched boundaries.
+ Instruction selection arrives in 19, RegAlloc in 20, Encoding/Export in 21.
+ FinalizeIR starts in 22 with unused-function cleanup and gains layout in 23.
+ Opto (SCCP) and IterPostOpto start in 24; earlier peepholes are named Iter.
+ Serialize, Lower, and Unlink stay in 25. Use short explicit chains such as
+ parse().iter(), and driver(Phase.target, ...) for longer compilation paths.
- 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.
@@ -89,8 +119,8 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m
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
+ Precise alias recovery can still be deferred. See `chapter25/README.md`.
+ Runtime lowering is implemented above; argument arrays 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
@@ -157,8 +187,7 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m
deliberately retains immutable shallow struct references resolved by name;
see its README's L0/L1 notes before trying to expand recursive types.
Keep Chapter 25's separate constructor-memory and incomplete-type design.
- See the concrete boundary notes
- in `docs/chapter-backports.md`. GCM readiness and anti-dependency checks must both
+ GCM readiness and anti-dependency checks must both
filter by alias and ignore MemMerge as a clobber; keep the evaluator's same
alias filtering too. MemMerge still needs ordinary data-dependency placement.
@@ -305,8 +334,8 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m
affected directories explicitly listed. Establish the destination baseline
before changing it; older Makefiles may need a forced rebuild after API changes.
- Keep unrelated discoveries separate in `docs/chapter-backports.md`. Completed
- fixes leave the pending queue; retain a concise validation summary and move
- unresolved discoveries out of the history into the actual queue. A green suite
+ fixes leave the pending queue; record validation in the change description.
+ Keep unresolved discoveries in the actual queue. A green suite
after changing graph/test order does not prove an intermittent failure fixed.
- Store disposable probes/logs under an appropriate ignored build directory.
Preserve durable reproducers in tests or the review record, not only scratch
@@ -512,6 +541,18 @@ For shared viewer work, read [the graph viewer notes](graph-viewer-codex-notes.m
before coloring, a fixed-register definition can clobber despite `_reg==-1`.
Cloning is valid only if its output mask can satisfy the use. A fixed-register
clone with flexible uses needs use-side splits, not a no-op def-side path.
+- Chapters 20-25 flag spills use specialized splits that exclude FP registers.
+ x86 can transfer FLAGS directly to a GPR or stack slot. ARM's
+ `SplitFlagsARM` transfers NZCV through a GPR and overrides
+ `isNonStackSplit()`; RegAlloc includes these transfers in loop-depth
+ collection and subsequent splitting. `SplitGPRARM` then admits GPRs and
+ stack slots, preserving the FP exclusion through cascading spills.
+ Keep ordinary splits excluded from those allocator paths. Test both flags
+ clobbers and simultaneous flags/GPR clobbers: `testFlagSpills` executes the
+ allocated moves for all 64 x86 status-bit and 16 ARM NZCV combinations from
+ Chapter 21 onward. Chapter 20 checks allocation and masks only; encoding
+ begins in 21. Preserve each allocator's existing ordinary-split policy:
+ 20-22 always skip ordinary splits here, while 23-25 skip them when min!=max.
- Check function ownership and register masks immediately after allocation,
before encoding rewrites tail calls or adds untyped branches. Call.regmap can
itself depend on CFG.fun/idom. The test-only CheckedCodeGen override keeps
diff --git a/skills/graph-viewer-codex-notes.md b/skills/graph-viewer-codex-notes.md
index e4775e91..0393cca9 100644
--- a/skills/graph-viewer-codex-notes.md
+++ b/skills/graph-viewer-codex-notes.md
@@ -26,7 +26,8 @@ diagnostic. Implemented in chapters 10-25 (the chapters with a TypeCheck pass):
- Chapters 10-17: SimpleGraphObserver calls STOP.typeCheck() after Iter; the
previously package-private method is now public. Chapters 18-25 call
- CodeGen.parse().opto().typeCheck(). Earlier chapters remain unchanged.
+ CodeGen.driver(CodeGen.Phase.TypeCheck), running the real ideal phases in
+ that chapter. Earlier chapters remain unchanged.
- GraphObserver.error(node,msg) is an optional callback. Existing compiler type
checking reports the node it diagnoses, rather than the viewer repeating the
check or trying to infer the node from text. Chapter 25 retains its originating