From 5400096ec10da839cf2006a8a02e11ffeff521dd Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 29 Dec 2024 16:06:07 -0800 Subject: More precise error reporting Demand CFG cleanup, even if peeps are off; cleans up some tests & a lot of fuzzer bugs related to trying to find infinite loops in dead code. Logical subclass for better error reporting on &,|,^,>>,<<,>>> More precise error reporting on multi returns and trinaries Fuzzer uses new Eval2 --- .../main/java/com/seaofnodes/simple/CodeGen.java | 5 ++- .../com/seaofnodes/simple/GlobalCodeMotion.java | 7 ++-- .../main/java/com/seaofnodes/simple/Parser.java | 23 +++++++---- .../java/com/seaofnodes/simple/node/AndNode.java | 17 ++------- .../com/seaofnodes/simple/node/LogicalNode.java | 25 ++++++++++++ .../java/com/seaofnodes/simple/node/LoopNode.java | 31 +++++++++------ .../main/java/com/seaofnodes/simple/node/Node.java | 17 ++++++--- .../java/com/seaofnodes/simple/node/OrNode.java | 17 ++------- .../java/com/seaofnodes/simple/node/PhiNode.java | 38 +++++++++++++++++-- .../com/seaofnodes/simple/node/RegionNode.java | 3 +- .../com/seaofnodes/simple/node/ReturnNode.java | 44 +++++++++++++++++++++- .../java/com/seaofnodes/simple/node/SarNode.java | 17 ++------- .../java/com/seaofnodes/simple/node/ShlNode.java | 17 ++------- .../java/com/seaofnodes/simple/node/ShrNode.java | 12 ++---- .../java/com/seaofnodes/simple/node/XorNode.java | 17 ++------- .../java/com/seaofnodes/simple/type/TypeMem.java | 1 + .../java/com/seaofnodes/simple/Chapter02Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter03Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter09Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter17Test.java | 11 ++++++ .../src/test/java/com/seaofnodes/simple/Eval2.java | 1 + .../java/com/seaofnodes/simple/fuzzer/Fuzzer.java | 41 ++++++++------------ .../com/seaofnodes/simple/fuzzer/FuzzerUtils.java | 9 +---- .../seaofnodes/simple/fuzzer/ScriptGenerator.java | 5 ++- 24 files changed, 211 insertions(+), 155 deletions(-) create mode 100644 chapter18/src/main/java/com/seaofnodes/simple/node/LogicalNode.java (limited to 'chapter18') diff --git a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java index 79d817f8..394b6d1c 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java @@ -57,7 +57,10 @@ public class CodeGen { } public CodeGen typeCheck() { - assert _phase == Phase.Opto || _phase == Phase.Parse; + // Demand phase Opto for cleaning up dead control flow at least, + // required for the following GCM. Note that peeps can be disabled, + // but still the dead CFG will get cleaned. + assert _phase == Phase.Opto; _phase = Phase.TypeCheck; // Type check diff --git a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java index 6b0956ca..f1593a81 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java @@ -107,6 +107,8 @@ public abstract class GlobalCodeMotion { if( n instanceof LoadNode ld ) for( Node memuse : ld.mem()._outputs ) if( late[memuse._nid]==null && + // New makes new memory, never crushes load memory + !(memuse instanceof NewNode) && // Load-use directly defines memory (memuse._type instanceof TypeMem || // Load-use indirectly defines memory @@ -188,10 +190,6 @@ public abstract class GlobalCodeMotion { assert late[st._nid]!=null; lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); break; - case NewNode st: - assert late[st._nid]!=null; - lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); - break; case PhiNode phi: // Repeat anti-dep for matching Phi inputs. // No anti-dep edges but may raise the LCA. @@ -199,6 +197,7 @@ public abstract class GlobalCodeMotion { if( phi.in(i)==load.mem() ) lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null); break; + case NewNode st: break; case LoadNode ld: break; // Loads do not cause anti-deps on other loads case ReturnNode ret: break; // Load must already be ahead of Return case ScopeMinNode ret: break; // Mem uses now on ScopeMin diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java index ac497fe8..235d2414 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java @@ -226,7 +226,7 @@ public class Parser { } // Parse the body - Node last=null; + Node last=ZERO; while (!peek('}') && !_lexer.isEOF()) last = parseStatement(); @@ -562,6 +562,9 @@ public class Parser { RegionNode r = ctrl(tScope.mergeScopes(fScope)); Node ret = peep(new PhiNode("",lhs._type.meet(rhs._type),r,lhs.unkeep(),rhs.unkeep())); + // Immediately fail e.g. `arg ? 7 : ptr` + if( !stmt && ret.err() !=null ) + throw error(ret.err()); r.peephole(); return ret; } @@ -938,6 +941,8 @@ public class Parser { else if( match(">>") ) lhs = new SarNode(lhs,null); else break; lhs.setDef(2,parseAddition()); + if( lhs.err() != null ) + throw error(lhs.err()); lhs = peep(lhs.widen()); } return lhs; @@ -973,7 +978,7 @@ public class Parser { * @return a multiply expression {@link Node}, never {@code null} */ private Node parseMultiplication() { - var lhs = parseUnary(); boolean mul; + var lhs = parseUnary(); while( true ) { if( false ) ; else if( match("*") ) lhs = new MulNode(lhs,null); @@ -1004,7 +1009,9 @@ public class Parser { if( n != null ) { if( n._final ) throw error("Cannot reassign final '"+n._name+"'"); - Node expr = zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); + Node expr = n.type() instanceof TypeFloat + ? peep(new AddFNode(_scope.in(n),con(TypeFloat.constant(-1)))) + : zsMask(peep(new AddNode(_scope.in(n),con(delta))),n.type()); _scope.update(n,expr); return expr; } @@ -1054,7 +1061,7 @@ public class Parser { // Widen RHS int to a RHS float rhs = widenInt(rhs,n.type()); // Complain a RHS float into a LHS int - if( !rhs._type.isa(n.type()) ) + if( !(rhs._type instanceof TypeInteger) && !rhs._type.isa(n.type()) ) throw error("Type " + rhs._type.str() + " is not of declared type " + n.type().str()); Node op = switch(ch) { @@ -1124,7 +1131,7 @@ public class Parser { } // Check that all fields are initialized for( int i=idx; i>"; } @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() { Type t1 = in(1)._type, t2 = in(2)._type; @@ -52,9 +46,4 @@ public class SarNode extends Node { return null; } @Override Node copy(Node lhs, Node rhs) { return new SarNode(lhs,rhs); } - @Override public 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/chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java index e454362a..24dff5e2 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -5,20 +5,14 @@ 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); } +public class ShlNode extends LogicalNode { + public ShlNode(Node lhs, Node rhs) { super(lhs, rhs); } @Override public String label() { return "Shl"; } + @Override public String op() { return "<<"; } @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() { Type t1 = in(1)._type, t2 = in(2)._type; @@ -56,9 +50,4 @@ public class ShlNode extends Node { return null; } @Override Node copy(Node lhs, Node rhs) { return new ShlNode(lhs,rhs); } - @Override public 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/chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java index 6f4a2c6c..52d2971d 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -4,20 +4,14 @@ 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); } +public class ShrNode extends LogicalNode { + public ShrNode(Node lhs, Node rhs) { super(lhs, rhs); } @Override public String label() { return "Shr"; } + @Override public String op() { return ">>>"; } @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() { Type t1 = in(1)._type, t2 = in(2)._type; diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java index c140ba83..a23e1304 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java @@ -5,20 +5,14 @@ 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); } +public class XorNode extends LogicalNode { + public XorNode(Node lhs, Node rhs) { super(lhs, rhs); } @Override public String label() { return "Xor"; } + @Override public String op() { return "^"; } @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() { Type t1 = in(1)._type, t2 = in(2)._type; @@ -57,9 +51,4 @@ public class XorNode extends Node { return null; } @Override Node copy(Node lhs, Node rhs) { return new XorNode(lhs,rhs); } - @Override public 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/chapter18/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeMem.java index 60b3612f..9438abe2 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeMem.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeMem.java @@ -44,6 +44,7 @@ public class TypeMem extends Type { return make(_alias,_t.dual()); } + @Override public boolean isHigh() { return _t.isHigh(); } @Override public Type glb() { return make(_alias,_t.glb()); } @Override int hash() { return 9876543 + _alias + _t.hashCode(); } diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter02Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter02Test.java index 4254f0a6..324ebf41 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter02Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter02Test.java @@ -12,7 +12,7 @@ public class Chapter02Test { public void testParseGrammar() { CodeGen code = new CodeGen("return 1+2*3+-5;"); code.parse(true); // Disable peepholes - assertEquals("return Phi(Region,((1+(2*3))+(-5)));", code.print()); + assertEquals("return ((1+(2*3))+(-5));", code.print()); } @Test diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter03Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter03Test.java index 2e45ee2a..a7fc3ae5 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter03Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter03Test.java @@ -30,7 +30,7 @@ public class Chapter03Test { public void testVarScopeNoPeephole() { CodeGen code = new CodeGen("int a=1; int b=2; int !c=0; { int b=3; c=a+b; } return c; "); code.parse(true); - assertEquals("return Phi(Region,(1+3));", code.print()); + assertEquals("return (1+3);", code.print()); } @Test diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java index 5b764013..b210f7e9 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter09Test.java @@ -194,7 +194,7 @@ int v0=0!=0<-0; return -0+0+0; """); code.parse().opto(); - assertEquals("return 0;", code.print()); + assertEquals("return Top;", code.print()); } @@ -243,7 +243,7 @@ while(1) { return 0!=0; """); code.parse().opto(); - assertEquals("return 0;", code.print()); + assertEquals("return Top;", code.print()); } @Test diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java index 4bcb9daa..5247f122 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter17Test.java @@ -353,6 +353,17 @@ return f; // missing widening assertEquals("1.2", Eval2.eval(code, 0)); } + + @Test + public void testTrinary6() { + CodeGen code = new CodeGen(""" +struct S{}; +return arg ? 7 : new S; +"""); + try { code.parse().opto().typeCheck(); fail(); } + catch( Exception e ) { assertEquals("No common type amongst int and reference",e.getMessage()); } + } + // --------------------------------------------------------------- @Test public void testFor0() { diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java index f7f58f32..77cbf115 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Eval2.java @@ -115,6 +115,7 @@ public abstract class Eval2 { case MinusNode sub -> - x(sub.in(1)); case MulNode mul -> x(mul.in(1)) * x(mul.in(2)); case NewNode alloc -> alloc(alloc); + case NotNode not -> x(not.in(1)) == 0 ? 1L : 0L; case OrNode or -> x(or .in(1)) | x(or .in(2)); case ProjNode proj -> proj._type instanceof TypeMem ? "$mem" : val(proj.in(0)); case ReadOnlyNode read -> val(read.in(1)); diff --git a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java index 6f5307bc..66395a74 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -1,7 +1,8 @@ package com.seaofnodes.simple.fuzzer; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.Eval2; import com.seaofnodes.simple.CodeGen; -import com.seaofnodes.simple.evaluator.Evaluator; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.StopNode; import java.util.ArrayList; @@ -55,18 +56,8 @@ public class Fuzzer { private static boolean neq(Object a, Object b) { if (Objects.equals(a, b)) return false; - if (a instanceof Evaluator.Obj ea && b instanceof Evaluator.Obj eb) { - if (ea.struct() != eb.struct()) return true; - for(int i=0;i