diff options
29 files changed, 333 insertions, 166 deletions
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Ary.java b/chapter18/src/main/java/com/seaofnodes/simple/Ary.java index d37c284f..b5184948 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/Ary.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/Ary.java @@ -67,20 +67,6 @@ public class Ary<E> extends AbstractList<E> implements List<E> { return tmp; } - ///** Element removal, using '=='. Does not preserve order. - // * @param e element to be removed - // * @return element removed */ - //public E del( E e ) { - // for( int i=0; i<_len; i++ ) { - // E tmp = _es[i]; - // if( tmp==e ) { - // _es[i]=_es[--_len]; - // return tmp; - // } - // } - // return null; - //} - /** Slow, linear-time, element removal. Preserves order. * @param i element to be removed * @return element removed */ @@ -117,6 +103,16 @@ public class Ary<E> extends AbstractList<E> implements List<E> { return this; } + public void swap( int i, int j ) { + range_check(i); + range_check(j); + if( i==j ) return; + E tmp = _es[i]; + _es[i] = _es[j]; + _es[j] = tmp; + } + + /** @param c Collection to be added */ public boolean addAll( Collection<? extends E> c ) { if( c==null ) return false; diff --git a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java index 9588fb01..bd56110c 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java @@ -66,9 +66,9 @@ public class CodeGen { _phase = Phase.TypeCheck; // Type check - String err = _stop.walk( Node::err ); + Parser.ParseException err = _stop.walk( Node::err ); if( err != null ) - throw new Parser.ParserException(err); + throw err; return this; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java index 49667ecc..194ef4f2 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java @@ -150,15 +150,15 @@ public class Parser { public StopNode parse() { return parse(false); } public StopNode parse(boolean show) { _xScopes.push(_scope); - _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null); - _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null); - _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null); + _scope.define(ScopeNode.CTRL, Type.CONTROL , false, null, _lexer); + _scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null, _lexer); + _scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, _lexer); ctrl(XCTRL); _scope.mem(new ScopeMinNode(false)); // Parse whole program, as-if function header "{ int arg -> body }" - ReturnNode ret = parseFunctionBody(TypeFunPtr.MAIN,"arg"); + ReturnNode ret = parseFunctionBody(TypeFunPtr.MAIN,loc(),"arg"); if( !_lexer.isEOF() ) throw _errorSyntax("unexpected"); @@ -177,19 +177,25 @@ public class Parser { /** * Parses a function body, assuming the header is parsed. */ - private ReturnNode parseFunctionBody(TypeFunPtr sig, String... ids) { + private ReturnNode parseFunctionBody(TypeFunPtr sig, Lexer loc, String... ids) { + // Stack parser state on the local Java stack, and unstack it later Node oldctrl = ctrl().keep(); Node oldmem = _scope.mem().keep(); FunNode oldfun = _fun; - FunNode fun = _fun = (FunNode)peep(new FunNode(START,sig)); + FunNode fun = _fun = (FunNode)peep(new FunNode(loc(),START,sig)); + // Once the function header is available, install in linker table - + // allowing recursive functions. Linker matches on declared args and + // exact fidx, and ignores the return (because the fidx will only match + // the exact single function). + CodeGen.CODE.link(fun); // Build a multi-exit return point for all function returns - RegionNode r = new RegionNode(null,null); + RegionNode r = new RegionNode((Node)null,null); assert r.inProgress(); PhiNode rmem = new PhiNode(ScopeNode.MEM0,TypeMem.BOT,r,null); PhiNode rrez = new PhiNode(ScopeNode.ARG0,Type.BOTTOM,r,null); - ReturnNode ret = new ReturnNode(r, rmem, rrez, fun); + ReturnNode ret = new ReturnNode(r, rmem, rrez, fun, fun._loc); fun.setRet(ret); assert ret.inProgress(); STOP.addDef(ret); @@ -208,7 +214,7 @@ public class Parser { for( int i=0; i<ids.length; i++ ) { Type t = sig.arg(i); assert t==t.glb(); // Expect args declared type not be lifted - _scope.define(ids[i], t, false, new ParmNode(ids[i],i+2,t,fun,con(t)).peephole()); + _scope.define(ids[i], t, false, new ParmNode(ids[i],i+2,t,fun,con(t)).peephole(), loc); } // Parse the body @@ -225,6 +231,7 @@ public class Parser { r ._inputs.pop(); rmem._inputs.pop(); rrez._inputs.pop(); + r._loc = loc(); // Final position assert !r.inProgress(); // Force peeps, which have been avoided due to inProgress @@ -240,13 +247,6 @@ public class Parser { ctrl(oldctrl.unkeep()); _scope.mem(oldmem.unkeep()); - // Once function body is complete, install in linker table. Linker - // matches on declared args and exact fidx, and ignores the return - // (because the fidx will only match the exact single function). The - // normal TFP interning ALSO matches on return, so wipe out the return - // for this arg+fidx only match. - CodeGen.CODE.link(fun); - return ret; } @@ -349,7 +349,7 @@ public class Parser { // used as an indicator to switch off peepholes of the region and // associated phis; see {@code inProgress()}. - ctrl(new LoopNode(ctrl()).peephole()); // Note we set back edge to null here + ctrl(new LoopNode(loc(),ctrl()).peephole()); // Note we set back edge to null here // At loop head, we clone the current Scope (this includes all // names in every nesting level within the Scope). @@ -453,7 +453,7 @@ public class Parser { return cur; // toScope is either the break scope, or a scope that was created here assert toScope._lexSize.size() <= _breakScope._lexSize.size(); - toScope.ctrl(toScope.mergeScopes(cur).peephole()); + toScope.ctrl(toScope.mergeScopes(cur,loc()).peephole()); return toScope; } @@ -517,7 +517,6 @@ public class Parser { Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep(); // In if true branch, the ifT proj node becomes the ctrl // But first clone the scope and set it as current - int ndefs = _scope.nIns(); ScopeNode fScope = _scope.dup(); // Duplicate current scope _xScopes.push(fScope); // For graph visualization we need all scopes @@ -527,6 +526,11 @@ public class Parser { Node lhs = (stmt ? parseStatement() : parseAsgn()).keep(); // Parse true-side _scope.removeGuards(ifT); + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + fScope.balanceIf(_scope); + ScopeNode tScope = _scope; // Parse the false side @@ -544,25 +548,25 @@ public class Parser { fScope = _scope; pred.unkeep(); - // Check for `if(pred) int x=17;` - if( tScope.nIns() != ndefs || fScope.nIns() != ndefs ) - throw error("Cannot define a new name on one arm of an if"); - // Check the trinary widening int/flt if( !stmt ) { rhs = widenInt( rhs.unkeep(), lhs._type ).keep(); lhs = widenInt( lhs.unkeep(), rhs._type ).keep(); } - // Merge results _scope = tScope; _xScopes.pop(); // Discard pushed from graph display + // See if a one-sided def was made: "if(pred) int x = 1;" and throw. + // See if any forward-refs were made, and copy them to the other side: + // "pred ? n*fact(n-1) : 1" + tScope.balanceIf(fScope); - RegionNode r = ctrl(tScope.mergeScopes(fScope)); + // Merge results + RegionNode r = ctrl(tScope.mergeScopes(fScope,loc())); 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()); + ParseException err; + if( !stmt && (err=ret.err()) !=null ) throw err; r.peephole(); return ret; } @@ -641,7 +645,9 @@ public class Parser { expr = zsMask(expr,t); // Type is sane if( !expr._type.isa(t) ) - throw error("Type " + expr._type.str() + " is not of declared type " + t.str()); + throw expr instanceof FRefNode fref + ? fref.err() + : error("Type " + expr._type.str() + " is not of declared type " + t.str()); return expr; } @@ -675,6 +681,7 @@ public class Parser { // Has var/val instead of a user-declared type boolean inferType = t==Type.TOP || t==Type.BOTTOM; boolean hasBang = match("!"); + Lexer loc = loc(); String name = requireId(); // Optional initializing expression follows @@ -715,8 +722,8 @@ public class Parser { t = tmp.makeRO(); // Define a new name, - if( !_scope.define(name,t,xfinal,lift) ) - throw error("Redefining name '" + name + "'"); + if( !_scope.define(name,t,xfinal,lift, loc) ) + throw error("Redefining name '" + name + "'", loc); return lift; } @@ -884,9 +891,9 @@ public class Parser { 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 if( match("&") ) lhs = new AndNode(loc(),lhs,null); + else if( match("|") ) lhs = new OrNode(loc(),lhs,null); + else if( match("^") ) lhs = new XorNode(loc(),lhs,null); else break; lhs.setDef(2,parseComparison()); lhs = peep(lhs); @@ -936,13 +943,14 @@ public class Parser { 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 if( match("<<") ) lhs = new ShlNode(loc(),lhs,null); + else if( match(">>>")) lhs = new ShrNode(loc(),lhs,null); + else if( match(">>") ) lhs = new SarNode(loc(),lhs,null); else break; lhs.setDef(2,parseAddition()); - if( lhs.err() != null ) - throw error(lhs.err()); + ParseException err; + if( (err=lhs.err()) != null ) + throw err; lhs = peep(lhs.widen()); } return lhs; @@ -1041,7 +1049,7 @@ public class Parser { if( matchx("new" ) ) return alloc(); if( match ("{" ) ) return require(func(),"}"); // Expect an identifier now - ScopeMinNode.Var n = requireLookupId("an identifier or expression"); + ScopeMinNode.Var n = requireLookupId(); Node rvalue = _scope.in(n); if( rvalue._type == Type.BOTTOM ) throw error("Cannot read uninitialized field '"+n._name+"'"); @@ -1080,13 +1088,18 @@ public class Parser { return (byte)ch== 1 || (byte)ch== -1 ? lhs : op; } - ScopeMinNode.Var requireLookupId(String msg) { + // Expect an ID here. If not found, assume a forward reference function + ScopeMinNode.Var requireLookupId() { String id = _lexer.matchId(); if( id == null || KEYWORDS.contains(id) ) - throw errorSyntax(msg); + throw errorSyntax("an identifier or expression"); ScopeMinNode.Var n = _scope.lookup(id); - if( n==null ) - throw error("Undefined name '" + id + "'"); + // If missing, assume a forward reference + if( n==null ) { + _scope.define(id, TypeFunPtr.BOT, false, XCTRL, loc()); + n = _scope.lookup(id); + //throw error("Undefined name '" + id + "'"); + } return n; } @@ -1122,8 +1135,9 @@ public class Parser { idx = _scope.nIns(); // Push a scope, and pre-assign all struct fields. _scope.push(); + Lexer loc = loc(); for( int i=0; i<fs.length; i++ ) - _scope.define(fs[i]._fname, fs[i]._type, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i)); + _scope.define(fs[i]._fname, fs[i]._type, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i), loc); // Parse the constructor body parseBlock(true); require("}"); @@ -1170,7 +1184,7 @@ public class Parser { private Node newArray(TypeStruct ary, Node len) { int base = ary.aryBase (); int scale= ary.aryScale(); - Node size = peep(new AddNode(con(base),peep(new ShlNode(len.keep(),con(scale))))); + Node size = peep(new AddNode(con(base),peep(new ShlNode(null,len.keep(),con(scale))))); ALTMP.clear(); ALTMP.add(len.unkeep()); ALTMP.add(con(ary._fields[1]._type.makeZero())); return newStruct(ary,size,0,ALTMP); } @@ -1225,7 +1239,7 @@ public class Parser { // Field offset; fixed for structs, computed for arrays Node off = (name.equals("[]") // If field is an array body // Array index math - ? peep(new AddNode(con(base.aryBase()),peep(new ShlNode(require(parseAsgn(),"]"),con(base.aryScale()))))) + ? peep(new AddNode(con(base.aryBase()),peep(new ShlNode(null,require(parseAsgn(),"]"),con(base.aryScale()))))) // Struct field offsets are hardwired : con(base.offset(fidx))).keep(); @@ -1234,7 +1248,7 @@ public class Parser { Node val = parseAsgn().keep(); Node lift = liftExpr( val, tf, f._final ); - Node st = new StoreNode(name, f._alias, tf, memAlias(f._alias), expr, off.unkeep(), lift, false); + Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr, off.unkeep(), lift, false); // Arrays include control, as a proxy for a safety range check. // Structs don't need this; they only need a NPE check which is // done via the type system. @@ -1243,7 +1257,7 @@ public class Parser { return val.unkeep(); // "obj.a = expr" returns the expression while updating memory } - Node load = new LoadNode(name, f._alias, tf.glb(), memAlias(f._alias), expr.keep(), off); + Node load = new LoadNode(loc(),name, f._alias, tf.glb(), memAlias(f._alias), expr.keep(), off); // Arrays include control, as a proxy for a safety range check // Structs don't need this; they only need a NPE check which is // done via the type system. @@ -1256,7 +1270,7 @@ public class Parser { throw error("Cannot reassign final '"+f._fname+"'"); Node inc = peep(new AddNode(load,con( _lexer.peek(-1)=='+' ? 1 : -1))); Node val = zsMask(inc,tf); - Node st = new StoreNode(name, f._alias, tf, memAlias(f._alias), expr.unkeep(), off, val, false); + Node st = new StoreNode(loc(), name, f._alias, tf, memAlias(f._alias), expr.unkeep(), off, val, false); // Arrays include control, as a proxy for a safety range check. // Structs don't need this; they only need a NPE check which is // done via the type system. @@ -1280,13 +1294,13 @@ public class Parser { return peep(new RoundF32Node(val)); } if( t0._min==0 ) // Unsigned - return peep(new AndNode(val,con(t0._max))); + return peep(new AndNode(null,val,con(t0._max))); // Signed extension int shift = Long.numberOfLeadingZeros(t0._max)-1; Node shf = con(shift); if( shf._type==TypeInteger.ZERO ) return val; - return peep(new SarNode(peep(new ShlNode(val,shf.keep())),shf.unkeep())); + return peep(new SarNode(null,peep(new ShlNode(null,val,shf.keep())),shf.unkeep())); } /** @@ -1300,6 +1314,8 @@ public class Parser { private Node func() { Ary<Type> ts = new Ary<>(Type.class); Ary<String> ids = new Ary<>(String.class); + _lexer.skipWhiteSpace(); + Lexer loc = loc(); // First argument location while( true ) { Type t = type(); // Arg type if( t==null ) break; @@ -1311,7 +1327,7 @@ public class Parser { require("->"); // Make a concrete function type, with a fidx TypeFunPtr tfp = TypeFunPtr.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM); - ReturnNode ret = parseFunctionBody(tfp,ids.asAry()); + ReturnNode ret = parseFunctionBody(tfp,loc,ids.asAry()); return con(ret._fun.sig()); } @@ -1325,7 +1341,7 @@ public class Parser { if( expr._type == Type.NIL ) throw error("Calling a null function pointer"); if( !(expr._type instanceof TypeFunPtr tfp) ) - throw error("Expected a function but got "+expr._type.str()); + throw error("Expected a function but got "+expr._type.glb().str()); expr.keep(); // Keep while parsing args Ary<Node> args = new Ary<Node>(Node.class); @@ -1395,6 +1411,9 @@ public class Parser { return old; } private Type posT(int pos) { _lexer._position = pos; return null; } + // Source code location + Lexer loc() { return new Lexer(_lexer); } + // Require and return an identifier private String requireId() { @@ -1419,16 +1438,15 @@ public class Parser { throw errorSyntax(syntax); } - RuntimeException errorSyntax(String syntax) { return _errorSyntax("expected "+syntax); } - private RuntimeException _errorSyntax(String msg) { - return _lexer.error("Syntax error, "+msg+": " + _lexer.getAnyNextToken()); - } - RuntimeException error(String msg) { return _lexer.error(msg); } - //////////////////////////////////// // Lexer components - private static class Lexer { + // Lexer provides low level access to the raw file bytes, peeks and matches + // short strings, parses numbers, skips comments and whitespace, tracks + // line numbers, allows the parse position to be saved and restored, and + // serves as a location indicator for errors. + + public static class Lexer { // Input buffer; an array of text bytes read from a file or a string private final byte[] _input; @@ -1454,6 +1472,16 @@ public class Parser { _input = buf; } + /** + * Copy a lexer from a lexer + */ + private Lexer(Lexer l) { + _input = l._input; + _position = l._position; + _line_number = l._line_number; + _line_start = l._line_start; + } + // Very handy in the debugger, shows the unparsed program @Override public String toString() { @@ -1574,7 +1602,7 @@ public class Parser { int len = isLongOrDouble(); if( len > 0 ) { if( len > 1 && _input[old]=='0' ) - throw error("Syntax error: integer values cannot start with '0'"); + throw error("Syntax error: integer values cannot start with '0'",this); long i = Long.parseLong(new String(_input,old,len)); return TypeInteger.constant(i); } @@ -1640,14 +1668,19 @@ public class Parser { return 0; } - public RuntimeException error( String errorMessage ) { - // file:line:charoff err - //String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage; - return new ParserException(errorMessage); - } + } + ParseException errorSyntax(String syntax) { return _errorSyntax("expected "+syntax); } + private ParseException _errorSyntax(String msg) { + return error("Syntax error, "+msg+": " + _lexer.getAnyNextToken()); } - public static class ParserException extends RuntimeException { - ParserException(String msg) { super(msg); } + ParseException error(String msg) { return error(msg,_lexer); } + public static ParseException error(String msg, Lexer loc) { return new ParseException(msg,loc); } + + public static class ParseException extends RuntimeException { + public final Lexer _loc; + // file:line:charoff err + //String msg = "src:"+_line_number+":"+(_position-_line_start)+" "+errorMessage; + ParseException( String msg, Lexer loc ) { super(msg); _loc = loc; assert loc!=null; } } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/AndNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/AndNode.java index 7f080ff2..36e3a949 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/AndNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/AndNode.java @@ -1,12 +1,13 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class AndNode extends LogicalNode { - public AndNode(Node lhs, Node rhs) { super(lhs, rhs); } + public AndNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "And"; } @Override public String op() { return "&"; } @@ -53,5 +54,5 @@ public class AndNode extends LogicalNode { return null; } - @Override Node copy(Node lhs, Node rhs) { return new AndNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new AndNode(_loc,lhs,rhs); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java index a3bb1ae9..b051b3ce 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -148,7 +148,9 @@ public abstract class CFGNode extends Node { boolean skip(CFGNode usecfg) { // Only walk control users that are alive. // Do not walk from a Call to linked Fun's. - return usecfg instanceof XCtrlNode || (this instanceof CallNode && usecfg instanceof FunNode); + return usecfg instanceof XCtrlNode || + (this instanceof CallNode && usecfg instanceof FunNode) || + (this instanceof ReturnNode && usecfg instanceof CallEndNode); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java new file mode 100644 index 00000000..5b46959e --- /dev/null +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java @@ -0,0 +1,45 @@ +package com.seaofnodes.simple.node; + +import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.type.Type; +import com.seaofnodes.simple.type.TypeFunPtr; +import java.util.BitSet; + +/** + * A Constant node represents a constant value. At present, the only constants + * that we allow are integer literals; therefore Constants contain an integer + * value. As we add other types of constants, we will refactor how we represent + * Constants. + * <p> + * Constants have no semantic inputs. However, we set Start as an input to + * Constants to enable a forward graph walk. This edge carries no semantic + * meaning, and it is present <em>solely</em> to allow visitation. + * <p> + * The Constant's value is the value stored in it. + */ +public class FRefNode extends ConstantNode { + public final ScopeMinNode.Var _n; + public FRefNode(ScopeMinNode.Var n) { super(TypeFunPtr.BOT); _n = n; } + + @Override + public String label() { return "FRef"+_n; } + + @Override + public String uniqueName() { return "FRef_" + _nid; } + + @Override + StringBuilder _print1(StringBuilder sb, BitSet visited) { + return sb.append("FRef_").append(_n); + } + + @Override public Node idealize() { + // When FRef finds its definition, idealize to it + return nIns()==1 ? null : in(1); + } + + public Parser.ParseException err() { return Parser.error("Undefined name '"+_n._name+"'",_n._loc); } + + @Override boolean eq(Node n) { return this==n; } + @Override int hash() { return _n._idx; } +} diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/FunNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/FunNode.java index c6d318bd..84058e2b 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.IterPeeps; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; @@ -13,7 +14,7 @@ public class FunNode extends RegionNode { private TypeFunPtr _sig; // Initial signature private ReturnNode _ret; // Return pointer - public FunNode( StartNode start, TypeFunPtr sig ) { super(null,start); _sig=sig; } + public FunNode( Parser.Lexer loc, StartNode start, TypeFunPtr sig ) { super(loc,null,start); _sig=sig; } @Override public String label() { return _sig._name; } @@ -65,6 +66,11 @@ public class FunNode extends RegionNode { return super.idealize(); } + // Bypass Region idom, always assume depth == 1, one more than Start + @Override public int idepth() { return (_idepth=1); } + // Bypass Region idom, always assume idom is Start + @Override public CFGNode idom(Node dep) { return cfg(1); } + // Always in-progress until we run out of unknown callers public boolean unknownCallers() { return in(1) instanceof StartNode; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/LoadNode.java index c69d319e..09494c15 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -1,7 +1,7 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.Parser; +import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -19,8 +19,8 @@ public class LoadNode extends MemOpNode { * @param ptr The ptr to the struct base from where we load a field * @param off The offset inside the struct base */ - public LoadNode(String name, int alias, Type glb, Node mem, Node ptr, Node off) { - super(name, alias, glb, mem, ptr, off); + public LoadNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { + super(loc, name, alias, glb, mem, ptr, off); } // GraphVis DOT code (must be valid Java identifiers) and debugger labels @@ -128,7 +128,7 @@ public class LoadNode extends MemOpNode { private Node ld( int idx ) { Node mem = mem(), ptr = ptr(); - return new LoadNode(_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole(); + return new LoadNode(_loc,_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole(); } private static boolean neverAlias( Node ptr1, Node ptr2 ) { diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/LogicalNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/LogicalNode.java index 094f2bf1..ccc1f6b5 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/LogicalNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/LogicalNode.java @@ -1,11 +1,15 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public abstract class LogicalNode extends Node { - public LogicalNode(Node lhs, Node rhs) { super(null, lhs, rhs); } + // Source location for late reported errors + Parser.Lexer _loc; + + public LogicalNode(Parser.Lexer loc, Node lhs, Node rhs) { super(null, lhs, rhs); _loc = loc; } abstract String op(); @Override @@ -16,10 +20,10 @@ public abstract class LogicalNode extends Node { } - @Override public String err() { + @Override public Parser.ParseException err() { if( in(1)._type.isHigh() || in(2)._type.isHigh() ) return null; - if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+op()+"' " + in(1)._type.glb(); - if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+op()+"' " + in(2)._type.glb(); + if( !(in(1)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(1)._type.glb(),_loc); + if( !(in(2)._type instanceof TypeInteger) ) return Parser.error("Cannot '"+op()+"' " + in(2)._type.glb(), _loc); return null; } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/LoopNode.java index f7f6f80d..b54403b7 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -9,7 +9,7 @@ import java.util.BitSet; import java.util.HashSet; public class LoopNode extends RegionNode { - public LoopNode( Node entry ) { super(null,entry,null); } + public LoopNode( Parser.Lexer loc, Node entry ) { super(loc,null,entry,null); } public CFGNode entry() { return cfg(1); } public CFGNode back () { return cfg(2); } @@ -68,7 +68,7 @@ public class LoopNode extends RegionNode { mem instanceof PhiNode pmem && pmem.region()==r && expr instanceof PhiNode prez && prez.region()==r ) ) { // Nope, insert an aligned exit layer - ctrl = new RegionNode(null,ctrl).init(); + ctrl = new RegionNode(_loc,null,ctrl).init(); mem = new PhiNode((RegionNode)ctrl,mem ).init(); expr = new PhiNode((RegionNode)ctrl,expr).init(); } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/MemOpNode.java index f0e66952..f9df2a40 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/MemOpNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/MemOpNode.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeMemPtr; @@ -14,15 +15,18 @@ public abstract class MemOpNode extends Node { // Declared type; not final because it might be a forward-reference // which will be lazily improved when the reference is declared. public Type _declaredType; + // Source location for late reported errors + public final Parser.Lexer _loc; - public MemOpNode(String name, int alias, Type glb, Node mem, Node ptr, Node off) { + public MemOpNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { super(null, mem, ptr, off); _name = name; _alias = alias; _declaredType = glb; + _loc = loc; } - public MemOpNode(String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value) { - this(name, alias, glb, mem, ptr, off); + public MemOpNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value) { + this(loc,name, alias, glb, mem, ptr, off); addDef(value); } @@ -44,7 +48,7 @@ public abstract class MemOpNode extends Node { int hash() { return _alias; } @Override - public String err() { + public Parser.ParseException err() { Type ptr = ptr()._type; // Already an error, but better error messages come from elsewhere if( ptr == Type.BOTTOM ) return null; @@ -52,6 +56,6 @@ public abstract class MemOpNode extends Node { // Better be a not-nil TMP if( ptr instanceof TypeMemPtr tmp && tmp.notNull() ) return null; - return "Might be null accessing '" + _name + "'"; + return Parser.error( "Might be null accessing '" + _name + "'",_loc); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter18/src/main/java/com/seaofnodes/simple/node/Node.java index b0b19311..4c6cce16 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/Node.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/Node.java @@ -589,9 +589,7 @@ public abstract class Node { // Swap inputs without letting either input go dead during the swap. Node swap12() { unlock(); // Hash is order dependent - Node tmp = in(1); - _inputs.set(1,in(2)); - _inputs.set(2,tmp); + _inputs.swap(1,2); return this; } @@ -618,7 +616,7 @@ public abstract class Node { Node copy(Node lhs, Node rhs) { throw Utils.TODO("Binary ops need to implement copy"); } // Report any post-optimize errors - public String err() { return null; } + public Parser.ParseException err() { return null; } /** * Used to allow repeating tests in the same JVM. This just resets the diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/OrNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/OrNode.java index 42bedc46..9b03dc5d 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/OrNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/OrNode.java @@ -1,12 +1,13 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class OrNode extends LogicalNode { - public OrNode(Node lhs, Node rhs) { super(lhs, rhs); } + public OrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Or"; } @Override public String op() { return "|"; } @@ -50,5 +51,5 @@ public class OrNode extends LogicalNode { return null; } - @Override Node copy(Node lhs, Node rhs) { return new OrNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new OrNode(_loc,lhs,rhs); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 35a60cd1..4dae6f36 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -1,8 +1,6 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.Ary; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.*; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -181,7 +179,7 @@ public class PhiNode extends Node { } @Override - public String err() { + public Parser.ParseException err() { if( _type != Type.BOTTOM ) return null; // BOTTOM means we mixed e.g. int and ptr @@ -199,6 +197,6 @@ public class PhiNode extends Node { tp |= t instanceof TypeMemPtr x; tn |= t==Type.NIL; } - return ReturnNode.mixerr(ti,tf,tp,tn); + return ReturnNode.mixerr(ti,tf,tp,tn, ((RegionNode)region())._loc); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/RegionNode.java index 5b3d90fd..5045b43b 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -10,8 +10,11 @@ import java.util.BitSet; import java.util.HashSet; public class RegionNode extends CFGNode { + // Source location for late discovered errors + public Parser.Lexer _loc; - public RegionNode(Node... nodes) { super(nodes); } + public RegionNode(Node... nodes) { this(null,nodes); } + public RegionNode(Parser.Lexer loc, Node... nodes) { super(nodes); _loc = loc; } @Override public String label() { return "Region"; } @@ -110,12 +113,9 @@ public class RegionNode extends CFGNode { } // True if last input is null - public boolean inProgress() { - return nIns()>1 && in(nIns()-1) == null; - } + public boolean inProgress() { return nIns()>1 && in(nIns()-1) == null; } // Never equal if inProgress - @Override boolean eq( Node n ) { - return !inProgress(); - } + @Override boolean eq( Node n ) { return !inProgress(); } + } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index adff7a89..3820a28d 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.*; @@ -17,10 +18,12 @@ import java.util.BitSet; public class ReturnNode extends CFGNode { public final FunNode _fun; + public final Parser.Lexer _loc; - public ReturnNode(Node ctrl, Node mem, Node data, FunNode fun) { + public ReturnNode(Node ctrl, Node mem, Node data, FunNode fun, Parser.Lexer loc) { super(ctrl, mem, data); _fun = fun; + _loc = loc; } public Node ctrl() { return in(0); } @@ -108,15 +111,15 @@ public class ReturnNode extends CFGNode { rez.addDef(null); } - @Override public String err() { - return mt==Type.BOTTOM ? mixerr(ti,tf,tp,tn) : null; + @Override public Parser.ParseException err() { + return mt==Type.BOTTOM ? mixerr(ti,tf,tp,tn,_loc) : null; } - static String mixerr( boolean ti, boolean tf, boolean tp, boolean tn ) { + static Parser.ParseException mixerr( boolean ti, boolean tf, boolean tp, boolean tn, Parser.Lexer loc ) { SB sb = new SB().p("No common type amongst "); if( ti ) sb.p("int and "); if( tf ) sb.p("f64 and "); if( tp || tn ) sb.p("reference and "); - return sb.unchar(5).toString(); + return Parser.error(sb.unchar(5).toString(),loc); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/SarNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/SarNode.java index a3e2a562..4a71ec06 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/SarNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/SarNode.java @@ -1,11 +1,12 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class SarNode extends LogicalNode { - public SarNode(Node lhs, Node rhs) { super(lhs, rhs); } + public SarNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Sar"; } @Override public String op() { return ">>"; } @@ -45,5 +46,5 @@ public class SarNode extends LogicalNode { return null; } - @Override Node copy(Node lhs, Node rhs) { return new SarNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new SarNode(_loc,lhs,rhs); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java index 8482b003..38c169fa 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java @@ -16,15 +16,17 @@ public class ScopeMinNode extends Node { /** The tracked fields are now complex enough to deserve a array-of-structs layout */ public static class Var { - public final int _idx; // index in containing scope public final String _name; // Declared name + public int _idx; // index in containing scope private Type _type; // Declared type - public final boolean _final; // Final field - public Var(int idx, String name, Type type, boolean xfinal) { + public boolean _final; // Final field + public Parser.Lexer _loc; // Source location + public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc) { _idx = idx; _name = name; _type = type; _final = xfinal; + _loc = loc; } public Type type() { if( !_type.isFRef() ) return _type; @@ -36,6 +38,19 @@ public class ScopeMinNode extends Node { Type t = type(); return t instanceof TypeMemPtr ? t : t.glb(); } + + // Forward reference variables (not types) can only be a bottom + // function pointer. + public boolean isFRef() { return type()==TypeFunPtr.BOT; } + + public boolean defFRef( Type type, boolean xfinal, Parser.Lexer loc ) { + assert isFRef(); + _type = type; + _final = xfinal; + _loc = loc; + return true; + } + @Override public String toString() { return _type.toString()+(_final ? " ": " !")+_name; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java index cf993561..2ad6e5b7 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java @@ -89,14 +89,36 @@ public class ScopeNode extends ScopeMinNode { _lexSize.push(_vars.size()); _inCons.push(inCon); } + + // Pop a lexical scope public void pop() { assert _lexSize._len==_inCons._len; + promote(); int n = _lexSize.pop(); _inCons.pop(); popUntil(n); _vars.setLen(n); } + + // Look for forward references in the last lexical scope and promote to the + // next outer lexical scope. At the last scope declare them an error. + public void promote() { + int n = _lexSize.last(); + for( int i=n; i<nIns(); i++ ) { + Var v = _vars.at(i); + if( !v.isFRef() ) continue; + if( _lexSize._len==1 ) + throw Parser.error("Undefined name '" + v._name + "'",v._loc); + _vars.swap(n,i); + _inputs.swap(n,i); + v._idx = n; + n++; + _lexSize.set(_lexSize._len-1,n); + } + } + + public boolean inCon() { return _inCons.last(); } // Find name in reverse, return an index into _vars or -1. Linear scan @@ -104,7 +126,7 @@ public class ScopeNode extends ScopeMinNode { // typically hits many dozens of variables. int find( String name ) { for( int i=_vars.size()-1; i>=0; i-- ) - if( _vars.get(i)._name.equals(name) ) + if( _vars.at(i)._name.equals(name) ) return i; return -1; } @@ -112,13 +134,23 @@ public class ScopeNode extends ScopeMinNode { /** * Create a new variable name in the current scope */ - public boolean define( String name, Type declaredType, boolean xfinal, Node init ) { + public boolean define( String name, Type declaredType, boolean xfinal, Node init, Parser.Lexer loc ) { assert _lexSize.isEmpty() || name.charAt(0)!='$' ; // Later scopes do not define memory if( _lexSize._len > 0 ) - for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) - if( _vars.get(i)._name.equals(name) ) - return false; // Double define - _vars.add(new Var(nIns(),name,declaredType,xfinal)); + for( int i=_vars.size()-1; i>=_lexSize.last(); i-- ) { + Var n = _vars.at(i); + if( n._name.equals(name) ) { + if( !n.isFRef() ) return false; // Double define + n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location + FRefNode fref = (FRefNode)in(n._idx); // Get forward ref + setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also + } + } + Var v = new Var(nIns(),name,declaredType,xfinal,loc); + _vars.add(v); + // Creating a forward reference + if( init==Parser.XCTRL ) + init = new FRefNode(v).peephole(); addDef(init); return true; } @@ -228,8 +260,8 @@ public class ScopeNode extends ScopeMinNode { * @param that The ScopeNode to be merged into this * @return A new node representing the merge point */ - public RegionNode mergeScopes(ScopeNode that) { - RegionNode r = ctrl(new RegionNode(null,ctrl(), that.ctrl()).keep()); + public RegionNode mergeScopes(ScopeNode that, Parser.Lexer loc) { + RegionNode r = ctrl(new RegionNode(loc,null,ctrl(), that.ctrl()).keep()); mem()._merge(that.mem(),r); this ._merge(that ,r); that.kill(); // Kill merged scope @@ -249,6 +281,20 @@ public class ScopeNode extends ScopeMinNode { } } + // Balance arms of an IF. Extra lonely defs are thrown: "if(pred) int x;". + // Forward refs are copied to the other side, "as if" they were there all along. + public void balanceIf( ScopeNode scope ) { + for( int i = nIns(); i < scope.nIns(); i++ ) { + ScopeMinNode.Var n = scope._vars.at(i); + if( n.isFRef() ) { // RHS has forward refs + _vars.add(n); // Copy to LHS + addDef(scope.in(i)); + } else + throw Parser.error("Cannot define a '"+n._name+"' on one arm of an if",n._loc); + } + } + + // peephole the backedge scope into this loop head scope // We set the second input to the phi from the back edge (i.e. loop body) public void endLoop(ScopeNode back, ScopeNode exit ) { @@ -319,7 +365,7 @@ public class ScopeNode extends ScopeMinNode { // Remove matching pred/cast pairs from this guarded region. - public void removeGuards( Node ctrl ) { + public ScopeNode removeGuards( Node ctrl ) { assert ctrl instanceof CFGNode; // 0,1 or 2 guards while( true ) { @@ -329,6 +375,7 @@ public class ScopeNode extends ScopeMinNode { g .unkill(); // Pop/kill cast _guards.pop().unkill(); // Pop/kill pred } + return this; } // If we find a guarded instance of pred, replace with the upcasted version 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 24dff5e2..21dbd503 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java @@ -6,7 +6,7 @@ import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class ShlNode extends LogicalNode { - public ShlNode(Node lhs, Node rhs) { super(lhs, rhs); } + public ShlNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Shl"; } @Override public String op() { return "<<"; } @@ -41,7 +41,7 @@ public class ShlNode extends LogicalNode { if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof TypeInteger c && c.isConstant() ) { long sum = c.value() << shl.value(); if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) - return new AddNode( new ShlNode(add.in(1),rhs).peephole(), Parser.con(sum) ); + return new AddNode(new ShlNode(_loc,add.in(1),rhs).peephole(), Parser.con(sum) ); } } @@ -49,5 +49,5 @@ public class ShlNode extends LogicalNode { return null; } - @Override Node copy(Node lhs, Node rhs) { return new ShlNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new ShlNode(_loc,lhs,rhs); } } 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 52d2971d..789fb8a8 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java @@ -1,11 +1,12 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; import java.util.BitSet; public class ShrNode extends LogicalNode { - public ShrNode(Node lhs, Node rhs) { super(lhs, rhs); } + public ShrNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Shr"; } @Override public String op() { return ">>>"; } @@ -41,5 +42,5 @@ public class ShrNode extends LogicalNode { return null; } - @Override Node copy(Node lhs, Node rhs) { return new ShrNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new ShrNode(_loc,lhs,rhs); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java index 8210a484..50cba348 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -18,7 +18,7 @@ public class StartNode extends LoopNode implements MultiNode { final Type _arg; - public StartNode(Type arg) { super(null); _arg = arg; _type = compute(); } + public StartNode(Type arg) { super(null,null); _arg = arg; _type = compute(); } @Override public String label() { return "Start"; } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 39a2dc2c..908b191b 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.type.*; @@ -20,8 +21,8 @@ public class StoreNode extends MemOpNode { * @param off The offset inside the struct base * @param value Value to be stored */ - public StoreNode(String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value, boolean init) { - super(name, alias, glb, mem, ptr, off, value); + public StoreNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off, Node value, boolean init) { + super(loc, name, alias, glb, mem, ptr, off, value); _init = init; } @@ -111,13 +112,13 @@ public class StoreNode extends MemOpNode { } @Override - public String err() { - String err = super.err(); + public Parser.ParseException err() { + Parser.ParseException err = super.err(); if( err != null ) return err; TypeMemPtr tmp = (TypeMemPtr)ptr()._type; if( tmp._obj.field(_name)._final ) - return "Cannot modify final field '"+_name+"'"; + return Parser.error("Cannot modify final field '"+_name+"'",_loc); Type t = val()._type; - return _init || t.isa(_declaredType) ? null : "Cannot store "+t+" into field "+_declaredType+" "+_name; + return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc); } } 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 a23e1304..37d7147d 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java @@ -1,12 +1,12 @@ package com.seaofnodes.simple.node; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeInteger; - import java.util.BitSet; public class XorNode extends LogicalNode { - public XorNode(Node lhs, Node rhs) { super(lhs, rhs); } + public XorNode(Parser.Lexer loc, Node lhs, Node rhs) { super(loc, lhs, rhs); } @Override public String label() { return "Xor"; } @Override public String op() { return "^"; } @@ -50,5 +50,5 @@ public class XorNode extends LogicalNode { return null; } - @Override Node copy(Node lhs, Node rhs) { return new XorNode(lhs,rhs); } + @Override Node copy(Node lhs, Node rhs) { return new XorNode(_loc,lhs,rhs); } } diff --git a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java index d5652f12..ace85edc 100644 --- a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java +++ b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java @@ -72,7 +72,7 @@ public class TypeFunPtr extends TypeNil { // RHS is NIL @Override public Type meet0() { return _nil==3 ? this : make((byte)3,_sig,_ret,_fidxs); } - @Override public TypeFunPtr glb() { return make(_nil,_sig,_ret,-1); } + @Override public TypeFunPtr glb() { return make(_nil,_sig,_ret,_fidxs); } @Override public boolean isConstant() { return _nil==2 && Long.bitCount(_fidxs)==1; } diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter01Test.java index 224a5f01..934eda76 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter01Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter01Test.java @@ -32,7 +32,7 @@ public class Chapter01Test { @Test public void testBad1() { try { - new CodeGen("ret").parse(); + new CodeGen("ret;").parse(); fail(); } catch( RuntimeException e ) { assertEquals("Undefined name 'ret'",e.getMessage()); @@ -74,7 +74,7 @@ public class Chapter01Test { @Test public void testBad6() { try { - new CodeGen("return100").parse(); + new CodeGen("return100;").parse(); fail(); } catch( RuntimeException e ) { assertEquals("Undefined name 'return100'",e.getMessage()); diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java index 3bea9b9a..f4db5624 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -142,10 +142,10 @@ return a; @Test public void testHalfDef() { try { - new Parser("if( arg==1 ) int b=2; return b;").parse(); + new CodeGen("if( arg==1 ) int b=2; return b;").parse(); fail(); } catch( RuntimeException e ) { - assertEquals("Cannot define a new name on one arm of an if",e.getMessage()); + assertEquals("Cannot define a 'b' on one arm of an if",e.getMessage()); } } @@ -162,10 +162,10 @@ return a; @Test public void testRegress1() { try { - new Parser("if(arg==2) int a=1; else int b=2; return a;").parse(); + new CodeGen("if(arg==2) int a=1; else int b=2; return a;").parse(); fail(); } catch( RuntimeException e ) { - assertEquals("Cannot define a new name on one arm of an if",e.getMessage()); + assertEquals("Cannot define a 'a' on one arm of an if",e.getMessage()); } } @@ -203,7 +203,7 @@ return a; @Test public void testKeyword3() { try { - new Parser("int a=1; ififif(arg)inta=2;return a;").parse(); + new CodeGen("int a=1; ififif(arg);a=2;return a;").parse(); fail(); } catch( RuntimeException e ) { assertEquals("Undefined name 'ififif'",e.getMessage()); diff --git a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 6fc2cc98..fc67e090 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -11,7 +11,7 @@ public class Chapter18Test { @Test public void testJig() { CodeGen code = new CodeGen(""" - return 0; +return 0; """); code.parse(true).opto().typeCheck().GCM(); assertEquals("return 0;", code._stop.toString()); @@ -76,4 +76,16 @@ return sq(2)+sq(3); assertEquals("Stop[ return (sq( 3)+sq( 2)); return (Parm_x(sq,int,3,2)*x); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 0)); } + + @Ignore @Test + public void testFcn2() { + CodeGen code = new CodeGen("val fact = { int x -> x < 1 ? 1 : x*fact(x-1); }; return fact(arg);"); + code.parse().opto(); + assertEquals("Stop[ return fact( arg); return Phi(Region,1,(Parm_x(fact,int,arg,(x-1))*fact( Sub))); ]", code._stop.toString()); + assertEquals( "1", Eval2.eval(code, 0)); + assertEquals( "1", Eval2.eval(code, 1)); + assertEquals( "2", Eval2.eval(code, 2)); + assertEquals( "6", Eval2.eval(code, 3)); + assertEquals("24", Eval2.eval(code, 4)); + } } 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 a6bef4d3..f57a0031 100644 --- a/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java +++ b/chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java @@ -1,6 +1,5 @@ package com.seaofnodes.simple.fuzzer; -import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Eval2; import com.seaofnodes.simple.CodeGen; @@ -89,7 +88,7 @@ public class Fuzzer { FuzzerUtils.parse(script, true); } catch (RuntimeException e2) { if (FuzzerUtils.isExceptionFromSameCause(e1, e2)) { - if (!valid || e1.getClass() == Parser.ParserException.class) return; + if (!valid || e1.getClass() == Parser.ParseException.class) return; } else { e1.addSuppressed(e2); } |
