summaryrefslogtreecommitdiff
path: root/chapter05/src/main/java/com
diff options
context:
space:
mode:
Diffstat (limited to 'chapter05/src/main/java/com')
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/Parser.java113
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java11
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/node/Node.java10
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/node/PhiNode.java4
-rw-r--r--chapter05/src/main/java/com/seaofnodes/simple/node/ScopeNode.java42
5 files changed, 83 insertions, 97 deletions
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java
index fc105523..4225e67b 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/Parser.java
@@ -15,10 +15,11 @@ import java.util.*;
public class Parser {
/**
- * The control token is a name that binds to the currently active control
+ * The control is a name that binds to the currently active control
* node in the graph
*/
- private static final String CTRL_TOKEN = "$ctrl";
+ public static final String CTRL = "$ctrl";
+
/**
* A Global Static, unique to each compilation. This is a public, so we
* can make constants everywhere without having to thread the StartNode
@@ -34,10 +35,10 @@ public class Parser {
* Current ScopeNode - ScopeNodes change as we parse code, but at any point of time
* there is one current ScopeNode. The reason the current ScopeNode can change is to do with how
* we handle branching. See {@link #parseIf()}.
- *
+ * <p>
* Each ScopeNode contains a stack of lexical scopes, each scope is a symbol table that binds
* variable names to Nodes. The top of this stack represents current scope.
- *
+ * <p>
* We keep a list of all ScopeNodes so that we can show them in graphs.
* @see #parseIf()
* @see #_allScopes
@@ -48,13 +49,12 @@ public class Parser {
* We clone ScopeNodes when control flows branch; it is useful to have
* a list of all active ScopeNodes for purposes of visualization of the SoN graph
*/
- public final List<ScopeNode> _allScopes = new ArrayList<>();
+ public final Stack<ScopeNode> _allScopes = new Stack<>();
public Parser(String source, TypeInteger arg) {
_lexer = new Lexer(source);
- _scope = new ScopeNode();
- _allScopes.add(_scope);
Node.reset();
+ _scope = new ScopeNode();
START = new StartNode(new Type[]{ Type.CONTROL, arg });
START.peephole();
}
@@ -65,23 +65,21 @@ public class Parser {
String src() { return new String( _lexer._input ); }
- private Node ctrlToken() {
- return _scope.lookup(CTRL_TOKEN);
- }
+ private Node ctrl() { return _scope.ctrl(); }
- private void setCtrlToken(Node n) {
- _scope.update(CTRL_TOKEN, n);
- }
+ private Node ctrl(Node n) { return _scope.ctrl(n); }
public ReturnNode parse() {
+ _allScopes.push(_scope);
_scope.push();
try {
- _scope.define(CTRL_TOKEN, new ProjNode(START, 0, CTRL_TOKEN).peephole());
- _scope.define("arg" , new ProjNode(START, 1, "arg" ).peephole());
+ _scope.define(CTRL , new ProjNode(START, 0, CTRL ).peephole());
+ _scope.define("arg", new ProjNode(START, 1, "arg").peephole());
return (ReturnNode) parseBlock();
}
finally {
_scope.pop();
+ _allScopes.pop();
}
}
@@ -113,10 +111,10 @@ public class Parser {
* </pre>
*/
private Node parseStatement() {
- if (match("return")) return parseReturn();
+ if (match("return") ) return parseReturn();
else if (match("int")) return parseDecl();
- else if (match("{")) return parseBlock();
- else if (match("if")) return parseIf();
+ else if (match("{" )) return parseBlock();
+ else if (match("if" )) return parseIf();
else if (match("#showGraph")) return showGraph();
else return parseExpressionStatement();
}
@@ -126,61 +124,33 @@ public class Parser {
// Parse predicate
var pred = require(parseExpression(), ")");
// IfNode takes current control and predicate
- IfNode ifNode = new IfNode(ctrlToken(), pred);
+ IfNode ifNode = (IfNode)new IfNode(ctrl(), pred).peephole();
// Setup projection nodes
- ProjNode ifT = new ProjNode(ifNode, 0, "True");
- ProjNode ifF = new ProjNode(ifNode, 1, "False");
+ Node ifT = new ProjNode(ifNode, 0, "True" ).peephole();
+ Node ifF = new ProjNode(ifNode, 1, "False").peephole();
// In if true branch, the ifT proj node becomes the ctrl
// But first clone the scope and set it as current
- ScopeNode savedScope = _scope;
- ScopeNode ifScope = _scope.dup(); // Duplicate current scope
- _allScopes.add(ifScope); // For graph visualization we need all scopes
- _scope = ifScope; // ifScope is now the current scope
- setCtrlToken(ifT); // set ctrl token to ifTrue projection
- parseStatement();
- _scope = savedScope; // Restore scope to original, we use this for else block if any
- setCtrlToken(ifF); // Ctrl token is now set to ifFalse projection
+ int ndefs = _scope.nIns();
+ ScopeNode fScope = _scope.dup(); // Duplicate current scope
+ _allScopes.push(fScope); // For graph visualization we need all scopes
+
+ // Parse the true side
+ ctrl(ifT); // set ctrl token to ifTrue projection
+ parseStatement(); // Parse true-side
+ ScopeNode tScope = _scope;
+
+ // Parse the false side
+ _scope = fScope; // Restore scope, then parse else block if any
+ ctrl(ifF); // Ctrl token is now set to ifFalse projection
if (match("else")) parseStatement();
- // Create region node and merge scopes
- // If a var is in both scopes and has different binding then we need to create PhiNode for it
- // After merge _scope remains, and ifScope is discarded
- //System.out.println(new GraphVisualizer().generateDotOutput(this));
- RegionNode region = mergeScopes(ifT, ifScope, ifF, _scope);
- setCtrlToken(region); // Now region becomes the ctrl token
- ifScope.clear(); // Clean up ifScope
- _allScopes.remove(ifScope); // Discard ifScope
- return region;
- }
- private RegionNode mergeScopes(ProjNode ifT, ScopeNode ifScope, ProjNode ifF, ScopeNode mergeScope) {
- RegionNode regionNode = new RegionNode(ifT, ifF);
- Set<String> namesDone = new HashSet<>();
- for (int level = ifScope._scopes.size()-1; level >= 0; level--) {
- var tab = ifScope._scopes.get(level);
- for (Map.Entry<String, Integer> e: tab.entrySet()) {
- String name = e.getKey();
- if (name.equals(CTRL_TOKEN)) // ignore ctrl token
- continue;
- if (namesDone.contains(name)) // don't visit a name twice
- continue;
- namesDone.add(name);
- Integer idx = e.getValue();
- Node ifTdef = ifScope.in(idx);
- Node ifNdef = mergeScope.lookup(name); // Lookup name in merge scope
- if (ifNdef == null)
- // The else scope didn't see this name
- // Is this correct? Do we need same level as in ifScope?
- // FIXME can this even happen, as all nested scopes should be gone
- // so we cannot have a new var in same level anyway
- mergeScope.define(name, ifTdef);
- else if (!ifTdef.equals(ifNdef)) { // FIXME do we need equals or just ID comparison?
- // Names are mapped to different nodes
- PhiNode phiNode = new PhiNode(regionNode, ifTdef, ifNdef);
- mergeScope.update(name, phiNode);
- }
- }
- }
- return regionNode;
+ if( tScope.nIns() != ndefs || fScope.nIns() != ndefs )
+ throw error("Cannot define a new name on one arm of an if");
+
+ // Merge results
+ _scope = tScope;
+ _allScopes.pop(); // Discard pushed from graph display
+ return ctrl(tScope.mergeScopes(fScope));
}
@@ -194,8 +164,8 @@ public class Parser {
*/
private Node parseReturn() {
var expr = require(parseExpression(), ";");
- Node ret = new ReturnNode(ctrlToken(), expr);
- _scope.update(CTRL_TOKEN,null);
+ Node ret = new ReturnNode(ctrl(), expr);
+ ctrl(null); // Kill control
return ret;
}
@@ -479,8 +449,7 @@ public class Parser {
private String parsePunctuation() {
int start = _position;
- if (isPunctuation(nextChar())) ;
- return new String(_input, start, --_position - start);
+ return new String(_input, start, start+1);
}
}
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java b/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java
index 8b157a9c..a726a22d 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/node/AddNode.java
@@ -57,11 +57,16 @@ public class AddNode extends Node {
if (t2.isConstant() && lhs instanceof AddNode && lhs.in(2)._type.isConstant() )
return new AddNode(lhs.in(1),new AddNode(lhs.in(2),rhs).peephole());
- // Do we have x + (y + con) ?
- // Swap to (x + y) + con
- if (rhs instanceof AddNode add && add.in(2)._type.isConstant() )
+ // Do we have x + (y + non_add) ?
+ // Swap to (x + y) + non_add
+ if (rhs instanceof AddNode add && !(add.in(2) instanceof AddNode) )
return new AddNode(new AddNode(lhs,add.in(1)).peephole(), add.in(2));
+ // Do we have (x + y) + x
+ // Swap to (x + x) + y
+ if (lhs instanceof AddNode add && add.in(1)==rhs )
+ return new AddNode(new AddNode(rhs,rhs).peephole(), add.in(2));
+
// Add of same to a multiply by 2
if( lhs==rhs )
return new MulNode(lhs,new ConstantNode(TypeInteger.constant(2)).peephole());
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java
index 2cafb1fa..f2fddd1c 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -25,8 +25,7 @@ public abstract class Node {
* <p>
* Generally fixed length, ordered, nulls allowed, no unused trailing space.
* Ordering is required because e.g. "a/b" is different from "b/a".
- * The first input (offset 0) is often a Control node.
- * @see Control
+ * The first input (offset 0) is often a {@link #isCFG} node.
*/
public final ArrayList<Node> _inputs;
@@ -102,13 +101,6 @@ public abstract class Node {
public int nIns() { return _inputs.size(); }
- /**
- * Gets the ith output node
- * @param i Offset of the output node
- * @return Output node (not null)
- */
- public Node out(int i) { return _outputs.get(i); }
-
public int nOuts() { return _outputs.size(); }
public boolean isUnused() { return nOuts() == 0; }
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter05/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index 5e979cfd..6fef99f3 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -31,6 +31,10 @@ public class PhiNode extends Node {
@Override
public Node idealize() {
+ // Remove a "junk" Phi: Phi(x,x) is just x
+ if( in(1)==in(2) )
+ return in(1);
+
return null;
}
}
diff --git a/chapter05/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter05/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 517ada00..60d2bd43 100644
--- a/chapter05/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/chapter05/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple.node;
+import com.seaofnodes.simple.Parser;
import com.seaofnodes.simple.type.TypeBot;
import java.util.*;
@@ -73,7 +74,15 @@ public class ScopeNode extends Node {
return null;
}
+ public Node ctrl() { assert lookup(Parser.CTRL)==in(0); return in(0); }
+
+ public Node ctrl(Node n) {
+ assert lookup(Parser.CTRL)==in(0);
+ set_def(0,n);
+ return n;
+ }
+
// If the name is present in any scope, then redefine
public Node update(String name, Node n) {
for (int i = _scopes.size() - 1; i >= 0; i--) {
@@ -86,23 +95,30 @@ public class ScopeNode extends Node {
}
/**
- * Duplicate a ScopeNode; including all levels.
+ * Duplicate a ScopeNode; including all levels, up to Nodes. So this is
+ * neither shallow (would dup the Scope but not the internal HashMap
+ * tables), nor deep (would dup the Scope, the HashMap tables, but then the
+ * also program Nodes).
*
- * We do it in a way that ensures that the new ScopeNode becomes
- * additional user of all defined names
+ * The new Scope is a full-fledged Node with proper use<->def edges.
*/
public ScopeNode dup() {
- ScopeNode dupScope = new ScopeNode();
- for (HashMap<String, Integer> tab: _scopes) {
- dupScope.push(); // Create same level in the duplicate
- for (Map.Entry<String, Integer> e: tab.entrySet()) {
- String name = e.getKey();
- Integer idx = e.getValue();
- Node n = in(idx);
- dupScope.define(name, n); // Ensure that dupScope is a user of n
- }
+ ScopeNode dup = new ScopeNode();
+ for( HashMap<String, Integer> tab : _scopes ) {
+ dup.push(); // Create same level in the duplicate
+ for( String name : tab.keySet() )
+ // Ensure that dup is a user of n
+ dup.define(name, in(tab.get(name)));
}
- return dupScope;
+ return dup;
+ }
+
+ public RegionNode mergeScopes(ScopeNode that) {
+ RegionNode r = (RegionNode)ctrl(new RegionNode(ctrl(), that.ctrl()).peephole());
+ for( int i=1; i<nIns(); i++ )
+ set_def(i,new PhiNode(r, in(i), that.in(i)).peephole());
+ that.kill(); // Kill merged scope
+ return r;
}
/**