summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java238
-rw-r--r--src/main/java/com/seaofnodes/simple/GraphVisualizer.java27
-rw-r--r--src/main/java/com/seaofnodes/simple/IRPrinter.java92
-rw-r--r--src/main/java/com/seaofnodes/simple/IterPeeps.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/Parser.java50
-rw-r--r--src/main/java/com/seaofnodes/simple/Utils.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CFGNode.java92
-rw-r--r--src/main/java/com/seaofnodes/simple/node/CProjNode.java61
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ConstantNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/IfNode.java17
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoadNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/LoopNode.java60
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MemOpNode.java4
-rw-r--r--src/main/java/com/seaofnodes/simple/node/MultiNode.java8
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NeverNode.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/NewNode.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/node/Node.java46
-rw-r--r--src/main/java/com/seaofnodes/simple/node/OutNode.java7
-rw-r--r--src/main/java/com/seaofnodes/simple/node/PhiNode.java10
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ProjNode.java19
-rw-r--r--src/main/java/com/seaofnodes/simple/node/RegionNode.java39
-rw-r--r--src/main/java/com/seaofnodes/simple/node/ReturnNode.java16
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StartNode.java15
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StopNode.java18
-rw-r--r--src/main/java/com/seaofnodes/simple/node/StoreNode.java6
-rw-r--r--src/main/java/com/seaofnodes/simple/node/SubNode.java1
-rw-r--r--src/main/java/com/seaofnodes/simple/node/XCtrlNode.java13
-rw-r--r--src/main/java/com/seaofnodes/simple/type/Field.java2
-rw-r--r--src/main/java/com/seaofnodes/simple/type/TypeTuple.java3
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter01Test.java2
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter04Test.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter05Test.java14
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter06Test.java16
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter07Test.java48
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter08Test.java24
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter09Test.java33
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter10Test.java26
-rw-r--r--src/test/java/com/seaofnodes/simple/Chapter11Test.java565
-rw-r--r--src/test/java/com/seaofnodes/simple/FuzzerWrap.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/SchedulerTest.java4
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java3
-rw-r--r--src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java12
42 files changed, 1401 insertions, 233 deletions
diff --git a/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
new file mode 100644
index 00000000..14540d46
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -0,0 +1,238 @@
+ package com.seaofnodes.simple;
+
+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 ) {
+ fixLoops(stop);
+ schedEarly();
+ Parser.SCHEDULED = true;
+ schedLate(Parser.START);
+ }
+
+ // ------------------------------------------------------------------------
+ // 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
+ private 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);
+ // Strictly for dead infinite loops, we can have entire code blocks
+ // not reachable from below - so we reach down, from above, one
+ // step. Since _schedEarly modifies the output arrays, the normal
+ // region._outputs ArrayList iterator throws CME. The extra edges
+ // are always *added* after any Phis, so just walk the Phi prefix.
+ if( cfg instanceof RegionNode region ) {
+ int len = region.nOuts();
+ for( int i=0; i<len; i++ )
+ if( region.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 not-pinned not-CFG inputs before self. Since skipping
+ // Pinned, this never walks the backedge of Phis (and thus spins around
+ // a data-only loop, eventually attempting relying on some pre-visited-
+ // not-post-visited data op with no scheduled control.
+ for( Node def : n._inputs )
+ if( def!=null && !def.isPinned() )
+ _schedEarly(def,visit);
+ // If not-pinned (e.g. constants, projections, phi) and not-CFG
+ if( !n.isPinned() ) {
+ // Schedule at deepest input
+ CFGNode early = Parser.START; // Maximally early, lowest idepth
+ for( int i=1; i<n.nIns(); i++ )
+ if( 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(StartNode start) {
+ CFGNode[] late = new CFGNode[Node.UID()];
+ Node[] ns = new Node[Node.UID()];
+ _schedLate(start,ns,late);
+ for( int i=0; i<late.length; i++ )
+ if( ns[i] != null )
+ ns[i].setDef(0,late[i]);
+ }
+
+ // Forwards post-order pass. Schedule all outputs first, then draw an
+ // idom-tree line from the LCA of uses to the early schedule. Schedule is
+ // legal anywhere on this line; pick the most control-dependent (largest
+ // idepth) in the shallowest loop nest.
+ private static void _schedLate(Node n, Node[] ns, CFGNode[] late) {
+ if( late[n._nid]!=null ) return; // Been there, done that
+ // 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);
+ if( n instanceof PhiNode phi ) late[n._nid] = phi.region();
+
+ // Walk Stores before Loads, so we can get the anti-deps right
+ for( Node use : n._outputs )
+ if( isForwardsEdge(use,n) &&
+ use._type instanceof TypeMem )
+ _schedLate(use,ns,late);
+ // Walk everybody now
+ for( Node use : n._outputs )
+ if( isForwardsEdge(use,n) )
+ _schedLate(use,ns,late);
+ // Already implicitly scheduled
+ if( n.isPinned() ) return;
+ // Need to schedule n
+
+ // Walk uses, gathering the LCA (Least Common Ancestor) of uses
+ CFGNode early = (CFGNode)n.in(0);
+ assert early != null;
+ CFGNode lca = null;
+ for( Node use : n._outputs )
+ lca = use_block(n,use, late).idom(lca);
+
+ // Loads may need anti-dependencies, raising their LCA
+ if( n instanceof LoadNode load )
+ lca = find_anti_dep(lca,load,early,late);
+
+ // 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 )
+ if( found==null ) found = phi.region().cfg(i);
+ else Utils.TODO(); // Can be more than once
+ 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.idepth() > best.idepth() || best instanceof IfNode);
+ }
+
+ // Skip iteration if a backedge
+ private static boolean isForwardsEdge(Node use, Node def) {
+ return use != null && def != null &&
+ !(use.nIns()>2 && use.in(2)==def && (use instanceof LoopNode || (use instanceof PhiNode phi && phi.region() instanceof LoopNode)));
+ }
+
+ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) {
+ // 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() )
+ cfg._anti = load._nid;
+ // Walk load->mem uses, looking for Stores causing an anti-dep
+ for( Node mem : load.mem()._outputs ) {
+ switch( mem ) {
+ case StoreNode st:
+ lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
+ 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);
+ break;
+ case LoadNode ld: break; // Loads do not cause anti-deps on other loads
+ case ReturnNode ret: break; // Load must already be ahead of Return
+ default: throw Utils.TODO();
+ }
+ }
+ return lca;
+ }
+
+ //
+ private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) {
+ // Walk store blocks "reach" from its scheduled location to its earliest
+ for( ; stblk != defblk.idom(); stblk = stblk.idom() ) {
+ // Store and Load overlap, need anti-dependence
+ if( stblk._anti==load._nid ) {
+ lca = stblk.idom(lca); // 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/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
index cfe6d5cc..604770a4 100644
--- a/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
+++ b/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.node.*;
import java.util.*;
@@ -27,7 +28,7 @@ public class GraphVisualizer {
// nodes in the graph.
Collection<Node> all = findAll(stop, scope);
StringBuilder sb = new StringBuilder();
- sb.append("digraph chapter10 {\n");
+ sb.append("digraph chapter11 {\n");
sb.append("/*\n");
sb.append(stop._src);
sb.append("\n*/\n");
@@ -79,7 +80,7 @@ public class GraphVisualizer {
// Just the Nodes first, in a cluster no edges
sb.append(doCtrl ? "\tsubgraph cluster_Controls {\n" : "\tsubgraph cluster_Nodes {\n"); // Magic "cluster_" in the subgraph name
for (Node n : all) {
- if (n instanceof ProjNode || n instanceof ScopeNode)
+ if (n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n==Parser.XCTRL)
continue; // Do not emit, rolled into MultiNode or Scope cluster already
if (_separateControlCluster && doCtrl && !n.isCFG()) continue;
if (_separateControlCluster && !doCtrl && n.isCFG()) continue;
@@ -100,9 +101,16 @@ public class GraphVisualizer {
sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n");
sb.append("\t\t\t\t<TR>");
}
- sb.append("<TD PORT=\"p").append(proj._idx).append("\"");
- if (proj.isCFG()) sb.append(" BGCOLOR=\"yellow\"");
- sb.append(">").append(proj.glabel()).append("</TD>");
+ sb.append("<TD PORT=\"p").append(proj._idx).append("\">").append(proj.glabel()).append("</TD>");
+ }
+ if (use instanceof CProjNode proj) {
+ if (!doProjTable) {
+ doProjTable = true;
+ sb.append("<TD>").append("\n");
+ sb.append("\t\t\t\t<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">").append("\n");
+ sb.append("\t\t\t\t<TR>");
+ }
+ sb.append("<TD PORT=\"p").append(proj._idx).append("\" BGCOLOR=\"yellow\">").append(proj.glabel()).append("</TD>");
}
}
if (doProjTable) {
@@ -178,7 +186,7 @@ public class GraphVisualizer {
// Do not display the Constant->Start edge;
// ProjNodes handled by Multi;
// ScopeNodes are done separately
- if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof ScopeNode )
+ if( n instanceof ConstantNode || n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeNode || n==Parser.XCTRL )
continue;
for( int i=0; i<n.nIns(); i++ ) {
Node def = n.in(i);
@@ -191,9 +199,12 @@ public class GraphVisualizer {
} else if( def != null ) {
// Most edges land here use->def
sb.append('\t').append(n.uniqueName()).append(" -> ");
- if( def instanceof ProjNode proj ) {
+ if( def instanceof CProjNode proj ) {
String mname = proj.ctrl().uniqueName();
sb.append(mname).append(":p").append(proj._idx);
+ } else if( def instanceof ProjNode proj ) {
+ String mname = proj.in(0).uniqueName();
+ sb.append(mname).append(":p").append(proj._idx);
} else sb.append(def.uniqueName());
// Number edges, so we can see how they track
sb.append("[taillabel=").append(i);
@@ -231,7 +242,7 @@ public class GraphVisualizer {
.append(scopeName).append(":")
.append('"').append(makePortName(scopeName, name)).append('"') // wrap port name with quotes because $ctrl is not valid unquoted
.append(" -> ");
- if( def instanceof ProjNode proj ) {
+ if( def instanceof CProjNode proj ) {
String mname = proj.ctrl().uniqueName();
sb.append(mname).append(":p").append(proj._idx);
} else sb.append(def.uniqueName());
diff --git a/src/main/java/com/seaofnodes/simple/IRPrinter.java b/src/main/java/com/seaofnodes/simple/IRPrinter.java
index f08f3208..43c6c9f7 100644
--- a/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/src/main/java/com/seaofnodes/simple/IRPrinter.java
@@ -1,11 +1,10 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.node.LoopNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.node.ProjNode;
-
+import com.seaofnodes.simple.node.*;
+import java.lang.StringBuilder;
import java.util.ArrayList;
import java.util.BitSet;
+import java.util.HashMap;
public class IRPrinter {
@@ -20,12 +19,12 @@ public class IRPrinter {
}
for( Node def : n._inputs )
sb.append(def==null ? "____ " : "%4d ".formatted(def._nid));
- for( int i = n._inputs.size(); i<3; i++ )
+ for( int i = n._inputs.size(); i<4; i++ )
sb.append(" ");
sb.append(" [[ ");
for( Node use : n._outputs )
sb.append(use==null ? "____ " : "%4d ".formatted(use._nid));
- int lim = 5 - Math.max(n._inputs.size(),3);
+ int lim = 6 - Math.max(n._inputs.size(),4);
for( int i = n._outputs.size(); i<lim; i++ )
sb.append(" ");
sb.append(" ]] ");
@@ -68,7 +67,9 @@ public class IRPrinter {
}
public static String prettyPrint(Node node, int depth) {
- return prettyPrint( node, depth, false );
+ return Parser.SCHEDULED
+ ? prettyPrintScheduled( node, depth, false )
+ : prettyPrint( node, depth, false );
}
// Another bulk pretty-printer. Makes more effort at basic-block grouping.
@@ -185,4 +186,81 @@ public class IRPrinter {
return false;
}
}
+
+ // Bulk pretty printer, knowing scheduling information is available
+ public static String prettyPrintScheduled( Node node, int depth, boolean llvmFormat ) {
+ // Backwards DFS walk to depth.
+ HashMap<Node,Integer> ds = new HashMap<>();
+ _walk(ds,node,depth);
+ // Print by block with least idepth
+ StringBuilder sb = new StringBuilder();
+ ArrayList<Node> bns = new ArrayList<>();
+ while( !ds.isEmpty() ) {
+ CFGNode blk = null;
+ for( Node n : ds.keySet() ) {
+ CFGNode cfg = n instanceof CFGNode cfg0 && cfg0.blockHead() ? cfg0 : (CFGNode)n.in(0);
+ if( blk==null || cfg.idepth() < blk.idepth() )
+ blk = cfg;
+ }
+ ds.remove(blk);
+
+ // Print block header
+ sb.append("%-13.13s".formatted(label(blk)+":"));
+ sb.append( " ".repeat(4) ).append(" [[ ");
+ if( blk instanceof RegionNode || blk instanceof StopNode )
+ for( int i=(blk instanceof StopNode ? 0 : 1); i<blk.nIns(); i++ )
+ label(sb,blk.cfg(i));
+ else if( !(blk instanceof StartNode) )
+ label(sb,blk.cfg(0));
+ sb.append(" ]] \n");
+
+ // Collect block contents that are in the depth limit
+ bns.clear();
+ int xd = Integer.MAX_VALUE;
+ for( Node use : blk._outputs ) {
+ Integer i = ds.get(use);
+ if( i!=null && !(use instanceof CFGNode cfg && cfg.blockHead()) ) {
+ bns.add(use);
+ xd = Math.min(xd,i);
+ }
+ }
+ // Print Phis up front, if any
+ for( int i=0; i<bns.size(); i++ )
+ if( bns.get(i) instanceof PhiNode phi )
+ printLine( phi, sb, llvmFormat,bns,i--,ds);
+
+ // Print block contents in depth order, bumping depth until whole block printed
+ for( ; !bns.isEmpty(); xd++ )
+ for( int i=0; i<bns.size(); i++ )
+ if( ds.get(bns.get(i))==xd )
+ printLine( bns.get(i), sb, llvmFormat, bns, i--, ds );
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+
+ private static void _walk( HashMap<Node,Integer> ds, Node node, int d ) {
+ Integer nd = ds.get(node);
+ if( nd!=null && d <= nd ) return; // Been there, done that
+ ds.put(node,d);
+ if( d == 0 ) return; // Depth cutoff
+ for( Node def : node._inputs )
+ if( def != null )
+ _walk(ds,def,d-1);
+ }
+
+ static String label( CFGNode blk ) {
+ if( blk instanceof StartNode ) return "START";
+ return (blk instanceof LoopNode ? "LOOP" : "L")+blk._nid;
+ }
+ static void label( StringBuilder sb, CFGNode blk ) {
+ if( !blk.blockHead() ) blk = blk.cfg(0);
+ sb.append( "%-9.9s ".formatted( label( blk ) ) );
+ }
+ static void printLine( Node n, StringBuilder sb, boolean llvmFormat, ArrayList<Node> bns, int i, HashMap<Node,Integer> ds ) {
+ printLine( n, sb, llvmFormat );
+ Utils.del(bns,i);
+ ds.remove(n);
+ }
+
}
diff --git a/src/main/java/com/seaofnodes/simple/IterPeeps.java b/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 1cee1ccb..b2b76777 100644
--- a/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -94,6 +94,7 @@ public abstract class IterPeeps {
if( show )
System.out.println(new GraphVisualizer().generateDotOutput(stop,null,null));
+ GlobalCodeMotion.buildCFG(stop);
return stop;
}
diff --git a/src/main/java/com/seaofnodes/simple/Parser.java b/src/main/java/com/seaofnodes/simple/Parser.java
index fc84c9d9..e367f499 100644
--- a/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/src/main/java/com/seaofnodes/simple/Parser.java
@@ -23,8 +23,14 @@ public class Parser {
*/
public static StartNode START;
+ public static ConstantNode ZERO;
+ public static XCtrlNode XCTRL;
+
public StopNode STOP;
+ // 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;
@@ -77,11 +83,14 @@ public class Parser {
Node.reset();
IterPeeps.reset();
OBJS.clear();
+ SCHEDULED = false;
_lexer = new Lexer(source);
_scope = new ScopeNode();
_continueScope = _breakScope = null;
START = new StartNode(new Type[]{ Type.CONTROL, arg });
STOP = new StopNode(source);
+ ZERO = new ConstantNode(TypeInteger.constant(0)).peephole().keep();
+ XCTRL= new XCtrlNode().peephole().keep();
}
public Parser(String source) {
@@ -105,9 +114,11 @@ 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 ProjNode(START, 0, ScopeNode.CTRL).peephole());
- _scope.define(ScopeNode.ARG0, TypeInteger.BOT, new ProjNode(START, 1, ScopeNode.ARG0).peephole());
+ _scope.define(ScopeNode.CTRL, Type.CONTROL , new CProjNode(START, 0, ScopeNode.CTRL).peephole());
+ _scope.define(ScopeNode.ARG0, TypeInteger.BOT, new ProjNode(START, 1, ScopeNode.ARG0).peephole());
parseBlock();
+ if( ctrl()._type==Type.CONTROL )
+ STOP.addReturn(new ReturnNode(ctrl(), new ConstantNode(TypeInteger.constant(0)).peephole(), _scope).peephole());
_scope.pop();
_xScopes.pop();
if (!_lexer.isEOF()) throw error("Syntax error, unexpected " + _lexer.getAnyNextToken());
@@ -236,8 +247,8 @@ public class Parser {
// IfNode takes current control and predicate
Node ifNode = new IfNode(ctrl(), pred).peephole();
// Setup projection nodes
- Node ifT = new ProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
- Node ifF = new ProjNode(ifNode.unkeep(), 1, "False").peephole();
+ Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
+ Node ifF = new CProjNode(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
@@ -283,7 +294,7 @@ public class Parser {
private ScopeNode jumpTo(ScopeNode toScope) {
ScopeNode cur = _scope.dup();
- ctrl(new ConstantNode(Type.XCONTROL).peephole()); // Kill current scope
+ ctrl(XCTRL); // 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() )
@@ -319,8 +330,8 @@ public class Parser {
// IfNode takes current control and predicate
Node ifNode = new IfNode(ctrl(), pred).peephole();
// Setup projection nodes
- Node ifT = new ProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
- Node ifF = new ProjNode(ifNode.unkeep(), 1, "False").peephole().keep();
+ Node ifT = new CProjNode(ifNode. keep(), 0, "True" ).peephole().keep();
+ Node ifF = new CProjNode(ifNode.unkeep(), 1, "False").peephole().keep();
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
int ndefs = _scope.nIns();
@@ -366,7 +377,7 @@ public class Parser {
private Node parseReturn() {
var expr = require(parseExpression(), ";");
Node ret = STOP.addReturn(new ReturnNode(ctrl(), expr, _scope).peephole());
- ctrl(new ConstantNode(Type.XCONTROL).peephole()); // Kill control
+ ctrl(XCTRL); // Kill control
return ret;
}
@@ -568,7 +579,7 @@ public class Parser {
if( _lexer.isNumber() ) return parseIntegerLiteral();
if( match("(") ) return require(parseExpression(), ")");
if( matchx("true" ) ) return new ConstantNode(TypeInteger.constant(1)).peephole();
- if( matchx("false") ) return new ConstantNode(TypeInteger.constant(0)).peephole();
+ if( matchx("false") ) return ZERO;
if( matchx("null" ) ) return new ConstantNode(TypeMemPtr.NULLPTR).peephole();
if( matchx("new") ) {
String structName = requireId();
@@ -586,12 +597,11 @@ public class Parser {
/**
* Return a NewNode but also generate instructions to initialize it.
*/
- private Node newStruct( TypeStruct obj ) {
+ private Node newStruct(TypeStruct obj) {
Node n = new NewNode(TypeMemPtr.make(obj), ctrl()).peephole().keep();
- Node initValue = new ConstantNode(TypeInteger.constant(0)).peephole();
int alias = START._aliasStarts.get(obj._name);
for( Field field : obj._fields ) {
- memAlias(alias, new StoreNode(field._fname, alias, memAlias(alias), n, initValue).peephole());
+ memAlias(alias, new StoreNode(field._fname, alias, ctrl(), memAlias(alias), n, ZERO).peephole());
alias++;
}
return n.unkeep();
@@ -629,7 +639,7 @@ public class Parser {
if( peek('=') ) _lexer._position--;
else {
Node val = parseExpression();
- memAlias(alias, new StoreNode(name, alias, memAlias(alias), expr, val).peephole());
+ memAlias(alias, new StoreNode(name, alias, ctrl(), memAlias(alias), expr, val).peephole());
return expr; // "obj.a = expr" returns the expression while updating memory
}
}
@@ -740,7 +750,16 @@ public class Parser {
* Return the next non-white-space character
*/
private void skipWhiteSpace() {
- while (isWhiteSpace()) _position++;
+ 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;
+ }
}
@@ -822,8 +841,9 @@ 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/src/main/java/com/seaofnodes/simple/Utils.java b/src/main/java/com/seaofnodes/simple/Utils.java
index 4f2af769..f3ac25b1 100644
--- a/src/main/java/com/seaofnodes/simple/Utils.java
+++ b/src/main/java/com/seaofnodes/simple/Utils.java
@@ -55,11 +55,4 @@ 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/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
new file mode 100644
index 00000000..b605071b
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/CFGNode.java
@@ -0,0 +1,92 @@
+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; }
+ @Override public boolean isPinned() { 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 int _idepth;
+ public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+
+ // Return the immediate dominator of this Node and compute dom tree depth.
+ public CFGNode idom() { return cfg(0); }
+
+ // Return the LCA of two idoms
+ public CFGNode idom(CFGNode rhs) {
+ if( rhs==null ) return this;
+ CFGNode lhs = this;
+ while( lhs != rhs ) {
+ var comp = lhs.idepth() - rhs.idepth();
+ if( comp >= 0 ) lhs = lhs.idom();
+ if( comp <= 0 ) rhs = rhs.idom();
+ }
+ return lhs;
+ }
+
+ // Loop nesting depth
+ public int _loopDepth;
+ public int loopDepth() { return _loopDepth==0 ? (_loopDepth = cfg(0).loopDepth()) : _loopDepth; }
+
+ // Anti-dependence field support
+ public int _anti; // Per-CFG field to help find anti-deps
+
+ // ------------------------------------------------------------------------
+ // 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/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
new file mode 100644
index 00000000..fb0ff879
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -0,0 +1,61 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.type.Type;
+import com.seaofnodes.simple.type.TypeMem;
+import com.seaofnodes.simple.type.TypeTuple;
+import java.util.BitSet;
+
+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 StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_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/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index ca42c2dc..cf1a76b0 100644
--- a/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -36,6 +36,8 @@ public class ConstantNode extends Node {
}
@Override public boolean isMultiTail() { return true; }
+ // The special forever-zero is pinned, all others can sink
+ @Override public boolean isPinned() { return this==Parser.ZERO; }
@Override
public Type compute() { return _con; }
diff --git a/src/main/java/com/seaofnodes/simple/node/IfNode.java b/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 1cf4ddaf..5457ac6e 100644
--- a/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -5,8 +5,9 @@ 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 MultiNode {
+public class IfNode extends CFGNode implements MultiNode {
public IfNode(Node ctrl, Node pred) {
super(ctrl, pred);
@@ -22,7 +23,6 @@ public class IfNode extends MultiNode {
return in(1)._print0(sb, visited).append(" )");
}
- @Override public boolean isCFG() { return true; }
@Override public boolean isMultiHead() { return true; }
public Node ctrl() { return in(0); }
@@ -52,11 +52,20 @@ public class IfNode extends 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( 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 ) {
+ 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 ) {
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/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 53ae1096..296237f4 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -21,7 +21,7 @@ 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, memSlice, memPtr, null);
+ super(name, alias, null, memSlice, memPtr);
_declaredType = glb;
}
@@ -45,7 +45,7 @@ public class LoadNode extends MemOpNode {
// Simple Load-after-Store on same address.
if( mem() instanceof StoreNode st &&
ptr() == st.ptr() ) { // Must check same object
- assert Utils.eq(_name,st._name); // Equiv class aliasing is perfect
+ assert _name.equals(st._name); // Equiv class aliasing is perfect
return st.val();
}
diff --git a/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
index ea1489c4..c662dee6 100644
--- a/src/main/java/com/seaofnodes/simple/node/LoopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/LoopNode.java
@@ -1,15 +1,17 @@
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); }
- Node entry() { return in(1); }
- Node back () { return in(2); }
+ public CFGNode entry() { return cfg(1); }
+ public CFGNode back () { return cfg(2); }
@Override
public String label() { return "Loop"; }
@@ -25,7 +27,51 @@ public class LoopNode extends RegionNode {
return inProgress() ? null : super.idealize();
}
- // Bypass Region idom, same as the default idom() using use entry in(1) instead of in(0)
- @Override int idepth() { return _idepth(1); }
- @Override Node idom() { return entry(); }
+ // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
+ @Override public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; }
+ // Bypass Region idom, same as the default idom() using use in(1) instead of in(0)
+ @Override public CFGNode idom() { 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 )
+ 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));
+ }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 08e08bbf..551d2aa9 100644
--- a/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -12,8 +12,8 @@ public abstract class MemOpNode extends Node {
public final String _name;
public final int _alias;
- public MemOpNode(String name, int alias, Node memSlice, Node memPtr, Node value) {
- super(null, memSlice, memPtr, value);
+ public MemOpNode(String name, int alias, Node... nodes) {
+ super(nodes);
_name = name;
_alias = alias;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/MultiNode.java b/src/main/java/com/seaofnodes/simple/node/MultiNode.java
index 2743bd19..379e140d 100644
--- a/src/main/java/com/seaofnodes/simple/node/MultiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/MultiNode.java
@@ -1,12 +1,10 @@
package com.seaofnodes.simple.node;
-public abstract class MultiNode extends Node {
-
- public MultiNode(Node... inputs) { super(inputs); }
+public interface MultiNode extends OutNode {
// Find a projection by index
- ProjNode proj( int idx ) {
- for( Node out : _outputs )
+ default ProjNode proj( int idx ) {
+ for( Node out : outs() )
if( out instanceof ProjNode prj && prj._idx==idx )
return prj;
return null;
diff --git a/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
new file mode 100644
index 00000000..f50d3503
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/NeverNode.java
@@ -0,0 +1,19 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.type.TypeInteger;
+import com.seaofnodes.simple.type.Type;
+import java.util.BitSet;
+
+// "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 StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Never"); }
+
+ @Override public Type compute() { return TypeInteger.BOT; }
+
+ @Override public Node idealize() { return null; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/NewNode.java b/src/main/java/com/seaofnodes/simple/node/NewNode.java
index 352689b5..0724ec56 100644
--- a/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -26,6 +26,8 @@ public class NewNode extends Node {
return sb.append(_ptr._obj.str());
}
+ @Override public boolean isPinned() { return true; }
+
@Override
public Type compute() { return _ptr; }
diff --git a/src/main/java/com/seaofnodes/simple/node/Node.java b/src/main/java/com/seaofnodes/simple/node/Node.java
index bb698860..fc49ad6a 100644
--- a/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.IRPrinter;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
@@ -13,7 +14,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 {
+public abstract class Node implements OutNode {
/**
* Each node has a unique dense Node ID within a compilation context
@@ -128,6 +129,7 @@ public abstract class Node {
*/
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(); }
@@ -135,9 +137,7 @@ public abstract class Node {
public boolean isUnused() { return nOuts() == 0; }
- public boolean isCFG() { return false; }
-
- public boolean isMem() { return false; }
+ public CFGNode cfg0() { return (CFGNode)in(0); }
/**
* Change a <em>def</em> into a Node. Keeps the edges correct, by removing
@@ -164,11 +164,11 @@ public abstract class Node {
// 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;
@@ -196,7 +196,7 @@ public abstract class Node {
* @param new_def the new definition, appended to the end of existing definitions
* @return new_def for flow coding
*/
- Node addDef(Node new_def) {
+ public Node addDef(Node new_def) {
unlock();
// Add use->def edge
_inputs.add(new_def);
@@ -323,8 +323,8 @@ public abstract class Node {
Type old = setType(compute());
// Replace constant computations from non-constants with a constant node
- if( !(this instanceof ConstantNode) && _type.isHighOrConst() )
- return new ConstantNode(_type).peepholeOpt();
+ if( !(this instanceof ConstantNode) && !(this instanceof XCtrlNode) && _type.isHighOrConst() )
+ return (_type==Type.XCONTROL ? new XCtrlNode() : new ConstantNode(_type)).peepholeOpt();
// Global Value Numbering
if( _hash==0 ) {
@@ -531,6 +531,20 @@ public abstract class Node {
// Subclasses add extra hash info (such as ConstantNodes constant)
int hash() { return 0; }
+ // ------------------------------------------------------------------------
+ //
+
+ /** Is this Node control-flow-graph related */
+ public boolean isCFG() { return false; }
+
+ /** Is this Node Memory related */
+ public boolean isMem() { return false; }
+
+ /** Return block start from a isCFG() */
+ public Node getBlockStart() { return null; }
+
+ /** Pinned in the schedule */
+ public boolean isPinned() { return false; }
// ------------------------------------------------------------------------
// Peephole utilities
@@ -561,20 +575,6 @@ public abstract class Node {
}
- /**
- * 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 int _idepth; // IDOM depth approx; Zero is unset; non-zero is cached legit
- final int _idepth(int idx) { return _idepth==0 ? (_idepth=in(idx).idepth()+1) : _idepth; }
- int idepth() { return _idepth(0); }
-
- // 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/src/main/java/com/seaofnodes/simple/node/OutNode.java b/src/main/java/com/seaofnodes/simple/node/OutNode.java
new file mode 100644
index 00000000..7e531145
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/OutNode.java
@@ -0,0 +1,7 @@
+package com.seaofnodes.simple.node;
+
+import java.util.ArrayList;
+
+public interface OutNode {
+ ArrayList<Node> outs();
+}
diff --git a/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index dd67bd06..a07dc140 100644
--- a/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -35,10 +35,10 @@ public class PhiNode extends Node {
return sb;
}
- Node region() { return in(0); }
+ public CFGNode region() { return (CFGNode)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 boolean isPinned() { return true; }
@Override
public Type compute() {
@@ -102,9 +102,9 @@ public class PhiNode extends Node {
Node val = in(3-nullx);
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.
- Node idom = region().in(nullx);
+ CFGNode idom = (CFGNode)region().in(nullx);
while( idom.in(0) != iff ) idom = idom.idom();
- if( idom instanceof ProjNode proj && proj._idx==1 )
+ if( idom instanceof CProjNode proj && proj._idx==1 )
return val;
}
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ProjNode.java b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
index 735fcdf3..f94e98a7 100644
--- a/src/main/java/com/seaofnodes/simple/node/ProjNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ProjNode.java
@@ -14,7 +14,7 @@ public class ProjNode extends Node {
// Debugging label
public final String _label;
- public ProjNode(MultiNode ctrl, int idx, String label) {
+ public ProjNode(Node ctrl, int idx, String label) {
super(ctrl);
_idx = idx;
_label = label;
@@ -26,11 +26,9 @@ public class ProjNode extends Node {
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(_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 boolean isPinned() { return true; }
@Override
public Type compute() {
@@ -38,18 +36,7 @@ public class ProjNode extends Node {
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 ProjNode((MultiNode)new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True");
- }
- return null;
- }
+ @Override public Node idealize() { return null; }
@Override
boolean eq( Node n ) { return _idx == ((ProjNode)n)._idx; }
diff --git a/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 42501118..dcc7ccef 100644
--- a/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -1,14 +1,15 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
-
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
+import java.util.HashSet;
-public class RegionNode extends Node {
+public class RegionNode extends CFGNode {
public RegionNode(Node... nodes) { super(nodes); }
@@ -20,8 +21,8 @@ public class RegionNode extends Node {
return sb.append(label()).append(_nid);
}
- @Override public boolean isCFG() { return true; }
@Override public boolean isMultiHead() { return true; }
+ @Override public boolean blockHead() { return true; }
@Override
public Type compute() {
@@ -57,7 +58,7 @@ public class RegionNode extends Node {
IterPeeps.addAll(phi._outputs);
}
}
- return isDead() ? new ConstantNode(Type.XCONTROL) : delDef(path);
+ return isDead() ? Parser.XCTRL : delDef(path);
}
// If down to a single input, become that input
if( nIns()==2 && !hasPhi() )
@@ -89,31 +90,31 @@ public class RegionNode extends Node {
}
// Immediate dominator of Region is a little more complicated.
- @Override int idepth() {
+ @Override public int idepth() {
if( _idepth!=0 ) return _idepth;
int d=0;
for( Node n : _inputs )
if( n!=null )
- d = Math.max(d,n.idepth());
+ d = Math.max(d,((CFGNode)n).idepth()+1);
return _idepth=d;
}
- @Override Node idom() {
- if( nIns()==2 ) return in(1); // 1-input is that one input
- if( nIns()!=3 ) return null; // Fails for anything other than 2-inputs
+ @Override public CFGNode idom() {
+ CFGNode lca = null;
// Walk the LHS & RHS idom trees in parallel until they match, or either fails.
// Because this does not cache, it can be linear in the size of the program.
- Node lhs = in(1);
- Node rhs = in(2);
- while( lhs != rhs ) {
- if( lhs==null || rhs==null ) return null;
- var comp = lhs.idepth() - rhs.idepth();
- if( comp >= 0 ) lhs = lhs.idom();
- if( comp <= 0 ) rhs = rhs.idom();
- }
- return lhs;
+ for( int i=1; i<nIns(); i++ )
+ lca = cfg(i).idom(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;
@@ -123,4 +124,6 @@ public class RegionNode extends Node {
@Override boolean eq( Node n ) {
return !inProgress();
}
+
+ @Override public Node getBlockStart() { return this; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 92bda5bf..444b76a8 100644
--- a/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -13,16 +13,18 @@ import java.util.BitSet;
* <p>
* The Return's output is the value from the data node.
*/
-public class ReturnNode extends Node {
+public class ReturnNode extends CFGNode {
public ReturnNode(Node ctrl, Node data, ScopeNode scope) {
super(ctrl, data);
// We lookup memory slices by the naming convention that they start with $
// We could also use implicit knowledge that all memory projects are at offset >= 2
- String[] names = scope.reverseNames();
- for (String name: names) {
- if (!name.equals("$ctrl") && name.startsWith("$"))
- addDef(scope.lookup(name));
+ if( scope != null ) {
+ String[] names = scope.reverseNames();
+ for (String name: names) {
+ if (!name.equals("$ctrl") && name.startsWith("$"))
+ addDef(scope.lookup(name));
+ }
}
}
@@ -39,8 +41,6 @@ public class ReturnNode extends Node {
return sb.append(";");
}
- @Override public boolean isCFG() { return true; }
-
@Override
public Type compute() {
return TypeTuple.make(ctrl()._type,expr()._type);
@@ -52,4 +52,6 @@ public class ReturnNode extends Node {
return ctrl();
return null;
}
+
+ @Override public Node getBlockStart() { return ctrl().getBlockStart(); }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StartNode.java b/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 4b7e02a8..62fc8a6b 100644
--- a/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -5,6 +5,7 @@ import com.seaofnodes.simple.type.*;
import java.util.Arrays;
import java.util.BitSet;
import java.util.HashMap;
+import java.util.HashSet;
/**
* The Start node represents the start of the function.
@@ -13,7 +14,7 @@ import java.util.HashMap;
* In ch10 we also add mem aliases as structs get defined; each field in struct
* adds a distinct alias to Start's tuple.
*/
-public class StartNode extends MultiNode {
+public class StartNode extends CFGNode implements MultiNode {
// This field is NOT final, because the tuple expands as we add memory aliases
TypeTuple _args;
@@ -63,8 +64,8 @@ public class StartNode extends MultiNode {
return sb.append(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; }
@@ -73,6 +74,12 @@ public class StartNode extends MultiNode {
public Node idealize() { return null; }
// No immediate dominator, and idepth==0
- @Override int idepth() { return 0; }
- @Override Node idom() { return null; }
+ @Override public int idepth() { return 0; }
+ @Override public CFGNode idom() { return null; }
+
+ @Override void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) { }
+
+ @Override public int loopDepth() { return (_loopDepth=1); }
+
+ @Override public Node getBlockStart() { return this; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StopNode.java b/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 3b5a7ce3..94b2ad6d 100644
--- a/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -5,7 +5,7 @@ import com.seaofnodes.simple.IterPeeps;
import java.util.BitSet;
-public class StopNode extends Node {
+public class StopNode extends CFGNode {
public final String _src;
@@ -28,7 +28,7 @@ public class StopNode extends Node {
return sb.append("]");
}
- @Override public boolean isCFG() { return true; }
+ @Override public boolean blockHead() { return true; }
// If a single Return, return it.
// Otherwise, null because ambiguous.
@@ -51,7 +51,17 @@ public class StopNode extends Node {
return null;
}
- @Override public Node idom() { 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 _idepth=d;
+ }
+ @Override public CFGNode idom() { return null; }
+
+ @Override public int loopDepth() { return (_loopDepth=1); }
public Node addReturn(Node node) {
return addDef(node);
@@ -64,4 +74,6 @@ public class StopNode extends Node {
if( err != null ) throw new RuntimeException(err);
return this;
}
+
+ @Override public Node getBlockStart() { return this; }
}
diff --git a/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 8d264601..db8e2ce6 100644
--- a/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -17,8 +17,8 @@ public class StoreNode extends MemOpNode {
* @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 memSlice, Node memPtr, Node value) {
- super(name, alias, memSlice, memPtr, value);
+ public StoreNode(String name, int alias, Node ctrl, Node memSlice, Node memPtr, Node value) {
+ super(name, alias, ctrl, memSlice, memPtr, value);
}
@Override
@@ -48,7 +48,7 @@ public class StoreNode extends MemOpNode {
// Must have exactly one use of "this" or you get weird
// non-serializable memory effects in the worse case.
st.checkNoUseBeyond(this) ) {
- assert Utils.eq(_name,st._name); // Equiv class aliasing is perfect
+ assert _name.equals(st._name); // Equiv class aliasing is perfect
setDef(1,st.mem());
return this;
}
diff --git a/src/main/java/com/seaofnodes/simple/node/SubNode.java b/src/main/java/com/seaofnodes/simple/node/SubNode.java
index b87115cd..5ddcdb68 100644
--- a/src/main/java/com/seaofnodes/simple/node/SubNode.java
+++ b/src/main/java/com/seaofnodes/simple/node/SubNode.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeInteger;
diff --git a/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
new file mode 100644
index 00000000..83779dd2
--- /dev/null
+++ b/src/main/java/com/seaofnodes/simple/node/XCtrlNode.java
@@ -0,0 +1,13 @@
+package com.seaofnodes.simple.node;
+
+import com.seaofnodes.simple.Parser;
+import com.seaofnodes.simple.type.Type;
+import java.util.BitSet;
+
+public class XCtrlNode extends CFGNode {
+ public XCtrlNode( ) { super(Parser.START); }
+ @Override public String label() { return "Xctrl"; }
+ @Override StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append("Xctrl"); }
+ @Override public Type compute() { return Type.XCONTROL; }
+ @Override public Node idealize() { return null; }
+}
diff --git a/src/main/java/com/seaofnodes/simple/type/Field.java b/src/main/java/com/seaofnodes/simple/type/Field.java
index 56626482..e1c337d4 100644
--- a/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -47,7 +47,7 @@ public class Field extends Type {
boolean eq(Type t) {
Field f = (Field)t;
- return Utils.eq(_fname,f._fname) && _type==f._type;
+ return _fname.equals(f._fname) && _type==f._type;
}
diff --git a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index d57f6c98..01bcbe9f 100644
--- a/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -58,8 +58,7 @@ public class TypeTuple extends Type {
for( Type t : _types )
t.typeName(sb).append(",");
sb.setLength(sb.length()-1);
- sb.append("]");
- return sb;
+ return sb.append("]");
}
public static final TypeTuple IF_BOTH = make(new Type[]{Type. CONTROL,Type. CONTROL});
diff --git a/src/test/java/com/seaofnodes/simple/Chapter01Test.java b/src/test/java/com/seaofnodes/simple/Chapter01Test.java
index 89882b2f..c10dc510 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter01Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter01Test.java
@@ -15,7 +15,7 @@ public class Chapter01Test {
StartNode start = Parser.START;
ReturnNode ret = (ReturnNode)stop.in(0);
- assertTrue(ret.ctrl() instanceof ProjNode);
+ assertTrue(ret.ctrl() instanceof CProjNode);
Node expr = ret.expr();
if( expr instanceof ConstantNode con ) {
assertEquals(start,con.in(0));
diff --git a/src/test/java/com/seaofnodes/simple/Chapter04Test.java b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
index e23a10b1..70f38dd6 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter04Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter04Test.java
@@ -55,8 +55,8 @@ public class Chapter04Test {
Parser parser = new Parser("return arg; ");
StopNode stop = parser.parse();
ReturnNode ret = stop.ret();
- assertTrue(ret.in(0) instanceof ProjNode);
- assertTrue(ret.in(1) instanceof ProjNode);
+ assertTrue(ret.in(0) instanceof CProjNode);
+ assertTrue(ret.in(1) instanceof ProjNode);
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 35e54fa9..8d15493f 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -22,7 +22,7 @@ else {
return a;""");
StopNode ret = parser.parse().iterate();
- assertEquals("return Phi(Region18,(arg+2),(arg-3));", ret.toString());
+ assertEquals("return Phi(Region20,(arg+2),(arg-3));", ret.toString());
}
@Test
@@ -37,7 +37,7 @@ if (arg == 1) {
}
return c;""", TypeInteger.BOT);
StopNode ret = parser.parse().iterate();
- assertEquals("return Phi(Region16,4,3);", ret.toString());
+ assertEquals("return Phi(Region18,4,3);", ret.toString());
}
@Test
@@ -65,7 +65,7 @@ else
b=a+1;
return a+b;""");
StopNode ret = parser.parse().iterate();
- assertEquals("return ((arg*2)+Phi(Region21,2,3));", ret.toString());
+ assertEquals("return ((arg*2)+Phi(Region23,2,3));", ret.toString());
}
@Test
@@ -80,7 +80,7 @@ else
a=b+1;
return a+b;""");
StopNode ret = parser.parse().iterate();
- assertEquals("return ((Phi(Region32,(arg*2),arg)+arg)+Phi(Region,4,5));", ret.toString());
+ assertEquals("return ((Phi(Region34,(arg*2),arg)+arg)+Phi(Region,4,5));", ret.toString());
}
@Test
@@ -100,7 +100,7 @@ else
return a;
""", TypeInteger.BOT);
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region36,Phi(Region22,2,3),Phi(Region34,4,5));", stop.toString());
+ assertEquals("return Phi(Region38,Phi(Region24,2,3),Phi(Region36,4,5));", stop.toString());
}
@Test
@@ -116,7 +116,7 @@ if( arg==0 )
return arg+a+b;
""", TypeInteger.BOT);
StopNode stop = parser.parse();
- assertEquals("return ((arg+Phi(Region13,1,0))+Phi(Region28,2,0));", stop.toString());
+ assertEquals("return ((arg+Phi(Region15,1,0))+Phi(Region30,2,0));", stop.toString());
}
@Test
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;""");
StopNode ret = parser.parse().iterate();
- assertEquals("return (arg==Phi(Region16,3,2));", ret.toString());
+ assertEquals("return (arg==Phi(Region18,3,2));", ret.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index 097c2617..7b11bc35 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -17,7 +17,7 @@ return 1;
""");
StopNode stop = parser.parse().iterate();
assertEquals("return 2;", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@Test
@@ -30,7 +30,7 @@ if (arg)
return (arg < a) < 3;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return ((arg<Phi(Region12,2,1))<3);", stop.toString());
+ assertEquals("return ((arg<Phi(Region14,2,1))<3);", stop.toString());
}
@Test
@@ -46,7 +46,7 @@ return a;
""");
StopNode stop = parser.parse().iterate();
assertEquals("return 2;", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@Test
@@ -65,7 +65,7 @@ else
b=5;
return b;""", TypeInteger.BOT);
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region37,42,5);", stop.toString());
+ assertEquals("return Phi(Region39,42,5);", stop.toString());
}
@Test
@@ -84,7 +84,7 @@ else
b=5;
return b;""", TypeInteger.BOT);
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region30,2,5);", stop.toString());
+ assertEquals("return Phi(Region32,2,5);", stop.toString());
}
@Test
@@ -144,7 +144,7 @@ else
return a;
""", TypeInteger.BOT);
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region41,3,Phi(Region39,4,5));", stop.toString());
+ assertEquals("return Phi(Region43,3,Phi(Region41,4,5));", stop.toString());
}
@Test
@@ -200,7 +200,7 @@ if( arg ) {
return a+b;
""");
StopNode ret = parser.parse().iterate();
- assertEquals("return Phi(Region24,4,1);", ret.toString());
+ assertEquals("return Phi(Region26,4,1);", ret.toString());
}
@@ -251,7 +251,7 @@ if( arg ) {
return a+b+c;
""");
StopNode ret = parser.parse().iterate();
- assertEquals("return (Phi(Region36,Phi(Region23,2,3),0)+Phi(Region,3,1));", ret.toString());
+ assertEquals("return (Phi(Region38,Phi(Region25,2,3),0)+Phi(Region,3,1));", ret.toString());
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index de9d16f2..e604937c 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -19,8 +19,8 @@ public class Chapter07Test {
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop6,arg,(Phi_arg+1));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop8,arg,(Phi_arg+1));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -37,7 +37,7 @@ if(arg){}else{
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region24,1,Phi(Loop13,1,(Phi_a+1)));", stop.toString());
+ assertEquals("return Phi(Region26,1,Phi(Loop15,1,(Phi_a+1)));", stop.toString());
}
@Test
@@ -57,7 +57,7 @@ while(i < arg) {
return sum;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop8,0,Phi(Loop20,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", stop.toString());
+ assertEquals("return Phi(Loop10,0,Phi(Loop22,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", stop.toString());
}
@Test
@@ -74,8 +74,8 @@ return b;
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop8,2,Phi(Region27,Phi_b,4));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop10,2,Phi(Region29,Phi_b,4));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -94,8 +94,8 @@ while(a < 10) {
return b;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop8,2,(Phi(Region27,Phi_b,4)+1));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop10,2,(Phi(Region29,Phi_b,4)+1));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@@ -112,8 +112,8 @@ return a;
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop7,1,((Phi_a+1)+2));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,((Phi_a+1)+2));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -129,8 +129,8 @@ while(a < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop7,1,(Phi_a+3));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,(Phi_a+3));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@Test
@@ -143,8 +143,8 @@ return a;
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop7,1,2);", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,2);", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -157,8 +157,8 @@ while(arg) a = 2;
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop7,1,2);", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,2);", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@Test
@@ -174,8 +174,8 @@ return a;
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop7,1,((Phi_a+1)+2));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,((Phi_a+1)+2));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -191,8 +191,8 @@ while(a < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop7,1,(Phi_a+3));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,(Phi_a+3));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@Test
@@ -209,8 +209,8 @@ return a;
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop8,1,((Phi_a+1)+2));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop10,1,((Phi_a+1)+2));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -227,8 +227,8 @@ while(a < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop8,1,(Phi_a+3));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop10,1,(Phi_a+3));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index 6688d598..98fd181b 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -24,7 +24,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region36,Phi(Region25,Phi(Loop6,arg,(Phi_arg+1)),Add),Add);", stop.toString());
+ assertEquals("return Phi(Region36,Phi(Region26,Phi(Loop8,arg,(Phi_arg+1)),Add),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
Assert.assertEquals(5L, Evaluator.evaluate(stop, 1));
Assert.assertEquals(10L, Evaluator.evaluate(stop, 6));
@@ -47,8 +47,8 @@ while(arg < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop7,1,Phi(Region42,Phi_a,(Phi_a+1)));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop9,1,Phi(Region41,Phi_a,(Phi_a+1)));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@@ -66,7 +66,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region34,Phi(Loop6,arg,(Phi_arg+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region34,Phi(Loop8,arg,(Phi_arg+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
@@ -82,7 +82,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region25,Phi(Loop6,arg,(Phi_arg+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region26,Phi(Loop8,arg,(Phi_arg+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
@@ -101,8 +101,8 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop6,arg,(Phi_arg+1));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop8,arg,(Phi_arg+1));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@@ -118,8 +118,8 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop6,arg,(Phi_arg+1));", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode);
+ assertEquals("return Phi(Loop8,arg,(Phi_arg+1));", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@Test
@@ -186,7 +186,7 @@ while(arg < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region28,Phi(Loop7,1,(Phi_a+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region29,Phi(Loop9,1,(Phi_a+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
@@ -202,8 +202,8 @@ while(1) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return (Phi(Loop7,1,Add)+1);", stop.toString());
- assertTrue(stop.ret().ctrl() instanceof ProjNode prj && prj._idx==1 );
+ assertEquals("return (Phi(Loop9,1,Add)+1);", stop.toString());
+ assertTrue(stop.ret().ctrl() instanceof CProjNode prj && prj._idx==1 );
}
}
diff --git a/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index 048a58c7..2abce3cb 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -70,7 +70,7 @@ while (arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop7,arg,(Phi_arg+2));", stop.toString());
+ assertEquals("return Phi(Loop9,arg,(Phi_arg+2));", stop.toString());
Assert.assertEquals(11L, Evaluator.evaluate(stop, 1));
}
@@ -87,7 +87,7 @@ while (arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop8,arg,(Phi_arg+4));", stop.toString());
+ assertEquals("return Phi(Loop10,arg,(Phi_arg+4));", stop.toString());
Assert.assertEquals(13L, Evaluator.evaluate(stop, 1));
}
@@ -116,7 +116,7 @@ while (arg) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop14,arg,(Phi_arg+1));", stop.toString());
+ assertEquals("return Phi(Loop16,arg,(Phi_arg+1));", stop.toString());
}
@Test
@@ -134,14 +134,14 @@ while(v1+arg) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("Stop[ return 0; return 0; ]", stop.toString());
}
@Test
public void testWhile0() {
Parser parser = new Parser("while(0) continue; if(0) arg=0;");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -153,7 +153,7 @@ if(0) while(0) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@@ -186,7 +186,7 @@ while(arg) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop9,0,(-(Phi_a+3)));", stop.toString());
+ assertEquals("return Phi(Loop11,0,(-(Phi_a+3)));", stop.toString());
}
@Test
@@ -200,7 +200,7 @@ int v0=0!=0<-0;
return -0+0+0;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@@ -215,7 +215,7 @@ return -0+0+0;
public void testFuzz3() {
Parser parser = new Parser("int v0=0; while(0==69) while(v0) return 0;");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -227,7 +227,7 @@ while(1) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -249,18 +249,19 @@ while(1) {
return 0!=0;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
public void testFuzz6() {
- Parser parser = new Parser("""
+ Parser parser = new Parser(
+"""
int v0=0;
while(0==1) while(v0)
v0=1+v0;
- """);
+""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -276,14 +277,14 @@ while(1)
return v1+v0;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
public void testFuzz8() {
Parser parser = new Parser("while(arg) arg = arg - 1; return arg;");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop6,arg,(Phi_arg-1));", stop.toString());
+ assertEquals("return Phi(Loop8,arg,(Phi_arg-1));", stop.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index f2e7ca2c..7371ff18 100644
--- a/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -1,12 +1,7 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StopNode;
-import com.seaofnodes.simple.type.*;
import org.junit.Test;
-
-import java.util.Arrays;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -108,7 +103,7 @@ while (arg) {
return bar.a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop10,0,(Phi_a+2));", stop.toString());
+ assertEquals("return Phi(Loop11,0,(Phi_a+2));", stop.toString());
}
@Test
@@ -160,7 +155,7 @@ if( bar ) bar.a = 1;
return bar;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region15,null,new Bar);", stop.toString());
+ assertEquals("return Phi(Region16,null,new Bar);", stop.toString());
}
@Test
@@ -176,7 +171,7 @@ else bar.a = 1;
return rez;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region32,4,3);", stop.toString());
+ assertEquals("return Phi(Region33,4,3);", stop.toString());
}
@Test
@@ -193,7 +188,8 @@ while(arg) {
return ret;
""");
try { parser.parse().iterate(); fail(); }
- catch( Exception e ) { assertEquals("Might be null accessing 'v0'", e.getMessage()); }
+ catch( Exception e ) {
+ assertEquals("Might be null accessing 'v0'", e.getMessage()); }
}
@Test
@@ -229,7 +225,7 @@ while( i.x < i.len ) {
return sum;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop14,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", stop.toString());
+ assertEquals("return Phi(Loop15,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", stop.toString());
}
@@ -245,11 +241,12 @@ while(arg) {
v0.v0 = arg;
arg = arg-1;
if (arg==5) ret=v0;
+
}
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop10,new s0,Phi(Region31,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop11,new s0,Phi(Region31,new s0,Phi_ret));", stop.toString());
}
@Test
@@ -262,6 +259,7 @@ while(arg) {
v0.v0 = arg;
arg = arg-1;
if (arg==5) ret=v0;
+
}
return ret;
""");
@@ -283,7 +281,7 @@ while(arg < 10) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop10,new s0,Phi(Region30,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop11,new s0,Phi(Region30,new s0,Phi_ret));", stop.toString());
}
@Test
@@ -313,7 +311,7 @@ if(0) {
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -387,7 +385,7 @@ while(0) {}
}
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ ]", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
diff --git a/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
new file mode 100644
index 00000000..fdf56b56
--- /dev/null
+++ b/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -0,0 +1,565 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.evaluator.Evaluator;
+import com.seaofnodes.simple.node.StopNode;
+import org.junit.Test;
+
+
+import static org.junit.Assert.assertEquals;
+
+public class Chapter11Test {
+
+ // A placeholder test used to rapidly rotate through fuzzer produced issues
+ @Test
+ public void testFuzzer() {
+ Parser parser = new Parser(
+"""
+while(-----arg) {
+ while(-arg) {
+ while(-----arg*---0) {
+ int N=--false==false<--arg;
+ while(N<-4==N) {
+ N=N+N;
+ }
+ }
+ {
+ int kzEl_r7EYz=3*----0!=-arg<(37*--97!=0/arg==arg)<=-(--arg!=0);
+ while(kzEl_r7EYz<---1) {
+ kzEl_r7EYz=kzEl_r7EYz+kzEl_r7EYz*arg>=24;
+ if(7) continue;
+ kzEl_r7EYz=false;
+ if(-kzEl_r7EYz+arg<-3) continue;
+ else {
+ while(kzEl_r7EYz) continue;
+ arg=----3;
+ kzEl_r7EYz=-(-----arg*1>-kzEl_r7EYz);
+ int v2uF_4ba=9;
+ }
+ }
+ }
+ int pjcp7rv=arg;
+ {
+ int m2ya3=--arg-pjcp7rv;
+ while(m2ya3<-----pjcp7rv>=pjcp7rv+--pjcp7rv!=---pjcp7rv!=-arg<---(-m2ya3)>m2ya3<=-----true>14!=--m2ya3) {
+ m2ya3=m2ya3+-(--0<=false<pjcp7rv/arg<=-true);
+ while((--m2ya3)<--arg<-----arg*false!=arg<pjcp7rv*arg) {
+ pjcp7rv=-3+-arg;
+ break;
+ }
+ while(-3) {
+ while(67+0-61) m2ya3=m2ya3;
+ if(----pjcp7rv==---(---arg)) pjcp7rv=0!=-23;
+ else pjcp7rv=----89;
+ while(m2ya3!=m2ya3<pjcp7rv) m2ya3=1;
+ while(--arg<=pjcp7rv) pjcp7rv=-32<=-m2ya3+62;
+ }
+ break;
+ }
+ }
+ {
+ int arg=-1<-9!=(-arg>=-pjcp7rv>=arg<=arg);
+ while(arg<-arg) {
+ arg=arg+--arg;
+ {
+ while((--93)--pjcp7rv*-pjcp7rv+arg) arg=0-(6>=(--(---41/(0))!=--pjcp7rv)>=---2*33);
+ pjcp7rv=11;
+ int c=pjcp7rv;
+ if(--107) break;
+ else break;
+ }
+ }
+ }
+ pjcp7rv=pjcp7rv;
+ int aR0Wkua=-pjcp7rv==pjcp7rv;
+ {
+ if(-0!=-0) while(-true) break;
+ else {
+ while(aR0Wkua) pjcp7rv=-0/pjcp7rv;
+ pjcp7rv=pjcp7rv;
+ }
+ }
+ }
+ if(-arg) {
+ {
+ int Isdlt0fr91=0!=arg;
+ while(Isdlt0fr91<-Isdlt0fr91==-Isdlt0fr91+true) {
+ Isdlt0fr91=Isdlt0fr91+-1!=true;
+ if(-arg) if(--arg<=Isdlt0fr91) Isdlt0fr91=-Isdlt0fr91;
+ int i=---arg<---arg!=--40;
+ {
+ int jVX43Ln4Dm=-(6)==-Isdlt0fr91/----0;
+ while(jVX43Ln4Dm<--(3-3)) {
+ jVX43Ln4Dm=jVX43Ln4Dm+-0<25;
+ i=24<--(jVX43Ln4Dm);
+ }
+ }
+ i=(arg!=----false>=7);
+ int Te=5>-arg;
+ break;
+ }
+ }
+ arg=arg!=--true/arg;
+ {
+ int UX=arg;
+ while(UX<-3*arg) {
+ UX=UX+arg/-1;
+ arg=-3--(-UX--47<arg==--arg==arg)/arg--UX>=-arg;
+ if(---12) while(-UX) break;
+ break;
+ }
+ }
+ continue;
+ }
+ if(arg) arg=--0;
+ else if(1) continue;
+ int DtTlhjYq=19==arg;
+ {
+ arg=--DtTlhjYq<=(DtTlhjYq/---DtTlhjYq<=-DtTlhjYq);
+ if(---arg) continue;
+ else {
+ int Zsl=40;
+ while(Zsl<--Zsl--false>((Zsl))==Zsl!=--DtTlhjYq-DtTlhjYq-DtTlhjYq) {
+ Zsl=Zsl+arg;
+ }
+ }
+ if((arg)) if(-DtTlhjYq) {
+ }
+ else while(((2)==3*arg/DtTlhjYq)<=0==---arg) return false;
+ else {
+ int lwbF=arg>=0;
+ while(lwbF<57) {
+ lwbF=lwbF+--(-arg+lwbF)>=-2>=arg!=arg;
+ arg=----0+0;
+ while(49) break;
+ if(true) DtTlhjYq=-----arg;
+ DtTlhjYq=0!=-----(0);
+ }
+ }
+ arg=---arg!=DtTlhjYq/--0;
+ DtTlhjYq=-DtTlhjYq/(--arg<arg);
+ if(32) {
+ int q=-DtTlhjYq;
+ while(q<arg*(0*q)/0) {
+ q=q+DtTlhjYq;
+ DtTlhjYq=arg;
+ }
+ }
+ else if(--arg) if(15) break;
+ else arg=0==arg<=-55>DtTlhjYq;
+ else {
+ }
+ DtTlhjYq=-0;
+ {
+ while((0)<-arg) DtTlhjYq=-DtTlhjYq;
+ }
+ if(30/--(arg)>=0) DtTlhjYq=-0>=arg;
+ else {
+ int Y2wcIQJ=--DtTlhjYq*---11;
+ if(arg==arg) {
+ Y2wcIQJ=-----Y2wcIQJ;
+ Y2wcIQJ=Y2wcIQJ<7;
+ if(DtTlhjYq) arg=arg;
+ else break;
+ while(-arg!=-31==(false<=-15>=-DtTlhjYq+--6*-----Y2wcIQJ<=---arg!=-Y2wcIQJ<=---0<-arg*--Y2wcIQJ)) DtTlhjYq=arg!=3;
+ }
+ continue;
+ }
+ arg=DtTlhjYq;
+ arg=--3/(---arg)>=80>=-DtTlhjYq<=DtTlhjYq;
+ arg=51+---(-true)!=arg!=----1==-DtTlhjYq;
+ if(11<=arg) if((arg)+79<32+(-arg)) continue;
+ else if(true==DtTlhjYq) break;
+ break;
+ }
+}
+if(arg!=62==arg) {
+ while(---arg) {
+ {
+ int bhmDacPM=arg/arg;
+ while(bhmDacPM<arg) {
+ bhmDacPM=bhmDacPM+80*((-bhmDacPM)/---bhmDacPM>1)<bhmDacPM-2!=7;
+ arg=-0;
+ arg=bhmDacPM>=-arg+-arg;
+ break;
+ }
+ }
+ int arg=true;
+ arg=arg<=arg;
+ {
+ int KlC=30;
+ while(KlC<-2) {
+ KlC=KlC+-1!=25<=-KlC>KlC>----0;
+ int Ij=arg>=-arg<=---10;
+ {
+ int LAbgurVa2R=-arg!=Ij;
+ while(LAbgurVa2R<-true) {
+ LAbgurVa2R=LAbgurVa2R+Ij;
+ KlC=-LAbgurVa2R;
+ Ij=42*(-Ij>true);
+ while(Ij) arg=KlC;
+ KlC=-----7>-(--KlC<--7)==-LAbgurVa2R;
+ }
+ }
+ int a=Ij<=-KlC;
+ if(Ij) {
+ int r1W=a!=-false;
+ }
+ else {
+ arg=56>=Ij;
+ while(54) break;
+ arg=8>=-(a<--Ij)/a>=-a-KlC;
+ }
+ int Yt_T=KlC;
+ arg=KlC;
+ }
+ }
+ if(----false/--arg/12+-----0) if(-9+-arg) while(arg!=(arg)) arg=--1>=-71-arg!=---(--arg*11+102)/--arg;
+ else if(----arg) continue;
+ if(-arg-arg) {
+ arg=-(-arg)*--(-3)<(-7==-arg----arg);
+ {
+ int arg=arg;
+ while(arg<-0>arg==-arg-(-arg>1+11)) {
+ arg=arg+6-arg!=--0>---30>=---arg<=arg/(3)+---arg;
+ }
+ }
+ arg=-arg+arg;
+ arg=--47+-arg<1>arg*(-arg==-arg--0>3);
+ arg=(----arg)!=arg+--59;
+ arg=--4+arg==-(arg)-arg;
+ }
+ else {
+ while(--true-----true*-25+-18>arg>=-----(-arg)---13) {
+ arg=-0+--true<false;
+ continue;
+ }
+ arg=true<=arg;
+ break;
+ }
+ arg=arg*22<--(0+-arg);
+ {
+ continue;
+ }
+ }
+ arg=1*---((3<=true)==---2);
+ arg=7+-(-1);
+ return arg+--true;
+}
+else {
+ int o3ypEsewxM=-(---arg);
+ int vzXB=(-(---11<=--o3ypEsewxM>=--0))<=-0/-arg;
+ int L=-arg;
+ int B4mHcrf=---vzXB;
+ int oZWGdzZl=-o3ypEsewxM;
+ int K6tQqa1=11;
+ while(0!=--L) {
+ int sdZfTM=vzXB*true+K6tQqa1;
+ while(sdZfTM<false) {
+ sdZfTM=sdZfTM+arg;
+ sdZfTM=oZWGdzZl;
+ K6tQqa1=-K6tQqa1;
+ arg=---K6tQqa1;
+ while((3+-K6tQqa1)<oZWGdzZl) {
+ if(-----(-1)) sdZfTM=-false;
+ {}
+ }
+ K6tQqa1=vzXB!=-(B4mHcrf+-7>false);
+ sdZfTM=vzXB;
+ }
+ }
+ L=false;
+ while(31) B4mHcrf=arg;
+ oZWGdzZl=-0<--oZWGdzZl<=--2;
+ if(K6tQqa1>=20<=46) {
+ if(-1+-L) if(oZWGdzZl) {
+ int wmhv=-----0;
+ }
+ int arg=0;
+ o3ypEsewxM=---(oZWGdzZl);
+ return -oZWGdzZl/0;
+ }
+ else if(arg) {
+ int yKk5=B4mHcrf>=4>K6tQqa1<-B4mHcrf/o3ypEsewxM;
+ return -K6tQqa1;
+ }
+ else {
+ if(---0) {
+ {}
+ {}
+ }
+ return o3ypEsewxM;
+ }
+}
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("Stop[ return 9; return 0; return 0; ]", stop.toString());
+ }
+
+
+ @Test
+ public void testPrimes() {
+ Parser parser = new Parser(
+"""
+if( arg < 2 ) return 0;
+int primeCount = 1;
+int prime = 3;
+while( prime <= arg ) {
+ int isPrime = 1;
+ // Check for even case, so the next loop need only check odds
+ if( (prime/2)*2 == prime )
+ continue;
+ // Check odds up to sqrt of prime
+ int j = 3;
+ while( j*j <= prime ) {
+ if( (prime/j)*j == prime ) {
+ isPrime = 0;
+ break;
+ }
+ j = j + 2;
+ }
+ if( isPrime )
+ primeCount = primeCount + 1;
+ prime = prime + 2;
+}
+return primeCount;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("Stop[ return 0; return Phi(Loop19,1,Phi(Region81,Phi_primeCount,Phi(Region76,(Phi_primeCount+1),Phi_primeCount))); ]", stop.toString());
+ assertEquals(0L, Evaluator.evaluate(stop, 1)); // No primes 1 or below
+ assertEquals(1L, Evaluator.evaluate(stop, 2)); // 2
+ assertEquals(2L, Evaluator.evaluate(stop, 3)); // 2, 3
+ assertEquals(2L, Evaluator.evaluate(stop, 4)); // 2, 3
+ assertEquals(3L, Evaluator.evaluate(stop, 5)); // 2, 3, 5
+ assertEquals(4L, Evaluator.evaluate(stop, 10)); // 2, 3, 5, 7
+ }
+
+ @Test
+ public void testAntiDeps1() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v=new S;
+v.f = 2;
+int i=new S.f;
+i=v.f;
+if (arg) v.f=1;
+return i;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return .f;", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps2() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v = new S;
+S t = new S;
+int i = 0;
+if (arg) {
+ if (arg+1) v = t;
+ i = v.f;
+} else {
+ v.f = 2;
+}
+return i;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return Phi(Region31,.f,0);", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps3() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v0 = new S;
+S? v1;
+if (arg) v1 = new S;
+if (v1) {
+ v0.f = v1.f;
+} else {
+ v0.f = 2;
+}
+return v0;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return new S;", stop.toString());
+ }
+
+
+ @Test
+ public void testAntiDeps4() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v = new S;
+S t = new S;
+int i = v.f;
+if (arg+1) arg= 0;
+while (arg) v.f = 2;
+return i;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return .f;", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps5() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v = new S;
+while(1) {
+ while(arg+1) { arg=arg-1; }
+ if (arg) break;
+ v.f = 2;
+}
+return v;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return new S;", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps6() {
+ Parser parser = new Parser(
+"""
+struct s { int v; }
+s ptr=new s;
+while( -arg )
+ ptr = new s;
+while(1)
+ arg = arg+ptr.v;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return 0;", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps7() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v = new S;
+S t = new S;
+int i = v.f;
+while (arg) {
+ v.f = arg;
+ arg = arg-1;
+}
+return i;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return .f;", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps8() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v = new S;
+S t = new S;
+while(arg) {
+ arg=arg-1;
+ int f = v.f;
+ v.f = 2;
+ if (arg) arg = f;
+ else v.f = 3;
+}
+return arg;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return Phi(Loop13,arg,Phi(Region35,.f,0));", stop.toString());
+ }
+
+ @Test
+ public void testAntiDeps9() {
+ Parser parser = new Parser(
+"""
+struct S { int f; }
+S v = new S;
+S t = new S;
+if (arg) {
+ v.f=2;
+ int i=t.f;
+ v.f=i;
+}
+return v;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return new S;", stop.toString());
+ }
+
+ @Test
+ public void testExample2() {
+ Parser parser = new Parser(
+ """
+struct S { int f; }
+S v = new S;
+int i = arg;
+while (arg > 0) {
+ int j = i/3;
+ if (arg == 5)
+ v.f = j;
+ arg = arg - 1;
+}
+return v;
+""");
+ StopNode stop = parser.parse().iterate();
+ //assertEquals("return new S;", stop.toString());
+ }
+
+ @Test
+ public void testSplit() {
+ Parser parser = new Parser(
+ """
+struct S { int f; }
+S s = new S;
+if( arg==0 ) s.f = 1;
+else if (arg == 1) s.f = 1;
+return s.f;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return Phi(Region35,1,Phi(Region33,1,0));", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ assertEquals(1L, Evaluator.evaluate(stop, 1));
+ assertEquals(0L, Evaluator.evaluate(stop, 2));
+ }
+
+ @Test
+ public void testScheduleUse() {
+ Parser parser = new Parser(
+"""
+int v0=0;
+while(0>=0) {
+ int v1=0;
+ v1=v0;
+ if(v1*0)
+ v0=-v1;
+}
+return 0;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return 0;", stop.toString());
+ }
+
+ @Test
+ public void testLoopCarriedDep() {
+ Parser parser = new Parser(
+"""
+int v0=0;
+{
+ int v1=0;
+ while(v1) {
+ v1=1+(v0!=0);
+ v0=v1/0;
+ }
+}
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return 0;", stop.toString());
+ }
+}
diff --git a/src/test/java/com/seaofnodes/simple/FuzzerWrap.java b/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
index bb3bec1c..699711fc 100644
--- a/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
+++ b/src/test/java/com/seaofnodes/simple/FuzzerWrap.java
@@ -10,12 +10,12 @@ import static org.junit.Assert.assertTrue;
/**
* To use the fuzzer run the <code>fuzzPeeps</code> methods.
* This can be done from IntelliJ Gui or remove the `@Ignore` annotation and start the test case from the command line
- * via <code>mvn clean test -Dtest=com.seaofnodes.simple.FuzzerWrap#fuzzPeeps</code>
+ * via <code>mvn clean test -Dtest=com.seaofnodes.simple.Fuzzer08Test#fuzzPeeps</code>
*/
public class FuzzerWrap {
- @Test
@Ignore
+ @Test
public void fuzzPeeps() {
var fuzzer = new Fuzzer();
for (int i=0; i<1000000; i++)
diff --git a/src/test/java/com/seaofnodes/simple/SchedulerTest.java b/src/test/java/com/seaofnodes/simple/SchedulerTest.java
index 5def2782..32c9c0c5 100644
--- a/src/test/java/com/seaofnodes/simple/SchedulerTest.java
+++ b/src/test/java/com/seaofnodes/simple/SchedulerTest.java
@@ -46,7 +46,7 @@ S v0=new S;
if(arg) v0.f=1;
return v0;
""");
- StopNode stop = parser.parse().iterate();
+ StopNode stop = parser.parse(false).iterate();
var eval = new Evaluator(stop);
assertObj(eval.evaluate(0, 10), "S", 0L);
assertObj(eval.evaluate(1, 10), "S", 1L);
@@ -65,7 +65,7 @@ i=v.f;
if (arg) v.f=1;
return i;
""");
- StopNode stop = parser.parse().iterate();
+ StopNode stop = parser.parse(false).iterate();
var eval = new Evaluator(stop);
assertEquals(2L, eval.evaluate(0, 10));
assertEquals(2L, eval.evaluate(1, 10));
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 3cf5c57b..4a405a3b 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -139,7 +139,8 @@ public class Evaluator {
case LoadNode load -> load(load);
case StoreNode store -> store(store);
case NewNode alloc -> alloc(alloc);
- case ProjNode proj -> ((Object[])val(proj.ctrl()))[proj._idx];
+ case CProjNode cproj -> ((Object[])val(cproj.ctrl()))[cproj._idx];
+ case ProjNode proj -> ((Object[])val( proj.in(0) ))[ proj._idx];
default -> throw new AssertionError("Unexpected node " + node);
};
}
diff --git a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
index 674d12f5..8b86d67a 100644
--- a/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
+++ b/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
@@ -164,7 +164,7 @@ public class Scheduler {
* @return true if the node is not XCtrl.
*/
private static boolean isNotXCtrl(Node node) {
- return !(node instanceof ConstantNode c && c.compute() == Type.XCONTROL);
+ return !(node instanceof ConstantNode c && c.compute() == Type.XCONTROL) && !(node instanceof XCtrlNode);
}
/**
@@ -322,6 +322,8 @@ public class Scheduler {
for(int i=1; i<r.nIns(); i++) {
if (od(r.in(i)).map(d->d.block==null).orElse(false)) return false;
}
+ } else if (node instanceof XCtrlNode) {
+ return false;
} else {
assert d(node.in(0)).block != null;
}
@@ -397,7 +399,7 @@ public class Scheduler {
}
}
if (!(node instanceof ReturnNode))
- for (Node n:node._outputs) if (n.isCFG() && isCFGNodeReady(n) && d(n).block == null) queue.push(d(n));
+ for (Node n:node._outputs) if (n!=null && n.isCFG() && isCFGNodeReady(n) && d(n).block == null) queue.push(d(n));
var b = block;
for(var in:data.node._inputs) od(in).ifPresent(d->update(d, b));
}
@@ -443,7 +445,7 @@ public class Scheduler {
node = data.node;
assert node.isCFG();
if (!(node instanceof ReturnNode)) {
- for (var out : node._outputs) if (out.isCFG() && isNotXCtrl(out)) markAlive(cfgQueue, out, true);
+ for (var out : node._outputs) if (out!=null && out.isCFG() && isNotXCtrl(out)) markAlive(cfgQueue, out, true);
}
for (var in : node._inputs) if(in!=null && !in.isCFG() && isNotXCtrl(in)) markAlive(dataQueue, in, false);
}
@@ -496,7 +498,7 @@ public class Scheduler {
*/
private static Node findSingleCFGOut(Node node) {
if (node instanceof StartNode) {
- for(var n:node._outputs) if (n instanceof ProjNode p && p._idx==0) return p;
+ for(var n:node._outputs) if (n instanceof CProjNode p && p._idx==0) return p;
return null;
}
assert node._outputs.stream().filter(Node::isCFG).limit(2).count()<=1;
@@ -563,7 +565,7 @@ public class Scheduler {
switch(block.exit) {
case null: break;
case IfNode i:
- for(var out:i._outputs) if (out instanceof ProjNode p) block.next[p._idx] = blocks.get(p);
+ for(var out:i._outputs) if (out instanceof CProjNode p) block.next[p._idx] = blocks.get(p);
break;
case RegionNode r:
block.next[0] = blocks.get(r);