summaryrefslogtreecommitdiff
path: root/chapter18/src/main/java/com
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-01-05 12:53:52 -0800
committerCliff Click <cliffc@acm.org>2025-01-15 15:07:30 -0800
commitb5b972aec72eeafe9e044704b3b6741a7362bb9b (patch)
tree9bce5f4b5eb7764b7b26deb4118bb794e685f604 /chapter18/src/main/java/com
parentf48010ac0f0d47321c5f0fa98bb57dd2278b27a0 (diff)
downloadSimple-b5b972aec72eeafe9e044704b3b6741a7362bb9b.tar.gz
Simple-b5b972aec72eeafe9e044704b3b6741a7362bb9b.zip
Finalize function semantics
Rename ScopeMin to MergeMem. Move Var to its own file. Allow out-of-scope id lookups in functions, as long as its a final constant. This allows e.g. factorial to find "fact" as an out-of-scope final constant function.
Diffstat (limited to 'chapter18/src/main/java/com')
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java4
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java9
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Parser.java62
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/Var.java51
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java30
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java (renamed from chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java)68
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java48
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java2
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/type/Type.java4
-rw-r--r--chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java8
12 files changed, 161 insertions, 129 deletions
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 69434472..d3ee62c3 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -206,7 +206,7 @@ public abstract class GlobalCodeMotion {
case NewNode st: 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
- case ScopeMinNode ret: break; // Mem uses now on ScopeMin
+ case MemMergeNode ret: break; // Mem uses now on ScopeMin
case NeverNode never: break;
default: throw Utils.TODO();
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java b/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
index bd778696..5d6d3328 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/GraphVisualizer.java
@@ -81,7 +81,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 CProjNode || n instanceof ScopeMinNode || n==Parser.XCTRL)
+ if (n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n==Parser.XCTRL)
continue; // Do not emit, rolled into MultiNode or Scope cluster already
if( _separateControlCluster && doCtrl && !(n instanceof CFGNode) ) continue;
if( _separateControlCluster && !doCtrl && (n instanceof CFGNode) ) continue;
@@ -163,7 +163,7 @@ public class GraphVisualizer {
sb.append("\tsubgraph cluster_").append(scopeName).append(" {\n"); // Magic "cluster_" in the subgraph name
// Special for memory ScopeMinNode
if( level==0 ) {
- ScopeMinNode n = scope.mem();
+ MemMergeNode n = scope.mem();
sb.append("\t\t").append(n.uniqueName()).append(" [label=\"").append(n.glabel()).append("\"];\n");
}
sb.append("\t\t").append(scopeName).append(" [label=<\n");
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java b/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java
index 870a890b..aa498b16 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/IRPrinter.java
@@ -18,7 +18,7 @@ public class IRPrinter {
for( Node def : n._inputs )
sb.p(def==null ? "____" : "%4d".formatted(def._nid))
// Lazy Phi indicator
- .p(n instanceof ScopeMinNode && def instanceof ScopeMinNode ? "^" : " ");
+ .p(n instanceof MemMergeNode && def instanceof MemMergeNode ? "^" : " ");
for( int i = n._inputs.size(); i<4; i++ )
sb.p(" ");
sb.p(" [[ ");
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java b/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java
index 869448fa..adbe9f30 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/JSViewer.java
@@ -1,6 +1,5 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.CodeGen;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.io.IOException;
@@ -132,7 +131,7 @@ public class JSViewer implements AutoCloseable {
// Just the Nodes first, in a cluster no edges
sb.i().p("subgraph cluster_Nodes {\n").ii(); // Magic "cluster_" in the subgraph name
for (Node n : all) {
- if( n instanceof ProjNode || n instanceof CProjNode || n instanceof ScopeMinNode || n==Parser.XCTRL )
+ if( n instanceof ProjNode || n instanceof CProjNode || n instanceof MemMergeNode || n==Parser.XCTRL )
continue; // Do not emit, rolled into MultiNode or Scope cluster already
sb.i().p(n.uniqueName()).p(" [ ");
if( n instanceof MultiNode ) {
@@ -194,7 +193,7 @@ public class JSViewer implements AutoCloseable {
sb.i().p("<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n");
int lexStart=scope._lexSize.at(level);
// Special for memory ScopeMinNode
- ScopeMinNode n = scope.nIns()>1 ? scope.mem() : null;
+ MemMergeNode n = scope.nIns()>1 ? scope.mem() : null;
if( level==0 && n!=null && n.nIns()>2 ) {
sb.i().p("<TR>");
for( int m=2; m<n.nIns(); m++ )
@@ -276,7 +275,7 @@ public class JSViewer implements AutoCloseable {
n instanceof CProjNode ||
// ScopeNodes are done separately
n instanceof ScopeNode ||
- n instanceof ScopeMinNode
+ n instanceof MemMergeNode
)
continue;
if( n.isDead() )
@@ -351,7 +350,7 @@ public class JSViewer implements AutoCloseable {
// Memory
if( scope.nIns()>1 ) {
- ScopeMinNode n = scope.mem();
+ MemMergeNode n = scope.mem();
for( int i=2; i<n.nIns(); i++ ) {
Node def = n.in(i);
while( def instanceof ScopeNode lazy )
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
index 9bce5a5c..e518d2a0 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/Parser.java
@@ -104,6 +104,7 @@ public class Parser {
public Parser(String source, TypeInteger arg) {
Node.reset();
IterPeeps.reset();
+ Type.reset();
_lexer = new Lexer(source);
_scope = new ScopeNode();
_continueScope = _breakScope = null;
@@ -155,10 +156,10 @@ public class Parser {
_scope.define(ScopeNode.ARG0, TypeInteger.BOT, false, null, _lexer);
ctrl(XCTRL);
- _scope.mem(new ScopeMinNode(false));
+ _scope.mem(new MemMergeNode(false));
// Parse whole program, as-if function header "{ int arg -> body }"
- ReturnNode ret = parseFunctionBody(TypeFunPtr.MAIN,loc(),"arg");
+ parseFunctionBody(TypeFunPtr.MAIN,loc(),"arg");
if( !_lexer.isEOF() ) throw _errorSyntax("unexpected");
@@ -205,10 +206,10 @@ public class Parser {
// Pre-call the function from Start, with worse-case arguments. This
// represents all the future, yet-to-be-parsed functions calls and
// external calls.
- _scope.push();
+ _scope.push(ScopeNode.Kind.Function);
ctrl(fun); // Scope control from function
// Private mem alias tracking per function
- ScopeMinNode mem = new ScopeMinNode(true);
+ MemMergeNode mem = new MemMergeNode(true);
mem.addDef(null); // Alias#0
mem.addDef(new ParmNode(ScopeNode.MEM0,1,TypeMem.BOT,fun,con(TypeMem.BOT)).peephole()); // All aliases
_scope.mem(mem);
@@ -261,9 +262,9 @@ public class Parser {
* Does not parse the opening or closing '{}'
* @return a {@link Node} or {@code null}
*/
- private Node parseBlock(boolean inCon) {
+ private Node parseBlock(ScopeNode.Kind kind) {
// Enter a new scope
- _scope.push(inCon);
+ _scope.push(kind);
Node last = ZERO;
while (!peek('}') && !_lexer.isEOF())
last = parseStatement();
@@ -295,7 +296,7 @@ public class Parser {
// Break ambiguity around leading function types and starting a block
else if (peek('{') && !isTypeFun() ) {
match("{");
- return require(parseBlock(false),"}");
+ return require(parseBlock(ScopeNode.Kind.Block),"}");
}
// Declaration or normal assignment/expression
else return parseDeclarationStatement();
@@ -331,7 +332,7 @@ public class Parser {
// }
// }
require("(");
- _scope.push(); // Scope for the index variables
+ _scope.push(ScopeNode.Kind.Block); // Scope for the index variables
if( !match(";") ) // Can be empty init "for(;test;next) body"
parseDeclarationStatement(); // Non-empty init
Node rez = parseLooping(true);
@@ -614,7 +615,7 @@ public class Parser {
Node expr = parseAsgn();
// Final variable to update
- ScopeMinNode.Var def = _scope.lookup(name);
+ Var def = _scope.lookup(name);
if( def==null )
throw error("Undefined name '" + name + "'");
@@ -647,9 +648,7 @@ public class Parser {
expr = zsMask(expr,t);
// Type is sane
if( !expr._type.isa(t) )
- throw expr instanceof FRefNode fref
- ? fref.err()
- : error("Type " + expr._type.str() + " is not of declared type " + t.str());
+ throw error("Type " + expr._type.str() + " is not of declared type " + t.str());
return expr;
}
@@ -720,10 +719,11 @@ public class Parser {
// Lift expression, based on type
Node lift = liftExpr(expr, t, xfinal);
+
if( xfinal && t instanceof TypeMemPtr tmp )
t = tmp.makeRO();
- // Define a new name,
+ // Define a new name
if( !_scope.define(name,t,xfinal,lift, loc) )
throw error("Redefining name '" + name + "'", loc);
return lift;
@@ -746,7 +746,7 @@ public class Parser {
throw error("struct '" + typeName + "' cannot be redefined");
// A Block scope parse, and inspect the scope afterward for fields.
- _scope.push(true);
+ _scope.push(ScopeNode.Kind.Constructor);
require("{");
while (!peek('}') && !_lexer.isEOF())
parseStatement();
@@ -758,7 +758,7 @@ public class Parser {
Field[] fs = new Field[varlen-lexlen];
for( int i=lexlen; i<varlen; i++ ) {
s.addDef(_scope.in(i));
- ScopeMinNode.Var v = _scope._vars.at(i);
+ Var v = _scope._vars.at(i);
fs[i-lexlen] = Field.make(v._name,v.type(),ALIAS++,v._final);
}
TypeStruct ts = s._ts = TypeStruct.make(typeName, fs);
@@ -814,6 +814,8 @@ public class Parser {
// Cannot check earlier, because cannot find required 'id' until after "[]?" syntax
int old2 = pos();
String id = _lexer.matchId();
+ if( !(peek(',') || peek(';')) )
+ return posT(old1);
pos(old2); // Reset lexer to reparse
if( id==null || _scope.lookup(id)!=null )
return posT(old1); // Reset lexer to reparse
@@ -1015,7 +1017,7 @@ public class Parser {
int delta = _lexer.peek(-1)=='+' ? 1 : -1; // Pre vs post
String name = _lexer.matchId();
if( name!=null ) {
- ScopeMinNode.Var n = _scope.lookup(name);
+ Var n = _scope.lookup(name);
if( n != null && !(n.type() instanceof TypeMemPtr) ) {
if( n._final )
throw error("Cannot reassign final '"+n._name+"'");
@@ -1051,10 +1053,11 @@ public class Parser {
if( matchx("new" ) ) return alloc();
if( match ("{" ) ) return require(func(),"}");
// Expect an identifier now
- ScopeMinNode.Var n = requireLookupId();
+ Var n = requireLookupId();
Node rvalue = _scope.in(n);
if( rvalue._type == Type.BOTTOM )
- throw error("Cannot read uninitialized field '"+n._name+"'");
+ if( rvalue instanceof FRefNode ) return rvalue;
+ else throw error("Cannot read uninitialized field '"+n._name+"'");
// Check for assign-update, x += e0;
char ch = _lexer.matchOperAssign();
if( ch==0 ) return rvalue;
@@ -1091,16 +1094,20 @@ public class Parser {
}
// Expect an ID here. If not found, assume a forward reference function
- ScopeMinNode.Var requireLookupId() {
+ Var requireLookupId() {
String id = _lexer.matchId();
if( id == null || KEYWORDS.contains(id) )
throw errorSyntax("an identifier or expression");
- ScopeMinNode.Var n = _scope.lookup(id);
+ Var n = _scope.lookup(id);
// If missing, assume a forward reference
if( n==null ) {
- _scope.define(id, TypeFunPtr.BOT, false, XCTRL, loc());
+ _scope.define(id, FRefNode.FREF_TYPE, true, XCTRL, loc());
n = _scope.lookup(id);
- //throw error("Undefined name '" + id + "'");
+ } else {
+ // Lookup worked on an out-of-function value. No closures, so this
+ // has to be a final constant.
+ if( _scope.outOfFunction(n) && !(n._final && _scope.in(n._idx)._type.isConstant()) )
+ throw error("Variable '"+n._name+"' is out of function scope and must be a final constant");
}
return n;
}
@@ -1138,13 +1145,12 @@ public class Parser {
if( hasConstructor ) {
idx = _scope.nIns();
// Push a scope, and pre-assign all struct fields.
- _scope.push();
+ _scope.push(ScopeNode.Kind.Block);
Lexer loc = loc();
for( int i=0; i<fs.length; i++ )
_scope.define(fs[i]._fname, fs[i]._type, fs[i]._final, s.in(i)._type==Type.TOP ? con(Type.BOTTOM) : s.in(i), loc);
// Parse the constructor body
- parseBlock(true);
- require("}");
+ require(parseBlock(ScopeNode.Kind.Constructor),"}");
init = _scope._inputs;
}
// Check that all fields are initialized
@@ -1261,7 +1267,7 @@ public class Parser {
return val.unkeep(); // "obj.a = expr" returns the expression while updating memory
}
- Node load = new LoadNode(loc(),name, f._alias, tf.glb(), memAlias(f._alias), expr.keep(), off);
+ Node load = new LoadNode(loc(),name, f._alias, tf, memAlias(f._alias), expr.keep(), off);
// Arrays include control, as a proxy for a safety range check
// Structs don't need this; they only need a NPE check which is
// done via the type system.
@@ -1344,7 +1350,7 @@ public class Parser {
private Node functionCall(Node expr) {
if( expr._type == Type.NIL )
throw error("Calling a null function pointer");
- if( !(expr._type instanceof TypeFunPtr tfp) )
+ if( !expr._type.isa(TypeFunPtr.BOT) )
throw error("Expected a function but got "+expr._type.glb().str());
expr.keep(); // Keep while parsing args
@@ -1372,7 +1378,7 @@ public class Parser {
// Control from CallEnd
ctrl(new CProjNode(cend,0,ScopeNode.CTRL).peephole());
// Memory from CallEnd
- ScopeMinNode mem = new ScopeMinNode(true);
+ MemMergeNode mem = new MemMergeNode(true);
mem.addDef(null); // Alias#0
mem.addDef(new ProjNode(cend,1,ScopeNode.MEM0).peephole());
_scope.mem(mem);
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/Var.java b/chapter18/src/main/java/com/seaofnodes/simple/Var.java
new file mode 100644
index 00000000..6a8fe86d
--- /dev/null
+++ b/chapter18/src/main/java/com/seaofnodes/simple/Var.java
@@ -0,0 +1,51 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.node.FRefNode;
+import com.seaofnodes.simple.type.*;
+
+
+/**
+ * The tracked fields are now complex enough to deserve a array-of-structs layout
+ */
+public class Var {
+
+ public final String _name; // Declared name
+ public int _idx; // index in containing scope
+ private Type _type; // Declared type
+ public boolean _final; // Final field
+ public Parser.Lexer _loc; // Source location
+
+ public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc) {
+ _idx = idx;
+ _name = name;
+ _type = type;
+ _final = xfinal;
+ _loc = loc;
+ }
+ public Type type() {
+ if( !_type.isFRef() ) return _type;
+ // Update self to no longer use the forward ref type
+ Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name);
+ return (_type=_type.meet(def));
+ }
+ public Type lazyGLB() {
+ Type t = type();
+ return t instanceof TypeMemPtr ? t : t.glb();
+ }
+
+ // Forward reference variables (not types) can only be a bottom
+ // function pointer.
+ public boolean isFRef() { return type()== FRefNode.FREF_TYPE; }
+
+ public boolean defFRef( Type type, boolean xfinal, Parser.Lexer loc ) {
+ assert isFRef() && xfinal;
+ _type = type;
+ _final = true;
+ _loc = loc;
+ return true;
+ }
+
+ @Override public String toString() {
+ return _type.toString()+(_final ? " ": " !")+_name;
+ }
+}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java
index 5b46959e..beb14870 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/FRefNode.java
@@ -1,35 +1,25 @@
package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Var;
import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.TypeFunPtr;
import java.util.BitSet;
/**
- * 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 will refactor how we represent
- * Constants.
- * <p>
- * 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 <em>solely</em> to allow visitation.
- * <p>
- * The Constant's value is the value stored in it.
+ * A Forward Reference. Its any final constant, including functions. When
+ * the Def finally appears its plugged into the forward reference, which then
+ * peepholes to the Def.
*/
public class FRefNode extends ConstantNode {
- public final ScopeMinNode.Var _n;
- public FRefNode(ScopeMinNode.Var n) { super(TypeFunPtr.BOT); _n = n; }
+ public static final Type FREF_TYPE = Type.TOP;
+ public final Var _n;
+ public FRefNode( Var n ) { super(FREF_TYPE); _n = n; }
- @Override
- public String label() { return "FRef"+_n; }
+ @Override public String label() { return "FRef"+_n; }
- @Override
- public String uniqueName() { return "FRef_" + _nid; }
+ @Override public String uniqueName() { return "FRef_" + _nid; }
- @Override
- StringBuilder _print1(StringBuilder sb, BitSet visited) {
+ @Override StringBuilder _print1(StringBuilder sb, BitSet visited) {
return sb.append("FRef_").append(_n);
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
index 38c169fa..61d99ed1 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java
@@ -2,61 +2,25 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.IterPeeps;
import com.seaofnodes.simple.Parser;
-import com.seaofnodes.simple.Utils;
+import com.seaofnodes.simple.Var;
import com.seaofnodes.simple.type.*;
import java.util.*;
-
import static com.seaofnodes.simple.Utils.TODO;
-/* */
-public class ScopeMinNode extends Node {
-
- public final boolean _inProgress;
+/**
+ * Memory Merge - a merge of many aliases into a "fat memory". All aliases
+ * are here, but most will be lazy - take the default fat memory.
+ */
+public class MemMergeNode extends Node {
- /** The tracked fields are now complex enough to deserve a array-of-structs layout
+ /*
+ * In-Progress means this is being used by the Parser to track memory
+ * aliases. No optimizations are allowed. When no longer "in progress"
+ * normal peeps work.
*/
- public static class Var {
- public final String _name; // Declared name
- public int _idx; // index in containing scope
- private Type _type; // Declared type
- public boolean _final; // Final field
- public Parser.Lexer _loc; // Source location
- public Var(int idx, String name, Type type, boolean xfinal, Parser.Lexer loc) {
- _idx = idx;
- _name = name;
- _type = type;
- _final = xfinal;
- _loc = loc;
- }
- public Type type() {
- if( !_type.isFRef() ) return _type;
- // Update self to no longer use the forward ref type
- Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name);
- return (_type=_type.meet(def));
- }
- public Type lazyGLB() {
- Type t = type();
- return t instanceof TypeMemPtr ? t : t.glb();
- }
-
- // Forward reference variables (not types) can only be a bottom
- // function pointer.
- public boolean isFRef() { return type()==TypeFunPtr.BOT; }
-
- public boolean defFRef( Type type, boolean xfinal, Parser.Lexer loc ) {
- assert isFRef();
- _type = type;
- _final = xfinal;
- _loc = loc;
- return true;
- }
-
- @Override public String toString() {
- return _type.toString()+(_final ? " ": " !")+_name;
- }
- }
+ public final boolean _inProgress;
- public ScopeMinNode(boolean inProgress) { _type = TypeMem.BOT; _inProgress = inProgress; }
+ public MemMergeNode( boolean inProgress) { _type = TypeMem.BOT; _inProgress = inProgress; }
// If being used by a Scope, this is "in progress" from the Parser.
@@ -88,7 +52,7 @@ public class ScopeMinNode extends Node {
// not related to the parser in any way.
public Node merge() {
// Force default memory to not be lazy
- ScopeMinNode merge = new ScopeMinNode(false);
+ MemMergeNode merge = new MemMergeNode(false);
for( Node n : _inputs )
merge.addDef(n);
merge._mem(1,null);
@@ -127,7 +91,7 @@ public class ScopeMinNode extends Node {
// then it must be START.proj(1)
Node old = alias(alias);
if( old instanceof ScopeNode loop ) {
- ScopeMinNode loopmem = loop.mem();
+ MemMergeNode loopmem = loop.mem();
Node memdef = loopmem.alias(alias);
// Lazy phi!
old = memdef instanceof PhiNode phi && loop.ctrl()==phi.region()
@@ -144,7 +108,7 @@ public class ScopeMinNode extends Node {
}
- void _merge(ScopeMinNode that, RegionNode r) {
+ void _merge( MemMergeNode that, RegionNode r) {
int len = Math.max(nIns(),that.nIns());
for( int i = 2; i < len; i++)
if( alias(i) != that.alias(i) ) { // No need for redundant Phis
@@ -158,7 +122,7 @@ public class ScopeMinNode extends Node {
}
// Fill in the backedge of any inserted Phis
- void _endLoopMem( ScopeNode scope, ScopeMinNode back, ScopeMinNode exit ) {
+ void _endLoopMem( ScopeNode scope, MemMergeNode back, MemMergeNode exit ) {
Node exit_def = exit.alias(1);
for( int i=1; i<back.nIns(); i++ ) {
if( in(i) instanceof PhiNode phi && phi.region()==scope.ctrl() ) {
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 2ad6e5b7..727f7bbf 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -6,9 +6,9 @@ import java.util.*;
/**
* The Scope node is purely a parser helper - it tracks names to nodes with a
- * stack of hashmaps.
+ * stack of variables.
*/
-public class ScopeNode extends ScopeMinNode {
+public class ScopeNode extends MemMergeNode {
/**
* The control is a name that binds to the currently active control
@@ -21,11 +21,12 @@ public class ScopeNode extends ScopeMinNode {
// All active/live variables in all nested scopes, all run together
public final Ary<Var> _vars;
- // Since of each nested lexical scope
+ // Size of each nested lexical scope
public final Ary<Integer> _lexSize;
- // True if parsing inside of a constructor
- public final Ary<Boolean> _inCons;
+ // Lexical scope is one of normal Block, constructor or function
+ public enum Kind { Block, Constructor, Function };
+ public final Ary<Kind> _kinds;
// Extra guards; tested predicates and casted results
private final Ary<Node> _guards;
@@ -35,7 +36,7 @@ public class ScopeNode extends ScopeMinNode {
super(true);
_vars = new Ary<>(Var .class);
_lexSize= new Ary<>(Integer.class);
- _inCons = new Ary<>(Boolean.class);
+ _kinds = new Ary<>(Kind .class);
_guards = new Ary<>(Node .class);
}
@@ -68,7 +69,7 @@ public class ScopeNode extends ScopeMinNode {
public Node ctrl() { return in(0); }
- public ScopeMinNode mem() { return (ScopeMinNode)in(1); }
+ public MemMergeNode mem() { return (MemMergeNode)in(1); }
/**
* The ctrl of a ScopeNode is always bound to the currently active
@@ -83,19 +84,18 @@ public class ScopeNode extends ScopeMinNode {
public <N extends Node> N ctrl(N n) { return setDef(0,n); }
public Node mem(Node n) { return setDef(1,n); }
- public void push() { push(false); }
- public void push(boolean inCon) {
- assert _lexSize._len==_inCons._len;
+ public void push(Kind kind) {
+ assert _lexSize._len==_kinds._len;
_lexSize.push(_vars.size());
- _inCons.push(inCon);
+ _kinds .push(kind);
}
// Pop a lexical scope
public void pop() {
- assert _lexSize._len==_inCons._len;
+ assert _lexSize._len==_kinds._len;
promote();
int n = _lexSize.pop();
- _inCons.pop();
+ _kinds.pop();
popUntil(n);
_vars.setLen(n);
}
@@ -119,7 +119,16 @@ public class ScopeNode extends ScopeMinNode {
}
- public boolean inCon() { return _inCons.last(); }
+ public boolean inCon() { return _kinds.last() == Kind.Constructor; }
+
+ // Is v outside any current function scope?
+ public boolean outOfFunction( Var v ) {
+ for( int i=_lexSize._len-1; i>=0 && v._idx<_lexSize.at(i); i-- )
+ if( _kinds.at(i)==Kind.Function )
+ return true;
+ return false;
+ }
+
// Find name in reverse, return an index into _vars or -1. Linear scan
// instead of hashtable, but probably doesn't matter until the scan
@@ -141,8 +150,9 @@ public class ScopeNode extends ScopeMinNode {
Var n = _vars.at(i);
if( n._name.equals(name) ) {
if( !n.isFRef() ) return false; // Double define
- n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location
FRefNode fref = (FRefNode)in(n._idx); // Get forward ref
+ if( !xfinal || !declaredType.isConstant() ) throw fref.err(); // Must be a final constant
+ n.defFRef(declaredType,xfinal,loc); // Declare full correct type, final, source location
setDef(n._idx,fref.addDef(init)); // Set FRef to defined; tell parser also
}
}
@@ -185,7 +195,7 @@ public class ScopeNode extends ScopeMinNode {
update(_vars.at(idx),n);
}
- public Var update( ScopeNode.Var v, Node st ) {
+ public Var update( Var v, Node st ) {
Node old = in(v._idx);
if( old instanceof ScopeNode loop ) {
// Lazy Phi!
@@ -225,7 +235,7 @@ public class ScopeNode extends ScopeMinNode {
// 3) Ensure that the order of defs is the same to allow easy merging
dup._vars .addAll(_vars );
dup._lexSize.addAll(_lexSize);
- dup._inCons .addAll(_inCons );
+ dup._kinds .addAll(_kinds );
dup._guards .addAll(_guards );
// The dup'd guards all need dup'd keepers, to keep proper accounting
// when later removing all guards
@@ -235,7 +245,7 @@ public class ScopeNode extends ScopeMinNode {
dup.addDef(ctrl()); // Control input is just copied
// Memory input is a shallow copy
- ScopeMinNode memdup = new ScopeMinNode(true), mem = mem();
+ MemMergeNode memdup = new MemMergeNode(true), mem = mem();
memdup.addDef(null);
memdup.addDef(loop ? this : mem.in(1));
for( int i=2; i<mem.nIns(); i++ )
@@ -285,7 +295,7 @@ public class ScopeNode extends ScopeMinNode {
// Forward refs are copied to the other side, "as if" they were there all along.
public void balanceIf( ScopeNode scope ) {
for( int i = nIns(); i < scope.nIns(); i++ ) {
- ScopeMinNode.Var n = scope._vars.at(i);
+ Var n = scope._vars.at(i);
if( n.isFRef() ) { // RHS has forward refs
_vars.add(n); // Copy to LHS
addDef(scope.in(i));
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 009296e8..feb8347f 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -47,7 +47,7 @@ public class StopNode extends CFGNode {
public Node idealize() {
int len = nIns();
for( int i=0; i<nIns(); i++ )
- if( ((ReturnNode)in(i)).fun()==null )
+ if( ((ReturnNode)in(i)).fun().isDead() )
delDef(i--);
if( len != nIns() ) return this;
return null;
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter18/src/main/java/com/seaofnodes/simple/type/Type.java
index ac7d559d..95fdca74 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -229,4 +229,8 @@ public class Type {
// This is used by error messages, and is a shorted print.
public String str() { return STRS[_type]; }
+
+ public static void reset() {
+ TypeFunPtr.reset();
+ }
}
diff --git a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
index ace85edc..f0dc36a7 100644
--- a/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
+++ b/chapter18/src/main/java/com/seaofnodes/simple/type/TypeFunPtr.java
@@ -124,4 +124,12 @@ public class TypeFunPtr extends TypeNil {
// Usage: for( long fidxs=fidxs(); fidxs!=0; fidxs=nextFIDX(fidxs) { int fidxs = Long.numberOfTrailingZeros(fidxs); ... }
public static long nextFIDX(long fidxs) { return fidxs & (fidxs-1); }
+ public static void reset() {
+ FIDXS.clear();
+ FIDXS.put(MAIN._sig,1);
+ for( Type t : INTERN.keySet() )
+ if( t instanceof TypeFunPtr tfp && tfp!=MAIN)
+ tfp._name = null;
+ }
+
}