summaryrefslogtreecommitdiff
path: root/chapter25/src/test/java/com/seaofnodes
diff options
context:
space:
mode:
Diffstat (limited to 'chapter25/src/test/java/com/seaofnodes')
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java35
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java32
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java9
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java27
4 files changed, 102 insertions, 1 deletions
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index 815a035f..2c61fb44 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -11,6 +11,41 @@ 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; }; "+
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index e39c09e7..245ea745 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -8,6 +8,38 @@ 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() {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
index c05c13f1..cad16f2b 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -25,6 +25,15 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter25Test {
+ @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 testExternData() throws IOException {
String src = """
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
index 873578c2..93380c87 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/codegen/RegAllocTestSupport.java
@@ -175,6 +175,28 @@ public class RegAllocTestSupport {
}
}
+ 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 )
@@ -190,7 +212,10 @@ public class RegAllocTestSupport {
}
if( n instanceof PhiNode && code._regAlloc.regnum(n)>=0 )
for( int i=n instanceof ParmNode ? 2 : 1; i<n.nIns(); i++ )
- assertEquals(code._regAlloc.regnum(n),code._regAlloc.regnum(n.in(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)));
}
}