summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chapter01/README.md35
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/Parser.java28
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/node/ConstantNode.java4
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/node/Node.java78
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/node/NodeIDGenerator.java19
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/node/ReturnNode.java4
-rw-r--r--chapter01/src/main/java/com/seaofnodes/simple/node/StartNode.java4
-rw-r--r--chapter01/src/test/java/com/seaofnodes/simple/ParserTest.java35
8 files changed, 82 insertions, 125 deletions
diff --git a/chapter01/README.md b/chapter01/README.md
index e5b79493..69a09328 100644
--- a/chapter01/README.md
+++ b/chapter01/README.md
@@ -71,34 +71,39 @@ at very little cost (nearly always small constant time) because all dependencies
### Unique Node ID
-Each node is assigned a unique dense integer Node ID when created. This ID is useful for debugging and e.g. as
-an index into a bit vector, which in turn is used to efficiently visit a (possibly cyclic) graph.
-The ID is also used to efficiently compute equality of nodes; a subject that we discuss in Chapter 8 Global Value Numbering.
+Each node is assigned a unique dense integer Node ID when created. This ID is
+useful for debugging, efficiently computing equality and e.g. as an index into
+a bit vector, which in turn is used to efficiently visit a (possibly cyclic)
+graph. We discuss Node equality in Chapter 8 Global Value Numbering.
### Start Node
-The Start node represents the start of the function. For now, we do not have any values in the Start node, this is because our function does not
-yet accept parameters. When we add parameters, the value of the Start node will be a tuple, and will require Projection nodes to extract the values.
+The Start node represents the start of the function. For now, we do not have any inputs to Start because our function does not
+yet accept parameters. When we add parameters the value of Start will be a tuple, and will require Projections to extract the values.
We discuss this in detail in Chapter 9: Functions and Calls.
### Constant Node
-The Constant node represents a constant value. At present, the only constants that we allow are integer literals; therefore the Constant node contains
-an integer value. As we add other types of constants, we will need to refactor how we represent Constant nodes.
+A Constant node represents a constant value. At present, the only constants
+that we allow are integer literals; therefore Constants contain an integer
+value. As we add other types of constants, we refactor how we represent
+Constants.
-The Constant node has no inputs. However, we set the Start node as an input to the Constant node to enable forward graph walk. This edge carries no semantic meaning,
-it is present _solely_ to allow visitation.
+Constants have no semantic inputs. However, we set Start as an input to
+Constants to enable a forward graph walk. This edge carries no semantic
+meaning, and it is present _solely_ to allow visitation.
-The Constant node's output is the value stored in it.
+The Constant's value is the value stored in it.
### Return Node
-The Return node has two inputs. The first input is a control node. The second input is the data node that supplies the return value.
+The Return node has two inputs. The first input is a control node and the
+second is the data node that supplies the return value.
-In this presentation, the Return node functions as the Stop node, since we are compiling a single unnamed function.
-A separate Stop node is not necessary. Compiling multiple functions is covered in Chapter 9: Functions and Calls.
+In this presentation, Return functions as a Stop node, since we are compiling a single unnamed function.
+A separate Stop is not necessary. Compiling multiple functions is covered in Chapter 9: Functions and Calls.
-The output of the Return node is the value it obtained from the data node.
+The Return's output is the value from the data node.
### Representation
@@ -111,4 +116,4 @@ return 1;
![Sea of Nodes Visual](./docs/02-graph.svg)
* Control nodes appear as square boxes
-* Control edges are in bold red \ No newline at end of file
+* Control edges are in bold red
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/Parser.java b/chapter01/src/main/java/com/seaofnodes/simple/Parser.java
index 2feeddf9..4da758b2 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/Parser.java
@@ -18,14 +18,8 @@ public class Parser {
*/
private Token _curTok;
- private final NodeIDGenerator _idGenerator;
-
private StartNode _startNode;
- public Parser() {
- _idGenerator = new NodeIDGenerator();
- }
-
public StartNode parse(String source) {
Lexer lexer = new Lexer(source);
nextToken(lexer);
@@ -33,7 +27,7 @@ public class Parser {
}
private StartNode parseProgram(Lexer lexer) {
- _startNode = new StartNode(_idGenerator);
+ _startNode = new StartNode();
parseReturnStatement(lexer);
return _startNode;
}
@@ -49,7 +43,7 @@ public class Parser {
matchIdentifier(lexer, "return");
var returnExpr = parseExpression(lexer);
matchPunctuation(lexer, ";");
- return new ReturnNode(_idGenerator, _startNode, returnExpr);
+ return new ReturnNode(_startNode, returnExpr);
}
/**
@@ -109,7 +103,7 @@ public class Parser {
* </pre>
*/
private ConstantNode parseIntegerLiteral(Lexer lexer) {
- var constantNode = new ConstantNode(_idGenerator, _curTok._num.longValue(), _startNode);
+ var constantNode = new ConstantNode(_curTok._num.longValue(), _startNode);
nextToken(lexer);
return constantNode;
}
@@ -159,8 +153,7 @@ public class Parser {
public final Kind _kind;
/**
- * String representation of a token - always
- * populated
+ * String representation of a token - always populated
*/
public final String _str;
/**
@@ -189,6 +182,7 @@ public class Parser {
*/
public static Token EOF = new Token(Kind.EOZ, "", null);
+ @Override
public String toString() {
return _str;
}
@@ -236,13 +230,13 @@ public class Parser {
* Note that EOF is not whitespace!
*/
private boolean isWhiteSpace(char c) {
- return c == ' ' ||
- c == '\t' ||
- c == '\n' ||
- c == '\r';
+ return c == ' ' ||
+ c == '\t' ||
+ c == '\n' ||
+ c == '\r';
}
- private void skipWhitespace() {
+ private void skipWhiteSpace() {
while (isWhiteSpace(_cur))
nextToken();
}
@@ -296,7 +290,7 @@ public class Parser {
* Gets the next token
*/
public Token next() {
- skipWhitespace();
+ skipWhiteSpace();
if (_cur == 0)
return Token.EOF;
if (Character.isDigit(_cur))
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/chapter01/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index 31718a1d..e7a3b3f2 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -13,8 +13,8 @@ public class ConstantNode extends Node {
public final long _value;
- public ConstantNode(NodeIDGenerator idGenerator, long value, StartNode startNode) {
- super(idGenerator, OP_CONSTANT, startNode);
+ public ConstantNode(long value, StartNode startNode) {
+ super(startNode);
_value = value;
}
}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter01/src/main/java/com/seaofnodes/simple/node/Node.java
index e78ab19a..bafa58f4 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -2,32 +2,14 @@ package com.seaofnodes.simple.node;
import java.util.ArrayList;
import java.util.Arrays;
-import java.util.List;
+import java.util.Collections;
/**
* All Nodes in the Sea of Nodes IR inherit from the Node class.
* The Node class provides common functionality used by all subtypes.
* Subtypes of Node specialize by overriding methods.
*/
-public class Node {
-
- /**
- * @see StartNode
- */
- static final byte OP_START = 1;
- /**
- * @see ReturnNode
- */
- static final byte OP_RETURN = 2;
- /**
- * @see ConstantNode
- */
- static final byte OP_CONSTANT = 3;
-
- /**
- * The opcode for this node
- */
- public final byte _opCode;
+public abstract class Node {
/**
* Each node has a unique dense Node ID within a compilation context
@@ -40,34 +22,40 @@ public class Node {
* Inputs to the node. These are use-def references to Nodes.
* <p>
* Generally fixed length, ordered, nulls allowed, no unused trailing space.
- * The first input (offset 0) must be a Control nodes.
+ * 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
*/
- public final List<Node> _inputs;
+ public final ArrayList<Node> _inputs;
/**
- * Outputs reference Nodes that are not null and
- * have this Node as an input. These nodes are users of this
- * node, thus these are def-use references to Nodes.
+ * Outputs reference Nodes that are not null and have this Node as an
+ * input. These nodes are users of this node, thus these are def-use
+ * references to Nodes.
* <p>
- * Note that the outputs are derived from inputs, it appears that
- * outputs are therefore a performance optimization to make it easier to
- * traverse the graph. The Sea of Nodes documentation in chapter 1 does not
- * mention these outputs list.
+
+ * Outputs directly match inputs, making a directed graph that can be
+ * walked in either direction. These outputs are typically used for
+ * efficient optimizations but otherwise have no semantics meaning.
*/
- public final List<Node> _outputs;
+ public final ArrayList<Node> _outputs;
+
+ /**
+ * A private Global Static mutable counter, for unique node id generation.
+ * To make the compiler multi-threaded, this field will have to move into a TLS.
+ * Starting with value 1, to avoid bugs confusing node ID 0 with uninitialized values.
+ * */
+ private static int UNIQUE_ID = 1;
- protected Node(NodeIDGenerator idGenerator, byte opCode, Node ...inputs)
- {
- _nid = idGenerator.newNodeID(); // allocate unique dense ID
- _opCode = opCode;
- _inputs = Arrays.asList(inputs);
+ protected Node(Node ...inputs) {
+ _nid = UNIQUE_ID++; // allocate unique dense ID
+ _inputs = new ArrayList<>();
+ Collections.addAll(_inputs,inputs);
_outputs = new ArrayList<>();
- for (int i = 0; i < _inputs.size(); i++) {
- Node n = _inputs.get(i);
- if (n != null)
- n._outputs.add(this);
- }
+ for( Node n : _inputs ) {
+ if( n != null )
+ n._outputs.add( this );
+ }
}
/**
@@ -77,17 +65,11 @@ public class Node {
*/
public Node in(int i) { return _inputs.get(i); }
- public int numInputs() { return _inputs.size(); }
+ public int nIns() { return _inputs.size(); }
public Node out(int i) { return _outputs.get(i); }
- public int numOutputs() { return _outputs.size(); }
-
- /**
- * Is this a control node, all control nodes are marked with
- * the interface Control.
- */
- public boolean isControl() { return this instanceof Control; }
+ public int nOuts() { return _outputs.size(); }
/*
* hashCode and equals implementation to be added in later chapter.
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/node/NodeIDGenerator.java b/chapter01/src/main/java/com/seaofnodes/simple/node/NodeIDGenerator.java
deleted file mode 100644
index 96a292df..00000000
--- a/chapter01/src/main/java/com/seaofnodes/simple/node/NodeIDGenerator.java
+++ /dev/null
@@ -1,19 +0,0 @@
-package com.seaofnodes.simple.node;
-
-/**
- * Nodes are given unique dense ids.
- */
-public class NodeIDGenerator {
- /**
- * Counter for node IDs, do not hand out NID 0
- * (Presumably) because it is used for the control node?
- */
- private int nodeIDCounter = 1;
-
- public int newNodeID() {
- if (nodeIDCounter > 100000) {
- throw new RuntimeException("infinite node create loop");
- }
- return nodeIDCounter++;
- }
-}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/chapter01/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index 0b62cd4c..eafcab94 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -10,7 +10,7 @@ package com.seaofnodes.simple.node;
*/
public class ReturnNode extends Node implements Control {
- public ReturnNode(NodeIDGenerator idGenerator, Node... inputs) {
- super(idGenerator, OP_RETURN, inputs);
+ public ReturnNode(Node ctrl, Node data) {
+ super(ctrl,data);
}
}
diff --git a/chapter01/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter01/src/main/java/com/seaofnodes/simple/node/StartNode.java
index 7a9233a8..6944e8fc 100644
--- a/chapter01/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/chapter01/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -7,7 +7,7 @@ package com.seaofnodes.simple.node;
*/
public class StartNode extends Node implements Control {
- public StartNode(NodeIDGenerator idGenerator, Node... inputs) {
- super(idGenerator, OP_START, inputs);
+ public StartNode(/*arguments go here*/) {
+ super();
}
}
diff --git a/chapter01/src/test/java/com/seaofnodes/simple/ParserTest.java b/chapter01/src/test/java/com/seaofnodes/simple/ParserTest.java
index ad420ece..9bc2fc1c 100644
--- a/chapter01/src/test/java/com/seaofnodes/simple/ParserTest.java
+++ b/chapter01/src/test/java/com/seaofnodes/simple/ParserTest.java
@@ -1,9 +1,6 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.node.ConstantNode;
-import com.seaofnodes.simple.node.Node;
-import com.seaofnodes.simple.node.ReturnNode;
-import com.seaofnodes.simple.node.StartNode;
+import com.seaofnodes.simple.node.*;
import org.junit.Assert;
import org.junit.Test;
@@ -14,22 +11,20 @@ public class ParserTest {
Parser parser = new Parser();
StartNode startNode = parser.parse("return 1;");
Assert.assertNotNull(startNode);
- Assert.assertTrue(startNode.isControl());
- Assert.assertEquals(2, startNode.numOutputs());
- for (int i = 0; i < startNode.numOutputs(); i++) {
- Node out = startNode.out(i);
- if (out instanceof ReturnNode) {
- Assert.assertEquals(2, out.numInputs());
- Assert.assertEquals(startNode, out.in(0));
- Assert.assertTrue(out.isControl());
- Assert.assertTrue(out.in(1) instanceof ConstantNode);
- }
- else if (out instanceof ConstantNode cn) {
- Assert.assertEquals(1, cn._value);
- Assert.assertEquals(1, cn.numInputs());
- Assert.assertEquals(startNode, cn.in(0));
- }
- else {
+ Assert.assertEquals(2, startNode.nOuts());
+ for (int i = 0; i < startNode.nOuts(); i++) {
+ switch( startNode.out(i) ) {
+ case ReturnNode ret:
+ Assert.assertEquals(2, ret.nIns());
+ Assert.assertEquals(startNode, ret.in(0));
+ Assert.assertTrue(ret.in(1) instanceof ConstantNode);
+ break;
+ case ConstantNode con:
+ Assert.assertEquals(1, con._value);
+ Assert.assertEquals(1, con.nIns());
+ Assert.assertEquals(startNode, con.in(0));
+ break;
+ default:
throw new IllegalStateException();
}
}