summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2024-08-06 13:09:38 +0100
committerCliff Click <cliffc@acm.org>2025-02-07 09:12:14 -0800
commitb29ddd596116ea5488078e823f75c9fbc3aad302 (patch)
tree93778e0efbdd8df6cdb3fe4c5d7af40cd9ed594e /src
parent5b505628993d614c3f44031c7c454bc6dc350f43 (diff)
downloadSimple-b29ddd596116ea5488078e823f75c9fbc3aad302.tar.gz
Simple-b29ddd596116ea5488078e823f75c9fbc3aad302.zip
Chapter 14: Narrow types
Co-authored-by: Thalia Archibald <thalia@archibald.dev> Co-authored-by: Dibyendu Majumdar <mobile@majumdar.org.uk> Co-authored-by: tannal <tannal2409@gmail.com> Co-authored-by: hadha <helsgithub15@gmail.com> Co-authored-by: XmiliaH <45106915+XmiliaH@users.noreply.github.com> Co-authored-by: Tamás Hadházy <85063808+Hels15@users.noreply.github.com> Co-authored-by: Robert Obkircher <robert.obkircher@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java106
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AddNode.java24
-rw-r--r--src/main/java/com/seaofnodes/simple/node/AndNode.java66
-rw-r--r--src/main/java/com/seaofnodes/simple/node/BoolNode.java90
-rw-r--r--src/main/java/com/seaofnodes/simple/node/DivNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MinusNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MulNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NotNode.java3
-rw-r--r--src/main/java/com/seaofnodes/simple/node/OrNode.java63
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java25
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RoundF32Node.java38
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SarNode.java56
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShlNode.java53
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ShrNode.java56
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubFNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java14
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XorNode.java63
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Type.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeFloat.java73
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeInteger.java110
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter07Test.java1
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter10Test.java10
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter12Test.java1
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter13Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter14Test.java317
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java6
-rw-r--r--src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java47
33 files changed, 1110 insertions, 183 deletions
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index 71bd6d02..9e4b9eb6 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -52,11 +52,19 @@ public class Parser {
* List of keywords disallowed as identifiers
*/
private static final HashSet<String> KEYWORDS = new HashSet<>(){{
+ add("bool");
add("break");
+ add("byte");
add("continue");
add("else");
+ add("f32");
+ add("f64");
add("false");
add("flt");
+ add("i16");
+ add("i32");
+ add("i64");
+ add("i8");
add("if");
add("int");
add("new");
@@ -64,6 +72,10 @@ public class Parser {
add("return");
add("struct");
add("true");
+ add("u1");
+ add("u16");
+ add("u32");
+ add("u8");
add("while");
}};
@@ -84,8 +96,20 @@ public class Parser {
Node.reset();
IterPeeps.reset();
TYPES.clear();
- TYPES.put("int",TypeInteger.BOT);
- TYPES.put("flt",TypeFloat .BOT);
+ TYPES.put("bool",TypeInteger.U1 );
+ TYPES.put("byte",TypeInteger.U8 );
+ TYPES.put("f32" ,TypeFloat .B32);
+ TYPES.put("f64" ,TypeFloat .BOT);
+ TYPES.put("flt" ,TypeFloat .BOT);
+ TYPES.put("i16" ,TypeInteger.I16);
+ TYPES.put("i32" ,TypeInteger.I32);
+ TYPES.put("i64" ,TypeInteger.BOT);
+ TYPES.put("i8" ,TypeInteger.I8 );
+ TYPES.put("int" ,TypeInteger.BOT);
+ TYPES.put("u1" ,TypeInteger.U1 );
+ TYPES.put("u16" ,TypeInteger.U16);
+ TYPES.put("u32" ,TypeInteger.U32);
+ TYPES.put("u8" ,TypeInteger.U8 );
SCHEDULED = false;
_lexer = new Lexer(source);
_scope = new ScopeNode();
@@ -191,7 +215,7 @@ public class Parser {
* Only allowed in top level scope.
* Structs cannot be redefined.
*
- * @return null
+ * @return The statement following the struct
*/
private Node parseStruct() {
if (_xScopes.size() > 1) throw errorSyntax("struct declarations can only appear in top level scope");
@@ -210,7 +234,7 @@ public class Parser {
TypeStruct ts = TypeStruct.make(typeName, fields);
TYPES.put(typeName, ts);
START.addMemProj(ts, _scope); // Insert memory edges
- return null;
+ return parseStatement();
}
/**
@@ -434,6 +458,8 @@ public class Parser {
// Auto-widen int to float
if( expr._type instanceof TypeInteger && t instanceof TypeFloat )
expr = new ToFloatNode(expr).peephole();
+ // Auto-narrow wide ints to narrow ints
+ expr = zsMask(expr,t);
// Auto-deepen forward ref types
Type e = expr._type;
if( e instanceof TypeMemPtr tmp && tmp._obj._fields==null )
@@ -475,7 +501,29 @@ public class Parser {
* </pre>
* @return an expression {@link Node}, never {@code null}
*/
- private Node parseExpression() { return parseComparison(); }
+ private Node parseExpression() { return parseBitwise(); }
+
+ /**
+ * Parse an bitwise expression
+ *
+ * <pre>
+ * bitwise : compareExpr (('&' | '|' | '^') compareExpr)*
+ * </pre>
+ * @return a bitwise expression {@link Node}, never {@code null}
+ */
+ private Node parseBitwise() {
+ Node lhs = parseComparison();
+ while( true ) {
+ if( false ) ;
+ else if( match("&") ) lhs = new AndNode(lhs,null);
+ else if( match("|") ) lhs = new OrNode(lhs,null);
+ else if( match("^") ) lhs = new XorNode(lhs,null);
+ else break;
+ lhs.setDef(2,parseComparison());
+ lhs = lhs.peephole();
+ }
+ return lhs;
+ }
/**
* Parse an expression of the form:
@@ -486,7 +534,7 @@ public class Parser {
* @return an comparator expression {@link Node}, never {@code null}
*/
private Node parseComparison() {
- var lhs = parseAddition();
+ var lhs = parseShift();
while( true ) {
int idx=0; boolean negate=false;
// Test for any local nodes made, and "keep" lhs during peepholes
@@ -499,7 +547,7 @@ public class Parser {
else if( match(">" ) ) { idx=1; lhs = new BoolNode.LT(null, lhs); }
else break;
// Peepholes can fire, but lhs is already "hooked", kept alive
- lhs.setDef(idx,parseAddition());
+ lhs.setDef(idx,parseShift());
lhs = lhs.widen().peephole();
if( negate ) // Extra negate for !=
lhs = new NotNode(lhs).peephole();
@@ -511,6 +559,28 @@ public class Parser {
* Parse an additive expression
*
* <pre>
+ * shiftExpr : additiveExpr (('<<' | '>>' | '>>>') additiveExpr)*
+ * </pre>
+ * @return a shift expression {@link Node}, never {@code null}
+ */
+ private Node parseShift() {
+ Node lhs = parseAddition();
+ while( true ) {
+ if( false ) ;
+ else if( match("<<") ) lhs = new ShlNode(lhs,null);
+ else if( match(">>>")) lhs = new ShrNode(lhs,null);
+ else if( match(">>") ) lhs = new SarNode(lhs,null);
+ else break;
+ lhs.setDef(2,parseAddition());
+ lhs = lhs.widen().peephole();
+ }
+ return lhs;
+ }
+
+ /**
+ * Parse an additive expression
+ *
+ * <pre>
* additiveExpr : multiplicativeExpr (('+' | '-') multiplicativeExpr)*
* </pre>
* @return an add expression {@link Node}, never {@code null}
@@ -640,6 +710,8 @@ public class Parser {
else {
Node val = parseExpression();
Type glb = base._fields[idx]._type;
+ // Auto-truncate when storing to narrow fields
+ val = zsMask(val,glb);
memAlias(alias, new StoreNode(name, alias,glb, ctrl(), memAlias(alias), expr, val, false).peephole());
return expr; // "obj.a = expr" returns the expression while updating memory
}
@@ -649,6 +721,26 @@ public class Parser {
return parsePostfix(new LoadNode(name, alias, declaredType.glb(), memAlias(alias), expr).peephole());
}
+ // zero/sign extend. "i" is limited to either classic unsigned (min==0) or
+ // classic signed (min=minus-power-of-2); max=power-of-2-minus-1.
+ private Node zsMask(Node val, Type t ) {
+ if( !(val._type instanceof TypeInteger tval && t instanceof TypeInteger t0 && !tval.isa(t0)) ) {
+ if( !(val._type instanceof TypeFloat tval && t instanceof TypeFloat t0 && !tval.isa(t0)) )
+ return val;
+ // Float rounding
+ return new RoundF32Node(val).peephole();
+ }
+ if( t0._min==0 ) // Unsigned
+ return new AndNode(val,new ConstantNode(TypeInteger.constant(t0._max)).peephole()).peephole();
+ // Signed extension
+ int shift = Long.numberOfLeadingZeros(t0._max)-1;
+ Node shf = new ConstantNode(TypeInteger.constant(shift)).peephole();
+ if( shf._type==TypeInteger.ZERO )
+ return val;
+ return new SarNode(new ShlNode(val,shf.keep()).peephole(),shf.unkeep()).peephole();
+ }
+
+
/**
* Parse integer literal
*
diff --git a/src/main/java/com/seaofnodes/simple/node/AddNode.java b/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 48350957..9d425f7a 100644
--- a/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -21,14 +21,24 @@ public class AddNode extends Node {
@Override
public Type compute() {
- if( in(1)._type instanceof TypeInteger i0 &&
- in(2)._type instanceof TypeInteger i1 ) {
- if (i0.isConstant() && i1.isConstant())
- return TypeInteger.constant(i0.value()+i1.value());
+ Type t1 = in(1)._type, t2 = in(2)._type;
+ if( t1.isHigh() || t2.isHigh() ) return TypeInteger.TOP;
+ if( t1 instanceof TypeInteger i1 &&
+ t2 instanceof TypeInteger i2 ) {
+ if (i1.isConstant() && i2.isConstant())
+ return TypeInteger.constant(i1.value()+i2.value());
+ // Fold ranges like {0-1} + {2-3} into {2-4}.
+ if( !overflow(i1._min,i2._min) &&
+ !overflow(i1._max,i2._max) )
+ return TypeInteger.make(i1._min+i2._min,i1._max+i2._max);
}
- return in(1)._type.meet(in(2)._type);
+ return TypeInteger.BOT;
}
+ private static boolean overflow( long x, long y ) {
+ if( (x ^ y ) < 0 ) return false; // unequal signs, never overflow
+ return (x ^ (x + y)) < 0; // sum has unequal signs, so overflow
+ }
@Override
public Node idealize () {
Node lhs = in(1);
@@ -37,7 +47,7 @@ public class AddNode extends Node {
// Add of 0. We do not check for (0+x) because this will already
// canonicalize to (x+0)
- if( t2 instanceof TypeInteger i && i.value()==0 )
+ if( t2 instanceof TypeInteger i && i.isConstant() && i.value()==0 )
return lhs;
// Add of same to a multiply by 2
@@ -100,7 +110,7 @@ public class AddNode extends Node {
return null;
}
- // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or.
+ // Rotation is only valid for associative ops, e.g. Add, Mul, And, Or, Xor.
// Do we have ((phi cons)|(x + (phi cons)) + con|(phi cons)) ?
// Push constant up through the phi: x + (phi con0+con0 con1+con1...)
static Node phiCon(Node op, boolean rotate) {
diff --git a/src/main/java/com/seaofnodes/simple/node/AndNode.java b/src/main/java/com/seaofnodes/simple/node/AndNode.java
new file mode 100644
index 00000000..350859db
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/AndNode.java
@@ -0,0 +1,66 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.util.BitSet;
+
+public class AndNode extends Node {
+ public AndNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "And"; }
+
+ @Override public String glabel() { return "&"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append("&"), visited);
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return TypeInteger.TOP;
+ if( in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1 ) {
+ if( i0.isConstant() && i1.isConstant() )
+ return TypeInteger.constant(i0.value()&i1.value());
+ // Sharpen allowed bits if either value is narrowed
+ long mask = i0.mask() & i1.mask();
+ return mask < 0 ? TypeInteger.BOT : TypeInteger.make(0,mask);
+ }
+ return TypeInteger.BOT;
+ }
+
+ @Override
+ public Node idealize() {
+ Node lhs = in(1);
+ Node rhs = in(2);
+ Type t1 = lhs._type;
+ Type t2 = rhs._type;
+
+ // And of -1. We do not check for (-1&x) because this will already
+ // canonicalize to (x&-1)
+ if( t2.isConstant() && t2 instanceof TypeInteger i && i.value()==-1 )
+ return lhs;
+
+ // Move constants to RHS: con*arg becomes arg*con
+ if ( t1.isConstant() && !t2.isConstant() )
+ return swap12();
+
+ // Do we have ((x & (phi cons)) & con) ?
+ // Do we have ((x & (phi cons)) & (phi cons)) ?
+ // Push constant up through the phi: x & (phi con0&con0 con1&con1...)
+ Node phicon = AddNode.phiCon(this,true);
+ if( phicon!=null ) return phicon;
+
+ return null;
+ }
+ @Override Node copy(Node lhs, Node rhs) { return new AndNode(lhs,rhs); }
+ @Override String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '&' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '&' " + in(2)._type;
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index 48faefd2..f0cca1e2 100644
--- a/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -8,6 +8,7 @@ import com.seaofnodes.simple.type.TypeFloat;
import com.seaofnodes.simple.type.TypeMemPtr;
import java.util.BitSet;
+import static com.seaofnodes.simple.type.TypeInteger.*;
abstract public class BoolNode extends Node {
@@ -31,30 +32,30 @@ abstract public class BoolNode extends Node {
}
@Override
- public Type compute() {
- if( in(1)._type instanceof TypeInteger i0 &&
- in(2)._type instanceof TypeInteger i1 ) {
- if (i0.isConstant() && i1.isConstant())
- return TypeInteger.constant(doOp(i0.value(), i1.value()) ? 1 : 0);
- }
- if( in(1)._type instanceof TypeFloat i0 &&
- in(2)._type instanceof TypeFloat i1 ) {
- if (i0.isConstant() && i1.isConstant())
- return TypeInteger.constant(doOp(i0.value(), i1.value()) ? 1 : 0);
- }
- return TypeInteger.BOT;
+ public TypeInteger compute() {
+ Type t1 = in(1)._type;
+ Type t2 = in(2)._type;
+ if( t1.isHigh() || t2.isHigh() )
+ return BOOL.dual();
+ // Compare of same
+ if( in(1)==in(2) )
+ return doOp(ZERO,ZERO);
+ if( t1 instanceof TypeInteger i1 &&
+ t2 instanceof TypeInteger i2 )
+ return doOp(i1,i2);
+ if( t1 instanceof TypeFloat f1 &&
+ t2 instanceof TypeFloat f2 &&
+ f1.isConstant() && f2.isConstant() )
+ return TypeInteger.constant(doOp(f1.value(), f2.value()) ? 1 : 0);
+ return BOOL;
}
- boolean doOp(long lhs, long rhs) { throw Utils.TODO(); }
+ TypeInteger doOp(TypeInteger t1, TypeInteger t2) { throw Utils.TODO(); }
boolean doOp(double lhs, double rhs) { throw Utils.TODO(); }
- Node copyF(Node lhs, Node rhs) { throw Utils.TODO(); }
+ Node copyF(Node lhs, Node rhs) { return null; }
@Override
public Node idealize() {
- // Compare of same
- if( in(1)==in(2) )
- return new ConstantNode(TypeInteger.constant(doOp(3,3)?1:0));
-
// Equals pushes constant to the right; 5==X becomes X==5.
if( this instanceof EQ ) {
if( !(in(2) instanceof ConstantNode) ) {
@@ -64,25 +65,52 @@ abstract public class BoolNode extends Node {
return in(1)._type instanceof TypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1));
}
// Equals X==0 becomes a !X
- if( (in(2)._type == TypeInteger.ZERO || in(2)._type == TypeMemPtr.NULLPTR) )
+ if( (in(2)._type == ZERO || in(2)._type == TypeMemPtr.NULLPTR) )
return new NotNode(in(1));
+ // Equals bool == 1 becomes bool
+ if( in(2)._type == TRUE && in(1)._type == BOOL )
+ return in(1);
}
- // Do we have ((x * (phi cons)) * con) ?
- // Do we have ((x * (phi cons)) * (phi cons)) ?
- // Push constant up through the phi: x * (phi con0*con0 con1*con1...)
- Node phicon = AddNode.phiCon(this,this instanceof EQ);
- if( phicon!=null ) return phicon;
-
return null;
}
- public static class EQ extends BoolNode { public EQ(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "=="; } boolean doOp(long lhs, long rhs) { return lhs == rhs; } Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); } Node copyF() { return new EQF(null,null); } }
- public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } boolean doOp(long lhs, long rhs) { return lhs < rhs; } Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); } Node copyF() { return new LTF(null,null); }}
- public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } boolean doOp(long lhs, long rhs) { return lhs <= rhs; } Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); } Node copyF() { return new LEF(null,null); } }
+ public static class EQ extends BoolNode {
+ public EQ(Node lhs, Node rhs) { super(lhs,rhs); }
+ String op() { return "=="; }
+ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
+ if( i1==i2 && i1.isConstant() ) return TRUE;
+ if( i1._max < i2._min || i1._min > i2._max ) return FALSE;
+ return BOOL;
+ }
+ Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); }
+ Node copyF() { return new EQF(null,null); }
+ }
+ public static class LT extends BoolNode {
+ public LT(Node lhs, Node rhs) { super(lhs,rhs); }
+ String op() { return "<" ; }
+ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
+ if( i1._max < i2._min ) return TRUE;
+ if( i1._min >= i2._max ) return FALSE;
+ return BOOL;
+ }
+ Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); }
+ Node copyF() { return new LTF(null,null); }
+ }
+ public static class LE extends BoolNode {
+ public LE(Node lhs, Node rhs) { super(lhs,rhs); }
+ String op() { return "<="; }
+ TypeInteger doOp(TypeInteger i1, TypeInteger i2) {
+ if( i1._max <= i2._min ) return TRUE;
+ if( i1._min > i2._max ) return FALSE;
+ return BOOL;
+ }
+ Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); }
+ Node copyF() { return new LEF(null,null); }
+ }
- public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } Node copyF() { return null; } }
- public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } Node copyF() { return null; } }
- public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } Node copyF() { return null; } }
+ public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } Node copy(Node lhs, Node rhs) { return new EQF(lhs,rhs); } }
+ public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } Node copy(Node lhs, Node rhs) { return new LTF(lhs,rhs); } }
+ public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } Node copy(Node lhs, Node rhs) { return new LEF(lhs,rhs); } }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/DivNode.java b/src/main/java/com/seaofnodes/simple/node/DivNode.java
index e0c95019..da9571db 100644
--- a/src/main/java/com/seaofnodes/simple/node/DivNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/DivNode.java
@@ -21,14 +21,17 @@ public class DivNode extends Node {
@Override
public Type compute() {
- if (in(1)._type instanceof TypeInteger i0 &&
- in(2)._type instanceof TypeInteger i1) {
- if (i0.isConstant() && i1.isConstant())
- return i1.value() == 0
+ Type t1 = in(1)._type, t2 = in(2)._type;
+ if( t1.isHigh() || t2.isHigh() )
+ return TypeInteger.TOP;
+ if( t1 instanceof TypeInteger i1 &&
+ t2 instanceof TypeInteger i2 ) {
+ if (i1.isConstant() && i2.isConstant())
+ return i2.value() == 0
? TypeInteger.ZERO
- : TypeInteger.constant(i0.value()/i1.value());
+ : TypeInteger.constant(i1.value()/i2.value());
}
- return in(1)._type.meet(in(2)._type);
+ return TypeInteger.BOT;
}
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index a033a7cf..78ccc8a9 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -73,4 +73,8 @@ public class LoadNode extends MemOpNode {
Node px = phi.in(idx);
return px!=null && px.addDep(this) instanceof StoreNode st1 && ptr()==st1.ptr();
}
+
+ Node copy(Node mem, Node ptr) {
+ return new LoadNode(_name,_alias,_declaredType,mem,ptr);
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 95319c24..76564db0 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -14,7 +14,7 @@ public abstract class MemOpNode extends Node {
public final Type _declaredType;
- public MemOpNode(String name, int alias, Type glb, Node... nodes ) {
+ public MemOpNode(String name, int alias, Type glb, Node... nodes) {
super(nodes);
_name = name;
_alias = alias;
diff --git a/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
index ae8e9e1b..8ba45d26 100644
--- a/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -20,8 +20,12 @@ public class MinusNode extends Node {
@Override
public Type compute() {
- if (in(1)._type instanceof TypeInteger i0)
- return i0.isConstant() ? TypeInteger.constant(-i0.value()) : i0;
+ if( in(1)._type instanceof TypeInteger i0 ) {
+ if( i0.isHigh() ) return TypeInteger.TOP;
+ // Catch overflow wrapping on `-Long.MIN_VALUE`
+ if( i0 == TypeInteger.BOT || i0._min == Long.MIN_VALUE ) return TypeInteger.BOT;
+ return TypeInteger.make(-i0._max,-i0._min);
+ }
return TypeInteger.TOP.meet(in(1)._type);
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MulNode.java b/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 8c8a6e0a..f231a97b 100644
--- a/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -21,12 +21,15 @@ public class MulNode extends Node {
@Override
public Type compute() {
- if (in(1)._type instanceof TypeInteger i0 &&
- in(2)._type instanceof TypeInteger i1) {
- if (i0.isConstant() && i1.isConstant())
- return TypeInteger.constant(i0.value()*i1.value());
+ Type t1 = in(1)._type, t2 = in(2)._type;
+ if( t1.isHigh() || t2.isHigh() )
+ return TypeInteger.TOP;
+ if( t1 instanceof TypeInteger i1 &&
+ t2 instanceof TypeInteger i2 ) {
+ if (i1.isConstant() && i2.isConstant())
+ return TypeInteger.constant(i1.value()*i2.value());
}
- return in(1)._type.meet(in(2)._type);
+ return TypeInteger.BOT;
}
@Override
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index d846fcef..96b569b5 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -93,10 +93,8 @@ public abstract class Node implements OutNode {
@Override
public final String toString() { return print(); }
- // This is a *deep* print. This version will fail on cycles, which we will
- // correct later when we can parse programs with loops. We print with a
- // tik-tok style; the common _print0 calls the per-Node _print1, which
- // calls back to _print0;
+ // This is a *deep* print. We print with a tik-tok style; the common
+ // _print0 calls the per-Node _print1, which calls back to _print0;
public final String print() {
return _print0(new StringBuilder(), new BitSet()).toString();
}
@@ -633,7 +631,12 @@ public abstract class Node implements OutNode {
E x = pred.apply(this);
if( x != null ) return x;
for( Node def : _inputs ) if( def != null && (x = def._walk(pred)) != null ) return x;
- for( Node use : _outputs ) if( use != null && (x = use._walk(pred)) != null ) return x;
+ // Unroll iterator to survive junk CME
+ for( int i=0; i<_outputs.size(); i++ ) {
+ Node use = _outputs.get(i);
+ if( use != null && (x = use._walk(pred)) != null )
+ return x;
+ }
return null;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/NotNode.java b/src/main/java/com/seaofnodes/simple/node/NotNode.java
index 3cf69520..b3c77c45 100644
--- a/src/main/java/com/seaofnodes/simple/node/NotNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NotNode.java
@@ -20,8 +20,9 @@ public class NotNode extends Node {
@Override
public Type compute() {
Type t0 = in(1)._type;
+ if( t0.isHigh() ) return TypeInteger.BOOL.dual();
switch( t0 ) {
- case TypeInteger i0: return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : i0;
+ case TypeInteger i0: return i0._max < 0 || i0._min > 0 ? TypeInteger.FALSE : (i0==TypeInteger.ZERO ? TypeInteger.TRUE : TypeInteger.BOT);
case TypeFloat i0: return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : i0;
case TypeMemPtr p0:
// top->top, bot->bot, null->1, *void->0, not-null ptr->0, ptr/nil->bot
diff --git a/src/main/java/com/seaofnodes/simple/node/OrNode.java b/src/main/java/com/seaofnodes/simple/node/OrNode.java
new file mode 100644
index 00000000..e18996b7
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/OrNode.java
@@ -0,0 +1,63 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.util.BitSet;
+
+public class OrNode extends Node {
+ public OrNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Or"; }
+
+ @Override public String glabel() { return "|"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append("|"), visited);
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return TypeInteger.TOP;
+ if( in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1 ) {
+ if( i0.isConstant() && i1.isConstant() )
+ return TypeInteger.constant(i0.value()|i1.value());
+ }
+ return TypeInteger.BOT;
+ }
+
+ @Override
+ 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 )
+ return lhs;
+
+ // Move constants to RHS: con*arg becomes arg*con
+ if ( t1.isConstant() && !t2.isConstant() )
+ return swap12();
+
+ // Do we have ((x | (phi cons)) | con) ?
+ // Do we have ((x | (phi cons)) | (phi cons)) ?
+ // Push constant up through the phi: x | (phi con0|con0 con1|con1...)
+ Node phicon = AddNode.phiCon(this,true);
+ if( phicon!=null ) return phicon;
+
+ return null;
+ }
+ @Override Node copy(Node lhs, Node rhs) { return new OrNode(lhs,rhs); }
+ @Override String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '|' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '|' " + in(2)._type;
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index ebed8dd1..05584423 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -11,7 +11,7 @@ public class PhiNode extends Node {
final String _label;
// The Phi type we compute must stay within the domain of the Phi.
- // Example Int stays Int, Ptr stays Ptr, Control stays Control, Mem stays Mem.
+ // Example Int stays Int, Ptr stays Ptr, Mem stays Mem.
final Type _declaredType;
public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; }
@@ -81,9 +81,16 @@ public class PhiNode extends Node {
lhss[i] = in(i).in(1);
rhss[i] = in(i).in(2);
}
- Node phi_lhs = new PhiNode(_label, _declaredType,lhss).peephole();
- Node phi_rhs = new PhiNode(_label, _declaredType,rhss).peephole();
- return op.copy(phi_lhs,phi_rhs);
+ Node phi_lhs = new PhiNode(_label, in(1).in(1)._type.glb(),lhss).peephole();
+ Node phi_rhs = new PhiNode(_label, in(1).in(2)._type.glb(),rhss).peephole();
+ Node phi_op = op.copy(phi_lhs,phi_rhs);
+ phi_op.setType(phi_op.compute());
+ // If losing precision, don't do it
+ if( !phi_op._type.isa(_type) ) {
+ phi_op.kill();
+ return null;
+ }
+ return phi_op;
}
// If merging Phi(N, cast(N)) - we are losing the cast JOIN effects, so just remove.
@@ -114,9 +121,13 @@ public class PhiNode extends Node {
}
private boolean same_op() {
- for( int i=2; i<nIns(); i++ )
- if( in(1).getClass() != in(i).getClass() )
- return false;
+ for( int i=2; i<nIns(); i++ ) {
+ if( in(1).getClass() != in(i).getClass() ) return false;
+ if( in(1).in(1)._type.glb() != in(i).in(1)._type.glb() ) return false;
+ if( in(1).in(2)._type.glb() != in(i).in(2)._type.glb() ) return false;
+ }
+ // Load merging needs an anti-dep check.
+ if( in(1) instanceof MemOpNode ) return false;
return true;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
new file mode 100644
index 00000000..44d3a587
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/RoundF32Node.java
@@ -0,0 +1,38 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeFloat;
+
+import java.util.BitSet;
+
+public class RoundF32Node extends Node {
+ public RoundF32Node(Node lhs) { super(null, lhs); }
+
+ @Override public String label() { return "RoundF32"; }
+
+ @Override public String glabel() { return "(f32)"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ return in(1)._print0(sb.append("((f32)"), visited).append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if (in(1)._type instanceof TypeFloat i0 && i0.isConstant() )
+ return TypeFloat.constant((float)i0.value());
+ return in(1)._type;
+ }
+
+ @Override
+ public Node idealize() {
+ Node lhs = in(1);
+ Type t1 = lhs._type;
+
+ // RoundF32 of float
+ if( t1 instanceof TypeFloat tf && tf._sz==32 )
+ return lhs;
+
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/SarNode.java b/src/main/java/com/seaofnodes/simple/node/SarNode.java
new file mode 100644
index 00000000..881a3812
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/SarNode.java
@@ -0,0 +1,56 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.util.BitSet;
+
+public class SarNode extends Node {
+ public SarNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Sar"; }
+
+ @Override public String glabel() { return ">>"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append(">>"), visited);
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return TypeInteger.TOP;
+ if (in(1)._type instanceof TypeInteger i1 &&
+ in(2)._type instanceof TypeInteger i2) {
+ if( i1.isConstant() && i2.isConstant() )
+ return TypeInteger.constant(i1.value()>>i2.value());
+ if( i2._min < 0 || i2._max >= 64 )
+ return TypeInteger.BOT;
+ return TypeInteger.make(i1._min>>i2._min,i1._max>>i2._min);
+ }
+ return TypeInteger.BOT;
+ }
+
+ @Override
+ 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 )
+ return lhs;
+
+ // TODO: x >> 3 >> (y ? 1 : 2) ==> x >> (y ? 4 : 5)
+
+ return null;
+ }
+ @Override Node copy(Node lhs, Node rhs) { return new SarNode(lhs,rhs); }
+ @Override String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '>>' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '>>' " + in(2)._type;
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
new file mode 100644
index 00000000..605c5d86
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/ShlNode.java
@@ -0,0 +1,53 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.util.BitSet;
+
+public class ShlNode extends Node {
+ public ShlNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Shl"; }
+
+ @Override public String glabel() { return "<<"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append("<<"), visited);
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return TypeInteger.TOP;
+ if (in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1) {
+ if( i0.isConstant() && i1.isConstant() )
+ return TypeInteger.constant(i0.value()<<i1.value());
+ }
+ return TypeInteger.BOT;
+ }
+
+ @Override
+ public Node idealize() {
+ Node lhs = in(1);
+ Node rhs = in(2);
+ Type t2 = rhs._type;
+
+ // Shl of 0.
+ if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 )
+ return lhs;
+
+ // TODO: x << 3 << (y ? 1 : 2) ==> x << (y ? 4 : 5)
+
+ return null;
+ }
+ @Override Node copy(Node lhs, Node rhs) { return new ShlNode(lhs,rhs); }
+ @Override String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '<<' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '<<' " + in(2)._type;
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/src/main/java/com/seaofnodes/simple/node/ShrNode.java
new file mode 100644
index 00000000..bed18d2b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/ShrNode.java
@@ -0,0 +1,56 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.util.BitSet;
+
+public class ShrNode extends Node {
+ public ShrNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Shr"; }
+
+ @Override public String glabel() { return ">>>"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append(">>>"), visited);
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return TypeInteger.TOP;
+ if (in(1)._type instanceof TypeInteger i1 &&
+ in(2)._type instanceof TypeInteger i2) {
+ if( i1.isConstant() && i2.isConstant() )
+ return TypeInteger.constant(i1.value()>>>i2.value());
+ if( i2._min < 0 || i2._max >= 64 )
+ return TypeInteger.BOT;
+ // Zero shifting a negative makes a larger positive
+ // so get the endpoints correct.
+ long s1 = i1._min>>>i2._min;
+ long s2 = i1._max>>>i2._min;
+ boolean wrap = i1._min < 0 && i1._max >=0;
+ return TypeInteger.make(wrap ? 0 : Math.min(s1,s2),Math.max(s1,s2));
+ }
+ return TypeInteger.BOT;
+ }
+
+ @Override
+ public Node idealize() {
+ Node lhs = in(1);
+ Node rhs = in(2);
+ Type t2 = rhs._type;
+
+ // Shr of 0.
+ if( t2.isConstant() && t2 instanceof TypeInteger i && (i.value()&63)==0 )
+ return lhs;
+
+ // TODO: x >>> 3 >>> (y ? 1 : 2) ==> x >>> (y ? 4 : 5)
+
+ return null;
+ }
+ @Override Node copy(Node lhs, Node rhs) { return new ShrNode(lhs,rhs); }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/src/main/java/com/seaofnodes/simple/node/SubFNode.java
index 682930e0..b591718d 100644
--- a/src/main/java/com/seaofnodes/simple/node/SubFNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SubFNode.java
@@ -34,7 +34,7 @@ public class SubFNode extends Node {
Node lhs = in(1);
Type t2 = in(2)._type;
- // Sub of 1.
+ // Sub of 0.
if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
return lhs;
diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java
index 81cc6076..6d2f1711 100644
--- a/src/main/java/com/seaofnodes/simple/node/SubNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java
@@ -26,18 +26,20 @@ public class SubNode extends Node {
if( in(1)==in(2) )
return TypeInteger.ZERO;
- if (in(1)._type instanceof TypeInteger i0 &&
- in(2)._type instanceof TypeInteger i1) {
- if (i0.isConstant() && i1.isConstant())
- return TypeInteger.constant(i0.value()-i1.value());
+ Type t1 = in(1)._type, t2 = in(2)._type;
+ if( t1.isHigh() || t2.isHigh() )
+ return TypeInteger.TOP;
+ if( t1 instanceof TypeInteger i1 &&
+ t2 instanceof TypeInteger i2 ) {
+ if (i1.isConstant() && i2.isConstant())
+ return TypeInteger.constant(i1.value()-i2.value());
}
- return in(1)._type.meet(in(2)._type);
+ return TypeInteger.BOT;
}
@Override
public Node idealize() {
-
// x - (-y) is x+y
if( in(2) instanceof MinusNode minus )
return new AddNode(in(1),minus.in(1));
diff --git a/src/main/java/com/seaofnodes/simple/node/XorNode.java b/src/main/java/com/seaofnodes/simple/node/XorNode.java
new file mode 100644
index 00000000..3978f048
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/XorNode.java
@@ -0,0 +1,63 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeInteger;
+
+import java.util.BitSet;
+
+public class XorNode extends Node {
+ public XorNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
+
+ @Override public String label() { return "Xor"; }
+
+ @Override public String glabel() { return "^"; }
+
+ @Override
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ in(1)._print0(sb.append("("), visited);
+ in(2)._print0(sb.append("^"), visited);
+ return sb.append(")");
+ }
+
+ @Override
+ public Type compute() {
+ if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return TypeInteger.TOP;
+ if( in(1)._type instanceof TypeInteger i0 &&
+ in(2)._type instanceof TypeInteger i1 ) {
+ if( i0.isConstant() && i1.isConstant() )
+ return TypeInteger.constant(i0.value()^i1.value());
+ }
+ return TypeInteger.BOT;
+ }
+
+ @Override
+ 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 )
+ return lhs;
+
+ // Move constants to RHS: con*arg becomes arg*con
+ if ( t1.isConstant() && !t2.isConstant() )
+ return swap12();
+
+ // Do we have ((x ^ (phi cons)) ^ con) ?
+ // Do we have ((x ^ (phi cons)) ^ (phi cons)) ?
+ // Push constant up through the phi: x ^ (phi con0^con0 con1^con1...)
+ Node phicon = AddNode.phiCon(this,true);
+ if( phicon!=null ) return phicon;
+
+ return null;
+ }
+ @Override Node copy(Node lhs, Node rhs) { return new XorNode(lhs,rhs); }
+ @Override String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '^' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '^' " + in(2)._type;
+ return null;
+ }
+}
diff --git a/src/main/java/com/seaofnodes/simple/type/Type.java b/src/main/java/com/seaofnodes/simple/type/Type.java
index 1f211430..49dc090a 100644
--- a/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -56,6 +56,7 @@ public class Type {
ts.add(CONTROL);
Field.gather(ts);
TypeInteger.gather(ts);
+ TypeFloat.gather(ts);
TypeMem.gather(ts);
TypeMemPtr.gather(ts);
TypeStruct.gather(ts);
@@ -67,6 +68,7 @@ public class Type {
}
// Is high or on the lattice centerline.
+ public boolean isHigh () { return _type==TTOP || _type==TXCTRL; }
public boolean isHighOrConst() { return _type==TTOP || _type==TXCTRL; }
// Strict constant values, things on the lattice centerline.
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
index 738aebc9..975c1a9e 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
@@ -7,44 +7,50 @@ import java.util.ArrayList;
*/
public class TypeFloat extends Type {
- public final static TypeFloat TOP = make(false, 0);
- public final static TypeFloat BOT = make(false, 1);
- public final static TypeFloat ZERO= make(true , 0);
+ public final static TypeFloat TOP = make((byte)-64, 0);
+ public final static TypeFloat T32 = make((byte)-32, 0);
+ public final static TypeFloat ZERO= make((byte) 0, 0);
+ public final static TypeFloat B32 = make((byte) 32, 0);
+ public final static TypeFloat BOT = make((byte) 64, 0);
- public final boolean _is_con;
+ /** +/-64 for double; +/-32 for float, or 0 for constants */
+ public final byte _sz;
/**
- * The constant value or
- * if not constant then 1=bottom, 0=top.
+ * The constant value or 0
*/
public final double _con;
- private TypeFloat(boolean is_con, double con) {
+ private TypeFloat(byte sz, double con) {
super(TFLT);
- _is_con = is_con;
+ _sz = sz ;
_con = con;
}
- public static TypeFloat make(boolean is_con, double con) {
- return new TypeFloat(is_con,con).intern();
+ private static TypeFloat make(byte sz, double con) {
+ return new TypeFloat(sz,con).intern();
}
- public static TypeFloat constant(double con) { return make(true, con); }
+ public static TypeFloat constant(double con) { return make((byte)0, con); }
- public static void gather(ArrayList<Type> ts) { ts.add(ZERO); ts.add(BOT); }
+ public static void gather(ArrayList<Type> ts) { ts.add(ZERO); ts.add(BOT); ts.add(B32); ts.add(constant(3.141592653589793)); }
// FIXME this display format is problematic
// In visualizer '#' gets prepended if its a constant
@Override
public StringBuilder print(StringBuilder sb) {
if( this==TOP ) return sb.append("FltTop");
+ if( this==T32 ) return sb.append("F32Top");
+ if( this==B32 ) return sb.append("F32Bot");
if( this==BOT ) return sb.append("FltBot");
return sb.append(_con);
}
@Override public String str() {
if( this==TOP ) return "~flt";
+ if( this==T32 ) return "~f32";
+ if( this==B32 ) return "f32";
if( this==BOT ) return "flt";
- return ""+_con;
+ return ""+_con+(isF32() ? "f" : "");
}
/**
@@ -53,15 +59,16 @@ public class TypeFloat extends Type {
@Override
public StringBuilder typeName(StringBuilder sb) {
if( this==TOP ) return sb.append("FltTop");
+ if( this==T32 ) return sb.append("F32Top");
+ if( this==B32 ) return sb.append("F32Bot");
if( this==BOT ) return sb.append("FltBot");
- return sb.append("Int");
+ return sb.append(isF32() ? "F32" : "Flt");
}
- @Override
- public boolean isHighOrConst() { return _is_con || _con==0; }
-
- @Override
- public boolean isConstant() { return _is_con; }
+ boolean isF32() { return ((float)_con)==_con; }
+ @Override public boolean isHigh() { return _sz< 0; }
+ @Override public boolean isHighOrConst() { return _sz<=0; }
+ @Override public boolean isConstant() { return _sz==0; }
public double value() { return _con; }
@@ -69,21 +76,27 @@ public class TypeFloat extends Type {
public Type xmeet(Type other) {
// Invariant from caller: 'this' != 'other' and same class (TypeFloat)
TypeFloat i = (TypeFloat)other; // Contract
- // BOT wins
- if ( this==BOT ) return this;
- if ( i ==BOT ) return i ;
- // TOP loses
- if ( i ==TOP ) return this;
- if ( this==TOP ) return i ;
+ // Larger size in i1, smaller in i0
+ TypeFloat i0 = _sz < i._sz ? this : i;
+ TypeFloat i1 = _sz < i._sz ? i : this;
+
+ if( i1._sz== 64 ) return BOT;
+ if( i0._sz==-64 ) return i1;
+ if( i1._sz== 32 )
+ return i0._sz==0 && !i0.isF32() ? BOT : B32;
+ if( i1._sz!= 0 ) return i1;
+ // i1 is a constant
+ if( i0._sz==-32 )
+ return i1.isF32() ? i1 : BOT;
// Since both are constants, and are never equals (contract) unequals
// constants fall to bottom
- return BOT;
+ return i0.isF32() && i1.isF32() ? B32 : BOT;
}
@Override
public Type dual() {
- if( isConstant() ) return this; // Constants are a self-dual
- return _con==0 ? BOT : TOP;
+ if( _sz==0 ) return this; // Constants are a self-dual
+ return make((byte)-_sz,0);
}
@Override
@@ -92,10 +105,10 @@ public class TypeFloat extends Type {
@Override public TypeFloat makeInit() { return ZERO; }
@Override
- int hash() { return (int)(Double.hashCode(_con) ^ (_is_con ? 0 : 0x4000)); }
+ int hash() { return (int)(Double.hashCode(_con) ^ _sz); }
@Override
public boolean eq( Type t ) {
TypeFloat i = (TypeFloat)t; // Contract
- return _con==i._con && _is_con==i._is_con;
+ return _con==i._con && _sz==i._sz;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
index 8c77672b..aca3e06b 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -9,42 +9,56 @@ public class TypeInteger extends Type {
public final static TypeInteger TOP = make(false, 0);
public final static TypeInteger BOT = make(false, 1);
- public final static TypeInteger ZERO= make(true , 0);
-
- public final boolean _is_con;
+ public final static TypeInteger ZERO= make(0,0);
+ public final static TypeInteger U1 = make(0,1);
+ public final static TypeInteger BOOL= U1;
+ public final static TypeInteger FALSE=ZERO;
+ public final static TypeInteger TRUE= make(1,1);
+ public final static TypeInteger I8 = make(-128,127);
+ public final static TypeInteger I16 = make(-32768,32767);
+ public final static TypeInteger I32 = make(-1L<<31,(1L<<31)-1);
+
+ public final static TypeInteger U8 = make(0,255);
+ public final static TypeInteger U16 = make(0,65535);
+ public final static TypeInteger U32 = make(0,(1L<<32)-1);
/**
- * The constant value or
- * if not constant then 1=bottom, 0=top.
+ * Describes an integer *range* - everything from min to max; both min and
+ * max are inclusive. If min==max, this is a constant.
+ *
+ * If min <= max, this is a below center (towards bottom).
+ * If min > max, this is an above center (towards top).
*/
- public final long _con;
+ public final long _min, _max;
- private TypeInteger(boolean is_con, long con) {
- super(TINT);
- _is_con = is_con;
- _con = con;
- }
+ private TypeInteger(long min, long max) { super(TINT); _min = min; _max = max; }
+ public static TypeInteger make(long lo, long hi) { return new TypeInteger(lo,hi).intern(); }
public static TypeInteger make(boolean is_con, long con) {
- return new TypeInteger(is_con,con).intern();
+ return make(is_con ? con : (con==0 ? Long.MAX_VALUE : Long.MIN_VALUE),
+ is_con ? con : (con==0 ? Long.MIN_VALUE : Long.MAX_VALUE));
}
public static TypeInteger constant(long con) { return make(true, con); }
- public static void gather(ArrayList<Type> ts) { ts.add(ZERO); ts.add(BOT); }
+ public static void gather(ArrayList<Type> ts) { ts.add(ZERO); ts.add(BOT); ts.add(U1); ts.add(U8); }
// FIXME this display format is problematic
// In visualizer '#' gets prepended if its a constant
@Override
- public StringBuilder print(StringBuilder sb) {
- if( this==TOP ) return sb.append("IntTop");
- if( this==BOT ) return sb.append("IntBot");
- return sb.append(_con);
- }
+ public StringBuilder print(StringBuilder sb) { return sb.append(str()); }
@Override public String str() {
if( this==TOP ) return "~int";
if( this==BOT ) return "int";
- return ""+_con;
+ if( this==BOOL) return ("bool");
+ if( this==I8 ) return ("i8");
+ if( this==I16 ) return ("i16");
+ if( this==I32 ) return ("i32");
+ if( this==U8 ) return ("u8");
+ if( this==U16 ) return ("u16");
+ if( this==U32 ) return ("u32");
+ if( isConstant() ) return ""+_min;
+ return "["+_min+"-"+_max+"]";
}
/**
@@ -57,45 +71,45 @@ public class TypeInteger extends Type {
return sb.append("Int");
}
- @Override
- public boolean isHighOrConst() { return _is_con || _con==0; }
-
- @Override
- public boolean isConstant() { return _is_con; }
-
- public long value() { return _con; }
+ @Override public boolean isHigh () { return _min > _max; }
+ @Override public boolean isHighOrConst() { return _min >= _max; }
+ @Override public boolean isConstant () { return _min == _max; }
+
+ 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
+ // 0x13 - no value in the range {16,17,18} will allow bit 0x04 to be set.
+ public long mask() {
+ if( isHigh() ) return 0;
+ if( isConstant() ) return _min;
+ //if( _min<0 ) return -1L;
+ //if( _max==Long.MAX_VALUE ) return -1L;
+ // Those bit positions which differ min to max
+ long x = _min ^ _max;
+ // Highest '1' bit in the differ set. Since the range is from min to
+ // max, all values below here are possible.
+ long ff1 = Long.highestOneBit(x);
+ // Make a all-1's mask from ff1, and set over the same bits (either min
+ // or max is ok).
+ long mask = _min | (ff1-1) | ff1;
+ return mask;
+ }
@Override
public Type xmeet(Type other) {
// Invariant from caller: 'this' != 'other' and same class (TypeInteger)
TypeInteger i = (TypeInteger)other; // Contract
- // BOT wins
- if ( this==BOT ) return this;
- if ( i ==BOT ) return i ;
- // TOP loses
- if ( i ==TOP ) return this;
- if ( this==TOP ) return i ;
- // Since both are constants, and are never equals (contract) unequals
- // constants fall to bottom
- return BOT;
- }
-
- @Override
- public Type dual() {
- if( isConstant() ) return this; // Constants are a self-dual
- return _con==0 ? BOT : TOP;
+ return make(Math.min(_min,i._min), Math.max(_max,i._max));
}
- @Override
- public Type glb() { return BOT; }
-
+ @Override public TypeInteger dual() { return make(_max,_min); }
+ @Override public Type glb() { return BOT; }
@Override public TypeInteger makeInit() { return ZERO; }
-
- @Override
- int hash() { return (int)(_con ^ (_is_con ? 0 : 0x4000)); }
+ @Override int hash() { return (int)((_min ^ (_min>>32)) *(_max ^ (_max>>32))); }
@Override
public boolean eq( Type t ) {
TypeInteger i = (TypeInteger)t; // Contract
- return _con==i._con && _is_con==i._is_con;
+ return _min==i._min && _max==i._max;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index e671c584..06fe2963 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -55,6 +55,8 @@ public class TypeMemPtr extends Type {
public Type glb() { return make(_obj.glb(),true); }
@Override public TypeMemPtr makeInit() { return NULLPTR; }
+ @Override public boolean isHigh() { return this==TOP; }
+
@Override
int hash() { return _obj.hashCode() ^ (_nil ? 1024 : 0); }
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 633b25e0..99ce8fa5 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -27,10 +27,10 @@ return 1;
int a = 1;
if (arg)
a = 2;
-return (arg < a) < 3;
+return (arg < a) < 3; // Because (arg < a) is a bool/uint1/[0-1], its always less than 3
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return ((arg<Phi(Region16,2,1))<3);", stop.toString());
+ assertEquals("return 1;", stop.toString());
}
@Test
@@ -65,7 +65,7 @@ else
b=5;
return b;""", TypeInteger.BOT);
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region39,42,5);", stop.toString());
+ assertEquals("return Phi(Region34,42,5);", stop.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index e604937c..e6e0560b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -1,7 +1,6 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.TypeInteger;
import org.junit.Test;
import static org.junit.Assert.*;
diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index c9f695c9..fb324fd9 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -28,7 +28,7 @@ if( (arg/13)==0 ) {
int r = g+h;
return p-r;
""");
- StopNode stop = parser.parse().iterate(false);
+ StopNode stop = parser.parse(false).iterate(false);
assertEquals("return 0;", stop.toString());
}
@@ -48,7 +48,7 @@ bar.a = 1;
bar.a = 2;
return bar.a;
""");
- StopNode stop = parser.parse().iterate();
+ StopNode stop = parser.parse(false).iterate();
assertEquals("return 2;", stop.toString());
}
@@ -102,7 +102,7 @@ while (arg) {
}
return bar.a;
""");
- StopNode stop = parser.parse().iterate();
+ StopNode stop = parser.parse(false).iterate();
assertEquals("return Phi(Loop12,0,(Phi_a+2));", stop.toString());
}
@@ -154,7 +154,7 @@ if (arg) bar = null;
if( bar ) bar.a = 1;
return bar;
""");
- StopNode stop = parser.parse().iterate();
+ StopNode stop = parser.parse(false).iterate();
assertEquals("return Phi(Region31,(*void)Phi(Region19,null,new Bar),null);", stop.toString());
}
@@ -170,7 +170,7 @@ if( !bar ) rez=4;
else bar.a = 1;
return rez;
""");
- StopNode stop = parser.parse().iterate();
+ StopNode stop = parser.parse(false).iterate();
assertEquals("return Phi(Region37,4,3);", stop.toString());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index da0bc087..63cd511a 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -292,7 +292,7 @@ else {
}
""");
StopNode stop = parser.parse().iterate(false);
- assertEquals("Stop[ return 9; return 0; return 0; return 0; return 0; ]", stop.toString());
+ assertEquals("Stop[ return 9; return 0; return 0; return 0; ]", stop.toString());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index 62dce12b..972118be 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -59,5 +59,4 @@ return x+1==x;
assertEquals("return ((flt)arg==(ToFloat+1.0));", stop.toString());
assertEquals(0L, Evaluator.evaluate(stop, 1));
}
-
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter13Test.java b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
index b425fd85..0edb2d51 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter13Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter13Test.java
@@ -127,7 +127,7 @@ n.i = 3.14;
return n.i;
""");
try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Cannot store 3.14 into field IntBot i",e.getMessage()); }
+ catch( Exception e ) { assertEquals("Cannot store 3.14 into field int i",e.getMessage()); }
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/src/test/java/com/seaofnodes/simple/Chapter14Test.java
new file mode 100644
index 00000000..fcb7fa24
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -0,0 +1,317 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.evaluator.Evaluator;
+import com.seaofnodes.simple.node.StopNode;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class Chapter14Test {
+
+ @Test
+ public void testJig() {
+ Parser parser = new Parser(
+"""
+return 3.14;
+""");
+ StopNode stop = parser.parse(false).iterate();
+ assertEquals("return 3.14;", stop.toString());
+ assertEquals(3.14, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testRange() {
+ Parser parser = new Parser(
+"""
+int b;
+if( arg ) b=1; else b=0;
+int c = 99;
+if( b < 0 ) c = -1;
+if( b > 2 ) c = 1;
+return c;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 99;", stop.toString());
+ assertEquals(99L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testU8() {
+ Parser parser = new Parser(
+"""
+u8 b = 123;
+b = b + 456;// Truncate
+return b;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 67;", stop.toString());
+ assertEquals(67L, Evaluator.evaluate(stop, 0));
+ }
+
+
+ @Test
+ public void testU8While() {
+ Parser parser = new Parser(
+"""
+u8 b = 123;
+while( b ) b = b + 456;// Truncate
+return b;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return Phi(Loop9,123,((Phi_b+456)&255));", stop.toString());
+ }
+
+ @Test
+ public void testU1() {
+ Parser parser = new Parser(
+"""
+bool b = 123;
+b = b + 456;// Truncate
+u1 c = b; // No more truncate needed
+return c;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 1;", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testAnd() {
+ Parser parser = new Parser(
+"""
+int b = 123;
+b = b+456 & 31; // Precedence
+return b;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 3;", stop.toString());
+ assertEquals(3L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testRefLoad() {
+ Parser parser = new Parser(
+"""
+struct Foo { u1 b; }
+Foo f = new Foo;
+f.b = 123;
+return f.b;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 1;", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testSigned() {
+ Parser parser = new Parser(
+"""
+i8 b = 255; // Chopped
+return b; // Sign extend
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return -1;", stop.toString());
+ assertEquals(-1L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testI8() {
+ Parser parser = new Parser(
+"""
+i8 b = arg;
+b = b + 1;// Truncate
+return b;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return (((((arg<<56)>>56)+1)<<56)>>56);", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testMask() {
+ Parser parser = new Parser(
+"""
+u16 mask = (1<<16)-1; // AND mask
+int c = 123456789 & mask;
+return c; //
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 52501;", stop.toString());
+ assertEquals(52501L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testWrapMinus() {
+ Parser parser = new Parser(
+"""
+int MAX = 9223372036854775807; //0x7FFFFFFFFFFFFFFF;
+int i = (arg&MAX) + -MAX + -1; // Basically (e0) + Long.MIN_VALUE
+int j = -i; // Negating Long.MIN_VALUE wraps, cannot constant fold
+if (arg) j = 1;
+return j;
+""");
+ StopNode stop = parser.parse(false).iterate();
+ assertEquals("return Phi(Region28,1,(-((arg&9223372036854775807)+-9223372036854775808)));", stop.toString());
+ assertEquals(-9223372036854775808L, Evaluator.evaluate(stop, 0));
+ assertEquals(1L, Evaluator.evaluate(stop, 1));
+ }
+
+ @Test
+ public void testWrapShr() {
+ Parser parser = new Parser(
+"""
+return (arg >>> 1)==0;
+""");
+ StopNode stop = parser.parse(false).iterate();
+ assertEquals("return (!(arg>>>1));", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ assertEquals(1L, Evaluator.evaluate(stop, 1));
+ assertEquals(0L, Evaluator.evaluate(stop, 2));
+ }
+
+ @Test
+ public void testOr() {
+ Parser parser = new Parser(
+"""
+return (arg | 123 ^ 456) >>> 1;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return (((arg|123)^456)>>>1);", stop.toString());
+ assertEquals(217L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testMaskFloat() {
+ Parser parser = new Parser(
+"""
+flt f = arg;
+arg = f & 0;
+return arg;
+""");
+ try { parser.parse().iterate(); fail(); }
+ catch( Exception e ) { assertEquals("Cannot '&' FltBot",e.getMessage()); }
+ }
+
+ @Test
+ public void testLoadBug() {
+ Parser parser = new Parser(
+"""
+struct A { int i; }
+struct B { int i; }
+A a = new A;
+A t1 = new A;
+B b = new B;
+B t2 = new B;
+int i;
+if (arg) i = a.i;
+else i = b.i;
+return i;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return Phi(Region31,.i,.i);", stop.toString());
+ assertEquals(0L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testBug2() {
+ Parser parser = new Parser(
+"""
+int z = 0;
+while (1) {
+ int j;
+ if (arg&3) {
+ j = arg >> 2;
+ } else {
+ j = (arg >> 3)+z;
+ }
+ return j+1;
+}
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return ((arg>>Phi(Region32,2,3))+1);", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ assertEquals(1L, Evaluator.evaluate(stop, 1));
+ assertEquals(2L, Evaluator.evaluate(stop, 7));
+ assertEquals(2L, Evaluator.evaluate(stop, 8));
+ }
+
+ @Test
+ public void testBug3() {
+ Parser parser = new Parser(
+"""
+flt f = arg;
+bool b;
+if (arg&3) b = f == 1.0;
+else b = f == 2.0;
+if (arg&5) b = arg == 1;
+return b;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return Phi(Region37,(arg==1),((flt)arg==Phi(Region23,1.0,2.0)));", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 1));
+ assertEquals(0L, Evaluator.evaluate(stop, 2));
+ assertEquals(0L, Evaluator.evaluate(stop, 3));
+ }
+
+ @Test
+ public void testBug4() {
+ Parser parser = new Parser(
+"""
+int i;
+if (arg&7) i=3;
+else i=2;
+return (arg == i) == 1;
+""");
+ StopNode stop = parser.parse(false).iterate();
+ assertEquals("return (arg==Phi(Region18,3,2));", stop.toString());
+ assertEquals(0L, Evaluator.evaluate(stop, 0));
+ assertEquals(0L, Evaluator.evaluate(stop, 1));
+ assertEquals(0L, Evaluator.evaluate(stop, 2));
+ assertEquals(1L, Evaluator.evaluate(stop, 3));
+ }
+
+ @Test
+ public void testBug5() {
+ Parser parser = new Parser(
+"""
+int d = -1;
+while (1) {
+ int i = (arg & 7) + (9223372036854775807 - 7);
+ if (arg & 1) i = ((arg&3) + (9223372036854775807 - 3)) & d;
+ return i + -1;
+}
+""");
+ StopNode stop = parser.parse(false).iterate();
+ assertEquals("return (Phi(Region43,((arg&3)+9223372036854775804),((arg&7)+9223372036854775800))+-1);", stop.toString());
+ assertEquals(0x7FFFFFFFFFFFFFF7L, Evaluator.evaluate(stop, 0));
+ assertEquals(0x7FFFFFFFFFFFFFFCL, Evaluator.evaluate(stop, 1));
+ }
+
+ @Test
+ public void testTypes() {
+ Parser parser = new Parser(
+"""
+i8 xi8 = 123456789; if( xi8 != 21 ) return -8;
+i16 xi16 = 123456789; if( xi16 != -13035 ) return -16;
+i32 xi32 = 123456789; if( xi32 != 123456789 ) return -32;
+i64 xi64 = 123456789; if( xi64 != 123456789 ) return -64;
+int xint = 123456789; if( xint != 123456789 ) return -64;
+
+u1 ui1 = 123456789; if( ui1 != 1 ) return 1;
+u8 ui8 = 123456789; if( ui8 != 21 ) return 8;
+u16 ui16 = 123456789; if( ui16 != 52501 ) return 16;
+u32 ui32 = 123456789; if( ui32 != 123456789 ) return 32;
+
+flt fflt = 3.141592653589793; if( fflt != 3.141592653589793 ) return 3;
+f64 ff64 = 3.141592653589793; if( ff64 != 3.141592653589793 ) return 3;
+f32 ff32 = 3.141592653589793; if( ff32 != 3.1415927410125732) return 5;
+
+return 0;
+""");
+ StopNode stop = parser.parse().iterate(false);
+ assertEquals("return 0;", stop.toString());
+ assertEquals(0L, Evaluator.evaluate(stop, 0));
+ }
+
+}
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3512d2e9..31a14c05 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -150,6 +150,12 @@ public class Evaluator {
case NotNode not -> isTrue(val(not.in(1))) ? 0L : 1L;
case SubNode sub -> vall(sub.in(1)) - vall(sub.in(2));
case SubFNode sub -> vald(sub.in(1)) - vald(sub.in(2));
+ case ShlNode shl -> vall(shl.in(1)) << vall(shl.in(2));
+ case ShrNode shr -> vall(shr.in(1)) >>> vall(shr.in(2));
+ case SarNode sar -> vall(sar.in(1)) >> vall(sar.in(2));
+ case AndNode and -> vall(and.in(1)) & vall(and.in(2));
+ case OrNode or -> vall(or .in(1)) | vall(or .in(2));
+ case XorNode xor -> vall(xor.in(1)) ^ vall(xor.in(2));
case CastNode cast -> val(cast.in(1));
case ToFloatNode cast -> (double)vall(cast.in(1));
case LoadNode load -> load(load);
diff --git a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
index 94929241..ec6e7816 100644
--- a/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
+++ b/src/test/java/com/seaofnodes/simple/fuzzer/ScriptGenerator.java
@@ -2,8 +2,12 @@ package com.seaofnodes.simple.fuzzer;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.ScopeNode;
+import com.seaofnodes.simple.type.TypeInteger;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Random;
import java.util.function.Predicate;
/**
@@ -21,7 +25,7 @@ public class ScriptGenerator {
private static final int MAX_BLOCK_DEPTH = 4;
private static final int MAX_EXPRESSION_DEPTH = 10;
private static final int MAX_SUFFIX_RECURSIVE_DEPTH = 10;
-
+
/**
* Number of spaces per indentation
*/
@@ -54,7 +58,7 @@ public class ScriptGenerator {
/**
* Binary operators.
*/
- private static final String[] BINARY_OP = {"==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/"};
+ private static final String[] BINARY_OP = {"==", "!=", "<", "<=", ">", ">=", "+", "-", "*", "/", "&", ">>", ">>>", "<<"};
/**
* Unary operators.
*/
@@ -91,18 +95,29 @@ public class ScriptGenerator {
private static class TypeInt extends Type {
- TypeInt() {
- super("int");
+ TypeInt(String name) {
+ super(name);
}
@Override
boolean isa(Type other) {
- return super.isa(other) || other == TYPE_FLT;
+ return super.isa(other) || other == TYPE_FLT || other instanceof TypeInt;
}
}
- private static final Type TYPE_INT = new TypeInt();
+ private static final List<TypeInt> INTTYPES = new ArrayList<>();
+
+ static {
+ new Parser("", TypeInteger.BOT);
+ for(var e:Parser.TYPES.entrySet()) {
+ if (e.getValue() instanceof TypeInteger) {
+ INTTYPES.add(new TypeInt(e.getKey()));
+ }
+ }
+ }
+ private static final Type TYPE_INT = INTTYPES.stream().filter(t->t.name.equals("int")).findAny().get();
+ private static final Type TYPE_BOOL = INTTYPES.stream().filter(t->t.name.equals("bool")).findAny().get();
private static class Variable {
final String name;
@@ -361,13 +376,17 @@ public class ScriptGenerator {
return v;
}
+ public TypeInt getIntType() {
+ return randFromList(INTTYPES);
+ }
+
/**
* Get a random type
* @return A random type
*/
private Type getType(boolean allowForward) {
int t = random.nextInt(10);
- if( t<5 || structs.isEmpty() ) return TYPE_INT;
+ if( t<5 || structs.isEmpty() ) return getIntType();
if( t<7 ) return TYPE_FLT;
if (allowForward && random.nextInt(10)==0) {
int i=random.nextInt(forwardStructs.size()+1);
@@ -448,7 +467,7 @@ public class ScriptGenerator {
int idx = random.nextInt(forwardStructs.size()+10);
TypeStruct struct;
if (idx < 10) {
- var name = getStructName();
+ var name = getStructName();
struct = new TypeStruct(name);
forwardStructs.add(struct);
} else {
@@ -561,7 +580,7 @@ public class ScriptGenerator {
*/
public int genIf() {
sb.append("if(");
- genExpression(TYPE_INT, true);
+ genExpression(TYPE_BOOL, true);
sb.append(") ");
var stop = genStatementBlock();
if ((stop & FLAG_IF_WITHOUT_ELSE) == 0 && random.nextInt(10) > 3) {
@@ -607,7 +626,7 @@ public class ScriptGenerator {
*/
public int genWhile() {
sb.append("while(");
- genExpression(TYPE_INT, true);
+ genExpression(TYPE_BOOL, true);
sb.append(") ");
loopDepth++;
genStatementBlock();
@@ -725,7 +744,7 @@ public class ScriptGenerator {
*/
public void genExpression(Type type, boolean change) {
if (change && generateInvalid()) type = getType();
- if (type != TYPE_INT && type != TYPE_FLT) {
+ if (!(type instanceof TypeInt) && type != TYPE_FLT) {
genUnary(type, false);
return;
}
@@ -742,7 +761,7 @@ public class ScriptGenerator {
*/
public void genUnary(Type type, boolean change) {
if (change && generateInvalid()) type = getType();
- if (type != TYPE_INT && type != TYPE_FLT) {
+ if (!(type instanceof TypeInt) && type != TYPE_FLT) {
genSuffix(type, false);
return;
}
@@ -822,7 +841,7 @@ public class ScriptGenerator {
*/
public void genConst(Type type) {
if (generateInvalid()) type = getType();
- if (type == TYPE_INT || (type == TYPE_FLT && random.nextBoolean())) {
+ if (type instanceof TypeInt || (type == TYPE_FLT && random.nextBoolean())) {
var rand = random.nextInt(10);
switch (rand) {
case 0 -> sb.append("true");