diff options
| author | Cliff Click <cliffc@acm.org> | 2026-09-14 10:39:24 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2026-09-14 10:39:24 -0700 |
| commit | ccd14c5901c5eff7a4be463bf7cdd91b03e1ca38 (patch) | |
| tree | f7a3b3bdac559d2217dc164e0dd94be4a14d6b92 | |
| parent | 86339c69320760c5b1acae681b9938d4c9f897f6 (diff) | |
| download | Simple-ccd14c5901c5eff7a4be463bf7cdd91b03e1ca38.tar.gz Simple-ccd14c5901c5eff7a4be463bf7cdd91b03e1ca38.zip | |
int-only ops force inputs to be int-only
Lest an int-only op, having lifted result types to int, get a zero-fold (e.g. X^0) to their mode-not-set input and have types fall.
Handle idepth-cache overflow
11 files changed, 52 insertions, 19 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 24a12e6d..ca2cc955 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -342,19 +342,23 @@ public class CodeGen { // idepths are cached and valid until *inserting* CFG edges (deleting is // OK). This happens with inlining, which bumps the version to bulk // invalidate the idepth caches. + private static final int IDEPTH_STRIDE = 1_000_000; private int _iDepthVersion = 0; - public void invalidateIDepthCaches() { _iDepthVersion++; } + public void invalidateIDepthCaches() { + ++_iDepthVersion; + assert _iDepthVersion < IDEPTH_STRIDE; // Wrapped; needs a major hack to fix + } public boolean validIDepth(int idepth) { if( idepth==0 ) return false; if( _iDepthVersion==0 ) return true; - return (idepth%100)==_iDepthVersion; + return (idepth%IDEPTH_STRIDE)==_iDepthVersion; } public int iDepthAt(int idepth) { - return 100*idepth+_iDepthVersion; + return IDEPTH_STRIDE*idepth+_iDepthVersion; } public int iDepthFrom(int idepth) { assert idepth==0 || validIDepth(idepth); - return idepth+100; + return idepth+IDEPTH_STRIDE; } // Popular visit bitset, declared here, so it gets reused all over @@ -815,7 +819,7 @@ public class CodeGen { // need to be re-hooked to stop/start less they go dead. for( FunNode fun : _linker ) { // Already linked to start, not going dead - if( fun==null || fun.isDead() || fun._type.isHigh() ) + if( fun==null || fun.isDead() ) continue; // Imported functions are not emitted by this object. Unlink any @@ -854,7 +858,7 @@ public class CodeGen { ReturnNode ret = fun.ret(); StartNode start = fun._compunit._start; assert start!=null; - if( fun.in(1) != start ) { + if( fun.nIns() <= 1 || fun.in(1) != start ) { fun.insertDef(1,start); for( Node use : fun._outputs ) if( use instanceof ParmNode parm ) @@ -876,6 +880,16 @@ public class CodeGen { } } + _stop.walk(n -> { + if( n instanceof CallEndNode cend ) + for( int i=1; i<cend.nIns(); i++ ) + if( cend.in(i) instanceof ReturnNode ) + cend.delDef(i--); + if( n instanceof CallNode call ) + call.unlink_all(); + return null; + }); + _times[Phase.Unlink.ordinal()] = System.currentTimeMillis() - t0; } diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java index 083a9a8c..95095ac3 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/AndNode.java @@ -22,6 +22,7 @@ public class AndNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t1 = lhs._type; Type t2 = rhs._type; if( !(t1 instanceof TypeInteger && t2 instanceof TypeInteger t2i) ) @@ -30,7 +31,7 @@ public class AndNode extends ArithNode { // And of -1. We do not check for (-1&x) because this will already // canonicalize to (x&-1). We do not check for zero, because // the compute() call will return a zero already. - if( t2i.isConstant() && t2i.value()==-1 ) + if( t2i.isConstant() && t2i.value()==-1 && forceInt(lhs) ) return lhs; // Move constants to RHS: con*arg becomes arg*con diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java index ffba1f16..dfd81504 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ArithNode.java @@ -50,6 +50,18 @@ public abstract class ArithNode extends Node implements ModeNode { return 0; } + // Int-only ops, such as &, | and shifts, can collapse to one input. If + // that input, or the live arms of a Phi input, are still mode-polymorphic + // ops, settle them before returning the value so the replacement remains + // at least as narrow as the int-only op. + static boolean forceInt(Node n) { + if( n instanceof ModeNode mode && mode.mode()==0 ) { + mode.setMode((byte)1); + n.setType(n.compute()); + } + return true; + } + // Check that a settled mode agrees with all data inputs. Takes the Node // instead of a varargs list both to avoid allocation and to keep this ready // to become shared mode-aware Node behavior later. diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java index 4df68a3b..2261adab 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MinusNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import com.seaofnodes.simple.util.BAOS; import com.seaofnodes.simple.util.Utils; @@ -49,7 +50,7 @@ public class MinusNode extends Node implements ModeNode { if( t instanceof TypeInteger ) mode=1; } if( mode==0 ) - return Type.BOTTOM; + return CodeGen.CODE._phase.ordinal() < CodeGen.Phase.Opto.ordinal() ? Type.BOTTOM : Type.TOP; if( mode==1 ) { if( t instanceof TypeInteger i0 && i0._min != Long.MIN_VALUE && i0._max != Long.MIN_VALUE ) diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java index 66e561dc..31d0565b 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/OrNode.java @@ -22,12 +22,13 @@ public class OrNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t1 = lhs._type; Type t2 = rhs._type; // Or of 0. We do not check for (0|x) because this will already // canonicalize to (x|0) - if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && t1.isa(TypeInteger.BOT) ) + if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && t1.isa(TypeInteger.BOT) && forceInt(lhs) ) return lhs; // Move constants to RHS: con*arg becomes arg*con diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java index dc48f6f2..81d0d6b9 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -111,7 +111,8 @@ public class RegionNode extends CFGNode { CodeGen.CODE.addAll(phi.delDef(path)._outputs); } } - return isDead() ? CodeGen.CODE.XCTRL : delDef(path); + if( !isDead() ) delDef(path); + return isDead() ? CodeGen.CODE.XCTRL : this; } private int findDeadInput(int startIdx) { diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java index 8ad5988d..e3f99ed4 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/SarNode.java @@ -24,10 +24,11 @@ public class SarNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t2 = rhs._type; // Sar of 0. - if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 ) + if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 && forceInt(lhs) ) return lhs; // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5) diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java index 459b26eb..24d9bdb4 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -33,7 +33,7 @@ public class ShlNode extends ArithNode { if( rhs._type instanceof TypeInteger shl && shl.isConstant() ) { // Shl of 0. - if( (shl.value()&63)==0 ) + if( (shl.value()&63)==0 && forceInt(lhs) ) return lhs; // (x + c) << i => (x << i) + (c << i) if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof TypeInteger c && c.isConstant() ) { diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java index 5e4870d1..1bf7d118 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -23,7 +23,7 @@ public class ShrNode extends ArithNode { Type t2 = rhs._type; // Shr of 0. - if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 ) + if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 && forceInt(lhs) ) return lhs; // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5) diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java index 95d4461e..27ebc3dc 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/XorNode.java @@ -20,12 +20,13 @@ public class XorNode extends ArithNode { public Node idealize() { Node lhs = in(1); Node rhs = in(2); + Type t1 = lhs._type; Type t2 = rhs._type; // Xor of 0. We do not check for (0^x) because this will already // canonicalize to (x^0) - if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 ) + if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==0 && forceInt(lhs) ) return lhs; // Move constants to RHS: con*arg becomes arg*con diff --git a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java index 3aaa290a..eb4454d5 100644 --- a/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java +++ b/chapter25/src/test/java/com/seaofnodes/simple/FuzzerWrap.java @@ -23,13 +23,13 @@ public class FuzzerWrap { 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 }; private static final long[] OPEN_FAILING_SEEDS = { - -4628356252269023530L, - -4022199781524079249L, - 8824787517620928178L, - -673501011619761901L, }; @@ -42,6 +42,7 @@ public class FuzzerWrap { fuzzer.fuzzPeepsRegression(seed); } + @Ignore @Test public void fuzzPeepsRandom() { Random R = new Random(System.currentTimeMillis()); @@ -61,8 +62,8 @@ public class FuzzerWrap { assertTrue(fuzzer.noExceptions()); } - @Test @Ignore + @Test public void fuzzPeepTiming() { var fuzzer = new Fuzzer(); int max_nid=0; |
