summaryrefslogtreecommitdiff
path: root/chapter25/src/test/java
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-24 15:23:20 -0700
committerCliff Click <cliffc@acm.org>2026-10-24 15:23:20 -0700
commitf49b184a093ab5f653ac36ccc4939c45515a2f4b (patch)
tree36b7b0b688a6b03ceb1412fc45f79f32e7540002 /chapter25/src/test/java
parenta216a23bbd4d6e05a40d86ec8e76c038e945f3ba (diff)
downloadSimple-f49b184a093ab5f653ac36ccc4939c45515a2f4b.tar.gz
Simple-f49b184a093ab5f653ac36ccc4939c45515a2f4b.zip
Move combined chapter 25 baseline to chapter 28b
Diffstat (limited to 'chapter25/src/test/java')
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java87
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java40
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter01Test.java95
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java61
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java66
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java159
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java204
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java305
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java222
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java208
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java299
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java510
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java283
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java284
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java104
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java228
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java412
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java148
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java109
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java622
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java522
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java459
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java307
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java715
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java374
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java341
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java293
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java528
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java550
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java94
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Eval2.java540
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java78
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Main.java18
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java104
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java83
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java67
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/NativeExecutionTest.java69
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/PhaseTest.java64
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Simple.java257
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/SpillStats.java92
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java173
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java53
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TestC.java273
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java55
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TestRunner.java17
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java223
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java141
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java76
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java352
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java192
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java111
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java177
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java933
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c18
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/c_returns.c28
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data.c22
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data_import.c14
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c25
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c7
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/person.c34
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c19
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c28
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c30
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/BrainFuckTest-testBrainFuck.smp46
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc0.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc2.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testArray1.smp8
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBasic1.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBrainfuck.smp43
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testCast.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags1.smp5
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags2.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFltArg.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testMergeSort.smp46
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonFloat.smp11
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonInteger.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp35
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testArray1.smp9
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testNewtonFloat.smp13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp35
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount-2.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testFibExport.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testLoop2.smp9
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testNewtonExport.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp9
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testSieve.smp30
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp33
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testHelloWorld.smp4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testNarrowCReturns.smp12
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp8
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp5
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp5
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp34
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAnd.smp11
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp12
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffAnd.smp17
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffOr.smp24
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp20
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFuncCall.smp16
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp17
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOr.smp13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp11
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp217
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testNewtonInteger.smp11
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testStackArguments.smp5
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack1.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10-2.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10.smp3
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11-2.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11.smp3
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12-2.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12.smp3
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13-2.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13.smp3
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack2.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack3.smp11
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack4.smp10
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack5.smp15
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9-2.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9.smp3
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/MergeSortTest-testMergeSort0.smp35
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md50
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/README.md78
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/spill/cohorts.tsv200
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp20
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp12
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp13
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m.smp2
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/a.smp1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/b.smp4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys.smp3
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/ary.smp6
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryi64.smp29
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryu8.smp50
150 files changed, 0 insertions, 14548 deletions
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
deleted file mode 100644
index c2713e4f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ /dev/null
@@ -1,87 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import java.io.IOException;
-
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class BrainFuckTest {
-
- @Test public void testBrainFuck() throws IOException {
- String brain_fuck = "Hello World!\n";
- String src =
-"""
-// -*- mode: java; -*-
-val brain_fuck = { ->
- var !program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
- int d = 0;
- u8[] !output = new u8[0];
- u8[] !data = new u8[100];
-
- for( int pc = 0; pc < program#; pc++ ) {
- var command = program[pc];
- if (command == '>') {
- d++;
- } else if (command == '<') {
- d--;
- } else if (command == '+') {
- data[d]++;
- } else if (command == '-') {
- data[d]--;
- } else if (command == '.') {
- // Output a byte; increase the output array size
- var old = output;
- output = new u8[output# + 1];
- for( int i = 0; i < old#; i++ )
- output[i] = old[i];
- output[old#] = data[d]; // Add the extra byte on the end
- } else if (command == ',') {
- data[d] = 42;
- } else if (command == '[') {
- if (data[d] == 0) {
- for( int d = 1; d > 0; ) {
- command = program[++pc];
- if (command == 91) d++;
- if (command == 93) d--;
- }
- }
- } else if (command == ']') {
- if (data[d]) {
- for( int d = 1; d > 0; ) {
- command = program[--pc];
- if (command == 93) d++;
- if (command == 91) d--;
- }
- }
- }
- }
- return output;
-};
-""";
- TestC.runC(src,"brain_fuck", brain_fuck, 40);
-
- EvalRisc5 R5 = TestRisc5.build( src, "brain_fuck", 0, 25, false);
- int trap = R5.step(100000);
- assertEquals(0,trap);
- int ptr = (int)R5.regs[com.seaofnodes.simple.node.cpus.riscv.riscv.A0];
- assertEquals(brain_fuck.length(),R5.ld4s(ptr));
- for( int i=0; i<brain_fuck.length(); i++ )
- assertEquals(brain_fuck.charAt(i), R5.ld1z(ptr+4+i));
-
- EvalArm64 A5 = TestArm64.build("brain_fuck", src, 0, 28, false);
- int trap_arm = A5.step(100000);
- assertEquals(0,trap_arm);
- assertEquals(0,trap);
- int ptr_arm = (int)A5.regs[com.seaofnodes.simple.node.cpus.arm.arm.X0];
- assertEquals(brain_fuck.length(),A5.ld4s(ptr_arm));
-
- for( int i=0; i<brain_fuck.length(); i++ )
- assertEquals(brain_fuck.charAt(i), A5.ld1z(ptr_arm+4+i));
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java b/chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java
deleted file mode 100644
index 31c0302b..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/BulkMemPhiTest.java
+++ /dev/null
@@ -1,40 +0,0 @@
-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/Chapter01Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter01Test.java
deleted file mode 100644
index fa7fb68a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter01Test.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Chapter01Test {
-
- @Test
- public void testSimpleProgram() {
- CodeGen code = new CodeGen("1;").parse();
- Node expr = code.expr();
- if( expr instanceof ConstantNode con ) {
- assertEquals(code._start,con.in(0));
- assertEquals(TypeInteger.constant(1), con._type);
- } else {
- fail();
- }
- }
-
- @Test
- public void testZero() {
- CodeGen code = new CodeGen("return 0;");
- code.parse();
- Node use = code.expr();
- if( use instanceof ConstantNode con && con._type instanceof TypeInteger )
- assertEquals(TypeInteger.constant(0),con._type);
- }
-
- @Test
- public void testBad1() {
- try {
- new CodeGen("ret;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'ret'",e.getMessage());
- }
- }
-
- @Test
- public void testBad2() {
- try {
- new CodeGen("return 0123;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error: integer values cannot start with '0'",e.getMessage());
- }
- }
-
- @Test
- public void testNotBad3() {
- // this test used to fail in chapter 1
- assertEquals("return 12;", new CodeGen("return - -12;").parse().print());
- }
-
- @Test
- public void testBad4() {
- try {
- new CodeGen("return 100").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, expected `;` but found `EOF`",e.getMessage());
- }
- }
-
- @Test
- public void testNotBad5() {
- // this test used to fail in chapter 1
- assertEquals("return -100;", new CodeGen("return -100;").parse().print());
- }
-
- @Test
- public void testBad6() {
- try {
- new CodeGen("return100;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'return100'",e.getMessage());
- }
- }
-
- @Test
- public void testBad7() {
- try {
- new CodeGen("return 1;}").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, unexpected but found `}`",e.getMessage());
- }
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java
deleted file mode 100644
index a51ac202..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter02Test.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Chapter02Test {
-
- @Test
- public void testParseGrammar() {
- CodeGen code = new CodeGen("return 1+2*3+-5;");
- code.parse();
- assertEquals("return 2;", code.print());
- }
-
- @Test
- public void testAddPeephole() {
- CodeGen code = new CodeGen("return 1+2;");
- code.parse();
- assertEquals("return 3;", code.print());
- }
-
- @Test
- public void testSubPeephole() {
- CodeGen code = new CodeGen("return 1-2;");
- code.parse();
- assertEquals("return -1;", code.print());
- }
-
- @Test
- public void testMulPeephole() {
- CodeGen code = new CodeGen("return 2*3;");
- code.parse();
- assertEquals("return 6;", code.print());
- }
-
- @Test
- public void testDivPeephole() {
- CodeGen code = new CodeGen("return 6/3;");
- code.parse();
- assertEquals("return 2;", code.print());
- }
-
- @Test
- public void testMinusPeephole() {
- CodeGen code = new CodeGen("return 6/-3;");
- code.parse();
- assertEquals("return -2;", code.print());
- }
-
- @Test
- public void testExample() {
- CodeGen code = new CodeGen("return 1+2*3+-5;");
- code.parse();
- assertEquals("return 2;", code.print());
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java
deleted file mode 100644
index f1e4af12..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter03Test.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter03Test {
-
- @Test
- public void testVarDecl() {
- CodeGen code = new CodeGen("int a=1; return a;");
- code.parse(); assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testVarAdd() {
- CodeGen code = new CodeGen("int a=1; int b=2; return a+b;");
- code.parse();
- assertEquals("return 3;", code.print());
- }
-
- @Test
- public void testVarScope() {
- CodeGen code = new CodeGen("int a=1; int b=2; int c=0; { int b=3; c=a+b; } return c;");
- code.parse();
- assertEquals("return 4;", code.print());
- }
-
- @Test
- public void testVarDist() {
- CodeGen code = new CodeGen("int x0=1; int y0=2; int x1=3; int y1=4; return (x0-x1)*(x0-x1) + (y0-y1)*(y0-y1); ");
- code.parse();
- assertEquals("return 8;", code.print());
- }
-
- @Test
- public void testSelfAssign() {
- try {
- new CodeGen("int a=a; return a;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Undefined name 'a'",e.getMessage());
- }
- }
-
- @Test
- public void testRedeclareVar() {
- try {
- new CodeGen("int a=1; int b=2; int c=0; int b=3; c=a+b;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Redefining name 'b'",e.getMessage());
- }
- }
-
- @Test
- public void testBad1() {
- try {
- new CodeGen("int a=1; int b=2; int !c=0; { int b=3; c=a+b;").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("Syntax error, expected `}` but found `EOF`",e.getMessage());
- }
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java
deleted file mode 100644
index 1132375e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter04Test.java
+++ /dev/null
@@ -1,159 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import org.junit.Test;
-import org.junit.Ignore;
-
-import static org.junit.Assert.*;
-
-public class Chapter04Test {
-
- @Test
- public void testPeephole() {
- CodeGen code = new CodeGen("return 1+arg+2; ");
- code.parse();
- assertEquals("return (arg+3);", code.print());
- }
-
- @Test
- public void testPeephole2() {
- CodeGen code = new CodeGen("return (1+arg)+2;");
- code.parse();
- assertEquals("return (arg+3);", code.print());
- }
-
- @Test
- public void testAdd0() {
- CodeGen code = new CodeGen("return 0+arg;");
- code.parse();
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void testAddAddMul() {
- CodeGen code = new CodeGen("return arg+0+arg;");
- code.parse();
- assertEquals("return (arg<<1);", code.print());
- }
-
- @Test
- public void testPeephole3() {
- CodeGen code = new CodeGen("return 1+arg+2+arg+3; ");
- code.parse();
- assertEquals("return ((arg<<1)+6);", code.print());
- }
-
- @Test
- public void testMul1() {
- CodeGen code = new CodeGen("return 1*arg;");
- code.parse();
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void testSub0() {
- CodeGen code = new CodeGen("return arg-0;");
- code.parse();
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void test0Sub() {
- CodeGen code = new CodeGen("return 0-arg;");
- code.parse();
- assertEquals("return (-arg);", code.print());
- }
-
- @Test
- public void testSubNeg() {
- CodeGen code = new CodeGen("return 3--arg;");
- code.parse();
- assertEquals("return (arg+3);", code.print());
- }
-
- @Test
- public void testNegSub() {
- CodeGen code = new CodeGen("return -arg-3;");
- code.parse();
- assertEquals("return (-(arg+3));", code.print());
- }
-
- @Test
- public void testVarArg() {
- CodeGen code = new CodeGen("return arg; ");
- code.parse();
- assertTrue(code.ctrl() instanceof FunNode);
- assertTrue(code.expr() instanceof ParmNode);
- }
-
- // Adding functions means `main` does not constant fold the incoming arg value
- @Ignore @Test
- public void testConstantArg() {
- CodeGen code = new CodeGen("return arg; ", TypeInteger.constant(2)).parse();
- assertEquals("return 2;", code.expr().in(1).toString());
- }
-
- @Test
- public void testCompEq() {
- CodeGen code = new CodeGen("return 3==3; ");
- code.parse();
- assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testCompEq2() {
- CodeGen code = new CodeGen("return 3==4; ");
- code.parse();
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testCompNEq() {
- CodeGen code = new CodeGen("return 3!=3; ");
- code.parse();
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testCompNEq2() {
- CodeGen code = new CodeGen("return 3!=4; ");
- code.parse();
- assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testBug1() {
- CodeGen code = new CodeGen("int a=arg+1; int !b=a; b=1; return a+2; ");
- code.parse();
- assertEquals("return (arg+3);", code.print());
- }
-
- @Test
- public void testBug2() {
- CodeGen code = new CodeGen("int !a=arg+1; a=a; return a; ");
- code.parse();
- assertEquals("return (arg+1);", code.print());
- }
-
- @Test
- public void testBug3() {
- try { new CodeGen("inta=1; return a;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Undefined name 'inta'",e.getMessage()); }
- }
-
- @Test
- public void testBug4() {
- CodeGen code = new CodeGen("return -arg;");
- code.parse();
- assertEquals("return (-arg);", code.print());
- }
-
- @Test
- public void testBug5() {
- CodeGen code = new CodeGen("return arg--2;");
- code.parse();
- assertEquals("return (arg--2);", code.print());
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java
deleted file mode 100644
index afdb1071..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ /dev/null
@@ -1,204 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Chapter05Test {
-
- @Test
- public void testIfStmt() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-if (arg == 1)
- a = arg+2;
-else {
- a = arg-3;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,(arg+2),(arg-3));", code.print());
- }
-
- @Test
- public void testTest() {
- CodeGen code = new CodeGen(
-"""
-int c = 3;
-int b = 2;
-if (arg == 1) {
- b = 3;
- c = 4;
-}
-return c;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,4,3);", code.print());
- }
-
- @Test
- public void testReturn2() {
- CodeGen code = new CodeGen(
-"""
-if( arg==1 )
- return 3;
-else
- return 4;
-""");
- code.parse();
- assertEquals("return Phi(Region,3,4);", code.print());
- }
-
- @Test
- public void testIfMergeB() {
- CodeGen code = new CodeGen(
-"""
-int a=arg+1;
-int b=0;
-if( arg==1 )
- b=a;
-else
- b=a+1;
-return a+b;""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return ((arg<<1)+Phi(Region,2,3));", code.print());
- }
-
- @Test
- public void testIfMerge2() {
- CodeGen code = new CodeGen(
-"""
-int a=arg+1;
-int b=arg+2;
-if( arg==1 )
- b=b+a;
-else
- a=b+1;
-return a+b;""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return ((Phi(Region,(arg<<1),arg)+arg)+Phi(Region,4,5));", code.print());
- }
-
- @Test
- public void testMerge3() {
- CodeGen code = new CodeGen(
-"""
-int a=1;
-if( arg==1 )
- if( arg==2 )
- a=2;
- else
- a=3;
-else if( arg==3 )
- a=4;
-else
- a=5;
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,3,5,2,4);", code.print());
- }
-
- @Test
- public void testMerge4() {
- CodeGen code = new CodeGen(
-"""
-int a=0;
-int b=0;
-if( arg )
- a=1;
-if( arg==0 )
- b=2;
-return arg+a+b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- String ir = code.print();
- assertTrue(ir,
- ir.equals("return ((arg+Phi(Region,1,0))+Phi(Region,2,0));") ||
- ir.equals("return (Phi(Region,((!=0)arg+1),0)+Phi(Region,2,0));"));
- }
-
- @Test
- public void testMerge5() {
- CodeGen code = new CodeGen(
-"""
-int a=arg==2;
-if( arg==1 )
-{
- a=arg==3;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return (arg==Phi(Region,3,2));", code.print());
- }
-
- @Test
- public void testMergeSameCon() {
- CodeGen code = new CodeGen(
-"""
-int a=arg;
-if( arg==1 )
- a=3;
-else
- a=3;
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- }
-
- @Test
- public void testTrue() {
- CodeGen code = new CodeGen("return true;").parse();
- assertEquals("return 1;",code.print());
- }
-
- @Test
- public void testHalfDef() {
- try { new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); }
- }
-
- @Test
- public void testHalfDef2() {
- try { new CodeGen("if( arg==1 ) { int b=2; } else { int b=3; } return b;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Undefined name 'b'",e.getMessage()); }
- }
-
- @Test
- public void testRegress1() {
- try { new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); }
- }
-
-
- @Test
- public void testBadNum() {
- try { new CodeGen("return 1-;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Syntax error, expected `an identifier or expression` but found `;`",e.getMessage()); }
- }
-
- @Test
- public void testKeyword1() {
- try { new CodeGen("int true=0; return true;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'true'",e.getMessage()); }
- }
-
- @Test
- public void testKeyword2() {
- try { new CodeGen("int else=arg; if(else) else=2; else else=1; return else;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Expected an identifier, found 'else'",e.getMessage()); }
- }
-
- @Test
- public void testKeyword3() {
- try { new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); }
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java
deleted file mode 100644
index d1af5d14..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ /dev/null
@@ -1,305 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Chapter06Test {
-
- @Test
- public void testPeepholeReturn() {
- CodeGen code = new CodeGen("""
-if( true ) return 2;
-return 1;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 2;", code.print());
- assertTrue(code.ctrl() instanceof FunNode);
- }
-
- @Test
- public void testPeepholeRotate() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-if (arg)
- a = 2;
-return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testPeepholeCFG() {
- CodeGen code = new CodeGen(
-"""
-int a=1;
-if( true )
- a=2;
-else
- a=3;
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 2;", code.print());
- assertTrue(code.ctrl() instanceof FunNode);
- }
-
- @Test
- public void testIfIf() {
- CodeGen code = new CodeGen(
-"""
-int a=1;
-if( arg!=1 )
- a=2;
-else
- a=3;
-int b=4;
-if( a==2 )
- b=42;
-else
- b=5;
-return b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,42,5);", code.print());
- }
-
- @Test
- public void testIfArgIf() {
- CodeGen code = new CodeGen(
-"""
-int a=1;
-if( 1==1 )
- a=2;
-else
- a=3;
-int b=4;
-if( arg==2 )
- b=a;
-else
- b=5;
-return b;""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,2,5);", code.print());
- }
-
- @Test
- public void testMerge3With2() {
- CodeGen code = new CodeGen(
-"""
-arg=2;
-int a=1;
-if( arg==1 )
- if( arg==2 )
- a=2;
- else
- a=3;
-else if( arg==3 )
- a=4;
-else
- a=5;
-return a;
-""", TypeInteger.constant(2)).driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 5;", code.print());
- }
-
- @Test
- public void testMerge3With1() {
- CodeGen code = new CodeGen(
-"""
-arg=1;
-int a=1;
-if( arg==1 )
- if( arg==2 )
- a=2;
- else
- a=3;
-else if( arg==3 )
- a=4;
-else
- a=5;
-return a;
-""", TypeInteger.constant(1)).driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- }
-
- @Test
- public void testMerge3Peephole() {
- CodeGen code = new CodeGen(
-"""
-int a=1;
-if( arg==1 )
- if( 1==2 )
- a=2;
- else
- a=3;
-else if( arg==3 )
- a=4;
-else
- a=5;
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,3,5,4);", code.print());
- }
-
- @Test
- public void testMerge3Peephole1() {
- CodeGen code = new CodeGen(
-"""
-arg=1;
-int a=1;
-if( arg==1 )
- if( 1==2 )
- a=2;
- else
- a=3;
-else if( arg==3 )
- a=4;
-else
- a=5;
-return a;
-""", TypeInteger.constant(1));
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- }
-
- @Test
- public void testMerge3Peephole3() {
- CodeGen code = new CodeGen(
-"""
-arg=3;
-int a=1;
-if( arg==1 )
- if( 1==2 )
- a=2;
- else
- a=3;
-else if( arg==3 )
- a=4;
-else
- a=5;
-return a;
-""", TypeInteger.constant(3));
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 4;", code.print());
- }
-
- @Test
- public void testDemo1NonConst() {
- CodeGen code = new CodeGen("""
-int a = 0;
-int b = 1;
-if( arg ) {
- a = 2;
- if( arg ) { b = 2; }
- else b = 3;
-}
-return a+b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,4,1);", code.print());
- }
-
-
- @Test
- public void testDemo1True() {
- CodeGen code = new CodeGen(
-"""
-arg=1;
-int a = 0;
-int b = 1;
-if( arg ) {
- a = 2;
- if( arg ) { b = 2; }
- else b = 3;
-}
-return a+b;
-""", TypeInteger.constant(1));
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 4;", code.print());
- }
-
- @Test
- public void testDemo1False() {
- CodeGen code = new CodeGen(
-"""
-arg=0;
-int a = 0;
-int b = 1;
-if( arg ) {
- a = 2;
- if( arg ) { b = 2; }
- else b = 3;
-}
-return a+b;
-""", TypeInteger.constant(0));
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testDemo2NonConst() {
- CodeGen code = new CodeGen("""
-int a = 0;
-int b = 1;
-int c = 0;
-if( arg ) {
- a = 1;
- if( arg==2 ) { c=2; } else { c=3; }
- if( arg ) { b = 2; }
- else b = 3;
-}
-return a+b+c;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,6,1,5);", code.print());
- }
-
-
- @Test
- public void testDemo2True() {
- CodeGen code = new CodeGen(
-"""
-arg=1;
-int a = 0;
-int b = 1;
-int c = 0;
-if( arg ) {
- a = 1;
- if( arg==2 ) { c=2; } else { c=3; }
- if( arg ) { b = 2; }
- else b = 3;
-}
-return a+b+c;
-""", TypeInteger.constant(1));
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 6;", code.print());
- }
-
- @Test
- public void testDemo2arg2() {
- CodeGen code = new CodeGen(
-"""
-arg=2;
-int a = 0;
-int b = 1;
-int c = 0;
-if( arg ) {
- a = 1;
- if( arg==2 ) { c=2; } else { c=3; }
- if( arg ) { b = 2; }
- else b = 3;
-}
-return a+b+c;
-""", TypeInteger.constant(2));
- 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
deleted file mode 100644
index 7094c0d5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ /dev/null
@@ -1,222 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Chapter07Test {
-
- @Test
- public void testExample() {
- CodeGen code = new CodeGen(
-"""
-while(arg < 10) {
- arg = arg + 1;
-}
-return arg;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testRegression() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-if(arg){}else{
- while(a < 10) {
- a = a + 1;
- }
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,1,Phi(Loop,1,(Phi_a+1)));", code.print());
- }
-
- @Test
- public void testWhileNested() {
- CodeGen code = new CodeGen(
-"""
-int sum = 0;
-int i = 0;
-while(i < arg) {
- i = i + 1;
- int j = 0;
- while( j < arg ) {
- sum = sum + j;
- j = j + 1;
- }
-}
-return sum;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,0,Phi(Loop,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", code.print());
- }
-
- @Test
- public void testWhileScope() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-int b = 2;
-while(a < 10) {
- if (a == 2) a = 3;
- else b = 4;
-}
-return b;
-""");
- code.parse();
- assertEquals("return Phi(Loop,2,Phi(Region,Phi_b,4));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhileNestedIfAndInc() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-int b = 2;
-while(a < 10) {
- if (a == 2) a = 3;
- else b = 4;
- b = b + 1;
- a = a + 1;
-}
-return b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,2,(Phi(Region,Phi_b,4)+1));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
-
- @Test
- public void testWhile() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-while(a < 10) {
- a = a + 1;
- a = a + 2;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhilePeep() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-while(a < 10) {
- a = a + 1;
- a = a + 2;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhile2() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-while(arg) a = 2;
-return a;
-""");
- code.parse();
- assertEquals("return Phi(Loop,1,2);", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhile2Peep() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-while(arg) a = 2;
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,1,2);", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhile3() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-while(a < 10) {
- int b = a + 1;
- a = b + 2;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhile3Peep() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-while(a < 10) {
- int b = a + 1;
- a = b + 2;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhile4() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-int b = 2;
-while(a < 10) {
- int b = a + 1;
- a = b + 2;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,1,(Phi_a+3));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testWhile4Peep() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-int b = 2;
-while(a < 10) {
- int b = a + 1;
- a = b + 2;
-}
-return a;
-""");
- 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
deleted file mode 100644
index 376e472b..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ /dev/null
@@ -1,208 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import org.junit.Assert;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class Chapter08Test {
-
- @Test
- public void testEx6() {
- CodeGen code = new CodeGen(
- """
-while(arg < 10) {
- arg = arg + 1;
- if (arg == 5)
- break;
- if (arg == 6)
- break;
-}
-return arg;
- """);
- 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));
- Assert.assertEquals("10", Eval2.eval(code, 6));
- }
-
-
- @Test
- public void testEx5() {
- CodeGen code = new CodeGen(
- """
-int a = 1;
-while(arg < 10) {
- arg = arg + 1;
- if (arg == 5)
- continue;
- if (arg == 7)
- continue;
- a = a + 1;
-}
-return a;
- """);
- 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);
- }
-
-
- @Test
- public void testEx4() {
- CodeGen code = new CodeGen(
- """
-while(arg < 10) {
- arg = arg + 1;
- if (arg == 5)
- continue;
- if (arg == 6)
- break;
-}
-return arg;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
- assertTrue(code.ctrl() instanceof RegionNode);
- }
-
- @Test
- public void testEx3() {
- CodeGen code = new CodeGen(
- """
-while(arg < 10) {
- arg = arg + 1;
- if (arg == 6)
- break;
-}
-return arg;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add);", code.print());
- assertTrue(code.ctrl() instanceof RegionNode);
- }
-
-
- @Test
- public void testEx2() {
- CodeGen code = new CodeGen(
- """
-while(arg < 10) {
- arg = arg + 1;
- if (arg == 5)
- continue;
- if (arg == 6)
- continue;
-}
-return arg;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
-
- @Test
- public void testEx1() {
- CodeGen code = new CodeGen(
- """
-while(arg < 10) {
- arg = arg + 1;
- if (arg == 5)
- continue;
-}
-return arg;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,arg,(Phi_arg+1));", code.print());
- assertTrue(code.ctrl() instanceof CProjNode);
- }
-
- @Test
- public void testRegress1() {
- CodeGen code = new CodeGen(
- """
-while( arg < 10 ) {
- int a = arg+2;
- if( a > 4 )
- break;
-}
-return arg;
- """);
- 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.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testBreakOutsideLoop() {
- try {
- new CodeGen("""
-if(arg <= 10) {
- break;
- arg = arg + 1;
-}
-return arg;
-""").parse();
- fail();
- } catch( RuntimeException e ) {
- assertEquals("No active loop for a break or continue",e.getMessage());
- }
- }
-
- @Test
- public void testRegress3() {
- CodeGen code = new CodeGen("""
-while(arg < 10) {
- break;
-}
-return arg;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void testRegress4() {
- CodeGen code = new CodeGen("""
-int a = 1;
-while(arg < 10) {
- a = a + 1;
- if (arg > 2) {
- int a = 17;
- break;
- }
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,Phi(Loop,1,(Phi_a+1)),Add);", code.print());
- assertTrue(code.ctrl() instanceof RegionNode);
- }
-
- @Test
- public void testRegress5() {
- CodeGen code = new CodeGen("""
-int a = 1;
-while(1) {
- a = a + 1;
- if (a<10) continue;
- break;
-}
-return a;
-""");
- 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
deleted file mode 100644
index 7025b98a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ /dev/null
@@ -1,299 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeInteger;
-import org.junit.Assert;
-import org.junit.Test;
-import org.junit.Ignore;
-
-import static org.junit.Assert.*;
-
-public class Chapter09Test {
-
- @Test
- @Ignore
- public void testJig() {
- CodeGen code = new CodeGen("""
-int v0=0;
-arg=0;
-while(v0) {
- while(1) if(arg*arg*0==0) {}
- while(0) {}
- arg=1;
-}
-return 0;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- }
-
- @Test
- public void testGVN1() {
- CodeGen code = new CodeGen(
- """
-int x = arg + arg;
-if(arg < 10) {
- return arg + arg;
-}
-else {
- x = x + 1;
-}
-return x;
- """);
- 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));
- }
-
- @Test
- public void testGVN2() {
- CodeGen code = new CodeGen("return arg*arg-arg*arg;");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- Assert.assertEquals("0", Eval2.eval(code, 1, 1));
- }
-
- @Test
- public void testWorklist1() {
- CodeGen code = new CodeGen("""
-int step = 1;
-while (arg < 10) {
- arg = arg + step + 1;
-}
-return arg;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,arg,(Phi_arg+2));", code.print());
- Assert.assertEquals("11", Eval2.eval(code, 1));
- }
-
- @Test
- public void testWorklist2() {
- CodeGen code = new CodeGen(
- """
-int cond = 0;
-int one = 1;
-while (arg < 10) {
- if (cond) one = 2;
- arg = arg + one*3 + 1;
-}
-return arg;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,arg,(Phi_arg+4));", code.print());
- Assert.assertEquals("13", Eval2.eval(code, 1));
- }
-
- @Test
- public void testWorklist3() {
- CodeGen code = new CodeGen(
- """
-int v1 = 0;
-int v2 = 0;
-int v3 = 0;
-int v4 = 0;
-int v5 = 0;
-int v6 = 0;
-int v7 = 0;
-int v8 = 0;
-while (arg) {
- if (v1) v2 = 1;
- if (v2) v3 = 1;
- if (v3) v4 = 1;
- if (v4) v5 = 1;
- if (v5) v6 = 1;
- if (v6) v7 = 1;
- if (v7) v8 = 1;
- arg = arg + v8 + 1;
-}
-return arg;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testRegionPeepBug() {
- CodeGen code = new CodeGen(
- """
-int v0=0;
-int v1=0;
-while(v1+arg) {
- arg=0;
- int v2=v0;
- while(arg+1) {}
- v0=1;
- v1=v2;
-}
-return 0;
- """);
- 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.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void testWhile1() {
- CodeGen code = new CodeGen("""
-if(0) while(0) {
- int arg=arg;
- while(0) {}
-}
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
-
- @Test
- public void testPrecedence() {
- CodeGen code = new CodeGen("return 3-1+2;");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 4;", code.print());
- }
-
- @Test
- public void testSwap2() {
- CodeGen code = new CodeGen("return 1+(1+1);");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- }
-
-
- @Test
- public void testFuzz0() {
- CodeGen code = new CodeGen("""
-int one = 1;
-int a = 0;
-int zero = 0;
-while(arg) {
- a = -(one + a + 2);
- arg = arg + 1;
- one = one + zero;
-}
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,0,(-(Phi_a+3)));", code.print());
- }
-
- @Test
- public void testFuzz1() {
- CodeGen code = new CodeGen("""
-while(1) {}
-while(arg) break;
-while(arg) arg=0;
-arg=0;
-int v0=0!=0<-0;
-return -0+0+0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Top;", code.print());
- }
-
-
- @Test
- public void testFuzz2() {
- CodeGen code = new CodeGen("return 0+-0;");
- 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.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testFuzz4() {
- CodeGen code = new CodeGen("""
-while(1) {
- arg=0<=0;
- if(1<0) while(arg==-0) arg=arg-arg;
-}
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Top;", code.print());
- }
-
- @Test
- public void testFuzz5() {
- CodeGen code = new CodeGen("""
-{
- int v0=0;
- while(1)
- int v1=0--0;
- while(v0)
- break;
- while(-v0) {
- while(0+0+v0) continue;
- break;
- }
- if(-0!=-0+0+v0) while(0+0+0+0)
- break;
-}
-return 0!=0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Top;", code.print());
- }
-
- @Test
- public void testFuzz6() {
- CodeGen code = new CodeGen(
-"""
-int v0=0;
-while(0==1) while(v0)
- v0=1+v0;
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testFuzz7() {
- CodeGen code = new CodeGen("""
-while(1) {}
-int v0=0;
-while(v0)
- {}
-int v1=0;
-while(1)
- v1=1;
-return v1+v0;
- """);
- 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.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testMeet() {
- Type t1 = Type.TOP;
- Type t2 = TypeInteger.TOP;
- Assert.assertEquals(TypeInteger.TOP, t1.meet(t2));
- Assert.assertEquals(TypeInteger.TOP, t2.meet(t1));
- t1 = Type.BOTTOM;
- t2 = TypeInteger.BOT;
- Assert.assertEquals(Type.BOTTOM, t1.meet(t2));
- Assert.assertEquals(Type.BOTTOM, t2.meet(t1));
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
deleted file mode 100644
index 6892db0c..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ /dev/null
@@ -1,510 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class Chapter10Test {
- @Test public void testConditionalStoreSharing() {
- // Sharing the writes on two of three paths must not write on the third.
- // Return the object so Load forwarding cannot hide the physical Store.
- String src = """
- struct S { int x; };
- !S !a = new S;
- a.x = 10;
- if (arg == 1) a.x = 9;
- else if (arg == 2) a.x = 9;
- return a;
- """;
- var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
- for( int arg : new int[]{0,1,2,3} ) {
- long expected = (arg==1 || arg==2) ? 9 : 10;
- assertEquals("Test.S{x="+expected+"}",Eval2.eval(code,arg));
- }
- }
-
- @Test public void testDeadReferenceReturn() {
- for( String body : new String[] {
- "return new S; return 0;",
- "if(1) return new S; else return 0;"
- } ) {
- String src = "struct S { int x; }; " + body;
- 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,
- entry.ret().expr()._type instanceof com.seaofnodes.simple.type.TypeMemPtr);
- }
- }
-
-
-
- private static final String NULLABLE_POINT_SOURCE = """
- struct Point { int x; new Point = { int v -> x = v; }; };
- !Point?[] !points = new !Point?[2];
- points[arg] = new Point(42);
- Point? p = points[1];
- """;
-
- @Test
- public void testNullGuards() {
- // Issue #246: != null lowers to !!p. Follow every negation, on either arm.
- for( String body : new String[] {
- "if (p != null) return p.x; return -1;",
- "if (null != p) return p.x; return -1;",
- "if (!!!!p) return p.x; return -1;",
- "if (!!!p) return -1; return p.x;",
- "val b = !!p; if (b) return p.x + b - 1; return -1;",
- "if (!(p == null || arg == 0)) return p.x; return -1;"
- } )
- for( long seed : new long[] { 1, 42, 126 } ) {
- CodeGen code = new CodeGen(NULLABLE_POINT_SOURCE+body,seed,true)
- .driver(CodeGen.Phase.TypeCheck);
- assertEquals(body,"-1",Eval2.eval(code,0));
- assertEquals(body,"42",Eval2.eval(code,1));
- code.driver(CodeGen.Phase.LocalSched);
- }
- }
-
- @Test
- public void testShortCircuitGuardScheduling() {
- // The call result exists only on the RHS path, not above the merge.
- CodeGen code = new CodeGen("""
- val f = { int n -> (n+1)&7; };
- int x = 0;
- if (!!(arg && (x=f(arg)))) return x;
- return -1;
- """).driver(CodeGen.Phase.TypeCheck);
- assertEquals("-1",Eval2.eval(code,0));
- assertEquals("2",Eval2.eval(code,1));
- assertEquals("-1",Eval2.eval(code,7));
- code.driver(CodeGen.Phase.LocalSched);
- }
-
- @Test
- public void testNullGuardErrors() {
- for( String body : new String[] {
- "return p.x;",
- "if (p == null) return p.x; return -1;",
- "if (!!points) return p.x; return -1;",
- "if (!!p) { int x = p.x; } return p.x;"
- } ) {
- try {
- new CodeGen(NULLABLE_POINT_SOURCE+body).driver(CodeGen.Phase.TypeCheck);
- fail(body);
- } catch( Parser.ParseException e ) {
- assertEquals(body,"Might be null accessing 'x'",e.getMessage());
- }
- }
- }
-
- @Test
- public void testFuzzer() {
- CodeGen code = new CodeGen(
-"""
-int a = arg/3;
-int b = arg*5;
-int x = arg*7;
-int y = arg/11;
-int p; int g; int h;
-if( (arg/13)==0 ) {
- p = x + y;
- g = x;
- h = y;
-} else {
- p = a + b;
- g = a;
- h = b;
-}
-int r = g+h;
-return p-r;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testStruct() {
- CodeGen code = new CodeGen("""
-struct _Bar {
- int a;
- int b;
-};
-struct _Foo {
- int x;
-};
-_Foo? foo = null;
-!_Bar !bar = new _Bar;
-bar.a = 1;
-bar.a = 2;
-return bar.a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 2;", code.print());
- }
-
- @Test
- public void testExample() {
- CodeGen code = new CodeGen("""
-struct Vector2D { int x; int y; };
-!Vector2D !v = new Vector2D;
-v.x = 1;
-if (arg)
- v.y = 2;
-else
- v.y = 3;
-return v;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return Test.Vector2D; return MEM[ 2:___ 3:.x=0; 4:.y=0;]; return Test.Vector2D; ]", code.print());
- }
-
- @Test
- public void testBug() {
- CodeGen code = new CodeGen("""
-struct s0 {
- int v0;
-};
-s0? v1=null;
-int v3=v1.zAicm;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'zAicm'",e.getMessage()); }
- }
-
- @Test
- public void testBug2() {
- CodeGen code = new CodeGen("""
-struct s0 { int v0; };
-arg=0+new s0.0;
-""");
- try { code.parse(); fail(); }
- catch( Exception e ) { assertEquals("Expected an identifier, found 'null'",e.getMessage()); }
- }
-
- @Test
- public void testLoop() {
- CodeGen code = new CodeGen("""
-struct Bar { int a; };
-!Bar !bar = new Bar;
-while (arg) {
- bar.a = bar.a + 2;
- arg = arg + 1;
-}
-return bar.a;
-""");
- 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());
- }
-
- @Test
- public void testIf() {
- CodeGen code = new CodeGen("""
-struct Bar { int a; };
-!Bar !bar = new Bar;
-if (arg) bar = null;
-bar.a = 1;
-return bar.a;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar",e.getMessage()); }
- }
-
- @Test
- public void testIf2() {
- CodeGen code = new CodeGen("""
-struct Bar { int a; };
-!Bar? !bar = null;
-if (arg) bar = new Bar;
-bar.a = 1;
-return bar.a;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'a'",e.getMessage()); }
- }
-
- @Test
- public void testIf3() {
- CodeGen code = new CodeGen("""
-struct Bar { int a; };
-!Bar !bar = null;
-if (arg) bar = null;
-bar.a = 1;
-return bar.a;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Type null is not of declared type *Test.Bar", e.getMessage()); }
- }
-
- @Test
- public void testIfOrNull() {
- CodeGen code = new CodeGen("""
-struct _Bar { int a; };
-!_Bar? !bar = new _Bar;
-if (arg) bar = null;
-if( bar ) bar.a = 1;
-return bar.a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,1,.a);", code.print());
- }
-
- @Test
- public void testIfOrNull2() {
- CodeGen code = new CodeGen(
-"""
-struct _Bar { int a; };
-!_Bar? !bar = new _Bar;
-if (arg) bar = null;
-int rez = 3;
-if( !bar ) rez=4;
-else bar.a = 1;
-return rez;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,4,3);", code.print());
- }
-
- @Test
- public void testWhileWithNullInside() {
- CodeGen code = new CodeGen("""
-struct s0 {int v0;};
-!s0? !v0 = new s0;
-int ret = 0;
-while(arg) {
- ret = v0.v0;
- v0 = null;
- arg = arg - 1;
-}
-return ret;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) {
- assertEquals("Might be null accessing 'v0'", e.getMessage()); }
- }
-
- @Test
- public void testRedeclareStruct() {
- CodeGen code = new CodeGen("""
-struct s0 {
- int v0;
-};
-s0? v1=new s0;
-!s0? !v1;
-v1=new s0;
-""");
- try { code.parse(); fail(); }
- catch( Exception e ) { assertEquals("Redefining name 'v1'", e.getMessage()); }
- }
-
- @Test
- public void testIter() {
- // Build and use an iterator
- CodeGen code = new CodeGen(
-"""
-struct Iter {
- int x;
- int len;
-};
-!Iter !i = new Iter;
-i.len = arg;
-int sum=0;
-while( i.x < i.len ) {
- sum = sum + i.x;
- i.x = i.x + 1;
-}
-return sum;
-""");
- 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());
- }
-
-
- @Test
- public void test1() {
- CodeGen code = new CodeGen("""
-struct s0 {int v0;};
-!s0 !ret = new s0;
-while(arg) {
- !s0 !v0 = new s0;
- v0.v0 = arg;
- arg = arg-1;
- if (arg==5) ret=v0;
-
-}
-return ret;
-""");
- 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());
- }
-
- @Test
- public void test2() {
- CodeGen code = new CodeGen("""
-struct _s0 {int v0;};
-!_s0 !ret = new _s0;
-!_s0 !v0 = new _s0;
-while(arg) {
- v0.v0 = arg;
- arg = arg-1;
- if (arg==5) ret=v0;
-}
-return ret.v0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return .v0;", code.print());
- }
-
-
- @Test
- public void test3() {
- CodeGen code = new CodeGen("""
-struct _s0 {int v0;};
-!_s0 !ret = new _s0;
-while(arg < 10) {
- !_s0 !v1 = new _s0;
- if (arg == 5) ret=v1;
- arg = arg + 1;
-}
-return ret.v0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return .v0;", code.print());
- }
-
-
-
- @Test
- public void testModGlobalInConstructor() {
- CodeGen code = new CodeGen(
-"""
-int cnt = 1;
-// cnt is incremented exactly once, it is not part of s0 constructor
-// hence s0.x is always 1, and cnt remains at 2
-struct s0 { int x = cnt++; };
-s0 a = new s0();
-s0 b = new s0();
-// a.x==1, b.x==1, cnt==2
-return a.x * 100 + b.x * 10 + cnt;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return 112; return MEM[ 2:___ 3:___ 4:.x=1;]; return Test.s0; ]", code.print());
- }
-
-
- @Test
- public void testBug3() {
- CodeGen code = new CodeGen("""
-struct s0 { int f0; };
-return new s0;
-int v0=null.f0;
-""");
- try { code.parse(); fail(); }
- catch( Exception e ) { assertEquals("Syntax error, expected `;` but found `.`",e.getMessage()); }
- }
-
- @Test
- public void testBug4() {
- CodeGen code = new CodeGen("""
-if(0) {
- while(0) if(arg) continue;
- int v0=0;
- while(1) {
- int v2=-arg;
- v0=v2;
- }
-}
-return 0;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testBug5() {
- CodeGen code = new CodeGen("""
-struct s0 {
- int f0;
-};
-if(0) return 0;
-else return new s0;
-if(new s0.f0) return 0;
- """);
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("Stop[ return Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print());
- }
-
- @Test
- public void testBug6MissedWorklist() {
- CodeGen code = new CodeGen("""
-while(0) {}
-int v4=0;
-while(0<arg) {
- v4=v4+1;
- while(1) v4=-v4;
- while(0) arg=-1;
-}
-return 0;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- }
-
- @Test
- public void testBug7() {
- CodeGen code = new CodeGen("""
-struct s0 { int f0; };
-s0 v0 = new s0;
-while(v0.f0) {}
-s0 v1 = v0;
-return v1;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return (const)Test.s0; return MEM[ 2:___ 3:.f0=0;]; return Test.s0; ]", code.print());
- }
-
-
- @Test
- public void testBug8() {
- CodeGen code = new CodeGen("""
-int v2=0;
-while(0)
-while(0) {}
-{
- {
- {
- int v36=0;
- {
- while(0) {
- {
- while(-v2) {
- {
- while(v36) {
- while(v2) return 0;
- break;
- } }
- if(-v2) break;
- }
- }
- }
- }
- } }
-}
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testBug9() {
- CodeGen code = new CodeGen("""
-int v0=arg==0;
-while(v0) continue;
-return 0;
-""");
- 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
deleted file mode 100644
index fae7611d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ /dev/null
@@ -1,283 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class Chapter12Test {
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 3;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- }
-
- @Test
- public void testLinkedList0() {
- CodeGen code = new CodeGen(
-"""
-struct LLI { !LLI? !next; int i; };
-!LLI? !head = null;
-while( arg ) {
- !LLI !x = new LLI;
- x.next = head;
- x.i = arg;
- head = x;
- arg = arg-1;
-}
-return head.next.i;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'next'",e.getMessage()); }
- }
-
- @Test
- public void testLinkedList1() {
- CodeGen code = new CodeGen(
-"""
-struct _LLI { !_LLI? !next; int q; };
-!_LLI? !head = null;
-while( arg ) {
- !_LLI !x = new _LLI;
- x.next = head;
- x.q = arg;
- head = x;
- arg = arg-1;
-}
-if( !head ) return 0;
-_LLI? next = head.next;
-if( next==null ) return 1;
-return next.q;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return Phi(Region,1,.q,0);", code.print());
- assertEquals("2", Eval2.eval(code, 3));
- }
-
- @Test
- public void testCoRecur() {
- CodeGen code = new CodeGen(
-"""
-struct _int0 { int i; !Test._flt0? !f; };
-struct _flt0 { flt f; !_int0? !i; };
-!_int0 !i0 = new _int0;
-i0.i = 17;
-!_flt0 !f0 = new _flt0;
-f0.f = 3.14;
-i0.f = f0;
-f0.i = i0;
-return f0.i.f.i.i;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 17;", code.print());
- }
-
- @Test
- public void testNullRef0() {
- CodeGen code = new CodeGen(
-"""
-struct _N { !_N? !next; int i; };
-_N n = new _N;
-return n.next;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return null;", code.print());
- }
-
- @Test
- public void testNullRef1() {
- CodeGen code = new CodeGen(
-"""
-struct M { int m; };
-struct N { !M !next; int i; new N = { !M m -> next = m; }; };
-N n = new N(new M);
-return n.next;
-""");
- 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());
- }
-
- @Test
- public void testNullRef2() {
- CodeGen code = new CodeGen(
-"""
-struct M { int m; };
-struct N { !M !next; int i; new N = { !M m -> next = m; }; };
-N n = new N(null);
-return n.next;
-""");
- 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()); }
- }
-
- @Test
- public void testNullRef3() {
- CodeGen code = new CodeGen(
-"""
-struct N { !N? !next; int i; };
-!N !n = new N;
-n.i = 3.14;
-return n.i;
-""");
- 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.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.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot null + 42",e.getMessage()); }
- }
-
- @Test
- public void testEmpty() {
- CodeGen code = new CodeGen(
-"""
-struct _S{};
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testForwardRef0() {
- CodeGen code = new CodeGen(
-"""
-struct S1 { S2? s; };
-return new S2;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Unknown struct type 'S2'",e.getMessage()); }
- }
-
- @Test
- public void testForwardRef1() {
- CodeGen code = new CodeGen(
-"""
-struct _S1 { !_S2? !s; };
-struct _S2 { int x; };
-return (new _S1).s=new _S2;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Test._S2;", code.print());
- }
-
- @Test
- public void testcheckNull() {
- CodeGen code = new CodeGen(
-"""
-struct I { int i; };
-struct P { !I? !pi; };
-!P !p1 = new P;
-!P !p2 = new P;
-p2.pi = new I;
-p2.pi.i = 2;
-if (arg) p1 = new P;
-return p1.pi.i + 1;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
- }
-
- @Test
- public void testCoRecur2() {
- CodeGen code = new CodeGen(
-"""
-struct _A { !Test._B? !f0; !Test._C? !f1; }; !_A !a = new _A;
-struct _B { !Test._C? !f0; !Test._A? !f1; }; !_B !b = new _B;
-struct _C { !Test._A? !f0; !Test._B? !f1; }; !_C !c = new _C;
-
-a.f0=b; a.f1=c;
-b.f0=c; b.f1=a;
-c.f0=a; c.f1=b;
-
-return a.f0.f1.f0.f1.f0;
-
-""");
- 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));
- }
-
- @Test
- public void testCoRecur3() {
- CodeGen code = new CodeGen(
-"""
-struct _A { !Test._L? !a; !Test._T? !b; !Test._F? !c; }; !_A !a = new _A;
-struct _B { !Test._M? !a; !Test._U? !b; !Test._G? !c; }; !_B !b = new _B;
-struct _C { !Test._N? !a; !Test._V? !b; !Test._H? !c; }; !_C !c = new _C;
-struct _D { !Test._O? !a; !Test._W? !b; !Test._I? !c; }; !_D !d = new _D;
-struct _E { !Test._P? !a; !Test._X? !b; !Test._J? !c; }; !_E !e = new _E;
-struct _F { !Test._Q? !a; !Test._Y? !b; !Test._K? !c; }; !_F !f = new _F;
-struct _G { !Test._R? !a; !Test._Z? !b; !Test._L? !c; }; !_G !g = new _G;
-struct _H { !Test._S? !a; !Test._A? !b; !Test._M? !c; }; !_H !h = new _H;
-struct _I { !Test._T? !a; !Test._B? !b; !Test._N? !c; }; !_I !i = new _I;
-struct _J { !Test._U? !a; !Test._C? !b; !Test._O? !c; }; !_J !j = new _J;
-struct _K { !Test._V? !a; !Test._D? !b; !Test._P? !c; }; !_K !k = new _K;
-struct _L { !Test._W? !a; !Test._E? !b; !Test._Q? !c; }; !_L !l = new _L;
-struct _M { !Test._X? !a; !Test._F? !b; !Test._R? !c; }; !_M !m = new _M;
-struct _N { !Test._Y? !a; !Test._G? !b; !Test._S? !c; }; !_N !n = new _N;
-struct _O { !Test._Z? !a; !Test._H? !b; !Test._T? !c; }; !_O !o = new _O;
-struct _P { !Test._A? !a; !Test._I? !b; !Test._U? !c; }; !_P !p = new _P;
-struct _Q { !Test._B? !a; !Test._J? !b; !Test._V? !c; }; !_Q !q = new _Q;
-struct _R { !Test._C? !a; !Test._K? !b; !Test._W? !c; }; !_R !r = new _R;
-struct _S { !Test._D? !a; !Test._L? !b; !Test._X? !c; }; !_S !s = new _S;
-struct _T { !Test._E? !a; !Test._M? !b; !Test._Y? !c; }; !_T !t = new _T;
-struct _U { !Test._F? !a; !Test._N? !b; !Test._Z? !c; }; !_U !u = new _U;
-struct _V { !Test._G? !a; !Test._O? !b; !Test._A? !c; }; !_V !v = new _V;
-struct _W { !Test._H? !a; !Test._P? !b; !Test._B? !c; }; !_W !w = new _W;
-struct _X { !Test._I? !a; !Test._Q? !b; !Test._C? !c; }; !_X !x = new _X;
-struct _Y { !Test._J? !a; !Test._R? !b; !Test._D? !c; }; !_Y !y = new _Y;
-struct _Z { !Test._K? !a; !Test._S? !b; !Test._E? !c; }; !_Z !z = new _Z;
-
-a.a=l; a.b=t; a.c=f;
-b.a=m; b.b=u; b.c=g;
-c.a=n; c.b=v; c.c=h;
-d.a=o; d.b=w; d.c=i;
-e.a=p; e.b=x; e.c=j;
-f.a=q; f.b=y; f.c=k;
-g.a=r; g.b=z; g.c=l;
-h.a=s; h.b=a; h.c=m;
-i.a=t; i.b=b; i.c=n;
-j.a=u; j.b=c; j.c=o;
-k.a=v; k.b=d; k.c=p;
-l.a=w; l.b=e; l.c=q;
-m.a=x; m.b=f; m.c=r;
-n.a=y; n.b=g; n.c=s;
-o.a=z; o.b=h; o.c=t;
-p.a=a; p.b=i; p.c=u;
-q.a=b; q.b=j; q.c=v;
-r.a=c; r.b=k; r.c=w;
-s.a=d; s.b=l; s.c=x;
-t.a=e; t.b=m; t.c=y;
-u.a=f; u.b=n; u.c=z;
-v.a=g; v.b=o; v.c=a;
-w.a=h; w.b=p; w.c=b;
-x.a=i; x.b=q; x.c=c;
-y.a=j; y.b=r; y.c=d;
-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.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
deleted file mode 100644
index 214bc69e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ /dev/null
@@ -1,284 +0,0 @@
-package com.seaofnodes.simple;
-import com.seaofnodes.simple.codegen.CodeGen;
-
-
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-
-public class Chapter13Test {
-
- @Test
- public void testPrimes() {
- CodeGen code = new CodeGen(
-"""
-if( arg < 2 ) return 0;
-int primeCount = 1;
-int prime = 3;
-while( prime <= arg ) {
- int isPrime = 1;
- // Check for even case, so the next loop need only check odds
- if( (prime/2)*2 == prime )
- continue;
- // Check odds up to sqrt of prime
- int j = 3;
- while( j*j <= prime ) {
- if( (prime/j)*j == prime ) {
- isPrime = 0;
- break;
- }
- j = j + 2;
- }
- if( isPrime )
- primeCount = primeCount + 1;
- prime = prime + 2;
-}
-return primeCount;
-""");
- 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
- assertEquals("2", Eval2.eval(code, 3)); // 2, 3
- assertEquals("2", Eval2.eval(code, 4)); // 2, 3
- assertEquals("3", Eval2.eval(code, 5)); // 2, 3, 5
- assertEquals("4", Eval2.eval(code, 10)); // 2, 3, 5, 7
- }
-
- @Test
- public void testClassField() {
- CodeGen code = new CodeGen(
-"""
-if( arg < 2 ) return 0;
-int primeCount = 1;
-while( arg-- )
- primeCount++;
-return primeCount;
-""");
- code.driver(CodeGen.Phase.LocalSched);
- assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below
- assertEquals("3", Eval2.eval(code, 2)); //
-}
-
-
- @Test
- public void testAntiDeps1() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v=new _S;
-v.f = 2;
-_S q=new _S;
-int i=q.f;
-i=v.f;
-if (arg) v.f=1;
-return i;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 2;", code.print());
- assertEquals("2", Eval2.eval(code, 0));
- assertEquals("2", Eval2.eval(code, 1));
- }
-
- @Test
- public void testAntiDeps2() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-v.f = arg;
-!_S !t = new _S;
-int i = 0;
-if (arg) {
- if (arg+1) v = t;
- i = v.f;
-} else {
- v.f = 2;
-}
-return i;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,.f,0);", code.print());
- }
-
- @Test
- public void testAntiDeps3() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v0 = new _S;
-!_S? !v1;
-if (arg) v1 = new _S;
-if (v1) {
- v0.f = v1.f;
-} else {
- v0.f = 2;
-}
-return v0.f;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,.f,2);", code.print());
- }
-
-
- @Test
- public void testAntiDeps4() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-v.f = arg;
-_S t = new _S;
-int i = v.f;
-if (arg+1) arg= 0;
-while (arg) v.f = 2;
-return i;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return arg;", code.print());
- }
-
- @Test
- public void testAntiDeps5() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-while(1) {
- while(arg+1) { arg=arg-1; }
- if (arg) break;
- v.f = 2;
-}
-return v.f;
-""");
- 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));
- }
-
- @Test
- public void testAntiDeps6() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int v; };
-!_S !ptr=new _S;
-while( -arg )
- ptr = new _S;
-while(1)
- arg = arg+ptr.v;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Top;", code.print());
- }
-
- @Test
- public void testAntiDeps7() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-_S t = new _S;
-int i = v.f;
-while (arg) {
- v.f = arg;
- arg = arg-1;
-}
-return i;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testAntiDeps8() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-_S t = new _S;
-while(arg) {
- arg=arg-1;
- int f = v.f;
- v.f = 2;
- if (arg) arg = f;
- else v.f = 3;
-}
-return arg;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testAntiDeps9() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-_S t = new _S;
-if (arg) {
- v.f=2;
- int i=t.f;
- v.f=i;
-}
-return v.f;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testExample2() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int f; };
-!_S !v = new _S;
-int i = arg;
-while (arg > 0) {
- int j = i/3;
- if (arg == 5)
- v.f = j;
- arg = arg - 1;
-}
-return v;
- """);
- code.driver(CodeGen.Phase.FinalizeIR);
- //assertEquals("return new _S;", code.print());
- }
-
- @Test
- public void testScheduleUse() {
- CodeGen code = new CodeGen(
-"""
-int v0=0;
-while(0>=0) {
- u1 v1=0;
- v1=v0;
- if(v1*0)
- v0=-v1;
-}
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Top;", code.print());
- }
-
- @Test
- public void testLoopCarriedDep() {
- CodeGen code = new CodeGen(
-"""
-u32 v0=0;
-{
- int v1=0;
- while(v1) {
- v1=1>>>v0!=0;
- v0=v1/0;
- }
- return v1;
-}
-""");
- 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
deleted file mode 100644
index e058a822..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14FloatTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-
-public class Chapter14FloatTest {
- @Test public void testNumericAssignments() {
- String[][] tests = {
- {"int rounded=17.3/2.5; return rounded;", "6", "6"},
- {"int rounded=arg/2.5; return rounded;", "6", "-6"},
- {"int rounded=0; rounded=arg/2.5; return rounded;", "6", "-6"},
- {"i8 rounded=arg*16.0; return rounded;", "16", "-16"},
- {"int rounded=1e100; return rounded;", "9223372036854775807", "9223372036854775807"},
- {"int rounded=-1e100; return rounded;", "-9223372036854775808", "-9223372036854775808"},
- {"int rounded=1e300*1e300-1e300*1e300; return rounded;", "0", "0"},
- {"f32 rounded=arg+16777216.0; return rounded;", "1.6777232E7", "1.6777199E7"}
- };
- for( String[] test : tests ) {
- 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));
- }
- }
-
-
- @Test public void testSubZeroFloat() {
- 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));
- }
-
- @Test public void testDeadNumericReturns() {
- for( String[] test : new String[][] {
- {"if(1) return 7; else return 2.5;", "7"},
- {"if(0) return 7; else return 2.5;", "2.5"},
- {"return 7; return 2.5;", "7"},
- {"return 2.5; return 7;", "2.5"}
- } ) {
- String src = test[0];
- var code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals(src,test[1],Eval2.eval(code,0));
- }
- }
-
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testFloat() {
- CodeGen code = new CodeGen(
-"""
-flt x = 3.14;
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testSquareRoot() {
- CodeGen code = new CodeGen(
-"""
-flt guess = arg;
-while( 1 ) {
- flt next = (arg/guess + guess)/2;
- if( next == guess ) break;
- guess = next;
-}
-return guess;
-""");
- // TODO: One of the problems with <clinit> returning an OS error code,
- // is it chops all results to int.
- 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));
- }
-
- @Test
- public void testFPOps() {
- CodeGen code = new CodeGen(
-"""
-flt x = arg;
-return x+1==x;
-""");
- 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
deleted file mode 100644
index bc2ad8d3..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ /dev/null
@@ -1,228 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.StopNode;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class Chapter14Test {
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 3.14;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3.14;", code.print());
- assertEquals("3.14", Eval2.eval(code, 0));
- }
-
- @Test
- public void testRange() {
- CodeGen code = new CodeGen(
-"""
-int b;
-if( arg ) b=1; else b=0;
-int c = 99;
-if( b < 0 ) c = -1;
-if( b > 2 ) c = 1;
-return c;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 99;", code.print());
- assertEquals("99", Eval2.eval(code, 0));
- }
-
- @Test
- public void testU8() {
- CodeGen code = new CodeGen(
-"""
-u8 b = 123;
-b = b + 456;// Truncate
-return b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 67;", code.print());
- assertEquals("67", Eval2.eval(code, 0));
- }
-
-
- @Test
- public void testU8While() {
- CodeGen code = new CodeGen(
-"""
-u8 b = 123;
-while( b ) b = b + 456;// Truncate
-return b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testU1() {
- CodeGen code = new CodeGen(
-"""
-bool b = 123;
-b = b + 456;// Truncate
-u1 c = b; // No more truncate needed
-return c;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testAnd() {
- CodeGen code = new CodeGen(
-"""
-int b = 123;
-b = b+456 & 31; // Precedence
-return b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- }
-
- @Test
- public void testRefLoad() {
- CodeGen code = new CodeGen(
-"""
-struct _Foo { u1 b; };
-!_Foo !f = new _Foo;
-f.b = 123;
-return f.b;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testSigned() {
- CodeGen code = new CodeGen(
-"""
-i8 b = 255; // Chopped
-return b; // Sign extend
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return -1;", code.print());
- assertEquals("-1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testI8() {
- CodeGen code = new CodeGen(
-"""
-i8 b = arg;
-b = b + 1;// Truncate
-return b;
-""");
- 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));
- }
-
- @Test
- public void testMask() {
- CodeGen code = new CodeGen(
-"""
-u16 mask = (1<<16)-1; // AND mask
-int c = 123456789 & mask;
-return c; //
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 52501;", code.print());
- assertEquals("52501", Eval2.eval(code, 0));
- }
-
- @Test
- public void testOr() {
- CodeGen code = new CodeGen(
-"""
-return (arg | 123 ^ 456) >>> 1;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return (((arg|123)^456)>>>1);", code.print());
- assertEquals("217", Eval2.eval(code, 0));
- }
-
- @Test
- public void testMaskFloat() {
- CodeGen code = new CodeGen(
-"""
-flt f = arg;
-arg = f & 1;
-return arg;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot '&' flt",e.getMessage()); }
- }
-
- @Test
- public void testCloneAnd() {
- CodeGen code = new CodeGen("""
-int v0=0;
-u32 v1 = 1&(1<<arg)&(1<<arg);
-while(arg) v1=-v0;
-while(v1) break;
-return v1;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,(((1<<arg)&1)&Shl),0);", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testAndHigh() {
- CodeGen code = new CodeGen("""
-int v0=0;
-if(0&0>>>0) {
- while(0) {
- u8 v1=0;
- v0=0>>>0;
- v1=arg;
- while(v1+0) {}
- }
-}
-return v0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testTypes() {
- CodeGen code = new CodeGen(
-"""
-i8 xi8 = 123456789; if( xi8 != 21 ) return -8;
-i16 xi16 = 123456789; if( xi16 != -13035 ) return -16;
-i32 xi32 = 123456789; if( xi32 != 123456789 ) return -32;
-i64 xi64 = 123456789; if( xi64 != 123456789 ) return -64;
-int xint = 123456789; if( xint != 123456789 ) return -64;
-
-u1 ui1 = 123456789; if( ui1 != 1 ) return 1;
-u8 ui8 = 123456789; if( ui8 != 21 ) return 8;
-u16 ui16 = 123456789; if( ui16 != 52501 ) return 16;
-u32 ui32 = 123456789; if( ui32 != 123456789 ) return 32;
-
-flt fflt = 3.141592653589793; if( fflt != 3.141592653589793 ) return 3;
-f64 ff64 = 3.141592653589793; if( ff64 != 3.141592653589793 ) return 3;
-f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5;
-
-return 0;
-""");
- 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
deleted file mode 100644
index c2071cb6..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ /dev/null
@@ -1,412 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import org.junit.Test;
-import org.junit.Ignore;
-import static org.junit.Assert.*;
-
-public class Chapter15Test {
-
- @Test public void testArrayLoadDominatingControl() {
- String src = """
- int[] !a=new int[arg+1]; a[arg]=7;
- if(a#>1) {
- if(arg==2) a[1]=9;
- return a[1];
- }
- return 42;
- """;
- var code = new CodeGen(src).parse();
- var stop = code._stop;
- var load = stop.walk(n -> n instanceof com.seaofnodes.simple.node.LoadNode ld && ld._name.equals("[]") ? ld : null);
- org.junit.Assert.assertNotNull(load);
- // The parser uses current control as a placeholder for a range check.
- // Model an explicit check already performed before the inner diamond:
- // both predecessors are below the a#>1 true projection.
- var region = (com.seaofnodes.simple.node.RegionNode)load.in(0);
- var checked = region.idom().cfg(0);
- org.junit.Assert.assertTrue(checked instanceof com.seaofnodes.simple.node.CProjNode);
- load.setDef(0,checked);
- code.driver(CodeGen.Phase.TypeCheck);
- org.junit.Assert.assertTrue("Common dominating control permits Phi splitting",load.isDead());
- var split = stop.walk(n -> n instanceof com.seaofnodes.simple.node.LoadNode ld && ld._name.equals("[]") ? ld : null);
- org.junit.Assert.assertNotNull(split);
- org.junit.Assert.assertSame("The surviving read retains the earlier check",checked,split.in(0));
- code.driver(CodeGen.Phase.LocalSched);
- assertEquals("42",Eval2.eval(code,0));
- assertEquals("7",Eval2.eval(code,1));
- assertEquals("9",Eval2.eval(code,2));
- }
-
-
- @Test public void testArrayLoadControl() {
- // With arg==0, a has length 1 and a[1] must not execute. The memory
- // merge must not pull the read above its array-length guard.
- String src = """
- int n=1; if(arg) n=2;
- int[] !a=new int[n]; a[arg]=7;
- if(arg) a[1]=9;
- if(a#>1) return a[1];
- return 42;
- """;
- var code = new CodeGen(src).parse();
- var stop = code._stop;
- var load = stop.walk(n -> n instanceof com.seaofnodes.simple.node.LoadNode ld && ld._name.equals("[]") ? ld : null);
- org.junit.Assert.assertNotNull(load);
- org.junit.Assert.assertNotNull("Element loads carry their range-check control",load.in(0));
- code.driver(CodeGen.Phase.LocalSched);
- assertEquals("42",Eval2.eval(code,0));
- assertEquals("9",Eval2.eval(code,1));
- }
-
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 3.14;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3.14;", code.print());
- assertEquals("3.14", Eval2.eval(code, 0));
- }
-
- @Test
- public void testCyclic() {
- CodeGen code = new CodeGen(
-"""
-struct _C { !_C? !l; };
-!_C !c = new _C;
-c.l = c;
-return c;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Test._C;", code.print());
- assertEquals("Test._C{l=$cyclic}", Eval2.eval(code, 0));
- }
-
- @Test
- public void testSafetyCheck() {
- CodeGen code = new CodeGen(
-"""
-u8[] old = new u8[0];
-u8[] !output = new u8[1];
-int i = 0;
-while (i < old#) {
- output[i] = old[i];
- i = i + 1;
-}
-output[i] = 1;
-return output;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return []u8;", code.print());
- assertEquals("\u0001", Eval2.eval(code, 0));
- }
-
- @Test
- public void testBasic1() {
- CodeGen code = new CodeGen(
-"""
-int[] is = new int[2];
-return is[1];
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testBasic2() {
- CodeGen code = new CodeGen(
-"""
-int[] is = new int[2];
-int[] is2 = new int[2];
-return is[1];
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testBasic3() {
- CodeGen code = new CodeGen(
-"""
-int[] !a = new int[2];
-a[0] = 1;
-a[1] = 2;
-return a[0];
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testBasic4() {
- CodeGen code = new CodeGen(
-"""
-struct _A { int i; };
-!_A?[] !a = new !_A?[2];
-return a;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return []*Test._A?;", code.print());
- assertEquals("*Test._A {i64 !i; }?[ null,null]", Eval2.eval(code, 0));
- }
-
- @Test
- public void testBasic5() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int x; flt y; };
-// A new _S
-!_S !s = new _S; s.x=99; s.y = 3.14;
-
-// Double-d array of _Ss. Fill in one row.
-!_S?[]?[] !iss = new !_S?[]?[2];
-iss[0] = new !_S?[7];
-iss[0][2] = s;
-
-// Now pull out the filled-in value, with null checks
-flt rez;
-!_S?[]? is = iss[arg];
-if( !is ) rez = 1.2;
-else {
- _S? i = is[2];
- if( !i ) rez = 2.3;
- else rez = i.y;
-}
-return rez;
-""");
- 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));
- }
-
- @Test
- public void testBasic6() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int x; flt y; };
-// A new _S
-!_S !s = new _S; s.x=99; s.y = 3.14;
-
-// Double-d array of _Ss. Fill in one row.
-!_S?[]?[] !iss = new !_S?[]?[2];
-iss[0] = new !_S?[7];
-iss[0][2] = s;
-
-// Now pull out the filled-in value, with null checks
-flt rez = 1.2;
-if( iss[arg] )
- if( iss[arg][2] )
- rez = iss[arg][2].y;
-return rez;""");
- 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
- catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); }
- }
-
- @Test
- public void testTree() {
- CodeGen code = new CodeGen(
-"""
-// Can we define a forward-reference array?
-struct _Tree { !_Tree?[]? !_kids; };
-!_Tree !root = new _Tree;
-root._kids = new !_Tree?[2]; // NO BANG SO ARRAY IS OF IMMUTABLE TREES????
-root._kids[0] = new _Tree;
-return root;
-""");
- 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));
- }
-
- @Test
- public void testNestedStructAddMemProj() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int a; int[] !b; new _S = { int[] ary -> b=ary; }; };
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testRollingSum() {
- CodeGen code = new CodeGen(
-"""
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-int i=0;
-while( i < ary# ) {
- ary[i] = i;
- i = i+1;
-}
-// Fill [0,1,3,6,10,...]
-i=0;
-while( i < ary# - 1 ) {
- ary[i+1] = ary[i+1] + ary[i];
- i = i+1;
-}
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return (.[]+(.[]*1000));", code.print());
- assertEquals("1006", Eval2.eval(code, 4));
- }
-
- @Test @Ignore
- 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.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return []u32;", code.print());
- assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20));
- }
-
- @Test
- public void testNewNodeInit() {
- CodeGen code = new CodeGen(
-"""
-struct _S {int i; flt f;};
-!_S !s1 = new _S;
-!_S !s2 = new _S;
-s2.i = 3;
-s2.f = 2.0;
-if (arg) s1 = new _S;
-return s1.i + s1.f;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return ((flt).i+.f);", code.print());
- assertEquals("0.0", Eval2.eval(code, 0));
- }
-
-
- @Test
- public void testBad0() {
- CodeGen code = new CodeGen(
-"""
-return new flt;
-""");
- try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
- catch( Exception e ) { assertEquals("Cannot allocate a flt",e.getMessage()); }
- }
-
- @Test
- public void testBad1() {
- CodeGen code = new CodeGen(
-"""
-flt is = new int[2];
-return is;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Type *[]i64 is not of declared type flt",e.getMessage()); }
- }
-
- @Test
- public void testBad2() {
- CodeGen code = new CodeGen(
-"""
-int[] is = new int[3.14];
-return is[1];
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot '<<' 3.14",e.getMessage()); }
- }
-
- @Test
- public void testBad3() {
- CodeGen code = new CodeGen("""
-int[] is = new int[arg];
-return is[1];
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 4));
- try { Eval2.eval(code,-1); fail(); }
- catch( NegativeArraySizeException e ) { assertEquals("-1",e.getMessage()); }
- // This test will not pass (i.e., failed range-check) until we have range checks.
- // Without the range check, the load `is[1]` optimizes against the `new int[]`
- // and assumes it loads a zero then folds away, and the Eval2 never sees an OOB load.
- //try { Eval2.eval(code,0); fail(); }
- //catch( ArrayIndexOutOfBoundsException e ) { assertEquals("Array index 1 out of bounds for array length 0",e.getMessage()); }
- }
-
- @Test
- public void testProgress() {
- CodeGen code = new CodeGen("""
-i8 v1=0&0;
-u8 v2=0;
-byte v4=0;
-if(0) {}
-while(v2<0) {
- v4=0-v1;
- break;
-}
-int v5=0&0;
-while(v5+(0&0)) {
- int v7=0&0;
- while(v7)
- v4=0>>>v5;
- while(v1)
- return 0;
-}
-return v1;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testSharpNot() {
- CodeGen code = new CodeGen(
-"""
-if(0>>0) {}
-while(0) {}
-u32 v7=0;
-int v8=0;
-while(0<--1>>>---(v7*0==v8)) {}
-return 0;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- }
-
-
- @Test
- public void testProgress2() {
- CodeGen code = new CodeGen(
-"""
-if(1) {}
-else {
- while(arg>>>0&0>>>0) {}
- byte v3=0>>>0;
- while(0) {}
- int v7=0>>>0;
- while(v7<0>>>0) {
- while(0+v7<=0) if(1) arg=-12;
- if(arg) {
- v3=arg+v3+0;
- arg=0;
- }
- }
-}
-return 0;
-""");
- 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
deleted file mode 100644
index 2942cfee..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter16Test.java
+++ /dev/null
@@ -1,148 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class Chapter16Test {
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 3.14;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3.14;", code.print());
- assertEquals("3.14", Eval2.eval(code, 0));
- }
-
-
- @Test
- public void testMulti0() {
- CodeGen code = new CodeGen(
-"""
-int x, y;
-return x+y;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
- @Test
- public void testMulti1() {
- CodeGen code = new CodeGen(
-"""
-int x=2, y=x+1;
-return x+y;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 5;", code.print());
- assertEquals("5", Eval2.eval(code, 0));
- }
-
-
- @Test
- public void testConstruct0() {
- CodeGen code = new CodeGen("""
-struct _X { int x=3; };
-_X z = new _X;
-return z.x;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- }
-
- @Test
- public void testConstruct2() {
- CodeGen code = new CodeGen("""
-struct _X { int x=3; new _X = { int xx -> x=xx; }; };
-_X z = new _X(4);
-return z.x;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 4;", code.print());
- assertEquals("4", Eval2.eval(code, 0));
- }
-
-
- @Test
- public void testLinkedList1() {
- CodeGen code = new CodeGen(
-"""
-struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; };
-!_LLI? !head = null;
-while( arg ) {
- head = new _LLI(head,arg);
- arg = arg-1;
-}
-if( !head ) return 0;
-_LLI? next = head.next;
-if( !next ) return 1;
-return next.i;
-""");
- 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));
- assertEquals("2", Eval2.eval(code, 3));
- }
-
- @Test
- public void testLinkedList2() {
- CodeGen code = new CodeGen(
-"""
-struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int a ->
- next=n;
- int !tmp=a;
- while( a > 10 ) {
- tmp = tmp + a;
- a = a - 1;
- }
- i=tmp;
-}; };
-!_LLI? !head = null;
-while( arg ) {
- head = new _LLI(head,arg);
- arg = arg-1;
-}
-if( !head ) return 0;
-_LLI? next = head.next;
-if( !next ) return 1;
-return next.i;
-""");
- 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));
- assertEquals("2", Eval2.eval(code, 11));
- }
-
- @Test
- public void testSquare() {
- CodeGen code = new CodeGen(
-"""
-struct _Square {
- flt !side = arg;
- // Newtons approximation to the square root, computed in a constructor.
- // The actual allocation will copy in this result as the initial
- // value for 'diag'.
- flt !diag = arg*arg/2;
- while( 1 ) {
- flt next = (side/diag + diag)/2;
- if( next == diag ) break;
- diag = next;
- }
-};
-return new _Square;
-""");
- 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
deleted file mode 100644
index ea7afe56..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17aTest.java
+++ /dev/null
@@ -1,109 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class Chapter17aTest {
-
- @Test
- public void testFinal1() {
- CodeGen code = new CodeGen(
-"""
-int x=2, y=3;
-if( arg ) { int x = y; x = x*x; y=x; } // Shadow final x
-return y;
-""");
- 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));
- }
-
- @Test
- public void testConstruct1() {
- CodeGen code = new CodeGen("""
-struct _X { int ~x; new _X = { int xx -> x=xx; }; };
-_X z = new _X(3);
-return z.x;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 3;", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- }
-
- @Test
- public void testStructFinal0() {
- CodeGen code = new CodeGen("""
-struct _Point { int ~x, ~y; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
-_Point p = new _Point(3,4);
-return p;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return (const)Test._Point;", code.print());
- assertEquals("Test._Point{x=3,y=4}", Eval2.eval(code, 0));
- }
-
- @Test
- public void testStructFinal1() {
- CodeGen code = new CodeGen("""
-struct _Point { int x=3, y=4; new _Point = { int xx, int yy -> x=xx; y=yy; }; };
-~_Point p = new _Point(5,6);
-p.x++;
-return p;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal2() {
- CodeGen code = new CodeGen("""
-struct _Point { int x=3, y=4; };
-~_Point p = new _Point;
-p.x++;
-return p;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal3() {
- CodeGen code = new CodeGen("""
-struct _Point { var x; var y; };
-_Point p = new _Point;
-p.x++;
-return p;
-""");
- 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()); }
- }
-
- @Test
- public void testStructFinal4() {
- CodeGen code = new CodeGen("""
-struct _Point { val x=2; val y=4; };
-_Point p = new _Point;
-p.x++;
-return p;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test
- public void testStructFinal5() {
- CodeGen code = new CodeGen("""
-struct _Point { var x=3; var y=4; };
-!_Point !p = new _Point;
-p.x++;
-return p;
-""");
- 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
deleted file mode 100644
index 08029c4b..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ /dev/null
@@ -1,622 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class Chapter17bTest {
-
- @Test public void testSinkLoopStores() {
- var code = new CodeGen("""
- struct S { int x; };
- !S !a = new S;
- a.x = 10;
- int i = 0;
- while (i < arg) { a.x = i + 1; i++; }
- return a;
- """).driver(CodeGen.Phase.LocalSched);
- int[] stores = {0};
- code._stop.walk(n -> {
- // Constructor zero Stores remain in separate private memory.
- if( n instanceof com.seaofnodes.simple.node.StoreNode st && st._name.equals("x") &&
- st.val() instanceof com.seaofnodes.simple.node.PhiNode ) {
- assertEquals(0,st.cfg0().loopDepth()); // One final Store outside the loop.
- stores[0]++;
- }
- return null;
- });
- assertEquals(1,stores[0]);
- for( int arg : new int[]{-1,0,1,4} ) {
- long expected = arg<=0 ? 10 : arg;
- assertEquals("Test.S{x="+expected+"}",Eval2.eval(code,arg));
- }
- }
-
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 1;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
-
- // ---------------------------------------------------------------
- @Test
- public void testInc0() {
- CodeGen code = new CodeGen("""
-return arg++;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return arg;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testInc1() {
- CodeGen code = new CodeGen("""
-return arg+++arg++;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return ((arg<<1)+1);", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testInc2() {
- CodeGen code = new CodeGen("""
-// -(arg--)-(arg--)
-return -arg---arg--;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return (-((arg<<1)+-1));", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testInc3() {
- CodeGen code = new CodeGen("""
-int[] !xs = new int[arg];
-xs[0]++;
-xs[1]++;
-return xs[0];
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 2));
- }
-
- @Test
- public void testInc4() {
- CodeGen code = new CodeGen("""
-u8[] !xs = new u8[1];
-xs[0]--;
-return xs[0];
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 255;", code.print());
- assertEquals("255", Eval2.eval(code, 2));
- }
-
- @Test
- public void testInc5() {
- CodeGen code = new CodeGen("""
-struct _S { u16 x; };
-!_S !s = new _S;
-s.x--;
-return s.x;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 65535;", code.print());
- assertEquals("65535", Eval2.eval(code, 2));
- }
-
- @Test
- public void testInc6() {
- CodeGen code = new CodeGen("return --arg;");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return (arg+-1);", code.print());
- assertEquals("-1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testInc7() {
- CodeGen code = new CodeGen("u8 x=0; return --x;");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 255;", code.print());
- assertEquals("255", Eval2.eval(code, 0));
- }
-
- @Test
- public void testInc8() {
- CodeGen code = new CodeGen("int x; x+=2; return x+=3;");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 5;", code.print());
- assertEquals("5", Eval2.eval(code, 0));
- }
-
- @Test
- public void testInc9() {
- CodeGen code = new CodeGen("flt x = 1.5; x++; return x;");
- 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.driver(CodeGen.Phase.TypeCheck);
- assertEquals("2",Eval2.eval(code,0));
- }
-
-
- // ---------------------------------------------------------------
- @Test public void testVar0() {
- CodeGen code = new CodeGen("var d; return d;");
- 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.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.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.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.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.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.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.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.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.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.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test public void testVar11() {
- CodeGen code = new CodeGen("""
-struct Bar { int x; };
-!Bar !bar = new Bar;
-bar.x = 3; // Ok, bar is mutable
-
-struct Foo { !Bar? !bar; int y; new Foo = { !Bar? b -> bar = b; }; };
-!Foo !foo = new Foo(bar);
-foo.bar = bar; // Ok foo is mutable
-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
-return xfoo.bar.x;
- """);
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot modify final field 'x'",e.getMessage()); }
- }
-
- @Test public void testVar12() {
- CodeGen code = new CodeGen("""
-struct _Bar { int x; };
-!_Bar !bar = new _Bar;
-bar.x = 3; // Ok, bar is mutable
-
-struct _Foo { !_Bar? !bar; int y; };
-!_Foo !foo = new _Foo;
-foo.bar = bar; // Ok bar is mutable
-foo.bar.x++; // Ok foo and foo.bar and foo.bar.x are all mutable
-
-~_Foo xfoo = foo; // Throw away mutability
-int x4 = xfoo.bar.x; // Ok to read through xfoo, gets 4
-foo.bar.x++; // Bumps to 5
-int x5 = xfoo.bar.x; // Ok to read through xfoo, gets 5
-return x4*10+x5;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 45;", code.print());
- assertEquals("45", Eval2.eval(code, 0));
- }
-
- @Test
- public void testVar13() {
- CodeGen code = new CodeGen("""
-int i,i++;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Redefining name 'i'",e.getMessage()); }
- }
-
-
- @Test
- public void testVar14() {
- CodeGen code = new CodeGen("""
-struct B {};
-struct A { !B !b; new A = { ->
- return b; // read before init
- b = new B;
-}; };
-A x = new A;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Field 'b' might not be initialized",e.getMessage()); }
- }
-
- @Test
- public void testVar15() {
- for( String fields : new String[] {
- "!B !b; new A = { -> if (arg) b = new B; };",
- "!B !b; new A = { -> if (arg) return 0; b = new B; };",
- "!B !b; new A = { !B b -> b = new B; };", // Argument shadows field
- "new A = { -> }; !B !b;", // Field declared later
- } ) {
- CodeGen code = new CodeGen("struct B {}; struct A { "+fields+" }; return 0;");
- try { code.parse(); fail(fields); }
- catch( Parser.ParseException e ) { assertEquals("'Test.A' is not fully initialized, field 'b' is only partially set in the constructor",e.getMessage()); }
- }
- }
-
- @Test
- public void testDefaultConstructorFields() {
- for( String field : new String[] { "!B !b;", "!B !b;" } ) {
- CodeGen code = new CodeGen("struct B {}; struct A { "+field+" }; return new A;");
- try { code.parse(); fail(field); }
- catch( Parser.ParseException e ) { assertEquals("'Test.A' is not fully initialized, field 'b' needs to be set in a constructor",e.getMessage()); }
- }
- for( String fields : new String[] {
- "B b = new B;",
- "!B !b; b = new B;",
- "new A = { -> }; B b = new B;",
- "!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.driver(CodeGen.Phase.TypeCheck);
- assertEquals("1",Eval2.eval(code,0));
- assertEquals("1",Eval2.eval(code,1));
- }
- // No returning path means no object can escape uninitialized.
- new CodeGen("struct B {}; struct A { !B !b; new A = { -> while (true) {} }; }; return 0;").parse();
- }
-
- @Test
- public void testVar16() {
- CodeGen code = new CodeGen(
-"""
-struct S{ new S = { -> x = 2; }; };
-val x = 1;
-~S s = new S;
-return x;
-"""
-);
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Undefined name 'x'",e.getMessage()); }
- }
-
- @Test
- public void testVar17() {
- CodeGen code = new CodeGen(
-"""
-struct Person {
- bool focused;
-};
-val focus = { !Person p ->
- p.focused = true;
-};
-!Person !me; // !Person !me = new Person;
-focus(me);
-return me;
-"""
-);
- try { code.parse(); fail(); }
- catch( Exception e ) { assertEquals("Syntax error, expected `=expression` but found `;`",e.getMessage()); }
- }
-
- // ---------------------------------------------------------------
- @Test
- public void testTrinary0() {
- CodeGen code = new CodeGen("""
-return arg ? 1 : 2;
-""");
- 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));
- }
-
- @Test
- public void testTrinary1() {
- CodeGen code = new CodeGen("""
-return arg ? 0 : arg;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- @Test
- public void testShortCircuitGuard() {
- CodeGen code = new CodeGen("""
-struct S { int !x; };
-val get = { !S s -> s.x; };
-!S? !s = arg ? new S;
-if( !s || !arg ) return 0;
-return get(s);
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- }
-
- @Test
- public void testTrinary2() {
- CodeGen code = new CodeGen("""
-struct _Bar { int x; };
-var b = arg ? new _Bar : null;
-return b ? b.x++ + b.x++ : -1;
-""");
- 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));
- }
-
- @Test
- public void testTrinary3() {
- CodeGen code = new CodeGen("""
-struct _Bar { int x; };
-var b = arg ? new _Bar;
-return b ? b.x++ + b.x++ : -1;
-""");
- 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));
- }
-
- @Test
- public void testTrinary4() {
- // This test case will benefit from an unzipping transformation
- CodeGen code = new CodeGen("""
-struct _Bar { !_Bar? !next; int x; new _Bar = { !_Bar? !n, int xx -> next=n; x=xx; }; };
-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.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));
- assertEquals("2", Eval2.eval(code, 2));
- }
-
- @Test
- public void testTrinary5() {
- CodeGen code = new CodeGen("""
-flt f=arg?1:1.2;
-return f; // missing widening
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Region,1.0f,1.2);", code.print());
- assertEquals("1.2", Eval2.eval(code, 0));
- }
-
-
- @Test
- public void testTrinary6() {
- CodeGen code = new CodeGen("""
-struct S{};
-return arg ? 7 : new S;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Cannot return generic scalar",e.getMessage()); }
- }
-
- // ---------------------------------------------------------------
- @Test
- public void testFor0() {
- CodeGen code = new CodeGen("""
-int sum=0;
-for( int i=0; i<arg; i++ )
- sum += i;
-return sum;
-""");
- 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));
- }
- @Test
- public void testFor1() {
- CodeGen code = new CodeGen("""
-int sum=0, i=0;
-for( ; i<arg; i++ )
- sum += i;
-return sum;
-""");
- 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));
- }
- @Test
- public void testFor2() {
- CodeGen code = new CodeGen("""
-int sum=0;
-for( int i=0; ; i++ ) {
- if( i>=arg ) break;
- sum += i;
-}
-return sum;
-""");
- 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));
- }
- @Test
- public void testFor3() {
- CodeGen code = new CodeGen("""
-int sum=0;
-for( int i=0; i<arg; )
- sum += i++;
-return sum;
-""");
- 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));
- }
- @Test
- public void testFor4() {
- CodeGen code = new CodeGen("""
-int sum=0;
-for( int i=0; i<arg; i++ )
- sum += i;
-return i;
-""");
- try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
- catch( Exception e ) { assertEquals("Undefined name 'i'",e.getMessage()); }
- }
- @Test
- public void testFor5() {
- CodeGen code = new CodeGen("""
-for(;;arg++;) {}
-""");
- try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
- catch( Exception e ) { assertEquals("Syntax error, expected `Unexpected code after expression` but found `;`",e.getMessage()); }
- }
-
-
-
- // ---------------------------------------------------------------
- @Test
- public void testForward0() {
- CodeGen code = new CodeGen("""
-struct _A {
- !B? !f1;
- !B? !f2;
-};
-return new _A;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Test._A;", code.print());
- assertEquals("Test._A{f1=null,f2=null}", Eval2.eval(code,0));
- }
-
- @Test
- public void testForward1() {
- CodeGen code = new CodeGen("""
-struct _A {
- !B?[]? !nil_array_of_b;
- !B?[] !array_of_b;
- new _A = { !B?[] bs -> array_of_b = bs; };
-};
-return new _A(new !B?[0]).array_of_b;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return []*B?;", code.print());
- assertEquals("*B {}?[]", Eval2.eval(code,0));
- }
-
- // ---------------------------------------------------------------
- @Test
- public void testLinkedList2() {
- CodeGen code = new CodeGen("""
-struct _LLI { !_LLI? !next; int i; new _LLI = { !_LLI? !n, int ii -> next=n; i=ii; }; };
-!_LLI? !head = null;
-while( arg-- )
- head = new _LLI(head,arg);
-int sum=0;
-var ptr = head; // A read-only ptr, to be assigned from read-only next fields
-for( ; ptr; ptr = ptr.next )
- sum += ptr.i;
-return sum;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return Phi(Loop,0,(Phi_sum+.i));", code.print());
- assertEquals("45", Eval2.eval(code, 10));
- }
-
- @Test
- public void sieveOfEratosthenes() {
- CodeGen code = new CodeGen(
-"""
-var ary = new bool[arg], primes = new int[arg];
-var nprimes=0, p=0;
-// Find primes while p^2 < arg
-for( p=2; p*p < arg; p++ ) {
- // skip marked non-primes
- while( ary[p] ) p++;
- // p is now a prime
- primes[nprimes++] = p;
- // Mark out the rest non-primes
- for( int i = p + p; i < ary#; i += p )
- ary[i] = true;
-}
-// Now just collect the remaining primes, no more marking
-for( ; p < arg; p++ )
- if( !ary[p] )
- primes[nprimes++] = p;
-// Copy/shrink the result array
-var !rez = new int[nprimes];
-for( int j=0; j<nprimes; j++ )
- rez[j] = primes[j];
-return rez;
-""");
- 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
deleted file mode 100644
index ae1dca5f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ /dev/null
@@ -1,522 +0,0 @@
-package com.seaofnodes.simple;
-import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.node.*;
-
-
-import com.seaofnodes.simple.codegen.CodeGen.Phase;
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-import static org.junit.Assert.fail;
-
-public class Chapter18Test {
- @Test public void testNeverExitLoopDepth() {
- String[] sources = {
- "if(arg) while(1) {} return 7;",
- "if(arg==1) while(1) {} if(arg==2) while(1) {} return 7;",
- "while(1) {}",
- "int x=0; while(arg) { if(arg==2) while(1) { x++; } x++; arg--; } return x;"
- };
- for( String src : sources ) {
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.LoopTree);
- int[] nevers = {0};
- code._stop.walk(n -> {
- if( n instanceof FunNode fun )
- assertEquals("Function entry is outside its loops",0,fun.loopDepth());
- if( n instanceof ReturnNode ret ) {
- assertEquals(ret.fun().loopDepth(),ret.loopDepth());
- assertEquals(ret.fun().loopDepth(),ret.cfg0().loopDepth());
- }
- if( n instanceof LoopNode loop && !(loop instanceof StartNode) &&
- loop.back() instanceof CProjNode back && back.in(0) instanceof NeverNode never ) {
- nevers[0]++;
- assertSame(loop,never.loop());
- assertSame(loop,back.loop());
- CProjNode exit = never.cproj(1-back._idx);
- CFGNode fun = loop;
- while( !(fun instanceof FunNode) ) fun=fun.idom();
- assertSame(fun.loop(),exit.loop());
- assertEquals(fun.loopDepth(),exit.loopDepth());
- assertTrue(loop.loopDepth()>fun.loopDepth());
- }
- return null;
- });
- assertTrue("Exercise a synthetic exit",nevers[0]>0);
- }
- }
-
- @Test public void testFunctionLocalConstantChains() {
- String src = "val f = { int x -> x ? f(x-1)*305420988+x/305420988 : 1; }; "+
- "val g = { int x -> x ? g(x-1)/305420988+x*305420988 : 1; }; "+
- "return f(arg)+g(arg);";
- CodeGen code = new CodeGen(src);
- code.driver(CodeGen.Phase.Schedule,"riscv","SystemV");
- var owners = new java.util.IdentityHashMap<CFGNode,Boolean>();
- code._stop.walk(n -> {
- if( !(n instanceof com.seaofnodes.simple.node.cpus.riscv.AddIRISC add) ||
- !(add.in(1) instanceof com.seaofnodes.simple.node.cpus.riscv.LUI upper) ||
- ((TypeInteger)upper._con).value()+((add._imm12<<20)>>20)!=305420988 )
- return null;
- CFGNode fun = n.cfg0();
- while( fun!=null && !(fun instanceof FunNode) ) fun = fun.idom();
- assertTrue("Constant must belong to a function",fun instanceof FunNode);
- assertNull("Share one constant chain within each function",owners.put(fun,true));
- // Follow all parts of a machine constant, including shared inputs.
- var todo = new java.util.ArrayList<Node>();
- var seen = new java.util.IdentityHashMap<Node,Boolean>();
- todo.add(n);
- for( int j=0; j<todo.size(); j++ ) {
- Node part = todo.get(j);
- if( seen.put(part,true)!=null ) continue;
- CFGNode owner = part.cfg0();
- while( owner!=null && !(owner instanceof FunNode) ) owner = owner.idom();
- assertSame("Every constant-building operation is function-local",fun,owner);
- for( int i=1; i<part.nIns(); i++ ) {
- Node def = part.in(i);
- if( def==null || def instanceof CFGNode ) continue;
- assertTrue("Copies must register their data edges",def._outputs.find(part)!=-1);
- if( def._type.isConstant() ) todo.add(def);
- }
- }
- return null;
- });
- assertTrue("Exercise constants shared across functions",owners.size()>=2);
- }
-
- @Test public void testReachableMixedReturns() {
- for( String[] test : new String[][] {
- {"if(arg) return 7; else return 2.5;", "No common type amongst int and f64"},
- {"struct S { int x; }; if(arg) return new S; else return 0;",
- "No common type amongst int and reference"}
- } ) {
- try {
- 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());
- }
- }
- }
-
-
- @Test public void testPrintingForwardReferenceScope() throws Exception {
- new CodeGen("return 0;").parse();
- var declared = TypeMemPtr.make(TypeStruct.make("PrintForward",true,true));
- var scope = new ScopeNode();
- scope.define("ptr",declared,false,new ConstantNode(TypeInteger.ZERO),null);
- Parser.TYPES.put("PrintForward",TypeMemPtr.make(TypeStruct.make("PrintForward",false,false)));
- var field = Var.class.getDeclaredField("_type");
- field.setAccessible(true);
- var v = scope.var(0);
- org.junit.Assert.assertSame(declared,field.get(v));
- scope.toString();
- org.junit.Assert.assertSame(declared,field.get(v));
- }
-
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen(
-"""
-return 0;
-""");
- code.driver(Phase.LocalSched);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 2));
- }
-
- @Test
- public void testPhiParalleAssign() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-int b = 2;
-while(arg--) {
- int t = a;
- a = b;
- b = t;
-}
-return a;
-""");
- 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));
- assertEquals("1", Eval2.eval(code, 2));
- assertEquals("2", Eval2.eval(code, 3));
- }
-
-
- // ---------------------------------------------------------------
- @Test
- public void testType0() {
- CodeGen code = new CodeGen(
-"""
-{int -> int}? x2 = null; // null function ptr
-return x2;
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return null;", code.print());
- assertEquals("null", Eval2.eval(code, 0 ) );
- }
-
- @Test
- public void testFcn0() {
- CodeGen code = new CodeGen(
-"""
-{int -> int}? sq = { int x ->
- x*x;
-};
-""");
- 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));
- }
-
- @Test
- public void testFcn1() {
- CodeGen code = new CodeGen(
-"""
-var _sq = { int x ->
- x*x;
-};
-return _sq(arg)+_sq(3);
-""");
- code.driver(Phase.LocalSched);
- assertEquals("return ((arg*arg)+9);", code.print());
- assertEquals("13", Eval2.eval(code, 2));
- }
-
- // Function scope test
- @Test
- public void testFcn2A() {
- CodeGen code = new CodeGen(
-"""
-int cnt=1; // Global scope; exactly one of these
-return { -> cnt++; }; // Escape global-scope variable is OK
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("Stop[ return { ptr -> i64 #[ 3]}; return .cnt; ]", code.print());
- assertEquals("{ ptr -> i64 #[ 3]}", Eval2.eval(code, 0));
- }
-
- // Function scope test
- @Test
- public void testFcn2B() {
- CodeGen code = new CodeGen(
-"""
-val ctr = { ->
- int cnt; // Function-local var
- return { -> cnt=cnt+1; cnt; }; // Escape cnt out of scope
-};
-val A = ctr(); // Make a counter
-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.driver(CodeGen.Phase.FinalizeIR); fail(); }
- catch( Exception e ) { assertEquals("Variable 'cnt' is out of function scope and must be a final constant",e.getMessage()); }
- }
-
- // Function scope test
- @Test
- public void testFcn3() {
- CodeGen code = new CodeGen(
-"""
-val cnt=2;
-return { -> cnt; }();
-""");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return 2;", code.print());
- assertEquals("2", Eval2.eval(code, 0));
- }
-
- // Function variables
- @Test
- public void testFcn4() {
- CodeGen code = new CodeGen(
-"""
-var fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
-return fcn(3);
-""");
- 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));
- }
-
- // Recursive factorial test
- @Test
- public void testFcn5() {
- CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1 : x*_fact(x-1); }; return _fact(arg);");
- 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)); ]") ||
- ir.equals("Stop[ return Phi(Region,1,(arg*Phi(Region,1,((arg-1)*#2)))); return Phi(Region,1,(Parm_x(_fact,(Sub-1),(x-1))*#2)); ]"));
- assertEquals( "1", Eval2.eval(code, 0));
- assertEquals( "1", Eval2.eval(code, 1));
- assertEquals( "2", Eval2.eval(code, 2));
- assertEquals( "6", Eval2.eval(code, 3));
- assertEquals("24", Eval2.eval(code, 4));
- }
-
- // The multiply initially sees one integer input and one unresolved
- // The multiply initially sees one integer input and one unresolvedfcn8
- // recursive result, but the floating base case requires a floating mode.
- @Test
- public void testFcn5Float() {
- CodeGen code = new CodeGen("val _fact = { int x -> x <= 1 ? 1.0 : x*_fact(x-1); }; return _fact(arg);");
- 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));
- assertEquals("6.0", Eval2.eval(code, 3));
- assertEquals("24.0", Eval2.eval(code, 4));
- }
-
- @Test
- public void testFcn5Minus() {
- CodeGen code = new CodeGen("val _neg = { int x -> x ? -_neg(x-1) : 1; }; return _neg(arg);");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals( "1", Eval2.eval(code, 0));
- assertEquals("-1", Eval2.eval(code, 1));
- assertEquals( "1", Eval2.eval(code, 2));
- assertEquals("-1", Eval2.eval(code, 3));
- }
-
- @Test
- public void testFcn6() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int i; new _S = { int x -> i=x; }; };
-val _newS = { int x -> return new _S(x); };
-return _newS(1).i;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- // Only forward refs to direct function calls allowed
- @Test
- public void testFcn7() {
- CodeGen code = new CodeGen(
-"""
-val _f = {->1;};
-val _g = {->2;};
-if( arg ? _f : _g ) return 1;
-return 2;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 0));
- }
-
- @Test
- public void testFcn8() {
- CodeGen code = new CodeGen(
-"""
-{int -> int}? !_i2i = null;
-var _id = {{int->int} f-> return f;};
-for(;;) {
- if (_i2i) return _i2i(arg);
- var _x = {int i-> return i;};
- arg = _x(3);
- _i2i = _id(_x);
-}
-""");
- code.driver(Phase.LocalSched);
- assertEquals("return Phi(Loop,arg,3);", code.print());
- assertEquals("3", Eval2.eval(code, 0));
- }
-
- @Test
- public void testFcn9() {
- CodeGen code = new CodeGen(
-"""
-{int -> int}? !i2i = null;
-for(;;) {
- if (i2i) return i2i(arg);
- var x = {int i-> return i;};
- arg = x(3);
-}
-""");
- code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Top; return Parm_i(x,i64); ]", code.print());
- assertNull( Eval2.eval( code, 0 ) ); // Infinite loop, <clinit> never exits
- }
-
-
- @Test
- public void testFcn10() {
- CodeGen code = new CodeGen(
-"""
-struct _Person {
- int age;
-};
-
-val _fcn = { !_Person?[] !ps, int x ->
- val tmp = ps[x];
- if( ps[x] )
- ps[x].age++;
-};
-
-var ps = new !_Person?[2];
-ps[0] = new _Person;
-ps[1] = new _Person;
-return _fcn(ps,1);
-""");
- code.driver(Phase.LocalSched);
- assertEquals("return .age;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- // Function break
- @Test
- public void testErr1() {
- CodeGen code = new CodeGen(
-"""
-for(;;) {
- val f = { ->
- break;
- };
- f();
- return 2;
-}
-return 1;
-""");
- try { code.driver(CodeGen.Phase.FinalizeIR); fail(); }
- catch( Exception e ) { assertEquals("No active loop for a break or continue",e.getMessage()); }
- }
-
- // Calling and inlining a null function
- @Test
- public void testErr2() {
- CodeGen code = new CodeGen(
-"""
-{int -> int}? !i2i = { int i -> return i; };
-for(;;) {
- if (i2i(2) == arg) break;
- i2i = null;
-}
-return 0;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Might be null calling { ptr i64 -> i64 #[ 3]}?",e.getMessage()); }
- }
-
-
- @Test
- public void testErr3() {
- CodeGen code = new CodeGen(
-"""
-val f = { int i, int j -> return i+j; };
-return f();
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Expecting 3 arguments, but found 1",e.getMessage()); }
- }
-
-
- @Test
- public void testErr4() {
- CodeGen code = new CodeGen(
-"""
-struct S {
- {int} f = { -> x(); return 0; }; // Do not let fref x be a field
-};
-val x = { -> return 1; };
-S? s = new S;
-return s.x;
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("Accessing unknown field 'x' from '*Test.S'",e.getMessage()); }
- }
-
- @Test @Ignore
- public void testErr5() {
- CodeGen code = new CodeGen(
-"""
-val g = { ->
- g();
-};
-return g();
-""");
- try { code.driver(CodeGen.Phase.TypeCheck); fail(); }
- catch( Exception e ) { assertEquals("No defined return type",e.getMessage()); }
- }
-
-
- // Mutual recursion. Fails without SCCP to lift the recursive return types.
- @Ignore @Test
- public void testFcnMutRec() {
- CodeGen code = new CodeGen(
-"""
-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.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));
- assertEquals("1", Eval2.eval(code, 2));
- assertEquals("0", Eval2.eval(code, 3));
- }
-
- // Forward ref to 'x' means that
- @Ignore @Test
- public void testForwardRef1() {
- CodeGen code = new CodeGen(
-"""
-struct S {
- { int } f = { -> return x(); };
-};
-val x = { -> return 1; };
-!S? !s = null;
-for(;;) {
- if (s) return s.f;
-}
-""");
- code.driver(CodeGen.Phase.FinalizeIR).driver(CodeGen.Phase.Schedule);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 0));
- }
-
- // Inline hidden called more than once
- @Test
- public void testInline() {
- CodeGen code = new CodeGen(
-"""
-{int->int}?! _i2i_noInline = {int i->return i;};
-{{int->int}->{int->int}}! _f2f = {{int->int} f->return f;};
-val _o = _i2i_noInline;
-if (arg) _i2i_noInline = null;
-if (_i2i_noInline) return _i2i_noInline(arg);
-return _f2f(_o)(1);
-""");
- code.driver(Phase.LocalSched);
- assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(_i2i_noInline,i64); ]", code.print());
- assertEquals("1", Eval2.eval(code, 2));
- }
-
- @Test
- public void testOperField() {
- CodeGen code = new CodeGen(
-"""
-struct _Person {
- int coffee_count;
-};
-!_Person !p = new _Person;
-p.coffee_count += 1;
-return p.coffee_count;
-""");
- code.driver(Phase.LocalSched);
- assertEquals("return 1;", code.print());
- assertEquals("1", Eval2.eval(code, 2));
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
deleted file mode 100644
index 6f290279..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ /dev/null
@@ -1,459 +0,0 @@
-package com.seaofnodes.simple;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.*;
-
-
-import com.seaofnodes.simple.codegen.CodeGen.Phase;
-import com.seaofnodes.simple.codegen.CodeGen;
-import java.io.IOException;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter19Test {
- @Test public void testPrintingFunctionLookup() {
- var code = new CodeGen("return 0;").parse();
- var tfp = TypeFunPtr.make1((byte)2,false,new Type[]{TypeInteger.constant(987654)},TypeInteger.constant(123456),30);
- var con = ConstantNode.raw(tfp);
- var call = new CallNode(null,code._start,con,con);
- // Diagnostic lookup may construct/intern the return-erased linker key.
- con.toString();
- org.junit.Assert.assertNull(call.name());
-
- var fun = new FunNode(null,tfp,"printerTarget",null);
- fun._name = "printerTarget";
- fun.addDef(code._start);
- code.link(fun);
- var otherReturn = tfp.makeFrom(TypeInteger.constant(654321));
- org.junit.Assert.assertSame(fun,code._link(otherReturn));
- org.junit.Assert.assertTrue(con.toString().contains("printerTarget"));
- assertEquals("printerTarget",call.name());
- }
-
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen("""
-return 0;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 2));
- }
-
-
- @Test
- public void testString() {
- String src =
-"""
-struct String {
- u8[~] !cs;
- int _hashCode;
- new String = { u8[~] s -> cs=s; };
-};
-
-// Compare two Strings
-val equals = { !String !self, !String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-
-val _hashCodeString = { !String self ->
- int hash=0;
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-hashCode(new String("Hello, World!"));
-""";
- CodeGen code = new CodeGen(src).driver(Phase.LocalSched);
- assertEquals("Stop[ return Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash)))); return MEM[ 2:___ 3:___ 4:___ 5:.cs=Parm_s(Test.String.String,*[]u8[final]); 6:._hashCode=0;]; return MEM[ 2:___ 3:___ 4:___ 5:___ 6:._hashCode=0;]; return Test.String; return Phi(Region,0,1,0,1); return Phi(Region,(!=0)._hashCode,Phi(Region,123456789,(!=0)Phi(Loop,0,(.[]+((Phi_hash<<5)-Phi_hash))))); ]", code.print());
- assertEquals("4029215624828139541", Eval2.eval(code, 2));
- }
-
- @Test
- public void testTernaryStoreCall() {
- String src =
-"""
-struct String {
- u8[~] !cs;
- int _hashCode;
- new String = { u8[~] s -> cs=s; };
-};
-
-val f = { !String self -> 7; };
-
-val hashCode = { !String self ->
- self._hashCode
- ? 3
- : (self._hashCode = f(self));
-};
-
-hashCode(new String("Hello"));
-""";
- CodeGen code = new CodeGen(src).driver(Phase.LocalSched);
- assertFalse(code.print().contains("return Top"));
- assertEquals("7", Eval2.eval(code, 2));
- }
-
- @Test
- public void testBasic0() {
- CodeGen code = new CodeGen("return 0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testBasic1() {
- CodeGen code = new CodeGen("return arg+1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (inc,arg);", code.print());
- }
-
- @Test
- public void testBasic2() {
- CodeGen code = new CodeGen("return -17;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return -17;", code.print());
- }
-
-
- @Test
- public void testBasic3() {
- CodeGen code = new CodeGen("return arg==1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (set==,(cmp,arg));", code.print());
- }
-
- @Test
- public void testBasic4() {
- CodeGen code = new CodeGen("return arg<<1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (shli,arg);", code.print());
- }
-
- @Test
- public void testBasic5() {
- CodeGen code = new CodeGen("return arg >> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (sari,arg);", code.print());
- }
-
- @Test
- public void testBasic6() {
- CodeGen code = new CodeGen("return arg >>> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (shri,arg);", code.print());
- }
-
- @Test
- public void testBasic7() {
- CodeGen code = new CodeGen("return arg / 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (div,arg,2);", code.print());
- }
-
- @Test
- public void testBasic8() {
- CodeGen code = new CodeGen("return arg * 6;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (muli,arg);", code.print());
- }
-
- @Test
- public void testBasic9() {
- CodeGen code = new CodeGen("return arg & 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (andi,arg);", code.print());
- }
-
- @Test
- public void testBasic10() {
- CodeGen code = new CodeGen("return arg | 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (ori,arg);", code.print());
- }
-
- @Test
- public void testBasic11() {
- CodeGen code = new CodeGen("return arg ^ 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (xori,arg);", code.print());
- }
-
- @Test
- public void testBasic12() {
- CodeGen code = new CodeGen("return arg + 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (addf,(cvtf,arg),2.0f);", code.print());
- }
-
- @Test
- public void testBasic13() {
- CodeGen code = new CodeGen("return arg - 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (subf,(cvtf,arg),2.0f);", code.print());
- }
-
- @Test
- public void testBasic14() {
- CodeGen code = new CodeGen("return arg * 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (mulf,(cvtf,arg),2.0f);", code.print());
- }
-
- @Test
- public void testBasic15() {
- CodeGen code = new CodeGen("return arg / 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (mulf,(cvtf,arg),0.5f);", code.print());
- }
-
- @Test
- public void testBasic16() {
- CodeGen code = new CodeGen(
-"""
-int arg1 = arg + 1;
-return arg1 / arg;""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (div,(inc,arg),arg);", code.print());
- }
-
- @Test
- public void testBasic17() {
- CodeGen code = new CodeGen(
-"""
-int arg1 = arg + 1;
-return arg1 * arg;
-""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (mul,(inc,arg),arg);", code.print());
- }
-
- @Test
- public void testToFloat() {
- CodeGen code = new CodeGen("""
-int a = arg;
-return a + 2.0;
-"""
- ).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (addf,(cvtf,arg),2.0f);", code.print());
- }
-
- @Test
- public void testIfStmt() {
- CodeGen code = new CodeGen(
-"""
-int a = 1;
-if (arg == 1)
- a = arg+2;
-else {
- a = arg-3;
-}
-return a;""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print());
- }
-
- @Test
- public void testIfMerge2() {
- CodeGen code = new CodeGen(
-"""
-int a=arg+1;
-int b=arg+2;
-if( arg==1 )
- b=b+a;
-else
- a=b+1;
-return a+b;""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print());
- }
-
- @Test
- public void testLoop() {
- CodeGen code = new CodeGen(
-"""
-int sum=0;
-for( int i=0; i<arg; i++ )
- sum += i;
-return sum;""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print());
- }
-
- @Test
- public void testAlloc1() {
- CodeGen code = new CodeGen(
-"""
-struct _S { int a; !_S? !c; };
-return new _S;""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return Test._S;", code.print());
- }
-
- @Test
- public void testLea1() {
- CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;");
- code.driver(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.driver(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.driver(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.driver(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.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return .[];", code.print());
- }
-
- @Test
- public void testArray1() {
- CodeGen code = new CodeGen(
-"""
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<ary#; i++ )
- ary[i] = i;
-// Fill [0,1,3,6,10,...]
-for( int i=0; i<ary#-1; i++ )
- ary[i+1] += ary[i];
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
-""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return .[];", code.print());
- }
-
- @Test
- public void testArray2() {
- CodeGen code = new CodeGen(
-"""
-flt[] !A = new flt[arg], !B = new flt[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<A#; i++ )
- A[i] = i;
-for( int i=0; i<A#; i++ )
- B[i] += A[i];
-return 0;
-""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return 0;", code.print());
- }
-
- @Test
- public void testArray3() {
- CodeGen code = new CodeGen(
-"""
-byte[] !A = new byte[arg];
-for( int i=0; i<A#; i++ )
- A[i]++;
-return A[1];
-""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return .[];", code.print());
- }
-
- @Test
- public void testNewton() throws IOException {
- String src =
-"""
-val _test_sqrt = { flt x ->
- flt epsilon = 1e-15;
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( guess-epsilon <= next & next <= guess+epsilon ) return guess;
- //if( guess==next ) return guess;
- guess = next;
- }
-};
-flt farg = arg;
-return _test_sqrt(farg);
-""";
- CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,Phi_guess,(divf,cvtf,Phi_guess)),0.5f));", code.print());
- };
-
-
- @Test
- public void sieveOfEratosthenes() {
- String src =
-"""
-val sieve = { int N ->
- // The main Sieve array
- bool[] !ary = new bool[N];
- // The primes less than N
- u32[] !primes = new u32[N>>1];
- // Number of primes so far, searching at index p
- int nprimes = 0, p=2;
- // Find primes while p^2 < N
- while( p*p < N ) {
- // skip marked non-primes
- while( ary[p] ) p++;
- // p is now a prime
- primes[nprimes++] = p;
- // Mark out the rest non-primes
- for( int i = p + p; i < ary#; i+= p )
- ary[i] = true;
- p++;
- }
-
- // Now just collect the remaining primes, no more marking
- for( ; p < N; p++ )
- if( !ary[p] )
- primes[nprimes++] = p;
-
- // Copy/shrink the result array
- u32[] !rez = new u32[nprimes];
- for( int j=0; j < nprimes; j++ )
- rez[j] = primes[j];
- return rez;
-};
-""";
- CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("Stop[ return { sieve}; return []u32; ]", code.print());
- //assertEquals("{ ptr i64 -> *[]u32 #[ 3]}",Eval2.eval(code, 20));
- }
-
-
- @Test
- public void testFcn1() {
- CodeGen code = new CodeGen(
-"""
-val _fcn = arg ? { int x -> x*x; } : { int x -> x+x; };
-return _fcn(2)*10 + _fcn(3);
-""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun3,[2-3]),x); return (shli,Parm_x($fun4,[2-3])); ]", code.print());
- }
-
- @Test
- public void testFcn2() {
- CodeGen code = new CodeGen(
-"""
-val _sq = { int x -> x*x; };
-return _sq(arg) + _sq(3);
-""");
- code.driver(Phase.LocalSched,"x86_64_v2", "SystemV");
- assertEquals("return (addi,(mul,arg,arg));", code.print());
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
deleted file mode 100644
index 0458941e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ /dev/null
@@ -1,307 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter20Test {
- @Test public void testAllocatorTopPhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.topPhi(); }
-
- @Test public void testNeverReturnAllocation() {
- String[] sources = {
- // A real integer return merged with a synthetic exit.
- "if(arg) while(1) {} return arg+1;",
- // TOP must not widen a non-null pointer or constrain a float register.
- "struct S { int x; }; !S !s=new S; if(arg) while(1) {} return s;",
- "if(arg) while(1) {} return 1.5;",
- // Several synthetic exits into the same return.
- "if(arg==1) while(1) {} if(arg==2) while(1) {} return arg+3;",
- // No real return, including a merge of only synthetic exits.
- "while(1) { arg+=1; }",
- "if(arg) while(1) {} while(1) {}",
- // Loop-carried memory and scalar self-conflicts exercise spilling.
- "struct S { int x; int y; }; !S !a=new S; if(arg) while(1) { a.x+=arg; a.y+=a.x; } return a.x+a.y;"
- };
- for( String target : new String[]{"x86_64_v2","riscv","arm"} )
- for( String src : sources ) {
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.RegAlloc,target,"SystemV");
- com.seaofnodes.simple.codegen.RegAllocTestSupport.checkRegisters(code);
- assertNotNull(code._stop.walk(n -> {
- if( n._type!=com.seaofnodes.simple.type.Type.TOP ) return null;
- assertEquals(-1,code._regAlloc.regnum(n));
- for( var use : n.outs() )
- assertFalse(use instanceof com.seaofnodes.simple.node.SplitNode);
- return n;
- }));
- code.encode();
- }
- }
-
- @Test public void testAllocatorFixedNeighbor() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.uncoloredFixedNeighbor(); }
-
- @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;")
- .driver(CodeGen.Phase.RegAlloc,"x86_64_v2","SystemV");
- int stores=0;
- for( var bb : code._cfg )
- for( var node : bb.outs() )
- if( node instanceof com.seaofnodes.simple.node.cpus.x86_64_v2.StoreX86 st && st._name.equals("x") ) {
- stores++;
- assertEquals(-1,st.regmap(4).nextReg((short)15));
- }
- assertTrue(stores>0);
- }
- }
-
- @Test public void testInlinedReturnValue() {
- String src = """
-struct S { int x; };
-val f = { !S s -> s.x = g(); };
-val g = { -> 123; };
-!S !s = new S;
-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).driver(CodeGen.Phase.TypeCheck);
- assertEquals("seed "+seed, "123", Eval2.eval(code,0));
- }
- }
-
-
- @Test public void testAllocatorMasks() { com.seaofnodes.simple.codegen.RegAllocTestSupport.masks(); }
- @Test public void testAllocatorUnion() { com.seaofnodes.simple.codegen.RegAllocTestSupport.union(); }
- @Test public void testAllocatorCopyClobber() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.copyClobber(); }
- @Test public void testAllocatorCommutativePhi() { com.seaofnodes.simple.codegen.RegAllocTestSupport.commutativePhi(); }
- @Test public void testAllocatorKills() { com.seaofnodes.simple.codegen.RegAllocTestSupport.killWithoutResult(); }
- @Test public void testAllocatorDependencies() { com.seaofnodes.simple.codegen.RegAllocTestSupport.nullUseMask(); }
- @Test public void testAllocatorCloneClass() { com.seaofnodes.simple.codegen.RegAllocTestSupport.cloneRegisterClass(); }
-
- @Test public void testPrintingRegisters() throws Exception {
- com.seaofnodes.simple.codegen.PrintRegTestSupport.check();
- }
-
-
- @Ignore
- @Test
- public void testJig() {
- CodeGen code = new CodeGen("return 0;");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 0;", code.print());
- assertEquals("0", Eval2.eval(code, 2));
- }
-
- static void testCPU( String src, String cpu, String os, int spills, String stop ) {
- CodeGen code = new CheckedCodeGen(src);
- code.driver(CodeGen.Phase.RegAlloc,cpu,os);
- int delta = spills>>3;
- if( delta==0 ) delta = 1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() )
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
- if( stop != null )
- assertEquals(stop, code.print());
- }
-
- private static void testAllCPUs( String src, int spills, String stop ) {
- testCPU(src,"x86_64_v2", "SystemV",spills,stop);
- testCPU(src,"riscv" , "SystemV",spills,stop);
- testCPU(src,"arm" , "SystemV",spills,stop);
- }
-
- @Test public void testAlloc0() {
- testCPU("return new u8[arg];","x86_64_v2", "SystemV",3,"return []u8;");
- testCPU("return new u8[arg];","riscv" , "SystemV",5,"return []u8;");
- testCPU("return new u8[arg];","arm" , "SystemV",5,"return []u8;");
- }
-
- @Test public void testBasic1() {
- String src = "return arg | 2;";
- testCPU(src,"x86_64_v2", "SystemV",1,"return mov((ori,arg));");
- testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );");
- testCPU(src,"arm" , "SystemV",0,"return (ori,arg);");
- }
-
- @Test
- public void testNewtonInteger() {
- String src =
-"""
-// Newtons approximation to the square root
-val _sqrt = { int x ->
- int guess = x;
- while( 1 ) {
- int next = (x/guess + guess)>>1;
- if( next == guess ) return guess;
- guess = next;
- }
-};
-return _sqrt(arg) + _sqrt(arg+2);
-""";
- testCPU(src,"x86_64_v2", "win64" ,38,null);
- testCPU(src,"riscv" , "SystemV",19,null);
- testCPU(src,"arm" , "SystemV",19,null);
- }
-
- @Test
- public void testNewtonFloat() {
- String src =
-"""
-val _test_sqrt_noInline = { flt x ->
- flt epsilon = 1e-15;
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( guess-epsilon <= next && next <= guess+epsilon )
- return guess;
- guess = next;
- }
-};
-flt farg = arg; return _test_sqrt_noInline(farg) + _test_sqrt_noInline(farg+2.0);
-""";
- testCPU(src,"x86_64_v2", "SystemV",40,null);
- testCPU(src,"riscv" , "SystemV",17,null);
- testCPU(src,"arm" , "SystemV",18,null);
- }
-
- @Test
- public void testIterPrettyPrintIsLocalAndReadOnly() {
- CodeGen code = new CodeGen("""
- val f_noInline = { flt x -> while(1) { if(x) return x; } };
- flt y=arg;
- return f_noInline(y)+f_noInline(y+2.0);
- """).driver(CodeGen.Phase.Iter);
- assertNull(code._start._ltree);
- int pre = code._start._pre;
-
- String p0 = code.toString();
- String p1 = code.toString();
- assertEquals(p0,p1);
- assertNull(code._start._ltree);
- assertEquals(pre,code._start._pre);
-
- int main0 = p0.indexOf("--- class:Test.<clinit>");
- int main1 = p0.indexOf("--- class:Test.<clinit> ----------------------",main0);
- int fun0 = p0.indexOf("--- f_noInline ",main1);
- int fun1 = p0.indexOf("--- f_noInline ----------------------",fun0);
- assertTrue(main0 < main1 && main1 < fun0 && fun0 < fun1);
- assertTrue(p0.indexOf("ToFloat",main0) < p0.indexOf("Call",main0));
- assertTrue(p0.indexOf("CallEnd",fun0) == -1 || p0.indexOf("CallEnd",fun0) > fun1);
- }
-
- @Test
- public void testAlloc2() {
- String src = "int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];";
- testCPU(src,"x86_64_v2", "SystemV",-1,"return .[];");
- testCPU(src,"riscv" , "SystemV", 6,"return .[];");
- testCPU(src,"arm" , "SystemV", 6,"return .[];");
- }
-
-
-
- @Test
- public void testArray1() {
- String src =
-"""
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<ary#; i++ )
- ary[i] = i;
-// Fill [0,1,3,6,10,...]
-for( int i=0; i<ary#-1; i++ )
- ary[i+1] += ary[i];
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
-""";
- testCPU(src,"x86_64_v2", "SystemV",-1,"return mov(.[]);");
- testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));");
- testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));");
- }
-
-
- @Test
- public void testString() {
- String src =
-"""
-struct String {
- u8[] !cs;
- int _hashCode;
- new String = { u8[] data -> cs=data; };
-};
-
-// Compare two Strings
-val equals = { !String !self, !String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !String self ->
- int hash=0;
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-""";
- testCPU(src,"x86_64_v2", "SystemV",21,null);
- testCPU(src,"riscv" , "SystemV",8,null);
- testCPU(src,"arm" , "SystemV",8,null);
- }
-
- @Test
- public void testCast() {
- String src = "struct Bar { int x; }; var b = arg ? new Bar; return b ? b.x++ + b.x++ : -1;";
- testCPU(src,"x86_64_v2", "SystemV",3,null);
- testCPU(src,"riscv" , "SystemV",6,null);
- testCPU(src,"arm" , "SystemV",6,null);
- }
-
- @Test
- public void testFltArg() {
- String src = "return {int i, flt f, int j->return i+f+j;};";
- testAllCPUs(src,0,null);
- }
-
- @Test
- public void testFlags1() {
- String src = """
-bool _b1 = arg == 1;
-bool b2 = arg == 2;
-if (b2) if (_b1) return 1;
-if (_b1) return 2;
-return 0;
-""";
- testCPU(src,"x86_64_v2", "SystemV",0,"return Phi(Region,2,0,1);");
- testCPU(src,"riscv" , "SystemV",0,"return Phi(Region,2,0,1);");
- testCPU(src,"arm" , "SystemV",0,"return Phi(Region,2,0,1);");
- }
-
- @Test
- public void testFlags2() {
- String src = """
-bool b1 = arg == 1;
-while (arg > 0) {
- arg--;
- if (b1) arg--;
-}
-return arg;
-""";
- testCPU(src,"x86_64_v2", "SystemV",3,null);
- testCPU(src,"riscv" , "SystemV",2,null);
- testCPU(src,"arm" , "SystemV",2,null);
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
deleted file mode 100644
index 07ae42eb..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ /dev/null
@@ -1,715 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.cpus.arm.arm;
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
-import com.seaofnodes.simple.util.SB;
-import java.io.IOException;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter21Test {
-
- @Test public void testFloatToInteger() throws IOException {
- String src = "val float_to_int={ flt x -> int i=x; return i; };"+
- "val float_to_u8={ flt x -> u8 i=x; return i; };";
- TestC.runC(src,"floatToInteger","",-1);
- for( double x : new double[]{0.0,-0.0,6.92,-6.92,255.75,-257.75,
- 0x1p63,Math.nextDown(0x1p63),-0x1p63,Math.nextDown(-0x1p63),
- Double.POSITIVE_INFINITY,Double.NEGATIVE_INFINITY,Double.NaN} ) {
- for( String name : new String[]{"float_to_int","float_to_u8"} ) {
- long expected = name.equals("float_to_u8") ? ((long)x)&255 : (long)x;
- EvalRisc5 r = TestRisc5.build(src,name,0,-1,false);
- r.fregs[riscv.FA0-riscv.F_OFFSET]=x;
- assertEquals(0,r.step(1000));
- assertEquals(name+"("+x+")",expected,r.regs[riscv.A0]);
- EvalArm64 a = TestArm64.build(name,src,0,-1,false);
- a.fregs[arm.D0-arm.D_OFFSET]=x;
- assertEquals(0,a.step(1000));
- assertEquals(name+"("+x+")",expected,a.regs[arm.X0]);
- }
- }
- }
-
- @Test public void testReadBeforeConditionalStore() {
- var code = new CodeGen("""
- struct S { int x; }; !S !a=new S; !S !b=new S;
- a.x=10; b.x=20; !S !p=a; if(arg&1) p=b;
- int sum=0;
- for(int i=0; i<6; i++) {
- sum+=a.x;
- if(i==2) p.x=30;
- else if(i==4) b.x=40;
- }
- return sum;
- """).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));
- }
-
-
- @Test public void testArmBitfieldExtracts() {
- // Independent SXT/UXT instruction words, plus ASR, LSR and LSL aliases.
- int[] ops = {0x93401c20,0xd3401c20,0x93403c20,0xd3403c20,
- 0x93407c20,0xd3407c20,0x9344fc20,0xd344fc20,0xd37cec20};
- long value = 0xa5a5a5a580008080L;
- long[] expected = {(byte)value,value&255,(short)value,value&65535,
- (int)value,value&0xffffffffL,value>>4,value>>>4,value<<4};
- for( int i=0; i<ops.length; i++ ) {
- EvalArm64 cpu = new EvalArm64(new byte[1<<16],1<<15);
- cpu.st4(0,ops[i]); cpu.regs[1]=value;
- assertEquals(0,cpu.step(1));
- assertEquals(expected[i],cpu.regs[0]);
- }
- }
-
-
- @Test public void testArmFloatMemory() throws IOException {
- for( String type : new String[]{"f32","f64"} ) {
- String src = type+"[] !a=new "+type+"[3]; a[arg]=1.25; return a[1];";
- EvalArm64 cpu = TestArm64.build("probe","val probe={ int arg -> "+src+" };",1,-1,false);
- cpu.regs[arm.X0]=1;
- assertEquals(0,cpu.step(1000));
- assertEquals(1.25,cpu.fregs[0],0);
- }
- // Independently exercise both SIMD widths and addressing modes. Keep
- // the GPR value different so accidentally reading that bank is visible.
- for( boolean wide : new boolean[]{false,true} )
- for( boolean indexed : new boolean[]{false,true} ) {
- EvalArm64 cpu = new EvalArm64(new byte[512],256);
- cpu.regs[1]=128; cpu.regs[2]=16; cpu.regs[3]=99;
- cpu.fregs[3]=-13.25;
- int store = (wide ? 0xFD000000 : 0xBD000000) | ((16/(wide?8:4))<<10) | (1<<5) | 3;
- if( indexed ) store=(wide ? 0xFC226823 : 0xBC226823); // STR D3/S3,[X1,X2]
- cpu.st4(0,store);
- cpu.st4(4,store | (1<<22)); // LDR to the same SIMD register.
- assertEquals(0,cpu.step(1));
- assertEquals(-13.25,wide ? cpu.ld8f(144) : cpu.ld4f(144),0);
- cpu.fregs[3]=0;
- assertEquals(0,cpu.step(1));
- assertEquals(-13.25,cpu.fregs[3],0);
- }
- }
-
-
- @Test public void testArmCallInstructions() {
- EvalArm64 cpu = new EvalArm64(new byte[512],256);
- for( int delta : new int[]{4,32,-4} ) {
- cpu._pc = 128;
- for( int i=0; i<32; i++ ) cpu.regs[i]=1000+i;
- cpu.st4(128,0x94000000 | ((delta>>2)&0x03FFFFFF)); // BL
- assertEquals(0,cpu.step(1));
- assertEquals(128+delta,cpu._pc);
- for( int i=0; i<32; i++ ) assertEquals(i==30 ? 132 : 1000+i,cpu.regs[i]);
- }
- for( int rn : new int[]{9,30} )
- for( boolean call : new boolean[]{false,true} ) {
- cpu._pc=4;
- cpu.regs[rn]=64;
- cpu.st4(4,(call ? 0xD63F0000 : 0xD65F0000) | (rn<<5)); // BLR / RET
- assertEquals(0,cpu.step(1));
- assertEquals(64,cpu._pc);
- if( call ) assertEquals(8,cpu.regs[30]);
- }
- }
-
- @Test public void testArmImmediateArithmetic() {
- EvalArm64 cpu = new EvalArm64(new byte[16],16);
- // Unsigned imm12, including bit 11 and the optional 12-bit shift.
- for( int[] test : new int[][]{{0x91200020,2148},{0xD1200020,-1948},
- {0x91400420,4196},{0xD1400420,-3996}} ) {
- cpu._pc=0; cpu.regs[1]=100;
- cpu.N=true; cpu.Z=false; cpu.C=true; cpu.V=true;
- cpu.st4(0,test[0]);
- assertEquals(0,cpu.step(1));
- assertEquals(test[1],cpu.regs[0]);
- assertTrue(cpu.N); assertFalse(cpu.Z); assertTrue(cpu.C); assertTrue(cpu.V);
- }
- }
-
- @Test public void testArmCallsAndFrames() throws IOException {
- String src = """
- val sum = { int n ->
- if( n<=0 ) return 1;
- return sum(n-1)+n;
- };
- val run = { int n -> return sum(n)+sum(n+1); };
- """;
- assertTrue("Ordinary calls must survive optimization",checkArmCalls(src,"run",4,27)>0);
- }
-
- @Test public void testArmVectorGrowth() throws IOException {
- String src = """
- struct _Vec {
- u32 !len;
- int[] !buf;
- new _Vec = { int[] initial -> buf=initial; };
- val grow = { int sz ->
- var buf2 = new int[sz];
- for( int i=0; i<len; i++ ) buf2[i]=buf[i];
- buf=buf2;
- };
- val add = { int e ->
- if( len>=buf# ) grow(buf#*2);
- buf[len++]=e;
- return self;
- };
- };
- val run = { int arg ->
- val v = new _Vec(new int[4]).add(2).add(3).add(5).add(7).add(11);
- return v.len*100+v.buf[arg];
- };
- """;
- checkArmCalls(src,"run",4,511);
- }
-
- private static int checkArmCalls(String src, String entryName, int arg, int result) throws IOException {
- CodeGen code = new CodeGen(src).driver("arm","SystemV",true,false);
- byte[] image = new byte[1<<20];
- System.arraycopy(code._encoding.bits(),0,image,0,code._encoding.bits().length);
- EvalArm64 cpu = new EvalArm64(image,1<<16);
- boolean entry=false;
- int frames=0, calls=0;
- for( var bb : code._cfg ) {
- if( bb instanceof com.seaofnodes.simple.node.FunNode fun ) {
- int off = code._encoding.opStart(fun);
- if( entryName.equals(fun._name) ) { cpu._pc=off; entry=true; }
- int frame=fun._frameAdjust;
- assertEquals(0,frame&15);
- if( frame>0 ) {
- frames++;
- assertEquals(0xD10003FF | (frame<<10),cpu.ld4s(off)); // SUB SP,SP,#bytes
- }
- }
- if( bb instanceof com.seaofnodes.simple.node.ReturnNode ret ) {
- int off=code._encoding.opStart(ret);
- int frame=ret.fun()._frameAdjust;
- if( frame>0 ) {
- assertEquals(0x910003FF | (frame<<10),cpu.ld4s(off)); // ADD SP,SP,#bytes
- off+=4;
- }
- assertEquals(0xD65F03C0,cpu.ld4s(off)); // RET X30
- }
- if( bb instanceof com.seaofnodes.simple.node.cpus.arm.CallARM ) calls++;
- }
- assertTrue("Must execute the program entry",entry);
- assertTrue("Exercise stack frames",frames>0);
- for( int i=19; i<=29; i++ ) cpu.regs[i]=1000+i;
- cpu.regs[0]=arg;
- assertEquals(0,cpu.step(10000));
- assertEquals("Must return to the harness",0,cpu._pc);
- assertEquals(result,cpu.regs[0]);
- assertEquals(1<<16,cpu.regs[31]);
- for( int i=19; i<=29; i++ ) assertEquals(1000+i,cpu.regs[i]);
- return calls;
- }
-
-
- @Test public void testCoalescing() { com.seaofnodes.simple.codegen.RegAllocTestSupport.coalescing(); }
- @Test public void testCopyForward() throws Exception { com.seaofnodes.simple.codegen.RegAllocTestSupport.copyForward(); }
-
- @Test public void testArmSubtractRegisters() {
- EvalArm64 cpu = new EvalArm64(new byte[16],16);
- cpu.st4(0,0xCB020020); // SUB X0,X1,X2, no shift or flag update.
- for( long[] pair : new long[][]{{7,3},{3,7},{Long.MIN_VALUE,1},{1L<<40,3}} ) {
- cpu._pc = 0;
- cpu.regs[1] = pair[0]; cpu.regs[2] = pair[1];
- cpu.N=true; cpu.Z=false; cpu.C=true; cpu.V=true;
- assertEquals(0,cpu.step(1));
- assertEquals(pair[0]-pair[1],cpu.regs[0]);
- assertTrue(cpu.N); assertFalse(cpu.Z); assertTrue(cpu.C); assertTrue(cpu.V);
- }
- }
-
-
- @Test public void testRisc64BitStore() {
- byte[] mem = new byte[24];
- EvalRisc5 cpu = new EvalRisc5(mem,mem.length);
- check64BitStore(mem,cpu::st8,cpu::ld8);
- }
-
- @Test public void testArm64BitStore() {
- byte[] mem = new byte[24];
- EvalArm64 cpu = new EvalArm64(mem,mem.length);
- check64BitStore(mem,cpu::st8,cpu::ld8);
- }
-
- // Check bytes independently of ld8, including overwrite and adjacent memory.
- private static void check64BitStore(byte[] mem,
- java.util.function.BiConsumer<Integer,Long> store,
- java.util.function.IntToLongFunction load) {
- java.util.Arrays.fill(mem,(byte)0xA5);
- for( long value : new long[]{0x0123456789ABCDEFL,0xFEDCBA9876543210L,
- -1L,0L,Long.MIN_VALUE,Long.MAX_VALUE} ) {
- store.accept(8,value);
- for( int i=0; i<mem.length; i++ ) {
- int expected = i>=8 && i<16 ? (int)(value >>> (8*(i-8)))&0xFF : 0xA5;
- assertEquals("byte "+i,expected,mem[i]&0xFF);
- }
- assertEquals(value,load.applyAsLong(8));
- }
- }
-
-
- @Ignore
- @Test
- public void testJig() throws IOException {
- String src =
-"""
-struct _s0 {
- bool v1;
- i16 v2;
- int v3;
- i8 v4;
- byte v5;
-};
-while(new _s0.v3)
- while(new _s0.v5<<new _s0.v4) {}
-if(0) {
- if(0) {
- flt !P5ZUD4=new _s0.v2;
- }
- while(0) {}
-}
-return new _s0.v1;
-""";
- testCPU(src,"x86_64_v2", "win64" ,-1,null);
- testCPU(src,"riscv" , "SystemV",-1,null);
- testCPU(src,"arm" , "SystemV",-1,null);
- }
-
- static void testCPU( String src, String cpu, String os, int spills, String stop ) {
- CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
- int delta = spills>>3;
- if( delta==0 ) delta = 1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() )
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
- if( stop != null )
- assertEquals(stop, code.print());
- }
-
-
- @Test public void testBasic1() {
- String src = "return arg | 2;";
- testCPU(src,"x86_64_v2", "SystemV",1,"return mov((ori,arg));");
- testCPU(src,"riscv" , "SystemV",0,"return ( arg | #2 );");
- testCPU(src,"arm" , "SystemV",0,"return (ori,arg);");
- }
-
- @Test public void testInfinite() {
- String src = "struct _S { int i; }; !_S !s = new _S; while(1) s.i++; return s.i;";
- testCPU(src,"x86_64_v2", "SystemV",0,"return Top;");
- testCPU(src,"riscv" , "SystemV",0,"return Top;");
- testCPU(src,"arm" , "SystemV",0,"return Top;");
- }
-
- @Test
- public void testArray1() {
- String src =
-"""
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<ary#; i++ )
- ary[i] = i;
-// Fill [0,1,3,6,10,...]
-for( int i=0; i<ary#-1; i++ )
- ary[i+1] += ary[i];
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
-""";
- testCPU(src,"x86_64_v2", "SystemV",-1,"return mov(.[]);");
- testCPU(src,"riscv" , "SystemV", 7,"return (add,.[],(mul,.[],1000));");
- testCPU(src,"arm" , "SystemV", 5,"return (add,.[],(mul,.[],1000));");
- }
-
- @Test
- public void testAntiDeps1() {
- String src =
-"""
-struct _S { int f; };
-var v0 = new _S;
-!_S? !v1;
-if (arg) v1 = new _S;
-if (v1) {
- v0.f = v1.f;
-} else {
- v0.f = 2;
-}
-return v0;
-""";
- testCPU(src,"x86_64_v2", "SystemV", 9,"return mov(mov(Test._S));");
- testCPU(src,"riscv" , "SystemV",10,"return mov(mov(Test._S));");
- testCPU(src,"arm" , "SystemV",11,"return mov(mov(Test._S));");
- }
-
- @Test
- public void testString() {
- String src =
-"""
-struct String {
- u8[] !cs;
- int _hashCode;
- new String = { u8[] data -> cs=data; };
-};
-
-// Compare two Strings
-val equals = { !String !self, !String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !String self ->
- int hash=0;
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-""";
- testCPU(src,"x86_64_v2", "SystemV",21,null);
- testCPU(src,"riscv" , "SystemV",8,null);
- testCPU(src,"arm" , "SystemV",8,null);
- }
-
- @Test public void testStringExport() throws IOException {
- String src =
-"""
-struct String {
- u8[] !cs;
- int _hashCode;
- new String = { u8[] data -> cs=data; };
-};
-
-// Compare two Strings
-val equals = { !String !self, !String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !String self ->
- int hash=0;
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-""";
- TestC.runC(src, "stringHash", "",21);
- }
-
- @Test public void testLoop2() {
- String src =
-"""
-int i = 0;
-while(true) {
- i += 1;
- if( i==0 ) continue;
- if( i==1 ) continue;
- if( i==2 ) break;
- continue;
-}
-return i;
-""";
- testCPU(src,"x86_64_v2", "win64" ,0,"return (!=0)(inc,Phi(Loop,0,Phi(Region,(!=0),(!=0),0)));");
- testCPU(src,"riscv" , "SystemV",0,"return (!=0)( Phi(Loop,0,Phi(Region,(!=0),(!=0),0)) + #1 );");
- testCPU(src,"arm" , "SystemV",0,"return (!=0)(inc,Phi(Loop,0,Phi(Region,(!=0),(!=0),0)));");
- }
-
- @Test public void testNewtonExport() throws IOException {
- String result = """
-0 0.000000 (0)
-1 1.000000 (0)
-2 1.414214 (2.22045e-16)
-3 1.732051 (0)
-4 2.000000 (0)
-5 2.236068 (0)
-6 2.449490 (0)
-7 2.645751 (0)
-8 2.828427 (4.44089e-16)
-9 3.000000 (0)
-""";
- String src =
-"""
-val test_sqrt = { flt x ->
- flt epsilon = 1e-15;
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( guess-epsilon <= next & next <= guess+epsilon ) return guess;
- //if( guess==next ) return guess;
- guess = next;
- }
-};
-""";
- TestC.runC(src, "newtonFloat", result, TestC.CALL_CONVENTION.equals("win64") ? 42 : 34);
-
- EvalRisc5 R5 = TestRisc5.build( src, "test_sqrt", 0, 10, false);
- R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 3.0;
- int trap_r5 = R5.step(1000);
- assertEquals(0,trap_r5);
- // Return register A0 holds fib(8)==55
- assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001);
-
- // arm
- EvalArm64 A5 = TestArm64.build("test_sqrt", src,0, 10, false);
- A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0;
- int trap_arm = A5.step(1000);
- assertEquals(0,trap_arm);
- assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001);
- }
-
-
- @Test public void testSieve() throws IOException {
- String src =
-"""
-val sieve = { int N ->
- // The main Sieve array
- bool[] !ary = new bool[N];
- // The primes less than N
- u32[] !primes = new u32[N>>1];
- // Number of primes so far, searching at index p
- int nprimes = 0, p=2;
- // Find primes while p^2 < N
- while( p*p < N ) {
- // skip marked non-primes
- while( ary[p] ) p++;
- // p is now a prime
- primes[nprimes++] = p;
- // Mark out the rest non-primes
- for( int i = p + p; i < ary#; i+= p )
- ary[i] = true;
- p++;
- }
-
- // Now just collect the remaining primes, no more marking
- for( ; p < N; p++ )
- if( !ary[p] )
- primes[nprimes++] = p;
-
- // Copy/shrink the result array
- u32[] !rez = new u32[nprimes];
- for( int j=0; j < nprimes; j++ )
- rez[j] = primes[j];
- return rez;
-};
-""";
- // The primes
- int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, };
- SB sb = new SB().p(primes.length).p("[");
- for( int prime : primes )
- sb.p(prime).p(", ");
- String sprimes = sb.p("]").toString();
-
- // Compile, link against native C; expect the above string of primes to be printed out by C
- TestC.runC(src, "sieve", sprimes, 186);
-
- // Evaluate on RISC5 emulator; expect return of an array of primes in
- // the simulated heap.
- EvalRisc5 R5 = TestRisc5.build( src, "sieve", 100, 90, false);
- int trap = R5.step(10000);
- assertEquals(0,trap);
- // Return register A0 holds sieve(100)
- int ary = (int)R5.regs[riscv.A0];
- // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4)
- assertEquals(primes.length, R5.ld4s(ary));
- for( int i=0; i<primes.length; i++ )
- assertEquals(primes[i], R5.ld4s(ary + 4 + i*4));
-
- // Evaluate on ARM5 emulator; expect return of an array of primes in
- // the simulated heap.
- EvalArm64 A5 = TestArm64.build("sieve", src, 100, 88, false);
- int trap_arm = A5.step(10000);
- assertEquals(0, trap_arm);
- int ary_arm = (int)A5.regs[arm.X0];
- // Memory layout starting at ary(length,pad, prime1, primt2, prime3, prime4)
- assertEquals(primes.length, A5.ld4s(ary_arm));
- for( int i = 0; i<primes.length; i++ )
- assertEquals(primes[i], A5.ld4s(ary_arm + 4 + i * 4));
- }
-
- @Test public void testFibExport() throws IOException {
- String src =
-"""
-val fib = { int n ->
- int f1=1;
- int f2=1;
- while( n-- > 1 ){
- int temp = f1+f2;
- f1=f2;
- f2=temp;
- }
- return f2;
-};
-""";
- String fib = "[1, 1, 2, 3, 5, 8, 13, 21, 34, 55]";
- TestC.runC(src, "fib", fib, 24);
-
- EvalRisc5 R5 = TestRisc5.build( src, "fib", 9, 17, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- // Return register A0 holds fib(8)==55
- assertEquals(55,R5.regs[riscv.A0]);
-
- // arm
- EvalArm64 A5 = TestArm64.build("fib", src, 9, 17, false);
- int trap_arm = A5.step(100);
- assertEquals(0,trap_arm);
- // Return register X0 holds fib(8)==55
- assertEquals(55, A5.regs[arm.X0]);
- }
-
- @Test public void testPerson() throws IOException {
- String src =
- """
- struct Person {
- i32 age;
- };
-
- val fcn = { !Person?[] !ps, int x ->
- if( ps[x] )
- ps[x].age++;
- };
- """;
- String person = "6\n";
- TestC.run(src, "person1", null, TestC.CALL_CONVENTION, "", TestC.C_DRIVERS_DIR+"person.c", person, 2);
-
- // Memory layout starting at PS:
- int ps = 1<<16; // Person array pointer starts at heap start
- // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8
- // P0 = { age } // sizeof=8
- int p0 = ps+4*8+0*8;
- // P1 = { age } // sizeof=8
- int p1 = ps+4*8+1*8;
- // P2 = { age } // sizeof=8
- int p2 = ps+4*8+2*8;
- EvalRisc5 R5 = TestRisc5.build( src, "fcn", ps, 2, false);
- R5.regs[riscv.A1] = 1; // Index 1
- R5.st8(ps,3); // Length
- R5.st8(ps+1*8,p0);
- R5.st8(ps+2*8,p1);
- R5.st8(ps+3*8,p2);
- R5.st8(p0, 5); // age= 5
- R5.st8(p1,17); // age=17
- R5.st8(p2,60); // age=60
-
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals( 5+0,R5.ld8(p0));
- assertEquals(17+1,R5.ld8(p1));
- assertEquals(60+0,R5.ld8(p2));
-
- EvalArm64 A5 = TestArm64.build("fcn", src, ps, 2, false);
- A5.regs[arm.X1] = 1; // Index 1
- A5.st8(ps, 3);
- A5.st8(ps+1*8,p0);
- A5.st8(ps+2*8,p1);
- A5.st8(ps+3*8,p2);
- A5.st8(p0, 5); // age= 5
- A5.st8(p1,17); // age=17
- A5.st8(p2,60); // age=60
-
- int trap_arm = A5.step(100);
- assertEquals(0,trap_arm);
- assertEquals( 5+0, A5.ld8(p0));
- assertEquals(17+1, A5.ld8(p1));
- assertEquals(60+0, A5.ld8(p2));
- }
-
- @Test public void testArgCount() throws IOException {
- // Test passes more args than registers in Sys5, which is far, far more
- // than what win64 allows - so win64 gets a lot more spills here.
- String src =
-"""
-val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 ->
- return
- i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 +
- i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ;
-};
-""";
- String arg_count = "191.000000\n";
-
- TestC.runC(src, "arg_count", arg_count, TestC.CALL_CONVENTION.equals("win64") ? 35 : 9);
-
- EvalRisc5 R5 = TestRisc5.build( src, "addAll", 0, 4, false);
-
- // Todo: handle stack(imaginary stack in emulator)
- // pass in float arguments
- R5.fregs[riscv.FA0 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA1 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA2 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA3 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA4 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA5 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA6 - riscv.F_OFFSET] = 1.1;
- R5.fregs[riscv.FA7 - riscv.F_OFFSET] = 1.1;
-
- R5.regs[riscv.A0] = 2;
- R5.regs[riscv.A1] = 2;
- R5.regs[riscv.A2] = 2;
- R5.regs[riscv.A3] = 2;
- R5.regs[riscv.A4] = 2;
- R5.regs[riscv.A5] = 2;
- R5.regs[riscv.A6] = 2;
-
- int trap = R5.step(100);
- assertEquals(0,trap);
-
- double result = R5.fregs[riscv.FA0 - riscv.F_OFFSET];
-
- assertEquals(22.8, result, 0.00001);
-
- // arm
- EvalArm64 A5 = TestArm64.build("addAll", src, 0, 4, false);
-
- A5.fregs[arm.D0 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D1 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D2 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D3 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D4 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D5 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D6 - arm.D_OFFSET] = 1.1;
- A5.fregs[arm.D7 - arm.D_OFFSET] = 1.1;
-
- A5.regs[arm.X1] = 2;
- A5.regs[arm.X2] = 2;
- A5.regs[arm.X3] = 2;
- A5.regs[arm.X4] = 2;
- A5.regs[arm.X5] = 2;
- A5.regs[arm.X6] = 2;
- A5.regs[arm.X7] = 2;
-
- int trap_arm = A5.step(100);
- assertEquals(0,trap_arm);
-
- double result1 = A5.fregs[arm.D0 - arm.D_OFFSET];
- assertEquals(22.8, result1, 0.00001);
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
deleted file mode 100644
index 00d77f65..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter22Test.java
+++ /dev/null
@@ -1,374 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.util.Utils;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.cpus.arm.arm;
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
-import java.io.IOException;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter22Test {
-
- @Test public void testNarrowCReturnsEmulated() {
- String[] types = {"i8","u8","i16","u16","i32","u32","int"};
- long[] values = {0xa5a5a5a500000000L,0xa5a5a5a50000007fL,0xa5a5a5a500000080L,
- 0xa5a5a5a500007fffL,0xa5a5a5a500008000L,0xa5a5a5a57fffffffL,
- 0xa5a5a5a580000000L,0xa5a5a5a5ffffffffL};
- for( String target : new String[]{"arm","riscv"} )
- for( int i=0; i<types.length; i++ ) {
- String src = "{"+types[i]+"} c_narrow=\"C\"; return c_narrow();";
- CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,target,"SystemV");
- var enc = code._encoding;
- // Link the external call to a tiny independent native stub.
- var call = code._stop.walk(n -> n instanceof com.seaofnodes.simple.node.CallNode ? n : null);
- int start = enc.opStart(call);
- ((com.seaofnodes.simple.codegen.RIPRelSize)call).patch(enc,start,enc.opLen(call),0x1000-start);
- for( long value : values ) {
- long expected = switch(i) {
- case 0 -> (byte)value; case 1 -> value&255;
- case 2 -> (short)value; case 3 -> value&65535;
- case 4 -> (int)value; case 5 -> value&0xffffffffL;
- default -> value;
- };
- byte[] image = new byte[1<<16];
- byte[] bits = enc.bits();
- System.arraycopy(bits,0,image,0,bits.length);
- if( target.equals("arm") ) {
- EvalArm64 cpu = new EvalArm64(image,1<<15);
- // MOVZ X0,0x1100; LDR X0,[X0]; RET X30.
- cpu.st4(0x1000,0xd2822000); cpu.st4(0x1004,0xf9400000);
- cpu.st4(0x1008,0xd65f03c0);
- cpu.st8(0x1100,value);
- assertEquals(0,cpu.step(1000));
- assertEquals(0,cpu._pc);
- assertEquals(types[i],expected,cpu.regs[0]);
- } else {
- EvalRisc5 cpu = new EvalRisc5(image,1<<15);
- // AUIPC a0,0; LD a0,12(a0); RET.
- cpu.st4(0x1000,0x00000517); cpu.st4(0x1004,0x00c53503);
- cpu.st4(0x1008,0x00008067);
- // RV64 widens even u32 returns by sign-extending bit 31.
- cpu.st8(0x100c,i==6 ? value : (int)expected);
- assertEquals(0,cpu.step(1000));
- assertEquals(0,cpu._pc);
- assertEquals(types[i],expected,cpu.regs[riscv.A0]);
- }
- }
- }
- }
-
- @Test public void testNarrowCReturns() throws IOException {
- String src = """
- {i8} c_i8="C"; {u8} c_u8="C";
- {i16} c_i16="C"; {u16} c_u16="C";
- {i32} c_i32="C"; {u32} c_u32="C";
- {int} c_i64="C";
- val test_i8={->return c_i8();};
- val test_u8={->return c_u8();};
- val test_i16={->return c_i16();};
- val test_u16={->return c_u16();};
- val test_i32={->return c_i32();};
- val test_u32={->return c_u32();};
- val test_i64={->return c_i64();};
- val test_cmp={->return c_i32()==-1;};
- """;
- for( String conv : new String[]{"SystemV","win64"} )
- TestC.run(src,"c_returns"+conv,null,conv,
- conv.equals("SystemV") ? "sysv_abi" : "ms_abi",
- TestC.C_DRIVERS_DIR+"c_returns.c","",-1);
- }
-
-
- @Test public void testSubZeroTypeError() {
- try {
- 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());
- }
- }
-
-
- @Ignore
- @Test
- public void testJig() throws IOException {
- String src = """
-struct s0 {
- bool v1;
- i16 v2;
- int v3;
- i8 v4;
- byte v5;
-};
-while(new s0.v3)
- while(new s0.v5<<new s0.v4) {}
-if(0) {
- if(0) {
- flt !P5ZUD4=new s0.v2;
- }
- while(0) {}
-}
-return new s0.v1;
-""";
- testCPU(src,"x86_64_v2", "win64" ,-1,null);
- testCPU(src,"riscv" , "SystemV",-1,null);
- testCPU(src,"arm" , "SystemV",-1,null);
- }
-
- static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) {
- CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
- int delta = spills>>3;
- if( delta==0 ) delta = 1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() )
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
- if( stop != null )
- assertEquals(stop, code.print());
- return code;
- }
-
-
- static int testCPUSize( String src, String cpu, String os, int spills ) {
- // Includes the file initializer's hidden $init=true store.
- return testCPU(src,cpu,os,spills, "return 0;" )._encoding._bits.size();
- }
-
- // Should not fold away
- @Test public void testSextFail() throws IOException {
- String src = """
-struct _Person { i32 age;};
-!_Person !p = new _Person;
-p.age = (arg<<17)>>17;
-return 0;
-""";
- assertEquals(76, testCPUSize(src, "x86_64_v2","win64", 3 ));
- assertEquals(92, testCPUSize(src, "riscv", "SystemV", 4 ));
- assertEquals(92, testCPUSize(src, "arm" , "SystemV", 4 ));
-
- // do assertEquals here
- EvalRisc5 R5 = TestRisc5.build( src, "sext_str_not_fold_away", 0, 4, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
-
- EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away", src, 0, 4, false);
- trap = A5.step(100);
- assertEquals(0,trap);
-
- }
-
- // Should not fold away
- @Test public void testSextFail2() throws IOException {
- String src = """
- struct _Person { i32 age;};
- !_Person !p = new _Person;
- p.age = (arg<<48)>>48;
- return 0;
- """;
-
- EvalRisc5 R5 = TestRisc5.build( src, "sext_str_not_fold_away_2", 0, 4, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
-
- EvalArm64 A5 = TestArm64.build("sext_str_not_fold_away_2", src, 0, 3, false);
- int trap_arm = A5.step(100);
- assertEquals(0,trap_arm);
-
- assertEquals(76, testCPUSize(src, "x86_64_v2","win64",3 ));
- assertEquals(92, testCPUSize(src, "riscv", "SystemV",4 ));
- assertEquals(92, testCPUSize(src, "arm", "SystemV",4 ));
-
- }
-
- // Should fold away
- @Test public void testSextSuccess() throws IOException {
- String src = """
-// Should fold away sign extend
-struct _Person { i8 age;};
-!_Person !p = new _Person;
-p.age = (arg<<48)>>48;
-return 0;
-""";
-
- EvalRisc5 R5 = TestRisc5.build( src, "sext_str_fold_away", 0, 5, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
-
- EvalArm64 A5 = TestArm64.build("sext_str_fold_away", src, 0, 5, false);
- int trap_arm = A5.step(100);
- assertEquals(0,trap_arm);
-
- assertEquals(66, testCPUSize(src, "x86_64_v2","win64",3 ));
- assertEquals(84, testCPUSize(src, "riscv", "SystemV",5 ));
- assertEquals(84, testCPUSize(src, "arm", "SystemV",5));
- // do assertEquals here
- }
-
-
- // Int now is changed to 4 bytes.
- @Test public void testPerson() throws IOException {
- String src =
-"""
-struct Person { // Exports a field typed singleton+final *class:Person{age:i32}
- i32 age;
-};
-
-val fcn = { !Person?[] !ps, int x -> // exports a field with a constant fcn ptr; exports the fcn also
- if( ps[x] )
- ps[x].age++;
-};
-""";
- String person = "6\n";
- TestC.run(src, "person2", null, TestC.CALL_CONVENTION, "", TestC.C_DRIVERS_DIR+"person.c", person, 2);
-
- // Memory layout starting at PS:
- int ps = 1<<16; // Person array pointer starts at heap start
- // Person[3] = { len,pad,P0,P1,P2 }; // sizeof = 4*8
- // P0 = { age } // sizeof=8
- int p0 = ps+4*8+0*8;
- // P1 = { age } // sizeof=8
- int p1 = ps+4*8+1*8;
- // P2 = { age } // sizeof=8
- int p2 = ps+4*8+2*8;
- EvalRisc5 R5 = TestRisc5.build( src, "fcn", ps, 2, false);
- R5.regs[riscv.A1] = 1; // Index 1
- R5.st8(ps,3); // Length
- R5.st8(ps+1*8,p0);
- R5.st8(ps+2*8,p1);
- R5.st8(ps+3*8,p2);
- R5.st8(p0, 5); // age= 5
- R5.st8(p1,17); // age=17
- R5.st8(p2,60); // age=60
-
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals( 5+0,R5.ld8(p0));
- assertEquals(17+1,R5.ld8(p1));
- assertEquals(60+0,R5.ld8(p2));
-
- EvalArm64 A5 = TestArm64.build("fcn", src, ps, 2, false);
- A5.regs[arm.X1] = 1; // Index 1
- A5.st8(ps, 3);
- A5.st8(ps+1*8,p0);
- A5.st8(ps+2*8,p1);
- A5.st8(ps+3*8,p2);
- A5.st8(p0, 5); // age= 5
- A5.st8(p1,17); // age=17
- A5.st8(p2,60); // age=60
-
- int trap_arm = A5.step(100);
- assertEquals(0,trap_arm);
- assertEquals( 5+0, A5.ld8(p0));
- assertEquals(17+1, A5.ld8(p1));
- assertEquals(60+0, A5.ld8(p2));
- }
-
- @Test
- public void testCoRecur() {
- String src = """
-val x = 5; // aa.az; // Error to self-define forward ref
-struct _A { !Test._B? !b; !Test._C? !c; i64 ax; val az = x*2; new _A = { i64 x -> ax=x; }; };
-struct _B { !Test._A? !a; !Test._C? !c; f32 bx; val bz = x*3; new _B = { f32 x, !Test._A? aa -> bx=x; a=aa; }; };
-struct _C { !Test._A? !a; !Test._B? !b; f64 cx; val cz = x*x; new _C = { f64 x, !Test._A? aa, !Test._B? bb -> cx=x; a=aa; b=bb; }; };
-!_A !aa = new _A(17);
-!_B !bb = new _B(3.14f, aa);
-!_C !cc = new _C(2.73, aa, bb);
-aa.b = bb;
-aa.c = cc;
-bb.c = cc;
-return cc.cz;
-""";
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 25;", code.print());
- assertEquals("25", Eval2.eval(code, 0));
- }
-
- @Test
- public void testHelloWorld() throws IOException {
- String src = """
-// fd buf len -> len
-{ i32 i64 u32 -> u32 } write = "C";
-// Write byte array to stdout
-val p = { u8[~] str ->
- i64 ptr = str; // cast array base to i64
- return write(1,ptr,str#);
-};
-p("Hello, World!");
-return 0;
-""";
- TestC.runSF(src,"helloWorld", "Hello, World!",2);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "helloWorld", 0, 5, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
- assertEquals("Hello, World!",R5._stdout.toString());
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("helloWorld", src,0, 5, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- assertEquals("Hello, World!",arm._stdout.toString());
- }
-
-
- @Test
- public void testStrLoad() {
- CodeGen code = new CodeGen("return \"abc\"[1];");
- code.driver(CodeGen.Phase.FinalizeIR);
- assertEquals("return .[];", code.print());
- }
-
-
- @Test
- public void testFinalArray() {
- String src = """
-int N=4;
-i32[] !is = new i32[N];
-for( int i=0; i<N; i++ )
- is[i] = i*i;
-val _sum = { i32[~] is -> // final array
- int sum=0;
- for( int i=0; i<is#; i++ )
- sum += is[i];
- return sum;
-};
-return _sum(is);
-""";
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("return Phi(Loop,0,(Phi_sum+.[]));", code.print());
- assertEquals("14", Eval2.eval(code, 0));
- }
-
-
- @Test @Ignore
- public void testEcho() throws IOException {
- String src =
-"""
-// Echo stdin to stdout.
-return sys.io.p( sys.io.stdin() );
-""";
- TestC.runSF(src,"echo", "",0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "echo", 0, 2, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("echo", src, 0, 2, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
deleted file mode 100644
index df02d3b4..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ /dev/null
@@ -1,341 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.*;
-
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter23Test {
- @Test public void testPopularUses() { com.seaofnodes.simple.codegen.RegAllocTestSupport.popularUses(); }
-
- @Test public void testDiagnosticTypeAccessors() throws Exception {
- new CodeGen("return 0;").parse();
- var tfp = TypeFunPtr.TEST;
- var con = ConstantNode.raw(tfp);
- var call = new CallNode(null,con,con,con);
- var field = Type.class.getDeclaredField("VISIT");
- field.setAccessible(true);
- var visit = (java.util.Map<Object,Type>)field.get(null);
- var marker = new Object();
- visit.put(marker,Type.BOTTOM);
- try {
- assertEquals(0,TypeInteger.ZERO.alignment());
- assertEquals(0,TypeInteger.ZERO.value());
- org.junit.Assert.assertTrue(tfp._isConstant());
- con.toString();
- call.name();
- con.p(1);
- org.junit.Assert.assertSame(Type.BOTTOM,visit.get(marker));
- assertEquals(1,visit.size());
- } finally { visit.clear(); }
- }
-
-
- @Test @Ignore
- public void testJig() throws IOException {
- //String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
- String src = Files.readString(Path.of("docs/examples/BubbleSort.smp"));
- testCPU(src,"x86_64_v2", "win64" ,-1,null);
- testCPU(src,"riscv" , "SystemV",-1,null);
- testCPU(src,"arm" , "SystemV",-1,null);
- }
-
-
- static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) {
- CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
- int delta = spills>>3;
- if( delta==0 ) delta = 1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() )
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
- if( stop != null )
- assertEquals(stop, code.print());
- return code;
- }
-
- @Test
- public void testOr() throws IOException {
- String src = """
-int a = 1;
-int b = 0;
-int rez = 2;
-
-if( a++ || b++ ) {
- if( b == 0 && a == 2 )
- rez = 0; // Expected answer
-} else {
- rez = 1;
-}
-return rez;
-""";
-
- TestC.runSF( src, "or1", "", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "or1", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("or1", src,0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
- @Test
- public void testAnd() throws IOException {
- String src = """
-int a = 1;
-int b = 1;
-return a && b ? 0 : 1; // Expected answer 0
-""";
- TestC.runSF( src, "and1", "", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "and1", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("and1", src, 0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
- @Test
- public void testLogicalValueSemantics() {
- 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
- public void testMixedScalarNotPhiPredicate() {
- CodeGen code = new CodeGen("""
-struct S {};
-S? s = arg ? new S;
-return !(!s || !arg) ? 1 : 0;
-""");
- code.driver(CodeGen.Phase.TypeCheck);
- assertEquals("0", Eval2.eval(code, 0));
- assertEquals("1", Eval2.eval(code, 1));
- }
-
- @Test
- public void testAndPtr() throws IOException {
- // Todo: have one src here
- String src = """
-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).driver(CodeGen.Phase.TypeCheck);
- assertEquals("true", Eval2.eval(code, 0));
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "class:Test.<clinit>", 1, 12, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- int adr = (int)R5.regs[riscv.A0]; // Returns a Simple *u8[~] string
- assertEquals(4,R5.ld4s(adr)); // String length
- assertEquals(0x65757274,R5.ld4s(adr+4)); // "true"
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("class:Test.<clinit>", src, 1, 12, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- int adr2 = (int)arm.regs[0]; // Returns a Simple *u8[~] string
- assertEquals(4,arm.ld4s(adr2)); // String length
- assertEquals(0x65757274,arm.ld4s(adr2+4)); // "true"
- }
-
- // conditional side effect
- @Test
- public void testCondSideEffAnd() throws IOException {
- String src = """
-int x=1;
-int y=1;
-int z=0;
-
-int g = x++ && y++ && z++;
-return x == 2 && y == 2 && z == 1 && g == 0
- ? 0 // Effected, Expected
- : 1; // Error, not effected
-""";
-
- TestC.runSF( src, "and2", "", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "and2", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("and2", src, 0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
- @Test
- public void testCondSideEffOr() throws IOException {
- String src = """
-int x = -1;
-int y = 1;
-int z = -1;
-int g = x++ || y++ || z++;
-int switch = (x == 4 || y == 4 || z == 4) ? -1 : 1;
-return (x == 0 && y == 1 && z==-1) && switch && g
- ? 0 // Effected, expected
- : 1; // Error
-""";
- TestC.runSF( src, "or2", "", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "or2", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("or2", src, 0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
- // test it with function calls
- @Test
- public void testFuncCall() throws IOException {
- String src = """
-int a = 1;
-int b = 1;
-var sq_noInline = { int x ->
- x*x;
-};
-return (a && sq_noInline(0))
- ? 1 // Error
- : 0; // Expected
-""";
- TestC.runSF( src, "and3", "", 2);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "and3", 0, 2, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("and3", src, 0, 2, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
-
- @Test
- public void testFRefFields() {
- String src = """
-// A hypothetical scanner class
-struct _Scan {
- int !x;
- u8[~] !buf;
- new _Scan = { u8[~] b -> buf=b; };
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- };
- // Peek a character; if matched consume it, else false.
- val peek = { u8 c ->
- skip();
- if( buf[x] != c ) return false;
- x++;
- return true;
- };
-};
-!_Scan !_s = new _Scan(" q");
-return _s.peek('q');
-""";
-
- 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);
- };
-
- @Test
- public void testFRefFields2() {
- String src ="""
-// A hypothetical scanner class
-struct _Scan {
- int !x;
- u8[~] !buf;
- new _Scan = { u8[~] b -> buf=b; };
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- return self;
- };
-};
-~_Scan s = new _Scan(" q");
-return s.skip().x;
-""";
-
- 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"));
- }
- };
-
-
- @Test
- public void testMethod() {
- String src = """
-// A hypothetical scanner class
-struct _Scan {
- int !x;
- u8[~] !buf;
- new _Scan = { u8[~] b -> buf=b; };
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- };
- val require = { u8 ch ->
- skip();
- buf[x++]==ch;
- };
-};
-!_Scan !_s = new _Scan(" [1,2]");
-return _s.require('[');
-""";
-
- 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
deleted file mode 100644
index 2bc2ce7f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
+++ /dev/null
@@ -1,293 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.*;
-
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
-import java.io.IOException;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class Chapter24Test {
- @Test public void testRecursiveWidening() {
- String[] srcs = {
- // Parm(x) must widen even though the recursive result is constant.
- "val f = { int x, int limit -> x >= limit ? 0 : f(x+1,limit); }; return f(0,arg);",
- // The growing result has its own cycle, which never crosses Parm(x).
- "val f = { int x -> x <= 0 ? 0 : f(x-1)+1; }; return f(3);"
- };
- for( int i=0; i<srcs.length; i++ )
- for( long seed : new long[]{0,42,126} ) {
- var code = new CodeGen(srcs[i],seed,true).driver(CodeGen.Phase.LocalSched);
- assertEquals(i==0 ? "0" : "3",Eval2.eval(code,3));
- }
- }
-
- @Test public void testColdLoopSelfConflict() { com.seaofnodes.simple.codegen.RegAllocTestSupport.coldLoopSelfConflict(); }
-
- @Test public void testPrintingDeadFunction() throws Exception {
- var code = new CodeGen("return 0;").parse();
- var tfp = TypeFunPtr.TEST;
- var fun = new FunNode(null,tfp,"deadPrinterTarget",null);
- org.junit.Assert.assertTrue(fun.isDead());
- code.link(fun);
- var con = ConstantNode.raw(tfp);
- var call = new CallNode(null,con,con,con);
- con.toString();
- call.name();
- code.funcName(tfp.fidx());
- org.junit.Assert.assertSame(fun,code._link(tfp));
- // Optimizer lookup still performs its usual lazy cleanup.
- org.junit.Assert.assertNull(code.link(tfp));
- org.junit.Assert.assertNull(code._link(tfp));
- }
-
-
- @Ignore
- @Test
- public void testJig() throws IOException {
- String src = "int v0=0; v0 = arg && 0== 0 !=0;";
- testCPU(src,"x86_64_v2", "win64" ,-1,null);
- testCPU(src,"riscv" , "SystemV",-1,null);
- testCPU(src,"arm" , "SystemV",-1,null);
- }
-
- static CodeGen testCPU( String src, String cpu, String os, int spills, String stop ) {
- CodeGen code = new CheckedCodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os);
- int delta = spills>>3;
- if( delta==0 ) delta = 1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() )
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
- if( stop != null )
- assertEquals(stop, code.print());
- return code;
- }
-
- // test it with function calls
- @Test
- public void testStack1() throws IOException {
- String src ="""
-int something_specific = 11;
-return (10 < something_specific < 20)
- ? 0 // Expected
- : 1; // Error
-""";
- TestC.runSF( src, "stacked_r_1", "", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "stacked_r_1", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_1", src, 0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
- @Test
- public void testStack3() throws IOException {
- String src ="""
-var sq_noInline = { int x ->
- x*x;
-};
-int score = 4;
-return (sq_noInline(2) <= score < sq_noInline(3))
- ? 0 // Expected
- : 1; // Error
-""";
- TestC.runSF( src, "stacked_r_3", "", 2);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "stacked_r_3", 0, 2, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_3", src, 0, 3, false);
- int trap_1 = arm.step(100);
- assertEquals(0,trap_1);
- assertEquals(0,arm.regs[0]);
- }
-
- @Test
- public void testStack4() throws IOException {
- String src ="""
-int something_specific = 9;
-return (10 < something_specific < 20)
- ? 1 // Error
- : 0; // Expected
-""";
-
- TestC.runSF( src, "stacked_r_4", "", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src, "stacked_r_4", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(0,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_4", src, 0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(0,arm.regs[0]);
- }
-
- @Test
- public void testStack6() {
- try { new CodeGen("return 0 < arg > 1;" ).parse(); fail(); }
- catch( RuntimeException e ) { assertEquals("Mixing relational directions in a chained relational test",e.getMessage()); }
- }
-
- @Test
- public void testStack9() throws IOException {
- String src = "var stack9 = { int x -> (x < 1) == 1; };";
- String src2 = "return (0 < 1) == 1;";
- TestC.runC(src, "stacked_r_9x", "1", 0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_9", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(1,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_9", src2, 0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(1,arm.regs[0]);
- }
-
- @Test
- public void testStack10() throws IOException {
- String src ="var stack10 = { int x -> (x < 1) == (1>x);};";
- String src2 ="return (0 < 1) == (1>0);";
- TestC.runC(src, "stacked_r_10x", "1",0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_10", 0, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(1,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_10", src2,0, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(1,arm.regs[0]);
- }
-
- @Test
- public void testStack11() throws IOException {
- String src = "var stack11 = { int x -> 0 < x < x+1 < 4;}; ";
- String src2 = "return 0 < arg < arg+1 < 4;";
- TestC.runC(src, "stacked_r_11x", "1", 2);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_11", 1, 0, false);
- R5.regs[riscv.A1] = 1; // Arg in A1, Test.class in A0
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(1,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_11", src2, 1, 0, false);
- arm.regs[1] = 1; // Arg in r1, Test.class in r0
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(1,arm.regs[0]);
- }
-
- // shows that it is parsed as
- // ((0 != x) != 2)
- @Test
- public void testStack12() throws IOException {
- String src = "var stack12 = { int x -> 0 != x != 2; };";
- String src2 = "return 0 != arg != 2;";
- TestC.runC(src, "stacked_r_12x", "1",0);
-
- // Evaluate on RISC5 emulator
- EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_12", 1, 0, false);
- int trap = R5.step(100);
- assertEquals(0,trap);
- assertEquals(1,R5.regs[riscv.A0]);
-
- // Evaluate on ARM emulator
- EvalArm64 arm = TestArm64.build("stacked_r_12", src2, 1, 0, false);
- trap = arm.step(100);
- assertEquals(0,trap);
- assertEquals(1,arm.regs[0]);
- }
-
-
- // shows that it is parsed as
- // ((0 == x) == 1)
- @Test
- public void testStack13() throws IOException {
- String src = "var stack13 = { int x -> 0 == x == 1; };";
- String src2 = "return 0 == arg == 1;";
- TestC.runC(src, "stacked_r_13x", "1",0);
-
- // Compile once per target, then check false, true and negative inputs.
- EvalRisc5 R5 = TestRisc5.build( src2, "stacked_r_13", 1, 0, false);
- for( int arg : new int[]{-1,0,1} ) {
- R5._pc = 0;
- R5.regs[riscv.A1] = arg;
- assertEquals(0,R5.step(100));
- assertEquals(arg==0 ? 1 : 0,R5.regs[riscv.A0]);
- }
- EvalArm64 arm = TestArm64.build("stacked_r_13", src2, 1, 0, false);
- for( int arg : new int[]{-1,0,1} ) {
- arm._pc = 0;
- arm.regs[1] = arg;
- assertEquals(0,arm.step(100));
- assertEquals(arg==0 ? 1 : 0,arm.regs[0]);
- }
- }
-
- @Test
- public void testSCCP0() {
- String src =
-"""
-int x = 1;
-int cnt = 5;
-while (cnt) {
- cnt -= 1;
- if (x == 0) {
- x += 1;
- }
-}
-return x;
-""";
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 1;", code.print());
- }
-
- @Test
- public void testSCCP1() {
- String src =
-"""
-int x = 1;
-int cnt = 5;
-while( cnt-- )
- if( x == 1 )
- x = 2-x;
-return x;
-""";
- 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
deleted file mode 100644
index 275be96a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ /dev/null
@@ -1,528 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.codegen.ElfReader;
-import com.seaofnodes.simple.codegen.ParseAll;
-import com.seaofnodes.simple.node.CallNode;
-import com.seaofnodes.simple.node.FunNode;
-import com.seaofnodes.simple.node.LoopNode;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.util.Ary;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import org.junit.Ignore;
-import org.junit.Test;
-
-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 {
- val visible = { u8 c -> c==255; };
- val _hidden = { u8 c -> c==1; };
- };
- struct _Private {
- val hiddenClassMethod = { u8 c -> c==2; };
- };
- return new Scan.visible(44);
- """;
- for( long seed : new long[]{1,123,987654321} ) {
- CodeGen code = new CodeGen(src,seed,true).driver(CodeGen.Phase.Parse);
- FunNode visible = null;
- int checked = 0;
- for( FunNode fun : code._linker ) {
- if( fun==null || fun._name==null ) continue;
- if( fun._name.equals("visible") ) {
- assertTrue(code.publicFIDX(fun.sig().fidx()));
- visible = fun;
- checked++;
- }
- if( fun._name.equals("_hidden") || fun._name.equals("hiddenClassMethod") ) {
- assertFalse(code.publicFIDX(fun.sig().fidx()));
- checked++;
- }
- }
- assertEquals(3,checked);
- code.driver(CodeGen.Phase.TypeCheck);
- assertTrue(visible.unknownCallers());
- assertEquals(TypeInteger.U8,visible.parm(3)._type);
- assertEquals("0",Eval2.eval(code,0));
- }
- }
-
- @Test public void testNeverReturnPointerRiscV() {
- new CodeGen("""
- struct _S { int x; };
- !_S !s = new _S;
- if(arg) while(1) { s.x += arg; }
- return s;
- """).driver(CodeGen.Phase.Encoding,"riscv","SystemV");
- }
-
- @Test public void testInlineNeverExit() {
- String[] sources={
- "val _spin={ -> while(1) {} return 1; }; _spin(); return 2;",
- // Two callers require cloning, including the non-exiting arm.
- "val _spin={int x -> if(x) while(1) {} return 7; }; return _spin(arg)+_spin(arg);"
- };
- for( int i=0; i<sources.length; i++ ) {
- for( long seed : new long[]{1,123,987654321} ) {
- CodeGen code=new CodeGen(sources[i],seed,true).driver(CodeGen.Phase.TypeCheck);
- assertNull("Calls should inline",code._start.walk(n -> n instanceof CallNode ? n : null));
- assertNotNull("The non-exiting path must survive",code._start.walk(n -> n instanceof LoopNode && n!=code._start ? n : null));
- if( i==1 ) assertEquals("14",Eval2.eval(code,0,10));
- assertNull(Eval2.eval(code,1,10));
- }
- for( String cpu : new String[]{"x86_64_v2","arm","riscv"} )
- new CheckedCodeGen(sources[i]).driver(CodeGen.Phase.Encoding,cpu,"SystemV");
- }
- }
-
- @Test
- public void testExternData() throws IOException {
- String src = """
- i32 counter="C";
- i8 small="C";
- f64 fraction="C";
- {int} bump="C";
- val test_global={ ->
- val before=counter;
- counter=before;
- counter=++counter+1;
- counter+=1;
- val mid=bump();
- small++;
- fraction=fraction+0.5;
- return before*10000+mid*100+counter;
- };
- """;
- TestC.runC(src,"extern_data","",-1);
- }
-
- @Test
- public void testExternDataEncoding() {
- for( String cpu : new String[]{"arm","riscv"} ) {
- CodeGen code = new CheckedCodeGen("i32 counter=\"C\"; counter=counter+3; return counter;");
- // Exercise signed low-12 relocation arithmetic, past the first page.
- int addr = 0x3800;
- code._externDataAddresses.put("counter",addr);
- code.driver(cpu,"SystemV",true,false);
- byte[] image = new byte[1<<20];
- var bits = code._encoding._bits;
- System.arraycopy(bits.buf(),0,image,0,bits.size());
- if( cpu.equals("arm") ) {
- EvalArm64 arm = new EvalArm64(image,1<<16);
- arm.st4(addr,-7);
- assertEquals(0,arm.step(1000));
- assertEquals(-4,arm.regs[0]);
- assertEquals(-4,arm.ld4s(addr));
- } else {
- EvalRisc5 r5 = new EvalRisc5(image,1<<16);
- r5.st4(addr,-7);
- assertEquals(0,r5.step(1000));
- assertEquals(-4,r5.regs[10]);
- assertEquals(-4,r5.ld4s(addr));
- }
- }
- }
-
- @Test
- public void testExternDataImports() throws IOException {
- Path dir = Files.createTempDirectory(Path.of("build/objs"),"extern_data_");
- String globals = "i32 counter=\"C\"; val read={ -> counter; };";
- CodeGen globalCode = new CodeGen(null,dir.toString(),null,"Globals",globals,126,true,TypeInteger.BOT)
- .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- var clz = globalCode.compunit()._clz;
- int counter = clz.find("counter");
- assertTrue(clz._fields[counter]._extern);
- assertEquals(-1,clz.offset(counter));
- assertEquals(clz.remove(counter).size(),clz.size());
- // Load the serialized declaration and ideal graph from Globals.o.
- String src = """
- i32 counter="C";
- val test_global={ ->
- val before=Globals.counter;
- Globals.counter=before+1;
- counter+=2;
- return before*100+Globals.read();
- };
- """;
- Ary<String> paths = new Ary<>(new String[]{dir.toString()});
- for( boolean imported : new boolean[]{true,false} ) {
- Path root = dir.resolve(imported ? "binary" : "source");
- Files.createDirectories(root.resolve("Main"));
- if( !imported ) Files.writeString(root.resolve("Main/Globals.smp"),globals);
- new CodeGen(root.toString(),root.toString(),imported ? paths : null,"Main",src,126,true,TypeInteger.BOT)
- .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- assertEquals("",TestC.gcc(root+"/Main.o","",TestC.C_DRIVERS_DIR+"extern_data_import.c",
- (String)null,new Ary<>(new String[]{dir+"/Globals.o"}),root+"/program"+(TestC.OS.startsWith("Windows") ? ".exe" : "")));
- }
- }
-
- @Test public void testPrintingConstantPool() throws Exception {
- com.seaofnodes.simple.codegen.PrintRegTestSupport.checkConstantPool();
- }
-
- private static final String SYS_BLDDIR = "build/objs/lib_"+TestC.CPU_ABI;
- private static final File SYS_FILE = new File(SYS_BLDDIR+"/sys.o");
-
- @Test
- public void testForwardConstructor() {
- CodeGen code = new CodeGen("src/test/java/com/seaofnodes/simple/test_smp/forward_ctor",
- "build/objs/forward_ctor_parse",null,
- "m",null,123L,true,TypeInteger.BOT);
- code.driver(CodeGen.Phase.TypeCheck);
- }
-
-
- @Test
- public void testPostfixFieldUpdate() {
- String src = """
- struct V {
- u32 !len = 0;
- i64[] !buf;
- new V = { i64[] b -> buf=b; };
- val grow = { i64 n -> self; };
- val add = { i64 x -> grow(1).buf[len++] = x; self; };
- };
- return new V(new i64[1]).add(7).len;
- """;
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("1",Eval2.eval(code,0));
- }
-
- @Test
- public void testStringEscapes() {
- String src = "u8[~] s=\"A\\n\\t\\r\\\\\\\"B\"; " +
- "return s#==7 && s[0]=='A' && s[1]==10 && s[2]==9 && s[3]==13 && " +
- "s[4]==92 && s[5]==34 && s[6]=='B';";
- CodeGen code = new CodeGen(src).driver(CodeGen.Phase.TypeCheck);
- assertEquals("1",Eval2.eval(code,0));
- }
-
- @Test
- public void testCharacterEscapes() {
- CodeGen code = new CodeGen("return '\\n'==10 && '\\t'==9 && '\\r'==13;")
- .driver(CodeGen.Phase.TypeCheck);
- assertEquals("1",Eval2.eval(code,0));
- }
-
- @Test
- public void testFuzzerUnresolvedNumericLoop() {
- CodeGen code = new CodeGen("""
- arg=0;
- while(arg+0)
- while(-arg)
- arg=0;
- """).driver(CodeGen.Phase.TypeCheck);
- assertEquals("return 0;",code.print());
- }
-
- @Test
- public void testFuzzerBadLCA() {
- new CodeGen("""
- int v0=arg;
- if(v0&&--v0?0:arg) {}
- """).driver(CodeGen.Phase.LocalSched);
- }
-
- @Test @Ignore
- public void testModule0() throws IOException {
- String MODDIR = "src/test/java/com/seaofnodes/simple/test0";
- String BLDDIR = "build/objs/test0";
- String a_obj = BLDDIR+"/A.o";
- String ab_obj = BLDDIR+"/A/B.o";
- // Remove any prior results so the test runs from scratch.
- delELFiles(new File(BLDDIR));
- writeB(MODDIR,5);
-
- // Compile MODDIR/A.smp into MODDIR/A.o
- // Since A refers to B also:
- // Compile MODDIR/A/B.smp into MODDIR/A/B.o
- CodeGen code1 = new CodeGen(MODDIR, BLDDIR,null,
- "A",null,123L,true,TypeInteger.BOT);
- code1.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION);
-
- // Verify produces A.o, A/B.o
- File a_file = new File( a_obj);
- File ab_file = new File(ab_obj);
- assertTrue( a_file.exists() );
- assertTrue( ab_file.exists() );
- long a_msec1 = a_file.lastModified();
- long ab_msec1 = ab_file.lastModified();
-
- // Link and execute: arg is true, so compute "5+1" as the exit code
- String rez1 = TestC.gcc("A", 1.2, a_obj, ab_obj);
- assertEquals("exec exit code: 6",rez1);
-
- // Compile again A, expecting both A.o and A/B.o to be up-to-date and not compiled
- CodeGen code2 = new CodeGen(MODDIR, BLDDIR, null,
- "A",null,123L,true,TypeInteger.BOT);
- code2.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION);
-
- assertTrue( a_file.exists() );
- assertTrue( ab_file.exists() );
- long a_msec2 = a_file.lastModified();
- long ab_msec2 = ab_file.lastModified();
-
- assertEquals( a_msec1, a_msec2 );
- assertEquals( ab_msec1, ab_msec2 );
-
- // Touch A.smp and recompile. A/B.o should not recompile.
- new File(MODDIR+"/A.smp").setLastModified(System.currentTimeMillis());
- CodeGen code3 = new CodeGen(MODDIR, BLDDIR,null,
- "A",null,123L,true,TypeInteger.BOT);
- code3.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION);
-
- long a_msec3 = a_file.lastModified();
- long ab_msec3 = ab_file.lastModified();
- assertTrue ( a_msec1 < a_msec3 );
- assertEquals( ab_msec1, ab_msec3 );
-
- // Link and execute: arg is true, so compute "5+1" as the exit code
- String rez3 = TestC.gcc("A", 1.2, a_obj, ab_obj);
- assertEquals("exec exit code: 6",rez3);
-
-
- // Modify B.smp and recompile A/B.o; it should recompile and A.o should not.
- writeB(MODDIR,7);
- CodeGen code4 = new CodeGen(MODDIR, BLDDIR,null,
- "A/B",null,123L,true,TypeInteger.BOT);
- code4.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION);
-
- long a_msec4 = a_file.lastModified();
- long ab_msec4 = ab_file.lastModified();
- assertEquals( a_msec3, a_msec4 );
- assertTrue ( ab_msec3 < ab_msec4 );
-
- // Link and execute: uses stale A.o, so remains '6' not '7+1' == 8
- String rez4 = TestC.gcc("A", 1.2, a_obj, ab_obj);
- assertEquals("exec exit code: 6",rez4);
-
- // Recompile A.o, it should recompile despite not being touched because
- // it depends on A/B.o which recompiled in the prior step.
- CodeGen code5 = new CodeGen(MODDIR, BLDDIR,null,
- "A",null,123L,true,TypeInteger.BOT);
- code5.driver(CodeGen.Phase.Export,TestC.CPU_PORT,TestC.CALL_CONVENTION);
-
- long a_msec5 = a_file.lastModified();
- long ab_msec5 = ab_file.lastModified();
- assertTrue ( a_msec4 < a_msec5 );
- assertEquals( ab_msec4, ab_msec5 );
-
- // Link and execute: updates A.o from B.o inlining, without compiling B.o
- String rez5 = TestC.gcc("A", 1.2, a_obj, ab_obj);
- assertEquals("exec exit code: 8",rez5);
- // Reset for next time
- writeB(MODDIR,5);
- }
-
- private void writeB(String MODDIR, int x) throws IOException {
- var bsmp = new FileWriter(MODDIR+"/A/B.smp");
- bsmp.write("val x="+x+";\n");
- bsmp.close();
- }
-
-
- // Recursive search (TODO: gzip, archives) and delete all .o files
- private void delELFiles( File dir) {
- if( dir.isDirectory() )
- for( File f : dir.listFiles() )
- delELFiles(f);
- else if( dir.getName().endsWith(".o") )
- dir.delete();
- }
-
-
- @Test
- public void testSys() {
- assertTrue("Missing "+SYS_FILE+"; run make tests_sys", SYS_FILE.exists());
-
- // Can read the ELF files
- CodeGen code1 = new CodeGen("return 0;");
- ElfReader sys_elf = ElfReader.load(SYS_FILE, null);
- sys_elf.loadPublicTypes(code1);
-
- // Elf files are sane
-
- // Sys depends on io, libc, char, collections, and array utilities.
- assertArrayEquals(new String[]{"sys", "sys/adt", "sys/adt/bitset", "sys/ary",
- "sys/aryi64", "sys/aryu8", "sys/char", "sys/io", "sys/libc", "sys/scan"},sys_elf._deps);
- assertSame("class:sys" ,sys_elf._clz._name);
- }
-
- @Test
- public void testConstantClassInitialization() throws IOException {
- TestC.runSF("val x = 42; return 0;","constantClassInt","",0);
- TestC.runSF("val f = { -> 7; }; return 0;","constantClassFunction","",0);
- }
-
- @Test
- public void testHelloWorld() throws IOException {
- String expected = "Hello, World!\n";
- String prog = "return sys.io.p(\""+expected+"\") - "+expected.length()+";";
- // tests_raw1 concurrently builds Chapter22Test's helloWorld executable.
- TestC.run(prog,"helloWorldSys",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION, null, null, expected,0);
- }
-
- @Test
- public void testHelloWorldDriver() throws Exception {
- String out = "build/objs/helloWorldDriver/";
- Simple.main(new String[]{"-L",SYS_BLDDIR,"-o",out,"--norun","docs/examples/A_helloWorld.smp"});
- assertTrue(Files.isRegularFile(Path.of(out,"A_helloWorld.o")));
- }
-
- @Test
- public void testHelloWorldDriverLibFile() throws Exception {
- ByteArrayOutputStream output = new ByteArrayOutputStream();
- PrintStream saved = System.out;
- try( PrintStream capture = new PrintStream(output,true,StandardCharsets.UTF_8) ) {
- System.setOut(capture);
- Simple.main(new String[]{"-L",SYS_FILE.toString(),"-o","build/objs/helloWorldDriverLibFile/",
- "docs/examples/A_helloWorld.smp"});
- } finally {
- System.setOut(saved);
- }
- assertEquals("Hello, World!\n",output.toString(StandardCharsets.UTF_8));
- }
-
- @Test
- public void testHelloWorldNoInline() throws Exception {
- String base = "helloWorldNoInline";
- String expected = "Hello, World!\n";
- String prog = "return sys.io.p_noInline(\""+expected+"\") - "+expected.length()+";";
- CodeGen code = new CheckedCodeGen(null,"build/objs",new Ary<>(new String[]{SYS_BLDDIR}),
- base,prog,123L,true,TypeInteger.BOT);
- code.driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,true);
-
- String obj = "build/objs/"+base+".o";
- String exe = "build/objs/"+base+(TestC.OS.startsWith("Windows") ? ".exe" : "");
- String syms = TestC.exec(TestC.TEST_TIMEOUT_SECONDS,"nm",obj);
- assertTrue(syms, syms.contains(" U sys.io.p_noInline"));
-
- TestC.linkExe(obj,null,null,new Ary<>(new String[]{SYS_FILE.toString()}),exe);
- String rez = TestC.exec(TestC.TEST_TIMEOUT_SECONDS,exe);
- assertEquals(expected,rez);
- }
-
- @Test
- public void testRedirectedRead() throws IOException {
- String src = """
-u8[] buf = new u8[10];
-i64 ptr = buf;
-int rez = sys.libc.read(0,ptr,buf#);
-return rez < buf# ? 0 : sys.libc._exit(-2);
-""";
- TestC.run(src,"redirectedRead",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION, null, null,
- "abc", "", -1);
- }
-
- @Test
- public void testErrnoAccessor() throws IOException {
- String src = "val status=sys.libc.close(-1); return status != -1 || sys.libc.errno()==0;";
- TestC.run(src,"errnoAccessor",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION,null,null,"",-1);
- }
-
- @Test
- public void testBubbles() throws IOException {
- String src = Files.readString( Path.of("docs/examples/BubbleSort.smp"));
- String exe = TestC.compile(src,"BubbleSort",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION,null,null,-1);
- assertEquals("[-17, 2, 3, 999]\n",
- TestC.exec(exe,"[3, 2,-17, 999 ]"));
- assertEquals("[1, 2, 3, 4, 4, 5]\n",
- TestC.exec(exe,"[4, 5, 3, 1, 4, 2]"));
- assertEquals("[1, 2, 3, 4, 5]\n",
- TestC.exec(exe,"[1, 2, 3, 4, 5]"));
- assertEquals("[1, 2, 3, 4, 5, 6, 7, 8, 9]\n",
- TestC.exec(exe,"[9, 8, 7, 6, 5, 4, 3, 2, 1]"));
-
- String usage = "Usage: please provide a list of at least two integers to sort in the format \"[1, 2, 3, 4, 5]\"\n";
- assertEquals(usage,TestC.exec(exe));
- assertEquals(usage,TestC.exec(exe,""));
- assertEquals(usage,TestC.exec(exe,"[1]"));
- assertEquals(usage,TestC.exec(exe,"[4 5 3]"));
- }
-
- @Test
- public void testCapitalize() throws IOException {
- String src = Files.readString(Path.of("docs/examples/Capitalize.smp"));
- String exe = TestC.compile(src,"Capitalize",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION,null,null,-1);
- assertEquals("Hello world\n",TestC.exec(exe,"hello world"));
- assertEquals("Hello World\n",TestC.exec(exe,"Hello World"));
- assertEquals("123 apples\n",TestC.exec(exe,"123 apples"));
- assertEquals("Usage: please provide a string\n",TestC.exec(exe));
- assertEquals("Usage: please provide a string\n",TestC.exec(exe,""));
- assertEquals("Use quotes around multiple strings.\n",TestC.exec(exe,"hello","world"));
- }
-
- @Test
- public void testDijkstra() throws IOException {
- String src = Files.readString(Path.of("docs/examples/Dijkstra.smp"));
- String matrix = "[0, 2, 0, 6, 0, 2, 0, 3, 8, 5, 0, 3, 0, 0, 7, 6, 8, 0, 0, 9, 0, 5, 7, 9, 0]";
- String exe = TestC.compile(src,"Dijkstra",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION,null,null,-1);
- assertEquals("2\n",TestC.exec(exe,matrix,"0","1"));
- String usage = "Usage: please provide three inputs: a serialized matrix, a source node and a destination node\n";
- assertEquals("7\n",TestC.exec(exe,matrix,"0","4"));
- assertEquals(usage,TestC.exec(exe));
- assertEquals(usage,TestC.exec(exe,"","",""));
- assertEquals(usage,TestC.exec(exe,"[1, 0, 3, 0, 5, 1]","1","2"));
- assertEquals(usage,TestC.exec(exe,"[0, 0, 0, 0]","0","1"));
- }
-
- @Test
- public void testFileIO() throws IOException {
- String src = Files.readString(Path.of("docs/examples/FileIO.smp"));
- String exe = TestC.compile(src,"FileIO",new Ary<>(new String[]{SYS_BLDDIR}),
- TestC.CALL_CONVENTION,null,null,-1);
- assertEquals("File I/O succeeded\n",TestC.exec(exe));
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java b/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
deleted file mode 100644
index 47e10f97..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/ClassInitDependenciesTest.java
+++ /dev/null
@@ -1,550 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.*;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFunPtr;
-import com.seaofnodes.simple.type.Field;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.type.TypeMem;
-import com.seaofnodes.simple.type.TypeTuple;
-import com.seaofnodes.simple.util.Ary;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.BitSet;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.TestRule;
-import org.junit.runners.model.Statement;
-import static org.junit.Assert.*;
-
-public class ClassInitDependenciesTest {
- private final ArrayList<Path> dirs = new ArrayList<>();
-
- @Rule public final TestRule cleanup = (test, description) -> new Statement() {
- @Override public void evaluate() throws Throwable {
- // Keep all fixtures if compilation or any later assertion fails.
- test.evaluate();
- for( Path dir : dirs ) {
- Path[] files;
- try( var paths=Files.walk(dir) ) {
- files=paths.sorted(Comparator.reverseOrder()).toArray(Path[]::new);
- }
- for( Path file : files ) Files.delete(file);
- }
- }
- };
-
- private Path tempDir(String prefix) throws Exception {
- Path build=Path.of("build/init-deps");
- Files.createDirectories(build);
- Path dir=Files.createTempDirectory(build,prefix);
- dirs.add(dir);
- return dir;
- }
-
- private CodeGen compile(String entry, Map<String,String> sources, long seed) throws Exception {
- Path root=tempDir("case-");
- Files.writeString(root.resolve("Root.smp"),"val entry="+entry+";");
- for( var source : sources.entrySet() ) {
- Path path=root.resolve("Root/"+source.getKey()+".smp");
- Files.createDirectories(path.getParent());
- Files.writeString(path,source.getValue());
- }
- return new CodeGen(root.toString(),root.resolve("obj").toString(),null,
- "Root",null,seed,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- }
-
- private void cycle(String entry, Map<String,String> sources, String expected) throws Exception {
- expected="Root."+expected.replace(" -> "," -> Root.");
- for( long seed : new long[]{1,123,987654321} ) {
- try { compile(entry,sources,seed); fail("Expected initialization cycle"); }
- catch( Parser.ParseException ex ) {
- assertCycle(ex.getMessage(),expected);
- }
- }
- }
-
- // The report lists internal SCC edges, including ordinary helper calls.
- private HashMap<String,HashSet<String>> reportEdges(String message) {
- String[] lines=message.split("\n");
- assertEquals(message,"Cyclic class initialization:",lines[0]);
- HashMap<String,HashSet<String>> edges=new HashMap<>();
- for( int i=1; i<lines.length; i++ ) {
- if( !lines[i].startsWith(" ") || lines[i].startsWith(" ") ) continue; // Diagnostic/source/caret.
- String[] edge=lines[i].trim().split(" -> ");
- assertEquals(message,2,edge.length);
- edges.computeIfAbsent(edge[0],key -> new HashSet<>()).add(edge[1]);
- }
- return edges;
- }
-
- // Follow helpers in the diagnostic, stopping at the next class initializer.
- private boolean reportedTouch(HashMap<String,HashSet<String>> edges, String from, String to, HashSet<String> seen) {
- if( !seen.add(from) || !edges.containsKey(from) ) return false;
- for( String dst : edges.get(from) )
- if( dst.equals(to) || (dst.contains(" [fidx ") && reportedTouch(edges,dst,to,seen)) ) return true;
- return false;
- }
-
- private void assertCycle(String message, String expected) {
- HashMap<String,HashSet<String>> edges=reportEdges(message);
- String[] path=expected.split(" -> ");
- for( int i=1; i<path.length; i++ )
- assertTrue("Missing "+path[i-1]+" -> "+path[i]+" in:\n"+message,
- reportedTouch(edges,path[i-1],path[i],new HashSet<>()));
- }
-
- // Follow ordinary helpers, stopping at the next initializer. This checks
- // the class prerequisite relation without requiring a total init order.
- private boolean touches(CodeGen code, ClassInitDependencies deps, String from, String to) {
- int src=code.link(code._compunits.get(from)).sig().fidx();
- int dst=code.link(code._compunits.get(to)).sig().fidx();
- return touches(code,deps._edgess,src,dst,new BitSet());
- }
-
- private boolean touches(CodeGen code, int[][] edges, int src, int dst, BitSet seen) {
- if( src>=edges.length || seen.get(src) || edges[src]==null ) return false;
- seen.set(src);
- for( int edge : edges[src] ) {
- if( edge==dst ) return true;
- FunNode fun=code.link(edge);
- if( fun!=null && !fun.isClz() && touches(code,edges,edge,dst,seen) ) return true;
- }
- return false;
- }
-
- @Test public void testEdgesAndPassiveReferences() throws Exception {
- CodeGen code = compile("A",Map.of("A","int value=B.value;", "B","int value=arg|1;"),123);
- ClassInitDependencies deps=new ClassInitDependencies(code);
- assertTrue(touches(code,deps,"Root/A","Root/B"));
- code=compile("A",Map.of("A","val b=B;", "B","val a=A;"),123);
- deps=new ClassInitDependencies(code);
- assertFalse(touches(code,deps,"Root/A","Root/B"));
- assertFalse(touches(code,deps,"Root/B","Root/A"));
- assertTrue(touches(code,deps,"Root/A","Root"));
- assertTrue(touches(code,deps,"Root/B","Root"));
- }
-
- @Test public void testCycles() throws Exception {
- cycle("A",Map.of("A","val get_noInline={ ->\n B.value;\n};\nint value=get_noInline();",
- "B","int value=A.value;"),"A -> B -> A");
- cycle("A",Map.of("A","int value=B.value;", "B","int value=A.value;"),"A -> B -> A");
- cycle("A",Map.of("A","val get={ -> B.value; }; int value=get();",
- "B","int value=A.value;"),"A -> B -> A");
- cycle("A",Map.of("A","int value=get(); val get={ -> B.value; };",
- "B","int value=A.value;"),"A -> B -> A");
- cycle("A",Map.of("A","int value=0; if(arg) value=B.value;",
- "B","int value=A.value;"),"A -> B -> A");
- cycle("A",Map.of("A","val f=arg ? { -> B.value; } : { -> 0; }; int value=f();",
- "B","int value=A.value;"),"A -> B -> A");
- cycle("P",Map.of("P","int value=A.value;", "P/A","int value=arg;"),"P -> P.A -> P");
- }
-
- @Test public void testSiblingsAndNonreturningCall() throws Exception {
- cycle("P",Map.of("P","val a=A; val b=B;", "P/A","int value=B.value;",
- "P/B","int value=A.value;"),"P.A -> P.B -> P.A");
- cycle("A",Map.of("A","val get={ -> val x=B.value; while(1) {} return x; }; int value=get();",
- "B","int value=A.value;"),"A -> B -> A");
- // An out-of-line nonreturning call retains its continuation's touches.
- cycle("A",Map.of(
- "A","int value=1; val stop_noInline={ -> while(1) {} return 1; }; stop_noInline(); return B.value;",
- "B","int value=A.value;"),"A -> B -> A");
- String source="val stop_noInline={ -> while(1) {} return 1; }; stop_noInline(); return 2;";
- CodeGen code=new CodeGen(source).driver(CodeGen.Phase.TypeCheck);
- assertNull(Eval2.eval(code,0,10)); // Evaluation reaches its loop budget.
- for( String cpu : new String[]{"x86_64_v2","arm","riscv"} )
- new CodeGen(source).driver(CodeGen.Phase.Encoding,cpu,"SystemV");
- }
-
- @Test public void testOptimizedDependencies() throws Exception {
- for( long seed : new long[]{1,123,987654321} ) {
- // SCCP learns this condition from a call; the parser cannot decide it.
- CodeGen code=compile("A",Map.of(
- "A","val test_noInline={ -> false; }; int value=1; if(test_noInline()) value=B.value;",
- "B","int value=A.value;"),seed);
- ClassInitDependencies deps=new ClassInitDependencies(code);
- assertFalse(touches(code,deps,"Root/A","Root/B"));
- assertTrue(touches(code,deps,"Root/B","Root/A"));
-
- code=compile("A",Map.of(
- "A","val test_noInline={ -> false; }; val f=test_noInline() ? { -> B.value; } : { -> 1; }; int value=f();",
- "B","int value=A.value;"),seed);
- assertFalse(touches(code,new ClassInitDependencies(code),"Root/A","Root/B"));
-
- // An unused helper's body must not become an initializer dependency.
- code=compile("A",Map.of("A","val unused={ -> B.value; }; int value=1;",
- "B","int value=A.value;"),seed);
- assertFalse(touches(code,new ClassInitDependencies(code),"Root/A","Root/B"));
- }
- // Discarding the value of a field read cannot discard its active touch.
- cycle("A",Map.of("A","int value=1; B.value; return 0;",
- "B","int value=2; A.value; return 0;"),"A -> B -> A");
- }
-
- @Test public void testRecursionAndNativeCalls() throws Exception {
- CodeGen code=compile("A",Map.of("A","val f={int n -> n ? f(n-1) : B.value; }; int value=f(arg);",
- "B","int value=arg|1;"),123);
- assertTrue(touches(code,new ClassInitDependencies(code),"Root/A","Root/B"));
- code=compile("A",Map.of("A","{int} native=\"C\"; return native();"),123);
-
- Path dir=tempDir("import-");
- new CodeGen(null,dir.toString(),null,"B","int value=1;",123,true,TypeInteger.BOT)
- .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- code=new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
- "A","return B.value;",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- assertTrue(touches(code,new ClassInitDependencies(code),"A","B"));
- }
-
- private CodeGen object(Path dir, Path imports, String name, String source) {
- return new CodeGen(null,dir.toString(),new Ary<>(new String[]{imports.toString()}),
- name,source,123,true,TypeInteger.BOT)
- .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- }
-
- private CodeGen module(Path dir, Path imports, String name, Map<String,String> sources) throws Exception {
- Path src=tempDir("import-");
- for( var entry : sources.entrySet() ) {
- Path file=src.resolve(entry.getKey()+".smp");
- Files.createDirectories(file.getParent());
- Files.writeString(file,entry.getValue());
- }
- return new CodeGen(src.toString(),dir.toString(),new Ary<>(new String[]{imports.toString()}),
- name,null,123,true,TypeInteger.BOT).driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- }
-
- @Test public void testImportedInitialization() throws Exception {
- Path dir=tempDir("import-"), stubs=tempDir("import-");
- object(stubs,stubs,"A","int value=arg;");
- object(stubs,stubs,"B","int value=arg;");
- // Each object is independently acyclic. Combining their real bodies
- // must recheck the graph rather than trust either object's local order.
- String asrc="int value=arg;\nvalue=B.value;";
- CodeGen acode=module(dir,stubs,"A",Map.of("A",asrc));
- String afile=acode._compunits.get("A").sourceFile();
- module(dir,stubs,"B",Map.of("B","int value=arg; value=A.value;"));
- for( String entry : new String[]{"A","B"} ) {
- try {
- new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
- "Main","return "+entry+".value;",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- fail("Expected imported initialization cycle");
- } catch( Parser.ParseException ex ) {
- assertCycle(ex.getMessage(),"A -> B -> A");
- assertTrue(ex.getMessage(),ex.getMessage().contains("value=B.value;\n -------^"));
- assertTrue(ex.getMessage(),ex.getMessage().contains("\n"+afile+":2:8: error: cyclic class initialization\n"));
- }
- }
- // The imported object must still report its saved path and offset
- // when the original source is no longer available.
- Files.delete(Path.of(afile));
- try {
- new CodeGen(null,dir.toString(),new Ary<>(new String[]{dir.toString()}),
- "Main","return A.value;",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- fail("Expected imported initialization cycle");
- } catch( Parser.ParseException ex ) {
- assertCycle(ex.getMessage(),"A -> B -> A");
- assertTrue(ex.getMessage(),ex.getMessage().contains("\n"+afile+": error: cyclic class initialization (source unavailable at byte offset "+asrc.indexOf('.')+")"));
- }
- }
-
- @Test public void testImportedDiamondAndHelpers() throws Exception {
- Path diamond=tempDir("import-");
- CodeGen dcode=object(diamond,diamond,"D","int value=arg;");
- var data=dcode._encoding._data.entries().stream().filter(obj -> obj.symbol.equals("D.$class")).findFirst().orElseThrow();
- var dclz=dcode._compunits.get("D")._clz;
- assertEquals(0,dcode._encoding._sdata.buf()[data.offset+dclz.offset(dclz.find("$init"))]);
- object(diamond,diamond,"B","int value=D.value;");
- object(diamond,diamond,"C","int value=D.value;");
- for( long seed : new long[]{1,123,987654321} ) {
- CodeGen code=new CodeGen(null,diamond.toString(),new Ary<>(new String[]{diamond.toString()}),
- "Main","int value=B.value+C.value;",seed,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- CompUnit b=code._compunits.get("B"), d=code._compunits.get("D");
- ClassInitDependencies deps=new ClassInitDependencies(code);
- assertTrue(touches(code,deps,"B","D"));
- assertTrue(touches(code,deps,"C","D"));
- assertTrue(touches(code,deps,"Main","B"));
- assertTrue(touches(code,deps,"Main","C"));
- Field flag=d._clz.field("$init");
- assertSame(TypeInteger.U1,flag._t);
- assertFalse(flag._final);
- assertNotEquals(flag._alias,b._clz.field("$init")._alias);
- // Loading just the header/types uses the same alias identity and
- // does not require allocating any graph nodes.
- ElfReader elf=ElfReader.load(diamond.resolve("D.o").toFile(),d);
- assertEquals(flag._alias,elf.loadPublicTypes(code).field("$init")._alias);
- assertNull(elf._nodes);
- }
- object(diamond,diamond,"Helper","val get={ -> D.value; }; val get_noInline={ -> D.value; };");
- for( String name : new String[]{"get","get_noInline"} ) {
- CodeGen code=new CodeGen(null,diamond.toString(),new Ary<>(new String[]{diamond.toString()}),
- "Main","return Helper."+name+"();",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- ClassInitDependencies deps=new ClassInitDependencies(code);
- assertTrue(touches(code,deps,"Main","D"));
- assertFalse(touches(code,deps,"Helper","D"));
- }
- object(diamond,diamond,"Wrapper","val get_noInline={ -> Helper.get_noInline(); };");
- CodeGen wrapped=new CodeGen(null,diamond.toString(),new Ary<>(new String[]{diamond.toString()}),
- "Main","return Wrapper.get_noInline();",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- assertTrue(touches(wrapped,new ClassInitDependencies(wrapped),"Main","D"));
- }
-
- @Test public void testInterleavedModules() throws Exception {
- // Two objects, each providing a parent and children. Their orders
- // must interleave: P.B precedes Q.A, which precedes P.A.
- Path modules=tempDir("import-"), qstub=tempDir("import-");
- module(qstub,qstub,"Q",Map.of("Q","val A=A;", "Q/A","int value=arg;"));
- module(modules,qstub,"P",Map.of("P","val A=A; val B=B;",
- "P/A","int value=arg; value=Q.A.value;", "P/B","int value=arg;"));
- module(modules,modules,"Q",Map.of("Q","val A=A;", "Q/A","int value=arg; value=P.B.value;"));
- CodeGen combined=new CodeGen(null,modules.toString(),new Ary<>(new String[]{modules.toString()}),
- "Main","return P.A.value;",123,true,TypeInteger.BOT).driver(CodeGen.Phase.TypeCheck);
- ClassInitDependencies deps=new ClassInitDependencies(combined);
- assertTrue(touches(combined,deps,"Q/A","P/B"));
- assertTrue(touches(combined,deps,"P/A","Q/A"));
- }
-
- @Test public void testCycleReportDirection() throws Exception {
- cycle("A",Map.of("A","int value=B.value;", "B","int value=C.value;",
- "C","int value=A.value;"),"A -> B -> C -> A");
- for( long seed : new long[]{1,123,987654321} ) {
- // Stack order A,B,C is not a cycle: B has no edge to C. All four
- // internal edges must be shown; edges to Root/D must be omitted.
- try {
- compile("A",Map.of("A","int value=B.value+C.value+D.value;",
- "B","int value=A.value;", "C","int value=A.value;",
- "D","int value=arg;"),seed);
- fail("Expected initialization cycle");
- } catch( Parser.ParseException ex ) {
- assertEquals(Map.of("Root.A",Set.of("Root.B","Root.C"),
- "Root.B",Set.of("Root.A"), "Root.C",Set.of("Root.A")),
- reportEdges(ex.getMessage()));
- }
- }
- }
-
- @Test public void testCycleSourceLocations() throws Exception {
- for( String name : new String[]{"read", "read_noInline"} ) {
- try {
- compile("A",Map.of("A","int value=Helper."+name+"();",
- "Helper","val "+name+"={ ->\n B.value;\n};",
- "B","int value=A.value;"),123);
- fail("Expected initialization cycle");
- } catch( Parser.ParseException ex ) {
- String message=ex.getMessage();
- assertCycle(message,"Root.A -> Root.B -> Root.A");
- // Inlining must keep Helper's source identity, not substitute A.
- assertTrue(message,message.contains("Helper.smp:2:4"));
- assertTrue(message,message.contains(" B.value;\n ---^"));
- if( name.endsWith("noInline") ) {
- assertTrue(message,message.contains("A.smp:1:31"));
- assertTrue(message,message.contains("int value=Helper.read_noInline();"));
- }
- }
- }
- }
-
- @Test public void testSourceError() {
- String src="// π\r\n\treturn 1;\r\n";
- CodeGen code=new CodeGen(src).driver(CodeGen.Phase.Parse);
- CompUnit cu=code._compunits.get("Test");
- int pos=src.substring(0,src.indexOf('1')).getBytes(java.nio.charset.StandardCharsets.UTF_8).length;
- assertEquals("Test.smp:2:9: error: example\n return 1;\n ---------------^",SourceError.format(cu,pos,"example"));
- assertEquals("Test.smp:3:1: error: example\n \n ^",SourceError.format(cu,src.getBytes(java.nio.charset.StandardCharsets.UTF_8).length,"example"));
- }
-
- @Test public void testSelfInitialization() throws Exception {
- for( String name : new String[]{"read","read_noInline"} )
- for( long seed : new long[]{1,123,987654321} )
- compile("A",Map.of("A","int value=arg; val "+name+"={ -> A.value; }; int result="+name+"();"),seed);
- }
-
- @Test public void testInitializationMemory() throws Exception {
- String[] sources={
- "int value=B.value+B.value;",
- "int value; if(arg) value=B.value; else value=B.value+1; value=value+B.value;",
- "int value; if(arg) value=B.value; value=value+B.value;"
- };
- int[] counts={1,2,2};
- for( long seed : new long[]{1,123,987654321} )
- for( int i=0; i<sources.length; i++ ) {
- CodeGen code=compile("A",Map.of("A",sources[i],"B","int value=arg;"),seed);
- FunNode fun=code.link(code._compunits.get("Root/A"));
- CompUnit b=code._compunits.get("Root/B");
- int[] touches={0};
- code._stop.walk(n -> {
- if( n instanceof CLInitNode init && init.fun()==fun && init.cu()==b ) {
- touches[0]++;
- TypeTuple state=(TypeTuple)init._type;
- assertSame(TypeMem.BOT,state._types[1]);
- TypeMem flag=(TypeMem)state._types[2];
- assertEquals(b._clz.field("$init")._alias,flag._alias);
- assertSame(TypeInteger.TRUE,flag._t);
- assertNotNull(init.ready());
- }
- return null;
- });
- assertEquals(sources[i],counts[i],touches[0]);
- }
- }
-
- @Test public void testForwardInstanceTouch() {
- String src="""
- struct S { int x=7; };
- val _get={ -> _make().x; };
- val _make={ -> new S; };
- return _get();
- """;
- for( long seed : new long[]{1,123,987654321} ) {
- CodeGen code=new CodeGen(src,seed,true).driver(CodeGen.Phase.Parse);
- CLInitNode init=code._stop.walk(n -> n instanceof CLInitNode c ? c : null);
- assertNotNull("The unresolved receiver needs a speculative check",init);
- assertNotNull("The readiness load is present from parsing",init.ready());
- code.driver(CodeGen.Phase.TypeCheck);
- assertNull("An instance needs neither CLInit nor its speculative $init load",
- code._stop.walk(n -> n instanceof CLInitNode ||
- (n instanceof LoadNode ld && ld._name.equals("$init")) ? n : null));
- assertEquals("7",Eval2.eval(code,0));
- }
- }
-
- @Test public void testInitializerFlagStore() throws Exception {
- // A same-named struct must retain the file class's hidden flag.
- CodeGen code=compile("A",Map.of("A","struct A { int value; }; int value=arg;"),123);
- CompUnit a=code._compunits.get("Root/A");
- Field flag=a._clz.field("$init");
- assertNotNull(flag);
- assertFalse(flag._final);
- FunNode fun=code.link(a);
- int[] stores={0};
- code._stop.walk(n -> {
- if( n instanceof StoreNode st && st._name.equals("$init") && st._alias==flag._alias ) {
- stores[0]++;
- assertSame(TypeInteger.TRUE,st.val()._type);
- assertSame(fun,CFGNode.earlyCFG(st).fun());
- }
- return null;
- });
- assertEquals(1,stores[0]);
- }
-
- @Test public void 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.
- CodeGen code=compile("A",Map.of("A","val b=B; int value=arg;", "B","int value=arg;"),123);
- int f=code._linker._len, g=f+1, a=f+2, b=f+3;
- String[] names={"f", "g", "A.<clinit>", "B.<clinit>"};
- for( int i=0; i<names.length; i++ ) {
- TypeFunPtr sig=TypeFunPtr.make1((byte)2,false,new Type[0],TypeInteger.BOT,i+f);
- CompUnit cu=code._compunits.get(i==3 ? "Root/B" : "Root/A");
- FunNode fun=new FunNode(null,sig,names[i],cu);
- fun._type=Type.CONTROL;
- code._linker.setX(i+f,fun);
- }
- ClassInitDependencies deps=new ClassInitDependencies(code);
- deps._edgess[f]=new int[]{g}; // f -> g
- deps._edgess[g]=new int[]{f}; // g -> f
- assertNull("Ordinary mutual recursion is legal",deps.check());
- deps._edgess[a]=new int[]{a}; // A -> A
- assertNull("A singleton initializer self-edge is legal",deps.check());
- deps._edgess[a]=new int[]{g}; // A -> g
- deps._edgess[f]=new int[]{g,a};
- assertNull("A cycle containing only one initializer is legal",deps.check());
- deps._edgess[a]=new int[]{b}; // A -> B
- deps._edgess[b]=new int[]{g}; // B -> g -> f -> A
- String forward=deps.check();
- deps._edgess[f]=new int[]{a,g};
- String reverse=deps.check();
- assertTrue("Both successor orders must reject the class cycle: "+forward+", "+reverse,
- forward!=null && reverse!=null);
- assertCycle(forward,"Root.A -> Root.B -> Root.A");
- assertCycle(reverse,"Root.A -> Root.B -> Root.A");
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java b/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
deleted file mode 100644
index e2d822d0..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/CompoundAssignmentTest.java
+++ /dev/null
@@ -1,94 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-public class CompoundAssignmentTest {
- private static final String[] OPS = {"&=", "|=", "^=", "<<=", ">>=", ">>>="};
-
- private static long apply(String op, long lhs, long rhs) {
- return switch (op) {
- case "&=" -> lhs & rhs;
- case "|=" -> lhs | rhs;
- case "^=" -> lhs ^ rhs;
- case "<<=" -> lhs << rhs;
- case ">>=" -> lhs >> rhs;
- case ">>>=" -> lhs >>> rhs;
- default -> throw new AssertionError(op);
- };
- }
-
- @Test public void testLocals() {
- for (String op : OPS)
- for (long arg : new long[]{-8, 0, 5, 64}) {
- long expected = apply(op, arg, 3);
- check("int x=arg; int r=(x " + op + " 3); return x*10+r;",
- arg, expected*11);
- }
- }
-
- @Test public void testRhsOnce() {
- for (String op : OPS) {
- long expected = apply(op, 5, 1);
- check("int x=arg; int count=0; int r=(x " + op +
- " (count=count+1)); return count*1000+x*10+r;", 5, 1000+expected*11);
- }
- }
-
- @Test public void testNarrowingAndChaining() {
- check("u8 x=255; x <<= 1; return x;", 0, 254);
- check("u8 x=255; x ^= 256; return x;", 0, 255);
- check("int x=1; int y=3; x |= y <<= 2; return x*100+y;", 0, 1312);
- check("int x=-8; x >>= 65; return x;", 0, -4);
- check("int x=-8; x >>>= 65; return x;", 0, Long.MAX_VALUE-3);
- }
-
- @Test public void testExistingOperators() {
- check("int x=8; x+=2; x-=1; x*=3; x/=9; return x;", 0, 3);
- check("int x=arg; int y=x++; int z=x--; return x*100+y*10+z;", 5, 556);
- check("return (arg << 2) + (arg >> 1) + (arg >>> 1);", 8, 40);
- check("return arg <= 8;", 8, 1);
- check("return arg >= 8;", 8, 1);
- }
-
- @Test public void testRejectedAssignments() {
- for (String op : OPS) {
- rejected("val x=3; x " + op + " 1; return x;");
- rejected("int x=3; x " + op + " 1.5; return x;");
- }
- }
-
- private static void rejected(String src) {
- try {
- check(src, 0, 0);
- fail("Accepted invalid assignment: " + src);
- } catch (RuntimeException e) {
- assertTrue("Must report a language error: " + e, e instanceof Parser.ParseException);
- assertNotNull(e.getMessage());
- assertFalse("Must report a language error: " + e, e.getMessage().contains("Not yet implemented"));
- }
- }
-
- @Test public void testFields() {
- for (String op : OPS) {
- long expected = apply(op, -8, 3);
- check("struct S { int x; }; !S !s=new S; s.x=arg; int r=(s.x " +
- op + " 3); return s.x*10+r;", -8, expected*11);
- }
- }
-
- @Test public void testArrayAddressAndRhsOnce() {
- for (String op : OPS) {
- long expected = apply(op, 5, 1);
- check("int[] !a=new int[2]; a[0]=arg; int i=0; int count=0; " +
- "int r=(a[i++] " + op + " (count=count+1)); " +
- "return i*1000+count*100+a[0]*10+r;", 5, 1100+expected*11);
- }
- }
-
- private static void check(String src, long arg, long expected) {
- 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
deleted file mode 100644
index 366bd7f4..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Eval2.java
+++ /dev/null
@@ -1,540 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.print.IRPrinter;
-import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.util.SB;
-import com.seaofnodes.simple.util.Utils;
-import java.util.*;
-
-public abstract class Eval2 {
-
- // A mapping from Node ID to evaluation data Object.
- // Data Objsts are a boxed Long or Double; or an Object[] of fields, which
- // themselves are just more Objects.
- private static class Frame {
- private static int UID=1;
-
- private final IdentityHashMap<Node,Object> _data;
- private Frame _prior; // Prior frame in this sequence, means when making a new frame we can find old stuff
- private int _refcnt; // TODO: Use this in a sentence (free/recycle unused stack frames)
- final int _uid = UID++; // Debugging aid
- Frame(Frame prior) {
- _prior = prior;
- if( prior!=null ) prior._refcnt++;
- _data = new IdentityHashMap<>();
- _refcnt = 1;
- }
-
- // Dec ref count
- void dec() {
- assert _refcnt>0;
- if( --_refcnt == 0 ) {
- Frame prior = _prior;
- _prior = null;
- _data.clear();
- // TODO: FREE LIST?
- if( prior!=null )
- prior.dec();
- }
- }
-
- // Put a mapping from Node->Object
- Node put(Node n, Object d) {
- assert d!=null;
- return put0(n,d);
- }
- // Put a mapping from Node->Object?
- Node put0(Node n, Object d) {
- assert _refcnt>0;
- _data.put(n,d);
- return n;
- }
- // Get a mapped Node, perhaps using the prior stack frame crawl
- Object get(Node n) {
- assert _refcnt>0;
- Object d = _data.get(n);
- return d != null ? d
- : _prior == null ? null
- : _prior.get(n);
- }
-
- // Short debugging print
- public String p() {
- return "#"+_uid + (_prior==null ? "" : " >> " + _prior.p());
- }
- // Long debugging print
- @Override public String toString() {
- String s = "";
- if( _prior != null ) s+= _prior+" << ";
- s += "#"+_uid+"[ ";
- for( Node n : _data.keySet() )
- s += n + ":" + _data.get(n) + ",";
- s += "]";
-
- return s;
- }
- }
-
- // Classic Closure; an environment (stack of frames) and an execution point
- private static class Closure {
- // Where to return too; either a CallEnd or a Function
- final CFGNode _cc;
-
- // Stack frame at the execution point. Includes Closures which
- // recursively include other stack frames
- private Frame _frame;
-
- Closure(CFGNode cc, Frame frame) {
- _cc = cc; // One of Fun, CallEnd or Stop (null)
- _frame = frame;
- assert _frame._refcnt >=0;
- _frame._refcnt++;
- }
-
- FunNode fun() { return (FunNode)_cc; }
- CallEndNode cend() { return (CallEndNode)_cc; }
- boolean isStop() { return _cc instanceof StopNode; } // or Stop
-
- //
- void pushFrame() {
- _frame = new Frame(_frame);
- _frame._refcnt++;
- }
-
- @Override public String toString() {
- return _cc.toString();
- }
- }
-
- // CodeGen has the linker tables
- public static CodeGen CODE;
-
- // 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
- SB trace = null; // new SB(); // TRACE, set to null for off, new SB() for on
- // Force local scheduling phase
- 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;
-
- // Current value for each Node; boxed Long or Double; or an Object[] of
- // fields, which themselves are just more Objects.
- F = new Frame(null);
-
- // Start at the START
- CFGNode BB = code._start, prior = null;
- // Return from main hits Stop
- F.put(BB,new Closure(code._stop,F));
- // For all <clinit> / Clazzes, install their uninitialized memory
- for( FunNode fun : code._linker )
- if( fun!=null && fun.isClz() ) {
- TypeStruct clz = ((TypeMemPtr)fun.sig().arg(0))._obj;
- // Map from the <clinit> self parameter to the
- // singleton class structure.
- F.put(fun.parm(2),classObject(clz));
- }
-
-
- // ------------
- // Evaluate until exit or timeout. Each outer loop step computes
- // all data nodes under some new Control.
- while( true ) {
- if( BB==null ) return null;
- traceCtrl(BB,trace);
- if( trace!=null ) System.out.println(F.p());
-
- // Parallel assign Phis
- int i = BB instanceof RegionNode r
- ? parallelAssignPhis(r,prior,arg,trace)
- : 0;
-
- // Compute the data nodes until BB control
- for( ; i < BB.nOuts(); i++ )
- if( !(BB.out(i) instanceof CFGNode) )
- step(BB.out(i), trace);
-
- // Find the next control target
- prior = BB;
- BB = BB.uctrl(); // Next unique control target
- switch( prior ) {
- case StartNode start: F = new Frame(F); break; // Frame for the call to main, as-if Called
- case CallNode call : BB = call(call); break;
- case NeverNode never: BB = never.cproj(0); break; // Synthetic exit is never taken.
- case IfNode iff : Object p = val(iff.pred()); BB = iff.cproj( p==null || (p instanceof Long x && x==0L) ? 1 : 0); break;
- case ReturnNode ret : if( clj(ret.rpc()).isStop() ) return exit(ret); else BB = ret(ret); break;
- case FunNode fun : if( loopCnt++ > timeout ) return null; break; // Timeout
- case LoopNode loop : if( loopCnt++ > timeout ) return null; break; // Timeout
- case CallEndNode cend: break;
- case CProjNode cproj: break;
- case RegionNode r : break;
- default: throw Utils.TODO();
- }
- if( BB==null ) return null;
- assert !BB.isDead();
- }
- }
-
-
- // Phi cache; size is limited to number of Phis at a merge point
- private static final Object[] PHICACHE = new Object[256];
- static int parallelAssignPhis(RegionNode r, CFGNode prior, long arg, SB trace) {
-
- // Compute input path on Phis for this Region
- int path = r._inputs.find( prior );
- // Parameters read from prior frame, Phis from local frame
- Frame frame = (r instanceof FunNode ? F._prior : F);
- boolean isClz = r instanceof FunNode fun && fun.isClz();
-
- // Parallel assign Phis. First parallel read and cache
- int i;
- for( i = 0; i < r.nOuts(); i++ ) {
- // Due to scheduling, all Phis are first in the outputs; first
- // non-Phi is the end of all Phis.
- if( !(r.out(i) instanceof PhiNode phi) ) break;
-
- // Parms read from Call args -
- // - except RPC is the actual prior frame
- // - except entry <clinit> takes the external program argument
- Object val;
- 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
- // RPC uses the prior Call directly;
- // Parms may not be linked, so read call args directly
- ? (parm._idx==0 ? prior : prior.in(parm._idx))
- // Phis read from path input
- : phi.in(path);
- // Read the frame before overwriting
- PHICACHE[i] = frame.get(n);
- }
- }
- // Parallel assign; might assign before read, so read from cache
- for( int j=0; j < i; j++ )
- traceData(F.put0(r.out(j),PHICACHE[j]),trace);
- // Return point in basic block past last Phi; the Phis have been
- // evaluated here, and now we need to evaluate the rest of the block
- return i;
- }
-
-
- // Make a call.
- private static CFGNode call( CallNode call ) {
- // Set up the Return PC and return frame (which is just the current frame).
- // Really making a Continuation, but it's just a Closure
- Closure cont = new Closure(call.cend(),F);
- // Set the return continuation into the current frame RPC
- F.put(call, cont );
- // Next Frame to use
- F = new Frame(F);
- // Target function and environment
- return CODE.link(tfp(call.fptr()));
- }
-
- private static CFGNode ret( ReturnNode ret ) {
- // Fetch return expression value and continuation from current frame
- Object rez = val(ret.expr());
- Closure cont = clj(ret.rpc());
- // Lower Frame reference count; might delete frame
- F.dec(); // Lower ref count
- // Install replacement Frame; set return result over the CallEnd
- F = cont._frame;
- F.put0(cont._cc,rez); // Set return value into CallEnd
- return cont._cc; // Return CallEnd as the new control
- }
-
- private static String exit( ReturnNode ret ) {
- return prettyPrint(ret.expr()._type,val(ret.expr()));
- }
-
- // Take a worklist step: one data op from the current Control is updated.
- private static void step(Node n, SB trace) {
- if( n instanceof PhiNode ) return;
- // Compute new value
- F.put0(n,compute(n));
- traceData(n,trace);
- // Also do any following projections, which are not in the local schedule otherwise
- if( n instanceof MultiNode )
- for( Node use : n._outputs )
- step(use,trace);
- }
-
- // From here down shamelessly copied from Evaluator, written by @Xmilia
- private static Object compute( Node n ) {
- return switch( n ) {
- case AddNode add -> add.mode()==1
- ? (Object)(x(add.in(1)) + x(add.in(2)))
- : (Object)(d(add.in(1)) + d(add.in(2)));
- case AndNode and -> x(and.in(1)) & x(and.in(2));
- case BoolNode.EQ eq -> Objects.equals(val(eq.in(1)), val(eq.in(2))) ? 1L : 0L; // Bool EQ supports pointers, nil, and integers
- case BoolNode.LE le -> le.mode()==1
- ? (x(le.in(1)) <= x(le.in(2)) ? 1L : 0L)
- : (d(le.in(1)) <= d(le.in(2)) ? 1L : 0L);
- case BoolNode.LT lt -> lt.mode()==1
- ? (x(lt.in(1)) < x(lt.in(2)) ? 1L : 0L)
- : (d(lt.in(1)) < d(lt.in(2)) ? 1L : 0L);
- case GuardNode guard -> val(guard.in(1));
- case ConstantNode con -> con(con._con);
- case DivNode div -> div.mode()==1
- ? (Object)(x(div.in(2))==0 ? 0L : x(div.in(1)) / x(div.in(2)))
- : (Object)((d(div.in(2))==0 ? 0D : d(div.in(1))) / d(div.in(2)));
- case EscapeNode esc -> esc(esc);
- case FunPtrNode fptr -> con(fptr._con);
- case LoadNode ld -> load(ld);
- case MemMergeNode merge-> "$mem";
- case MinusNode sub -> sub.mode()==1 ? (Object)(- x(sub.in(1))) : (Object)(- d(sub.in(1)));
- case MulNode mul -> mul.mode()==1
- ? (Object)(x(mul.in(1)) * x(mul.in(2)))
- : (Object)(d(mul.in(1)) * d(mul.in(2)));
- case NewNode alloc-> alloc(alloc);
- case NotNode not -> not(not);
- case OrNode or -> x(or .in(1)) | x(or .in(2));
- case ProjNode proj -> proj._type instanceof TypeMem ? "$mem" : val(proj.in(0));
- case ReadOnlyNode read -> val(read.in(1));
- case SarNode sar -> x(sar.in(1)) >> x(sar.in(2));
- case ShlNode shl -> x(shl.in(1)) << x(shl.in(2));
- case ShrNode shr -> x(shr.in(1)) >>>x(shr.in(2));
- case StoreNode st -> store(st);
- case SubNode sub -> sub.mode()==1
- ? (Object)(x(sub.in(1)) - x(sub.in(2)))
- : (Object)(d(sub.in(1)) - d(sub.in(2)));
- case RoundF32Node rnd -> (double)(float)d(rnd.in(1));
- case ToIntegerNode cvt -> (long)d(cvt.in(1));
- case ToFloatNode toflt-> (double)x(toflt.in(1));
- case XorNode xor -> x(xor.in(1)) ^ x(xor.in(2));
- default -> throw Utils.TODO();
- };
- }
-
- // Fetch without unboxing, searching up-Frame
- static Object val( Node n ) { return F.get(n); }
- // Fetch and unbox as primitive long
- static long x( Node n ) { Object d = F.get(n); return d==null ? 0 : (Long)d; }
- // Fetch and unbox as primitive double
- static double d( Node n ) { Object d = F.get(n); return d==null ? 0 : (Double)d; }
- // Fetch and unbox a function constant
- static TypeFunPtr tfp(Node n) {
- return (TypeFunPtr)F.get(n);
- }
- // Fetch and unbox a closure
- static Closure clj(Node n) { return (Closure)F.get(n); }
-
- // Java box ints and floats. Other things can just be null or some informative string
- private static Object con( Type t ) {
- return switch ( t ) {
- case TypeInteger i -> i.isConstant() ? (Long )i.value() : "INT";
- case TypeFloat f -> f.isConstant() ? (Double)f.value() : "FLT";
- case TypeFunPtr tfp -> tfp;
- case TypeMem mem -> "MEM";
- case TypeMemPtr tmp -> {
- if( tmp._obj.isAry() ) { // Constant array ptr
- Type elem = tmp._obj.field("[]")._t;
- if( elem instanceof TypeConAry con ) {
- Object[] xs = new Object[con.len()];
- for( int i=0; i<con.len(); i++ )
- xs[i] = con.at8(i);
- yield xs;
- } else {
- // Generic constant array, used as a default input to a
- // function; should never execute.
- 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);
- }
- }
- default -> null;
- };
- }
-
- // Escapes of constant memory actually needs to set the memory
- private static Object esc(EscapeNode esc) {
- if( esc.priv() instanceof ConstantNode con ) {
- TypeMem mem = (TypeMem)con._type;
- Object val = con(mem._t);
- TypeMemPtr tmp = (TypeMemPtr)esc.self()._type;
- int idx = tmp._obj.findAlias(esc.fld()._alias);
- Object[] fs = (Object[])val(esc.self());
- if( tmp._obj.isAry() ) {
- assert idx==0; // length field
- assert (Long)val==fs.length;
- } else {
- fs[idx] = val;
- }
- }
- return "$mem";
- }
-
-
- // Convert array size to array element count
- private static int offToIdx( long off, TypeStruct t) {
- off -= t.aryBase();
- int scale = t.aryScale();
- assert (off & ((1L << scale)-1)) == 0;
- return (int)(off>>scale);
- }
-
- // Builds and returns a pointer Object
- private static Object[] alloc(NewNode alloc) {
- TypeStruct type = alloc._ts;
- if( type.isAry() ) {
- long sz = (Long)val(alloc.in(1));
- long x = offToIdx(sz, type);
- int n = (int)x;
- if( n!=x || n<0 )
- throw new NegativeArraySizeException(""+n);
- Object[] ary = new Object[n]; // Array body
- var elem = type._fields[1]._t;
- if (elem instanceof TypeInteger) {
- Arrays.fill( ary, 0L );
- } else if (elem instanceof TypeFloat) {
- Arrays.fill( ary, 0D );
- } else {
- assert elem instanceof TypeMemPtr || elem instanceof TypeFunPtr;
- }
- return ary;
- }
-
- return alloc(type);
- }
- private static Object[] alloc(TypeStruct type) {
- assert !type.isAry();
- int num = type._fields.length;
- Object[] ptr = new Object[num];
- for( int i=0; i<num; i++ )
- ptr[i] = con(type._fields[i]._t.makeZero());
- 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 )
- ? 1L : 0L;
- }
-
- private static Object load( LoadNode ld ) {
- Object f = val(ld.ptr());
- // Check for dense constant array
- if( f instanceof TypeMemPtr tmp ) {
- throw Utils.TODO();
- }
- Object[] fs = (Object[])f;
- if( ld._name.equals("#") )
- return (long)fs.length;
- TypeMemPtr tmp = (TypeMemPtr)ld.ptr()._type;
- int idx = tmp._obj.isAry()
- ? offToIdx(x(ld.off()),tmp._obj)
- : tmp._obj.find(ld._name);
- return fs[idx];
- }
-
- private static Object store( StoreNode st ) {
- if( st._name.equals("#") )
- return "$mem"; // Eval stores the length in the java object[], no need to set it now
- TypeMemPtr tmp = (TypeMemPtr)st.ptr()._type;
- Object[] fs = (Object[])val(st.ptr());
- int idx = tmp._obj.isAry()
- ? offToIdx(x(st.off()),tmp._obj)
- : tmp._obj.find(st._name);
- Object val = val(st.val());
- fs[idx] = val;
- return "$mem";
- }
-
-
- // ------------------------------------------------------------------------
- private static final IdentityHashMap<Object,Object> VISIT = new IdentityHashMap<>();
-
-
- static void traceCtrl( CFGNode BB, SB sb ) {
- if( sb==null ) return;
- IRPrinter.printLine(BB,sb.p("--- "));
- System.out.print(sb);
- sb.clear();
- }
- static void traceData( Node n, SB sb ) {
- if( sb==null ) return;
- IRPrinter.printLine(n,sb).unchar();
- _print( n._type, F.get(n), sb.p('\t'), VISIT );
- System.out.println(sb);
- sb.clear();
- VISIT.clear();
- }
-
- // Use type to print x as a string. Uses a new VISIT table so the debugger
- // doesn't try to reuse VISIT if stopping mid-print. The trace code reuses
- // VISIT endlessly for some minor speed hope
- static String prettyPrint( Type t, Object x ) { return _print(t,x,new SB(), new IdentityHashMap<>()).toString(); }
- static SB _print( Type t, Object x, SB sb, IdentityHashMap<Object,Object> visit ) {
- if( x instanceof String s ) return sb.p(s);
- if( x == null ) return sb.p("null");
- return switch( t ) {
- case TypeInteger i -> sb.p( (Long)x);
- case TypeFloat f -> sb.p((Double)x);
- case TypeMemPtr tmp -> {
- if( visit.containsKey(x) ) yield sb.p("$cyclic");
- visit.put(x,x);
-
- Object[] xs = (Object[])x; // Array of fields
- if( tmp._obj.isAry() ) {
- Type elem = tmp._obj._fields[1]._t;
- if( elem.isa(TypeInteger.U8) ) {
- // Shortcut u8[] as a String
- for( Object o : xs )
- sb.p((char)(long)(Long)o);
- } else {
- // Array of elements
- elem.print(sb).p("[ ");
- if( elem instanceof TypeConAry ) elem = TypeInteger.ZERO;
- for( Object o : xs )
- _print( elem, o, sb, visit ).p( "," );
- sb.unchar().p("]");
- }
- } else {
- sb.p(tmp._obj._name).p("{");
- Field[] flds = tmp._obj._fields;
- for( int i=0; i<flds.length; i++ )
- _print( flds[i]._t,xs[i], sb.p(flds[i]._fname).p("="), visit ).p(",");
- sb.unchar().p("}");
- }
- yield sb;
- }
- case TypeFunPtr tfp -> sb.p(x.toString());
- case TypeRPC rpc -> sb.p(x.toString());
- case TypeMem mem -> sb.p("$mem");
- case TypeTuple tt -> {
- if( tt._types.length>1 && tt._types[1] instanceof TypeMemPtr )
- // Assume a NewNode
- yield _print( tt._types[1], x, sb, visit );
- throw Utils.TODO();
- }
- case Type tt -> {
- if( tt == Type.NIL ) yield sb.p("null");
- throw Utils.TODO();
- }
- };
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
deleted file mode 100644
index 68bbe918..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.fuzzer.Fuzzer;
-import java.util.Random;
-import org.junit.Ignore;
-import org.junit.Test;
-import static org.junit.Assert.assertTrue;
-
-/**
- * To use the fuzzer run the <code>fuzzPeeps</code> methods.
- * This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
- */
-public class FuzzerWrap {
-
- private static final long[] REGRESSION_SEEDS = {
- 375135762521757909L, // bad LCA
- -148471672577312953L, // bulk mem
- -5037182906211190034L, // dead Guard control
- -6359653295501938199L, // monotonicity, escape is dead
- 6506797708065910879L, // required field without a user constructor
- 650692970082394944L, // recursive bulk mem split
- 8335100609598232836L, // ternary result killed by region cleanup
- 2520958273643234516L, // nested guard merge
- -3772838984504063325L, // fuzzer regression
- -4628356252269023530L, // idepth cache version wrap
- -4022199781524079249L, // fuzzer regression
- 8824787517620928178L, // unknown-mode unary minus during SCCP
- -673501011619761901L, // peephole killed self
- -8212834489697770130L, // bad keep/unkeep in parser
- 478567486335112458L, // dying escape node
- -5114548365532699915L, // dominate flags dependencies
- -4710931876754133070L, // Tests monotonic phi widening
- };
-
- private static final long[] OPEN_FAILING_SEEDS = {
- };
-
-
- @Test public void fuzzPeepsRegression () { fuzzPeepsSeeds( REGRESSION_SEEDS); }
- @Test @Ignore public void fuzzPeepsOpenFailures() { fuzzPeepsSeeds(OPEN_FAILING_SEEDS); }
-
- private static void fuzzPeepsSeeds(long... seeds) {
- var fuzzer = new Fuzzer();
- for (long seed : seeds)
- fuzzer.fuzzPeepsRegression(seed);
- }
-
- @Ignore
- @Test
- public void fuzzPeepsRandom() {
- Random R = new Random(System.currentTimeMillis());
- var fuzzer = new Fuzzer();
- for (int i=0; i<100; i++)
- fuzzer.fuzzPeeps(R.nextLong());
- assertTrue(fuzzer.noExceptions());
- }
-
-
- @Ignore
- @Test
- public void fuzzPeepsLarge() {
- var fuzzer = new Fuzzer();
- for (int i=0; i<1000000; i++)
- fuzzer.fuzzPeeps(i);
- assertTrue(fuzzer.noExceptions());
- }
-
- @Ignore
- @Test
- public void fuzzPeepTiming() {
- var fuzzer = new Fuzzer();
- int max_nid=0;
- for (int i=0; i<1000000; i++)
- max_nid = fuzzer.fuzzPeepTiming(i, max_nid);
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Main.java b/chapter25/src/test/java/com/seaofnodes/simple/Main.java
deleted file mode 100644
index 1248a6cf..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Main.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import java.nio.file.Files;
-import java.nio.file.Path;
-
-public class Main {
- // Compile and run a simple program
- public static void main( String[] args ) throws Exception {
- // First arg is file, 2nd+ args are program args
- String src = Files.readString(Path.of(args[0]));
- int arg = Integer.valueOf(args[1]);
- CodeGen code = new CodeGen(src);
- 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
deleted file mode 100644
index adeba136..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/MemoryEncodingTest.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.*;
-import java.util.BitSet;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-// Execution regressions are separate from the fixed spill-measurement cohorts.
-public class MemoryEncodingTest {
- @Test public void testLoopLoadSearch() {
- String[] sources = {
- """
- u8[] !program=new u8[106]; u8[] !out=new u8[0];
- int sum=0;
- for(int i=0; i<arg; i++) {
- sum+=program#;
- if(i==1) out=new u8[i+1];
- else if(i==3) out=new u8[2];
- }
- return sum+out#;
- """,
- // One possibly aliasing arm must reject the unchanged-memory proof.
- """
- struct S { int x; }; !S !a=new S; !S !b=new S;
- a.x=10; b.x=20; !S !p=a; if(arg&1) p=b;
- int sum=0;
- for(int i=0; i<6; i++) {
- if(i==2) p.x=30;
- else if(i==4) b.x=40;
- sum+=a.x;
- }
- return sum;
- """,
- // Unchanged memory does not imply an unchanged loop-carried pointer.
- """
- struct S { int x; }; !S !a=new S; !S !b=new S;
- a.x=10; b.x=20; !S !p=a; int sum=0;
- for(int i=0; i<6; i++) {
- if(i==2) a.x=30;
- sum+=p.x;
- p=b; if(i&1) p=a;
- }
- return sum;
- """,
- // All arms fold, but their values differ: build a value Phi.
- """
- struct S { int x; }; !S !a=new S; a.x=1; int sum=0;
- for(int i=0; i<6; i++) {
- sum+=a.x;
- if(i&1) a.x=2;
- else if(i==2) a.x=3;
- else a.x=4;
- }
- return sum;
- """
- };
- // 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).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 &&
- nn.size()._type==com.seaofnodes.simple.type.TypeInteger.constant(110) ? ld : null));
- for( int arg=0; arg<7; arg++ ) {
- long expected=switch(p) {
- case 0 -> 106*arg+(arg>=2 ? 2 : 0);
- case 1 -> (arg&1)==0 ? 140 : 60;
- case 2 -> 130;
- default -> 16;
- };
- assertEquals(Long.toString(expected),Eval2.eval(code,arg));
- }
- }
- }
-
- @Test public void testKeepReadsBeforeWrites() {
- var code = new CodeGen("""
- struct S { int x; int y; };
- !S !a = new S; !S !b = new S;
- a.x=arg; b.x=arg+1;
- !S !p=a; !S !q=b;
- if (arg<0) { p=b; q=a; }
- int v;
- if (arg>1) { v=p.x; p.x=41; p.y=5; }
- else { v=q.x; q.x=42; }
- return v;
- """).driver(CodeGen.Phase.FinalizeIR);
- StopNode stop = code._stop;
- assertEquals(2,countMemoryNodes(stop,LoadNode.class,new BitSet()));
- assertEquals("-1",Eval2.eval(code,-1));
- assertEquals("1",Eval2.eval(code,0));
- assertEquals("3",Eval2.eval(code,3));
- }
-
- private static int countMemoryNodes(Node n, Class<?> kind, BitSet seen) {
- if (n==null || seen.get(n._nid)) return 0;
- seen.set(n._nid);
- int cnt=kind.isInstance(n) ? 1 : 0;
- for (Node def : n._inputs) cnt+=countMemoryNodes(def,kind,seen);
- return cnt;
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java
deleted file mode 100644
index f5d3d146..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/MergeSortTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
-import com.seaofnodes.simple.util.SB;
-import java.io.IOException;
-
-import org.junit.Test;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class MergeSortTest {
-
- @Test public void testMergeSort0() throws IOException {
- // Test merge sort on a small array of primes
- String src =
-"""
-val merge_sort = { int[] a ->
- int[] !b = new int[a#];
- _copy_array (a, 0, a#, b);
- _split_merge(a, 0, a#, b);
-};
-
-val _split_merge = { int[] !b, int begin, int end, int[] a ->
- if (end - begin <= 1)
- return 0;
- int middle = (end + begin) / 2;
- _split_merge(a, begin, middle, b);
- _split_merge(a, middle, end, b);
- _merge(b, begin, middle, end, a);
- return 0;
-};
-
-val _merge = { int[] !b, int begin, int middle, int end, int[] a ->
- int i = begin, j = middle;
-
- for (int k = begin; k < end; k++) {
- // && and ||
- bool cond = false;
- if (i < middle) {
- if (j >= end) cond = true;
- else if (a[i] <= a[j]) cond = true;
- }
- if (cond) b[k] = a[i++];
- else b[k] = a[j++];
- }
-};
-
-val _copy_array = { int[] !a, int begin, int end, int[] b ->
- for (int k = begin; k < end; k++)
- b[k] = a[k];
-};
-""";
- int[] primes = new int[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, };
- SB sb = new SB().p(primes.length).p("[");
- for( int prime : primes )
- sb.p(prime).p(", ");
- String sprimes = sb.p("]").toString();
- TestC.runC(src, "sort", sprimes, 34);
-
-
- EvalRisc5 R5 = TestRisc5.build( src, "merge_sort", 0, 36, false);
- // Allocate an array of primes, filled in reverse order
- int ps = R5._heap;
- R5._heap += (primes.length+1)*8; // 25 primes, and length
- R5.st4(ps,primes.length);
- for( int i=0; i<primes.length; i++ )
- R5.st8(ps+(primes.length-1-i+1)*8,primes[i]);
- // Pass array base when calling `merge_sort`
- R5.regs[riscv.A0] = ps; // Array base to be sorted
-
- int trap = R5.step(100000);
- assertEquals(0,trap);
-
- assertEquals(primes.length,R5.ld4s(ps));
- for( int i=0; i<primes.length; i++ )
- assertEquals(primes[i], R5.ld8(ps+(i+1)*8));
-
- // TODO: replace with ARM Eval
- Chapter21Test.testCPU(src,"arm", "SystemV",36,null);
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java b/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
deleted file mode 100644
index 8bd77c06..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/MutabilityTest.java
+++ /dev/null
@@ -1,67 +0,0 @@
-package com.seaofnodes.simple;
-
-import org.junit.Test;
-import static org.junit.Assert.*;
-import com.seaofnodes.simple.codegen.CodeGen;
-
-public class MutabilityTest {
- private static void ok(String src, String result) {
- 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).driver(CodeGen.Phase.TypeCheck);
- } catch( RuntimeException expected ) {
- String msg = expected.getMessage();
- assertTrue("Expected a permission error, got: "+expected,
- msg!=null && (msg.contains("final") || msg.contains("not of declared type") || msg.contains("Cannot store") || msg.contains("Argument #")));
- return;
- }
- fail("Accepted forbidden write: "+src);
- }
-
- @Test public void testIndependentBindingsAndViews() {
- ok("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); p.x=7; return p.x;", "7");
- ok("struct P { int x; new P = { int v -> x=v; }; }; ~P !p=new P(3); p=new P(7); return p.x;", "7");
- bad("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); p=new P(0); return 0;");
- bad("struct P { int x; new P = { int v -> x=v; }; }; ~P !p=new P(0); p.x=7; return 0;");
- bad("int ~x=3; x=7; return x;");
- ok("int !x=3; x=7; return x;", "7");
- }
-
- @Test public void testDeepViewAndAliases() {
- ok("struct P { int x; new P = { int v -> x=v; }; }; !P ~p=new P(0); ~P ~r=p; p.x=7; return r.x;", "7");
- bad("struct P { int x; new P = { int v -> x=v; }; }; struct Box { !P p; new Box = { !P v -> p=v; }; }; !P p=new P(0); Box b=new Box(p); b.p.x=7; return 0;");
- bad("struct P { int x; new P = { int v -> x=v; }; }; P p=new P(0); !P q=p; q.x=7; return 0;");
- bad("struct P { P? next; int x; new P = { P? v -> next=v; }; }; !P p=new P(new P(null)); p.next.x=7; return 0;");
- bad("struct P { !P? next; int x; new P = { !P? v -> next=v; }; }; P p=new P(new P(null)); p.next.x=7; return 0;");
- }
-
- @Test public void testInferencePreservesAccess() {
- ok("struct P { int x; }; val p=new P; p.x=7; return p.x;", "7");
- bad("struct P { int x; }; val p=new P; p=new P; return 0;");
- bad("struct P { int x; }; P p=new P; var q=p; q.x=7; return 0;");
- }
-
- @Test public void testParameterPermissions() {
- ok("struct P { int x; }; val f={ !P p -> p.x=7; return p.x; }; return f(new P);", "7");
- ok("val f={ int ~x -> return x; }; return f(7);", "7");
- bad("val f={ int ~x -> x=7; return x; }; return f(3);");
- bad("struct P { int x; }; val f={ P p -> p.x=7; return p.x; }; return f(new P);");
- bad("struct P { int x; }; val f={ !P p -> p.x=7; return p.x; }; P p=new P; return f(p);");
- bad("struct P { int x; }; val f={ P?[] a -> return a#; }; !P?[] a=new !P?[1]; return f(a);");
- }
-
- @Test public void testArrayLayers() {
- bad("struct P { int x; }; !P?[] rw=new !P?[1]; P?[] ro=rw; rw[0]=new P; ro[0].x=7; return 0;");
- ok("u8[] a=new u8[2]; a[0]=7; return a[0];", "7");
- ok("struct P { int x; new P = { int v -> x=v; }; }; P?[] a=new P?[1]; a[0]=new P(7); return a[0].x;", "7");
- bad("struct P { int x; new P = { int v -> x=v; }; }; P?[] a=new P?[1]; a[0]=new P(0); a[0].x=7; return 0;");
- ok("struct P { int x; new P = { int v -> x=v; }; }; !P?[] a=new !P?[1]; a[0]=new P(0); a[0].x=7; return a[0].x;", "7");
- bad("u8[~] a=new u8[1]; a[0]=7; return 0;");
- ok("u8[~]?[] rows=new u8[~]?[1]; rows[0]=new u8[1]; return rows#;", "1");
- bad("u8[~]?[] rows=new u8[~]?[1]; rows[0]=new u8[1]; rows[0][0]=7; return 0;");
- bad("u8[]?[~] rows=new u8[]?[1]; rows[0]=new u8[1]; return 0;");
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/NativeExecutionTest.java b/chapter25/src/test/java/com/seaofnodes/simple/NativeExecutionTest.java
deleted file mode 100644
index f3f65183..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/NativeExecutionTest.java
+++ /dev/null
@@ -1,69 +0,0 @@
-package com.seaofnodes.simple;
-
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import org.junit.Test;
-import static org.junit.Assert.*;
-import static org.junit.Assume.assumeTrue;
-
-public class NativeExecutionTest {
- // A subprocess with deterministic stdout, stderr, and process status.
- public static class Child {
- public static void main(String[] args) {
- System.out.println("before exit");
- System.err.println("child diagnostic");
- System.exit(Integer.parseInt(args[0]));
- }
- }
-
- private static String child(int exit) throws IOException {
- String java = Path.of(System.getProperty("java.home"),"bin","java").toString();
- return TestC.exec(java,"-cp",System.getProperty("java.class.path"),
- Child.class.getName(),Integer.toString(exit));
- }
-
- private static void checkFailure(int exit) throws IOException {
- try {
- child(exit);
- fail("Accepted process exit "+exit);
- } catch( IOException e ) {
- assertTrue(e.getMessage(),e.getMessage().contains("exit code: "+exit));
- assertTrue(e.getMessage(),e.getMessage().contains("before exit"));
- assertTrue(e.getMessage(),e.getMessage().contains("child diagnostic"));
- }
- }
-
- @Test public void testNonzeroExit() throws IOException { checkFailure(7); }
-
- @Test public void testFullWindowsExitCode() throws IOException {
- assumeTrue(TestC.OS.startsWith("Windows"));
- checkFailure(256); // Narrowing to a byte incorrectly reports success.
- checkFailure(2816); // Observed Cygwin status for the Hello World crash.
- }
-
- @Test public void testSuccessfulOutput() throws IOException {
- assertEquals("before exit"+System.lineSeparator(),child(0));
- }
-
- @Test public void testHelloWorldArtifacts() throws IOException {
- new Chapter22Test().testHelloWorld();
- new Chapter25Test().testHelloWorld();
- // Model a parallel run paused between Chapter 22's link and execution.
- // Chapter 25 must not replace the executable Chapter 22 is about to run.
- String exe = "build/objs/helloWorld"+(TestC.OS.startsWith("Windows") ? ".exe" : "");
- assertEquals("Hello, World!",TestC.exec(exe));
- }
-
- @Test public void testDriverNonzeroExit() throws Exception {
- Path dir = Files.createTempDirectory(Path.of("build/objs"),"driver-exit-");
- Path src = dir.resolve("fail.smp");
- Files.writeString(src,"return 7;");
- try {
- Simple.main(new String[]{src.toString()});
- fail("Driver accepted a failed executable");
- } catch( IOException e ) {
- assertTrue(e.getMessage(),e.getMessage().contains("exit code: 7"));
- }
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/PhaseTest.java b/chapter25/src/test/java/com/seaofnodes/simple/PhaseTest.java
deleted file mode 100644
index 7252378a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/PhaseTest.java
+++ /dev/null
@@ -1,64 +0,0 @@
-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
deleted file mode 100644
index 2d688bbe..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/Simple.java
+++ /dev/null
@@ -1,257 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.CompUnit;
-import com.seaofnodes.simple.codegen.CodeGen.Phase;
-import com.seaofnodes.simple.print.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.Utils;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.ArrayList;
-import java.util.stream.Collectors;
-
-public class Simple {
- static void usage() {
- System.out.println(
-"""
-simple [options] input-file.smp ...program args...
-Options are applied in-order.
-Options:
- --help - This text
-
- --root root/ - Project root directory; this must end in a '/' and input
- files are relative paths to this. If missing, the one input
- file path is treated as its own root directory.
-
- -o output - Project output directory; similar to --root, if 'output' ends in a
- '/', then all result files use relative paths to 'output', using a input file
- root-relative path. If --root is missing, the one result file is named 'output'
-
- -L path - add an external Simple object search path
- --eval - (slowly) evaluate the compiled code in emulator
- --run - run the compiled code natively; this is the default
- --main - export the top-level initializer as C `main`
- --cpu <cpu-name> - use specific CPU (x86_64_v2, riscv, arm)
- --abi <abi-name> - use specific ABI variant (SystemV)
- --target - print native CPU and ABI
- --version
- -S - dump generated assembler code
- --dump-size - print the size of generated code
- --dump-time - print compilation and execution times
- --dump-after-parse - dump intermediate representation after parse
- --dump-after-iter - dump intermediate representation after pessimistic iter pass
- --dump-after-opto - dump intermediate representation after SCCP
- --dump-after-iter-post-opto - dump after SCCP-driven peepholes and inlining
- --dump-after-finalize-ir - dump after field layout and IR finalization
- --dump-after-type-check - dump intermediate representation after type check pass
- --dump-after-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
- --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 - dump final intermediate representation only
-"""
-);
- System.exit(0);
- }
-
- static void bad_usage() { throw bad("Invalid usage (use --help)"); }
- static RuntimeException bad(String err) {
- System.err.println("ERROR: "+err);
- System.exit(1);
- return new RuntimeException(err);
- }
-
- static void target() {
- System.out.print(TestC.CPU_PORT);
- System.out.print("-");
- System.out.println(TestC.CALL_CONVENTION);
- }
-
- static void print_compilation_times(CodeGen code) {
- long[] times = code._times;
- for( int i=0; i<code._times.length; i++ )
- System.out.printf("%20s %.3f sec%n", Phase.values()[i], times[i]);
- }
-
- static String getInput() {
- BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
- return reader.lines().collect(Collectors.joining("\n"));
- }
-
- public static void main(String[] args) throws Exception {
- String root = null;
- String input_filename = null;
- boolean do_eval = false;
- boolean do_run = true;
- boolean do_codegen = false;
- boolean emit_main = false;
- boolean print_time = false;
- boolean print_size = false;
- boolean print_asm = false;
- int dump = 0;
- int first_arg = 0;
- String src = null;
- String cpu = null;
- String abi = null;
- String out = null;
- ArrayList<String> libPaths = new ArrayList<>();
-
- // Parse command line
- int i; for( i = 0; i < args.length; i++ ) {
- String arg = args[i];
- if( arg.charAt(0) != '-' ) {
- input_filename = arg;
- break;
- }
-
- switch (arg) {
- case "-": input_filename=""; break;
- case "--dump-after-parse": dump |= 1<<Phase.Parse .ordinal() ; break;
- case "--dump-after-iter": dump |= 1<<Phase.Iter .ordinal() ; break;
- case "--dump-after-opto": dump |= 1<<Phase.Opto .ordinal() ; break;
- case "--dump-after-iter-post-opto": dump |= 1<<Phase.IterPostOpto.ordinal(); break;
- case "--dump-after-finalize-ir": dump |= 1<<Phase.FinalizeIR.ordinal() ; break;
- case "--dump-after-type-check": dump |= 1<<Phase.TypeCheck .ordinal() ; break;
- case "--dump-after-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;
- case "--dump-after-local-sched": dump |= 1<<Phase.LocalSched.ordinal() ; break;
- case "--dump-after-reg-alloc": dump |= 1<<Phase.RegAlloc .ordinal() ; break;
- case "--dump-after-encode": dump |= 1<<Phase.Encoding .ordinal() ; break;
- case "--dump-after-export": dump |= 1<<Phase.Export .ordinal() ; break;
- case "--dump": dump |= 1<<Phase.LastPhase .ordinal() ; break;
- case "--dump-after-all": dump |= (2<<Phase.LastPhase .ordinal())-1; break;
- case "-S": print_asm = true; break;
- case "--eval": do_eval = true ; do_run = false; break;
- case "--run": do_run = true ; break;
- case "--norun": do_run = false; break;
- case "--main": emit_main = true; break;
- case "--dump-size": print_size = true; break;
- case "--dump-time": print_time = true; break;
- case "-o": if (out != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
- out = args[++i]; do_codegen=true;
- break;
- case "-L": if (i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
- libPaths.add(args[++i]);
- break;
- case "--cpu": if (cpu != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
- cpu = args[++i];
- break;
- case "--abi": if (abi != null || i + 1 >= args.length || args[i + 1].charAt(0) == '-') bad_usage();
- abi = args[++i];
- break;
- case "--target": target(); break;
- case "--version": throw Utils.TODO();
- case "--help": usage();
- default: bad_usage();
- }
- }
-
- // Break the paths into these parts:
- // - A module path, given from "--root" or inferred from the one input file
- // - Some sources [modpath/relpath/file.smp]*, all relative to the module path.
- // - A build path, given from "-o" or inferred from the one input file
- //
- // All the sources are compiled, with outputs in [buildpath/relpath/file.o]*
-
- if (input_filename == null) throw bad("no input file' (use --help)");
- if( !input_filename.isEmpty() && !input_filename.endsWith(".smp") )
- throw bad("File extension must be .smp");
- Path inPath = Path.of(input_filename);
-
- // Figure out the module path
- Path modPath;
- if( root == null ) {
- modPath = inPath.getParent();
- if( modPath == null )
- modPath = Path.of(".");
- } else {
- throw Utils.TODO();
- }
-
- // Compute the module-relative paths for all sources
- Path srcRelPath = modPath.relativize(inPath);
- int n = srcRelPath.getNameCount();
- Path relPath = n==1 ? null : srcRelPath.subpath(0,n-1);
- // Compute source/class name
- String srcNameExt = srcRelPath.getName(n-1).toString();
- String srcName = srcNameExt.substring(0,srcNameExt.length()-4);
-
- // Compute the build path.
- Path outPath;
- if( out == null ) {
- // If no output given, the build dir is the modPath
- outPath = modPath;
- } else {
- // If the output is a directory, that is our out path
- outPath = Path.of(out);
- if( !out.endsWith("/") ) {
- // If the output is a file, the directory is our outPath
- Path fullOut = outPath;
- outPath = outPath.getParent();
- if( outPath == null )
- outPath = Path.of(".");
- Path foo = outPath.relativize(fullOut);
- if( !foo.toString().equals(srcName+".o") )
- throw bad("Output path needs to be of the form "+srcName+".o");
- }
- }
- String outBase = outPath.resolve(srcName).toString();
-
- if( do_run || print_asm || print_size ) {
- if (cpu == null) cpu = TestC.CPU_PORT;
- if (abi == null) abi = TestC.CALL_CONVENTION;
- do_codegen = true;
- }
- if( cpu == null ) cpu = TestC.CPU_PORT;
- if( abi == null ) abi = TestC.CALL_CONVENTION;
-
- String simpleHome = System.getenv("SIMPLE_HOME");
- if( simpleHome != null && !simpleHome.isEmpty() )
- libPaths.add(Path.of(simpleHome,cpu+"_"+abi).toString());
-
- // Read input file
- try {
- src = input_filename.isEmpty() ? getInput() : Files.readString(Path.of(input_filename));
- } catch( IOException e ) { throw bad("Cannot read input file: "+input_filename); }
-
- // Compilation pipeline
- Ary<String> externPaths = libPaths.isEmpty() ? null : new Ary<>(libPaths.toArray(new String[0]));
- CodeGen code = new CodeGen(modPath.toString(), outPath.toString(), externPaths, srcName, src, 456, true, TypeInteger.BOT );
- code.driver(Phase.LastPhase,cpu,abi, false, emit_main || do_run || do_eval, dump);
-
- if( do_codegen && print_asm )
- System.out.println(code.asm());
-
- if( print_size )
- System.out.printf( "Code Size: %d%n", code._encoding._bits.size());
-
- if( print_time )
- print_compilation_times(code);
-
- if( do_eval ) {
- // TODO: Support for evaluation of functions with different argument numbers and types
- long t = System.currentTimeMillis();
- long arg = (i+1 < args.length) ? Integer.parseInt(args[i+1]) : 0;
- System.out.println(Eval2.eval(code, arg, 100000));
- if( print_time )
- System.out.printf( "EXECUTION TIME: %.3f sec%n", (System.currentTimeMillis() - t) / 1e3);
- }
- if( do_run ) {
- if ( !TestC.CPU_PORT.equals( cpu ) || !TestC.CALL_CONVENTION.equals( abi ) )
- throw bad("cannot run code on not native target");
- String exe = TestC.OS.startsWith("Windows") ? outBase+".exe" : outBase;
- String result = TestC.gcc(outBase+".o", null, null, "", TestC.linkObjs(externPaths), exe);
- System.out.print(result);
- }
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/SpillStats.java b/chapter25/src/test/java/com/seaofnodes/simple/SpillStats.java
deleted file mode 100644
index d43f1a36..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/SpillStats.java
+++ /dev/null
@@ -1,92 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.nio.file.*;
-import java.util.Map;
-import java.util.TreeMap;
-import org.junit.runner.*;
-import org.junit.runner.notification.*;
-
-// Earlier cohorts freeze source/target membership; Chapter 25 retains native checks.
-public class SpillStats extends RunListener {
- private static SpillStats ACTIVE;
- private Description _test;
- private int _qualityFailures;
- private final Map<String,long[]> _totals = new TreeMap<>();
-
- @Override public void testStarted(Description test) { _test = test; }
- @Override public void testFinished(Description test) { _test = null; }
-
- public static void record25(CodeGen code) {
- if( ACTIVE==null || ACTIVE._test==null || ACTIVE._test.getTestClass()!=Chapter25Test.class ) return;
- ACTIVE.record(code,"Chapter25",ACTIVE._test.getMethodName(),code._mach.getClass().getSimpleName(),code._callingConv);
- }
-
- private void record(CodeGen code, String cohort, String test, String cpu, String abi) {
- for( String key : new String[]{cohort+","+cpu+","+abi,cohort+",ALL,ALL"} ) {
- long[] sum = _totals.computeIfAbsent(key,k -> new long[3]);
- sum[0]++; sum[1] += code._regAlloc._spills; sum[2] += code._regAlloc._spillScaled;
- }
- System.out.println("allocation,"+cohort+","+test+","+cpu+","+abi+","+
- code._regAlloc._spills+","+code._regAlloc._spillScaled);
- }
-
- // Keep native result checks running during a quality comparison.
- public static void checkSpills(int expected, int actual) {
- if( expected==-1 || CodeGen.iterSeedOverridden() ) return;
- try { org.junit.Assert.assertEquals("Expect spills:",expected,actual,Math.max(1,expected>>3)); }
- catch( AssertionError error ) {
- if( ACTIVE==null ) throw error;
- ACTIVE._qualityFailures++;
- System.err.println(ACTIVE._test+": "+error.getMessage());
- }
- }
-
- public static void main(String[] args) throws Exception {
- ACTIVE = new SpillStats();
- Path dir = Path.of("src/test/java/com/seaofnodes/simple/spill");
- int failures=0, attempts=0;
- for( String line : Files.readAllLines(dir.resolve("cohorts.tsv")) ) {
- if( line.startsWith("#") || line.isBlank() ) continue;
- attempts++;
- String[] row = line.split("\t");
- try {
- String src = Files.readString(dir.resolve(row[5]));
- CodeGen code = new CheckedCodeGen(null,null,null,null,src,123L,true,TypeInteger.BOT)
- .driver(CodeGen.Phase.RegAlloc,row[2],row[3]);
- ACTIVE.record(code,row[0],row[1],row[2],row[3]);
- } catch( Throwable error ) {
- failures++;
- System.err.println(line);
- error.printStackTrace();
- }
- }
- // Include the library's allocation even when make's sys.o is up to date.
- try {
- CodeGen code = new CheckedCodeGen("src/main/smp","build/spill-sys",null,"sys",
- Files.readString(Path.of("src/main/smp/sys.smp")),456L,true,TypeInteger.BOT)
- .driver(CodeGen.Phase.Encoding,TestC.CPU_PORT,TestC.CALL_CONVENTION);
- ACTIVE.record(code,"Chapter25","sys",TestC.CPU_PORT,TestC.CALL_CONVENTION);
- } catch( Throwable error ) {
- failures++;
- error.printStackTrace();
- }
- JUnitCore junit = new JUnitCore();
- junit.addListener(ACTIVE);
- Result result = junit.run(Chapter25Test.class);
- for( Failure failure : result.getFailures() ) System.err.println(failure.getTrace());
- failures += result.getFailureCount();
- if( failures!=0 ) System.out.println("FAILED: totals are not a complete comparison.");
- System.out.println("cohort,cpu,abi,allocations,splits,scaled");
- for( var row : ACTIVE._totals.entrySet() ) {
- long[] sum = row.getValue();
- System.out.println(row.getKey()+","+sum[0]+","+sum[1]+","+sum[2]);
- }
- System.out.println("Historical allocations: "+attempts+"; native tests: "+result.getRunCount()+"; failures: "+failures+"; spill golden failures: "+ACTIVE._qualityFailures);
- failures += ACTIVE._qualityFailures;
- ACTIVE = null;
- if( failures!=0 ) System.exit(1);
- }
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java b/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java
deleted file mode 100644
index 88d0246d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/StaticDataTest.java
+++ /dev/null
@@ -1,173 +0,0 @@
-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;
-import com.seaofnodes.isa.eval.EvalArm64;
-import com.seaofnodes.isa.eval.EvalRisc5;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import org.junit.Test;
-import static org.junit.Assert.*;
-
-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) {
- 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(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;
- int cp=(e._bits.size()+15)&-16, sd=(cp+e._cpool.size()+15)&-16;
- byte[] image=new byte[1<<20];
- System.arraycopy(e._bits.buf(),0,image,0,e._bits.size());
- System.arraycopy(e._cpool.buf(),0,image,cp,e._cpool.size());
- System.arraycopy(e._sdata.buf(),0,image,sd,e._sdata.size());
- int n=sd+obj.offset+ts.offset(ts.find("n"));
- int f=sd+obj.offset+ts.offset(ts.find("f"));
- if( cpu.equals("arm") ) {
- var arm=new EvalArm64(image,1<<16);
- assertEquals(42,arm.ld4s(n));
- arm._pc=arm.ld4s(f);
- arm.regs[0]=7;
- assertEquals(0,arm.step(1000));
- assertEquals(49,arm.regs[0]);
- } else {
- var r5=new EvalRisc5(image,1<<16);
- assertEquals(42,r5.ld4s(n));
- r5._pc=r5.ld4s(f);
- r5.regs[10]=7;
- assertEquals(0,r5.step(1000));
- assertEquals(49,r5.regs[10]);
- }
- }
- }
-
- @Test public void testNativeImage() throws Exception {
- Path dir=Path.of("build/objs/static-data");
- Files.createDirectories(dir);
- CodeGen code=new CodeGen(null,dir.toString(),null,"Test",SRC,123L,true,
- TypeInteger.BOT)
- .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- TypeStruct ts=(TypeStruct)root(code).type;
- Path driver=dir.resolve("driver.c");
- Files.writeString(driver,"""
- #include <stdint.h>
- #include <string.h>
- extern unsigned char data[] __asm__("Test.$class");
- typedef int64_t (__attribute__((CALL_CONV)) *fun)(int64_t);
- int main(void) {
- int64_t n; uint32_t f;
- memcpy(&n,data+%d,sizeof(n));
- memcpy(&f,data+%d,sizeof(f));
- return n!=42 || ((fun)(uintptr_t)f)(7)!=49;
- }
- """.formatted(ts.offset(ts.find("n")),ts.offset(ts.find("f"))));
- String exe=dir.resolve(TestC.OS.startsWith("Windows")?"test.exe":"test").toString();
- TestC.linkExe(dir.resolve("Test.o").toString(),
- TestC.CALL_CONVENTION.equals("win64")?"ms_abi":"sysv_abi",
- driver.toString(),null,exe);
- assertEquals("",TestC.exec(TestC.TEST_TIMEOUT_SECONDS,exe));
- }
-
- // Native entries must agree too, both in static fields (before <clinit>)
- // and when code materializes the address. The native body may live above 4G.
- @Test public void testNativeFunctionIdentity() throws Exception {
- Path dir=Path.of("build/objs/static-native-functions");
- Files.createDirectories(dir);
- int[] offsets=new int[2];
- for( int i=0; i<2; i++ ) {
- String name=i==0 ? "A" : "B";
- String src="{ i32 -> i32 } abs = \"C\"; val get"+name+"={ -> abs; }; return 0;";
- CodeGen code=new CodeGen(null,dir.toString(),null,name,src,123L,true,TypeInteger.BOT)
- .driver(CodeGen.Phase.TypeCheck);
- assertNull("The native address is initialized in the static image",
- code._stop.walk(n -> n instanceof StoreNode st && st._name.equals("abs") ? n : null));
- code.driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- for( var obj : code._encoding._data.entries() )
- if( obj.symbol.equals(name+".$class") ) {
- TypeStruct ts=(TypeStruct)obj.type;
- offsets[i]=ts.offset(ts.find("abs"));
- }
- }
- Path driver=dir.resolve("driver.c");
- Files.writeString(driver,"""
- #include <stdint.h>
- #include <string.h>
- extern unsigned char a[] __asm__("A.$class"), b[] __asm__("B.$class");
- typedef int32_t (__attribute__((CALL_CONV)) *fun)(int32_t);
- __attribute__((CALL_CONV)) extern fun getA(void), getB(void);
- int main(void) {
- uint32_t x,y;
- memcpy(&x,a+%d,sizeof(x));
- memcpy(&y,b+%d,sizeof(y));
- fun f=(fun)(uintptr_t)x;
- if(!x || x!=y || f!=getA() || f!=getB()) return 1;
- return f(-7)!=7;
- }
- """.formatted(offsets[0],offsets[1]));
- String exe=dir.resolve(TestC.OS.startsWith("Windows")?"test.exe":"test").toString();
- String conv=TestC.CALL_CONVENTION.equals("win64")?"ms_abi":"sysv_abi";
- // Either object's weak definition can win; every reference must agree.
- for( String first : new String[]{"A","B"} ) {
- String second=first.equals("A")?"B":"A";
- TestC.linkExe(dir.resolve(first+".o").toString(),conv,driver.toString(),
- new Ary<>(new String[]{dir.resolve(second+".o").toString()}),exe);
- assertEquals("",TestC.exec(TestC.TEST_TIMEOUT_SECONDS,exe));
- }
- }
-
- // Two separately compiled users must reference the same mutable class object.
- @Test public void testSharedClass() throws Exception {
- Path dir=Path.of("build/objs/static-data-shared");
- Files.createDirectories(dir);
- Path lib=dir.resolve("lib");
- Files.createDirectories(lib);
- var paths=new Ary<>(new String[]{lib.toString()});
- String[] names={"Globals","A","B"};
- String[] sources={"int count=0;",
- "val a={ -> Globals; };", "val b={ -> Globals; };"};
- for( int i=0; i<names.length; i++ )
- new CodeGen(null,(i==0?lib:dir).toString(),i==0?null:paths,names[i],sources[i],123L,true,TypeInteger.BOT)
- .driver(TestC.CPU_PORT,TestC.CALL_CONVENTION,false,false);
- Path driver=dir.resolve("driver.c");
- Files.writeString(driver,"""
- #include <stdint.h>
- __attribute__((CALL_CONV)) extern int64_t *a(void),*b(void);
- int main(void) {
- int64_t *x=a(),*y=b();
- if(x!=y) return 1;
- *x=37;
- return *y!=37;
- }
- """);
- String exe=dir.resolve(TestC.OS.startsWith("Windows")?"test.exe":"test").toString();
- TestC.linkExe(lib.resolve("Globals.o").toString(),
- TestC.CALL_CONVENTION.equals("win64")?"ms_abi":"sysv_abi",
- driver.toString(),new Ary<>(new String[]{dir+"/A.o",dir+"/B.o"}),exe);
- assertEquals("",TestC.exec(TestC.TEST_TIMEOUT_SECONDS,exe));
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java b/chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java
deleted file mode 100644
index f1089cf0..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/TestArm64.java
+++ /dev/null
@@ -1,53 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalArm64;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.FunNode;
-import com.seaofnodes.simple.node.cpus.arm.arm;
-import com.seaofnodes.simple.util.BAOS;
-import java.io.IOException;
-
-import static org.junit.Assert.assertEquals;
-
-// Runs 32-bit ARM code in an emulator
-
-public class TestArm64 {
-
- public static EvalArm64 build( String main, String src, int arg, int spills, boolean print ) throws IOException {
- // Compile and export Simple
- CodeGen code = new CheckedCodeGen(src).driver("arm", "SystemV",true,false);
-
- // Allocation quality not degraded
- int delta = spills>>3;
- if( delta == 0 ) delta =1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() ) assertEquals("Expect spills:", spills, code._regAlloc._spillScaled, delta);
-
- // Image
- byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes)
- EvalArm64 ARM = new EvalArm64(image, 1<<16);
- int start = 0; // Code at offset 0
- int cpool = fill(code._encoding._bits ,image,start);
- int sdata = fill(code._encoding._cpool,image,cpool);
- int end = fill(code._encoding._sdata,image,sdata);
-
- // Initial incoming int arg
- ARM.regs[arm.X0] = arg;
- // malloc memory starts at 64K and goes up
-
- // Look up starting PC or zero if not given
- if( main!=null )
- for( FunNode fun : code._linker )
- if( fun != null && !fun.isDead() && fun._compunit == code.compunit() && main.equals(fun._name) )
- ARM._pc = code._encoding.opStart(fun);
-
- return ARM;
- }
-
- // Fill bits into image; return end point rounded up.
- private static int fill( BAOS bits, byte[] image, int off) {
- System.arraycopy(bits.buf(), 0, image, off, bits.size());
- return (off+bits.size() + 15) & -16;
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestC.java b/chapter25/src/test/java/com/seaofnodes/simple/TestC.java
deleted file mode 100644
index b08be824..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/TestC.java
+++ /dev/null
@@ -1,273 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.util.Ary;
-import com.seaofnodes.simple.util.Utils;
-
-import java.io.File;
-import java.io.IOException;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.concurrent.TimeUnit;
-import static org.junit.Assert.*;
-
-// Call a C driver program, which calls a Simple-generated .o, and self-checks.
-// Expects GCC from the default CLI, compiles C, compiles Simple, links and runs.
-public abstract class TestC {
- public static final int TEST_TIMEOUT_SECONDS = 60;
-
- public static final String OS = System.getProperty("os.name");
- public static final String CPU = System.getProperty("os.arch");
-
- public static final String CALL_CONVENTION = OS.startsWith("Windows") ? "win64" : "SystemV";
- public static final String CPU_PORT = switch( CPU ) {
- case "amd64" -> "x86_64_v2";
- default -> throw Utils.TODO("Map Yer CPU Port Here");
- };
- public static final String CPU_ABI = CPU_PORT+"_"+CALL_CONVENTION;
- public static final String RELEASE_SYS_DIR = "build/release/"+CPU_ABI;
-
- public static String runtimeObject() {
- String home = System.getenv("SIMPLE_HOME");
- String root = home==null || home.isEmpty() ? "build/release" : home;
- return Path.of(root,CPU_ABI,"simple_crt.o").toString();
- }
-
- public static final String C_DRIVERS_DIR = "src/test/java/com/seaofnodes/simple/progs/";
-
- /**
- * Compile, link and run
- * - WITH a C driver
- * - using the default OS/CPU calling convention.
- * - no lib sys
- */
- public static void runC( String src, String base, String expected, int spills) throws IOException {
- String cfile = C_DRIVERS_DIR+base+".c";
- run(src, base, null, CALL_CONVENTION, "", cfile, expected, spills);
- }
-
- /**
- * Compile, link and run
- * - withOUT a C driver
- * - using the default OS/CPU calling convention.
- * - no lib sys
- */
- public static void runSF( String src, String base, String expected, int spills ) throws IOException {
- run(src, base, null, CALL_CONVENTION, null,null, expected,spills);
- }
-
- /**
- * Compile, link and run
- * - withOUT a C driver
- * - using the default OS/CPU calling convention.
- * - WITH a test lib sys
- */
- public static void runSYS( String src, String base, String expected, int spills ) throws IOException {
- Ary<String> externPaths = new Ary<>(new String[]{RELEASE_SYS_DIR});
- run(src, base, externPaths, CALL_CONVENTION, null,null, expected,spills);
- }
-
-
- /**
- * Compile, link and run - general case
- *
- * @param src Program source to compile
- * @param base Compiled binary final base name
- * @param externPaths External symbol search path
- * @param simple_conv Argument convention when calling Simple code
- * @param c_conv Argument convention when calling C code, or null if not
- * linking against a C driver program
- * @param cfile Associated C program which will drive the Simple program,
- * or null if not linking against a C driver program
- * @param expected Expected stdout string
- * @param spills Expected limit on spill code, used to validate the
- * register allocation is reasonable
- */
- public static void run( String src, String base, Ary<String> externPaths, String simple_conv, String c_conv, String cfile, String expected, int spills ) throws IOException {
- run(src, base, externPaths, simple_conv, c_conv, cfile, null, expected, spills);
- }
-
- public static void runArgs( String src, String base, Ary<String> externPaths, String simple_conv,
- String expected, int spills, String... programArgs ) throws IOException {
- run0(src,base,externPaths,simple_conv,null,null,null,expected,spills,programArgs);
- }
-
- public static void run( String src, String base, Ary<String> externPaths, String simple_conv, String c_conv, String cfile, String stdin, String expected, int spills ) throws IOException {
- run0(src,base,externPaths,simple_conv,c_conv,cfile,stdin,expected,spills,new String[0]);
- }
-
- private static void run0( String src, String base, Ary<String> externPaths, String simple_conv, String c_conv,
- String cfile, String stdin, String expected, int spills, String[] programArgs ) throws IOException {
- String exe = compile(src,base,externPaths,simple_conv,c_conv,cfile,spills);
-
- String[] execArgs = new String[programArgs.length+1];
- execArgs[0] = exe;
- System.arraycopy(programArgs,0,execArgs,1,programArgs.length);
- String result = execStdin(stdin, 0, execArgs );
- assertEquals(expected,result);
- }
-
- public static String compile( String src, String base, Ary<String> externPaths, String simple_conv, String c_conv,
- String cfile, int spills ) throws IOException {
- // Simple file base-name example:
- // foo.smp ->
- // build/objs/foo.o - object file
- // build/objs/foo.exe - linked executable Windows
- // build/objs/foo - linked executable Linux
- String pathBase = "build/objs/"+base;
- String obj = pathBase+".o";
- String exe = pathBase+(OS.startsWith("Windows") ? ".exe" : "");
- // Compile simple, emit ELF
- CodeGen code = new CheckedCodeGen(null,"build/objs",externPaths,base,src,126L,true,TypeInteger.BOT);
- code.driver( CPU_PORT, simple_conv, false, cfile==null );
-
- linkExe(obj, c_conv, cfile, linkObjs(externPaths), exe);
-
- // Allocation quality not degraded
- SpillStats.checkSpills(spills,code._regAlloc._spillScaled);
- return exe;
- }
-
- // Link with gcc, and execute the resulting binary, returning stdout as a
- // String. Any errors *assert* instead of returning some error code; this
- // utility is meant to execute inside a JUnit test which will catch the
- // assert and flag the test as failed.
- public static String gcc( String main, double ignore, String... objs ) throws IOException {
- String BLDDIR = "build/objs/test0/";
- String exe = BLDDIR+main+(OS.startsWith("Windows") ? ".exe" : "");
- linkExe(objs[0],null,null,null,exe);
- // Module tests intentionally use the program exit code as their result.
- ExecResult result = execute(null,0,exe);
- return result.exit==0 ? result.out : "exec exit code: "+result.exit;
- }
-
- // Link with gcc, and execute the resulting binary, returning stdout as a
- // String. Any errors *assert* instead of returning some error code; this
- // utility is meant to execute inside a JUnit test which will catch the
- // assert and flag the test as failed.
- public static String gcc( String obj, String c_conv, String cfile, boolean stdin, String... args ) throws IOException {
- return gcc(obj,c_conv,cfile,stdin ? "" : null,null,args);
- }
-
- public static String gcc( String obj, String c_conv, String cfile, String stdin, String... args ) throws IOException {
- return gcc(obj,c_conv,cfile,stdin,null,args);
- }
-
- public static String gcc( String obj, String c_conv, String cfile, String stdin, Ary<String> linkObjs, String... args ) throws IOException {
- linkExe(obj,c_conv,cfile,linkObjs,args[0]);
- return execStdin(stdin,0,args);
- }
-
- public static void linkExe( String obj, String c_conv, String cfile, Ary<String> linkObjs, String exe ) throws IOException {
- // Compile the C program. Compiling code and constants in the low
- // 2Gig. Pointers are 64b BUT since always in the low 2G all the
- // high bits are zero - and Simple code can be emitted treating
- // pointers as 4bytes.
- var params = new Ary<>(String.class);
- params.add("gcc");
- params.add(OS.startsWith("Windows") ? "-Wl,--image-base,0x400000" : "-no-pie");
- if( cfile!=null ) params.add(cfile); // Associated C driver, usually has a `main`
- params.add(obj);
- // Standalone Simple programs export `simple_main`; the small C
- // runtime owns the native main symbol and converts argc/argv into a
- // normal Simple array before making the ABI-qualified call.
- if( cfile==null ) params.add(runtimeObject());
- if( linkObjs != null )
- params.addAll(linkObjs.asAry());
- params.addAll(new String[] {
- "-lm", // Picks up 'sqrt' for newtonFloat tests to compare
- "-g",
- "-o",
- exe,
- });
- // Calling convention for C calls, if any
- if( cfile!=null ) {
- params.add("-D");
- params.add("CALL_CONV="+c_conv);
- }
-
- // Run GCC to link (optionally compile C driver code)
- exec(TEST_TIMEOUT_SECONDS,params.asAry());
- }
-
- public static String exec( String... args ) throws IOException {
- return execStdin(null,0,args);
- }
-
- // Tests can bound execution without imposing a deadline on the CLI driver.
- public static String exec( int seconds, String... args ) throws IOException {
- return execStdin(null,seconds,args);
- }
-
- private static String execStdin( String stdin, int seconds, String... args ) throws IOException {
- ExecResult result = execute(stdin,seconds,args);
- if( result.exit!=0 )
- throw new IOException(String.join(" ",args)+"\nexec exit code: "+result.exit+
- "\nstdout:\n"+result.out+"\nstderr:\n"+result.err);
- return result.out;
- }
-
- private record ExecResult(int exit, String out, String err) {}
-
- private static ExecResult execute( String stdin, int seconds, String... args ) throws IOException {
- // Execute results
- ProcessBuilder smp = new ProcessBuilder(args);
- Path stdinFile = null;
- Path stdoutFile = Files.createTempFile("simple-stdout",".txt");
- Path stderrFile = Files.createTempFile("simple-stderr",".txt");
- Process p = null;
- try {
- smp.redirectOutput(stdoutFile.toFile());
- smp.redirectError(stderrFile.toFile());
- if( stdin!=null && stdin.isEmpty() ) smp.redirectInput(ProcessBuilder.Redirect.INHERIT);
- else if( stdin!=null ) {
- stdinFile = Files.createTempFile("simple-stdin",".txt");
- Files.writeString(stdinFile,stdin);
- smp.redirectInput(stdinFile.toFile());
- }
- p = smp.start();
- if( stdin==null )
- p.getOutputStream().close();
- if( seconds>0 && !p.waitFor(seconds,TimeUnit.SECONDS) ) {
- String process = "pid="+p.pid()+", cpu="+
- p.info().totalCpuDuration().map(Object::toString).orElse("unknown");
- p.destroyForcibly();
- boolean stopped = p.waitFor(5,TimeUnit.SECONDS);
- throw new IOException(String.join(" ",args)+"\ntimed out after "+seconds+" seconds ("+process+")"+
- (stopped ? "" : "; process did not terminate")+
- "\nstdout:\n"+Files.readString(stdoutFile)+
- "\nstderr:\n"+Files.readString(stderrFile));
- }
- // Windows statuses have 32 bits; narrowing can turn a failure into 0.
- int exit = seconds>0 ? p.exitValue() : p.waitFor();
- return new ExecResult(exit,Files.readString(stdoutFile),Files.readString(stderrFile));
- } catch( InterruptedException e ) {
- if( p!=null ) p.destroyForcibly();
- Thread.currentThread().interrupt();
- throw new IOException("interrupted",e);
- } finally {
- if( stdinFile!=null ) Files.deleteIfExists(stdinFile);
- Files.deleteIfExists(stdoutFile);
- Files.deleteIfExists(stderrFile);
- }
- }
-
- static Ary<String> linkObjs(Ary<String> externPaths) {
- if( externPaths == null )
- return null;
- Ary<String> objs = new Ary<>(String.class);
- for( String path : externPaths ) {
- File file = new File(path);
- if( file.isFile() && file.getName().endsWith(".o") )
- objs.add(file.toString());
- else if( file.isDirectory() ) {
- File sys = new File(file,"sys.o");
- if( sys.isFile() )
- objs.add(sys.toString());
- }
- }
- return objs.isEmpty() ? null : objs;
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java b/chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java
deleted file mode 100644
index 10c75d3f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/TestRisc5.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.isa.eval.EvalRisc5;
-
-import com.seaofnodes.simple.codegen.RegAllocTestSupport.CheckedCodeGen;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.codegen.Encoding;
-import com.seaofnodes.simple.node.FunNode;
-import com.seaofnodes.simple.node.cpus.riscv.riscv;
-import com.seaofnodes.simple.util.BAOS;
-import java.io.IOException;
-
-import static org.junit.Assert.*;
-
-// Runs 32-bit R5 code in an emulator
-public abstract class TestRisc5 {
-
- public static EvalRisc5 build( String src, String main, int arg, int spills, boolean print ) throws IOException {
- // Compile and export Simple
- CodeGen code = new CheckedCodeGen(src).driver("riscv", "SystemV",true,false);
-
- // Allocation quality not degraded
- int delta = spills>>3;
- if( delta==0 ) delta = 1;
- if( spills != -1 && !CodeGen.iterSeedOverridden() )
- assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);
-
- // Image
- byte[] image = new byte[1<<20]; // A megabyte (1024*1024 bytes)
- EvalRisc5 R5 = new EvalRisc5(image, 1<<16);
- int start = 0; // Code at offset 0
- int cpool = fill(code._encoding._bits ,image,start);
- int sdata = fill(code._encoding._cpool,image,cpool);
- int end = fill(code._encoding._sdata,image,sdata);
-
- // Initial incoming int arg
- R5.regs[riscv.A0] = arg;
- // malloc memory starts at 64K and goes up
-
- // Look up starting PC or zero if not given
- if( main!=null )
- for( FunNode fun : code._linker )
- if( fun != null && !fun.isDead() && fun._compunit == code.compunit() && main.equals(fun._name) )
- R5._pc = code._encoding.opStart(fun);
-
- return R5;
- }
-
- // Fill bits into image; return end point rounded up.
- private static int fill( BAOS bits, byte[] image, int off) {
- System.arraycopy(bits.buf(), 0, image, off, bits.size());
- return (off+bits.size() + 15) & -16;
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TestRunner.java b/chapter25/src/test/java/com/seaofnodes/simple/TestRunner.java
deleted file mode 100644
index 5389967e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/TestRunner.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package com.seaofnodes.simple;
-
-import org.junit.internal.TextListener;
-import org.junit.runner.JUnitCore;
-import org.junit.runner.Request;
-
-/** Make's single-class/single-method JUnit entry point. */
-public class TestRunner {
- public static void main(String[] args) throws ClassNotFoundException {
- String name=args[0];
- Class<?> test=Class.forName(name.contains(".") ? name : "com.seaofnodes.simple."+name);
- Request request=args[1].isEmpty() ? Request.aClass(test) : Request.method(test,args[1]);
- JUnitCore runner=new JUnitCore();
- runner.addListener(new TextListener(System.out));
- System.exit(runner.run(request).wasSuccessful() ? 0 : 1);
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
deleted file mode 100644
index f6099607..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ /dev/null
@@ -1,223 +0,0 @@
-package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.type.*;
-import org.junit.Assert;
-import org.junit.Test;
-import static org.junit.Assert.assertSame;
-
-public class TypeTest {
-
- // Removing zero must not reset the widening history of a remaining range.
- @Test public void testNonZeroWidening() {
- for( byte widen=0; widen<=3; widen++ ) {
- TypeInteger pos = TypeInteger.make(0,255,widen).nonZero();
- TypeInteger neg = TypeInteger.make(-255,0,widen).nonZero();
- Assert.assertEquals("positive range widening",widen,pos._widen);
- Assert.assertEquals("negative range widening",widen,neg._widen);
- assertSame(TypeInteger.make(1,255,widen),pos);
- assertSame(TypeInteger.make(-255,-1,widen),neg);
- // Singleton ranges retain their widening level too.
- assertSame(TypeInteger.make(1,1,widen),TypeInteger.make(0,1,widen).nonZero());
- assertSame(TypeInteger.make(-1,-1,widen),TypeInteger.make(-1,0,widen).nonZero());
- }
- Assert.assertNull(TypeInteger.ZERO.nonZero());
- }
-
- // Struct hashes omit field types, so interning compares unlike leaf kinds.
- @Test
- public void testCyclicLeafKinds() {
- TypeStruct bottom = TypeStruct.make("B14LeafKinds",false,
- Field.make("x",Type.BOTTOM,2,false));
- TypeStruct one = TypeStruct.make("B14LeafKinds",false,
- Field.make("x",TypeFloat.constant(1.0),2,false));
- TypeStruct two = TypeStruct.make("B14LeafKinds",false,
- Field.make("x",TypeFloat.constant(2.0),2,false));
-
- // Meeting creates a new struct while cyclic equality is active. Its
- // float leaf must compare unequal to the previously interned BOTTOM.
- TypeStruct meet = (TypeStruct)one.meet(two);
- assertSame(TypeFloat.F32,meet._fields[0]._t);
- assertSame(meet,two.meet(one));
- assertSame(meet,TypeStruct.make("B14LeafKinds",false,
- Field.make("x",TypeFloat.F32,2,false)));
- assertSame(bottom,meet.meet(bottom));
- assertSame(meet.dual(),one.dual().join(two.dual()));
- }
-
- // Test basic type properties
- @Test
- public void testTypeAdHoc() {
- Assert.assertEquals( TypeScalar.BOT, TypeInteger.TRUE.meet(TypeNil.NIL) );
- Assert.assertEquals( TypeScalar.BOT, TypeInteger.TOP .meet(TypeNil.NIL) );
-
- TypeStruct s1 = TypeStruct.make("s1",false,
- Field.make("a", TypeInteger.BOT,-1, false),
- Field.make("b", TypeInteger.BOT,-2, false) );
- TypeStruct s2 = TypeStruct.make("s2",false,
- Field.make("a", TypeInteger.BOT,-3, false),
- Field.make("b", TypeInteger.BOT,-4, false) );
- TypeMemPtr p1 = TypeMemPtr.make(s1);
- Assert.assertNotEquals(s1, s1.dual());
-
- TypeMem m1 = TypeMem.make(2,TypeNil.NIL);
- TypeMem m2 = TypeMem.make(3,TypeInteger.U16);
- TypeMem m3 = TypeMem.make(4,TypeFloat.F64);
- TypeMem m4 = TypeMem.make(5,TypeInteger.BOT);
-
- Assert.assertNotEquals(m1, m2);
- Assert.assertNotEquals(m2, m3);
- Assert.assertNotEquals(m3, m4);
-
- Assert.assertEquals(TypeStruct.BOT, s1.meet(s2));
- TypeMem scalarMemBot = TypeMem.make(1,TypeScalar.BOT,false,false,false,XInt.FULL,XInt.FULL);
- Assert.assertEquals(scalarMemBot, ((TypeMem)m1.meet(m2)).makeFrom( XInt.FULL, XInt.FULL));
- Assert.assertEquals(TypeMem.make(1,TypeScalar.BOT), m1.meet(m3));
- Assert.assertEquals(scalarMemBot, ((TypeMem)m3.meet(m4)).makeFrom( XInt.FULL, XInt.FULL));
-
- Assert.assertEquals(TypeMem.make(2,Type.XNIL,true,true,true, XInt.FULL, XInt.FULL), m1.dual());
-
- TypeMemPtr ptr1 = TypeMemPtr.make(s1);
- Assert.assertEquals(s1, ptr1._obj);
- TypeMemPtr ptr2 = TypeMemPtr.make(s2);
- Assert.assertEquals(s2, ptr2._obj);
-
- TypeMemPtr ptr1nil = TypeMemPtr.makeNullable(s1);
- Assert.assertEquals(s1, ptr1nil._obj);
- Assert.assertTrue( ptr1nil.nullable() );
- Assert.assertNotNull( ptr1nil._obj );
- TypeMemPtr ptr2nil = TypeMemPtr.makeNullable( s2);
- Assert.assertEquals(s2, ptr2nil._obj);
-
- Assert.assertNotEquals(ptr1, ptr2);
- Assert.assertEquals(ptr1, ptr1.dual().dual());
- Assert.assertEquals(TypeMemPtr.makeNullable(TypeStruct.BOT).withAccess(false), ptr1.meet(ptr2nil));
-
- TypeMemPtr TOP = TypeMemPtr.TOP;
- TypeMemPtr BOT = TypeMemPtr.makeNullable(TypeStruct.BOT);
- TypeMemPtr PTR = TypeMemPtr.make(TypeStruct.BOT);
- Type NULL = TypeNil.NIL;
- Type PTR_meet_NULL = NULL.meet(PTR);
- Assert.assertEquals(BOT, PTR_meet_NULL);
- Type ptr1_meet_ptr2 = ptr1.meet(ptr2);
- Assert.assertEquals(PTR.withAccess(false), ptr1_meet_ptr2);
- Type NULL_join_ptr1 = NULL.join(ptr1);
- Assert.assertEquals(TypePtr.XNPTR, NULL_join_ptr1);
- Type NULL_join_PTR = PTR.join(NULL);
- Assert.assertEquals(TypePtr.XNPTR, NULL_join_PTR);
- Type ptr1_dual = ptr1.dual();
- Type nullableptr1_dual = ptr1nil.dual();
-
- // Cyclic check
- TypeStruct S1 = ((TypeMemPtr)TypeStruct.SFLT1.field("s1")._t)._obj.close();
- Assert.assertFalse(s1.isFinal());
- Type s1ro = S1.makeRO();
- Type s1ro2 = S1.makeRO();
- Assert.assertSame(s1ro,s1ro2);
- Assert.assertTrue(s1ro.isFinal());
-
- Assert.assertFalse(S1.isConstant());
- }
-
- // Test theoretical properties.
- // This is a symmetric complete bounded (ranked) lattice.
- // Also, the meet is commutative and associative.
- // The lattice has a dual (symmetric), and join is ~(~x meet ~y).
- // See https://en.wikipedia.org/wiki/Lattice_(order).
- @Test
- public void testLatticeTheory() {
- Type[] ts = Type.gather();
-
- // Confirm commutative & complete
- for( Type t0 : ts )
- for( Type t1 : ts ) {
- check_commute (t0,t1);
- check_symmetric(t0,t1);
- }
-
- // Confirm associative
- for( Type t0 : ts )
- for( Type t1 : ts )
- for( Type t2 : ts )
- assoc(t0,t1,t2);
-
- // Confirm symmetry. If A isa B, then A.join(C) isa B.join(C)
- for( Type t0 : ts )
- for( Type t1 : ts )
- if( t0.isa(t1) )
- for( Type t2 : ts ) {
- Type t02 = t0.join(t2);
- Type t12 = t1.join(t2);
- Type mt = t02.meet(t12);
- assertSame(t0+" isa "+t1+" joined "+t2, mt,t12);
- }
- }
-
- // By design in meet, args are already flipped to order _type, which forces
- // symmetry for things with badly ordered _type fields. The question is
- // still interesting for other orders.
- private static void check_commute( Type t0, Type t1 ) {
- if( t0==t1 ) return;
- if( t0.is_simple() && !t1.is_simple() ) return; // By design, flipped the only allowed order
- Type mta = t0.meet(t1);
- Type mtb = t1.meet(t0); // Reverse args and try again
- assertSame(mta,mtb);
- }
-
- // A & B = MT
- // Expect: ~A & ~MT == ~A
- // Expect: ~B & ~MT == ~B
- private static void check_symmetric( Type t0, Type t1 ) {
- if( t1==t0 ) return;
- Type mt = t0.meet(t1);
- Type dm = mt.dual();
- Type d0 = t0.dual();
- Type d1 = t1.dual();
- Type ta = dm.meet(d1);
- Type tb = dm.meet(d0);
- assertSame(t0+" & "+t1+" mt="+mt, ta,d1);
- assertSame(t0+" & "+t1+" mt="+mt, tb,d0);
- }
-
- private static void assoc( Type t0, Type t1, Type t2 ) {
- Type t01 = t0 .meet(t1 );
- Type t12 = t1 .meet(t2 );
- Type t01_2 = t01.meet(t2 );
- Type t0_12 = t0 .meet(t12);
- assertSame(t0+" & "+t1+" & "+t2, t01_2,t0_12);
- }
-
- // Test cyclic types and meets
- @Test
- public void testCyclic0() {
- Type d0 = TypeStruct.SFLT1.dual();
- Type d1 = d0.dual();
- assertSame(TypeStruct.SFLT1,d1);
- }
-
- @Test
- public void testList() {
- TypeStruct list = TypeStruct.open("List");
- TypeMemPtr plist = TypeMemPtr.makeNullable(list);
- list = list.add(Field.make("next", plist, 2, false) );
- list = list.add(Field.make("x", TypeInteger.BOT, 3, false));
- // Make a cyclic type
- list = list.close();
- // Fields are mutable
- Assert.assertFalse(list.isFinal());
- TypeStruct flist = (TypeStruct)list.makeRO();
- Assert.assertTrue(flist.isFinal());
- Assert.assertNotSame( list, flist );
- }
-
- @Test
- public void testOpen() {
- TypeStruct open = TypeStruct.make("List",false);
- TypeMemPtr plist = TypeMemPtr.makeNullable(open);
- TypeStruct list = open.add(Field.make("next", plist, 2, false) );
- list = list.add(Field.make("x", TypeInteger.BOT, 3, false));
- // Make a cyclic type
- list = list.close();
- Type mt = open.meet(list);
- Assert.assertEquals(list,mt);
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
deleted file mode 100644
index 445acb61..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/codegen/FlagSpillTestSupport.java
+++ /dev/null
@@ -1,141 +0,0 @@
-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/PrintRegTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java
deleted file mode 100644
index b255bcf7..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package com.seaofnodes.simple.codegen;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
-import static org.junit.Assert.*;
-public class PrintRegTestSupport {
- public static void check() throws Exception {
- var code = new CodeGen("return 0;").parse();
- var alloc = new RegAlloc(code);
- var n = new ConstantNode(TypeInteger.ZERO);
- var a = new LRG((short)1,null);
- var b = new LRG((short)2,null);
- var c = new LRG((short)3,null);
- a._reg = -1;
- c._leader = b;
- b._leader = a;
- var field = RegAlloc.class.getDeclaredField("_lrgs");
- field.setAccessible(true);
- var map = (java.util.Map<Node,LRG>)field.get(alloc);
- map.put(n,c);
- assertEquals("V1",alloc.reg(n));
- assertEquals(-1,alloc.regnum(n));
- assertSame(c,map.get(n));
- assertSame(b,c._leader);
- assertSame(a,b._leader);
- }
- public static void checkConstantPool() throws Exception {
- var code = new CodeGen("return 0;").parse();
- code._phase = CodeGen.Phase.Encoding;
- var enc = new Encoding(code);
- var t = com.seaofnodes.simple.type.TypeStruct.make("PrintPool",false,
- com.seaofnodes.simple.type.Field.make("x",TypeInteger.ZERO,2,true));
- var n = new ConstantNode(TypeInteger.ZERO);
- enc.largeConstant(n,t,0,2);
- int size = t.size(); // Encoding lays out data before printing the pool.
- for( int i=0; i<size; i++ ) enc._cpool.write(0);
- // Layout queries are forbidden during printing, even if they would
- // happen to return a cached answer without changing the type.
- boolean[] printing = {false};
- var scalar = new com.seaofnodes.simple.type.Type(com.seaofnodes.simple.type.Type.BOTTOM._type) {
- @Override public int alignment() {
- assertFalse("alignment queried by printer",printing[0]);
- return 0;
- }
- @Override public int size() {
- assertFalse("size queried by printer",printing[0]);
- return 1;
- }
- @Override public String str() { return "LayoutProbe"; }
- };
- var scalarNode = new ConstantNode(TypeInteger.TRUE);
- enc._bigCons.put(scalarNode,new Encoding.Relo(scalarNode,scalar,(byte)0,(byte)2));
- enc._cpool.write(0);
- printing[0] = true;
- var offsets = com.seaofnodes.simple.type.TypeStruct.class.getDeclaredField("_offs");
- offsets.setAccessible(true);
- offsets.set(t,null); // Printer must not lazily rebuild the cached layout.
- var field = com.seaofnodes.simple.type.Type.class.getDeclaredField("VISIT");
- field.setAccessible(true);
- var visit = (java.util.Map<Object,Object>)field.get(null);
- var marker = new Object();
- visit.put(marker,t);
- var method = com.seaofnodes.simple.print.ASMPrinter.class.getDeclaredMethod(
- "printConstantPool",int.class,com.seaofnodes.simple.util.SB.class,
- com.seaofnodes.simple.util.BAOS.class,java.util.HashMap.class,boolean.class,String.class);
- method.setAccessible(true);
- try {
- var sb = new com.seaofnodes.simple.util.SB();
- method.invoke(null,0,sb,enc._cpool,enc._bigCons,true,"Constant Pool");
- assertTrue(sb.toString().contains("PrintPool"));
- assertTrue(sb.toString().contains("LayoutProbe"));
- assertNull(offsets.get(t));
- assertSame(t,visit.get(marker));
- assertEquals(1,visit.size());
- } finally { visit.clear(); }
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
deleted file mode 100644
index 292f1ae7..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ /dev/null
@@ -1,352 +0,0 @@
-package com.seaofnodes.simple.codegen;
-
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.node.cpus.x86_64_v2.x86_64_v2;
-import com.seaofnodes.simple.type.TypeInteger;
-import static org.junit.Assert.*;
-
-// Small scheduled machine graphs isolate register constraints from peepholes.
-public class RegAllocTestSupport {
- private static final RegMask A = new RegMask(0), B = new RegMask(1);
- private static class Op extends MachConcreteNode {
- final RegMask _def, _use, _kill;
- final boolean _clone;
- Op(RegMask def, RegMask use, RegMask kill, boolean clone, Node... ins) {
- super(ins); _def=def; _use=use; _kill=kill; _clone=clone; _type=TypeInteger.BOT;
- }
- @Override public String op() { return "testOp"; }
- @Override public RegMask outregmap() { return _def; }
- @Override public RegMask regmap(int i) { return _use; }
- @Override public RegMask killmap() { return _kill; }
- @Override public boolean isClone() { return _clone; }
- @Override public Node copy() { return new Op(_def,_use,_kill,_clone,(Node)null); }
- @Override public void encoding(Encoding enc) { throw new AssertionError(); }
- }
- private static CodeGen graph() {
- CodeGen code = new CodeGen("");
- code._mach = new x86_64_v2(code);
- code._cfg.add(code._start);
- 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);
- Op def = new Op(new RegMask(3L),null,null,false,code._start);
- LRG lrg = alloc.newLRG(def,null);
- LRG neighbor = new LRG((short)99,null);
- neighbor._mask = A;
- lrg.addNeighbor(neighbor); // Uncolored, but its only possible color is A.
- var bias = IFG.class.getDeclaredMethod("biasColorNeighbors",RegAlloc.class,Node.class,RegMask.class);
- bias.setAccessible(true);
- assertEquals((short)1,bias.invoke(null,alloc,def,new RegMaskRW(3L,0L)));
- }
-
- public static void masks() {
- for( int reg : new int[]{0,63,64,65,127} ) {
- RegMask mask = new RegMask(reg);
- assertEquals(reg,mask.firstReg());
- assertEquals(1,mask.size());
- }
- assertEquals(-1,new RegMask(0L).firstReg());
- assertFalse(new RegMask(0L).size1());
- RegMask boundary = new RegMask(Long.MIN_VALUE,Long.MIN_VALUE|1);
- assertEquals(64,boundary.nextReg((short)63));
- assertEquals(127,boundary.nextReg((short)64));
- assertEquals(-1,boundary.nextReg((short)127));
- }
-
- public static void union() {
- CodeGen code = graph();
- Op use = new Op(null,B,null,false,code._start);
- LRG a = new LRG((short)1,null), b = new LRG((short)2,null);
- a._mask = new RegMask(3L);
- b._mask = new RegMask(6L);
- b.machUse(use,(short)2,true);
- LRG both = a.union(b);
- assertNotNull(both._mask);
- assertEquals(1,both._mask.firstReg());
- assertEquals(1,both._mask.size());
- assertSame(use,both._machUse);
- assertEquals(2,both._uidx);
- }
-
- public static void killWithoutResult() {
- CodeGen code = graph();
- Op def = new Op(A,null,null,false,code._start);
- new Op(null,null,A,false,code._start); // Clobbers A, produces no value.
- new Op(null,A,null,false,code._start,def);
- RegAlloc alloc = new RegAlloc(code);
- assertTrue(BuildLRG.run(0,alloc));
- assertFalse("A live value cannot survive a register kill",IFG.build(0,alloc));
- }
-
- public static void nullUseMask() {
- CodeGen code = graph();
- Op def = new Op(A,null,null,false,code._start);
- new Op(null,null,null,false,code._start,def); // Scheduling dependency only.
- RegAlloc alloc = new RegAlloc(code);
- assertTrue(BuildLRG.run(0,alloc));
- assertTrue(IFG.build(0,alloc));
- }
-
- public static void cloneRegisterClass() {
- for( RegMask required : new RegMask[]{B,new RegMask(6L)} ) {
- CodeGen code = graph();
- Op def = new Op(A,null,null,true,code._start);
- Op use = new Op(null,required,null,false,code._start,def);
- Op use2 = new Op(null,required,null,false,code._start,def);
- RegAlloc alloc = new RegAlloc(code);
- alloc.regAlloc(); // Re-cloning an A-only value cannot satisfy B/C uses.
- assertEquals(0,alloc.regnum(def));
- assertTrue(required.test(alloc.regnum(use.in(1))));
- assertTrue(required.test(alloc.regnum(use2.in(1))));
- assertTrue(use.in(1) instanceof SplitNode);
- }
- }
-
- public static void commutativePhi() {
- CodeGen code = graph();
- Op a = new Op(A,null,null,false,code._start);
- Op b = new Op(A,null,null,false,code._start);
- Op add = new Op(A,A,null,false,code._start,a,b) {
- @Override public boolean commutes() { return true; }
- };
- RegionNode region = new RegionNode(null,code._start,code._start);
- new PhiNode("sum",region,add,a);
- code._cfg.add(region);
- assertTrue(BuildLRG.run(0,new RegAlloc(code)));
- }
-
- private static class CallOp extends CallNode implements MachNode {
- CallOp(Node ctrl, Node def) { super(null,ctrl,null,def,def); }
- @Override public RegMask regmap(int i) { return i==2 ? A : i==3 ? B : null; }
- @Override public RegMask outregmap() { return null; }
- @Override public void encoding(Encoding enc) { throw new AssertionError(); }
- }
-
- public static void popularUses() {
- for( int calls=0; calls<=2; calls++ ) {
- CodeGen code = graph();
- RegAlloc alloc = new RegAlloc(code);
- RegionNode block = new RegionNode(null,code._start,code._start);
- Op def = new Op(new RegMask(7L),null,null,false,block);
- LRG lrg = alloc.newLRG(def,null);
- lrg._machDef = def;
- lrg._mask = new RegMask(0L);
- Op ab = new Op(null,new RegMask(3L),null,false,block,def);
- Op bc = new Op(null,new RegMask(6L),null,false,block,def);
- Op a = new Op(null,A,null,false,block,def,def);
- Op b = new Op(null,B,null,false,block,def);
- Op dep = new Op(null,null,null,false,block,def);
- CallOp call = calls>0 ? new CallOp(block,def) : null;
- if( calls==2 ) new CallOp(block,def);
- boolean split = alloc.splitEmptyMaskByUse((byte)0,lrg);
- assertEquals(calls<2,split);
- if( !split ) { assertSame(def,a.in(1)); assertSame(def,b.in(1)); continue; }
- // Two shared copies, even with overlapping masks and repeated inputs.
- assertTrue(a.in(1) instanceof SplitNode);
- assertTrue(b.in(1) instanceof SplitNode);
- assertNotSame(a.in(1),b.in(1));
- assertSame(a.in(1),a.in(2));
- assertSame(b.in(1),ab.in(1));
- assertSame(b.in(1),bc.in(1));
- assertSame(def,dep.in(1)); // A scheduling dependency has no register.
- assertEquals(3,def.nOuts()); // Two copies and the dependency.
- if( call!=null ) {
- assertSame(a.in(1),call.in(2));
- assertSame(b.in(1),call.in(3));
- }
- }
- }
-
- // Check the scheduled graph before encoding adds untyped branches or rewrites tail calls.
- public static class CheckedCodeGen extends CodeGen {
- public CheckedCodeGen(String src) { super(src); }
- public CheckedCodeGen(String modDir, String buildDir, com.seaofnodes.simple.util.Ary<String> externPaths,
- String srcName, String src, long seed, boolean reset, TypeInteger arg) {
- super(modDir,buildDir,externPaths,srcName,src,seed,reset,arg);
- }
- @Override public CodeGen regAlloc() {
- super.regAlloc();
- checkRegisters(this);
- com.seaofnodes.simple.SpillStats.record25(this);
- return this;
- }
- }
-
- public static void topPhi() {
- // Either arm can be the synthetic exit. Both predecessors share a
- // block here so making TOP live would conflict with the real value.
- for( int slot=1; slot<=2; slot++ ) {
- CodeGen code = graph();
- RegAlloc alloc = new RegAlloc(code);
- Op def = new Op(A,null,null,false,code._start);
- Node top = new ConstantNode(com.seaofnodes.simple.type.Type.TOP);
- RegionNode region = new RegionNode(null,code._start,code._start);
- PhiNode phi = new PhiNode("value",region,slot==1 ? top : def,slot==2 ? top : def);
- phi._type = TypeInteger.BOT;
- new Op(null,A,null,false,region,phi);
- code._cfg.add(region);
- assertTrue(BuildLRG.run(0,alloc));
- assertNull(alloc.lrg(top));
- assertSame(alloc.lrg(def),alloc.lrg(phi));
- assertTrue(IFG.build(0,alloc));
- alloc.insertBefore(phi,slot,"test",(byte)0,alloc.lrg(phi));
- assertSame(top,phi.in(slot));
- }
- }
-
- public static void checkRegisters(CodeGen code) {
- assertTrue(code._regAlloc.verifyFunctionLocalEdges());
- for( CFGNode bb : code._cfg )
- for( Node n : bb._outputs ) {
- if( n instanceof MachNode mach ) {
- if( code._regAlloc.regnum(n)>=0 && mach.outregmap()!=null )
- assertTrue(n.getClass().getSimpleName()+"#"+n._nid,code._regAlloc.regnum(n)>=0 && mach.outregmap().test(code._regAlloc.regnum(n)));
- for( int i=n instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ )
- if( n.in(i)!=null && code._regAlloc.regnum(n.in(i))>=0 && mach.regmap(i)!=null )
- assertTrue(n.getClass().getSimpleName()+"#"+n._nid+" input "+i,code._regAlloc.regnum(n.in(i))>=0 && mach.regmap(i).test(code._regAlloc.regnum(n.in(i))));
- if( mach.twoAddress()!=0 )
- assertEquals(code._regAlloc.regnum(n),code._regAlloc.regnum(n.in(mach.twoAddress())));
- }
- if( n instanceof PhiNode && code._regAlloc.regnum(n)>=0 )
- for( int i=n instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ )
- if( n.in(i)._type==com.seaofnodes.simple.type.Type.TOP )
- assertNull(code._regAlloc.lrg(n.in(i)));
- else
- assertEquals(code._regAlloc.regnum(n),code._regAlloc.regnum(n.in(i)));
- }
- }
-
- public static void copyClobber() throws Exception {
- for( boolean killed : new boolean[]{false,true} ) {
- CodeGen code = graph();
- RegAlloc alloc = new RegAlloc(code);
- Op def = new Op(A,null,null,false,code._start);
- alloc.newLRG(def,null)._reg=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 = 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);
- var post = RegAlloc.class.getDeclaredMethod("postColor");
- post.setAccessible(true); post.invoke(alloc);
- assertSame(killed ? lo : def,use.in(1));
- }
- CodeGen pre = graph();
- RegAlloc uncolored = new RegAlloc(pre);
- Op def0 = new Op(A,null,null,false,pre._start);
- uncolored.newLRG(def0,null)._mask=A;
- Op fixed = new Op(A,null,null,false,pre._start);
- uncolored.newLRG(fixed,null)._mask=A;
- 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));
- }
-
- public static void coldLoopSelfConflict() {
- for( boolean phiConflict : new boolean[]{false,true} ) {
- CodeGen code = graph();
- RegAlloc alloc = new RegAlloc(code);
- Op init = new Op(A,null,null,false,code._start);
- LoopNode loop = new LoopNode(null,null,code._start);
- PhiNode phi = new PhiNode("loop",loop,init,null);
- phi._type = TypeInteger.BOT;
- Op back = new Op(A,A,null,false,loop,phi);
- phi.setDef(2,back);
- Op use = new Op(null,A,null,false,loop,phi);
- loop.setDef(2,loop);
- LRG lrg = alloc.newLRG(phi,null);
- lrg._mask = A;
- alloc.union(lrg,init); alloc.union(lrg,back);
- lrg.selfConflict(back);
- if( phiConflict ) lrg.selfConflict(phi);
- assertTrue(alloc.splitSelfConflict((byte)0,lrg));
- assertSame(phi,use.in(1)); // First try leaves the hot definition alone.
- assertSame(back,phi.in(2));
- if( phiConflict ) assertTrue(phi.in(1) instanceof SplitNode);
- // Even if the first attempt made no graph change, retry must split.
- assertTrue(alloc.splitSelfConflict((byte)1,lrg));
- assertTrue(phi.in(2) instanceof SplitNode);
- if( phiConflict ) assertTrue(use.in(1) instanceof SplitNode);
- }
- }
-
- public static void copyForward() throws Exception {
- // Forwarding, fixed operand, two-address tie, intervening clobber,
- // another user, and a scheduling-only edge.
- for( int kind=0; kind<6; kind++ ) {
- CodeGen code = graph();
- RegAlloc alloc = new RegAlloc(code);
- Op def = new Op(A,null,null,false,code._start);
- alloc.newLRG(def,null)._reg=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);
- int twoAddress = kind==2 ? 1 : 0;
- RegMask mask = kind==1 ? B : kind==5 ? null : new RegMask(3L);
- Op use = new Op(null,mask,null,false,code._start,copy) {
- @Override public int twoAddress() { return twoAddress; }
- };
- if( kind==4 ) new Op(null,B,null,false,code._start,copy);
- var post = RegAlloc.class.getDeclaredMethod("postColor");
- post.setAccessible(true); post.invoke(alloc);
- assertSame("copy case "+kind,kind==0 ? def : copy,use.in(1));
- assertEquals(kind==0 ? 0 : 1,alloc._spills);
- }
- }
-
- public static void coalescing() {
- // Merge, incompatible masks, interference, capacity, and adjacency remapping.
- for( int kind=0; kind<5; kind++ ) {
- CodeGen code = graph();
- RegAlloc alloc = new RegAlloc(code);
- Op def = new Op(new RegMask(3L),null,null,false,code._start);
- 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);
- from._mask = kind==1 ? A : new RegMask(3L);
- to._mask = kind==1 ? B : new RegMask(3L);
- from.machDef(def,false); from.machUse(copy,(short)1,false);
- to.machDef(copy,false); to.machUse(use,(short)1,false);
- LRG left = new LRG((short)100,null), right = new LRG((short)101,null);
- if( kind==2 ) { from.addNeighbor(to); to.addNeighbor(from); }
- if( kind>=3 ) {
- from.addNeighbor(left); left.addNeighbor(from);
- if( kind==3 ) { to.addNeighbor(right); right.addNeighbor(to); }
- else { to.addNeighbor(left); left.addNeighbor(to); }
- }
- int fcnt=from.nadj(), tcnt=to.nadj();
- assertTrue(Coalesce.coalesce(0,alloc));
- if( kind==0 || kind==4 ) {
- assertSame(def,use.in(1));
- assertSame(alloc.lrg(def),alloc.lrg(copy));
- assertSame(use,alloc.lrg(def)._machUse);
- assertEquals(1,alloc.lrg(def)._uidx);
- if( kind==4 ) {
- assertEquals(1,left.nadj());
- assertSame(alloc.lrg(def),left._adj.at(0));
- assertSame(left,alloc.lrg(def)._adj.at(0));
- }
- } else {
- assertSame(copy,use.in(1));
- assertNotSame(alloc.lrg(def),alloc.lrg(copy));
- assertEquals(fcnt,from.nadj());
- assertEquals(tcnt,to.nadj());
- }
- }
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
deleted file mode 100644
index 55c2c040..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java
+++ /dev/null
@@ -1,192 +0,0 @@
-package com.seaofnodes.simple.fuzzer;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Eval2;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.Node;
-import java.util.ArrayList;
-import java.util.Objects;
-import java.util.Random;
-import java.util.function.Consumer;
-
-/**
- * Implementation of the fuzzer.
- * Random testcases are generated using the ScriptGenerator.
- * This will generate a script similar to how the parser parses it but instead of parsing it will
- * randomly choose a variation the parser would parse and generate it.
- * These scripts are the parsed by the parser and all exceptions are caught and filtered
- * to only show one occurrence of one problem. This includes script which generate different
- * results when graphs optimized with different worklist seeds are executed.
- * To aid debugging scripts that cause errors are then reduced by applying rules and checking
- * that the same issue persists.
- */
-public class Fuzzer {
-
- private static final int EVAL_TIMEOUT = 10000;
-
- /**
- * List of exceptions already encountered.
- * This is used to filter new exceptions and discard them if they are already found.
- */
- private final ArrayList<Throwable> exceptions = new ArrayList<>();
-
- /**
- * Filter and record an exception in a script.
- * @param e The exception caused by the script
- * @param script The script causing the exception
- * @param reproducer Reproducer which should be used to reproduce the test case.
- */
- private void recordException(Throwable e, String script, Consumer<String> reproducer, long seed) {
- for (var ex : exceptions) {
- if (FuzzerUtils.isExceptionFromSameCause(ex, e)) return;
- }
- exceptions.add(e);
- System.out.println("========== Stack ==========");
- e.printStackTrace(System.out);
- System.out.println("========== Seed ==========");
- System.out.println(seed);
- //System.out.println("========== Code ===========");
- //System.out.println(script);
- System.out.println("========= Reduced =========");
- System.out.println(Reducer.reduce(script, e, reproducer));
- System.out.println("===========================");
- System.out.flush();
- }
-
- private static boolean neq(Object a, Object b) {
- if (Objects.equals(a, b)) return false;
- return true;
- }
-
- /**
- * Check that two graphs result in the same output when supplied with a value
- * @param e1 Evaluator for the first graph
- * @param e2 Evaluator for the second graph
- * @param in The input value to both graphs to test for an equal output
- */
- private static void checkGraphs(CodeGen code1, CodeGen code2, long in) {
- var r1 = Eval2.eval(code1, in, EVAL_TIMEOUT);
- var r2 = Eval2.eval(code2, in, EVAL_TIMEOUT);
- if( r1 == null || r2 == null ) return; // Timeout test invalid
- if( neq(r1, r2) )
- throw new RuntimeException("Different calculations values " + r1 + " vs " + r2);
- }
-
- /**
- * Run checks for script. Compile the script with two different optimizer worklist seeds.
- * Check that exceptions raised in the parser by both methods are the same and only happens if the script may be invalid.
- * If the script was successfully parsed check that both version behave the same.
- * @param script The script to test
- * @param valid If the script is definitely valid. If not some exceptions may be suppressed.
- */
- private static void runCheck(String script, boolean valid) {
- CodeGen code1;
- try {
- code1 = FuzzerUtils.parse(script, 123, true);
- } catch (RuntimeException e1) {
- try {
- FuzzerUtils.parse(script, 456, true);
- } catch (RuntimeException e2) {
- if (FuzzerUtils.isExceptionFromSameCause(e1, e2)) {
- if (!valid || e1.getClass() == Parser.ParseException.class) return;
- } else {
- e1.addSuppressed(e2);
- }
- }
- throw e1;
- }
- CodeGen code2 = FuzzerUtils.parse(script, 456, false);
- checkGraphs(code1, code2, 0);
- checkGraphs(code1, code2, 1);
- checkGraphs(code1, code2, 10);
- }
-
- /**
- * Check that the script does not result in exceptions.
- * @param script The script to test.
- * @param valid If the script is valid or if it might contain syntax errors.
- */
- private void check(String script, boolean valid, long seed) {
- try {
- runCheck(script, valid);
- } catch (Throwable e) {
- recordException(e, script, s->runCheck(s, valid), seed);
- }
- }
-
- /**
- * Run one test with the given seed, and check for peephole counts.
- * @param seed The seed to use for generating this test case
- */
- public int fuzzPeepTiming(long seed, int max_nids) {
- var rand = new Random(seed);
- var sb = new StringBuilder();
- new ScriptGenerator(rand, sb, true).genProgram();
- try {
- var code = new CodeGen(sb.toString());
-
- code.parse();
- int nids = code.UID();
- if( nids <= max_nids ) return max_nids; // Smaller, looking for trends as we grow
- int parse_peeps= code._iter_cnt;
- int parse_nops = code._iter_nop_cnt;
- double parse_nop_ratio = (double)parse_nops/parse_peeps;
- double parse_peeps_per_node = (double)parse_peeps/nids;
-
- 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;
- double iter_peeps_per_node = (double)iter_peeps/nids;
-
- System.out.printf("%6d | Parsing: peeps: %5d, nops: %5d, ratio: %5.3f, last UID: %5d ps/node: %5.3f | Iter: peeps: %6d, nops: %6d, ratio: %5.3f, ps/node: %5.3f\n",
- seed,
- parse_peeps,parse_nops,parse_nop_ratio,nids,parse_peeps_per_node,
- iter_peeps , iter_nops, iter_nop_ratio, iter_peeps_per_node);
- return nids;
- } catch (Throwable e) {
- // ignore exceptions, let the normal fullPeeps catch and reduce,
- // we're interested in valid program optimizations
- }
- return max_nids;
- }
-
-
- /**
- * Run one test with the given seed.
- * @param seed The seed to use for generating this test case
- */
- public void fuzzPeeps(long seed) {
- var rand = new Random(seed);
- var sb = new StringBuilder();
- var valid = new ScriptGenerator(rand, sb, true).genProgram();
- check(sb.toString(), valid, seed);
- }
-
- /**
- * Run one seed as a regression test. Unlike the exploratory fuzzer path,
- * this deliberately does not reduce on failure; old bad seeds should fail
- * fast with the seed visible in the JUnit assertion.
- */
- public void fuzzPeepsRegression(long seed) {
- var rand = new Random(seed);
- var sb = new StringBuilder();
- var valid = new ScriptGenerator(rand, sb, true).genProgram();
- try {
- runCheck(sb.toString(), valid);
- } catch( Throwable e ) {
- AssertionError ae = new AssertionError("Fuzzer regression seed failed: "+seed);
- ae.initCause(e);
- throw ae;
- }
- }
-
-
- /**
- * Check that no exceptions happened.
- * @return true if no exception happened
- */
- public boolean noExceptions() {
- return exceptions.isEmpty();
- }
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
deleted file mode 100644
index 9fe2e405..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java
+++ /dev/null
@@ -1,111 +0,0 @@
-package com.seaofnodes.simple.fuzzer;
-
-import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.codegen.CodeGen;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.node.StopNode;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.io.OutputStream;
-import java.io.PrintStream;
-import java.lang.invoke.MethodHandle;
-import java.lang.invoke.MethodHandles;
-import java.lang.reflect.Field;
-import java.util.BitSet;
-
-/**
- * Some utilities for the fuzzer.
- */
-class FuzzerUtils {
-
- /**
- * Get the val of a private field
- */
- @SuppressWarnings("unchecked")
- public static <T> T getFieldValue(Object obj, String name) throws NoSuchFieldException, IllegalAccessException {
- return (T) (obj instanceof Class<?> clz ? getField(clz, name).get(null) : getField(obj.getClass(), name).get(obj));
- }
-
- /**
- * Get access to a private field
- */
- public static Field getField(Class<?> clz, String name) throws NoSuchFieldException {
- var field = clz.getDeclaredField(name);
- try {
- field.setAccessible(true);
- } catch (Throwable e) {
- // Intentionally left empty
- }
- return field;
- }
-
- /**
- * Try to rethrow an exception or create a runtime exception and throw it.
- */
- public static RuntimeException rethrow(Throwable e) {
- if (e instanceof RuntimeException re) return re;
- if (e instanceof Error er) throw er;
- return new RuntimeException(e);
- }
-
- /**
- * Some problems might trigger debug output. Suppress this output with a null print stream during fuzzing.
- */
- public static final PrintStream NULL_PRINT_STREAM = new PrintStream(OutputStream.nullOutputStream());
-
- /**
- * Check that the messages are similar.
- */
- private static boolean similarMessages(String a, String b) {
- if (a == null || b == null) return true;
- a = a.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized");
- b = b.replaceAll("'\\w+'", "'normalized'").replaceAll("variable \\w+", "variable normalized");
- return a.equals(b);
- }
-
- /**
- * Check if both stack traces have the same first entry. Empty stack traces can happen with NPE optimization
- * and allow the other to be any stack trace.
- */
- private static boolean sameStackTraceOrigin(StackTraceElement[] a, StackTraceElement[] b) {
- if (a == null || a.length == 0 || b == null || b.length == 0) return true; // One or both stack traces are missing.
- return a[0].equals(b[0]);
- }
-
- /**
- * Checks if two exceptions are caused by the same problem.
- */
- public static boolean isExceptionFromSameCause(Throwable a, Throwable b) {
- if (a == null || b == null) return a == b; // Handle the null case
- if (a.getClass() != b.getClass()) return false; // Different classes
- if (!similarMessages(a.getMessage(), b.getMessage())) return false; // Different messages. Null is allowed as wildcard
- var s1 = a.getStackTrace();
- var s2 = b.getStackTrace();
- if (!sameStackTraceOrigin(s1, s2)) return false; // Different stack trace origins
- if (!isExceptionFromSameCause(a.getCause(), b.getCause())) return false; // Different causes
- var o1 = a.getSuppressed();
- var o2 = b.getSuppressed();
- if (o1 == null || o2 == null || o1.length != o2.length) return o1 == o2; // Different suppression
- for (int i=0; i<o1.length; i++) {
- if (!isExceptionFromSameCause(o1[i], o2[i])) return false; // Different suppression
- }
- return true;
- }
-
- /**
- * Parse and optimize using the given worklist seed
- */
- public static CodeGen parse(String script, long workListSeed, boolean resetTypes ) {
- var err = System.err;
- var out = System.out;
- try {
- System.setErr(NULL_PRINT_STREAM);
- System.setOut(NULL_PRINT_STREAM);
- var code = new CodeGen(script, workListSeed, resetTypes);
- return code.driver(CodeGen.Phase.LocalSched);
- } finally {
- System.setErr(err);
- System.setOut(out);
- }
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java
deleted file mode 100644
index f6e12e11..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/Reducer.java
+++ /dev/null
@@ -1,177 +0,0 @@
-package com.seaofnodes.simple.fuzzer;
-
-import java.util.Arrays;
-import java.util.function.BiConsumer;
-import java.util.function.Consumer;
-import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-/**
- * This class is used to reduce test cases created by the fuzzer.
- * It applies rewrite rules to the test case and checks that the
- * test still produces the same exception.
- * This is done in a fixpoint iteration until every rewrite produces
- * no or a different exception.
- */
-public class Reducer {
-
- private interface Replacer extends BiConsumer<StringBuilder, Matcher> {}
-
- /**
- * Holder for the rewrite rules
- */
- private record Replace(Pattern pattern, boolean iterate, Replacer... replacements) {
- Replace(Pattern p, Object... replacements) {
- this(p, replacements[0] == Boolean.TRUE, Arrays.stream(replacements).filter(o->!(o instanceof Boolean)).map(Replace::from).toArray(Replacer[]::new));
- }
- private static Replacer from(Object o) {
- if (o instanceof Replacer r) return r;
- if (o instanceof Integer i) return (s, m)->s.append(m.group(i));
- if (o instanceof String r) return (s, m)->s.append(r);
- throw new IllegalArgumentException();
- }
- }
-
- /**
- * List of rewrite rules.
- */
- private static final Replace[] REPLACEMENTS = {
- new Replace(Pattern.compile("\\b(?:[^\\W0]|\\w\\w+)\\b"), "0", "1"),
- new Replace(Pattern.compile("(?<=\\n|^)[^\\n]+(?=\\n|$)"), true, ""),
- new Replace(Pattern.compile("(?<=[+\\-*/<>=(])--"), ""),
- new Replace(Pattern.compile("\\b--"), "+"),
- new Replace(Pattern.compile("[-!]"), ""),
- new Replace(Pattern.compile("\\b\\+-"), "-"),
- new Replace(Pattern.compile("(?<![+\\-*/<>=&|])(?:-|\\*|/|>=|<=|>>>|>>|>|<<|<|!=|==|&&|\\|\\||&|\\||^)(?![+*/<>=&|])"), "+"),
- new Replace(Pattern.compile("\\?([^;]+):"), "+"),
- new Replace(Pattern.compile("\\{((?:[^{}]|\\{})+)(?:\\n[ \\t]*)?}"), "", "{}", 1),
- new Replace(Pattern.compile("(?<=\\n|^)([ \\t]*)\\{\\n(.*?)\\n\\1}", Pattern.DOTALL), 2),
- new Replace(Pattern.compile("\\(([^()]+)\\)"), 1),
- new Replace(Pattern.compile("\\d+ ?\\+ ?"), ""),
- new Replace(Pattern.compile(" ?\\+ ?\\d+"), ""),
- new Replace(Pattern.compile("\\n\\n+"), "\n"),
- new Replace(Pattern.compile("^\\n+|\\n+$"), ""),
- new Replace(Pattern.compile("else *"), ""),
- new Replace(Pattern.compile("\\bif\\([^()]+\\) *"), ""),
- new Replace(Pattern.compile("\\bwhile\\([^()]+\\) *"), ""),
- new Replace(Pattern.compile("\\bstruct \\w+ \\{[^{}]*}"), "")
- };
-
- /**
- * Predicate to test if the reduced script generates the same error.
- */
- private final Predicate<String> tester;
-
- /**
- * Create a reducer with a test function.
- * @param tester Test function which returns true when the same issue is found in the script.
- */
- private Reducer(Predicate<String> tester) {
- this.tester = tester;
- }
-
- /**
- * Rewrites the script with one rewrite rule.
- */
- private String doReplacement(String script, Pattern pattern, Replacer[] replacements) {
- var matcher = pattern.matcher(script);
- var sb = new StringBuilder();
- var last = 0;
- var skip = 0;
- outer:
- while (matcher.find(last + skip)) {
- sb.append(script, last, matcher.start());
- last = matcher.end();
- skip = last == matcher.start() ? 1 : 0;
- var pos = sb.length();
- for (var replacement : replacements) {
- replacement.accept(sb, matcher);
- var tail = sb.length();
- sb.append(script, last, script.length());
- if (tester.test(sb.toString())) {
- sb.setLength(tail);
- continue outer;
- }
- sb.setLength(pos);
- }
- last = matcher.start();
- skip = 1;
- }
- sb.append(script, last, script.length());
- return sb.toString();
- }
-
- /**
- * Clean up variable names from the random ones generated by the fuzzer.
- */
- private String cleanVariables(String script) {
- var matcher = Pattern.compile("\\b([0-9a-zA-Z_]+) !?([0-9a-zA-Z_]+)").matcher(script);
- int[] num = {0, 0};
- while (matcher.find()) {
- var type = matcher.group(1);
- var var = matcher.group(2);
- if (type.equals("new")) continue;
- String prefix = "v";
- int t = 0;
- if (type.equals("struct")) {
- t = 1;
- prefix = "s";
- }
- if (var.matches(prefix+"\\d+")) continue;
- while (script.contains(prefix+num[t])) num[t]++;
- var n = script.replaceAll("\\b"+var+"\\b", prefix + num[t]);
- if (tester.test(n)) {
- script = n;
- }
- }
- return script;
- }
-
- /**
- * Run all rewrite rules once.
- */
- private String doAllReplacements(String script) {
- for (var replace: REPLACEMENTS) {
- String old;
- do {
- old = script;
- script = doReplacement(script, replace.pattern, replace.replacements);
- } while (replace.iterate && !old.equals(script));
- }
- return script;
- }
-
- /**
- * Reduce the script to a minimal version which produces the same problem with a test function.
- * @param tester Test function which returns true when the same issue is found in the script.
- */
- public static String reduce(String script, Predicate<String> tester) {
- var reducer = new Reducer(tester);
- String old;
- do {
- old = script;
- script = reducer.doAllReplacements(script);
- script = reducer.cleanVariables(script);
- } while (!script.equals(old));
- return script;
- }
-
- /**
- * Reduce the script to a minimal version which produces the same problem with a reproducer.
- * @param ex The exception originally caused by the script
- * @param reproducer Test function which throws an exception which will be checked against ex.
- */
- public static String reduce(String script, Throwable ex, Consumer<String> reproducer) {
- return reduce(script, s->{
- Throwable nex = null;
- try {
- reproducer.accept(s);
- } catch (Throwable e) {
- nex = e;
- }
- return FuzzerUtils.isExceptionFromSameCause(nex, ex);
- });
- }
-
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
deleted file mode 100644
index c5e94047..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ /dev/null
@@ -1,933 +0,0 @@
-package com.seaofnodes.simple.fuzzer;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.node.ScopeNode;
-import com.seaofnodes.simple.type.TypeInteger;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Random;
-import java.util.function.Predicate;
-import static com.seaofnodes.simple.Parser.KEYWORDS;
-
-
-/**
- * Generate a pseudo random script.
- * This generator will generate the same script for two random number generators initialized with the same seed.
- * To generate valid code it implements the parser but instead of parsing it emits the statements and expressions.
- * It is guaranteed to terminate.
- */
-public class ScriptGenerator {
-
- private static final int MAX_STATEMENTS_PER_BLOCK = 5;
- private static final int MAX_NAME_LENGTH = 10;
- private static final int MAX_BLOCK_DEPTH = 4;
- private static final int MAX_EXPRESSION_DEPTH = 10;
-
- /**
- * Number of spaces per indentation
- */
- private static final int INDENTATION = 4;
- /**
- * Valid characters for identifiers. The last 10 are not valid for the first character.
- */
- private static final String VAR_CHARS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789";
-
- /**
- * Flag for a statement that it does not pass on control flow to the next statement.
- */
- private static final int FLAG_STOP = 0x01;
- /**
- * Flag for a statement that it contains a final if statement without an else branch.
- */
- private static final int FLAG_IF_WITHOUT_ELSE = 0x02;
-
- private static class Type {
- final String name;
- Type(String name) {this.name=name;}
- boolean isa(Type other) { return this == other; }
- }
-
- private static class TypeStruct extends Type {
- record Field (String name, Type type, TypeStruct struct) {}
- Field[] fields;
- final TypeNullable nullable = new TypeNullable(this);
- TypeStruct(String name) {
- super(name);
- }
- boolean isa(Type other) { return this == other || other == nullable; }
- }
-
- private static class TypeNullable extends Type {
-
- final Type base;
-
- TypeNullable(Type base) {
- super(base.name+"?");
- this.base = base;
- }
-
- }
-
- private static final Type TYPE_FLT = new Type("flt");
-
- private static class TypeInt extends Type {
-
- TypeInt(String name) {
- super(name);
- }
-
- @Override
- boolean isa(Type other) {
- return super.isa(other) || other == TYPE_FLT || other instanceof TypeInt;
- }
- }
-
-
- private static final TypeInt TYPE_INT = new TypeInt("int");
- private static final TypeInt TYPE_BOOL = new TypeInt("bool");
- private static final TypeInt TYPE_U8 = new TypeInt("u8");
- private static final List<TypeInt> INTTYPES = new ArrayList<>(){{add(TYPE_INT); add(TYPE_BOOL); add(TYPE_U8); }};
-
- private static class Variable {
- final String name;
- final Type declared;
- final Variable shadowing;
- Type type;
- boolean shadowed = false;
-
- Variable(String name, Type type, Variable var) {
- this.name = name;
- this.declared = type;
- this.type = type;
- this.shadowing = var;
- }
- }
-
- /**
- * The random number generator used for random decisions.
- */
- private final Random random;
- /**
- * Indentation depth.
- */
- private int indentation = 0;
- /**
- * Output string builder
- */
- private final StringBuilder sb;
- /**
- * Number of nested loops
- */
- private int loopDepth = 0;
- /**
- * Current scope start in the variables list.
- */
- private int currScopeStart = 0;
- /**
- * The depth of blocks allowed.
- */
- private int depth = MAX_BLOCK_DEPTH;
- /**
- * Current variables in scope.
- */
- private final ArrayList<Variable> variables = new ArrayList<>();
- /**
- * All defined structs.
- */
- private final ArrayList<TypeStruct> structs = new ArrayList<>();
- /**
- * All forward declared structs
- */
- private final ArrayList<TypeStruct> forwardStructs = new ArrayList<>();
- /**
- * Allow to declare structs.
- */
- private boolean allowStructs = true;
- /**
- * If this needs to generate valid scripts or is also allowed to generate invalid ones.
- */
- private boolean generateValid;
- /**
- * If this script might be invalid.
- */
- private boolean maybeInvalid = false;
-
-
- /**
- *
- * @param random the random number generator.
- * @param sb the script will be written to this string builder.
- * @param generateValid if the script should be guaranteed valid or if it is allowed to contain bugs.
- */
- public ScriptGenerator(Random random, StringBuilder sb, boolean generateValid) {
- this.random = random;
- this.sb = sb;
- this.generateValid = generateValid;
- }
-
- /**
- * Random number with log distribution
- * @param max The maximum value
- * @return A random number in the range [0, max) with higher numbers being more uncommon.
- */
- private int randLog(int max) {
- max--;
- int n = 1<<max;
- var num = random.nextInt(n);
- for (int i=max-1; i>=0; i--) {
- if ((num & (1<<i))!=0) return max-i-1;
- }
- return max;
- }
-
- /**
- * Helper function to print indentation.
- * @return sb for chaining
- */
- private StringBuilder printIndentation() {
- return sb.repeat(' ', indentation);
- }
-
- /**
- * Generate a random name for variables. This might be invalid when it is a keyword.
- * @return The random name generated
- */
- private StringBuilder getRandomName() {
- int len = random.nextInt(MAX_NAME_LENGTH) + 1;
- StringBuilder sb = new StringBuilder(len);
- sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length()-10)));
- for (int i=1; i<len; i++)
- sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length())));
- return sb;
- }
-
- /**
- * Should invalid code be generated?
- * @return True if invalid code should be generated
- */
- private boolean generateInvalid() {
- if (generateValid || random.nextInt(1000)>2) return false;
- maybeInvalid = true;
- return true;
- }
-
- /**
- * Lookup a variable by name
- * @param name name of the variable
- * @return The index of the variable in variables or -1
- */
- private int lookup(String name) {
- for (int i=variables.size()-1; i>=0; i--) {
- if (variables.get(i).name.equals(name)) return i;
- }
- return -1;
- }
-
- /**
- * Lookup a variable by name
- * @param name name of the variable
- * @return THe variable of null if not found
- */
- private Variable lookupVar(String name) {
- int idx = lookup(name);
- return idx == -1 ? null : variables.get(idx);
- }
-
- /**
- * Add a variable to the current scope
- * @param name The name of the variable
- * @param type The declared type of the variable
- */
- private void addVariable(String name, Type type) {
- var old = lookupVar(name);
- variables.add(new Variable(name, type, old));
- if (old != null) old.shadowed = true;
- }
-
- /**
- * Pop variables to level to
- * @param to Level until which variables should be popped
- */
- private void popVariables(int to) {
- while (variables.size() > to) {
- var v = variables.removeLast();
- if (v.shadowing != null) v.shadowing.shadowed = false;
- }
- }
-
- /**
- * Get a random visible variable patching pred
- * @param pred The predicate the variable needs to match
- * @return A random visible variable matching pred or null if no variable mached pred.
- */
- private Variable findVisibleVariablesMatching(Predicate<Variable> pred) {
- int num=0;
- for (var v:variables) {
- if (!v.shadowed && pred.test(v)) num++;
- }
- if (num == 0) return null;
- num = random.nextInt(num);
- for (var v:variables) {
- if (!v.shadowed && pred.test(v) && num-- == 0) return v;
- }
- throw new AssertionError();
- }
-
- /**
- * Get a structure by name
- * @param name Name of the structure
- * @return The structure of null
- */
- private TypeStruct getStruct(String name) {
- for (var s:structs) {
- if (s.name.equals(name)) return s;
- }
- for (var s:forwardStructs) if (s.name.equals(name)) return s;
- return null;
- }
-
- /**
- * Generate a variable name. This might be new random name or a name from an outer scope.
- * If the program is allowed to be invalid this can return keywords and already used variable names in the current scope.
- * @return The variable name.
- */
- private String getVarName() {
- if (currScopeStart > 0 && random.nextInt(10) > 7) {
- // Use a variable outside the current scope.
- var v = variables.get(random.nextInt(currScopeStart));
- if (lookup(v.name) < currScopeStart) return v.name;
- if (!generateValid) {
- maybeInvalid = true;
- return v.name;
- }
- }
- // Generate a new random variable name
- StringBuilder sb = getRandomName();
- var v = sb.toString();
- if (!generateValid && (KEYWORDS.contains(v) || lookup(v) >= currScopeStart)) {
- maybeInvalid = true;
- return v;
- }
- while (KEYWORDS.contains(v) || lookup(v) >= currScopeStart) {
- sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length())));
- v = sb.toString();
- }
- return v;
- }
-
- private <T> T randFromList(List<T> list) {
- return list.get(random.nextInt(list.size()));
- }
-
- /**
- * Get a random structure name
- * @return A random structure name
- */
- private String getStructName() {
- if (!structs.isEmpty() && generateInvalid()) return randFromList(structs).name;
- if (!forwardStructs.isEmpty() && generateInvalid()) return randFromList(forwardStructs).name;
-
- // Generate a new random struct name
- StringBuilder sb = getRandomName();
- var v = sb.toString();
- if (!generateValid && (KEYWORDS.contains(v) || getStruct(v) != null)) {
- maybeInvalid = true;
- return v;
- }
- while (KEYWORDS.contains(v) || getStruct(v) != null) {
- sb.append(VAR_CHARS.charAt(random.nextInt(VAR_CHARS.length())));
- v = sb.toString();
- }
- return v;
- }
-
- public TypeInt getIntType() {
- return randFromList(INTTYPES);
- }
-
- /**
- * Get a random type
- * @return A random type
- */
- private Type getType(boolean allowForward) {
- int t = random.nextInt(10);
- if( t<5 || structs.isEmpty() ) return getIntType();
- if( t<7 ) return TYPE_FLT;
- if (allowForward && random.nextInt(10)==0) {
- int i=random.nextInt(forwardStructs.size()+1);
- if (i>0) return forwardStructs.get(i-1).nullable;
- var name = getStructName();
- var struct = new TypeStruct(name);
- forwardStructs.add(struct);
- return struct.nullable;
- }
- TypeStruct struct = randFromList(structs);
- if (random.nextBoolean()) return struct.nullable;
- return struct;
- }
-
- private Type getType() {
- return getType(false);
- }
-
- /**
- * Generates a program and writes it into the string builder supplied in the constructor.
- * @return If the program is allowed to be invalid returns if something potentially invalid was generated.
- */
- public boolean genProgram() {
- var oldGenerateValid = generateValid;
- if (!generateValid && random.nextBoolean()) generateValid = true;
- this.maybeInvalid = false;
- addVariable(ScopeNode.ARG0, TYPE_INT);
- currScopeStart = variables.size();
- if ((genStatements() & FLAG_STOP) == 0) {
- if (generateValid || random.nextInt(10)<7) {
- genReturn();
- } else {
- this.maybeInvalid = true;
- }
- }
- generateValid = oldGenerateValid;
- return !this.maybeInvalid;
- }
-
- /**
- * Generate a list of statements.
- * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the last statement generated
- */
- public int genStatements() {
- var num = random.nextInt(MAX_STATEMENTS_PER_BLOCK * (depth+1));
- for (int i=0; i<num; i++) {
- printIndentation();
- var stop = genStatement();
- sb.append("\n");
- if ((stop & FLAG_STOP) != 0) return stop;
- }
- return 0;
- }
-
- /**
- * Generate a single statement
- * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement
- */
- public int genStatement() {
- return switch (random.nextInt(12)) {
- case 0 -> allowStructs || generateInvalid() ? genStruct() : genAssignment();
- case 1 -> genBlock();
- case 2 -> genIf();
- case 3 -> genCountedLoop();
- case 4 -> genWhile();
- case 5, 6 -> genDecl();
- case 7, 8, 9 -> genAssignment();
- case 10 -> genNullCheck();
- default -> genExit();
- };
- }
-
- /**
- * Generate a new structure
- * @return 0
- */
- public int genStruct() {
- int idx = random.nextInt(forwardStructs.size()+10);
- TypeStruct struct;
- if (idx < 10) {
- var name = getStructName();
- struct = new TypeStruct(name);
- forwardStructs.add(struct);
- } else {
- struct = forwardStructs.get(idx-10);
- }
- var fields = new TypeStruct.Field[generateInvalid() ? 0 : random.nextInt(10)];
- sb.append("struct ").append(struct.name).append(" {\n");
- indentation += INDENTATION;
- for (int i=0; i<fields.length; i++) {
- var fieldName = getRandomName();
- var type = getType(true);
- printIndentation().append(declType(type)).append(" !").append(fieldName).append(";\n");
- fields[i] = new TypeStruct.Field(fieldName.toString(), type, struct);
- }
- indentation -= INDENTATION;
- printIndentation().append("};");
- struct.fields = fields;
- forwardStructs.remove(struct);
- structs.add(struct);
- return 0;
- }
-
- /**
- * Generate a statement for if, else and while blocks.
- * This is special since it disallows declarations and prefers to generate blocks.
- * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement
- */
- public int genStatementBlock() {
- var oldAllowStructs = allowStructs;
- allowStructs = false;
- indentation += INDENTATION;
- int res;
- if (depth == 0) {
- res = random.nextBoolean() ? genAssignment() : genExit();
- } else {
- depth--;
- res = switch (random.nextInt(12)) {
- case 1, 2, 3, 4, 5 -> {
- depth++;
- indentation -= INDENTATION;
- var ret = genBlock();
- indentation += INDENTATION;
- depth--;
- yield ret;
- }
- case 6 -> genIf();
- case 7 -> genCountedLoop();
- case 8 -> genWhile();
- case 9 -> genAssignment();
- case 10 -> genNullCheck();
- default -> genExit();
- };
- depth++;
- }
- indentation -= INDENTATION;
- allowStructs = oldAllowStructs;
- return res;
- }
-
- /**
- * Generate an exit. This can be return or break and continue when in a loop.
- * @return FLAG_STOP
- */
- public int genExit() {
- if (loopDepth == 0 && generateValid) return genReturn();
- return switch (random.nextInt(7)) {
- case 0,1,2 -> {
- if (loopDepth == 0) maybeInvalid = true;
- sb.append("continue;");
- yield FLAG_STOP;
- }
- case 3,4,5 -> {
- if (loopDepth == 0) maybeInvalid = true;
- sb.append("break;");
- yield FLAG_STOP;
- }
- default -> genReturn();
- };
- }
-
- /**
- * Generate a block statement. After a certain depth only empty blocks are generated to ensure termination of the generator.
- * @return flag FLAG_STOP for the generated statement
- */
- public int genBlock() {
- if (depth == 0) {
- sb.append("{}");
- return 0;
- }
- var oldAllowStructs = allowStructs;
- var oldCSS = currScopeStart;
- allowStructs = false;
- currScopeStart = variables.size();
- depth--;
- sb.append("{\n");
- indentation += INDENTATION;
- var stop = genStatements();
- indentation -= INDENTATION;
- printIndentation().append("}");
- popVariables(currScopeStart);
- depth++;
- currScopeStart = oldCSS;
- allowStructs = oldAllowStructs;
- return stop & ~FLAG_IF_WITHOUT_ELSE;
- }
-
- /**
- * Generate an if statement with an optional else block.
- * @return flags FLAG_STOP and FLAG_IF_WITHOUT_ELSE for the generated statement
- */
- public int genIf() {
- sb.append("if(");
- genExpression(TYPE_BOOL, true, true);
- sb.append(") ");
- var stop = genStatementBlock();
- if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) {
- sb.append("\n");
- printIndentation().append("else ");
- stop &= genStatementBlock();
- } else {
- stop = FLAG_IF_WITHOUT_ELSE;
- }
- return stop;
- }
-
- /**
- * Generate a block with a null check
- * if (nullable) { handle_nullable_as_non_nullable; }
- */
- public int genNullCheck() {
- var v = findVisibleVariablesMatching(va->va.declared instanceof TypeNullable);
- if (v == null) return genIf();
- var n = (TypeNullable)v.declared;
- boolean negate = random.nextBoolean();
- sb.append("if(");
- if (negate) sb.append("!");
- sb.append(v.name).append(")");
- if (!negate) v.type = n.base;
- var stop = genStatementBlock();
- v.type = n;
- if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) {
- sb.append("\n");
- printIndentation().append("else ");
- if (negate) v.type = n.base;
- stop &= genStatementBlock();
- v.type = n;
- } else {
- stop = FLAG_IF_WITHOUT_ELSE;
- }
- return stop;
- }
-
- /**
- * Generate a while loop.
- * @return 0
- */
- public int genWhile() {
- sb.append("while(");
- genExpression(TYPE_BOOL, true, true);
- sb.append(") ");
- loopDepth++;
- genStatementBlock();
- loopDepth--;
- return 0;
- }
-
- /**
- * Generate a counted loop of the form
- * <code>
- * {
- * int varname = start_expr;
- * while (varname < end_expr) loop_body;
- * }
- * </code>
- * @return 0
- */
- public int genCountedLoop() {
- if (depth == 0) return genWhile();
- var oldCSS = currScopeStart;
- currScopeStart = variables.size();
- sb.append("{\n");
- indentation += INDENTATION;
- String name = getVarName();
- printIndentation().append("int ").append(name).append("=");
- genExpression(TYPE_INT, true, false);
- sb.append(";\n");
- addVariable(name, TYPE_INT);
- printIndentation().append("while(").append(name).append("<");
- genShift(TYPE_INT, true, false, MAX_EXPRESSION_DEPTH);
- sb.append(") {\n");
- indentation += INDENTATION;
- depth--;
- loopDepth++;
- printIndentation().append(name).append("=").append(name).append("+");
- genExpression(TYPE_INT, true, false);
- sb.append(";\n");
- genStatements();
- loopDepth--;
- depth++;
- indentation -= INDENTATION;
- printIndentation().append("}\n");
- indentation -= INDENTATION;
- printIndentation().append("}");
- popVariables(currScopeStart);
- currScopeStart = oldCSS;
- return 0;
- }
-
- // This generator mutates references and fields; request both permissions.
- private static String declType(Type type) {
- return (type instanceof TypeStruct || type instanceof TypeNullable ? "!" : "") + type.name;
- }
-
- /** Generate a declaration statement. */
- public int genDecl() {
- var type = getType();
- var name = getVarName();
- // Always make them mutable
- sb.append(generateInvalid() ? getRandomName() : declType(type)).append(" !").append(name);
- if (!(type instanceof TypeNullable) || random.nextBoolean()) {
- sb.append("=");
- genExpression(type, true, type==TYPE_BOOL);
- }
- sb.append(";");
- addVariable(name, type);
- return 0;
- }
-
- /**
- * Generate an assignment statement.
- * @return 0
- */
- public int genAssignment() {
- Type type;
- Type declared;
- if (generateInvalid()) {
- sb.append(getRandomName());
- type = getType();
- declared = type;
- } else {
- var v = findVisibleVariablesMatching(va->true);
- if (v == null) return genDecl();
- sb.append(v.name);
- type = v.type;
- declared = v.declared;
- }
- if (generateInvalid()) {
- var name = getRandomName();
- sb.append(".").append(name);
- declared = getType();
- } else if (type instanceof TypeStruct s && s.fields.length > 0 && random.nextBoolean()) {
- var field = s.fields[random.nextInt(s.fields.length)];
- sb.append(".").append(field.name);
- declared = field.type;
- }
- sb.append("=");
- genExpression(declared, true, declared==TYPE_BOOL);
- sb.append(";");
- return 0;
- }
-
- /**
- * Generate a return statement.
- * @return FLAG_STOP
- */
- public int genReturn() {
- var type = getType();
- sb.append("return ");
- genExpression(type, true, false);
- sb.append(";");
- return FLAG_STOP;
- }
-
- public void genExpression(Type type, boolean change, boolean preferLogical) {
- genExpression(type, change, preferLogical, MAX_EXPRESSION_DEPTH);
- }
-
- public void genExpression(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- if (d > 0 && random.nextInt(10)==0) {
- genLogical(TYPE_INT, true, true, d);
- sb.append("?");
- genExpression(type, true, preferLogical, d-1);
- sb.append(":");
- genExpression(type, false, preferLogical, d-1);
- } else {
- genLogical(type, false, preferLogical, d);
- }
- }
-
- public void genLogical(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- genBitwise(type, false, preferLogical, d);
- while (d-->0 && random.nextInt(preferLogical?5:50) == 0) {
- sb.append(random.nextBoolean()?"&&":"||");
- genBitwise(type, true, preferLogical, d);
- }
- }
-
- public void genBitwise(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- genComparison(type, false, preferLogical, d);
- while (type instanceof TypeInt && d-->0 && random.nextInt(preferLogical?50:5)==0) {
- sb.append("&|^".charAt(random.nextInt(3)));
- genComparison(type, true, preferLogical, d);
- }
- }
-
- public void genComparison(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- if (d > 0 && type instanceof TypeInt && random.nextInt(preferLogical?5:50)==0) {
- type = getType();
- genComparison(type, true, preferLogical, d-1);
- sb.append(random.nextBoolean()?"==":"!=");
- }
- genChaining(type, false, preferLogical, d);
- }
-
- public void genChaining(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- if (d>0 && type instanceof TypeInt && random.nextInt(preferLogical?5:50)==0) {
- genShift(TYPE_INT, true, false, d);
- char c = random.nextBoolean()?'>':'<';
- sb.append(c);
- if (random.nextInt(4)==0) sb.append('=');
- genShift(TYPE_INT, true, false, --d);
- while (d-->0 && random.nextInt(preferLogical?5:50)==0) {
- if (generateInvalid()) c = c=='>' ? '<' : '>';
- sb.append(c);
- if (random.nextInt(4)==0) sb.append('=');
- genShift(TYPE_INT, true, false, d);
- }
- } else {
- genShift(type, false, preferLogical, d);
- }
- }
-
- private static final String[] SHIFTS = {">>", ">>>", "<<"};
-
- public void genShift(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- genAddition(type, false, preferLogical, d);
- while (d-- > 0 && type instanceof TypeInt && random.nextInt(50)==0) {
- sb.append(SHIFTS[random.nextInt(3)]);
- genAddition(type, true, preferLogical, d);
- }
- }
-
- public void genAddition(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- genMultiplication(type, false, preferLogical, d);
- while (d-- > 0 && (type instanceof TypeInt || type==TYPE_FLT) && random.nextInt(50)==0) {
- sb.append(random.nextBoolean()?" + ":" - ");
- genMultiplication(type, true, preferLogical, d);
- }
- }
-
- public void genMultiplication(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- genUnary(type, false, preferLogical, d);
- while (d-- > 0 && (type instanceof TypeInt || type==TYPE_FLT) && random.nextInt(50)==0) {
- sb.append(random.nextBoolean()?'*':'/');
- genUnary(type, true, preferLogical, d);
- }
- }
-
- /**
- * Generate a unary expression.
- */
- public void genUnary(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- if (!(type instanceof TypeInt) && type != TYPE_FLT) {
- genSuffix(type, false, preferLogical, d);
- return;
- }
- while (d>0 && random.nextInt(5)==0) {
- d--;
- boolean what = type instanceof TypeInt && random.nextInt(preferLogical?2:20) == 0;
- sb.append(what?'!':'-');
- }
- if (random.nextInt(10)==0) {
- var t = type;
- var v = findVisibleVariablesMatching(va->va.type.isa(t));
- if (v != null) {
- sb.append(random.nextBoolean()?"--":"++").append(v.name);
- return;
- }
- }
- genSuffix(type, false, preferLogical, d);
- }
-
- public TypeStruct.Field getField(Type type) {
- int n=0;
- for (var s: structs) {
- for (var f: s.fields) {
- if (f.type.isa(type)) n++;
- }
- }
- if (n==0) return null;
- n = random.nextInt(n);
- for (var s: structs) {
- for (var f: s.fields) {
- if (f.type.isa(type)) if (n--==0) return f;
- }
- }
- throw new AssertionError();
- }
-
- public void genSuffix(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- if (d > 0 && random.nextBoolean() && genField(type, d-1)) return;
- if (d > 0 && random.nextInt(10) == 0) {
- if (random.nextBoolean() && genField(type, d-1)) return;
- var t = type;
- var v = findVisibleVariablesMatching(va -> va.type.isa(t));
- if (v != null) {
- sb.append(v.name).append(random.nextBoolean() ? "--" : "++");
- return;
- }
- }
- genPrimary(type, false, preferLogical, d);
- }
-
- public boolean genField(Type type, int d) {
- if (generateInvalid()) {
- var field = getRandomName();
- var t = getType();
- genSuffix(t, true, false, d);
- sb.append(".").append(field);
- } else {
- var field = getField(type);
- if (field == null) return false;
- genSuffix(field.struct, true, false, d);
- sb.append(".").append(field.name);
- }
- return true;
- }
-
- /**
- * Generate a primary expression.
- */
- public void genPrimary(Type type, boolean change, boolean preferLogical, int d) {
- if (change && generateInvalid()) type = getType();
- var rand = random.nextInt(10);
- if (d > 0 && rand == 0) {
- sb.append("(");
- genExpression(type, false, preferLogical, d-1);
- sb.append(")");
- } else if (rand < 6) {
- genVariable(type);
- } else {
- genConst(type);
- }
- }
-
- /**
- * Generate a constant.
- */
- public void genConst(Type type) {
- if (generateInvalid()) type = getType();
- if (type instanceof TypeInt || (type == TYPE_FLT && random.nextBoolean())) {
- var rand = random.nextInt(10);
- switch (rand) {
- case 0 -> sb.append("true");
- case 1 -> sb.append("false");
- default -> sb.append(random.nextInt(1<<(rand-2)));
- }
- } else if (type == TYPE_FLT) {
- sb.append(random.nextFloat());
- } else if (type instanceof TypeNullable n) {
- if ((n.base instanceof TypeStruct s && s.fields == null) || random.nextBoolean()) {
- sb.append("null");
- } else {
- sb.append("new ").append(generateInvalid() ? getRandomName() : n.base.name);
- }
- } else {
- sb.append("new ").append(generateInvalid() ? getRandomName() : type.name);
- }
- }
-
- /**
- * Generate a variable. If there are none generate a number instead.
- * If the program is allowed to be invalid a random name can be generated in some cases.
- */
- public void genVariable(Type type) {
- if (generateInvalid()) {
- sb.append(getRandomName());
- return;
- }
- if (generateInvalid()) type = getType();
- var t = type;
- var v = findVisibleVariablesMatching(va->va.type.isa(t));
- if (v == null) genConst(type);
- else sb.append(v.name);
- }
-
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c
deleted file mode 100644
index e247e28a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/arg_count.c
+++ /dev/null
@@ -1,10 +0,0 @@
-#include <stdio.h>
-
-__attribute__ ((CALL_CONV))
-extern double addAll( long long i0, double f1, long long i2, double f3, long long i4, double f5, long long i6, double f7, long long i8, double f9, long long i10, double f11, long long i12, double f13, long long i14, double f15, long long i16, double f17, long long i18, double f19 );
-
-int main() {
- double result = addAll( 0, 1.1, 2, 3.1, 4, 5.1, 6, 7.1, 8, 9.1, 10, 11.1, 12, 13.1, 14, 15.1, 16, 17.1, 18, 19.1 );
- printf("%f\n", result);
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c
deleted file mode 100644
index 127cbe73..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/brain_fuck.c
+++ /dev/null
@@ -1,18 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-
-typedef struct AryInt {
- unsigned int len;
- unsigned char data[];
-} AryInt;
-
-__attribute__ ((CALL_CONV))
-extern AryInt* brain_fuck();
-
-int main() {
- AryInt* ps = brain_fuck();
-
- printf("%.*s", ps->len, ps->data);
-
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/c_returns.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/c_returns.c
deleted file mode 100644
index 6d0ff2fe..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/c_returns.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <stdint.h>
-#include <stdio.h>
-
-uint64_t c_bits;
-// Legal narrow C returns: unused RAX bits deliberately contain garbage.
-#define RET(name) __asm__(".text\n.globl " #name "\n" #name ":\nmovq c_bits(%rip), %rax\nret\n");
-RET(c_i8) RET(c_u8) RET(c_i16) RET(c_u16) RET(c_i32) RET(c_u32) RET(c_i64)
-#define DECL(name) extern int64_t __attribute__((CALL_CONV)) name(void);
-DECL(test_i8) DECL(test_u8) DECL(test_i16) DECL(test_u16)
-DECL(test_i32) DECL(test_u32) DECL(test_i64) DECL(test_cmp)
-
-int main(void) {
- uint64_t values[] = {0xa5a5a5a500000000ULL, 0xa5a5a5a50000007fULL,
- 0xa5a5a5a500000080ULL, 0xa5a5a5a500007fffULL, 0xa5a5a5a500008000ULL,
- 0xa5a5a5a57fffffffULL, 0xa5a5a5a580000000ULL, 0xa5a5a5a5ffffffffULL};
- for(unsigned i=0; i<sizeof(values)/sizeof(values[0]); i++) {
- c_bits=values[i];
- int64_t actual[]={test_i8(),test_u8(),test_i16(),test_u16(),test_i32(),test_u32(),test_i64(),test_cmp()};
- int64_t expected[]={(int8_t)c_bits,(uint8_t)c_bits,(int16_t)c_bits,(uint16_t)c_bits,
- (int32_t)c_bits,(uint32_t)c_bits,(int64_t)c_bits,(int32_t)c_bits==-1};
- for(unsigned j=0; j<8; j++)
- if(actual[j]!=expected[j]) {
- printf("case %u type %u: %lld != %lld\n",i,j,(long long)actual[j],(long long)expected[j]);
- return 1;
- }
- }
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data.c
deleted file mode 100644
index 9325f65e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data.c
+++ /dev/null
@@ -1,22 +0,0 @@
-#include <stdint.h>
-#include <stdio.h>
-
-int32_t counter=7;
-int8_t small=-3;
-double fraction=1.25;
-
-int64_t bump(void) { counter+=5; return counter; }
-__attribute__((CALL_CONV)) extern int64_t test_global(void);
-
-int main(void) {
- int64_t result=test_global();
- if(result!=71515 || counter!=15 || small!=-2 || fraction!=1.75) {
- printf("result=%lld counter=%d small=%d fraction=%g\n",
- (long long)result,counter,small,fraction);
- return 1;
- }
- // A second call must observe C's new values, not a Simple initialization copy.
- counter=-2;
- result=test_global();
- return result!=-19394 || counter!=6 || small!=-1 || fraction!=2.25;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data_import.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data_import.c
deleted file mode 100644
index f65e5c84..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/extern_data_import.c
+++ /dev/null
@@ -1,14 +0,0 @@
-#include <stdint.h>
-#include <stdio.h>
-
-int32_t counter=7;
-__attribute__((CALL_CONV)) extern int64_t test_global(void);
-
-int main(void) {
- int64_t result=test_global();
- if(result!=710 || counter!=10) {
- printf("result=%lld counter=%d\n",(long long)result,counter);
- return 1;
- }
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c
deleted file mode 100644
index 263d2f13..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/fib.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-
-
-__attribute__ ((CALL_CONV))
-extern int fib(int x);
-
-int main() {
- printf("[");
- for (int i = 0; i < 10; i++) {
- printf("%d", fib(i));
- if (i < 9) printf(", ");
- }
- printf("]");
- return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c
deleted file mode 100644
index cf9390d8..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/floatToInteger.c
+++ /dev/null
@@ -1,25 +0,0 @@
-#include <stdint.h>
-#include <math.h>
-#include <stdio.h>
-
-extern int64_t float_to_int(double);
-extern int64_t float_to_u8(double);
-
-int main(void) {
- double values[] = {0.0,-0.0,6.92,-6.92,0.99,-0.99,255.75,-257.75,
- 0x1p63,0x1.fffffffffffffp62,-0x1p63,-0x1.0000000000001p63,
- INFINITY,-INFINITY,NAN,-NAN};
- for( unsigned i=0; i<sizeof(values)/sizeof(values[0]); i++ ) {
- double x=values[i];
- int64_t expected=isnan(x) ? 0 : x>=0x1p63 ? INT64_MAX
- : x<=-0x1p63 ? INT64_MIN : (int64_t)x;
- int64_t actual=float_to_int(x), narrow=float_to_u8(x);
- if( actual!=expected || narrow!=(expected&255) ) {
- fprintf(stderr,"conversion of %g: got %lld / %lld, expected %lld / %lld\n",
- x,(long long)actual,(long long)narrow,
- (long long)expected,(long long)(expected&255));
- return 1;
- }
- }
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c
deleted file mode 100644
index 934f2d89..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/loop2.c
+++ /dev/null
@@ -1,7 +0,0 @@
-
-__attribute__ ((CALL_CONV))
-extern int loop2();
-
-int main() {
- return loop2();
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c
deleted file mode 100644
index 7e7e796d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/newtonFloat.c
+++ /dev/null
@@ -1,16 +0,0 @@
-#include <stdio.h>
-#include <math.h>
-#include <stdlib.h>
-
-extern double test_sqrt(double);
-
-int main( int argc, char** argv ) {
- for( int i=0; i<10; i++ ) {
- double d = test_sqrt((double)i), expect = sqrt((double)i);
- double delta = fabs(d-expect);
- printf("%d %f (%g)\n",i,d,delta);
- if( delta > 1e-15 )
- return 1;
- }
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/person.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/person.c
deleted file mode 100644
index df95e1ca..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/person.c
+++ /dev/null
@@ -1,34 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-typedef struct Person {
- int age;
-} Person;
-
-typedef struct PersonArray {
- unsigned int length;
- int pad;
- Person* data[];
-} PersonArray;
-
-__attribute__ ((CALL_CONV))
-extern void fcn(PersonArray* people, int index);
-
-int main() {
- Person base[5] = {{25},{1},{18},{45},{60}};
- unsigned char buf[8+8*5];
- PersonArray* people_array = (PersonArray*)buf;
- people_array->length=5;
- for (int i=0; i<people_array->length; i++) people_array->data[i] = &base[i];
-
- fcn(people_array, 1);
- fcn(people_array, 1);
- fcn(people_array, 1);
- fcn(people_array, 1);
- fcn(people_array, 1);
-
- printf("%d\n", people_array->data[1]->age); // expected: 6
-
- return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c
deleted file mode 100644
index cac4cfa3..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/sieve.c
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-
-typedef struct AryInt {
- unsigned int len;
- unsigned int data[];
-} AryInt;
-
-__attribute__ ((CALL_CONV))
-extern AryInt *sieve(int N);
-
-int main() {
- AryInt* ps = sieve(100);
- printf("%d[",ps->len);
- for( int i=0; i<ps->len; i++ )
- printf("%d, ",ps->data[i]);
- printf("]");
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c
deleted file mode 100644
index ad76cc7f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/sort.c
+++ /dev/null
@@ -1,28 +0,0 @@
-#include <stdio.h>
-#include <stdlib.h>
-
-// Test wrapper to call `sort.smp`
-
-typedef struct AryInt {
- unsigned int len;
- long long data[];
-} AryInt;
-
-__attribute__ ((CALL_CONV))
-extern AryInt* merge_sort(AryInt*);
-
-
-long long primes[] = { 25, 97, 89, 83, 79, 73, 71, 67, 61, 59, 53, 47, 43, 41, 37, 31, 29, 23, 19, 17, 13, 11, 7, 5, 3, 2 };
-
-int main() {
- AryInt *as = (AryInt*)primes;
-
- merge_sort(as);
-
- printf("%d[",as->len);
- for( int i=0; i<as->len; i++ )
- printf("%d, ",as->data[i]);
- printf("]");
-
- return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c
deleted file mode 100644
index d44d5387..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_10x.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-__attribute__ ((CALL_CONV))
-extern int stack10(int x);
-
-int main() {
-int result = stack10(0);
-
-printf("%d", result);
-return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c
deleted file mode 100644
index d71979d1..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_11x.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-__attribute__ ((CALL_CONV))
-extern int stack11(int x);
-
-int main() {
-int result = stack11(1);
-
-printf("%d", result);
-return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c
deleted file mode 100644
index a4e425cb..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_12x.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-__attribute__ ((CALL_CONV))
-extern int stack12(int x);
-
-int main() {
-int result = stack12(0);
-
-printf("%d", result);
-return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c
deleted file mode 100644
index a3a51d0e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_13x.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-__attribute__ ((CALL_CONV))
-extern int stack13(int x);
-
-int main() {
-int result = stack13(0);
-
-printf("%d", result);
-return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c
deleted file mode 100644
index 4f93ba24..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/stacked_r_9x.c
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-#include <stdlib.h>
-
-__attribute__ ((CALL_CONV))
-extern int stack9(int x);
-
-int main() {
-int result = stack9(0);
-
-printf("%d", result);
-return 0;
-} \ No newline at end of file
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c b/chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c
deleted file mode 100644
index 2c6d0fa9..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/progs/stringHash.c
+++ /dev/null
@@ -1,30 +0,0 @@
-#include <stdio.h>
-#include <stdbool.h>
-
-typedef struct ArrayU8 {
- unsigned int length;
- unsigned char data[];
-} ArrayU8;
-typedef struct String {
- ArrayU8 * cs;
- long long _hashCode;
-} String;
-
-
-__attribute__ ((CALL_CONV))
-extern bool equals(String*st1, String*st2);
-
-int main() {
- String test1 = {.cs=(ArrayU8*)"\x04\x00\x00\x00test"};
- String test2 = {.cs=(ArrayU8*)"\x05\x00\x00\x00test1"};
-
- if( equals(&test1, &test2) ) {
- printf("Expected test1 and test2 unequal");
- return 1;
- }
- if( !equals(&test2, &test2) ) {
- printf("Expected test2 and test2 equal");
- return 1;
- }
- return 0;
-}
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/BrainFuckTest-testBrainFuck.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/BrainFuckTest-testBrainFuck.smp
deleted file mode 100644
index 71af2b7a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/BrainFuckTest-testBrainFuck.smp
+++ /dev/null
@@ -1,46 +0,0 @@
-// -*- mode: java; -*-
-val brain_fuck = { ->
- var !program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
- int d = 0;
- u8[] !output = new u8[0];
- u8[] !data = new u8[100];
-
- for( int pc = 0; pc < program#; pc++ ) {
- var command = program[pc];
- if (command == 62) {
- d++;
- } else if (command == 60) {
- d--;
- } else if (command == 43) {
- data[d]++;
- } else if (command == 45) {
- data[d]--;
- } else if (command == 46) {
- // Output a byte; increase the output array size
- var old = output;
- output = new u8[output# + 1];
- for( int i = 0; i < old#; i++ )
- output[i] = old[i];
- output[old#] = data[d]; // Add the extra byte on the end
- } else if (command == 44) {
- data[d] = 42;
- } else if (command == 91) {
- if (data[d] == 0) {
- for( int d = 1; d > 0; ) {
- command = program[++pc];
- if (command == 91) d++;
- if (command == 93) d--;
- }
- }
- } else if (command == 93) {
- if (data[d]) {
- for( int d = 1; d > 0; ) {
- command = program[--pc];
- if (command == 93) d++;
- if (command == 91) d--;
- }
- }
- }
- }
- return output;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc0.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc0.smp
deleted file mode 100644
index d85e39e5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc0.smp
+++ /dev/null
@@ -1 +0,0 @@
-return new u8[arg];
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc2.smp
deleted file mode 100644
index 864bcdc8..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testAlloc2.smp
+++ /dev/null
@@ -1 +0,0 @@
-int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testArray1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testArray1.smp
deleted file mode 100644
index 7f3bd007..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testArray1.smp
+++ /dev/null
@@ -1,8 +0,0 @@
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<ary#; i++ )
- ary[i] = i;
-// Fill [0,1,3,6,10,...]
-for( int i=0; i<ary#-1; i++ )
- ary[i+1] += ary[i];
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBasic1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBasic1.smp
deleted file mode 100644
index 671e5b74..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBasic1.smp
+++ /dev/null
@@ -1 +0,0 @@
-return arg | 2;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBrainfuck.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBrainfuck.smp
deleted file mode 100644
index 4e5ab81d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testBrainfuck.smp
+++ /dev/null
@@ -1,43 +0,0 @@
-u8[] !program = new u8[106];program[0] = 43;program[1] = 43;program[2] = 43;program[3] = 43;program[4] = 43;program[5] = 43;program[6] = 43;program[7] = 43;program[8] = 91;program[9] = 62;program[10] = 43;program[11] = 43;program[12] = 43;program[13] = 43;program[14] = 91;program[15] = 62;program[16] = 43;program[17] = 43;program[18] = 62;program[19] = 43;program[20] = 43;program[21] = 43;program[22] = 62;program[23] = 43;program[24] = 43;program[25] = 43;program[26] = 62;program[27] = 43;program[28] = 60;program[29] = 60;program[30] = 60;program[31] = 60;program[32] = 45;program[33] = 93;program[34] = 62;program[35] = 43;program[36] = 62;program[37] = 43;program[38] = 62;program[39] = 45;program[40] = 62;program[41] = 62;program[42] = 43;program[43] = 91;program[44] = 60;program[45] = 93;program[46] = 60;program[47] = 45;program[48] = 93;program[49] = 62;program[50] = 62;program[51] = 46;program[52] = 62;program[53] = 45;program[54] = 45;program[55] = 45;program[56] = 46;program[57] = 43;program[58] = 43;program[59] = 43;program[60] = 43;program[61] = 43;program[62] = 43;program[63] = 43;program[64] = 46;program[65] = 46;program[66] = 43;program[67] = 43;program[68] = 43;program[69] = 46;program[70] = 62;program[71] = 62;program[72] = 46;program[73] = 60;program[74] = 45;program[75] = 46;program[76] = 60;program[77] = 46;program[78] = 43;program[79] = 43;program[80] = 43;program[81] = 46;program[82] = 45;program[83] = 45;program[84] = 45;program[85] = 45;program[86] = 45;program[87] = 45;program[88] = 46;program[89] = 45;program[90] = 45;program[91] = 45;program[92] = 45;program[93] = 45;program[94] = 45;program[95] = 45;program[96] = 45;program[97] = 46;program[98] = 62;program[99] = 62;program[100] = 43;program[101] = 46;program[102] = 62;program[103] = 43;program[104] = 43;program[105] = 46;
-int d = 0;
-u8[] !output = new u8[0];
-u8[] !data = new u8[100];
-
-for( int pc = 0; pc < program#; pc++ ) {
- var command = program[pc];
- if (command == 62) {
- d++;
- } else if (command == 60) {
- d--;
- } else if (command == 43) {
- data[d]++;
- } else if (command == 45) {
- data[d]--;
- } else if (command == 46) {
- // Output a byte; increase the output array size
- var old = output;
- output = new u8[output# + 1];
- for( int i = 0; i < old#; i++ )
- output[i] = old[i];
- output[old#] = data[d]; // Add the extra byte on the end
- } else if (command == 44) {
- data[d] = 42;
- } else if (command == 91) {
- if (data[d] == 0) {
- for( int d = 1; d > 0; ) {
- command = program[++pc];
- if (command == 91) d++;
- if (command == 93) d--;
- }
- }
- } else if (command == 93) {
- if (data[d]) {
- for( int d = 1; d > 0; ) {
- command = program[--pc];
- if (command == 93) d++;
- if (command == 91) d--;
- }
- }
- }
-}
-return output;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testCast.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testCast.smp
deleted file mode 100644
index d1368767..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testCast.smp
+++ /dev/null
@@ -1 +0,0 @@
-struct _Bar { int x; }; var b = arg ? new _Bar; return b ? b.x++ + b.x++ : -1;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags1.smp
deleted file mode 100644
index f630e959..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags1.smp
+++ /dev/null
@@ -1,5 +0,0 @@
-bool b1 = arg == 1;
-bool b2 = arg == 2;
-if (b2) if (b1) return 1;
-if (b1) return 2;
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags2.smp
deleted file mode 100644
index 70bfc226..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFlags2.smp
+++ /dev/null
@@ -1,6 +0,0 @@
-bool b1 = arg == 1;
-while (arg > 0) {
- arg--;
- if (b1) arg--;
-}
-return arg;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFltArg.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFltArg.smp
deleted file mode 100644
index caa11f12..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testFltArg.smp
+++ /dev/null
@@ -1 +0,0 @@
-return {int i, flt f, int j->return i+f+j;};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testMergeSort.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testMergeSort.smp
deleted file mode 100644
index 417eba72..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testMergeSort.smp
+++ /dev/null
@@ -1,46 +0,0 @@
-// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
-
-val _merge_sort = { int[] a, int[] b, int n ->
- _copy_array(a, 0, n, b);
- _split_merge(a, 0, n, b);
-};
-
-val _split_merge = { int[] b, int begin, int end, int[] a ->
- if (end - begin <= 1)
- return 0;
- int middle = (end + begin) / 2;
- _split_merge(a, begin, middle, b);
- _split_merge(a, middle, end, b);
- _merge(b, begin, middle, end, a);
- return 0;
-};
-
-val _merge = { int[] b, int begin, int middle, int end, int[] a ->
- int i = begin, j = middle;
-
- for (int k = begin; k < end; k++) {
- // && and ||
- bool cond = false;
- if (i < middle) {
- if (j >= end) cond = true;
- else if (a[i] <= a[j]) cond = true;
- }
- if (cond) b[k] = a[i++];
- else b[k] = a[j++];
- }
-};
-
-val _copy_array = { int[] a, int begin, int end, int[] b ->
- for (int k = begin; k < end; k++)
- b[k] = a[k];
-};
-
-int[] !a = new int[arg];
-int[] !b = new int[a#];
-
-for (int i = 0; i < a#; i++)
- a[i] = a# - i;
-
-_merge_sort(a, b, a#);
-
-return a;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonFloat.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonFloat.smp
deleted file mode 100644
index fd76478f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonFloat.smp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Newtons approximation to the square root
-val _sqrt = { flt x ->
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( next == guess ) return guess;
- guess = next;
- }
-};
-flt farg = arg;
-return _sqrt(farg) + _sqrt(farg+2.0);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonInteger.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonInteger.smp
deleted file mode 100644
index 8912dcd9..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testNewtonInteger.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-// Newtons approximation to the square root
-val _sqrt = { int x ->
- int guess = x;
- while( 1 ) {
- int next = (x/guess + guess)/2;
- if( next == guess ) return guess;
- guess = next;
- }
-};
-return _sqrt(arg) + _sqrt(arg+2);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp
deleted file mode 100644
index 951cdc32..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter20Test-testString.smp
+++ /dev/null
@@ -1,35 +0,0 @@
-struct _String {
- u8[] cs;
- new _String={ u8[] b -> cs=b; };
- int _hashCode;
-};
-
-val _equals = { !_String !self, !_String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-val _hashCode = { !_String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !_String self ->
- int hash=0;
- if( self.cs ) {
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- }
- if( !hash ) hash = 123456789;
- return hash;
-};
-
-!_String !s = new _String(new u8[17]);
-s.cs[0] = 67; // C
-s.cs[1] = 108; // l
-_hashCode(s);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testArray1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testArray1.smp
deleted file mode 100644
index 6b10f92c..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testArray1.smp
+++ /dev/null
@@ -1,9 +0,0 @@
-// -*- mode: java; -*-
-int[] !ary = new int[arg];
-// Fill [0,1,2,3,4,...]
-for( int i=0; i<ary#; i++ )
- ary[i] = i;
-// Fill [0,1,3,6,10,...]
-for( int i=0; i<ary#-1; i++ )
- ary[i+1] += ary[i];
-return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testNewtonFloat.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testNewtonFloat.smp
deleted file mode 100644
index 58449f9c..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testNewtonFloat.smp
+++ /dev/null
@@ -1,13 +0,0 @@
-// -*- mode: java; -*-
-// Newtons approximation to the square root
-val _test_sqrt = { flt x ->
- flt epsilon = 1e-15;
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( guess-epsilon <= next & next <= guess+epsilon ) return guess;
- //if( guess==next ) return guess;
- guess = next;
- }
-};
-flt farg = arg; return _test_sqrt(farg) + _test_sqrt(farg+2.0);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp
deleted file mode 100644
index a18a5b88..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21AllocTest-testString.smp
+++ /dev/null
@@ -1,35 +0,0 @@
-// -*- mode: java; -*-
-struct _String {
- u8[] cs;
- int _hashCode;
- new _String = { u8[] data -> cs=data; };
-};
-
-// Compare two Strings
-val equals = { !_String !self, !_String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !_String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !_String self ->
- int hash=0;
- if( self.cs )
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-
-// This fixture exports its API, not the last declaration as a result.
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp
deleted file mode 100644
index 315cc5b6..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testAntiDeps1.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-struct _S { int f; };
-var v0 = new _S;
-!_S? !v1;
-if (arg) v1 = new _S;
-if (v1) {
- v0.f = v1.f;
-} else {
- v0.f = 2;
-}
-return v0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount-2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount-2.smp
deleted file mode 100644
index 20a05445..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount-2.smp
+++ /dev/null
@@ -1,6 +0,0 @@
-// -*- mode: java; -*-
-val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15 ->
- return
- i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 +
- i10+f11+i12+f13+i14+f15;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount.smp
deleted file mode 100644
index 6b6bc0ae..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testArgCount.smp
+++ /dev/null
@@ -1,6 +0,0 @@
-// -*- mode: java; -*-
-val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 ->
- return
- i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 +
- i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testFibExport.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testFibExport.smp
deleted file mode 100644
index 149872a7..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testFibExport.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-val fib = {int n ->
- int f1=1;
- int f2=1;
- while( n-- > 1 ){
- int temp = f1+f2;
- f1=f2;
- f2=temp;
- }
- return f2;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp
deleted file mode 100644
index 1804ad59..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testInfinite.smp
+++ /dev/null
@@ -1 +0,0 @@
-struct _S { int i; }; !_S !s = new _S; while(1) s.i++;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testLoop2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testLoop2.smp
deleted file mode 100644
index cc54f790..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testLoop2.smp
+++ /dev/null
@@ -1,9 +0,0 @@
-int i = 0;
-while(true) {
- i += 1;
- if( i==0 ) continue;
- if( i==1 ) continue;
- if( i==2 ) break;
- continue;
-}
-return i;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testNewtonExport.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testNewtonExport.smp
deleted file mode 100644
index 2786558e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testNewtonExport.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-val test_sqrt = { flt x ->
- flt epsilon = 1e-15;
- flt guess = x;
- while( 1 ) {
- flt next = (x/guess + guess)/2;
- if( guess-epsilon <= next & next <= guess+epsilon ) return guess;
- //if( guess==next ) return guess;
- guess = next;
- }
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp
deleted file mode 100644
index 09285034..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testPerson.smp
+++ /dev/null
@@ -1,9 +0,0 @@
-// -*-java-*-
-struct Person {
- int age;
-};
-
-val fcn = { !Person?[] ps, int x ->
- if( ps[x] )
- ps[x].age++;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testSieve.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testSieve.smp
deleted file mode 100644
index beb0b3ad..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testSieve.smp
+++ /dev/null
@@ -1,30 +0,0 @@
-val sieve = { int N ->
- // The main Sieve array
- bool[] !ary = new bool[N];
- // The primes less than N
- u32[] !primes = new u32[N>>1];
- // Number of primes so far, searching at index p
- int nprimes = 0, p=2;
- // Find primes while p^2 < N
- while( p*p < N ) {
- // skip marked non-primes
- while( ary[p] ) p++;
- // p is now a prime
- primes[nprimes++] = p;
- // Mark out the rest non-primes
- for( int i = p + p; i < ary#; i+= p )
- ary[i] = true;
- p++;
- }
-
- // Now just collect the remaining primes, no more marking
- for( ; p < N; p++ )
- if( !ary[p] )
- primes[nprimes++] = p;
-
- // Copy/shrink the result array
- u32[] !rez = new u32[nprimes];
- for( int j=0; j < nprimes; j++ )
- rez[j] = primes[j];
- return rez;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp
deleted file mode 100644
index ee5a0560..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter21Test-testStringExport.smp
+++ /dev/null
@@ -1,33 +0,0 @@
-struct String {
- u8[] cs;
- int _hashCode;
- new String = { u8[] data -> cs=data; };
-};
-
-// Compare two Strings
-val equals = { !String !self, !String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !String self ->
- int hash=0;
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-
-// This fixture exports its API, not the last declaration as a result.
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testHelloWorld.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testHelloWorld.smp
deleted file mode 100644
index 1bcac36d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testHelloWorld.smp
+++ /dev/null
@@ -1,4 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-_p("Hello, World!");
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp
deleted file mode 100644
index cc549808..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testInfiniteReturn.smp
+++ /dev/null
@@ -1 +0,0 @@
-struct _S { int i; }; !_S !s = new _S; while(1) s.i++; return s.i;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testNarrowCReturns.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testNarrowCReturns.smp
deleted file mode 100644
index 21293a7a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testNarrowCReturns.smp
+++ /dev/null
@@ -1,12 +0,0 @@
-{i8} c_i8="C"; {u8} c_u8="C";
-{i16} c_i16="C"; {u16} c_u16="C";
-{i32} c_i32="C"; {u32} c_u32="C";
-{int} c_i64="C";
-val test_i8={->return c_i8();};
-val test_u8={->return c_u8();};
-val test_i16={->return c_i16();};
-val test_u16={->return c_u16();};
-val test_i32={->return c_i32();};
-val test_u32={->return c_u32();};
-val test_i64={->return c_i64();};
-val test_cmp={->return c_i32()==-1;};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp
deleted file mode 100644
index f860091d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testPerson.smp
+++ /dev/null
@@ -1,8 +0,0 @@
-struct Person {
- i32 age;
-};
-
-val fcn = { !Person?[] ps, int x ->
- if( ps[x] )
- ps[x].age++;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp
deleted file mode 100644
index ec9c6d50..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail.smp
+++ /dev/null
@@ -1,4 +0,0 @@
-struct _Person { i32 age;};
-!_Person !p = new _Person;
-p.age = (arg<<17)>>17;
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp
deleted file mode 100644
index d5264aa2..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextFail2.smp
+++ /dev/null
@@ -1,5 +0,0 @@
-
- struct _Person { i32 age;};
- !_Person !p = new _Person;
- p.age = (arg<<48)>>48;
- return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp
deleted file mode 100644
index c4398a89..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter22Test-testSextSuccess.smp
+++ /dev/null
@@ -1,5 +0,0 @@
-// Should fold away sign extend
-struct _Person { i8 age;};
-!_Person !p = new _Person;
-p.age = (arg<<48)>>48;
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp
deleted file mode 100644
index 49c80a99..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23AllocTest-testString.smp
+++ /dev/null
@@ -1,34 +0,0 @@
-// -*- mode: java; -*-
-struct _String {
- u8[] cs;
- int _hashCode;
- new _String = { u8[] data -> cs=data; };
-};
-
-// Compare two Strings
-val equals = { !_String !self, !_String s ->
- if( self == s ) return true;
- if( self.cs# != s.cs# ) return false;
- for( int i=0; i< self.cs#; i++ )
- if( self.cs[i] != s.cs[i] )
- return false;
- return true;
-};
-
-// Return the String hashCode (cached, and never 0)
-val hashCode = { !_String self ->
- self._hashCode
- ? self._hashCode
- : (self._hashCode = _hashCodeString(self));
-};
-
-val _hashCodeString = { !_String self ->
- int hash=0;
- for( int i=0; i< self.cs#; i++ )
- hash = hash*31 + self.cs[i];
- if( !hash ) hash = 123456789;
- return hash;
-};
-
-// This fixture exports its API, not the last declaration as a result.
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAnd.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAnd.smp
deleted file mode 100644
index da85ef0e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAnd.smp
+++ /dev/null
@@ -1,11 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int a = 1;
-int b = 1;
-
-if(a && b) {
- _p("And");
-} else {
- _p("Or");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp
deleted file mode 100644
index a1e4e9ab..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testAndPtr.smp
+++ /dev/null
@@ -1,12 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-struct _S { !_S? !fld; new _S={ !_S? f -> fld=f; }; };
-
-val ptr = arg == 1 ? null : new _S(arg==1 ? null : new _S(null));
-if( ptr && ptr.fld ) {
- _p("true");
-} else {
- _p("false");
-}
-
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffAnd.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffAnd.smp
deleted file mode 100644
index 1a6b5cf3..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffAnd.smp
+++ /dev/null
@@ -1,17 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int a = 1;
-int b = 1;
-
-int x=1;
-int y=1;
-int z=0;
-
-int g = x++ && y++ && z++;
-
-if(x == 2 && y == 2 && z == 1 && g == 0) {
- _p("Effected");
-} else {
- _p("Not effected");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffOr.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffOr.smp
deleted file mode 100644
index f2bb16c5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testCondSideEffOr.smp
+++ /dev/null
@@ -1,24 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int a = 1;
-int b = 1;
-
-int x = -1;
-int y=1;
-int z= -1;
-
-int g = x++ || y++ || z++;
-
-int switch = 0;
-if(x == 4 || y == 4 || z == 4) {
- switch = -1;
-} else {
- switch = 1;
-}
-int cd = 1;
-if((x == 0 && y == 1 && z==-1) && switch && g) {
- _p("Effected");
-} else {
- _p("Not effected");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp
deleted file mode 100644
index eac0a542..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFRefFields.smp
+++ /dev/null
@@ -1,20 +0,0 @@
-// A hypothetical scanner class
-struct _Scan {
- int !x;
- u8[~] buf;
- new _Scan={ u8[~] b -> buf=b; };
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- };
- // Peek a character; if matched consume it, else false.
- val peek = { u8 c ->
- skip();
- if( buf[x] != c ) return false;
- x++;
- return true;
- };
-};
-!_Scan !s = new _Scan(" q");
-return s.peek('q');
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFuncCall.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFuncCall.smp
deleted file mode 100644
index 8a7b90d9..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testFuncCall.smp
+++ /dev/null
@@ -1,16 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-// -*- mode: java; -*-
-int a = 1;
-int b = 1;
-
-var _sq = { int x ->
- x*x;
-};
-
-if(a && _sq(0)) {
- _p("And");
-} else {
- _p("Or");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp
deleted file mode 100644
index ced649e3..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testMethod.smp
+++ /dev/null
@@ -1,17 +0,0 @@
-// A hypothetical scanner class
-struct _Scan {
- int !x;
- u8[~] buf;
- new _Scan={ u8[~] b -> buf=b; };
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- };
- val require = { u8 ch ->
- skip();
- buf[x++]==ch;
- };
-};
-!_Scan !s = new _Scan(" [1,2]");
-return s.require('[');
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOr.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOr.smp
deleted file mode 100644
index dbab1ae5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOr.smp
+++ /dev/null
@@ -1,13 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int a = 1;
-int b = 0;
-
-if(a++ || b++ ) {
- if(b == 0 && a == 2) {
- _p("Or");
- }
-} else{
- _p("And");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp
deleted file mode 100644
index c4e4f9b5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopLocal.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-struct _S { int f; };
-int x=0;
-int result=arg&1;
-if(!result) {
- !_S !s=new _S;
- while(x<3) x++;
- s.f=++x;
- result=s.f;
-}
-return x*10+result;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp
deleted file mode 100644
index 79478408..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter23Test-testOrRhsLoopMemory.smp
+++ /dev/null
@@ -1,11 +0,0 @@
-struct _S { int f; };
-!_S !s=new _S;
-int result=arg&1;
-if(!result) {
- !_S !t=new _S;
- while(s.f<3) s.f++;
- s.f++;
- t.f=s.f;
- result=t.f;
-}
-return s.f*10+result;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp
deleted file mode 100644
index f292254e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testBubbleSort.smp
+++ /dev/null
@@ -1,217 +0,0 @@
-
- i32 errno = "C";
- // fd buf len -> len
- { i32 i64 u32 -> u32 } write = "C";
- // fd buf len -> len
- { i32 i64 u32 -> u32 } read = "C";
- // status
- { i32 -> i32 } _exit = "C";
- // addr len prot flags fd off -> void*
- { i64 i64 i32 i32 i32 i32 -> i64 } mmap = "C";
- // mmap flags
- i32 PROT_EXEC = 4;
- i32 PROT_READ = 1;
- i32 PROT_WRITE= 2;
- i32 PROT_NONE = 0;
- i32 MAP_PRIVATE = 2;
- i32 MAP_ANON = 32;
-
- val _copy = { u8[] dst, int doff, u8[~] src, int soff, int len ->
- for( int i=0; i<len; i++ )
- dst[i+doff] = src[i+soff];
- return dst;
- };
- struct _aryu8 {
- u32 !len = 0;
- u8[] !buf;
- new _aryu8={ u8[] b -> buf=b; };
- // Grow as needed
- val _grow = { int len2 ->
- if( len+len2 < buf# ) return self;
- int newlen = buf# * 2;
- while( len+len2 >= newlen ) newlen *= 2;
- u8[] !buf2 = new u8[newlen];
- _copy(buf2,0,buf,0,buf#);
- buf = buf2;
- self;
- };
-
- // Add a character to aryu8
- val add = { u8 c ->
- _grow(1).buf[len++] = c;
- self;
- };
-
- // Add a plain array to the aryu8
- val addAry = { u8[~] ary ->
- _grow(ary#);
- _copy(buf,len,ary,0,ary#);
- len += ary#;
- self;
- };
-
- // Remove x chars from the tail.
- val undo = { int x ->
- len = len >= x ? len-x : 0;
- self;
- };
-
- // Print ASCII number into aryu8
- val num = { i64 x ->
- if( !x ) { add('0'); return self; }
- if( x < 0 ) { add('-'); x = -x; }
- i64 ten = 1, log = 0;
- while( ten <= x ) { ten *= 10; log++; }
- for( int i=0; i<log; i++ ) {
- ten /= 10;
- i64 q = x/ten;
- u8 digit = q+'0';
- add(digit);
- x -= q*ten;
- }
- self;
- };
-
- // Write to fd, returns the number of bytes written (len).
- val write = { i32 fd -> i64 ptr = buf; write(fd,ptr,len); };
- };
-struct _Scan {
- int !x;
- u8[~] buf;
- new _Scan={ u8[~] b -> buf=b; };
-
- // Skip whitespace
- val skip = { ->
- while( buf[x] <= ' ' )
- x++;
- return self;
- };
-
- // True if find required character, and skip it; else False.
- val match = { u8 c ->
- skip();
- if( buf[x] != c ) return false;
- x++;
- return true;
- };
-
- // Parse a simple number
- val number = { ->
- skip();
- bool neg = match('-');
- i64 n = 0;
- while( '0' <= buf[x] & buf[x] <= '9' )
- n = n*10 + (buf[x++] - '0');
- return neg ? -n : n;
- };
- };
-struct _aryi64 {
- u32 !len = 0;
- i64[] !buf;
- new _aryi64={ i64[] b -> buf=b; };
- // Grow as needed
- val _grow = { int len2 ->
- if( len+len2 < buf# ) return self;
- int newlen = buf# * 2;
- while( len+len2 >= newlen ) newlen *= 2;
- i64[] !buf2 = new i64[newlen];
- for( int i=0; i<buf#; i++ )
- buf2[i] = buf[i];
- buf = buf2;
- self;
- };
-
- // Add a i64 to aryu8
- val add = { i64 c ->
- _grow(1).buf[len++] = c;
- self;
- };
-
- // Write to aryu8
- val str = { ->
- // TODO: clunky default constructor syntax
- var str = new _aryu8(new u8[1]).add('[');
- for( int i=0; i<len; i++ )
- str.num(buf[i]).add(',').add(' ');
- str.undo(2).add(']');
- };
- };
- val _parseAryI64 = { u8[~] str ->
- !_Scan !s = new _Scan(str);
- if( !s.match('[') ) return null;
- // Array of parsed results
- var nums = new _aryi64(new i64[1]);
- // Parse a list of numbers
- if( !s.match(']') ) { // Handle the empty list
- // Parse "123, " until a something else
- while( s.x < s.buf# ) {
- nums.add(s.number());
- if( !s.match(',') )
- break;
- };
- if( !s.match(']') ) return null; // Close the list
- }
- return nums;
- };
-
-
- // Print string to stdout
- val _p = { u8[~] str ->
- i64 ptr = str; // cast array base to i64
- return write(1,ptr,str#);
- };
-
- // Return stdin as a u8[]
- val _stdin = { ->
- var buf = new u8[8*1024];
- i64 ptr = buf;
- int len = 0;
- while( true ) {
- int rez = read(0,ptr,buf#);
- if( rez < 0 ) _exit(errno);
- len += rez;
- if( rez < buf# )
- break;
- _exit(-2); // Need to implement
- }
- // Compress
- var dst = new u8[len];
- return _copy(dst,0,buf,0,len);
- };
- // -*- mode: java; -*-
-
-// An implementation of
-// https://sampleprograms.io/projects/bubble-sort/
-//
-// Bubble sort, with reading (parsing) and writing (formatting) the data.
-
-val _sort = { !_aryi64 nums ->
- var done = false;
- while( !done ) {
- done = true;
- for( var i = 0; i < nums.len-1; i++ )
- if( nums.buf[i] > nums.buf[i+1] ) {
- int tmp = nums.buf[i];
- nums.buf[i] = nums.buf[i+1];
- nums.buf[i+1] = tmp; // Swap
- done = false;
- }
- }
-};
-
-// Read from stdin a line of the form "[ 4, 3, 2, 1]" and print it sorted.
-val main = { ->
- // Parse array of numbers from stdin
- var nums = _parseAryI64( _stdin() );
- if( !nums ) {
- _p("stdin must look like [4, 3, 2, 1]");
- return 1;
- }
-
- // Bubble sort array of integers
- _sort(nums);
-
- // Print a list of numbers to stdout
- nums.str().write(1);
- return 0;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testNewtonInteger.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testNewtonInteger.smp
deleted file mode 100644
index 572e4ad7..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testNewtonInteger.smp
+++ /dev/null
@@ -1,11 +0,0 @@
-// Newtons approximation to the square root
-val _sqrt = { int x ->
- int guess = x;
- while( 1 ) {
- int next = (x/guess + guess)/2;
- if( next == guess ) return guess;
- guess = next;
- }
-};
-int cast_int = arg+2;
-return _sqrt(arg) + _sqrt(cast_int);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testStackArguments.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testStackArguments.smp
deleted file mode 100644
index b7d52dc5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24AllocTest-testStackArguments.smp
+++ /dev/null
@@ -1,5 +0,0 @@
-val addAll = { int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, flt f7, int x8, flt f9, int i10, flt f11, int i12, flt f13, int i14, flt f15, int x16, flt f17 int x18, flt f19 ->
- return
- i0 + f1+ i2+ f3+ i4+ f5+ i6+ f7+ x8 +f9 +
- i10+f11+i12+f13+i14+f15+x16+f17+x18+f19 ;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack1.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack1.smp
deleted file mode 100644
index ded92426..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack1.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int something_specific = 11;
-
-if (10 < something_specific < 20) {
- _p("In Range");
-} else {
- _p("Out of Range");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10-2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10-2.smp
deleted file mode 100644
index d9fb38d5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10-2.smp
+++ /dev/null
@@ -1 +0,0 @@
-return (0 < 1) == (1>0);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10.smp
deleted file mode 100644
index f92e5b00..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack10.smp
+++ /dev/null
@@ -1,3 +0,0 @@
-var stack10 = { int x ->
- (x < 1) == (1>x);
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11-2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11-2.smp
deleted file mode 100644
index b5aac70b..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11-2.smp
+++ /dev/null
@@ -1 +0,0 @@
-return 0 < arg < arg+1 < 4;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11.smp
deleted file mode 100644
index f50e3512..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack11.smp
+++ /dev/null
@@ -1,3 +0,0 @@
-var stack11 = { int x ->
- 0 < x < x+1 < 4;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12-2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12-2.smp
deleted file mode 100644
index 575d1b4e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12-2.smp
+++ /dev/null
@@ -1 +0,0 @@
-return 0 != arg != 2;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12.smp
deleted file mode 100644
index b4f36c2e..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack12.smp
+++ /dev/null
@@ -1,3 +0,0 @@
-var stack12 = { int x ->
- 0 != x != 2;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13-2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13-2.smp
deleted file mode 100644
index 9bde9687..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13-2.smp
+++ /dev/null
@@ -1 +0,0 @@
-return 0 == arg == 1;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13.smp
deleted file mode 100644
index ae7b38a3..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack13.smp
+++ /dev/null
@@ -1,3 +0,0 @@
-var stack13 = { int x ->
- 0 == x == 1;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack2.smp
deleted file mode 100644
index 5590c42f..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack2.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int score = 75;
-
-if (60 <= score < 90) {
- _p("In Range");
-} else {
-_p("Out of range");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack3.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack3.smp
deleted file mode 100644
index 4700f8ce..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack3.smp
+++ /dev/null
@@ -1,11 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-var _sq = { int x ->
- x*x;
-};
-
-int score = 5;
-
-val str = (_sq(2) <= score < _sq(3)) ? "In Range" : "Out of range";
-_p(str);
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack4.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack4.smp
deleted file mode 100644
index 47623b30..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack4.smp
+++ /dev/null
@@ -1,10 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-int something_specific = 9;
-
-if (10 < something_specific < 20) {
- _p("In Range");
-} else {
- _p("Out of Range");
-}
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack5.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack5.smp
deleted file mode 100644
index 3c2c4208..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack5.smp
+++ /dev/null
@@ -1,15 +0,0 @@
-{i32 i64 u32 -> u32} write="C";
-val _p={u8[~] str -> i64 ptr=str; return write(1,ptr,str#);};
-var _sq = { int x ->
- x*x;
-};
-
-int score = 5;
-
-if (_sq(2) <= score < _sq(3)) {
-_p("In Range");
-} else {
-_p("Out of range");
-}
-
-return 0;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9-2.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9-2.smp
deleted file mode 100644
index f54558f5..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9-2.smp
+++ /dev/null
@@ -1 +0,0 @@
-return (0 < 1) == 1;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9.smp
deleted file mode 100644
index 15979be6..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/Chapter24Test-testStack9.smp
+++ /dev/null
@@ -1,3 +0,0 @@
-var stack9 = { int x ->
- (x < 1) == 1;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/MergeSortTest-testMergeSort0.smp b/chapter25/src/test/java/com/seaofnodes/simple/spill/MergeSortTest-testMergeSort0.smp
deleted file mode 100644
index e6478eeb..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/MergeSortTest-testMergeSort0.smp
+++ /dev/null
@@ -1,35 +0,0 @@
- val merge_sort = { int[] a ->
- int[] !b = new int[a#];
- copy_array (a, 0, a#, b);
- split_merge(a, 0, a#, b);
- };
-
- val split_merge = { int[] b, int begin, int end, int[] a ->
- if (end - begin <= 1)
- return 0;
- int middle = (end + begin) / 2;
- split_merge(a, begin, middle, b);
- split_merge(a, middle, end, b);
- merge(b, begin, middle, end, a);
- return 0;
- };
-
- val merge = { int[] b, int begin, int middle, int end, int[] a ->
- int i = begin, j = middle;
-
- for (int k = begin; k < end; k++) {
- // && and ||
- bool cond = false;
- if (i < middle) {
- if (j >= end) cond = true;
- else if (a[i] <= a[j]) cond = true;
- }
- if (cond) b[k] = a[i++];
- else b[k] = a[j++];
- }
- };
-
- val copy_array = { int[] a, int begin, int end, int[] b ->
- for (int k = begin; k < end; k++)
- b[k] = a[k];
- };
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md b/chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md
deleted file mode 100644
index 5ae019dd..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.ja.md
+++ /dev/null
@@ -1,50 +0,0 @@
-# 過去の割り当てコホート
-
-[English](README.md) | 日本語
-
-この文書は [Historical allocation cohorts](README.md) の日本語訳です。
-
-`cohorts.tsv` は、第24章の `SpillStats` で測定した212件のコンパイル項目を固定したものです。
-コホート20〜24の項目数は、それぞれ39、52、24、30、67です。
-ソースファイルは内容で重複を除き、最初に参照したテストの名前を付けています。
-マニフェストには、テストの識別情報、ターゲット、ABI、元の停止フェーズを保持しています。
-`Win64` は、第25章で使う ABI の表記 `win64` に変更しています。
-引数の型は i64、再実行のシードは全項目で123です。
-
-これらの入力は、この最終監査で新たな正しさの回帰テストを追加する前に、
-第24章のテストから取得しました。比較のための安定したコーパスです。
-変更する場合は、比較可能性が失われる理由を説明する必要があります。
-人工的なアロケータの回帰テストや命令の回帰テストでの割り当ては、これらのコホートには含めません。
-
-第25章では、以前のソースをすべて無変更で解析できるわけではありません。適応のための変更を明記します。
-
-- String、Scan、連結された S のコンストラクタでは、`new S { field=...; }` を、
- 宣言されたコンストラクタとその引数に置き換えています。
- レシーバーを明示するスキャナ関数は、操作を保ったままインスタンスメソッドにしています。
-- 保存した `Chapter21Test-testStringExport`、`Chapter21AllocTest-testString`、
- `Chapter23AllocTest-testString` のソースは、必須の非 null フィールド `cs` を初期化するために、
- `new String = { u8[] data -> cs=data; };` を宣言しています。
- これによって10件の再実行項目(4件、3件、3件)を修正していますが、
- 等価性・ハッシュの本体やマニフェストの構成項目は変えていません。
- これらの適応済みソースが新しい比較基準です。以前のコンストラクタなしのフィクスチャと
- 同一入力として、その集計値を比較することはできません。
-- 短絡評価の右辺で初期化する2つのループは、同等のガード付きブロックにしています。
- 割り当て、副作用、最終結果を保持しています。
-- 単純な I/O のケースには、以前の `write` の束縛と表示用の補助関数を直接含めています。
- Bubble Sort には、名前空間の接頭辞を平坦化し、コンストラクタを明示し、
- 置換用バッファを可変にした、以前のライブラリの本体を含めています。
- これにより、第25章で大幅に改訂した例に差し替えるのではなく、以前のワークロードを保持しています。
-- 元の Newton の浮動小数点数版2種類を含め、ほかのソース本文は保持しています。
-
-すべての項目を **RegAlloc まで**再実行し、スケジューリング済みのレジスタ制約を確認します。
-過去のフェーズの列は来歴情報であり、第25章で以前のネイティブ実行ハーネスを動かすことを約束するものではありません。
-適応済み Bubble Sort は、割り当ての比較可能性のために、過去の `i32 errno="C"` 宣言を保持しています。
-直接のデータ束縛は現在、3つのターゲットすべてでエンコードできますが、
-errno がスレッドローカルまたはアクセサ経由の libc とは、この宣言をリンクできません。
-現在のシステムライブラリのコードは、代わりに `libc.errno()` を使います。
-このコーパスを、過去のランタイムの実行カバレッジとして数えてはいけません。
-
-その後 `SpillStats` は、シード456で現在のシステムライブラリをエンコーディングまでコンパイルし、
-既存のシードとネイティブ検査を使って Chapter25Test を実行します。
-これらを合わせてコホート25とします。ライブラリまたはテストが失敗すると、レポートも失敗します。
-部分的な集計値では改善を示せません。現在のコンパイラの実行検証は、通常の章全体のテストスイートが担います。
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md b/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md
deleted file mode 100644
index b5ab9d80..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/README.md
+++ /dev/null
@@ -1,78 +0,0 @@
-# Historical allocation cohorts
-
-English | [日本語](README.ja.md)
-
-`cohorts.tsv` freezes 199 compilation entries matching Chapter 24's
-`SpillStats`: 39, 52, 26, 24, and 58 entries for cohorts 20-24. The source files
-are deduplicated by contents and named for their first referring test. The
-manifest preserves test identity, target, ABI, and the original stopping phase.
-`Win64` becomes `win64`, the Chapter 25 ABI spelling. Argument type is i64 and
-the replay seed is 123 throughout.
-
-The original 212 entries were captured from the Chapter 24 tests before the
-allocator audit's new correctness regressions. The 2026-10-07 refresh adds
-Chapter22Test.testNarrowCReturns on x86 SystemV and Win64, matching the two
-entries already measured in Chapters 22-24. The source is unchanged; only the
-manifest's ABI spelling changes. The [cross-chapter matrix](../../../../../../../../docs/regalloc-spills.md)
-checks matching test/CPU/ABI membership, including repeated compilations.
-The same refresh removes all 15 Jig entries: six from cohort 23 and nine from
-cohort 24. Jig methods are ignored scratch pads for debugging, not regression
-tests or allocation benchmarks. Their frozen source copies were removed too.
-These inputs are a stable comparison corpus; changing one
-requires explaining the resulting break in comparability. Synthetic allocator
-and instruction regressions do not contribute allocations to these cohorts.
-
-Chapter 25 cannot parse all earlier sources unchanged. Adaptations are explicit:
-
-- String, Scan, and linked S constructors replace `new S { field=...; }` with
- declared constructors and arguments. The explicit-receiver scanner functions
- become instance methods, preserving their operations.
-- The saved `Chapter21Test-testStringExport`, `Chapter21AllocTest-testString`,
- and `Chapter23AllocTest-testString` sources declare
- `new String = { u8[] data -> cs=data; };` to initialize the required non-null
- `cs` field. This repairs ten replay entries (four, three, and three), without
- changing their equality/hash bodies or manifest membership. These adapted
- sources are a new comparison baseline; their totals cannot be compared as
- identical inputs with the earlier constructor-free fixtures.
-- The two short-circuit RHS initializer loops become equivalent guarded blocks,
- preserving allocation, side effects, and the final result. Their RHS assigns
- the field value directly: Simple's `||` returns an operand, not a normalized
- boolean. The 2026-10-07 cleanup removes an erroneous `!!` in those adaptations;
- both fixtures return 44 for even arguments and 1 for odd arguments.
-- Simple I/O cases include the old `write` binding and print helper directly.
- Bubble Sort includes the old library bodies, with namespace prefixes flattened,
- explicit constructors, and mutable replacement buffers. This retains the old
- workload instead of substituting Chapter 25's substantially revised example.
-- Local implementation names are private in eleven fixtures: the four Newton
- variants that call their helper from the top level, Chapter 20's MergeSort,
- String and Cast programs, and Chapter 22's three sign-extension programs and
- InfiniteReturn. Their helpers/classes acquire an underscore prefix; operations,
- inputs and final results are unchanged. This avoids exporting implementation
- functions and allocation factories solely because Chapter 25 treats top-level
- declarations as module members. Export/API fixtures, including NewtonExport,
- StringExport, Person and the C-return wrappers, retain their public names.
- These visibility adaptations change the generated workload and are recorded
- separately from the allocator improvement in the cross-chapter comparison.
-- The remaining visibility cleanup makes local classes/functions private in
- AntiDeps1, Infinite, the Chapter 21/23 allocation String cases, the Chapter 23
- scanner/pointer/short-circuit/function cases, and Stack3/Stack5. Bubble Sort's
- copied library implementation is private; its `main` stays public. The three
- adapted String API fixtures end in `return 0;`, avoiding an accidental return
- of the last declared function. Their equality/hash APIs stay public, and the
- named StringExport fixture also keeps its public String factory. No cohort
- entries, workload operations, or intentional export APIs are removed.
-- Other source texts are retained.
-
-All entries replay **through RegAlloc**, checking scheduled register constraints.
-The historical phase column is provenance, not a promise to run those old native
-harnesses in Chapter 25. The adapted Bubble Sort retains its historical
-`i32 errno="C"` declaration for allocation comparability. Direct data bindings
-now encode on all three targets, but this declaration cannot link to a libc
-whose errno is thread-local or accessor-based. Current system-library code uses
-`libc.errno()` instead. Do not count this corpus as historical runtime coverage.
-
-`SpillStats` then compiles the current system library through encoding at seed
-456, and runs Chapter25Test with its existing seeds and native checks. Together
-these form cohort 25. A library or test failure makes the report fail; partial
-totals cannot establish an improvement. The ordinary full chapter suite remains
-the execution validation for the current compiler.
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/spill/cohorts.tsv b/chapter25/src/test/java/com/seaofnodes/simple/spill/cohorts.tsv
deleted file mode 100644
index 83b19d2a..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/spill/cohorts.tsv
+++ /dev/null
@@ -1,200 +0,0 @@
-# cohort test cpu abi original phase source
-Chapter20 Chapter20Test.testMergeSort x86_64_v2 SystemV RegAlloc Chapter20Test-testMergeSort.smp
-Chapter20 Chapter20Test.testMergeSort riscv SystemV RegAlloc Chapter20Test-testMergeSort.smp
-Chapter20 Chapter20Test.testMergeSort arm SystemV RegAlloc Chapter20Test-testMergeSort.smp
-Chapter20 Chapter20Test.testNewtonInteger x86_64_v2 SystemV RegAlloc Chapter20Test-testNewtonInteger.smp
-Chapter20 Chapter20Test.testNewtonInteger riscv SystemV RegAlloc Chapter20Test-testNewtonInteger.smp
-Chapter20 Chapter20Test.testNewtonInteger arm SystemV RegAlloc Chapter20Test-testNewtonInteger.smp
-Chapter20 Chapter20Test.testCast x86_64_v2 SystemV RegAlloc Chapter20Test-testCast.smp
-Chapter20 Chapter20Test.testCast riscv SystemV RegAlloc Chapter20Test-testCast.smp
-Chapter20 Chapter20Test.testCast arm SystemV RegAlloc Chapter20Test-testCast.smp
-Chapter20 Chapter20Test.testNewtonFloat x86_64_v2 SystemV RegAlloc Chapter20Test-testNewtonFloat.smp
-Chapter20 Chapter20Test.testNewtonFloat riscv SystemV RegAlloc Chapter20Test-testNewtonFloat.smp
-Chapter20 Chapter20Test.testNewtonFloat arm SystemV RegAlloc Chapter20Test-testNewtonFloat.smp
-Chapter20 Chapter20Test.testBrainfuck x86_64_v2 SystemV RegAlloc Chapter20Test-testBrainfuck.smp
-Chapter20 Chapter20Test.testBrainfuck riscv SystemV RegAlloc Chapter20Test-testBrainfuck.smp
-Chapter20 Chapter20Test.testBrainfuck arm SystemV RegAlloc Chapter20Test-testBrainfuck.smp
-Chapter20 Chapter20Test.testAlloc0 x86_64_v2 SystemV RegAlloc Chapter20Test-testAlloc0.smp
-Chapter20 Chapter20Test.testAlloc0 riscv SystemV RegAlloc Chapter20Test-testAlloc0.smp
-Chapter20 Chapter20Test.testAlloc0 arm SystemV RegAlloc Chapter20Test-testAlloc0.smp
-Chapter20 Chapter20Test.testAlloc2 x86_64_v2 SystemV RegAlloc Chapter20Test-testAlloc2.smp
-Chapter20 Chapter20Test.testAlloc2 riscv SystemV RegAlloc Chapter20Test-testAlloc2.smp
-Chapter20 Chapter20Test.testAlloc2 arm SystemV RegAlloc Chapter20Test-testAlloc2.smp
-Chapter20 Chapter20Test.testArray1 x86_64_v2 SystemV RegAlloc Chapter20Test-testArray1.smp
-Chapter20 Chapter20Test.testArray1 riscv SystemV RegAlloc Chapter20Test-testArray1.smp
-Chapter20 Chapter20Test.testArray1 arm SystemV RegAlloc Chapter20Test-testArray1.smp
-Chapter20 Chapter20Test.testBasic1 x86_64_v2 SystemV RegAlloc Chapter20Test-testBasic1.smp
-Chapter20 Chapter20Test.testBasic1 riscv SystemV RegAlloc Chapter20Test-testBasic1.smp
-Chapter20 Chapter20Test.testBasic1 arm SystemV RegAlloc Chapter20Test-testBasic1.smp
-Chapter20 Chapter20Test.testFlags1 x86_64_v2 SystemV RegAlloc Chapter20Test-testFlags1.smp
-Chapter20 Chapter20Test.testFlags1 riscv SystemV RegAlloc Chapter20Test-testFlags1.smp
-Chapter20 Chapter20Test.testFlags1 arm SystemV RegAlloc Chapter20Test-testFlags1.smp
-Chapter20 Chapter20Test.testFlags2 x86_64_v2 SystemV RegAlloc Chapter20Test-testFlags2.smp
-Chapter20 Chapter20Test.testFlags2 riscv SystemV RegAlloc Chapter20Test-testFlags2.smp
-Chapter20 Chapter20Test.testFlags2 arm SystemV RegAlloc Chapter20Test-testFlags2.smp
-Chapter20 Chapter20Test.testFltArg x86_64_v2 SystemV RegAlloc Chapter20Test-testFltArg.smp
-Chapter20 Chapter20Test.testFltArg riscv SystemV RegAlloc Chapter20Test-testFltArg.smp
-Chapter20 Chapter20Test.testFltArg arm SystemV RegAlloc Chapter20Test-testFltArg.smp
-Chapter20 Chapter20Test.testString x86_64_v2 SystemV RegAlloc Chapter20Test-testString.smp
-Chapter20 Chapter20Test.testString riscv SystemV RegAlloc Chapter20Test-testString.smp
-Chapter20 Chapter20Test.testString arm SystemV RegAlloc Chapter20Test-testString.smp
-Chapter21 Chapter21Test.testFibExport x86_64_v2 win64 Export Chapter21Test-testFibExport.smp
-Chapter21 Chapter21Test.testFibExport riscv SystemV Export Chapter21Test-testFibExport.smp
-Chapter21 Chapter21Test.testFibExport arm SystemV Export Chapter21Test-testFibExport.smp
-Chapter21 Chapter21Test.testAntiDeps1 x86_64_v2 SystemV Encoding Chapter21Test-testAntiDeps1.smp
-Chapter21 Chapter21Test.testAntiDeps1 riscv SystemV Encoding Chapter21Test-testAntiDeps1.smp
-Chapter21 Chapter21Test.testAntiDeps1 arm SystemV Encoding Chapter21Test-testAntiDeps1.smp
-Chapter21 Chapter21Test.testLoop2 x86_64_v2 win64 Encoding Chapter21Test-testLoop2.smp
-Chapter21 Chapter21Test.testLoop2 riscv SystemV Encoding Chapter21Test-testLoop2.smp
-Chapter21 Chapter21Test.testLoop2 arm SystemV Encoding Chapter21Test-testLoop2.smp
-Chapter21 Chapter21Test.testSieve x86_64_v2 win64 Export Chapter21Test-testSieve.smp
-Chapter21 Chapter21Test.testSieve riscv SystemV Export Chapter21Test-testSieve.smp
-Chapter21 Chapter21Test.testSieve arm SystemV Export Chapter21Test-testSieve.smp
-Chapter21 Chapter21Test.testArgCount x86_64_v2 win64 Export Chapter21Test-testArgCount.smp
-Chapter21 Chapter21Test.testArgCount riscv SystemV Export Chapter21Test-testArgCount-2.smp
-Chapter21 Chapter21Test.testArgCount arm SystemV Export Chapter21Test-testArgCount-2.smp
-Chapter21 Chapter21Test.testStringExport x86_64_v2 win64 Export Chapter21Test-testStringExport.smp
-Chapter21 Chapter21Test.testInfinite x86_64_v2 SystemV Encoding Chapter21Test-testInfinite.smp
-Chapter21 Chapter21Test.testInfinite riscv SystemV Encoding Chapter21Test-testInfinite.smp
-Chapter21 Chapter21Test.testInfinite arm SystemV Encoding Chapter21Test-testInfinite.smp
-Chapter21 Chapter21Test.testArray1 x86_64_v2 SystemV Encoding Chapter20Test-testArray1.smp
-Chapter21 Chapter21Test.testArray1 riscv SystemV Encoding Chapter20Test-testArray1.smp
-Chapter21 Chapter21Test.testArray1 arm SystemV Encoding Chapter20Test-testArray1.smp
-Chapter21 Chapter21Test.testBasic1 x86_64_v2 SystemV Encoding Chapter20Test-testBasic1.smp
-Chapter21 Chapter21Test.testBasic1 riscv SystemV Encoding Chapter20Test-testBasic1.smp
-Chapter21 Chapter21Test.testBasic1 arm SystemV Encoding Chapter20Test-testBasic1.smp
-Chapter21 Chapter21Test.testNewtonExport x86_64_v2 win64 Export Chapter21Test-testNewtonExport.smp
-Chapter21 Chapter21Test.testNewtonExport riscv SystemV Export Chapter21Test-testNewtonExport.smp
-Chapter21 Chapter21Test.testNewtonExport arm SystemV Export Chapter21Test-testNewtonExport.smp
-Chapter21 Chapter21Test.testPerson x86_64_v2 win64 Export Chapter21Test-testPerson.smp
-Chapter21 Chapter21Test.testPerson riscv SystemV Export Chapter21Test-testPerson.smp
-Chapter21 Chapter21Test.testPerson arm SystemV Export Chapter21Test-testPerson.smp
-Chapter21 Chapter21Test.testString x86_64_v2 SystemV Encoding Chapter21Test-testStringExport.smp
-Chapter21 Chapter21Test.testString riscv SystemV Encoding Chapter21Test-testStringExport.smp
-Chapter21 Chapter21Test.testString arm SystemV Encoding Chapter21Test-testStringExport.smp
-Chapter21 Chapter21AllocTest.testNewtonInteger x86_64_v2 win64 Encoding Chapter20Test-testNewtonInteger.smp
-Chapter21 Chapter21AllocTest.testNewtonInteger riscv SystemV Encoding Chapter20Test-testNewtonInteger.smp
-Chapter21 Chapter21AllocTest.testNewtonInteger arm SystemV Encoding Chapter20Test-testNewtonInteger.smp
-Chapter21 Chapter21AllocTest.testNewtonFloat x86_64_v2 SystemV Encoding Chapter21AllocTest-testNewtonFloat.smp
-Chapter21 Chapter21AllocTest.testNewtonFloat riscv SystemV Encoding Chapter21AllocTest-testNewtonFloat.smp
-Chapter21 Chapter21AllocTest.testNewtonFloat arm SystemV Encoding Chapter21AllocTest-testNewtonFloat.smp
-Chapter21 Chapter21AllocTest.testArray1 x86_64_v2 SystemV Encoding Chapter21AllocTest-testArray1.smp
-Chapter21 Chapter21AllocTest.testArray1 riscv SystemV Encoding Chapter21AllocTest-testArray1.smp
-Chapter21 Chapter21AllocTest.testArray1 arm SystemV Encoding Chapter21AllocTest-testArray1.smp
-Chapter21 Chapter21AllocTest.testString x86_64_v2 SystemV Encoding Chapter21AllocTest-testString.smp
-Chapter21 Chapter21AllocTest.testString riscv SystemV Encoding Chapter21AllocTest-testString.smp
-Chapter21 Chapter21AllocTest.testString arm SystemV Encoding Chapter21AllocTest-testString.smp
-Chapter22 Chapter22Test.testSextFail x86_64_v2 win64 Encoding Chapter22Test-testSextFail.smp
-Chapter22 Chapter22Test.testSextFail riscv SystemV Encoding Chapter22Test-testSextFail.smp
-Chapter22 Chapter22Test.testSextFail arm SystemV Encoding Chapter22Test-testSextFail.smp
-Chapter22 Chapter22Test.testSextFail riscv SystemV Export Chapter22Test-testSextFail.smp
-Chapter22 Chapter22Test.testSextFail arm SystemV Export Chapter22Test-testSextFail.smp
-Chapter22 Chapter22Test.testSextSuccess riscv SystemV Export Chapter22Test-testSextSuccess.smp
-Chapter22 Chapter22Test.testSextSuccess arm SystemV Export Chapter22Test-testSextSuccess.smp
-Chapter22 Chapter22Test.testSextSuccess x86_64_v2 win64 Encoding Chapter22Test-testSextSuccess.smp
-Chapter22 Chapter22Test.testSextSuccess riscv SystemV Encoding Chapter22Test-testSextSuccess.smp
-Chapter22 Chapter22Test.testSextSuccess arm SystemV Encoding Chapter22Test-testSextSuccess.smp
-Chapter22 Chapter22Test.testInfiniteReturn x86_64_v2 SystemV Encoding Chapter22Test-testInfiniteReturn.smp
-Chapter22 Chapter22Test.testInfiniteReturn riscv SystemV Encoding Chapter22Test-testInfiniteReturn.smp
-Chapter22 Chapter22Test.testInfiniteReturn arm SystemV Encoding Chapter22Test-testInfiniteReturn.smp
-Chapter22 Chapter22Test.testSextFail2 riscv SystemV Export Chapter22Test-testSextFail2.smp
-Chapter22 Chapter22Test.testSextFail2 arm SystemV Export Chapter22Test-testSextFail2.smp
-Chapter22 Chapter22Test.testSextFail2 x86_64_v2 win64 Encoding Chapter22Test-testSextFail2.smp
-Chapter22 Chapter22Test.testSextFail2 riscv SystemV Encoding Chapter22Test-testSextFail2.smp
-Chapter22 Chapter22Test.testSextFail2 arm SystemV Encoding Chapter22Test-testSextFail2.smp
-Chapter22 Chapter22Test.testPerson x86_64_v2 win64 Export Chapter22Test-testPerson.smp
-Chapter22 Chapter22Test.testPerson riscv SystemV Export Chapter22Test-testPerson.smp
-Chapter22 Chapter22Test.testPerson arm SystemV Export Chapter22Test-testPerson.smp
-Chapter22 Chapter22Test.testHelloWorld x86_64_v2 win64 Export Chapter22Test-testHelloWorld.smp
-Chapter22 Chapter22Test.testHelloWorld riscv SystemV Export Chapter22Test-testHelloWorld.smp
-Chapter22 Chapter22Test.testHelloWorld arm SystemV Export Chapter22Test-testHelloWorld.smp
-Chapter23 Chapter23Test.testAnd x86_64_v2 win64 Export Chapter23Test-testAnd.smp
-Chapter23 Chapter23Test.testAnd riscv SystemV Export Chapter23Test-testAnd.smp
-Chapter23 Chapter23Test.testAnd arm SystemV Export Chapter23Test-testAnd.smp
-Chapter23 Chapter23Test.testOr x86_64_v2 win64 Export Chapter23Test-testOr.smp
-Chapter23 Chapter23Test.testOr riscv SystemV Export Chapter23Test-testOr.smp
-Chapter23 Chapter23Test.testOr arm SystemV Export Chapter23Test-testOr.smp
-Chapter23 Chapter23Test.testCondSideEffAnd x86_64_v2 win64 Export Chapter23Test-testCondSideEffAnd.smp
-Chapter23 Chapter23Test.testCondSideEffAnd riscv SystemV Export Chapter23Test-testCondSideEffAnd.smp
-Chapter23 Chapter23Test.testCondSideEffAnd arm SystemV Export Chapter23Test-testCondSideEffAnd.smp
-Chapter23 Chapter23Test.testFRefFields x86_64_v2 win64 Encoding Chapter23Test-testFRefFields.smp
-Chapter23 Chapter23Test.testAndPtr riscv SystemV Export Chapter23Test-testAndPtr.smp
-Chapter23 Chapter23Test.testAndPtr arm SystemV Export Chapter23Test-testAndPtr.smp
-Chapter23 Chapter23Test.testOrRhsLoopLocal riscv SystemV Export Chapter23Test-testOrRhsLoopLocal.smp
-Chapter23 Chapter23Test.testMethod x86_64_v2 win64 Encoding Chapter23Test-testMethod.smp
-Chapter23 Chapter23Test.testOrRhsLoopMemory riscv SystemV Export Chapter23Test-testOrRhsLoopMemory.smp
-Chapter23 Chapter23Test.testCondSideEffOr x86_64_v2 win64 Export Chapter23Test-testCondSideEffOr.smp
-Chapter23 Chapter23Test.testCondSideEffOr riscv SystemV Export Chapter23Test-testCondSideEffOr.smp
-Chapter23 Chapter23Test.testCondSideEffOr arm SystemV Export Chapter23Test-testCondSideEffOr.smp
-Chapter23 Chapter23Test.testFuncCall x86_64_v2 win64 Export Chapter23Test-testFuncCall.smp
-Chapter23 Chapter23Test.testFuncCall riscv SystemV Export Chapter23Test-testFuncCall.smp
-Chapter23 Chapter23Test.testFuncCall arm SystemV Export Chapter23Test-testFuncCall.smp
-Chapter23 Chapter23AllocTest.testString x86_64_v2 SystemV Encoding Chapter23AllocTest-testString.smp
-Chapter23 Chapter23AllocTest.testString riscv SystemV Encoding Chapter23AllocTest-testString.smp
-Chapter23 Chapter23AllocTest.testString arm SystemV Encoding Chapter23AllocTest-testString.smp
-Chapter24 Chapter24Test.testAnd x86_64_v2 win64 Export Chapter23Test-testAnd.smp
-Chapter24 Chapter24Test.testAnd riscv SystemV Export Chapter23Test-testAnd.smp
-Chapter24 Chapter24Test.testAnd arm SystemV Export Chapter23Test-testAnd.smp
-Chapter24 Chapter24Test.testOr x86_64_v2 win64 Export Chapter23Test-testOr.smp
-Chapter24 Chapter24Test.testOr riscv SystemV Export Chapter23Test-testOr.smp
-Chapter24 Chapter24Test.testOr arm SystemV Export Chapter23Test-testOr.smp
-Chapter24 Chapter24Test.testCondSideEffAnd x86_64_v2 win64 Export Chapter23Test-testCondSideEffAnd.smp
-Chapter24 Chapter24Test.testCondSideEffAnd riscv SystemV Export Chapter23Test-testCondSideEffAnd.smp
-Chapter24 Chapter24Test.testCondSideEffAnd arm SystemV Export Chapter23Test-testCondSideEffAnd.smp
-Chapter24 Chapter24Test.testFRefFields x86_64_v2 win64 Encoding Chapter23Test-testFRefFields.smp
-Chapter24 Chapter24Test.testAndPtr riscv SystemV Export Chapter23Test-testAndPtr.smp
-Chapter24 Chapter24Test.testAndPtr arm SystemV Export Chapter23Test-testAndPtr.smp
-Chapter24 Chapter24Test.testMethod x86_64_v2 win64 Encoding Chapter23Test-testMethod.smp
-Chapter24 Chapter24Test.testStack1 x86_64_v2 win64 Export Chapter24Test-testStack1.smp
-Chapter24 Chapter24Test.testStack1 riscv SystemV Export Chapter24Test-testStack1.smp
-Chapter24 Chapter24Test.testStack1 arm SystemV Export Chapter24Test-testStack1.smp
-Chapter24 Chapter24Test.testStack2 x86_64_v2 win64 Export Chapter24Test-testStack2.smp
-Chapter24 Chapter24Test.testStack2 riscv SystemV Export Chapter24Test-testStack2.smp
-Chapter24 Chapter24Test.testStack2 arm SystemV Export Chapter24Test-testStack2.smp
-Chapter24 Chapter24Test.testStack3 x86_64_v2 win64 Export Chapter24Test-testStack3.smp
-Chapter24 Chapter24Test.testStack3 riscv SystemV Export Chapter24Test-testStack3.smp
-Chapter24 Chapter24Test.testStack3 arm SystemV Export Chapter24Test-testStack3.smp
-Chapter24 Chapter24Test.testStack4 x86_64_v2 win64 Export Chapter24Test-testStack4.smp
-Chapter24 Chapter24Test.testStack4 riscv SystemV Export Chapter24Test-testStack4.smp
-Chapter24 Chapter24Test.testStack4 arm SystemV Export Chapter24Test-testStack4.smp
-Chapter24 Chapter24Test.testStack5 x86_64_v2 win64 Export Chapter24Test-testStack5.smp
-Chapter24 Chapter24Test.testStack5 riscv SystemV Export Chapter24Test-testStack5.smp
-Chapter24 Chapter24Test.testStack5 arm SystemV Export Chapter24Test-testStack5.smp
-Chapter24 Chapter24Test.testStack9 x86_64_v2 win64 Export Chapter24Test-testStack9.smp
-Chapter24 Chapter24Test.testStack9 riscv SystemV Export Chapter24Test-testStack9-2.smp
-Chapter24 Chapter24Test.testStack9 arm SystemV Export Chapter24Test-testStack9-2.smp
-Chapter24 Chapter24Test.testCondSideEffOr x86_64_v2 win64 Export Chapter23Test-testCondSideEffOr.smp
-Chapter24 Chapter24Test.testCondSideEffOr riscv SystemV Export Chapter23Test-testCondSideEffOr.smp
-Chapter24 Chapter24Test.testCondSideEffOr arm SystemV Export Chapter23Test-testCondSideEffOr.smp
-Chapter24 Chapter24Test.testStack10 x86_64_v2 win64 Export Chapter24Test-testStack10.smp
-Chapter24 Chapter24Test.testStack10 riscv SystemV Export Chapter24Test-testStack10-2.smp
-Chapter24 Chapter24Test.testStack10 arm SystemV Export Chapter24Test-testStack10-2.smp
-Chapter24 Chapter24Test.testStack11 x86_64_v2 win64 Export Chapter24Test-testStack11.smp
-Chapter24 Chapter24Test.testStack11 riscv SystemV Export Chapter24Test-testStack11-2.smp
-Chapter24 Chapter24Test.testStack11 arm SystemV Export Chapter24Test-testStack11-2.smp
-Chapter24 Chapter24Test.testStack12 x86_64_v2 win64 Export Chapter24Test-testStack12.smp
-Chapter24 Chapter24Test.testStack12 riscv SystemV Export Chapter24Test-testStack12-2.smp
-Chapter24 Chapter24Test.testStack12 arm SystemV Export Chapter24Test-testStack12-2.smp
-Chapter24 Chapter24Test.testStack13 x86_64_v2 win64 Export Chapter24Test-testStack13.smp
-Chapter24 Chapter24Test.testStack13 riscv SystemV Export Chapter24Test-testStack13-2.smp
-Chapter24 Chapter24Test.testStack13 arm SystemV Export Chapter24Test-testStack13-2.smp
-Chapter24 Chapter24Test.testFuncCall x86_64_v2 win64 Export Chapter23Test-testFuncCall.smp
-Chapter24 Chapter24Test.testFuncCall riscv SystemV Export Chapter23Test-testFuncCall.smp
-Chapter24 Chapter24Test.testFuncCall arm SystemV Export Chapter23Test-testFuncCall.smp
-Chapter24 Chapter24AllocTest.testNewtonInteger x86_64_v2 win64 Encoding Chapter24AllocTest-testNewtonInteger.smp
-Chapter24 Chapter24AllocTest.testNewtonInteger riscv SystemV Encoding Chapter24AllocTest-testNewtonInteger.smp
-Chapter24 Chapter24AllocTest.testNewtonInteger arm SystemV Encoding Chapter24AllocTest-testNewtonInteger.smp
-Chapter24 Chapter24AllocTest.testStackArguments x86_64_v2 win64 Export Chapter24AllocTest-testStackArguments.smp
-Chapter24 Chapter24AllocTest.testStackArguments riscv SystemV Export Chapter24AllocTest-testStackArguments.smp
-Chapter24 Chapter24AllocTest.testStackArguments arm SystemV Export Chapter24AllocTest-testStackArguments.smp
-Chapter24 Chapter24AllocTest.testBubbleSort x86_64_v2 win64 Encoding Chapter24AllocTest-testBubbleSort.smp
-Chapter24 Chapter24AllocTest.testBubbleSort riscv SystemV Encoding Chapter24AllocTest-testBubbleSort.smp
-Chapter24 Chapter24AllocTest.testBubbleSort arm SystemV Encoding Chapter24AllocTest-testBubbleSort.smp
-Chapter21 BrainFuckTest.testBrainFuck x86_64_v2 win64 Export BrainFuckTest-testBrainFuck.smp
-Chapter21 BrainFuckTest.testBrainFuck riscv SystemV Export BrainFuckTest-testBrainFuck.smp
-Chapter21 BrainFuckTest.testBrainFuck arm SystemV Export BrainFuckTest-testBrainFuck.smp
-Chapter21 MergeSortTest.testMergeSort0 x86_64_v2 win64 Export MergeSortTest-testMergeSort0.smp
-Chapter21 MergeSortTest.testMergeSort0 riscv SystemV Export MergeSortTest-testMergeSort0.smp
-Chapter21 MergeSortTest.testMergeSort0 arm SystemV Encoding MergeSortTest-testMergeSort0.smp
-Chapter22 Chapter22Test.testNarrowCReturns x86_64_v2 SystemV Export Chapter22Test-testNarrowCReturns.smp
-Chapter22 Chapter22Test.testNarrowCReturns x86_64_v2 win64 Export Chapter22Test-testNarrowCReturns.smp
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp b/chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp
deleted file mode 100644
index b94fa147..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test0/A.smp
+++ /dev/null
@@ -1,6 +0,0 @@
-// -*- mode: java; -*-
-
-val _fcn = { -> arg ? { int x -> x+1; } : { int x -> x-1; }; };
-
-// Refer to child B is OK because last field of A has been initialized
-_fcn()(B.x);
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp b/chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp
deleted file mode 100644
index 82844f74..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test0/A/B.smp
+++ /dev/null
@@ -1 +0,0 @@
-val x=5;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp b/chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp
deleted file mode 100644
index f19bd57b..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test5/par.smp
+++ /dev/null
@@ -1,20 +0,0 @@
-// -*- mode: java; -*-
-
-// Parent module
-val par0 = 3; // Some final field
-
-// Nested class in parent.
-// Makes a field: "val child0 = ..."
-struct child0 {
- val kid0 = 5;
-};
-
-// Nested class in parent, comes from disk.
-// These are defined in-order
-val child1 = child1;
-
-// Nested class in parent, comes from disk
-val child2 = child2;
-
-// Nested class in parent, comes from disk
-val child3 = child3;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp b/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp
deleted file mode 100644
index 7bca0cf7..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child1.smp
+++ /dev/null
@@ -1,12 +0,0 @@
-// -*- mode: java; -*-
-
-// Child module
-
-// Same field name as child2
-val kid1 = 7;
-
-// Can read parent directly
-val kid2 = par0;
-
-// Error to reference a later sibling
-// val kid3 = par.child2.kid1;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp b/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp
deleted file mode 100644
index ab5e2ac1..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test5/par/child2.smp
+++ /dev/null
@@ -1,13 +0,0 @@
-// -*- mode: java; -*-
-
-// Child module
-
-// Same field name as child2
-val kid1 = 11;
-
-// Can read parent directly.
-// Can read defined-in-parent fields.
-val kid2 = child0.kid0;
-
-// OK to reference a past sibling
-val kid3 = child1.kid1;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m.smp
deleted file mode 100644
index 569f38be..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m.smp
+++ /dev/null
@@ -1,2 +0,0 @@
-val a = a;
-val b = b;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/a.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/a.smp
deleted file mode 100644
index 97113b71..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/a.smp
+++ /dev/null
@@ -1 +0,0 @@
-val f = { -> new b(1); };
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/b.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/b.smp
deleted file mode 100644
index 43770b60..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/forward_ctor/m/b.smp
+++ /dev/null
@@ -1,4 +0,0 @@
-struct b {
- int x;
- new b = { int y -> x = y; };
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys.smp
deleted file mode 100644
index 1725c57d..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys.smp
+++ /dev/null
@@ -1,3 +0,0 @@
-// Reduced multi-compilation-unit reproducer for order-sensitive inlining.
-var aryu8 = aryu8;
-var aryi64 = aryi64;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/ary.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/ary.smp
deleted file mode 100644
index e3dd6140..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/ary.smp
+++ /dev/null
@@ -1,6 +0,0 @@
-// Array utilities, discovered indirectly from aryu8.
-val copy = { u8[] dst, int doff, u8[~] src, int soff, int len ->
- for( int i=0; i<len; i++ )
- dst[i+doff] = src[i+soff];
- return dst;
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryi64.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryi64.smp
deleted file mode 100644
index 53c93666..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryi64.smp
+++ /dev/null
@@ -1,29 +0,0 @@
-struct aryi64 {
- u32 !len = 0;
- i64[] !buf;
-
- new aryi64 = { i64[] b -> buf = b; };
-
- val _grow = { int len2 ->
- if( len+len2 < buf# ) return self;
- int newlen = buf# * 2;
- while( len+len2 >= newlen ) newlen *= 2;
- i64[] !buf2 = new i64[newlen];
- for( int i=0; i<buf#; i++ )
- buf2[i] = buf[i];
- buf = buf2;
- self;
- };
-
- val add = { i64 c ->
- _grow(1).buf[len++] = c;
- self;
- };
-
- val str = { ->
- var str = new aryu8(new u8[1]).add('[');
- for( int i=0; i<len; i++ )
- str.num(buf[i]).add(',').add(' ');
- str.undo(2).add(']');
- };
-};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryu8.smp b/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryu8.smp
deleted file mode 100644
index e6b708a4..00000000
--- a/chapter25/src/test/java/com/seaofnodes/simple/test_smp/seed_repro/sys/aryu8.smp
+++ /dev/null
@@ -1,50 +0,0 @@
-struct aryu8 {
- u32 !len = 0;
- u8[] !buf;
-
- new aryu8 = { u8[] b -> buf = b; };
-
- val _grow = { int len2 ->
- if( len+len2 < buf# ) return self;
- int newlen = buf# * 2;
- while( len+len2 >= newlen ) newlen *= 2;
- u8[] !buf2 = new u8[newlen];
- ary.copy(buf2,0,buf,0,buf#);
- buf = buf2;
- self;
- };
-
- val clear = { -> len = 0; self; };
-
- val add = { u8 c ->
- _grow(1).buf[len++] = c;
- self;
- };
-
- val addAry = { u8[~] xs ->
- _grow(xs#);
- ary.copy(buf,len,xs,0,xs#);
- len += xs#;
- self;
- };
-
- val undo = { int x ->
- len = len >= x ? len-x : 0;
- self;
- };
-
- val num = { i64 x ->
- if( !x ) { add('0'); return self; }
- if( x < 0 ) { add('-'); x = -x; }
- i64 ten = 1, log = 0;
- while( ten <= x ) { ten *= 10; log++; }
- for( int i=0; i<log; i++ ) {
- ten /= 10;
- i64 q = x/ten;
- u8 digit = q+'0';
- add(digit);
- x -= q*ten;
- }
- self;
- };
-};