summaryrefslogtreecommitdiff
path: root/chapter25/src/test/java/com/seaofnodes
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-05 09:35:31 -0700
committerCliff Click <cliffc@acm.org>2026-10-05 09:35:31 -0700
commit3e950abc9eb2bcafbb36761ace5fcf5707a503a3 (patch)
tree263244543dc0a63d454b16521a30ccb4526f58e1 /chapter25/src/test/java/com/seaofnodes
parent9b3ad43f7aa2ec6d5f61fb9ff3c432101a5fba52 (diff)
downloadSimple-3e950abc9eb2bcafbb36761ace5fcf5707a503a3.tar.gz
Simple-3e950abc9eb2bcafbb36761ace5fcf5707a503a3.zip
Partial integer updateabandoned
Diffstat (limited to 'chapter25/src/test/java/com/seaofnodes')
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java1
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/IntegerIdentityTest.java113
2 files changed, 114 insertions, 0 deletions
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index 66bc4717..912c78ae 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -34,6 +34,7 @@ public class FuzzerWrap {
};
private static final long[] OPEN_FAILING_SEEDS = {
+ 5948158604996761547L,
};
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/IntegerIdentityTest.java b/chapter25/src/test/java/com/seaofnodes/simple/IntegerIdentityTest.java
new file mode 100644
index 00000000..90864731
--- /dev/null
+++ b/chapter25/src/test/java/com/seaofnodes/simple/IntegerIdentityTest.java
@@ -0,0 +1,113 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.*;
+import com.seaofnodes.simple.codegen.CodeGen;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+public class IntegerIdentityTest {
+ private static final String[] OPS = {"And","Or","Xor","Shl","Sar","Shr","Add","Mul","Div"};
+
+ @Test public void testIdentityWaitsForIntegerInput() {
+ new CodeGen("return 0;").parse();
+ for( String name : OPS ) {
+ Node lhs = input(Type.BOTTOM);
+ Node rhs = input(TypeInteger.constant(identity(name)));
+ Node op = op(name,lhs,rhs);
+ assertFalse(name,lhs._type.isa(op.compute()));
+ assertNull(name,op.idealize());
+ // Model a provisional input lifting to int. The same fold can now fire.
+ lhs._type = TypeInteger.BOT;
+ assertSame(name,lhs,op.idealize());
+ assertTrue(name,lhs._type.isa(op.compute()));
+ }
+ }
+
+ @Test public void testNonIntegerIdentityCannotEraseTheOperator() {
+ new CodeGen("return 0;").parse();
+ for( Type type : new Type[]{TypeFloat.constant(3.5),TypeScalar.BOT} )
+ for( String name : OPS ) {
+ Node lhs=input(type);
+ Node op=op(name,lhs,input(TypeInteger.constant(identity(name))));
+ assertNull(name,op.idealize());
+ }
+ }
+
+ @Test public void testIntegerIdentitiesAndMaskedShiftsStillFold() {
+ new CodeGen("return 0;").parse();
+ for( String name : OPS )
+ for( Type type : new Type[]{TypeInteger.BOT,TypeInteger.make(2,7),TypeInteger.constant(5)} ) {
+ Node lhs=input(type);
+ long[] identities = name.equals("And") ? new long[]{-1} :
+ name.equals("Shl") || name.equals("Sar") || name.equals("Shr") ? new long[]{0,64,-64} : new long[]{identity(name)};
+ for( long identity : identities ) {
+ Node op=op(name,lhs,input(TypeInteger.constant(identity)));
+ assertSame(name,lhs,op.idealize());
+ assertTrue(name,lhs._type.isa(op.compute()));
+ }
+ }
+ }
+
+ @Test public void testFloatingIdentitiesKeepTheirResultType() {
+ new CodeGen("return 0;").parse();
+ for( Type type : new Type[]{Type.BOTTOM,TypeInteger.BOT,TypeFloat.F64} ) {
+ Node lhs=input(type);
+ Node zero=input(TypeInteger.ZERO), one=input(TypeInteger.TRUE);
+ Node[] ops = {new AddNode(lhs,zero,(byte)2), new MulNode(lhs,one,(byte)2),
+ new DivNode(lhs,one,(byte)2),
+ new DivNode(lhs,input(TypeFloat.constant(1.)),(byte)2)};
+ for( Node op : ops ) {
+ Node replacement=op.idealize();
+ if( type==TypeFloat.F64 )
+ assertSame(lhs,replacement);
+ else
+ assertNotSame(lhs,replacement);
+ if( replacement!=null )
+ assertTrue(replacement.compute().isa(op.compute()));
+ }
+ }
+ }
+
+ @Test public void testDoubleNegationWaitsForIntegerInput() {
+ new CodeGen("return 0;").parse();
+ for( Type type : new Type[]{Type.BOTTOM,TypeFloat.constant(3.5),TypeScalar.BOT} ) {
+ Node lhs=input(type);
+ Node inner=new MinusNode(lhs,(byte)1);
+ inner._type=inner.compute();
+ Node outer=new MinusNode(inner,(byte)1);
+ assertFalse(lhs._type.isa(outer.compute()));
+ assertNull(outer.idealize());
+ lhs._type=TypeInteger.make(2,7);
+ inner._type=inner.compute();
+ assertSame(lhs,outer.idealize());
+ assertTrue(lhs._type.isa(outer.compute()));
+ }
+ }
+
+ // These tests exercise idealize directly, before GVN or constant folding.
+ private static Node input(Type type) {
+ Node n = new ConstantNode(type);
+ n._type=type;
+ return n;
+ }
+
+ private static long identity(String name) {
+ return name.equals("And") ? -1 : name.equals("Mul") || name.equals("Div") ? 1 : 0;
+ }
+
+ private static Node op(String name, Node lhs, Node rhs) {
+ return switch(name) {
+ case "And" -> new AndNode(null,lhs,rhs);
+ case "Or" -> new OrNode(null,lhs,rhs);
+ case "Xor" -> new XorNode(null,lhs,rhs);
+ case "Shl" -> new ShlNode(null,lhs,rhs);
+ case "Sar" -> new SarNode(null,lhs,rhs);
+ case "Shr" -> new ShrNode(null,lhs,rhs);
+ case "Add" -> new AddNode(lhs,rhs,(byte)1);
+ case "Mul" -> new MulNode(lhs,rhs,(byte)1);
+ case "Div" -> new DivNode(lhs,rhs,(byte)1);
+ default -> throw new AssertionError(name);
+ };
+ }
+}