summaryrefslogtreecommitdiff
path: root/chapter25
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-09-26 16:02:24 -0700
committerCliff Click <cliffc@acm.org>2026-09-26 16:02:24 -0700
commit4391fcc03a214e6296e19ca42487de023ab12f6a (patch)
tree7eca88e0bea31cdae4d5f485b84604a41c82f795 /chapter25
parentdd907c2d86eaaad0910e7bfe3f0d25e5caf8bdf7 (diff)
downloadSimple-4391fcc03a214e6296e19ca42487de023ab12f6a.tar.gz
Simple-4391fcc03a214e6296e19ca42487de023ab12f6a.zip
bug fix widen
Failed lattice law tests mixing constants and have wide type ints; or mixing wide ints and constant arrays.
Diffstat (limited to 'chapter25')
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java5
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/Type.java4
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java74
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java10
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java12
-rw-r--r--chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java21
-rw-r--r--chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java16
7 files changed, 77 insertions, 65 deletions
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 09c55cde..a4c73472 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -85,10 +85,11 @@ public class PhiNode extends Node {
}
}
- // phi loop widening part
+ // Above-center ranges may still fall to unwidened constants.
+ // Widen only below-center ranges to preserve monotonicity.
if( r instanceof LoopNode && // Only around loops
t instanceof TypeInteger ti && // Only widen integers
- !ti.isConstant() ) { // No need to widen constants
+ !ti.isHigh() && !ti.isConstant() ) { // Only widen live ranges
// Widen, to prevent infinite falling of TypeIntegers
return ti.same_but_slightly_wider_than();
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java
index 39203a09..d1af6ca8 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -272,8 +272,8 @@ public class Type /*implements Cloneable*/ {
if( is_simple() ) return this.xmeet(t );
if( t.is_simple() ) return t .xmeet(this);
// TypeConAry meet its element type is OK
- if( this instanceof TypeConAry tcon && tcon.elemT() == t._type ) return tcon.ymeet(t);
- if( t instanceof TypeConAry tcon && tcon.elemT() == _type ) return tcon.ymeet(this);
+ if( this instanceof TypeConAry tcon && t instanceof TypeInteger ti ) return tcon.ymeet(ti);
+ if( t instanceof TypeConAry tcon && this instanceof TypeInteger ti ) return tcon.ymeet(ti);
// Two distinct scalar families meet at the scalar envelope.
if( this instanceof TypeScalar scalar0 && t instanceof TypeScalar scalar1 )
return scalar0.smeet(scalar1);
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
index 29e65c94..c9093e3b 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAry.java
@@ -1,6 +1,5 @@
package com.seaofnodes.simple.type;
-import com.seaofnodes.simple.util.AryInt;
import com.seaofnodes.simple.util.BAOS;
import com.seaofnodes.simple.util.Utils;
import java.util.ArrayList;
@@ -11,39 +10,48 @@ import java.util.HashMap;
*/
public abstract class TypeConAry<A> extends TypeScalar {
boolean _any;
- // One of byte,short,int,long,float,double array
+ byte _widen;
+ // One of byte or int array
public final A _ary;
- TypeConAry( boolean any, A ary ) { super(TCONARY); _any = any; _ary = ary; }
+ TypeConAry( boolean any, byte widen, A ary ) { super(TCONARY); _any = any; _widen = widen; _ary = ary; }
public static void gather(ArrayList<Type> ts) {
- TypeConAryB.gather(ts);
- TypeConAryI.gather(ts);
+ ts.add(TypeConAryB.ABC);
+ ts.add(TypeConAryB.ABCD);
+ ts.add(TypeConAryI.I123);
}
+ // Fresh, uninterned type sharing this array; xdual must not intern.
+ abstract TypeConAry<A> _make(boolean any, byte widen);
- @Override public String str() { return (_any?"~":"") + "[]"; }
- @Override TypeConAry xdual() { return this; }
+ @Override TypeConAry<A> xdual() { return _make( !_any, (byte)(3-_widen) ); }
@Override Type xmeet(Type t) {
TypeConAry ary = (TypeConAry)t; // Invariant
- assert _ary!=ary._ary; // Already interned and this!=t
- // Unrelated constant arrays
+ // Same base array but different?
+ if( _equals(ary) )
+ // Return array with larger widen
+ return _widen >= ary._widen ? this : ary;
+ // Unrelated constant arrays, falls to some int range
return elem().meet(ary.elem());
}
- Type ymeet( Type t ) {
- if( t instanceof TypeInteger ti ) {
- // if i can isa *each* element, then maybe can keep.
- // no good to i.isa(elem()) because fails the dual
- boolean fail = false;
- for( int j=0; j<len(); j++ )
- if( !ti.isa(TypeInteger.constant(at8(j))) )
- { fail=true; break; }
- Type e = elem();
- if( fail )
- return ti.meet(e);
- return this;
- }
- throw Utils.TODO("Should not reach here: constant array meet with non-element type");
+ Type ymeet( TypeInteger ti ) {
+ // if i can isa *each* element, then maybe can keep.
+ // no good to i.isa(elem()) because fails the dual
+ for( int j=0; j<len(); j++ )
+ if( !ti.isa(TypeInteger.make(at8(j),at8(j),ti._widen)) )
+ return ti.meet(elem());
+ byte widen = (byte)Math.max(_widen,ti._widen);
+ return widen==_widen ? this : _make(_any,widen).intern();
+ }
+
+ private boolean _equals(TypeConAry ary) {
+ int len = len();
+ if( len != ary.len() ) return false;
+ for( int i=0; i<len; i++ )
+ if( at8(i) != ary.at8(i) )
+ return false;
+ return true;
}
@Override public boolean isHigh() { return this==TOP; }
@@ -61,13 +69,11 @@ public abstract class TypeConAry<A> extends TypeScalar {
min = Math.min(min,at8(i));
max = Math.max(max,at8(i));
}
- return TypeInteger.make(min,max);
+ return TypeInteger.make(min,max,_widen);
}
- // Generic element _type
- public int elemT() { return TBOT; }
- public long at8(int idx) { throw Utils.TODO("Should not reach here: abstract constant array has no elements"); }
- public int len() { throw Utils.TODO("Should not reach here: abstract constant array has no length"); }
- @Override public int log_size() { throw Utils.TODO("Should not reach here: abstract constant array has no element size"); }
+ public abstract long at8(int idx);
+ public abstract int len();
+ @Override public abstract int log_size();
public void write( BAOS baos ) { throw Utils.TODO("Should not reach here: abstract constant array cannot be written"); }
// Reserve tags for u8 array
@@ -81,14 +87,4 @@ public abstract class TypeConAry<A> extends TypeScalar {
static TypeConAry packed( int tag, BAOS bais ) {
return TypeConAryB.make(bais.read(new byte[bais.packed4()]));
}
-
- @Override boolean eq(Type t) {
- return t instanceof TypeConAry ary && _any==ary._any && ary._ary==null;
- }
-
- @Override int hash() {
- assert _ary==null;
- return _any ? 1024 : 0;
- }
-
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
index bd2827ed..5877c309 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryB.java
@@ -1,22 +1,20 @@
package com.seaofnodes.simple.type;
import com.seaofnodes.simple.util.BAOS;
-import java.util.ArrayList;
import java.util.Arrays;
public class TypeConAryB extends TypeConAry<byte[]> {
- private TypeConAryB(byte[] bs) { super(false,bs); }
- public static TypeConAryB make( byte[] bs ) { return new TypeConAryB(bs).intern(); }
+ private TypeConAryB(boolean any, byte widen, byte[] bs) { super(any,widen,bs); }
+ @Override TypeConAryB _make( boolean any, byte widen ) { return new TypeConAryB(any,widen,_ary); }
+ public static TypeConAryB make( byte[] bs ) { return new TypeConAryB(false,(byte)0,bs).intern(); }
public static TypeConAryB make( String s ) { return make(s.getBytes()); }
static final TypeConAryB ABC = make("abc");
static final TypeConAryB ABCD = make("abcd");
- public static void gather(ArrayList<Type> ts) { ts.add(ABC); ts.add(ABCD); }
- @Override public int elemT() { return TINT; }
@Override public long at8(int idx) { return _ary[idx]; }
@Override public int len() { return _ary.length; }
@Override public int log_size() { return 0; }
@Override public String str() { return "[\""+new String(_ary)+"\"]"; }
- @Override boolean eq(Type t) { return t instanceof TypeConAryB ary && Arrays.equals(_ary,ary._ary); }
+ @Override boolean eq(Type t) { return t instanceof TypeConAryB ary && _widen==ary._widen && Arrays.equals(_ary,ary._ary); }
@Override int hash() { return Arrays.hashCode(_ary); }
@Override public void write( BAOS baos ) { baos.write(_ary); }
}
diff --git a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java
index bb2ab83c..b1b061de 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeConAryI.java
@@ -1,18 +1,16 @@
package com.seaofnodes.simple.type;
-import java.util.ArrayList;
import java.util.Arrays;
public class TypeConAryI extends TypeConAry<int[]> {
- private TypeConAryI(int[] bs) { super(false,bs); }
- public static TypeConAryI make( int[] bs ) { return new TypeConAryI(bs).intern(); }
- private static final TypeConAryI I123 = make(new int[]{1,2,3});
- public static void gather(ArrayList<Type> ts) { ts.add(I123); }
- @Override public int elemT() { return TINT; }
+ private TypeConAryI(boolean any, byte widen, int[] bs) { super(any,widen,bs); }
+ public static TypeConAryI make( int[] bs ) { return new TypeConAryI(false,(byte)0,bs).intern(); }
+ @Override TypeConAryI _make( boolean any, byte widen ) { return new TypeConAryI(any,widen,_ary); }
+ static final TypeConAryI I123 = make(new int[]{1,2,3});
@Override public long at8(int idx) { return _ary[idx]; }
@Override public int len() { return _ary.length; }
@Override public int log_size() { return 2; }
@Override public String str() { return "[i32]"; }
- @Override boolean eq(Type t) { return t instanceof TypeConAryI ary && Arrays.equals(_ary,ary._ary); }
+ @Override boolean eq(Type t) { return t instanceof TypeConAryI ary && _widen==ary._widen && Arrays.equals(_ary,ary._ary); }
@Override int hash() { return Arrays.hashCode(_ary); }
}
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 1306a39a..a0c2266d 100644
--- a/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
+++ b/chapter25/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -25,17 +25,13 @@ public class TypeInteger extends TypeScalar {
private static final Ary<TypeInteger> FREE = new Ary<>(TypeInteger.class);
private TypeInteger(long min, long max, byte widen) { super(TINT); init(min,max,widen); }
private TypeInteger init(long min, long max, byte widen) {
- // Constants must always have a widen value of zero. Widening is only
- // meaningful for ranges of values - which otherwide can increase in
- // range size indefinitely.
- assert min!=max || widen==0; // Constants always zero widen
_min = min; _max = max; _widen=widen;
return this;
}
public static TypeInteger malloc(long lo, long hi, byte widen) { return FREE.isEmpty() ? new TypeInteger(lo,hi,widen) : FREE.pop().init(lo,hi,widen); }
public static TypeInteger make(long lo, long hi) { return make(lo,hi,(byte)0); }
public static TypeInteger make(long lo, long hi, byte widen) {
- TypeInteger i = malloc(lo,hi,lo==hi ? 0 : widen);
+ TypeInteger i = malloc(lo,hi,widen);
TypeInteger t2 = i.intern();
return t2==i ? i : t2.free(i);
}
@@ -85,7 +81,16 @@ public class TypeInteger extends TypeScalar {
public final static TypeInteger FATBOOL = make(0,1,(byte)2);
- public static void gather(ArrayList<Type> ts) { ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); ts.add(ZERO); ts.add(FATBOOL); }
+ public static void gather(ArrayList<Type> ts) {
+ ts.add(I32); ts.add(BOT); ts.add(U1); ts.add(I1); ts.add(U8); ts.add(ZERO); ts.add(FATBOOL);
+ // Ranges before and after excluding zero, at each widening level.
+ for( byte widen=0; widen<=3; widen++ ) {
+ ts.add(make(0,255,widen));
+ ts.add(make(1,255,widen));
+ ts.add(make(-255,0,widen));
+ ts.add(make(-255,-1,widen));
+ }
+ }
@Override public String str() {
if( _isConstant() ) return ""+_min;
@@ -157,9 +162,7 @@ public class TypeInteger extends TypeScalar {
return make(Math.min(_min,i._min), Math.max(_max,i._max), (byte)Math.max(_widen,i._widen));
}
- @Override TypeInteger xdual() {
- return _min==_max ? this : malloc(_max,_min,(byte)(-_widen));
- }
+ @Override TypeInteger xdual() { return malloc(_max,_min,(byte)(3-_widen)); }
@Override public TypeInteger nonZero() {
if( isHigh() ) return this;
diff --git a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
index b83cc9e6..b9cba5e8 100644
--- a/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/chapter25/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -7,6 +7,22 @@ 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() {