summaryrefslogtreecommitdiff
path: root/chapter12/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'chapter12/src/main/java/com')
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java271
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/IRPrinter.java7
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java4
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/Parser.java156
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/Utils.java7
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/AddFNode.java37
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/AddNode.java7
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java36
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java96
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/CProjNode.java62
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/ConstantNode.java1
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/DivFNode.java39
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/DivNode.java1
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/IfNode.java18
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/LoadNode.java18
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java63
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/MemOpNode.java7
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java30
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java1
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/MulFNode.java44
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/MulNode.java6
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java16
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java22
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/NewNode.java1
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/Node.java79
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/NotNode.java6
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/OutNode.java7
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java13
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/ProjNode.java18
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java29
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/ReturnNode.java9
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java21
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java18
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/StoreNode.java25
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/SubFNode.java37
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/SubNode.java3
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java25
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java14
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java8
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/Field.java2
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/Type.java11
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/TypeFloat.java101
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/TypeMem.java7
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java27
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/TypeStruct.java45
-rw-r--r--chapter12/src/main/java/com/seaofnodes/simple/type/TypeTuple.java3
46 files changed, 293 insertions, 1165 deletions
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
deleted file mode 100644
index 74b58e6b..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ /dev/null
@@ -1,271 +0,0 @@
- package com.seaofnodes.simple;
-
-import com.seaofnodes.simple.IterPeeps.WorkList;
-import com.seaofnodes.simple.node.*;
-import com.seaofnodes.simple.type.*;
-import java.util.*;
-
-public abstract class GlobalCodeMotion {
-
- // Arrange that the existing isCFG() Nodes form a valid CFG. The
- // Node.use(0) is always a block tail (either IfNode or head of the
- // following block). There are no unreachable infinite loops.
- public static void buildCFG( StopNode stop ) {
- schedEarly();
- Parser.SCHEDULED = true;
- schedLate(stop);
- }
-
- // ------------------------------------------------------------------------
- // Backwards walk on the CFG only, looking for unreachable code - which has
- // to be an infinite loop. Insert a bogus never-taken exit to Stop, so the
- // loop becomes reachable. Also, set loop nesting depth
- static void fixLoops(StopNode stop) {
- // Backwards walk from Stop, looking for unreachable code
- BitSet visit = new BitSet();
- HashSet<CFGNode> unreach = new HashSet<>();
- unreach.add(Parser.START);
- for( Node ret : stop._inputs )
- ((ReturnNode)ret).walkUnreach(visit,unreach);
- if( unreach.isEmpty() ) return;
-
- // Forwards walk from unreachable, looking for loops with no exit test.
- visit.clear();
- for( CFGNode cfg : unreach )
- walkInfinite(cfg,visit,stop);
- // Set loop depth on remaining graph
- unreach.clear();
- visit.clear();
- for( Node ret : stop._inputs )
- ((ReturnNode)ret).walkUnreach(visit,unreach);
- assert unreach.isEmpty();
- }
-
-
- // Forwards walk over previously unreachable, looking for loops with no
- // exit test.
- static private void walkInfinite( CFGNode n, BitSet visit, StopNode stop ) {
- if( visit.get(n._nid) ) return; // Been there, done that
- visit.set(n._nid);
- if( n instanceof LoopNode loop )
- loop.forceExit(stop);
- for( Node use : n._outputs )
- if( use instanceof CFGNode cfg )
- walkInfinite(cfg,visit,stop);
- }
-
- // ------------------------------------------------------------------------
- private static void schedEarly() {
- ArrayList<CFGNode> rpo = new ArrayList<>();
- BitSet visit = new BitSet();
- _rpo_cfg(Parser.START, visit, rpo);
- // Reverse Post-Order on CFG
- for( int j=rpo.size()-1; j>=0; j-- ) {
- CFGNode cfg = rpo.get(j);
- cfg.loopDepth();
- for( Node n : cfg._inputs )
- _schedEarly(n,visit);
- // In dead infinite loops, entire code blocks may be unreachable
- // from below. Reach down from the CFG to their Phis so their
- // inputs are scheduled too.
- if( cfg instanceof RegionNode ) {
- int len = cfg.nOuts();
- for( int i=0; i<len; i++ )
- if( cfg.out(i) instanceof PhiNode phi )
- _schedEarly(phi,visit);
- }
- }
- }
-
- // 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) )
- return; // Been there, done that
- visit.set(cfg._nid);
- for( Node use : cfg._outputs )
- _rpo_cfg(use,visit,rpo);
- rpo.add(cfg);
- }
-
- private static void _schedEarly(Node n, BitSet visit) {
- if( n==null || visit.get(n._nid) ) return; // Been there, done that
- visit.set(n._nid);
- // Schedule inputs first, except Phis: following their backedges would
- // enter a data cycle before its control has been scheduled.
- for( Node def : n._inputs )
- if( def!=null && !(def instanceof PhiNode) )
- _schedEarly(def,visit);
- // An existing edge 0 already supplies control (or a Phi/Proj binding).
- if( n.in(0)==null ) {
- // Schedule at deepest input
- CFGNode early = Parser.START; // Maximally early, lowest idepth
- for( int i=1; i<n.nIns(); i++ )
- if( n.in(i)!=null && n.in(i).cfg0().idepth() > early.idepth() )
- early = n.in(i).cfg0(); // Latest/deepest input
- n.setDef(0,early); // First place this can go
- }
- }
-
- // ------------------------------------------------------------------------
- private static void schedLate( StopNode stop) {
- CFGNode[] late = new CFGNode[Node.UID()];
- Node[] ns = new Node[Node.UID()];
- // Record Load NIDs at all their CFG block choices, then check against
- // Store block choices to force a Load above an anti-dependent Store.
- int[] anti = new int[Node.UID()];
- // Breadth-first scheduling
- breadth(stop,ns,late,anti);
-
- // Copy the best placement choice into the control slot
- for( int i=0; i<late.length; i++ )
- if( ns[i] != null && !(ns[i] instanceof ProjNode) )
- ns[i].setDef(0,late[i]);
- }
-
- private static void breadth(Node stop, Node[] ns, CFGNode[] late, int[] anti) {
- // Things on the worklist have some (but perhaps not all) uses done.
- WorkList<Node> work = new WorkList<>();
- work.push(stop);
- Node n;
- outer:
- while( (n = work.pop()) != null ) {
- assert late[n._nid]==null; // No double visit
- // These I know the late schedule of, and need to set early for loops
- if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0);
- else if( n instanceof PhiNode phi ) late[n._nid] = phi.region();
- // These nodes have a fixed late placement at their original control.
- else if( n instanceof ProjNode || n instanceof NewNode || n==Parser.ZERO ) late[n._nid] = n.cfg0();
- else {
-
- // All uses done?
- for( Node use : n._outputs )
- if( use!=null && late[use._nid]==null )
- continue outer; // Nope, await all uses done
-
- // Loads need their memory inputs' uses also done
- if( n instanceof LoadNode ld )
- for( Node memuse : ld.mem()._outputs )
- if( antiUse(ld,memuse) && late[memuse._nid]==null )
- continue outer;
-
- // All uses done, schedule
- _doSchedLate(n,ns,late,anti);
- }
-
- // A use just finished; reconsider its inputs and waiting loads,
- // even when the shared memory input was already scheduled.
- for( Node def : n._inputs ) {
- if( def==null ) continue;
- if( late[def._nid]==null ) work.push(def);
- for( Node out : def._outputs )
- if( out instanceof LoadNode ld && late[ld._nid]==null )
- work.push(ld);
- }
- if( n instanceof LoopNode loop )
- for( Node phi : loop._outputs )
- if( phi instanceof PhiNode && late[phi._nid]==null )
- work.push(phi);
- }
- }
-
- private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late, int[] anti) {
- // Walk uses, gathering the LCA (Least Common Ancestor) of uses
- CFGNode early = n.in(0) instanceof CFGNode cfg ? cfg : n.in(0).cfg0();
- assert early != null;
- CFGNode lca = null;
- for( Node use : n._outputs )
- if( use != null )
- lca = use_block(n,use, late).domLCA(lca,null);
-
- // Loads may need anti-dependencies, raising their LCA
- if( n instanceof LoadNode load )
- lca = find_anti_dep(lca,load,early,late,anti);
-
- // Walk up from the LCA to the early, looking for best place. This is
- // the lowest execution frequency, approximated by least loop depth and
- // deepest control flow.
- CFGNode best = lca;
- lca = lca.idom(); // Already found best for starting LCA
- for( ; lca != early.idom(); lca = lca.idom() )
- if( better(lca,best) )
- best = lca;
- assert !(best instanceof IfNode);
- ns [n._nid] = n;
- late[n._nid] = best;
- }
-
- // Block of use. Normally from late[] schedule, except for Phis, which go
- // to the matching Region input.
- private static CFGNode use_block(Node n, Node use, CFGNode[] late) {
- if( !(use instanceof PhiNode phi) )
- return late[use._nid];
- CFGNode found=null;
- for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==n )
- found = phi.region().cfg(i).domLCA(found,null); // Can be more than one matching input.
- assert found!=null;
- return found;
- }
-
-
- // Least loop depth first, then largest idepth
- private static boolean better( CFGNode lca, CFGNode best ) {
- return lca.loopDepth() < best.loopDepth() ||
- lca instanceof NeverNode ||
- lca.idepth() > best.idepth() ||
- best instanceof IfNode;
- }
-
- // Only a store or memory Phi covering this alias can constrain a load.
- // MemMerge packages slices without overwriting them.
- private static boolean antiUse(LoadNode load, Node use) {
- return switch( use ) {
- case StoreNode st -> st._alias==load._alias;
- case MemPhiNode phi -> phi._alias==load._alias;
- case BulkMemPhiNode phi -> !phi.isSplit(load._alias);
- default -> false;
- };
- }
-
- private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late, int[] anti) {
- // We could skip final-field loads here.
- // Walk LCA->early, flagging Load's block location choices
- for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() )
- anti[cfg._nid] = load._nid;
- // Walk load->mem uses, looking for Stores causing an anti-dep
- for( Node mem : load.mem()._outputs ) {
- if( !antiUse(load,mem) ) continue;
- switch( mem ) {
- case StoreNode st:
- lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti);
- break;
- case PhiNode phi:
- // Repeat anti-dep for matching Phi inputs.
- // No anti-dep edges but may raise the LCA.
- for( int i=1; i<phi.nIns(); i++ )
- if( phi.in(i)==load.mem() )
- lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null,anti);
- break;
- default: throw Utils.TODO();
- }
- }
- return lca;
- }
-
- //
- private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st, int[] anti ) {
- // Preserve the full store range for the earlier evaluator scheduler.
- // It places nodes independently of GCM and may hoist this store.
- for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
- // Store and Load overlap, need anti-dependence
- if( anti[stblk._nid]==load._nid ) {
- lca = stblk.domLCA(lca,null); // Raise Loads LCA
- if( lca == stblk && st != null && Utils.find(st._inputs,load) == -1 ) // And if something moved,
- st.addDef(load); // Add anti-dep as well
- return lca; // Cap this stores' anti-dep to here
- }
- }
- return lca;
- }
-
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/IRPrinter.java b/chapter12/src/main/java/com/seaofnodes/simple/IRPrinter.java
index 7e93e9da..d5ab15fe 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/IRPrinter.java
@@ -8,6 +8,7 @@ public final class IRPrinter extends IRAdapter<Node> {
private static final com.seaofnodes.print.IRPrinter<Node> PRINT =
new com.seaofnodes.print.IRPrinter<>(new IRPrinter());
@Override public String type(Node n) { return n._type==null ? "" : n._type.str(); }
+ @Override public int inputColumns() { return 3; }
@Override public String inputMark(Node n, Node def) { return n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " "; }
@Override public Kind kind(Node n) {
if( n instanceof StartNode ) return Kind.START;
@@ -17,15 +18,13 @@ public final class IRPrinter extends IRAdapter<Node> {
if( n instanceof PhiNode ) return Kind.PHI;
if( n instanceof ReturnNode ) return Kind.RETURN;
if( n instanceof ConstantNode ) return Kind.CONSTANT;
- if( n instanceof CProjNode ) return Kind.CPROJ;
- if( n instanceof ProjNode ) return n instanceof CFGNode ? Kind.CPROJ : Kind.PROJ;
- if( n instanceof CFGNode ) return Kind.CTRL;
+ if( n instanceof ProjNode ) return n.isCFG() ? Kind.CPROJ : Kind.PROJ;
+ if( n.isCFG() ) return Kind.CTRL;
if( n instanceof MultiNode ) return Kind.MULTI;
return Kind.DATA;
}
@Override public int index(Node n) {
if( n instanceof ProjNode p ) return p._idx;
- if( n instanceof CProjNode p ) return p._idx;
return n._nid;
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java b/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 65a5e64f..7075d526 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -98,10 +98,6 @@ public abstract class IterPeeps {
if( obs != null ) obs.after(n, x, true);
}
- // Break infinite loops, forcing a Never-branch to exit
- GlobalCodeMotion.fixLoops(stop);
-
- GlobalCodeMotion.buildCFG(stop);
return stop;
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/Parser.java b/chapter12/src/main/java/com/seaofnodes/simple/Parser.java
index 9c213165..0fa7ee0a 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/Parser.java
@@ -31,18 +31,12 @@ public class Parser {
*/
public static StartNode START;
- public static ConstantNode ZERO;
- public static XCtrlNode XCTRL;
-
public StopNode STOP;
// Field identities are independent of the memory graph and Start's tuple.
private final HashMap<String,Integer> _aliases = new HashMap<>();
private int _alias = 2;
- // Debugger Printing.
- public static boolean SCHEDULED; // True if debug printer can use schedule info
-
// The Lexer. Thin wrapper over a byte[] buffer with a cursor.
private final Lexer _lexer;
@@ -68,7 +62,6 @@ public class Parser {
add("continue");
add("else");
add("false");
- add("flt");
add("if");
add("int");
add("new");
@@ -89,7 +82,7 @@ public class Parser {
ScopeNode _continueScope;
ScopeNode _breakScope;
- // Mapping from a type name to a Type.
+ // Authoritative declarations, indexed by type name.
public static HashMap<String, Type> TYPES = new HashMap<>();
public Parser(String source, TypeInteger arg) {
@@ -98,15 +91,11 @@ public class Parser {
IterPeeps.reset();
TYPES.clear();
TYPES.put("int",TypeInteger.BOT);
- TYPES.put("flt",TypeFloat .BOT);
- SCHEDULED = false;
_lexer = new Lexer(source);
_scope = new ScopeNode();
_continueScope = _breakScope = null;
START = new StartNode(new Type[]{ Type.CONTROL, TypeMem.BOT, arg });
STOP = new StopNode(source);
- ZERO = new ConstantNode(TypeInteger.constant(0)).peephole().keep();
- XCTRL= new XCtrlNode().peephole().keep();
}
public Parser(String source) {
@@ -129,8 +118,8 @@ public class Parser {
_xScopes.push(_scope);
// Enter a new scope for the initial control and arguments
_scope.push();
- _scope.define(ScopeNode.CTRL, Type.CONTROL , new CProjNode(START, 0, ScopeNode.CTRL).peephole());
- _scope.define(ScopeNode.ARG0, TypeInteger.BOT, new ProjNode(START, 2, ScopeNode.ARG0).peephole());
+ _scope.define(ScopeNode.CTRL, Type.CONTROL , new ProjNode(START, 0, ScopeNode.CTRL).peephole());
+ _scope.define(ScopeNode.ARG0, TypeInteger.BOT, new ProjNode(START, 2, ScopeNode.ARG0).peephole());
_scope.define("$mem", TypeMem.BOT, new ProjNode(START, 1, "$mem").peephole());
parseBlock();
if( ctrl()._type==Type.CONTROL )
@@ -190,7 +179,7 @@ public class Parser {
* type IDENTIFIER ;
* </pre>
*/
- private Field parseField(String sname) {
+ private Field parseField() {
Type t = type();
if( t==null )
throw errorSyntax("Requires a field type, found '"+_lexer.getAnyNextToken()+"'");
@@ -198,20 +187,21 @@ public class Parser {
}
/**
- * Parse a struct declaration, and return the following statement.
+ * Parse a complete struct declaration.
* Only allowed in top level scope.
* Structs cannot be redefined.
*
- * @return The statement following the struct
+ * @return null
*/
private Node parseStruct() {
if (_xScopes.size() > 1) throw errorSyntax("struct declarations can only appear in top level scope");
String typeName = requireId();
- if ( TYPES.containsKey(typeName)) throw errorSyntax("struct '" + typeName + "' cannot be redefined");
+ Type t = TYPES.get(typeName);
+ if( t!=null && !(t instanceof TypeStruct ts && ts._fields==null) ) throw errorSyntax("struct '" + typeName + "' cannot be redefined");
ArrayList<Field> fields = new ArrayList<>();
require("{");
while (!peek('}') && !_lexer.isEOF()) {
- Field field = parseField(typeName);
+ Field field = parseField();
if (fields.contains(field)) throw errorSyntax("Field '" + field + "' already defined in struct '" + typeName + "'");
fields.add(field);
}
@@ -221,7 +211,7 @@ public class Parser {
TYPES.put(typeName, ts); // Insert the struct name in the collection of all struct names
_aliases.put(typeName,_alias);
_alias += ts._fields.length;
- return parseStatement();
+ return null;
}
/**
@@ -262,8 +252,8 @@ public class Parser {
// IfNode takes current control and predicate
Node ifNode = new IfNode(ctrl(), pred).peephole();
// Setup projection nodes
- Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
- Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole();
+ Node ifT = new ProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
+ Node ifF = new ProjNode(ifNode.unkeep(), 1, "False").peephole();
// Clone the body Scope to create the break/exit Scope which accounts for any
// side effects in the predicate. The break/exit Scope will be the final
@@ -309,7 +299,7 @@ public class Parser {
private ScopeNode jumpTo(ScopeNode toScope) {
ScopeNode cur = _scope.dup();
- ctrl(XCTRL); // Kill current scope
+ ctrl(new ConstantNode(Type.XCONTROL).peephole()); // Kill current scope
// Prune nested lexical scopes that have depth > than the loop head
// We use _breakScope as a proxy for the loop head scope to obtain the depth
while( cur._scopes.size() > _breakScope._scopes.size() )
@@ -345,8 +335,8 @@ public class Parser {
// IfNode takes current control and predicate
Node ifNode = new IfNode(ctrl(), pred).peephole();
// Setup projection nodes
- Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
- Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep();
+ Node ifT = new ProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
+ Node ifF = new ProjNode(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();
@@ -363,8 +353,8 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
ifF.unkeep(); // fScope already owns the false control
+ _scope.upcast(ifF,pred,true); // Up-cast predicate, also without an else
if (matchx("else")) {
- _scope.upcast(ifF,pred,true); // Up-cast predicate
parseStatement();
fScope = _scope;
}
@@ -393,7 +383,7 @@ public class Parser {
private Node parseReturn() {
var expr = require(parseExpression(), ";");
Node ret = STOP.addReturn(new ReturnNode(ctrl(), expr, _scope).peephole());
- ctrl(XCTRL); // Kill control
+ ctrl(new ConstantNode(Type.XCONTROL).peephole()); // Kill control
return ret;
}
@@ -436,12 +426,13 @@ public class Parser {
throw error("Undefined name '" + name + "'");
t = _scope.lookupDeclaredType(name);
}
- // Auto-widen int to float
- if( expr._type instanceof TypeInteger && t instanceof TypeFloat )
- expr = new ToFloatNode(expr).peephole();
+ // Auto-deepen forward ref types
+ Type e = expr._type;
+ if( e instanceof TypeMemPtr tmp && tmp._obj._fields==null )
+ e = tmp.make_from((TypeStruct)TYPES.get(tmp._obj._name));
// Type is sane
- if( !expr._type.isa(t) )
- throw error("Type " + expr._type.str() + " is not of declared type " + t.str());
+ if( !e.isa(t) )
+ throw error("Type " + e.str() + " is not of declared type " + t.str());
return _scope.update(name,expr);
}
@@ -450,16 +441,22 @@ public class Parser {
int old = _lexer._position;
String tname = _lexer.matchId();
if( tname==null ) return null;
+ boolean nullable = match("?");
Type t = TYPES.get(tname);
+ // Assume a forward-reference type
if( t == null ) {
- // Not a type; unwind the parse
- _lexer._position = old;
- return null;
+ int old2 = _lexer._position;
+ String id = _lexer.matchId();
+ if( id==null ) {
+ _lexer._position = old;
+ return null;
+ }
+ TYPES.put(tname,t = TypeStruct.make(tname));
+ _lexer._position = old2; // Reparse ID in caller
}
- return t instanceof TypeStruct obj ? TypeMemPtr.make(obj,match("?")) : t;
+ return t instanceof TypeStruct obj ? TypeMemPtr.make(obj,nullable) : t;
}
-
/**
* Parse an expression of the form:
*
@@ -493,7 +490,7 @@ public class Parser {
else break;
// Peepholes can fire, but lhs is already "hooked", kept alive
lhs.setDef(idx,parseAddition());
- lhs = lhs.widen().peephole();
+ lhs = lhs.peephole();
if( negate ) // Extra negate for !=
lhs = new NotNode(lhs).peephole();
}
@@ -516,7 +513,7 @@ public class Parser {
else if( match("-") ) lhs = new SubNode(lhs,null);
else break;
lhs.setDef(2,parseMultiplication());
- lhs = lhs.widen().peephole();
+ lhs = lhs.peephole();
}
return lhs;
}
@@ -530,14 +527,14 @@ public class Parser {
* @return a multiply expression {@link Node}, never {@code null}
*/
private Node parseMultiplication() {
- var lhs = parseUnary(); boolean mul;
+ var lhs = parseUnary();
while( true ) {
if( false ) ;
else if( match("*") ) lhs = new MulNode(lhs,null);
else if( match("/") ) lhs = new DivNode(lhs,null);
else break;
lhs.setDef(2,parseUnary());
- lhs = lhs.widen().peephole();
+ lhs = lhs.peephole();
}
return lhs;
}
@@ -551,7 +548,7 @@ public class Parser {
* @return a unary expression {@link Node}, never {@code null}
*/
private Node parseUnary() {
- if (match("-")) return new MinusNode(parseUnary()).widen().peephole();
+ if (match("-")) return new MinusNode(parseUnary()).peephole();
if (match("!")) return new NotNode(parseUnary()).peephole();
return parsePostfix(parsePrimary());
}
@@ -565,15 +562,16 @@ public class Parser {
* @return a primary {@link Node}, never {@code null}
*/
private Node parsePrimary() {
- if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral();
+ if( _lexer.isNumber(_lexer.peek()) ) return parseIntegerLiteral();
if( match("(") ) return require(parseExpression(), ")");
if( matchx("true" ) ) return new ConstantNode(TypeInteger.constant(1)).peephole();
- if( matchx("false") ) return ZERO;
+ if( matchx("false") ) return new ConstantNode(TypeInteger.ZERO).peephole();
if( matchx("null" ) ) return new ConstantNode(TypeMemPtr.NULLPTR).peephole();
if( matchx("new") ) {
String structName = requireId();
Type t = TYPES.get(structName);
- if( t == null || !(t instanceof TypeStruct obj) ) throw errorSyntax("Unknown struct type '" + structName + "'");
+ if( !(t instanceof TypeStruct obj) || obj._fields==null )
+ throw error("Unknown struct type '" + structName + "'");
return newStruct(obj);
}
String name = _lexer.matchId();
@@ -590,7 +588,7 @@ public class Parser {
Node n = new NewNode(TypeMemPtr.make(obj), ctrl()).peephole().keep();
int alias = _aliases.get(obj._name);
for( Field field : obj._fields ) {
- store(field._fname,alias,n,ZERO);
+ store(field._fname,alias,field._type,n,new ConstantNode(field._type.makeInit()).peephole(),true);
alias++;
}
return n.unkeep();
@@ -600,9 +598,9 @@ public class Parser {
private Node mem() { return _scope.lookup("$mem"); }
private Node mem(Node n) { return _scope.update("$mem",n); }
- private void store(String name, int alias, Node ptr, Node val) {
+ private void store(String name, int alias, Type glb, Node ptr, Node val, boolean init) {
Node prior = mem().keep();
- Node st = new StoreNode(name,alias,ctrl(),prior,ptr,val).peephole();
+ Node st = new StoreNode(name,alias,glb,null,prior,ptr,val,init).peephole();
mem(new MemMergeNode(prior,alias,st).peephole());
prior.unkeep();
}
@@ -623,7 +621,9 @@ public class Parser {
String name = requireId().intern();
if( expr._type == TypeMemPtr.TOP ) throw error("Accessing field '" + name + "' from null");
- int idx = ptr._obj==null ? -1 : ptr._obj.find(name);
+ if( ptr._obj == null ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+ TypeStruct base = (TypeStruct)TYPES.get(ptr._obj._name);
+ int idx = base==null ? -1 : base.find(name);
if( idx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
int alias = _aliases.get(ptr._obj._name)+idx;
@@ -632,13 +632,14 @@ public class Parser {
if( peek('=') ) _lexer._position--;
else {
Node val = parseExpression();
- store(name,alias,expr,val);
+ Type glb = base._fields[idx]._type;
+ store(name,alias,glb,expr,val,false);
return expr; // "obj.a = expr" returns the expression while updating memory
}
}
- Type declaredType = ptr._obj._fields[idx]._type;
- return parsePostfix(new LoadNode(name, alias, declaredType, mem(), expr).peephole());
+ Type declaredType = base._fields[idx]._type;
+ return parsePostfix(new LoadNode(name, alias, declaredType.glb(), mem(), expr).peephole());
}
/**
@@ -646,10 +647,9 @@ public class Parser {
*
* <pre>
* integerLiteral: [1-9][0-9]* | [0]
- * floatLiteral: [1-9][0-9]* | [0]
* </pre>
*/
- private ConstantNode parseLiteral() {
+ private ConstantNode parseIntegerLiteral() {
return (ConstantNode) new ConstantNode(_lexer.parseNumber()).peephole();
}
@@ -744,16 +744,7 @@ public class Parser {
* Return the next non-white-space character
*/
private void skipWhiteSpace() {
- while( true ) {
- if( isWhiteSpace() ) _position++;
- // Skip // to end of line
- else if( _position+2 < _input.length &&
- _input[_position ] == '/' &&
- _input[_position+1] == '/') {
- _position += 2;
- while( !isEOF() && _input[_position] != '\n' ) _position++;
- } else break;
- }
+ while (isWhiteSpace()) _position++;
}
@@ -804,37 +795,19 @@ public class Parser {
return String.valueOf(peek());
}
-
+ boolean isNumber() {return isNumber(peek());}
boolean isNumber(char ch) {return Character.isDigit(ch);}
- // Return a constant Type, either TypeInteger or TypeFloat
private Type parseNumber() {
- int old = _position;
- int len = isLongOrDouble();
- if( len > 0 ) {
- if( len > 1 && _input[old]=='0' )
- throw error("Syntax error: integer values cannot start with '0'");
- return TypeInteger.constant(Long.parseLong(new String(_input,old,len)));
- }
- return TypeFloat.constant(Double.parseDouble(new String(_input,old,-len)));
+ String snum = parseNumberString();
+ if (snum.length() > 1 && snum.charAt(0) == '0')
+ throw error("Syntax error: integer values cannot start with '0'");
+ return TypeInteger.constant(Long.parseLong(snum));
}
private String parseNumberString() {
- int old = _position;
- int len = Math.abs(isLongOrDouble());
- _position += len;
- return new String(_input,old,len);
- }
-
- // Return +len that ends a long
- // Return -len that ends a double
- private int isLongOrDouble() {
- int old = _position;
- char c;
- while( Character.isDigit(c=nextChar()) ) ;
- if( !(c=='e' || c=='.') )
- return --_position - old;
- while( Character.isDigit(c=nextChar()) || c=='e' || c=='.' ) ;
- return -(--_position - old);
+ int start = _position;
+ while (isNumber(nextChar())) ;
+ return new String(_input, start, --_position - start);
}
// First letter of an identifier
@@ -853,9 +826,8 @@ public class Parser {
return new String(_input, start, --_position - start);
}
- //
private boolean isPunctuation(char ch) {
- return "=;[]<>()+-/*".indexOf(ch) != -1;
+ return "=;[]<>(){}+-/*!".indexOf(ch) != -1;
}
private String parsePunctuation() {
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/Utils.java b/chapter12/src/main/java/com/seaofnodes/simple/Utils.java
index f3ac25b1..4f2af769 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/Utils.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/Utils.java
@@ -55,4 +55,11 @@ public class Utils {
// Fold a 64bit hash into 32 bits
public static int fold( long x ) { return (int)((x>>32) ^ x); }
+
+ public static boolean eq(String s0, String s1) {
+ if( s0==s1 ) return true;
+ assert s0==s0.intern();
+ assert s1==s1.intern();
+ return false;
+ }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/AddFNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/AddFNode.java
deleted file mode 100644
index fd713156..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/AddFNode.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.seaofnodes.simple.node;
-
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-
-
-public class AddFNode extends Node {
- public AddFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "AddF"; }
-
- @Override protected String format() { return "(%1+%2)"; }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0 &&
- in(2)._type instanceof TypeFloat i1) {
- if (i0.isConstant() && i1.isConstant())
- return TypeFloat.constant(i0.value()+i1.value());
- }
- return in(1)._type.meet(in(2)._type);
- }
-
- @Override
- public Node idealize() {
- Node lhs = in(1);
- Type t2 = in(2)._type;
-
- // Add of 0.
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
- return lhs;
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new AddFNode(lhs,rhs); }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/AddNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 01aa209f..e993a20e 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -103,7 +103,6 @@ public class AddNode extends Node {
static Node phiCon(Node op, boolean rotate) {
Node lhs = op.in(1);
Node rhs = op.in(2);
- if( rhs._type==TypeInteger.TOP ) return null;
// LHS is either a Phi of constants, or another op with Phi of constants
PhiNode lphi = pcon(lhs,op);
if( rotate && lphi==null && lhs.nIns() > 2 ) {
@@ -169,5 +168,9 @@ public class AddNode extends Node {
}
@Override Node copy(Node lhs, Node rhs) { return new AddNode(lhs,rhs); }
- @Override Node copyF() { return new AddFNode(null,null); }
+ @Override public String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
+ return null;
+ }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java
index e515a5f1..63452dba 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/BoolNode.java
@@ -2,11 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.type.TypeFloat;
import com.seaofnodes.simple.type.TypeMemPtr;
@@ -27,21 +24,19 @@ abstract public class BoolNode extends Node {
@Override
public Type compute() {
- if( in(1)._type instanceof TypeInteger i0 &&
- in(2)._type instanceof TypeInteger i1 ) {
- if (i0.isConstant() && i1.isConstant())
- return TypeInteger.constant(doOp(i0.value(), i1.value()) ? 1 : 0);
- }
- if( in(1)._type instanceof TypeFloat i0 &&
- in(2)._type instanceof TypeFloat i1 ) {
+ Type t1 = in(1)._type, t2 = in(2)._type;
+ if( t1==Type.TOP || t1==TypeInteger.TOP ||
+ t2==Type.TOP || t2==TypeInteger.TOP )
+ return TypeInteger.TOP;
+ if( t1 instanceof TypeInteger i0 &&
+ t2 instanceof TypeInteger i1 ) {
if (i0.isConstant() && i1.isConstant())
return TypeInteger.constant(doOp(i0.value(), i1.value()) ? 1 : 0);
}
return TypeInteger.BOT;
}
- boolean doOp(long lhs, long rhs) { throw Utils.TODO(); }
- boolean doOp(double lhs, double rhs) { throw Utils.TODO(); }
+ abstract boolean doOp(long lhs, long rhs);
@Override
public Node idealize() {
@@ -53,9 +48,11 @@ abstract public class BoolNode extends Node {
if( this instanceof EQ ) {
if( !(in(2) instanceof ConstantNode) ) {
// con==noncon becomes noncon==con
- if( in(1) instanceof ConstantNode || in(1)._nid > in(2)._nid )
+ if( in(1) instanceof ConstantNode )
+ return new EQ(in(2),in(1));
// Equals sorts by NID otherwise: non.high == non.low becomes non.low == non.high
- return in(1)._type instanceof TypeFloat ? new EQF(in(2),in(1)) : new EQ(in(2),in(1));
+ else if( in(1)._nid > in(2)._nid )
+ return new EQ(in(2),in(1));
}
// Equals X==0 becomes a !X
if( (in(2)._type == TypeInteger.ZERO || in(2)._type == TypeMemPtr.NULLPTR) )
@@ -71,12 +68,7 @@ abstract public class BoolNode extends Node {
return null;
}
- public static class EQ extends BoolNode { public EQ(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "=="; } boolean doOp(long lhs, long rhs) { return lhs == rhs; } Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); } Node copyF() { return new EQF(null,null); } }
- public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } boolean doOp(long lhs, long rhs) { return lhs < rhs; } Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); } Node copyF() { return new LTF(null,null); }}
- public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } boolean doOp(long lhs, long rhs) { return lhs <= rhs; } Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); } Node copyF() { return new LEF(null,null); } }
-
- public static class EQF extends EQ { public EQF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs == rhs; } Node copyF() { return null; } }
- public static class LTF extends LT { public LTF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs < rhs; } Node copyF() { return null; } }
- public static class LEF extends LE { public LEF(Node lhs, Node rhs) { super(lhs,rhs); } boolean doOp(double lhs, double rhs) { return lhs <= rhs; } Node copyF() { return null; } }
-
+ public static class EQ extends BoolNode { public EQ(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "=="; } boolean doOp(long lhs, long rhs) { return lhs == rhs; } Node copy(Node lhs, Node rhs) { return new EQ(lhs,rhs); } }
+ public static class LT extends BoolNode { public LT(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<" ; } boolean doOp(long lhs, long rhs) { return lhs < rhs; } Node copy(Node lhs, Node rhs) { return new LT(lhs,rhs); } }
+ public static class LE extends BoolNode { public LE(Node lhs, Node rhs) { super(lhs,rhs); } String op() { return "<="; } boolean doOp(long lhs, long rhs) { return lhs <= rhs; } Node copy(Node lhs, Node rhs) { return new LE(lhs,rhs); } }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java
deleted file mode 100644
index c690a83c..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/CFGNode.java
+++ /dev/null
@@ -1,96 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.*;
-import com.seaofnodes.simple.Utils;
-
-import java.util.BitSet;
-import java.util.HashSet;
-import java.util.HashMap;
-
-/** Control Flow Graph Nodes
- * <p>
- * CFG nodes have a immediate dominator depth (idepth) and a loop nesting
- * depth(loop_depth).
- * <p>
- * idepth is computed lazily upon first request, and is valid even in the
- * Parser, and is used by peepholes during parsing and afterward.
- * <p>
- * loop_depth is computed after optimization as part of scheduling.
- *
- */
-public abstract class CFGNode extends Node {
-
- public CFGNode(Node... nodes) { super(nodes); }
-
- @Override public boolean isCFG() { return true; }
-
- public CFGNode cfg(int idx) { return (CFGNode)in(idx); }
-
- // Block head is Start, Region, CProj, but not e.g. If, Return, Stop
- public boolean blockHead() { return false; }
-
- // Should be exactly 1 tail from a block head
- public CFGNode blockTail() {
- assert blockHead();
- for( Node n : _outputs )
- if( n instanceof CFGNode cfg )
- return cfg;
- return null;
- }
-
- // ------------------------------------------------------------------------
- /**
- * Immediate dominator tree depth, used to approximate a real IDOM during
- * parsing where we do not have the whole program, and also peepholes
- * change the CFG incrementally.
- * <p>
- * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>}
- */
- public char _idepth;
- public int idepth() { return _idepth!=0 ? _idepth : cacheIDepth(idom().idepth()+1); }
- // Zero depth means uncached. Check before narrowing so overflow cannot wrap.
- final int cacheIDepth(int depth) {
- assert 0 <= depth && depth <= Character.MAX_VALUE : "Dominator depth exceeds 65535";
- return _idepth = (char)depth;
- }
-
-
- // Return the immediate dominator of this Node and compute dom tree depth.
- public CFGNode idom(Node dep) { return cfg(0); }
- public final CFGNode idom() { return idom(null); }
-
- // Return the LCA of two idoms
- public CFGNode domLCA(CFGNode rhs, Node dep) {
- if( rhs==null ) return this;
- CFGNode lhs = this;
- while( lhs != rhs ) {
- if( lhs==null || rhs==null ) return null;
- int comp = lhs.idepth() - rhs.idepth();
- if( comp >= 0 ) lhs = (dep==null ? lhs : (CFGNode)lhs.addDep(dep)).idom();
- if( comp <= 0 ) rhs = (dep==null ? rhs : (CFGNode)rhs.addDep(dep)).idom();
- }
- return lhs;
- }
-
- // Loop nesting depth
- public int _loopDepth;
- public int loopDepth() { return _loopDepth==0 ? (_loopDepth = cfg(0).loopDepth()) : _loopDepth; }
-
- // ------------------------------------------------------------------------
- // Support routines for Global Code Motion
-
- // Tik-tok recursion pattern. This method is final, and every caller does
- // this work.
- public final void walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) {
- if( visit.get(_nid) ) return;
- visit.set(_nid);
- _walkUnreach(visit,unreach);
- unreach.remove(this); // Since we reached here... Node was not unreachable
- }
-
- // Tik-tok recursion pattern; not-final; callers override this.
- void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) {
- cfg(0).walkUnreach(visit,unreach);
- }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/CProjNode.java
deleted file mode 100644
index ff5d1a76..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.print.ExprPrinter;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeMem;
-import com.seaofnodes.simple.type.TypeTuple;
-
-public class CProjNode extends CFGNode {
-
- // Which slice of the incoming multipart value
- public final int _idx;
-
- // Debugging label
- public final String _label;
-
- public CProjNode(Node ctrl, int idx, String label) {
- super(ctrl);
- _idx = idx;
- _label = label;
- }
-
- @Override public String label() { return _label; }
-
- @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p(_label); }
-
- @Override public boolean isMultiTail() { return in(0).isMultiHead(); }
- @Override public boolean blockHead() { return true; }
-
- public CFGNode ctrl() { return cfg(0); }
-
- @Override
- public Type compute() {
- Type t = ctrl()._type;
- return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM;
- }
-
- @Override
- public Node idealize() {
- if ( ctrl() instanceof IfNode iff ) {
- if ( iff._type instanceof TypeTuple tt && tt._types[1 - _idx] == Type.XCONTROL )
- return iff.in(0); // We become our input control
-
- // Flip a negating if-test, to remove the not
- if( iff.pred().addDep(this) instanceof NotNode not )
- return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True");
- }
- return null;
- }
-
- @Override
- public Node getBlockStart() {
- return ctrl() instanceof IfNode ? this : ctrl().getBlockStart();
- }
- @Override
- boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
-
- @Override
- int hash() { return _idx; }
-
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index 9ecd1152..426736ed 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -38,7 +38,6 @@ public class ConstantNode extends Node {
}
@Override public boolean isMultiTail() { return true; }
- // The special forever-zero is pinned, all others can sink
@Override
public Type compute() { return _con; }
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/DivFNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/DivFNode.java
deleted file mode 100644
index a3f4318d..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/DivFNode.java
+++ /dev/null
@@ -1,39 +0,0 @@
-package com.seaofnodes.simple.node;
-
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-
-
-public class DivFNode extends Node {
- public DivFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "DivF"; }
-
- @Override protected String format() { return "(%1/%2)"; }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0 &&
- in(2)._type instanceof TypeFloat i1) {
- if (i0.isConstant() && i1.isConstant())
- return TypeFloat.constant(i0.value()/i1.value());
- }
- return in(1)._type.meet(in(2)._type);
- }
-
- @Override
- public Node idealize() {
- Node lhs = in(1);
- Node rhs = in(2);
- Type t1 = lhs._type;
- Type t2 = rhs._type;
-
- // Div of 1.
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==1 )
- return lhs;
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new DivFNode(lhs,rhs); }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/DivNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/DivNode.java
index cd52d117..d7244ace 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/DivNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/DivNode.java
@@ -37,5 +37,4 @@ public class DivNode extends Node {
}
@Override Node copy(Node lhs, Node rhs) { return new DivNode(lhs,rhs); }
- @Override Node copyF() { return new DivFNode(null,null); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/IfNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 70340f7f..45f695a5 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -5,10 +5,8 @@ import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
import com.seaofnodes.simple.type.TypeTuple;
-import java.util.BitSet;
-import java.util.HashSet;
-public class IfNode extends CFGNode implements MultiNode {
+public class IfNode extends MultiNode {
public IfNode(Node ctrl, Node pred) {
super(ctrl, pred);
@@ -20,6 +18,7 @@ public class IfNode extends CFGNode implements MultiNode {
@Override protected String format() { return "if( %1 )"; }
+ @Override public boolean isCFG() { return true; }
@Override public boolean isMultiHead() { return true; }
public Node ctrl() { return in(0); }
@@ -49,20 +48,11 @@ public class IfNode extends CFGNode implements MultiNode {
// Hunt up the immediate dominator tree. If we find an identical if
// test on either the true or false branch, that side wins.
if( !pred()._type.isHighOrConst() )
- for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() )
- if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) {
+ for( Node dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() )
+ if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof ProjNode prj ) {
setDef(1,new ConstantNode(TypeInteger.make(true,prj._idx==0?1:0)).peephole());
return this;
}
return null;
}
-
- @Override void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) {
- for( Node proj : _outputs )
- if( ((CProjNode)proj)._loopDepth == 0 )
- unreach.add((CProjNode)proj);
- super._walkUnreach(visit,unreach);
- }
-
- @Override public Node getBlockStart() { return ctrl().getBlockStart(); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 01a82a79..b2ee6939 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -14,7 +14,6 @@ import com.seaofnodes.simple.type.Field;
*/
public class LoadNode extends MemOpNode {
- Type _declaredType;
/**
* Load a value from a ptr.field.
*
@@ -23,12 +22,11 @@ public class LoadNode extends MemOpNode {
* @param memPtr The ptr to the struct from where we load a field
*/
public LoadNode(String name, int alias, Type glb, Node memSlice, Node memPtr) {
- super(name, alias, null, memSlice, memPtr);
- _declaredType = glb;
+ super(name, alias, glb, null, memSlice, memPtr);
}
@Override boolean canDrop(MemOpNode other, Node dep) {
- return super.canDrop(other,dep) && _declaredType==((LoadNode)other)._declaredType && !clobbered(dep);
+ return super.canDrop(other,dep) && !clobbered(dep);
}
// Check only immediate memory users. Stores clobber memory; Phis and
@@ -67,7 +65,7 @@ public class LoadNode extends MemOpNode {
// Simple Load-after-Store on same address.
if( mem() instanceof StoreNode st &&
ptr() == st.ptr() && _alias==st._alias ) { // Must check same object
- assert _name.equals(st._name); // Equiv class aliasing is perfect
+ assert Utils.eq(_name,st._name); // Equiv class aliasing is perfect
return st.val();
}
@@ -83,13 +81,9 @@ public class LoadNode extends MemOpNode {
if( profit(memphi,2) ||
// Else must not be a loop to count profit on LHS.
(!(memphi.region() instanceof LoopNode) && profit(memphi,1)) ) {
- if( ptr() instanceof NewNode || !(ptr() instanceof CastNode) ) {
- if( !(ptr() instanceof NewNode) ) throw Utils.TODO(); // Validate
-
- Node ld1 = new LoadNode(_name,_alias,_declaredType,memphi.in(1),ptr()).peephole();
- Node ld2 = new LoadNode(_name,_alias,_declaredType,memphi.in(2),ptr()).peephole();
- return new PhiNode(_name,_type,memphi.region(),ld1,ld2);
- }
+ Node ld1 = new LoadNode(_name,_alias,_declaredType,memphi.in(1),ptr()).peephole();
+ Node ld2 = new LoadNode(_name,_alias,_declaredType,memphi.in(2),ptr()).peephole();
+ return new PhiNode(_name,_type,memphi.region(),ld1,ld2);
}
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index a0e988dd..a0ba973e 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -1,17 +1,15 @@
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.TypeInteger;
+
import java.util.BitSet;
-import java.util.HashSet;
public class LoopNode extends RegionNode {
+
public LoopNode( Node entry ) { super(null,entry,null); }
- public CFGNode entry() { return cfg(1); }
- public CFGNode back () { return cfg(2); }
+ Node entry() { return in(1); }
+ Node back () { return in(2); }
@Override
public String label() { return "Loop"; }
@@ -27,54 +25,7 @@ public class LoopNode extends RegionNode {
return inProgress() ? null : super.idealize();
}
- // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
- @Override public int idepth() { return _idepth!=0 ? _idepth : cacheIDepth(entry().idepth()+1); }
- // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
- @Override public CFGNode idom(Node dep) { return entry(); }
-
- @Override public int loopDepth() {
- if( _loopDepth!=0 ) return _loopDepth; // Was already set
- _loopDepth = entry()._loopDepth+1; // Entry depth plus one
- // One-time tag loop exits
- for( CFGNode idom = back(); idom!=this; idom = idom.idom() ) {
- // Walk idom in loop, setting depth
- idom._loopDepth = _loopDepth;
- // Loop exit hits the CProj before the If, instead of jumping from
- // Region directly to If.
- if( idom instanceof CProjNode proj ) {
- assert proj.in(0) instanceof IfNode; // Loop exit test
- // Find the loop exit CProj, and set loop_depth
- for( Node use : proj.in(0)._outputs )
- if( use instanceof CProjNode proj2 && proj2 != idom )
- proj2._loopDepth = _loopDepth-1;
- }
- }
- return _loopDepth;
- }
-
- // If this is an unreachable loop, it may not have an exit. If it does not
- // (i.e., infinite loop), force an exit to make it reachable.
- public void forceExit( StopNode stop ) {
- // Walk the backedge, then immediate dominator tree util we hit this
- // Loop again. If we ever hit a CProj from an If (as opposed to
- // directly on the If) we found our exit.
- CFGNode x = back();
- while( x != this ) {
- if( x instanceof CProjNode exit && exit.in(0) instanceof IfNode iff ) {
- Node other = iff.cproj(1-exit._idx);
- if( !(other.out(0) instanceof LoopNode loop) || loop.entry()==exit )
- return; // Found an exit, not an infinite loop
- }
- x = x.idom();
- }
- // Found a no-exit loop. Insert an exit
- NeverNode iff = new NeverNode(back());
- for( Node use : _outputs )
- if( use instanceof PhiNode phi )
- iff.addDef(use);
- CProjNode t = new CProjNode(iff,0,"True" );
- CProjNode f = new CProjNode(iff,1,"False");
- setDef(2,f);
- stop.addDef(new ReturnNode(t,Parser.ZERO,null));
- }
+ // Bypass Region idom, same as the default idom() using use entry in(1) instead of in(0)
+ @Override int idepth() { return _idepth!=0 ? _idepth : cacheIDepth(entry().idepth()+1); }
+ @Override Node idom() { return entry(); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 5a57a8c8..7c6afb0c 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -11,11 +11,14 @@ public abstract class MemOpNode extends Node {
public final String _name;
public final int _alias;
+ public final Type _declaredType;
- public MemOpNode(String name, int alias, Node... nodes) {
+
+ public MemOpNode(String name, int alias, Type glb, Node... nodes ) {
super(nodes);
_name = name;
_alias = alias;
+ _declaredType = glb;
}
public Node mem() { return in(1); }
@@ -25,7 +28,7 @@ public abstract class MemOpNode extends Node {
// The caller has already checked the opcode, input shape and control.
boolean canDrop(MemOpNode other, Node dep) {
ptr().addDep(dep);
- return _alias==other._alias && err()==null;
+ return _alias==other._alias && _declaredType==other._declaredType && err()==null;
}
@Override
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
deleted file mode 100644
index 1b45c7c5..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusFNode.java
+++ /dev/null
@@ -1,30 +0,0 @@
-package com.seaofnodes.simple.node;
-
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-
-
-public class MinusFNode extends Node {
- public MinusFNode(Node in) { super(null, in); }
-
- @Override public String label() { return "MinusF"; }
-
- @Override protected String format() { return "(-%1)"; }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0)
- return i0.isConstant() ? TypeFloat.constant(-i0.value()) : i0;
- return TypeFloat.BOT;
- }
-
- @Override
- public Node idealize() {
- // -(-x) is x
- if( in(1) instanceof MinusFNode minus )
- return minus.in(1);
-
- return null;
- }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java
index ae450b22..5b65b3fb 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MinusNode.java
@@ -27,5 +27,4 @@ public class MinusNode extends Node {
return null;
}
- @Override Node copyF() { return new MinusFNode(null); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MulFNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MulFNode.java
deleted file mode 100644
index 6a57f3c5..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/MulFNode.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package com.seaofnodes.simple.node;
-
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-
-
-public class MulFNode extends Node {
- public MulFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "MulF"; }
-
- @Override protected String format() { return "(%1*%2)"; }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0 &&
- in(2)._type instanceof TypeFloat i1) {
- if (i0.isConstant() && i1.isConstant())
- return TypeFloat.constant(i0.value()*i1.value());
- }
- return in(1)._type.meet(in(2)._type);
- }
-
- @Override
- public Node idealize() {
- Node lhs = in(1);
- Node rhs = in(2);
- Type t1 = lhs._type;
- Type t2 = rhs._type;
-
- // Mul of 1. We do not check for (1*x) because this will already
- // canonicalize to (x*1)
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==1 )
- return lhs;
-
- // Move constants to RHS: con*arg becomes arg*con
- if ( t1.isConstant() && !t2.isConstant() )
- return swap12();
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new MulFNode(lhs,rhs); }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MulNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MulNode.java
index 2c13fd0f..b0359a52 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/MulNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MulNode.java
@@ -51,5 +51,9 @@ public class MulNode extends Node {
return null;
}
@Override Node copy(Node lhs, Node rhs) { return new MulNode(lhs,rhs); }
- @Override Node copyF() { return new MulFNode(null,null); }
+ @Override public String err() {
+ if( !(in(1)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(1)._type;
+ if( !(in(2)._type instanceof TypeInteger) ) return "Cannot '"+label()+"' " + in(2)._type;
+ return null;
+ }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java
index ac9c0045..2743bd19 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/MultiNode.java
@@ -1,19 +1,13 @@
package com.seaofnodes.simple.node;
-public interface MultiNode extends OutNode {
+public abstract class MultiNode extends Node {
- // Find a projection by index
- default ProjNode proj( int idx ) {
- for( Node out : outs() )
- if( out instanceof ProjNode prj && prj._idx==idx )
- return prj;
- return null;
- }
+ public MultiNode(Node... inputs) { super(inputs); }
// Find a projection by index
- default CProjNode cproj( int idx ) {
- for( Node out : outs() )
- if( out instanceof CProjNode prj && prj._idx==idx )
+ ProjNode proj( int idx ) {
+ for( Node out : _outputs )
+ if( out instanceof ProjNode prj && prj._idx==idx )
return prj;
return null;
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java
deleted file mode 100644
index 7e42ccf9..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/NeverNode.java
+++ /dev/null
@@ -1,22 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.print.ExprPrinter;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.TypeInteger;
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeTuple;
-
-
-// "Never true" for infinite loop exits
-public class NeverNode extends IfNode {
- public NeverNode(Node ctrl) { super(ctrl,Parser.ZERO); }
-
- @Override public String label() { return "Never"; }
-
- @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Never"); }
-
- @Override public Type compute() { return TypeTuple.IF_BOTH; }
-
- @Override public Node idealize() { return null; }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/NewNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/NewNode.java
index 74ba5184..857920bc 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -22,7 +22,6 @@ public class NewNode extends Node {
return p.p("new ").p(_ptr._obj.str());
}
-
@Override
public Type compute() { return _ptr; }
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java
index e3503a7c..999e38fd 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -8,7 +8,6 @@ import com.seaofnodes.simple.IRPrinter;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
import java.util.*;
import java.util.function.Function;
@@ -18,7 +17,7 @@ import java.util.function.Function;
* The Node class provides common functionality used by all subtypes.
* Subtypes of Node specialize by overriding methods.
*/
-public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable {
+public abstract class Node extends BaseNode<Node> implements Cloneable {
/**
* Inputs to the node. These are use-def references to Nodes.
@@ -84,7 +83,6 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
*/
public Node in(int i) { return _inputs.get(i); }
public Node out(int i) { return _outputs.get(i); }
- @Override public ArrayList<Node> outs() { return _outputs; }
public int nIns() { return _inputs.size(); }
@@ -92,7 +90,7 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
public boolean isUnused() { return nOuts() == 0; }
- public CFGNode cfg0() { return (CFGNode)in(0); }
+ public boolean isMem() { return false; }
/**
* Change a <em>def</em> into a Node. Keeps the edges correct, by removing
@@ -119,11 +117,11 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
// the new_def might get killed if the old node kills it recursively.
if( new_def != null )
new_def.addUse(this);
- // Set the new_def over the old (killed) edge
- _inputs.set(idx,new_def);
if( old_def != null && // If the old def exists, remove a def->use edge
old_def.delUse(this) ) // If we removed the last use, the old def is now dead
old_def.kill(); // Kill old def
+ // Set the new_def over the old (killed) edge
+ _inputs.set(idx,new_def);
moveDepsToWorklist();
// Return self for easy flow-coding
return new_def;
@@ -151,7 +149,7 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
* @param new_def the new definition, appended to the end of existing definitions
* @return new_def for flow coding
*/
- public Node addDef(Node new_def) {
+ Node addDef(Node new_def) {
unlock();
// Add use->def edge
_inputs.add(new_def);
@@ -271,8 +269,8 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
Type old = setType(compute());
// Replace constant computations from non-constants with a constant node
- if( !(this instanceof ConstantNode) && !(this instanceof XCtrlNode) && _type.isHighOrConst() )
- return (_type==Type.XCONTROL ? new XCtrlNode() : new ConstantNode(_type)).peepholeOpt();
+ if( !(this instanceof ConstantNode) && _type.isHighOrConst() )
+ return new ConstantNode(_type).peepholeOpt();
// Global Value Numbering
if( _hash==0 ) {
@@ -487,37 +485,6 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
int hash() { return 0; }
// ------------------------------------------------------------------------
- //
-
- /** Is this Node control-flow-graph related */
-
- /** Is this Node Memory related */
- public boolean isMem() { return false; }
-
- /** Return block start from a isCFG() */
- public Node getBlockStart() { return null; }
-
- // Semantic change to the graph (so NOT a peephole), used by the Parser.
- // If any input is a float, flip to a float-flavored opcode and widen any
- // non-float input.
- public final Node widen() {
- if( !hasFloatInput() ) return this;
- Node flt = copyF();
- if( flt==null ) return this;
- for( int i=1; i<nIns(); i++ )
- flt.setDef(i, in(i)._type instanceof TypeFloat ? in(i) : new ToFloatNode(in(i)).peephole());
- kill();
- return flt;
- }
- private boolean hasFloatInput() {
- for( int i=1; i<nIns(); i++ )
- if( in(i)._type instanceof TypeFloat )
- return true;
- return false;
- }
- Node copyF() { return null; }
-
- // ------------------------------------------------------------------------
// Peephole utilities
// Swap inputs without letting either input go dead during the swap.
@@ -545,6 +512,38 @@ public abstract class Node extends BaseNode<Node> implements OutNode, Cloneable
return true;
}
+ /**
+ * Immediate dominator tree depth, used to approximate a real IDOM depth
+ * during parsing where we do not have the whole program, and also
+ * peepholes change the CFG incrementally.
+ * <p>
+ * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>}
+ */
+ public char _idepth; // IDOM depth approx; Zero is unset; non-zero is cached legit
+
+ // Find the lowest common ancestor in the current dominator tree.
+ Node domLCA(Node rhs) {
+ if( rhs==null ) return this;
+ Node lhs = this;
+ while( lhs != rhs ) {
+ if( lhs==null || rhs==null ) return null;
+ int comp = lhs.idepth() - rhs.idepth();
+ if( comp >= 0 ) lhs = lhs.idom();
+ if( comp <= 0 ) rhs = rhs.idom();
+ }
+ return lhs;
+ }
+
+ int idepth() { return _idepth!=0 ? _idepth : cacheIDepth(idom().idepth()+1); }
+ // Zero depth means uncached. Check before narrowing so overflow cannot wrap.
+ final int cacheIDepth(int depth) {
+ assert 0 <= depth && depth <= Character.MAX_VALUE : "Dominator depth exceeds 65535";
+ return _idepth = (char)depth;
+ }
+
+ // Return the immediate dominator of this Node.
+ Node idom() { return in(0); }
+
// Make a shallow copy (same class) of this Node, with given inputs and
// empty outputs and a new Node ID. The original inputs are ignored.
// Does not need to be implemented in isCFG() nodes.
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/NotNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/NotNode.java
index 4539f982..5e556390 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/NotNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/NotNode.java
@@ -15,10 +15,8 @@ public class NotNode extends Node {
public Type compute() {
Type t0 = in(1)._type;
switch( t0 ) {
- case TypeInteger i0: return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : i0;
- case TypeFloat i0:
- if( i0==TypeFloat.TOP ) return TypeInteger.TOP;
- return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : TypeInteger.BOT;
+ case TypeInteger i0:
+ return i0.isConstant() ? TypeInteger.constant(i0.value()==0 ? 1 : 0) : i0;
case TypeMemPtr p0:
// top->top, bot->bot, null->1, *void->0, not-null ptr->0, ptr/nil->bot
// If input in null then true
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/OutNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/OutNode.java
deleted file mode 100644
index 7e531145..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/OutNode.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import java.util.ArrayList;
-
-public interface OutNode {
- ArrayList<Node> outs();
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 8dfa04cf..6995cc2f 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -32,9 +32,10 @@ public class PhiNode extends Node {
return p.unchar(',').close();
}
- public CFGNode region() { return (CFGNode)in(0); }
+ Node region() { return in(0); }
@Override public boolean isMultiTail() { return true; }
- @Override public boolean isMem() { return _declaredType instanceof TypeMem; }
+ @Override
+ public boolean isMem() { return _declaredType instanceof TypeMem; }
@Override
public Type compute() {
@@ -82,11 +83,11 @@ public class PhiNode extends Node {
if( in(2)._type == in(2)._type.makeInit() ) nullx = 2;
if( nullx != -1 ) {
Node val = in(3-nullx);
- if( region().idom(this) instanceof IfNode iff && iff.pred().addDep(this)==val ) {
+ if( region().idom() instanceof IfNode iff && iff.pred().addDep(this)==val ) {
// Must walk the idom on the null side to make sure we hit False.
- CFGNode idom = (CFGNode)region().in(nullx);
- while( idom.nIns() > 0 && idom.in(0) != iff ) idom = idom.idom();
- if( idom instanceof CProjNode proj && proj._idx==1 )
+ Node idom = region().in(nullx);
+ while( idom.in(0) != iff ) idom = idom.idom();
+ if( idom instanceof ProjNode proj && proj._idx==1 )
return val;
}
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index e82c69b3..a2632110 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -15,7 +15,7 @@ public class ProjNode extends Node {
// Debugging label
public final String _label;
- public ProjNode(Node ctrl, int idx, String label) {
+ public ProjNode(MultiNode ctrl, int idx, String label) {
super(ctrl);
_idx = idx;
_label = label;
@@ -27,16 +27,30 @@ public class ProjNode extends Node {
@Override
protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p(_label); }
+ @Override public boolean isCFG() { return _idx==0 || ctrl() instanceof IfNode; }
@Override public boolean isMultiTail() { return in(0).isMultiHead(); }
@Override public boolean isMem() { return _type instanceof TypeMem; }
+ public MultiNode ctrl() { return (MultiNode)in(0); }
+
@Override
public Type compute() {
Type t = in(0)._type;
return t instanceof TypeTuple tt ? tt._types[_idx] : Type.BOTTOM;
}
- @Override public Node idealize() { return null; }
+ @Override
+ public Node idealize() {
+ if ( ctrl() instanceof IfNode iff ) {
+ if ( iff._type instanceof TypeTuple tt && tt._types[1 - _idx] == Type.XCONTROL )
+ return iff.in(0); // We become our input control
+
+ // Flip a negating if-test, to remove the not
+ if( iff.pred().addDep(this) instanceof NotNode not )
+ return new ProjNode((MultiNode)new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True");
+ }
+ return null;
+ }
@Override
boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index cdfd10ad..8b52494a 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -3,15 +3,13 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.IterPeeps;
-import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
+
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.BitSet;
-import java.util.HashSet;
-public class RegionNode extends CFGNode {
+public class RegionNode extends Node {
public RegionNode(Node... nodes) { super(nodes); }
@@ -22,8 +20,8 @@ public class RegionNode extends CFGNode {
return p.p(label()).p(_nid);
}
+ @Override public boolean isCFG() { return true; }
@Override public boolean isMultiHead() { return true; }
- @Override public boolean blockHead() { return true; }
@Override
public Type compute() {
@@ -59,7 +57,7 @@ public class RegionNode extends CFGNode {
IterPeeps.addAll(phi._outputs);
}
}
- return isDead() ? Parser.XCTRL : delDef(path);
+ return isDead() ? new ConstantNode(Type.XCONTROL) : delDef(path);
}
// If down to a single input, become that input
if( nIns()==2 && !hasPhi() )
@@ -91,30 +89,23 @@ public class RegionNode extends CFGNode {
}
// Immediate dominator of Region is a little more complicated.
- @Override public int idepth() {
+ @Override int idepth() {
if( _idepth!=0 ) return _idepth;
int depth=0;
for( Node n : _inputs )
if( n!=null )
- depth = Math.max(depth,((CFGNode)n).idepth()+1);
+ depth = Math.max(depth,n.idepth()+1);
return cacheIDepth(depth);
}
- @Override public CFGNode idom(Node dep) {
- CFGNode lca = null;
+ @Override Node idom() {
+ Node lca = null;
// Recompute from predecessors: CFG edits can change the dominator.
for( int i=1; i<nIns(); i++ )
- lca = cfg(i).domLCA(lca,dep);
+ lca = in(i).domLCA(lca);
return lca;
}
- @Override void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) {
- for( int i=1; i<nIns(); i++ )
- cfg(i).walkUnreach(visit,unreach);
- }
-
- @Override public int loopDepth() { return _loopDepth==0 ? (_loopDepth = cfg(1).loopDepth()) : _loopDepth; }
-
// True if last input is null
public final boolean inProgress() {
return nIns()>1 && in(nIns()-1) == null;
@@ -124,6 +115,4 @@ public class RegionNode extends CFGNode {
@Override boolean eq( Node n ) {
return !inProgress();
}
-
- @Override public Node getBlockStart() { return this; }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 161cc7d6..ef4ddb3a 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -6,11 +6,10 @@ import com.seaofnodes.simple.type.*;
/** Keeps all preceding memory effects and the return value alive: {ctrl, $mem, value}. */
-public class ReturnNode extends CFGNode {
+public class ReturnNode extends Node {
public ReturnNode(Node ctrl, Node data, ScopeNode scope) {
- // A synthetic never-taken loop exit has no source scope.
- super(ctrl, scope == null ? null : scope.lookup("$mem"), data);
+ super(ctrl, scope.lookup("$mem"), data);
}
public Node ctrl() { return in(0); }
@@ -27,6 +26,8 @@ public class ReturnNode extends CFGNode {
return p.p(";");
}
+ @Override public boolean isCFG() { return true; }
+
@Override
public Type compute() {
// Return exposes the complete memory state.
@@ -39,6 +40,4 @@ public class ReturnNode extends CFGNode {
return ctrl();
return null;
}
-
- @Override public Node getBlockStart() { return ctrl().getBlockStart(); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java
index a5d4923f..3404f111 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -3,13 +3,10 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.print.ExprPrinter;
import com.seaofnodes.simple.type.*;
-import java.util.BitSet;
-import java.util.HashSet;
-/** Start supplies control, whole memory, and the argument, in that order. */
-public class StartNode extends CFGNode implements MultiNode {
-
- private final TypeTuple _args;
+/** The entry tuple: control, initial whole-memory state, and argument. */
+public class StartNode extends MultiNode {
+ final TypeTuple _args;
public StartNode(Type[] args) {
super();
@@ -23,8 +20,8 @@ public class StartNode extends CFGNode implements MultiNode {
return p.p(label());
}
+ @Override public boolean isCFG() { return true; }
@Override public boolean isMultiHead() { return true; }
- @Override public boolean blockHead() { return true; }
@Override
public TypeTuple compute() { return _args; }
@@ -33,12 +30,6 @@ public class StartNode extends CFGNode implements MultiNode {
public Node idealize() { return null; }
// No immediate dominator, and idepth==0
- @Override public int idepth() { return 0; }
- @Override public CFGNode idom(Node dep) { return null; }
-
- @Override void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) { }
-
- @Override public int loopDepth() { return (_loopDepth=1); }
-
- @Override public Node getBlockStart() { return this; }
+ @Override int idepth() { return 0; }
+ @Override Node idom() { return null; }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java
index b5d2d716..4b4e9615 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -8,7 +8,7 @@ import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.IterPeeps;
-public class StopNode extends CFGNode {
+public class StopNode extends Node {
public final String _src;
@@ -31,7 +31,7 @@ public class StopNode extends CFGNode {
return p.p("]");
}
- @Override public boolean blockHead() { return true; }
+ @Override public boolean isCFG() { return true; }
// If a single Return, return it.
// Otherwise, null because ambiguous.
@@ -54,17 +54,7 @@ public class StopNode extends CFGNode {
return null;
}
- @Override public int idepth() {
- if( _idepth!=0 ) return _idepth;
- int d=0;
- for( Node n : _inputs )
- if( n!=null )
- d = Math.max(d,((CFGNode)n).idepth()+1);
- return cacheIDepth(d);
- }
- @Override public CFGNode idom(Node dep) { return null; }
-
- @Override public int loopDepth() { return (_loopDepth=1); }
+ @Override public Node idom() { return null; }
public Node addReturn(Node node) {
return addDef(node);
@@ -82,6 +72,4 @@ public class StopNode extends CFGNode {
if( obs != null ) obs.phase("TypeCheck");
return this;
}
-
- @Override public Node getBlockStart() { return this; }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index f61afe78..e9eaaf2e 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -13,20 +13,26 @@ import com.seaofnodes.simple.type.*;
*/
public class StoreNode extends MemOpNode {
+ private final boolean _init; // Initializing writes are allowed to write null
+
/**
* @param name The struct field we are assigning to
* @param memSlice The memory alias node - this is updated after a Store
* @param memPtr The ptr to the struct where we will store a value
* @param value Value to be stored
*/
- public StoreNode(String name, int alias, Node ctrl, Node memSlice, Node memPtr, Node value) {
- super(name, alias, ctrl, memSlice, memPtr, value);
+ public StoreNode(String name, int alias, Type glb, Node ctrl, Node memSlice, Node memPtr, Node value, boolean init) {
+ super(name, alias, glb, ctrl, memSlice, memPtr, value);
+ _init = init;
}
- @Override
- public String label() { return "Store"; }
- @Override
- public boolean isMem() { return true; }
+ @Override boolean canDrop(MemOpNode other, Node dep) {
+ return super.canDrop(other,dep) && _init==((StoreNode)other)._init;
+ }
+
+ @Override public String label() { return "." +_name+"="; }
+ @Override public boolean isMem() { return true; }
+
public Node val() { return in(3); }
@Override
@@ -72,4 +78,11 @@ public class StoreNode extends MemOpNode {
return false;
}
+ @Override
+ String err() {
+ String err = super.err();
+ if( err != null ) return err;
+ Type ptr = val()._type;
+ return _init || ptr.isa(_declaredType) ? null : "Cannot store "+ptr+" into field "+_declaredType+" "+_name;
+ }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/SubFNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/SubFNode.java
deleted file mode 100644
index 2d218a03..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/SubFNode.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.seaofnodes.simple.node;
-
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-
-
-public class SubFNode extends Node {
- public SubFNode(Node lhs, Node rhs) { super(null, lhs, rhs); }
-
- @Override public String label() { return "SubF"; }
-
- @Override protected String format() { return "(%1-%2)"; }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeFloat i0 &&
- in(2)._type instanceof TypeFloat i1) {
- if (i0.isConstant() && i1.isConstant())
- return TypeFloat.constant(i0.value()-i1.value());
- }
- return in(1)._type.meet(in(2)._type);
- }
-
- @Override
- public Node idealize() {
- Node lhs = in(1);
- Type t2 = in(2)._type;
-
- // Sub of 0.
- if ( t2.isConstant() && t2 instanceof TypeFloat i && i.value()==0 )
- return lhs;
-
- return null;
- }
- @Override Node copy(Node lhs, Node rhs) { return new SubFNode(lhs,rhs); }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/SubNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/SubNode.java
index f36f2af5..6fa87a07 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/SubNode.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/node/SubNode.java
@@ -1,7 +1,6 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
@@ -28,7 +27,6 @@ public class SubNode extends Node {
if (i0.isConstant() && i1.isConstant())
return TypeInteger.constant(i0.value()-i1.value());
}
-
return TypeInteger.BOT;
}
@@ -57,5 +55,4 @@ public class SubNode extends Node {
}
@Override Node copy(Node lhs, Node rhs) { return new SubNode(lhs,rhs); }
- @Override Node copyF() { return new SubFNode(null,null); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
deleted file mode 100644
index 26cf50e1..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/ToFloatNode.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.seaofnodes.simple.node;
-
-
-import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFloat;
-import com.seaofnodes.simple.type.TypeInteger;
-
-
-public class ToFloatNode extends Node {
- public ToFloatNode(Node lhs) { super(null, lhs); }
-
- @Override public String label() { return "ToFloat"; }
-
- @Override protected String format() { return "(flt)%1"; }
-
- @Override
- public Type compute() {
- if (in(1)._type instanceof TypeInteger i0 && i0.isConstant() )
- return TypeFloat.constant(i0.value());
- return TypeFloat.BOT;
- }
-
- @Override public Node idealize() { return null; }
- @Override Node copy(Node lhs, Node rhs) { return new ToFloatNode(lhs); }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/chapter12/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
deleted file mode 100644
index 056dfba1..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package com.seaofnodes.simple.node;
-
-import com.seaofnodes.print.ExprPrinter;
-
-import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.type.Type;
-
-public class XCtrlNode extends CFGNode {
- public XCtrlNode( ) { super(Parser.START); }
- @Override public String label() { return "Xctrl"; }
- @Override protected ExprPrinter<Node> _print1(ExprPrinter<Node> p) { return p.p("Xctrl"); }
- @Override public Type compute() { return Type.XCONTROL; }
- @Override public Node idealize() { return null; }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java b/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
index e65963db..92d970d8 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/print/SimpleGraphAdapter.java
@@ -6,13 +6,13 @@ import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.type.TypeMem;
-/** Chapter 12's view of the IR; browser, transport and layout are shared. */
+/** Chapter 10's view of the IR; browser, transport and layout are shared. */
public class SimpleGraphAdapter extends GraphAdapter<Node> {
@Override protected String[] edgeNames(Node n) {
return n instanceof ScopeNode scope && n.nIns()!=0 ? scope.reverseNames() : null;
}
- @Override protected int projectionIndex(Node n) { return n instanceof ProjNode p ? p._idx : n instanceof CProjNode p ? p._idx : -1; }
+ @Override protected int projectionIndex(Node n) { return n instanceof ProjNode p ? p._idx : -1; }
@Override protected Kind kind(Node n) {
if( n instanceof StopNode ) return Kind.STOP;
@@ -25,9 +25,9 @@ public class SimpleGraphAdapter extends GraphAdapter<Node> {
}
@Override protected Role role(Node n, int i) {
- if( n instanceof ScopeNode || n instanceof ConstantNode || n instanceof XCtrlNode ) return Role.ASSOC;
+ if( n instanceof ScopeNode || n instanceof ConstantNode ) return Role.ASSOC;
if( n instanceof PhiNode ) return i == 0 ? Role.ASSOC : isMem(n) ? Role.MEM : Role.DATA;
- if( n instanceof ProjNode || n instanceof CProjNode ) return n.isCFG() ? Role.CTRL : isMem(n) ? Role.MEM : Role.DATA;
+ if( n instanceof ProjNode ) return n.isCFG() ? Role.CTRL : isMem(n) ? Role.MEM : Role.DATA;
if( n instanceof RegionNode && i == 0 ) return Role.ASSOC;
if( i == 0 || n instanceof RegionNode || n instanceof StopNode ) return Role.CTRL;
if( i == 1 && (n instanceof MemOpNode || n instanceof ReturnNode) ) return Role.MEM;
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/Field.java b/chapter12/src/main/java/com/seaofnodes/simple/type/Field.java
index b50a4fa6..b2c006dd 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -53,7 +53,7 @@ public class Field extends Type {
@Override
public StringBuilder print( StringBuilder sb ) {
- return _type.print(sb.append(_fname).append(":"));
+ return _type.print(sb.append(_fname).append(" :"));
}
@Override public String str() { return _fname; }
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter12/src/main/java/com/seaofnodes/simple/type/Type.java
index 45d66be4..93e27c76 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -33,12 +33,11 @@ public class Type {
static final byte TXCTRL = 3; // Ctrl flow top (mini-lattice: any-xctrl-ctrl-all)
static final byte TSIMPLE = 4; // End of the Simple Types
static final byte TINT = 5; // All Integers; see TypeInteger
- static final byte TFLT = 6; // All Integers; see TypeInteger
- static final byte TTUPLE = 7; // Tuples; finite collections of unrelated Types, kept in parallel
- static final byte TMEM = 8; // All memory (alias 0) or A slice of memory - with specific alias
- static final byte TMEMPTR = 9; // Memory pointer type
- static final byte TSTRUCT =10; // Structs; tuples with named fields
- static final byte TFLD =11; // Fields into struct
+ static final byte TTUPLE = 6; // Tuples; finite collections of unrelated Types, kept in parallel
+ static final byte TMEM = 7; // All memory (alias 0) or A slice of memory - with specific alias
+ static final byte TMEMPTR = 8; // Memory pointer type
+ static final byte TSTRUCT = 9; // Structs; tuples with named fields
+ static final byte TFLD =10; // Fields into struct
public final byte _type;
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
deleted file mode 100644
index 738aebc9..00000000
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
+++ /dev/null
@@ -1,101 +0,0 @@
-package com.seaofnodes.simple.type;
-
-import java.util.ArrayList;
-
-/**
- * Float Type
- */
-public class TypeFloat extends Type {
-
- public final static TypeFloat TOP = make(false, 0);
- public final static TypeFloat BOT = make(false, 1);
- public final static TypeFloat ZERO= make(true , 0);
-
- public final boolean _is_con;
-
- /**
- * The constant value or
- * if not constant then 1=bottom, 0=top.
- */
- public final double _con;
-
- private TypeFloat(boolean is_con, double con) {
- super(TFLT);
- _is_con = is_con;
- _con = con;
- }
- public static TypeFloat make(boolean is_con, double con) {
- return new TypeFloat(is_con,con).intern();
- }
-
- public static TypeFloat constant(double con) { return make(true, con); }
-
- public static void gather(ArrayList<Type> ts) { ts.add(ZERO); ts.add(BOT); }
-
- // FIXME this display format is problematic
- // In visualizer '#' gets prepended if its a constant
- @Override
- public StringBuilder print(StringBuilder sb) {
- if( this==TOP ) return sb.append("FltTop");
- if( this==BOT ) return sb.append("FltBot");
- return sb.append(_con);
- }
-
- @Override public String str() {
- if( this==TOP ) return "~flt";
- if( this==BOT ) return "flt";
- return ""+_con;
- }
-
- /**
- * Display Type name in a format that's good for IR printer
- */
- @Override
- public StringBuilder typeName(StringBuilder sb) {
- if( this==TOP ) return sb.append("FltTop");
- if( this==BOT ) return sb.append("FltBot");
- return sb.append("Int");
- }
-
- @Override
- public boolean isHighOrConst() { return _is_con || _con==0; }
-
- @Override
- public boolean isConstant() { return _is_con; }
-
- public double value() { return _con; }
-
- @Override
- public Type xmeet(Type other) {
- // Invariant from caller: 'this' != 'other' and same class (TypeFloat)
- TypeFloat i = (TypeFloat)other; // Contract
- // BOT wins
- if ( this==BOT ) return this;
- if ( i ==BOT ) return i ;
- // TOP loses
- if ( i ==TOP ) return this;
- if ( this==TOP ) return i ;
- // Since both are constants, and are never equals (contract) unequals
- // constants fall to bottom
- return BOT;
- }
-
- @Override
- public Type dual() {
- if( isConstant() ) return this; // Constants are a self-dual
- return _con==0 ? BOT : TOP;
- }
-
- @Override
- public Type glb() { return BOT; }
-
- @Override public TypeFloat makeInit() { return ZERO; }
-
- @Override
- int hash() { return (int)(Double.hashCode(_con) ^ (_is_con ? 0 : 0x4000)); }
- @Override
- public boolean eq( Type t ) {
- TypeFloat i = (TypeFloat)t; // Contract
- return _con==i._con && _is_con==i._is_con;
- }
-}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMem.java
index b6fc8824..38b3635a 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMem.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMem.java
@@ -15,7 +15,7 @@ public class TypeMem extends Type {
// 0 means TOP, no slice.
// -1 means BOT, all memory.
// N means slice#N.
- private final int _alias;
+ public final int _alias;
private TypeMem(int alias) { super(TMEM); _alias = alias; }
@@ -23,7 +23,6 @@ public class TypeMem extends Type {
public static final TypeMem TOP = make( 0);
public static final TypeMem BOT = make(-1);
-
public static void gather(ArrayList<Type> ts) { ts.add(make(1)); ts.add(BOT); }
@Override
@@ -40,7 +39,7 @@ public class TypeMem extends Type {
}
@Override
- public Type glb() { return TypeMem.BOT; }
+ public Type glb() { return make(_alias); }
@Override
int hash() { return 9876543 + _alias; }
@@ -62,5 +61,5 @@ public class TypeMem extends Type {
});
}
- @Override public String str() { return print(new StringBuilder()).toString(); }
+ @Override public String str() { return toString(); }
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index 6c874a93..5caa816d 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -13,21 +13,27 @@ import java.util.ArrayList;
*/
public class TypeMemPtr extends Type {
// A TypeMemPtr is pair (obj,nil)
- // where obj is one of
- // (null,TypeStruct).
- // where nil is one of
- // (true,false) meaning an explicit null is allowed or not
+ // where obj is a TypeStruct, possibly TypeStruct.BOT
+ // where nil is an explicit null is allowed or not
- public final TypeStruct _obj; // null, a TypeStruct, or sentinel TypeStruct.MANY
+ // Examples:
+ // (Person,false) - a not-nil Person
+ // (Person,true ) - a Person or a nil
+ // (BOT ,false) - a not-nil void* (unspecified struct)
+ // (TOP ,true ) - a nil
+
+ public final TypeStruct _obj;
public final boolean _nil;
private TypeMemPtr(TypeStruct obj, boolean nil) {
super(TMEMPTR);
+ assert obj!=null;
_obj = obj;
_nil = nil;
}
public static TypeMemPtr make(TypeStruct obj, boolean nil) { return new TypeMemPtr(obj, nil).intern(); }
- public static TypeMemPtr make(TypeStruct obj) { return TypeMemPtr.make(obj, false); }
+ public static TypeMemPtr make(TypeStruct obj) { return make(obj, false); }
+ public TypeMemPtr make_from(TypeStruct obj) { return make(obj, _nil); }
public static final TypeMemPtr BOT = make(TypeStruct.BOT,true);
public static final TypeMemPtr TOP = BOT.dual();
@@ -43,17 +49,14 @@ public class TypeMemPtr extends Type {
}
@Override
- public TypeMemPtr dual() { return TypeMemPtr.make(_obj==null ? null : _obj.dual(), !_nil); }
+ public TypeMemPtr dual() { return TypeMemPtr.make(_obj.dual(), !_nil); }
@Override
- public Type glb() {
- if( _obj==null ) return BOT;
- return make(_obj.glb(),true);
- }
+ public Type glb() { return make(_obj.glb(),true); }
@Override public TypeMemPtr makeInit() { return NULLPTR; }
@Override
- int hash() { return (_obj==null ? 0xDEADBEEF : _obj.hashCode()) ^ (_nil ? 1024 : 0); }
+ int hash() { return _obj.hashCode() ^ (_nil ? 1024 : 0); }
@Override
boolean eq(Type t) {
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 44741def..02de7cc8 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -8,9 +8,12 @@ import java.util.ArrayList;
*/
public class TypeStruct extends Type {
- // For now in terms of the lattice
- // a struct type just stays as is
-
+ // Immutable shallow references, resolved by name in Parser.TYPES when needed.
+ // A name-only struct has _fields == null, even after its definition is known.
+ // For LLI { LLI? next; int i; }, L0 is name-only and L1.next points to L0;
+ // TYPES["LLI"] holds L1. We deliberately stop recursive type expansion here,
+ // keeping interning and lattice operations finite and acyclic. Never patch
+ // L0 into L1: field access looks up the complete declaration by name instead.
public final String _name;
public final Field[] _fields;
@@ -20,13 +23,23 @@ public class TypeStruct extends Type {
_fields = fields;
}
+ // Forward-ref version
+ public static TypeStruct make(String name) { return new TypeStruct(name, null).intern(); }
+ // All fields directly listed
public static TypeStruct make(String name, Field[] fields) { return new TypeStruct(name, fields).intern(); }
+ // Parser uses an ArrayList to gather the fields
public static TypeStruct make(String name, ArrayList<Field> fields) { return make(name,fields.toArray(new Field[fields.size()])); }
public static final TypeStruct TOP = make("$TOP",new Field [0]);
public static final TypeStruct BOT = make("$BOT",new Field [0]);
public static final TypeStruct TEST = make("test",new Field[]{Field.TEST});
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); }
+
+ // A pair of self-cyclic types
+ private static final TypeStruct S1F = make("S1");
+ private static final TypeStruct S2F = make("S2");
+ public static final TypeStruct S1 = make("S1", new Field[]{ Field.make("a", TypeInteger.BOT), Field.make("s2",TypeMemPtr.make(S2F,false)) });
+ private static final TypeStruct S2 = make("S2", new Field[]{ Field.make("b", TypeInteger.BOT), Field.make("s1",TypeMemPtr.make(S1F,false)) });
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); }
public int find(String fname) {
for( int i=0; i<_fields.length; i++ )
@@ -51,6 +64,8 @@ public class TypeStruct extends Type {
// And the answer is: "don't ask".
if( !_name.equals(that._name) )
return BOT; // It's a struct; that's about all we know
+ if( this._fields==null ) return this;
+ if( that._fields==null ) return that;
// Now all fields should be the same, so just do field meets
assert _fields.length == that._fields.length;
Field[] flds = new Field[_fields.length];
@@ -63,6 +78,7 @@ public class TypeStruct extends Type {
public TypeStruct dual() {
if( this==TOP ) return BOT;
if( this==BOT ) return TOP;
+ if( _fields == null ) return this;
Field[] flds = new Field[_fields.length];
for( int i=0; i<_fields.length; i++ )
flds[i] = _fields[i].dual();
@@ -80,9 +96,10 @@ public class TypeStruct extends Type {
return make(_name,flds);
}
private boolean _glb() {
- for( Field f : _fields )
- if( f.glb() != f )
- return false;
+ if( _fields!=null )
+ for( Field f : _fields )
+ if( f.glb() != f )
+ return false;
return true;
}
@@ -91,6 +108,7 @@ public class TypeStruct extends Type {
TypeStruct ts = (TypeStruct)t; // Invariant
if( !_name.equals(ts._name) )
return false;
+ if( _fields == ts._fields ) return true;
if( _fields.length!=ts._fields.length )
return false;
for( int i = 0; i < _fields.length; i++ )
@@ -102,7 +120,9 @@ public class TypeStruct extends Type {
@Override
int hash() {
long hash = _name.hashCode();
- for( Field f : _fields ) hash = Utils.rot(hash,13) ^ f.hashCode();
+ if( _fields != null )
+ for( Field f : _fields )
+ hash = Utils.rot(hash,13) ^ f.hashCode();
return Utils.fold(hash);
}
@@ -110,11 +130,12 @@ public class TypeStruct extends Type {
public StringBuilder print(StringBuilder sb) {
if( this==BOT ) return sb.append("StructBot");
if( this==TOP ) return sb.append("StructTop");
- sb.append(_name).append(" {\n");
+ sb.append(_name);
+ if( _fields == null ) return sb; // Forward reference struct, just print the name
+ sb.append(" {\n");
for( Field f : _fields ) {
- sb.append(" ").append(f._fname).append(":");
- f._type.print(sb);
- sb.append(";\n");
+ sb.append(" ").append(f._fname).append(" :");
+ f._type.print(sb).append(";\n");
}
return sb.append("}");
}
diff --git a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index ec7390ca..0aa019a7 100644
--- a/chapter12/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/chapter12/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -58,7 +58,8 @@ public class TypeTuple extends Type {
for( Type t : _types )
t.typeName(sb).append(",");
sb.setLength(sb.length()-1);
- return sb.append("]");
+ sb.append("]");
+ return sb;
}
public static final TypeTuple IF_BOTH = make(new Type[]{Type. CONTROL,Type. CONTROL});