From ba4e72de9899cb57eb0756203241e2b0c23b4c19 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Wed, 1 Jan 2025 15:25:02 -0800 Subject: Add function forward refs Add FRefNode, making and defining a forward ref. Forward refs promote out of scopes instead of being an undefined error. Add location data to all errors, because introducing more "delayed errors"- forward references are not errors if they get eventually defined. Touches many Nodes. Fix FunNode idom/idepth. --- .../src/main/java/com/seaofnodes/simple/Ary.java | 24 ++- .../main/java/com/seaofnodes/simple/CodeGen.java | 4 +- .../main/java/com/seaofnodes/simple/Parser.java | 169 ++++++++++++--------- .../java/com/seaofnodes/simple/node/AndNode.java | 5 +- .../java/com/seaofnodes/simple/node/CFGNode.java | 4 +- .../java/com/seaofnodes/simple/node/FRefNode.java | 45 ++++++ .../java/com/seaofnodes/simple/node/FunNode.java | 8 +- .../java/com/seaofnodes/simple/node/LoadNode.java | 8 +- .../com/seaofnodes/simple/node/LogicalNode.java | 12 +- .../java/com/seaofnodes/simple/node/LoopNode.java | 4 +- .../java/com/seaofnodes/simple/node/MemOpNode.java | 14 +- .../main/java/com/seaofnodes/simple/node/Node.java | 6 +- .../java/com/seaofnodes/simple/node/OrNode.java | 5 +- .../java/com/seaofnodes/simple/node/PhiNode.java | 8 +- .../com/seaofnodes/simple/node/RegionNode.java | 14 +- .../com/seaofnodes/simple/node/ReturnNode.java | 13 +- .../java/com/seaofnodes/simple/node/SarNode.java | 5 +- .../com/seaofnodes/simple/node/ScopeMinNode.java | 21 ++- .../java/com/seaofnodes/simple/node/ScopeNode.java | 65 ++++++-- .../java/com/seaofnodes/simple/node/ShlNode.java | 6 +- .../java/com/seaofnodes/simple/node/ShrNode.java | 5 +- .../java/com/seaofnodes/simple/node/StartNode.java | 2 +- .../java/com/seaofnodes/simple/node/StoreNode.java | 13 +- .../java/com/seaofnodes/simple/node/XorNode.java | 6 +- .../com/seaofnodes/simple/type/TypeFunPtr.java | 2 +- 25 files changed, 312 insertions(+), 156 deletions(-) create mode 100644 chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java (limited to 'chapter18/src/main/java') 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 extends AbstractList implements List { 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 extends AbstractList implements List { 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 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>>")) 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 ts = new Ary<>(Type.class); Ary 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 args = new Ary(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. + *

+ * 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 solely to allow visitation. + *

+ * 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=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; } -- cgit v1.2.3