summaryrefslogtreecommitdiff
path: root/chapter25/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-09-19 18:50:22 -0700
committerCliff Click <cliffc@acm.org>2026-09-19 18:50:22 -0700
commit2f779dd115acf1fc99c7b3b416273fdb66ad48cb (patch)
treea2b7c9ad70ef2e56d5ec3fb7fbb908c77c00ea06 /chapter25/src
parent628d94039c38f9d13df661da66df69943ccb2061 (diff)
downloadSimple-2f779dd115acf1fc99c7b3b416273fdb66ad48cb.tar.gz
Simple-2f779dd115acf1fc99c7b3b416273fdb66ad48cb.zip
debug printers no longer call self-optimizing accessors
Diffstat (limited to 'chapter25/src')
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java11
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java12
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java6
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java13
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java2
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java14
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java28
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java22
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java5
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java25
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java20
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java4
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java76
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/node/PrintTestSupport.java39
20 files changed, 272 insertions, 25 deletions
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
index ca2cc955..2fccb5f3 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java
@@ -378,8 +378,8 @@ public class CodeGen {
public FunNode link( TypeFunPtr tfp ) { return link(tfp.fidx()); }
// Read-only linker lookup, suitable for printers and debugger display.
// In particular, do not lazily remove a partially dead function.
- public FunNode lookupFun(int fidx) { return _linker.atX(fidx); }
- public FunNode lookupFun(TypeFunPtr tfp) { return lookupFun(tfp.fidx()); }
+ public FunNode _link(int fidx) { return _linker.atX(fidx); }
+ public FunNode _link(TypeFunPtr tfp) { return _link(tfp.fidx()); }
// Return the FunNode from a fidx
public FunNode link( int fidx ) {
FunNode fun =_linker.atX(fidx);
@@ -409,7 +409,7 @@ public class CodeGen {
public String externFunc(int fidx) { return _externFunc.get(fidx); }
public String funcName(int fidx ) {
- FunNode fun = link(fidx);
+ FunNode fun = _link(fidx);
if( fun!=null ) return fun._name;
return externFunc(fidx);
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
index 48ca4335..618ec689 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -46,17 +46,24 @@ public class Encoding {
public static class Relo {
public final Node _op;
public final Type _t; // Constant type
+ public final int _align; // Log alignment, captured for diagnostic display
+ public final int _structSize; // Capture layout during encoding, not printing
public final byte _off; // Offset from start of opcode
public final byte _elf; // ELF relocation type, e.g. 2/PC32
public int _target; // Where constant is finally placed
public int _opStart; // Opcode start
Relo( Node op, Type t, byte off, byte elf ) {
_op=op; _t=t; _off=off; _elf=elf;
+ _align = t.alignment();
+ _structSize = t instanceof TypeStruct ts ? ts.size() : 0;
+ _readOnly = readOnly(t);
}
- public boolean readOnly() {
+ public boolean readOnly() { return _readOnly; }
+ private final boolean _readOnly;
+ private static boolean readOnly(Type t) {
// Class initializers still write their fields, even when every
// final value is constant. Their storage must remain writable.
- return !(_t instanceof TypeStruct ts) ||
+ return !(t instanceof TypeStruct ts) ||
(!Parser.startsClzPrefix(ts._name) && ts.isConstant());
}
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java
index 7c14965c..132705aa 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/Opto.java
@@ -140,7 +140,7 @@ abstract public class Opto {
// unlinkStart deliberately leaves uncalled functions in a
// temporarily dead limbo. A newly discovered call revives them;
// do not use the cleanup lookup which removes dead linker entries.
- FunNode fun = code.lookupFun(fidx);
+ FunNode fun = code._link(fidx);
// null here means an external function; i.e. this Call
// calls to an outside library and all its arguments escape.
if( fun != null && !call.linked(fun) ) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
index 6650b19c..f8c96ff2 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -130,14 +130,22 @@ public class RegAlloc {
}
+ // Diagnostic lookup: do not compress leaders or rewrite the node mapping.
+ private LRG _lrg(Node n) {
+ LRG lrg = _lrgs.get(n);
+ if( lrg != null )
+ while( lrg._leader != null ) lrg = lrg._leader;
+ return lrg;
+ }
+
public short regnum( Node n ) {
- LRG lrg = lrg(n);
+ LRG lrg = _lrg(n);
return lrg==null ? -1 : lrg._reg;
}
// Printable register number for node n
String reg( Node n ) {
- LRG lrg = lrg(n);
+ LRG lrg = _lrg(n);
return lrg==null ? null : reg(lrg);
}
String reg( LRG lrg ) {
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 51d10702..c053f377 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -38,8 +38,8 @@ public class CallNode extends CFGNode {
return sb.append(")");
}
public String name() {
- if( fptr()._type instanceof TypeFunPtr tfp && tfp.isConstant() ) {
- FunNode fun = CodeGen.CODE.lookupFun(tfp);
+ if( fptr()._type instanceof TypeFunPtr tfp && tfp._isConstant() && tfp.notNull() ) {
+ FunNode fun = CodeGen.CODE._link(tfp);
if( fun !=null ) return fun._name;
if( fptr() instanceof ExternNode ex ) return ex._extern;
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index feed8c5c..df2dc27b 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -45,7 +45,7 @@ public class ConstantNode extends TypeNode {
CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Opto.ordinal() )
return raw(type);
if( type instanceof TypeFunPtr tfp && tfp.isConstant() ) {
- FunNode fun = CodeGen.CODE.lookupFun(tfp);
+ FunNode fun = CodeGen.CODE._link(tfp);
if( fun != null )
return new FunPtrNode(tfp,CodeGen.CODE._start,fun.ret()).init();
// TODO: A singleton FIDX can also emerge after merging and
@@ -74,8 +74,8 @@ public class ConstantNode extends TypeNode {
@Override
public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- if( _con instanceof TypeFunPtr tfp && tfp.isConstant() ) {
- FunNode fun = CodeGen.CODE.lookupFun(tfp);
+ if( _con instanceof TypeFunPtr tfp && tfp._isConstant() && tfp.notNull() ) {
+ FunNode fun = CodeGen.CODE._link(tfp);
if( fun!=null && fun._name != null )
return sb.append("{ ").append(fun._name).append("}");
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java
index ce53b073..7f0b2b7d 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/FunPtrNode.java
@@ -35,7 +35,7 @@ public class FunPtrNode extends TypeNode {
@Override
public StringBuilder _print1(StringBuilder sb, BitSet visited) {
- FunNode fun = CodeGen.CODE.lookupFun(((TypeFunPtr)_con).fidx());
+ FunNode fun = CodeGen.CODE._link(((TypeFunPtr)_con).fidx());
return fun!=null && fun._name!=null
? sb.append("{ ").append(fun._name).append("}")
: sb.append(_con);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 87d982f1..e16eee9e 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -32,7 +32,7 @@ public class MemMergeNode extends Node {
Node n = in(j);
while( n instanceof ScopeNode loop ) {
sb.append("Lazy_");
- n = loop.mem(j);
+ n = loop.mem();
}
if( n==null ) sb.append("___ ");
else n._print0(sb, visited).append(" ");
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index 7f9f3ad9..ff3eada2 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -8,7 +8,9 @@ import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import com.seaofnodes.simple.util.*;
import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
+import java.util.IdentityHashMap;
+import java.util.Set;
public abstract class ASMPrinter {
@@ -43,16 +45,15 @@ public abstract class ASMPrinter {
sb.p("--- ").p(msg).p(" ------").nl();
// radix sort the big constants by alignment
Ary<Relo>[] raligns = new Ary[5];
- for( Node op : bigCons.keySet() ) {
- Relo relo = bigCons.get(op);
+ for( Relo relo : bigCons.values() ) {
if( relo.readOnly() != ro )
continue;
- int align = relo._t.alignment();
+ int align = relo._align;
Ary<Relo> relos = raligns[align]==null ? (raligns[align]=new Ary<>( Relo.class)) : raligns[align];
relos.add(relo);
}
- HashSet<Type> targets = new HashSet<>();
+ Set<Type> targets = Collections.newSetFromMap(new IdentityHashMap<>());
// By alignment
int dadr=0;
@@ -64,7 +65,7 @@ public abstract class ASMPrinter {
targets.add(relo._t);
sb.hex2(iadr+dadr).p(" ");
if( relo._t instanceof TypeStruct ts ) {
- int sz = ts.size();
+ int sz = relo._structSize;
int log = 1<<align;
sz = (sz + (log -1)) & -log; // Round up final padding
for( int i=0; i<sz; i++ )
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index 975bce3d..fdf7fe2f 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -180,7 +180,7 @@ public class TypeFunPtr extends TypeNil {
@Override public boolean isHigh() { return _nil <= 1 || (_nil==2 && _fidxs== XInt.EMPTY); }
- @Override boolean _isConstant() { return (_nil==2 && XInt.isConstant(_fidxs)) || (_nil==3 && _fidxs== XInt.EMPTY); }
+ @Override public boolean _isConstant() { return (_nil==2 && XInt.isConstant(_fidxs)) || (_nil==3 && _fidxs== XInt.EMPTY); }
@Override boolean _isFinal() { return true; }
@Override TypeFunPtr _makeStorage() { return this; }
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
index 405e64d0..1306a39a 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -118,7 +118,7 @@ public class TypeInteger extends TypeScalar {
if( this==I16 || this==U16 ) return 1; // 1<<1 == 2 bytes
if( this==I32 || this==U32 ) return 2; // 1<<2 == 4 bytes
if( this==BOT ) return 3; // 1<<3 == 8 bytes
- if( isConstant() ) return log(_min); // just const here
+ if( _isConstant() ) return log(_min); // just const here
int lo = log(_min);
int hi = log(_max);
return Math.max(lo,hi);
@@ -131,7 +131,7 @@ public class TypeInteger extends TypeScalar {
return 3;
}
- public long value() { assert isConstant(); return _min; }
+ public long value() { assert _isConstant(); return _min; }
// AND-mask of forced zeros. e.g. unsigned types will return their mask;
// u8 will return 0xFF. But also a range of 16-18 (0x10-0x12) will return
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index ab730b45..60a31c40 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -1,10 +1,22 @@
package com.seaofnodes.simple;
-
import com.seaofnodes.simple.codegen.CodeGen;
+
+
import org.junit.Test;
import static org.junit.Assert.assertEquals;
public class Chapter11Test {
+ @Test public void testPrintingPreservesGraph() throws Exception {
+ var code = new CodeGen("while(arg < 10) arg = arg + 1; return arg;").parse().opto().typeCheck().GCM().localSched();
+ var stop = code._stop;
+ new com.seaofnodes.simple.node.ConstantNode(com.seaofnodes.simple.type.TypeInteger.constant(123456));
+ com.seaofnodes.simple.node.PrintTestSupport.unchanged(stop, () -> {
+ String first = stop.p(99);
+ assertEquals(first,stop.p(99));
+ stop.toString();
+ });
+ }
+
@Test
public void testPrimes() {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
index dc29dafa..d7fd12ff 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter18Test.java
@@ -1,4 +1,7 @@
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;
@@ -8,6 +11,31 @@ import static org.junit.Assert.*;
import static org.junit.Assert.fail;
public class Chapter18Test {
+ @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));
+ }
+
+ @Test public void testPrintingLazyMemory() throws Exception {
+ var code = new CodeGen("return 0;").parse();
+ var base = new ConstantNode(com.seaofnodes.simple.type.TypeMem.BOT);
+ var outer = new ScopeNode();
+ var inner = new ScopeNode();
+ outer.addDef(null); outer.addDef(base);
+ inner.addDef(null); inner.addDef(outer);
+ var mem = new MemMergeNode(null,base,inner);
+ PrintTestSupport.unchanged(mem, () -> org.junit.Assert.assertTrue(mem.toString().contains("Lazy_")));
+ }
+
@Test
public void testJig() {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
index 10c4690e..5ab7ba97 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter19Test.java
@@ -1,4 +1,7 @@
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;
@@ -7,6 +10,25 @@ 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());
+ }
+
@Test
public void testJig() {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
index e8a3a875..43112594 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter20Test.java
@@ -1,10 +1,15 @@
package com.seaofnodes.simple;
+
import com.seaofnodes.simple.codegen.CodeGen;
import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter20Test {
+ @Test public void testPrintingRegisters() throws Exception {
+ com.seaofnodes.simple.codegen.PrintRegTestSupport.check();
+ }
+
@Test
public void testJig() {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
index 3346aa51..0e6398ee 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter23Test.java
@@ -1,4 +1,7 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.cpus.riscv.riscv;
@@ -10,6 +13,28 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter23Test {
+ @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 {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
index eb89dbb3..1307d2ce 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter24Test.java
@@ -1,4 +1,7 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+
import com.seaofnodes.simple.codegen.CodeGen;
import com.seaofnodes.simple.node.cpus.riscv.riscv;
@@ -7,6 +10,23 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter24Test {
+ @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));
+ }
+
@Test
public void testJig() throws IOException {
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
index d2357ef1..b96ae836 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/Chapter25Test.java
@@ -20,6 +20,10 @@ import org.junit.Test;
import static org.junit.Assert.*;
public class Chapter25Test {
+ @Test public void testPrintingConstantPool() throws Exception {
+ com.seaofnodes.simple.codegen.PrintRegTestSupport.checkConstantPool();
+ }
+
@Ignore
@Test
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java
new file mode 100644
index 00000000..b255bcf7
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/codegen/PrintRegTestSupport.java
@@ -0,0 +1,76 @@
+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/node/PrintTestSupport.java b/chapter25/src/test/java/com/seaofnodes/simple/node/PrintTestSupport.java
new file mode 100644
index 00000000..70d9071a
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/node/PrintTestSupport.java
@@ -0,0 +1,39 @@
+package com.seaofnodes.simple.node;
+import java.util.*;
+import static org.junit.Assert.*;
+
+// Inspect raw fields: the regression must not itself use lazy accessors.
+public class PrintTestSupport {
+ public static void unchanged(Node root, Runnable print) throws Exception {
+ var hash = Node.class.getDeclaredField("_hash");
+ hash.setAccessible(true);
+ var nodes = new ArrayList<Node>();
+ var seen = new IdentityHashMap<Node,Boolean>();
+ nodes.add(root);
+ seen.put(root,true);
+ for( int i=0; i<nodes.size(); i++ ) {
+ Node n = nodes.get(i);
+ for( Node def : n._inputs )
+ if( def!=null && seen.put(def,true)==null ) nodes.add(def);
+ for( Node use : n._outputs )
+ if( use!=null && seen.put(use,true)==null ) nodes.add(use);
+ }
+ var ins = new IdentityHashMap<Node,Node[]>();
+ var outs = new IdentityHashMap<Node,Node[]>();
+ var hashes = new IdentityHashMap<Node,Integer>();
+ var depths = new IdentityHashMap<Node,Integer>();
+ for( Node n : nodes ) {
+ ins.put(n,n._inputs.toArray(new Node[0]));
+ outs.put(n,n._outputs.toArray(new Node[0]));
+ hashes.put(n,hash.getInt(n));
+ }
+ print.run();
+ for( Node n : nodes ) {
+ assertEquals("hash of node "+n._nid,(int)hashes.get(n),hash.getInt(n));
+ assertEquals(ins.get(n).length,n.nIns());
+ assertEquals(outs.get(n).length,n.nOuts());
+ for( int i=0; i<n.nIns(); i++ ) assertSame(ins.get(n)[i],n.in(i));
+ for( int i=0; i<n.nOuts(); i++ ) assertSame(outs.get(n)[i],n._outputs.get(i));
+ }
+ }
+}