summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Ary.java24
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java4
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Parser.java169
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/AndNode.java5
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java4
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java45
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/FunNode.java8
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/LoadNode.java8
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/LogicalNode.java12
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/LoopNode.java4
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/MemOpNode.java14
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/Node.java6
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/OrNode.java5
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/PhiNode.java8
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/RegionNode.java14
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java13
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/SarNode.java5
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java21
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java65
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ShlNode.java6
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ShrNode.java5
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/StoreNode.java13
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/XorNode.java6
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java2
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter01Test.java4
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter05Test.java10
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/Chapter18Test.java14
-rw-r--r--chapter18/src/test/java/com/seaofnodes/simple/fuzzer/Fuzzer.java3
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);
}