summaryrefslogtreecommitdiff
path: root/chapter18/src/main/java/com/seaofnodes
diff options
context:
space:
mode:
Diffstat (limited to 'chapter18/src/main/java/com/seaofnodes')
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java18
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java12
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java39
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Parser.java129
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java18
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/CallEndNode.java10
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/CallNode.java32
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/FunNode.java24
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ParmNode.java4
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ProjNode.java7
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java15
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java8
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java23
15 files changed, 230 insertions, 113 deletions
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java
index 51a6e78e..9588fb01 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/CodeGen.java
@@ -20,8 +20,10 @@ public class CodeGen {
public StopNode _stop ;
- // "Linker" mapping from constant TypeFunPtrs to heads of function
- public final HashMap<TypeFunPtr,FunNode> _linker = new HashMap<>();
+ // "Linker" mapping from constant TypeFunPtrs to heads of function. These
+ // TFPs all have exact single fidxs and their return is wiped to BOTTOM (so
+ // the return is not part of the match).
+ private final HashMap<TypeFunPtr,FunNode> _linker = new HashMap<>();
// Last created CodeGen as a global, for easier debugging prints
@@ -87,13 +89,23 @@ public class CodeGen {
return this;
}
- public CodeGen localSched() {
+ // Local (basic block) scheduler phase, a classic list scheduler
+ public CodeGen localSched() {
assert _phase == Phase.Schedule;
_phase = Phase.LocalSched;
ListScheduler.sched(this);
return this;
}
+ // Reverse from a constant function pointer to the IR function being called
+ public FunNode link( TypeFunPtr tfp ) {
+ assert tfp.isConstant();
+ return _linker.get(tfp.makeFrom(Type.BOTTOM));
+ }
+
+ public void link(FunNode fun) {
+ _linker.put(fun.sig().makeFrom(Type.BOTTOM),fun);
+ }
// Testing shortcuts
Node ctrl() { return _stop.ret().ctrl(); }
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index f1593a81..bacbd538 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -24,7 +24,7 @@ public abstract class GlobalCodeMotion {
private static void schedEarly(StartNode start) {
ArrayList<CFGNode> rpo = new ArrayList<>();
BitSet visit = new BitSet();
- _rpo_cfg(start, visit, rpo);
+ _rpo_cfg(null, start, visit, rpo);
// Reverse Post-Order on CFG
for( int j=rpo.size()-1; j>=0; j-- ) {
CFGNode cfg = rpo.get(j);
@@ -39,12 +39,14 @@ public abstract class GlobalCodeMotion {
}
// Post-Order of CFG
- private static void _rpo_cfg(Node n, BitSet visit, ArrayList<CFGNode> rpo) {
- if( !(n instanceof CFGNode cfg) || visit.get(cfg._nid) )
+ private static void _rpo_cfg(CFGNode def, Node use, BitSet visit, ArrayList<CFGNode> rpo) {
+ if( !(use instanceof CFGNode cfg) || visit.get(cfg._nid) )
return; // Been there, done that
+ if( def instanceof CallNode && cfg instanceof FunNode )
+ return; // Ignore linked function calls
visit.set(cfg._nid);
- for( Node use : cfg._outputs )
- _rpo_cfg(use,visit,rpo);
+ for( Node useuse : cfg._outputs )
+ _rpo_cfg(cfg,useuse,visit,rpo);
rpo.add(cfg);
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java b/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java
index adf67d8b..870a890b 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple;
import com.seaofnodes.simple.node.*;
+import com.seaofnodes.simple.type.TypeFunPtr;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.HashMap;
@@ -31,13 +32,6 @@ public class IRPrinter {
return sb.p("\n");
}
- private static SB nodeId(SB sb, Node n) {
- sb.p("%%%d".formatted(n._nid));
- if (n instanceof ProjNode proj) {
- sb.p(".").p(proj._idx);
- }
- return sb;
- }
public static String prettyPrint(Node node, int depth) {
return CodeGen.CODE._phase.ordinal() > CodeGen.Phase.Schedule.ordinal()
@@ -61,14 +55,24 @@ public class IRPrinter {
Node n = rpos.get(i);
if( n instanceof CFGNode || n.isMultiHead() ) {
if( !gap ) sb.p("\n"); // Blank before multihead
+ if( n instanceof FunNode fun ) {
+ TypeFunPtr sig = fun.sig();
+ sig.print(sb.p("--- ").p(sig._name==null ? "" : sig._name).p(" "),false).p("----------------------\n");
+ }
printLine( n, sb ); // Print head
while( --i >= 0 ) {
Node t = rpos.get(i);
if( !t.isMultiTail() ) { i++; break; }
printLine( t, sb );
}
- sb.p("\n"); // Blank after multitail
- gap = true;
+ if( n instanceof ReturnNode ret ) {
+ TypeFunPtr sig = ret.fun().sig();
+ sig.print(sb.p("--- ").p(sig._name==null ? "" : sig._name).p(" "),false).p("----------------------\n");
+ }
+ if( !(n instanceof CallNode) ) {
+ sb.p("\n"); // Blank after multitail
+ gap = true;
+ }
} else {
printLine( n, sb );
gap = false;
@@ -87,15 +91,20 @@ public class IRPrinter {
// First walk the CFG, then everything
if( n instanceof CFGNode ) {
for( Node use : n._outputs )
- if( use instanceof CFGNode && use.nOuts() >= 1 &&
- !(use._outputs.get( 0 ) instanceof LoopNode) )
+ // Follow CFG, not across call/function borders, and not around backedges
+ if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) &&
+ use.nOuts() >= 1 && !(use._outputs.get(0) instanceof LoopNode) )
postOrd(use, n, rpos,visit,bfs);
for( Node use : n._outputs )
- if( use instanceof CFGNode )
+ // Follow CFG, not across call/function borders
+ if( use instanceof CFGNode && !(n instanceof CallNode && use instanceof FunNode) )
postOrd(use,n,rpos,visit,bfs);
}
+ // Follow all outputs
for( Node use : n._outputs )
- if( use != null )
+ if( use != null &&
+ !(n instanceof CallNode && use instanceof FunNode) &&
+ (n instanceof FunNode || !(use instanceof ParmNode)) )
postOrd(use, n, rpos,visit,bfs);
// Post-order
rpos.add(n);
@@ -247,7 +256,9 @@ public class IRPrinter {
!(node instanceof LoopNode loop && loop.back()==def) &&
// Don't walk into or out of functions
!(node instanceof CallEndNode && def instanceof ReturnNode) &&
- !(node instanceof FunNode && def instanceof CallNode) )
+ !(node instanceof FunNode && def instanceof CallNode) &&
+ !(node instanceof ParmNode && !(def instanceof FunNode))
+ )
_walk(ds,ns,def,d-1);
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
index 79d5ce4a..49667ecc 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
@@ -150,32 +150,15 @@ public class Parser {
public StopNode parse() { return parse(false); }
public StopNode parse(boolean show) {
_xScopes.push(_scope);
- //// Enter a new scope for the initial control and arguments
- //_scope.push();
- //Node ctrl = new CProjNode(START, 0, ScopeNode.CTRL).peephole();
- //Node pmem = new ProjNode(START, 1, ScopeNode.MEM0).peephole();
- //Node arg = new ProjNode(START, 2, ScopeNode.ARG0).peephole();
- //ScopeMinNode mem = new ScopeMinNode();
- //mem.addDef(null);
- //mem.addDef(pmem);
_scope.define(ScopeNode.CTRL, Type.CONTROL , false, null);
_scope.define(ScopeNode.MEM0, TypeMem.BOT , false, null);
_scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null);
- //
- //// Call main
- //CallNode main = (CallNode)new CallNode(ctrl,pmem,arg,con(TypeFunPtr.MAIN)).peephole();
- //// Call End
- //CallEndNode cend = (CallEndNode)new CallEndNode(main).peephole();
- //STOP.addDef(new CProjNode(cend,0,ScopeNode.CTRL).peephole());
- //STOP.addDef(new ProjNode(cend,1,ScopeNode.MEM0).peephole());
- //STOP.addDef(new ProjNode(cend,2,ScopeNode.ARG0).peephole());
+
+ ctrl(XCTRL);
+ _scope.mem(new ScopeMinNode(false));
// Parse whole program, as-if function header "{ int arg -> body }"
- ReturnNode ret = parseFunctionBody("main",TypeFunPtr.MAIN,"arg");
- CodeGen.CODE._linker.put(TypeFunPtr.MAIN,ret.fun());
- //main.peephole();
- //if( cend.peephole() != null )
- // IterPeeps.addAll(cend._outputs);
+ ReturnNode ret = parseFunctionBody(TypeFunPtr.MAIN,"arg");
if( !_lexer.isEOF() ) throw _errorSyntax("unexpected");
@@ -194,9 +177,12 @@ public class Parser {
/**
* Parses a function body, assuming the header is parsed.
*/
- private ReturnNode parseFunctionBody(String name, TypeFunPtr sig, String... ids) {
- sig.setName(name);
- FunNode fun = _fun = (FunNode)peep(new FunNode(START,sig,name));
+ private ReturnNode parseFunctionBody(TypeFunPtr sig, String... ids) {
+ Node oldctrl = ctrl().keep();
+ Node oldmem = _scope.mem().keep();
+ FunNode oldfun = _fun;
+
+ FunNode fun = _fun = (FunNode)peep(new FunNode(START,sig));
// Build a multi-exit return point for all function returns
RegionNode r = new RegionNode(null,null);
@@ -232,11 +218,7 @@ public class Parser {
// Last expression is the return
if( ctrl()._type==Type.CONTROL )
- STOP.addReturn(ctrl(), _scope.mem().merge(), last, fun);
-
- // Function scope ends
- _scope.pop();
- _fun = null;
+ fun.addReturn(ctrl(), _scope.mem().merge(), last);
// Pop off the inProgress node on the multi-exit Region merge
assert r.inProgress();
@@ -249,7 +231,23 @@ public class Parser {
ret.setDef(1,rmem.peephole());
ret.setDef(2,rrez.peephole());
ret.setDef(0,r.peephole());
- return (ReturnNode)ret.peephole();
+ ret = (ReturnNode)ret.peephole();
+
+ // Function scope ends
+ _scope.pop();
+ _fun = oldfun;
+ // Reset control and memory to pre-function parsing days
+ 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;
}
/**
@@ -582,7 +580,7 @@ public class Parser {
var expr = require(parseAsgn(), ";");
// Need default memory, since it can be lazy, need to force
// a non-lazy Phi
- STOP.addReturn(ctrl(), _scope.mem().merge(), expr, _fun);
+ _fun.addReturn(ctrl(), _scope.mem().merge(), expr);
ctrl(XCTRL); // Kill control
return expr;
}
@@ -694,6 +692,8 @@ public class Parser {
throw error("a not-null/non-zero expression");
t = expr._type.glb();
}
+ if( t instanceof TypeFunPtr && expr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
+ tfp.setName(name);
} else {
if( inferType && !_scope.inCon() )
@@ -1039,7 +1039,7 @@ public class Parser {
if( matchx("null" ) ) return NIL;
if( match ("(" ) ) return require(parseAsgn(), ")");
if( matchx("new" ) ) return alloc();
- if( match ("{" ) ) return func();
+ if( match ("{" ) ) return require(func(),"}");
// Expect an identifier now
ScopeMinNode.Var n = requireLookupId("an identifier or expression");
Node rvalue = _scope.in(n);
@@ -1191,25 +1191,17 @@ public class Parser {
* expr ('.' IDENTIFIER)* [ = expr ]
* expr #
* expr ('[' expr ']')* = [ = expr ]
+ * expr '(' [args,]* ')'
* </pre>
*/
private Node parsePostfix(Node expr) {
- String name = null;
+ String name;
if( match(".") ) name = requireId();
else if( match("#") ) name = "#";
else if( match("[") ) name = "[]";
+ else if( match("(") ) return require(functionCall(expr),")");
else return expr; // No postfix
- // Happens when parsing known dead code, which often has other typing
- // issues. Since the code is dead, possibly due to inlining, lets not
- // spoil the user experience with error messages.
- //if( ctrl()._type==Type.XCONTROL ) {
- // if( expr.isUnused() ) expr.kill(); // Losing last use of expr
- // // Exit out via parsing the trailing expression
- // return matchOpx( '=', '=' ) ? parseAsgn() : parsePostfix( con( Type.TOP ) );
- //}
-
- //
if( expr._type==Type.NIL )
throw error("Accessing unknown field '" + name + "' from 'null'");
@@ -1298,7 +1290,7 @@ public class Parser {
}
/**
- * Parse a function; the leading '{' is already parsed.
+ * Parse a function body; the caller will parse the surrounding "{}"
*
* <pre>
* { [type arg,]* -> expr }
@@ -1306,7 +1298,6 @@ public class Parser {
* </pre>
*/
private Node func() {
- int old = pos(); // Record lexer position
Ary<Type> ts = new Ary<>(Type.class);
Ary<String> ids = new Ary<>(String.class);
while( true ) {
@@ -1320,7 +1311,53 @@ public class Parser {
require("->");
// Make a concrete function type, with a fidx
TypeFunPtr tfp = TypeFunPtr.makeFun(TypeTuple.make(ts.asAry()),Type.BOTTOM);
- return parseFunctionBody(null,tfp,ids.asAry());
+ ReturnNode ret = parseFunctionBody(tfp,ids.asAry());
+ return con(ret._fun.sig());
+ }
+
+ /**
+ * Parse function call arguments; caller will parse the surrounding "()"
+ * <pre>
+ * ( arg* )
+ * </pre>
+ */
+ private Node functionCall(Node expr) {
+ 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());
+ expr.keep(); // Keep while parsing args
+
+ Ary<Node> args = new Ary<Node>(Node.class);
+ args.push(null); // Space for ctrl,mem
+ args.push(null);
+ while( !peek(')') ) {
+ Node arg = parseAsgn();
+ if( arg==null ) break;
+ args.push(arg.keep());
+ if( !match(",") ) break;
+ }
+ // Control & memory after parsing args
+ args.set(0,ctrl().keep());
+ args.set(1,_scope.mem().merge().keep());
+ args.push(expr); // Function pointer
+ // Unkeep them all
+ for( Node arg : args )
+ arg.unkeep();
+ // Into the call
+ CallNode call = (CallNode)new CallNode(args.asAry()).peephole();
+
+ // Post-call setup
+ CallEndNode cend = (CallEndNode)new CallEndNode(call).peephole();
+ // Control from CallEnd
+ ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole());
+ // Memory from CallEnd
+ ScopeMinNode mem = new ScopeMinNode(true);
+ mem.addDef(null); // Alias#0
+ mem.addDef(new ProjNode(cend,1,ScopeNode.MEM0).peephole());
+ _scope.mem(mem);
+ // Call result
+ return new ProjNode(cend,2,null).peephole();
}
/**
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 78071920..a3bb1ae9 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -95,22 +95,23 @@ public abstract class CFGNode extends Node {
_ltree = stop._ltree = Parser.XCTRL._ltree = new LoopTree((StartNode)this);
_bltWalk(2,null,stop, new BitSet());
}
- private int _bltWalk(int pre, FunNode fun, StopNode stop, BitSet post) {
+ int _bltWalk( int pre, FunNode fun, StopNode stop, BitSet post ) {
// Pre-walked?
if( _pre!=0 ) return pre;
_pre = pre++;
// Pre-walk
for( Node use : _outputs )
- if( use instanceof CFGNode usecfg && !(usecfg instanceof XCtrlNode) )
+ if( use instanceof CFGNode usecfg && !skip(usecfg) )
pre = usecfg._bltWalk(pre,use instanceof FunNode fuse ? fuse : fun,stop,post);
// Post-order work: find innermost loop
LoopTree inner = null, ltree;
for( Node use : _outputs ) {
if( !(use instanceof CFGNode usecfg) ) continue;
- if( usecfg instanceof XCtrlNode ) continue;
- if( usecfg._type == Type.XCONTROL ) continue; // Dead, only possible if peeps disabled
- if( usecfg._type == TypeTuple.IF_NEITHER ) continue; // Dead again
+ if( skip(usecfg) ) continue;
+ if( usecfg._type == Type.XCONTROL || // Do not walk dead control
+ usecfg._type == TypeTuple.IF_NEITHER ) // Nor dead IFs
+ continue;
// Child visited but not post-visited?
if( !post.get(usecfg._nid) ) {
// Must be a backedge to a LoopNode then
@@ -143,4 +144,11 @@ public abstract class CFGNode extends Node {
post.set(_nid);
return pre;
}
+
+ 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);
+ }
+
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
index 31c245f9..578d7f87 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/CallEndNode.java
@@ -3,8 +3,7 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFunPtr;
+import com.seaofnodes.simple.type.*;
import java.util.BitSet;
/**
@@ -19,6 +18,7 @@ public class CallEndNode extends CFGNode implements MultiNode {
@Override
public String label() { return "CallEnd"; }
+ @Override public boolean isMultiHead() { return true; }
@Override public boolean blockHead() { return true; }
CallNode call() { return (CallNode)in(0); }
@@ -36,10 +36,8 @@ public class CallEndNode extends CFGNode implements MultiNode {
@Override
public Type compute() {
- for( Node n : _inputs )
- if( n!=null && n._type==Type.CONTROL )
- return Type.CONTROL;
- return Type.XCONTROL;
+ Type ret = call().fptr()._type instanceof TypeFunPtr tfp ? tfp.ret() : Type.BOTTOM;
+ return TypeTuple.make(call()._type,TypeMem.BOT,ret);
}
@Override
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/CallNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/CallNode.java
index 3ec1cd0c..b836188c 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/CallNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/CallNode.java
@@ -21,7 +21,7 @@ public class CallNode extends CFGNode {
String fname = null;
Node fptr = fptr();
if( fptr._type instanceof TypeFunPtr tfp && tfp.isConstant() )
- fname = CodeGen.CODE._linker.get(tfp)._name;
+ fname = tfp._name;
if( fname==null ) fptr._print0(sb,visited);
else sb.append(fname);
sb.append("( ");
@@ -36,14 +36,32 @@ public class CallNode extends CFGNode {
// Args mapped 1-to-1 on inputs
Node arg(int idx) { return in(idx); }
// args from input 2 to last
- Node fptr() { return _inputs.last(); }
+ public Node fptr() { return _inputs.last(); }
// Find the Call End from the Call
- CallEndNode cend() {
+ public CallEndNode cend() {
// Always in use slot 0
- return (CallEndNode)out(0);
+ if( nOuts()>0 && out(0) instanceof CallEndNode cend ) {
+ assert _cend()==cend;
+ return cend;
+ } else {
+ assert _cend()==null;
+ return null;
+ }
+ }
+ private CallEndNode _cend() {
+ CallEndNode cend=null;
+ for( Node n : _outputs )
+ if( n instanceof CallEndNode cend0 )
+ { assert cend == null; cend = cend0; }
+ return cend;
}
+ // Get the one control following; error to call with more than one e.g. an
+ // IfNode or other multi-way branch.
+ @Override public CFGNode uctrl() { return cend(); }
+
+
@Override
public Type compute() {
return ctrl()._type;
@@ -51,6 +69,9 @@ public class CallNode extends CFGNode {
@Override
public Node idealize() {
+ CallEndNode cend = cend();
+ if( cend==null ) return null; // Still building
+
// Link: call calls target function. Linking makes the target FunNode
// point to this Call, and all his Parms point to the call arguments;
// also the CallEnd points to the Return.
@@ -58,11 +79,12 @@ public class CallNode extends CFGNode {
// If fidxs is negative, then infinite unknown functions
long fidxs = tfp.fidxs();
if( fidxs > 0 ) {
+ // Wipe out the return which matching in the linker table
// Walk the (63 max) bits and link
for( ; fidxs!=0; fidxs = TypeFunPtr.nextFIDX(fidxs) ) {
int fidx = Long.numberOfTrailingZeros(fidxs);
TypeFunPtr tfp0 = tfp.makeFrom(fidx);
- FunNode fun = CodeGen.CODE._linker.get(tfp0);
+ FunNode fun = CodeGen.CODE.link(tfp0);
if( fun!=null && !fun._folding && !linked(fun) )
link(fun);
}
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 94d7eff7..c6d318bd 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.IterPeeps;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeFunPtr;
@@ -9,14 +10,13 @@ public class FunNode extends RegionNode {
// When set true, this Call/CallEnd/Fun/Return is being trivially inlined
boolean _folding;
- public final TypeFunPtr _sig; // Initial signature
- public String _name; // Debug label, optional
- private ReturnNode _ret; // Return pointer
+ private TypeFunPtr _sig; // Initial signature
+ private ReturnNode _ret; // Return pointer
- public FunNode( StartNode start, TypeFunPtr sig, String name ) { super(null,start); _sig=sig; _name=name; }
+ public FunNode( StartNode start, TypeFunPtr sig ) { super(null,start); _sig=sig; }
@Override
- public String label() { return _name; }
+ public String label() { return _sig._name; }
// Find the one CFG user from Fun. It's not always the Return, but always
// the Return *is* a CFG user of Fun.
@@ -28,9 +28,20 @@ public class FunNode extends RegionNode {
return null;
}
+ // Cannot create the Return and Fun at the same time; one has to be first.
+ // So setting the return requires a second step.
public void setRet(ReturnNode ret) { _ret=ret; }
ReturnNode ret() { assert _ret!=null; return _ret; }
+ // Signature can improve over time
+ public TypeFunPtr sig() { return _sig; }
+ void setSig( TypeFunPtr sig ) {
+ assert sig.isa(_sig);
+ if( _sig != sig )
+ IterPeeps.add(this);
+ _sig = sig;
+ }
+
@Override
public Type compute() {
// Only dead if no callers after SCCP
@@ -58,4 +69,7 @@ public class FunNode extends RegionNode {
public boolean unknownCallers() { return in(1) instanceof StartNode; }
@Override public boolean inProgress() { return unknownCallers(); }
+
+ // Add a new function exit point.
+ public void addReturn(Node ctrl, Node mem, Node rez) { _ret.addReturn(ctrl,mem,rez); }
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ParmNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ParmNode.java
index f139e4ca..784b9146 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/ParmNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ParmNode.java
@@ -22,9 +22,9 @@ public class ParmNode extends PhiNode {
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) {
- if( fun()._name.equals("main") && _label.equals("arg") )
+ if( "main".equals(fun().sig()._name) && _label.equals("arg") )
return sb.append("arg");
- sb.append(_label).append("(");
+ sb.append("Parm_").append(_label).append("(");
for( Node in : _inputs ) {
if (in == null) sb.append("____");
else in._print0(sb, visited);
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index 9519f5ec..ea211dd2 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -24,7 +24,12 @@ public class ProjNode extends Node implements MultiUse {
@Override public int idx() { return _idx; }
@Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_label); }
+ StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ if( _label != null ) return sb.append(_label);
+ if( in(0) instanceof CallEndNode cend && cend.call()!=null )
+ return cend.call()._print0(sb,visited);
+ return sb.append("LONELY PROJ");
+ }
@Override public CFGNode cfg0() {
return in(0) instanceof CFGNode cfg ? cfg : in(0).cfg0();
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 5232cfbb..adff7a89 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -38,6 +38,9 @@ public class ReturnNode extends CFGNode {
return sb.append(";");
}
+ // No one unique control follows; can be many call end sites
+ @Override public CFGNode uctrl() { return null; }
+
@Override
public Type compute() {
if( inProgress() ) return TypeTuple.RET; // In progress
@@ -46,6 +49,14 @@ public class ReturnNode extends CFGNode {
@Override public Node idealize() {
if( inProgress() ) return null;
+
+ // Upgrade signature based on return type
+ Type ret = expr()._type;
+ TypeFunPtr fcn = _fun.sig();
+ assert ret.isa(fcn.ret());
+ if( ret != fcn.ret() )
+ _fun.setSig(fcn.makeFrom(ret));
+
// If dead (cant be reached; infinite loop), kill the exit values
if( ctrl()._type==Type.XCONTROL &&
!(mem() instanceof ConstantNode && expr() instanceof ConstantNode) ) {
@@ -54,6 +65,7 @@ public class ReturnNode extends CFGNode {
setDef(2,top);
return this;
}
+
return null;
}
@@ -104,8 +116,7 @@ public class ReturnNode extends CFGNode {
SB sb = new SB().p("No common type amongst ");
if( ti ) sb.p("int and ");
if( tf ) sb.p("f64 and ");
- if( tp ) sb.p("reference and ");
- if( !tp && tn ) sb.p("nil and ");
+ if( tp || tn ) sb.p("reference and ");
return sb.unchar(5).toString();
}
}
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 ebf650fc..8482b003 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java
@@ -53,7 +53,7 @@ public class ScopeMinNode extends Node {
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) {
- sb.append("MEM[ ");
+ sb.append(_inProgress ? "Merge[" : "MEM[ ");
for( int j=2; j<nIns(); j++ ) {
sb.append(j);
sb.append(":");
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 008a910f..8210a484 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -37,7 +37,7 @@ public class StartNode extends LoopNode implements MultiNode {
// Find "main", its the start.
CFGNode C = null;
for( Node use : _outputs )
- if( use instanceof FunNode fun && fun._sig==TypeFunPtr.MAIN )
+ if( use instanceof FunNode fun && fun.sig().isa(TypeFunPtr.MAIN) )
{ assert C==null; C = fun; }
return C;
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java
index fb78f720..009296e8 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -60,12 +60,4 @@ public class StopNode extends CFGNode {
d = Math.max(d,((ReturnNode)ret).idepth()+1);
return _idepth=d;
}
-
- // Add a new function exit point. Validates Parsers function borders, then
- // hands off to the guaranteed return.
- public void addReturn(Node ctrl, Node mem, Node rez, FunNode fun) {
- ReturnNode ret = (ReturnNode)_inputs.last();
- assert ret.fun()==fun && ret.inProgress();
- ret.addReturn(ctrl,mem,rez);
- }
}
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 6acd6798..d5652f12 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -37,8 +37,9 @@ public class TypeFunPtr extends TypeNil {
static TypeFunPtr make( byte nil, TypeTuple sig, Type ret, long fidxs ) { return new TypeFunPtr(nil,sig,ret,fidxs).intern(); }
public static TypeFunPtr make( boolean nil, TypeTuple sig, Type ret ) { return make((byte)(nil ? 3 : 2),sig,ret,-1); }
- @Override TypeFunPtr makeFrom( byte nil ) { return nil==_nil ? this : make(nil,_sig,_ret,_fidxs); }
- public TypeFunPtr makeFrom( int fidx ) { return make(_nil,_sig,_ret,1L<<fidx); }
+ @Override TypeFunPtr makeFrom( byte nil ) { return nil ==_nil ? this : make( nil,_sig,_ret, _fidxs).setName(_name); }
+ public TypeFunPtr makeFrom( int fidx ) { return 1L<<fidx==_fidxs ? this : make(_nil,_sig,_ret,1L<<fidx ).setName(_name); }
+ public TypeFunPtr makeFrom( Type ret ) { return ret ==_ret ? this : make(_nil,_sig, ret, _fidxs).setName(_name); }
// Compute "function indices": FIDX
private static final HashMap<TypeTuple,Integer> FIDXS = new HashMap<>();
@@ -56,7 +57,7 @@ public class TypeFunPtr extends TypeNil {
public static TypeFunPtr BOT = make((byte)3,TypeTuple.BOT,Type.BOTTOM,-1);
public static TypeFunPtr TEST = make((byte)2,TypeTuple.TEST,TypeInteger.BOT,1);
public static TypeFunPtr TEST0 = make((byte)3,TypeTuple.TEST,TypeInteger.BOT,3);
- public static TypeFunPtr MAIN = makeFun(TypeTuple.MAIN,Type.BOTTOM);
+ public static TypeFunPtr MAIN = makeFun(TypeTuple.MAIN,Type.BOTTOM).setName("main");
public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(TEST0); ts.add(BOT); ts.add(MAIN); }
@Override
@@ -71,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() { throw Utils.TODO(); }
+ @Override public TypeFunPtr glb() { return make(_nil,_sig,_ret,-1); }
@Override public boolean isConstant() { return _nil==2 && Long.bitCount(_fidxs)==1; }
@@ -79,6 +80,7 @@ public class TypeFunPtr extends TypeNil {
public Type arg(int i) { return _sig._types[i]; }
public long fidxs() { return _fidxs; }
+ public Type ret() { return _ret; }
@Override
int hash() { return Utils.fold(_sig.hashCode() ^ _ret.hashCode() ^ _fidxs ^ super.hash()); }
@@ -89,19 +91,22 @@ public class TypeFunPtr extends TypeNil {
return _sig == ptr._sig && _ret == ptr._ret && _fidxs == ptr._fidxs && super.eq(ptr);
}
- public void setName(String name) {
+ public TypeFunPtr setName(String name) {
+ if( name==null ) return this;
assert _name==null || _name.equals(name);
assert _fidxs > 0 && Long.bitCount(_fidxs) == 1;
_name = name;
+ return this;
}
@Override public String str() { return print(new SB()).toString(); }
- @Override public SB print(SB sb) { return _print(sb,false); }
- @Override public SB gprint(SB sb) { return _print(sb,true ); }
+ @Override public SB print(SB sb) { return _print(sb,false,true); }
+ public SB print(SB sb, boolean n) { return _print(sb,false,n); }
+ @Override public SB gprint(SB sb) { return _print(sb,true ,true); }
private static SB _print(SB sb, boolean g, Type t) { return g ? t.gprint(sb) : t.print(sb); }
- private SB _print(SB sb, boolean g) {
+ private SB _print(SB sb, boolean g, boolean n) {
sb.p(x()).p("{ ");
- if( _name!=null ) sb.p(_name);
+ if( n && _name!=null ) sb.p(_name);
else {
for( Type t : _sig._types )
_print(sb,g,t).p(" ");