summaryrefslogtreecommitdiff
path: root/chapter17/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2024-11-07 14:17:20 -0800
committerCliff Click <cliffc@acm.org>2024-11-23 09:03:20 -0800
commit59a06668f3975c90459409e25e884b4b594cbe3b (patch)
treee53eac8d61685807275d2f861c7df672e3bc1032 /chapter17/src
parent59fb2c02e08b9bf577f2dedfabd290cb6e00e016 (diff)
downloadSimple-59a06668f3975c90459409e25e884b4b594cbe3b.tar.gz
Simple-59a06668f3975c90459409e25e884b4b594cbe3b.zip
Allow post ++, --
Fix eval auto-string result. Fix tests hacked because of this. Turn on BrainFuck in ch15,16,17, also disabled because of this. Use new features in BrainFuck. Fix bug not lifting forward refs; add a toString to Vars. Fix some missing addDeps. Remove large complex not-productive peep. Upcast after "while" tests. Improves a bunch of tests. Remove dead getBlockStart.
Diffstat (limited to 'chapter17/src')
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/Parser.java37
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/CProjNode.java4
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/IfNode.java2
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/LoadNode.java11
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/MemOpNode.java5
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/Node.java6
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/RegionNode.java58
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/ReturnNode.java2
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java20
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/ScopeNode.java18
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/ShlNode.java2
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/StartNode.java2
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/StopNode.java2
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/node/StoreNode.java12
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/type/Type.java3
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java2
-rw-r--r--chapter17/src/main/java/com/seaofnodes/simple/type/TypeStruct.java13
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java27
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java6
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java8
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java10
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java4
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java6
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java4
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java2
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java2
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java55
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java2
-rw-r--r--chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
29 files changed, 174 insertions, 153 deletions
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/Parser.java b/chapter17/src/main/java/com/seaofnodes/simple/Parser.java
index d2a1a9ae..9710be30 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/Parser.java
@@ -96,7 +96,7 @@ public class Parser {
// Mapping from a type name to a Type. The string name matches
// `type.str()` call. No TypeMemPtrs are in here, because Simple does not
// have C-style '*ptr' references.
- public final HashMap<String, Type> TYPES;
+ public static HashMap<String, Type> TYPES;
// Mapping from a type name to the constructor for a Type.
public final HashMap<String, StructNode> INITS;
@@ -267,6 +267,7 @@ public class Parser {
// the loop predicate. Note that body Scope is still our current scope.
ctrl(ifF);
_xScopes.push(_breakScope = _scope.dup());
+ _breakScope.upcast(ifF,pred,true); // Up-cast predicate
// No continues yet
_continueScope = null;
@@ -274,6 +275,7 @@ public class Parser {
// Parse the true side, which corresponds to loop body
// Our current scope is the body Scope
ctrl(ifT.unkeep()); // set ctrl token to ifTrue projection
+ _scope.upcast(ifT,pred,false); // Up-cast predicate
parseStatement(); // Parse loop body
// Merge the loop bottom into other continue statements
@@ -302,7 +304,8 @@ public class Parser {
// the scope is the exit scope after the exit test.
_xScopes.pop();
_xScopes.push(exit);
- return _scope = exit;
+ _scope = exit;
+ return _scope;
}
private ScopeNode jumpTo(ScopeNode toScope) {
@@ -438,7 +441,7 @@ public class Parser {
// need to check the final bit.
if( _scope.in(def._idx)._type!=Type.TOP && def._final )
throw error("Cannot reassign final '"+name+"'");
- t = def._type; // Declared field type
+ t = def.type(); // Declared field type
}
// Auto-widen int to float
@@ -527,7 +530,7 @@ public class Parser {
for( int i=lexlen; i<varlen; i++ ) {
s.addDef(_scope.in(i));
ScopeMinNode.Var v = _scope._vars.at(i);
- fs[i-lexlen] = Field.make(v._name,v._type,ALIAS++,v._final);
+ fs[i-lexlen] = Field.make(v._name,v.type(),ALIAS++,v._final);
}
TypeStruct ts = s._ts = TypeStruct.make(typeName, fs);
TYPES.put(typeName, TypeMemPtr.make(ts));
@@ -550,7 +553,7 @@ public class Parser {
if( tname==null ) return null;
// Convert the type name to a type.
Type t0 = TYPES.get(tname);
- Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.make(tname)) : t0; // Null: assume a forward ref type
+ Type t1 = t0 == null ? TypeMemPtr.make(TypeStruct.makeFRef(tname)) : t0; // Null: assume a forward ref type
// Nest arrays and '?' as needed
while( true ) {
assert !(t1 instanceof TypeStruct);
@@ -744,23 +747,33 @@ public class Parser {
* Parse a primary expression:
*
* <pre>
- * primaryExpr : integerLiteral | Identifier | true | false | null | new Type | '(' expression ')'
+ * primaryExpr : integerLiteral | true | false | null | new Type | '(' expression ')' | Id['++','--']
* </pre>
* @return a primary {@link Node}, never {@code null}
*/
private Node parsePrimary() {
if( _lexer.isNumber(_lexer.peek()) ) return parseLiteral();
- if( match("(") ) return require(parseExpression(), ")");
if( matchx("true" ) ) return con(1);
if( matchx("false") ) return ZERO;
if( matchx("null" ) ) return con(TypeMemPtr.NULLPTR);
+ if( match("(") ) return require(parseExpression(), ")");
if( matchx("new" ) ) return alloc();
// Expect an identifier now
- String name = _lexer.matchId();
- if( name == null) throw errorSyntax("an identifier or expression");
- ScopeMinNode.Var n = _scope.lookup(name);
- if( n!=null ) return _scope.in(n);
- throw error("Undefined name '" + name + "'");
+ ScopeMinNode.Var n = requireLookupId("an identifier or expression");
+ Node rvalue = _scope.in(n).keep();
+ if( matchx("++") ) _scope.update(n,new AddNode(rvalue,con( 1)).peephole());
+ if( matchx("--") ) _scope.update(n,new AddNode(rvalue,con(-1)).peephole());
+ return rvalue.unkeep();
+ }
+
+ ScopeMinNode.Var requireLookupId(String msg) {
+ String id = _lexer.matchId();
+ if( id == null || KEYWORDS.contains(id) )
+ throw errorSyntax(msg);
+ ScopeMinNode.Var n = _scope.lookup(id);
+ if( n==null )
+ throw error("Undefined name '" + id + "'");
+ return n;
}
/**
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/CProjNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/CProjNode.java
index 3d6fd1fa..d2f1bbe1 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/CProjNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/CProjNode.java
@@ -52,10 +52,6 @@ public class CProjNode extends CFGNode {
}
@Override
- public Node getBlockStart() {
- return ctrl() instanceof IfNode ? this : ctrl().getBlockStart();
- }
- @Override
boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; }
@Override
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/IfNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/IfNode.java
index 3cce89ab..41de4c82 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/IfNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/IfNode.java
@@ -66,6 +66,4 @@ public class IfNode extends CFGNode implements MultiNode {
unreach.add((CProjNode)proj);
super._walkUnreach(visit,unreach);
}
-
- @Override public Node getBlockStart() { return ctrl().getBlockStart(); }
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index d15d4f83..2c0afda2 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -33,10 +33,13 @@ public class LoadNode extends MemOpNode {
@Override
public Type compute() {
- if( mem()._type instanceof TypeMem mem &&
- // No constant folding if ptr might null-check
- _declaredType != mem._t && err()==null ) {
- return _declaredType.join(mem._t);
+ if( mem()._type instanceof TypeMem mem ) {
+ // Update declared forward ref to the actual
+ if( _declaredType.isFRef() && mem._t instanceof TypeMemPtr tmp && !tmp.isFRef() )
+ _declaredType = tmp;
+ // No lifting if ptr might null-check
+ if( err()==null )
+ return _declaredType.join(mem._t);
}
return _declaredType;
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index c7a1efec..82aab8ac 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -1,6 +1,5 @@
package com.seaofnodes.simple.node;
-import com.seaofnodes.simple.type.Field;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.type.TypeMemPtr;
@@ -11,7 +10,9 @@ public abstract class MemOpNode extends Node {
public final String _name;
public final int _alias;
- public final Type _declaredType;
+ // Declared type; not final because it might be a forward-reference
+ // which will be lazily improved when the reference is declared.
+ public Type _declaredType;
public MemOpNode(String name, int alias, Type glb, Node mem, Node ptr, Node off) {
super(null, mem, ptr, off);
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/Node.java b/chapter17/src/main/java/com/seaofnodes/simple/node/Node.java
index 462a403c..c67fb910 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/Node.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/Node.java
@@ -278,6 +278,7 @@ public abstract class Node implements OutNode {
int idx = n._inputs.find(this);
n._inputs.set(idx,nnn);
nnn.addUse(n);
+ IterPeeps.addAll(n._outputs);
}
kill();
}
@@ -419,7 +420,7 @@ public abstract class Node implements OutNode {
* And more folding:
* <pre> ary + ((idx<<2) + 16)</pre>
* And during code-gen:
- * <pre> MOV4 Rary,Ridx,16 // or some such hardware-specific notation </pre>
+ * <pre> MOV4 Rary,[Ridx<<2 +16] // or some such hardware-specific notation </pre>
* <p>
* {@link #idealize} has a very specific calling convention:
* <ul>
@@ -542,9 +543,6 @@ public abstract class Node implements OutNode {
/** 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; these are data nodes whose input#0 is not allowed to change */
public boolean isPinned() { return false; }
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/RegionNode.java
index 941de76b..a55bb479 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/RegionNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/RegionNode.java
@@ -72,65 +72,9 @@ public class RegionNode extends CFGNode {
p1.in(0) instanceof IfNode iff )
return iff.ctrl();
-
- // Look for direct uses of a guarded expression, and replace with the guard result.
- if( !(idom() instanceof StartNode) && // Dead or dying
- !(this instanceof LoopNode) ) {
- VISIT.clear();
- boolean progress = false;
- outer:
- for( int i=1; i<nIns(); i++ ) {
- CFGNode idom = cfg(i);
- while( !(idom instanceof CProjNode cproj) ) {
- idom = idom.idom();
- if( idom==null || idom.idepth() < idom().idepth() )
- continue outer;
- }
- if( cproj.cfg0() instanceof IfNode iff && cproj.compute()==Type.CONTROL ) {
- VISIT.set(iff.pred()._nid);
- for( Node out : _outputs )
- if( out instanceof PhiNode phi )
- progress |= walk(iff,cproj,phi.in(i));
- }
- }
- if( progress )
- return this;
- }
-
return null;
}
- // A walk that can be cutoff early
- private static final BitSet VISIT = new BitSet();
- private boolean walk( IfNode iff, CProjNode cproj, Node n ) {
- if( VISIT.get(n._nid) ) return false;
- VISIT.set(n._nid);
- if( n instanceof CFGNode ) return false;
- if( n.in(0)!=null && n.cfg0().idepth() < iff.idepth() )
- return false;
- boolean progress = false;
- Node pred = iff.pred();
- if( pred._type.isHighOrConst() ) return false; // This will collapse the IF already
- for( int i=1; i<n.nIns(); i++ ) {
- // Using a tested value
- if( n.in(i) == pred && !(n instanceof CastNode) ) {
- // Checking pred for T/F
- Type t = pred._type;
- Type tcast = cproj._idx==0 ? t.nonZero() : t.makeInit();
- if( t!=tcast ) { // Progress to cast
- Node cast = new CastNode( tcast, cproj, pred ).peephole();
- n.setDef( i, cast );
- IterPeeps.add(n);
- progress = true;
- } else pred.addDep( this );
- }
- if( n.in(i)!=null )
- progress |= walk(iff,cproj,n.in(i));
- }
- return progress;
- }
-
-
private int findDeadInput() {
for( int i=1; i<nIns(); i++ )
if( in(i)._type==Type.XCONTROL )
@@ -180,6 +124,4 @@ public class RegionNode extends CFGNode {
@Override boolean eq( Node n ) {
return !inProgress();
}
-
- @Override public Node getBlockStart() { return this; }
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
index de4eba52..b586a44f 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/ReturnNode.java
@@ -47,6 +47,4 @@ public class ReturnNode extends CFGNode {
return ctrl();
return null;
}
-
- @Override public Node getBlockStart() { return ctrl().getBlockStart(); }
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java
index e4874e15..c51349fa 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeMinNode.java
@@ -14,16 +14,28 @@ public class ScopeMinNode extends Node {
/** The tracked fields are now complex enough to deserve a array-of-structs layout
*/
public static class Var {
- public final int _idx;
- public final String _name;
- public final Type _type;
- public final boolean _final;
+ public final int _idx; // index in containing scope
+ public final String _name; // Declared name
+ private Type _type; // Declared type
+ public final boolean _final; // Final field
public Var(int idx, String name, Type type, boolean xfinal) {
_idx = idx;
_name = name;
_type = type;
_final = xfinal;
}
+ public Type type() {
+ if( !_type.isFRef() ) return _type;
+ Type def = Parser.TYPES.get(((TypeMemPtr)_type)._obj._name);
+ return (_type=_type.meet(def));
+ }
+ public Type glb() {
+ Type t = type();
+ return t instanceof TypeMemPtr ? t : t.glb();
+ }
+ @Override public String toString() {
+ return _type.toString()+(_final ? " !": " ")+_name;
+ }
}
public ScopeMinNode() { _type = TypeMem.BOT; }
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
index 11e54d82..f7446c51 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/ScopeNode.java
@@ -43,7 +43,7 @@ public class ScopeNode extends ScopeMinNode {
for( int i=0; i<nIns(); i++ ) {
if( j < _lexSize._len && i == _lexSize.at(j) ) { sb.append("| "); j++; }
Var v = _vars.get(i);
- v._type.print(sb);
+ v.type().print(sb);
sb.append(" ");
if( v._final ) sb.append("!");
sb.append(v._name);
@@ -125,7 +125,7 @@ public class ScopeNode extends ScopeMinNode {
public Var lookup( String name ) {
int idx = find(name);
// -1 is missed in all scopes, not found
- return idx == -1 ? null : _update(_vars.at(idx),null);
+ return idx == -1 ? null : update(_vars.at(idx),null);
}
/**
@@ -137,10 +137,10 @@ public class ScopeNode extends ScopeMinNode {
public void update( String name, Node n ) {
int idx = find(name);
assert idx>=0;
- _update(_vars.at(idx),n);
+ update(_vars.at(idx),n);
}
- Var _update( ScopeNode.Var v, Node st ) {
+ public Var update( ScopeNode.Var v, Node st ) {
Node old = in(v._idx);
if( old instanceof ScopeNode loop ) {
// Lazy Phi!
@@ -151,7 +151,7 @@ public class ScopeNode extends ScopeMinNode {
// Set real Phi in the loop head
// The phi takes its one input (no backedge yet) from a recursive
// lookup, which might have insert a Phi in every loop nest.
- : loop.setDef(v._idx,new PhiNode(v._name, v._type instanceof TypeMemPtr ? v._type : v._type.glb(), loop.ctrl(), loop.in(loop._update(v,null)._idx),null).peephole());
+ : loop.setDef(v._idx,new PhiNode(v._name, v.glb(), loop.ctrl(), loop.in(loop.update(v,null)._idx),null).peephole());
setDef(v._idx,old);
}
if( st!=null ) setDef(v._idx,st); // Set new value
@@ -223,9 +223,9 @@ public class ScopeNode extends ScopeMinNode {
// If we are in lazy phi mode we need to a lookup
// by name as it will trigger a phi creation
Var v = _vars.at(i);
- Node lhs = this.in(this._update(v,null));
- Node rhs = that.in(that._update(v,null));
- setDef(i, new PhiNode(v._name, v._type, r, lhs, rhs).peephole());
+ Node lhs = this.in(this.update(v,null));
+ Node rhs = that.in(that.update(v,null));
+ setDef(i, new PhiNode(v._name, v.type(), r, lhs, rhs).peephole());
}
}
@@ -268,6 +268,8 @@ public class ScopeNode extends ScopeMinNode {
// This Scope looks for direct variable uses, or certain simple
// combinations, and replaces the variable with the upcast variant.
public Node upcast( Node ctrl, Node pred, boolean invert ) {
+ if( ctrl._type == Type.XCONTROL || pred.isDead() )
+ return null; // Dead, do not do anything
// Invert the If conditional
if( invert )
pred = pred instanceof NotNode not ? not.in(1) : IterPeeps.add(new NotNode(pred).peephole());
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/ShlNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/ShlNode.java
index b67d985a..6fd951e4 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/ShlNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/ShlNode.java
@@ -44,7 +44,7 @@ public class ShlNode extends Node {
if( (shl.value()&63)==0 )
return lhs;
// (x + c) << i => (x << i) + (c << i)
- if( lhs instanceof AddNode add && add.in(2)._type instanceof TypeInteger c && c.isConstant() && c!=TypeInteger.ZERO ) {
+ if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof TypeInteger c && c.isConstant() && c!=TypeInteger.ZERO ) {
long sum = c.value() << shl.value();
if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE )
return new AddNode( new ShlNode(add.in(1),rhs).peephole(), Parser.con(sum) );
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/StartNode.java
index c03a4b06..d82030f8 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/StartNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/StartNode.java
@@ -44,6 +44,4 @@ public class StartNode extends CFGNode implements MultiNode {
@Override void _walkUnreach( BitSet visit, HashSet<CFGNode> unreach ) { }
@Override public int loopDepth() { return (_loopDepth=1); }
-
- @Override public Node getBlockStart() { return this; }
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/StopNode.java
index 0eacc1f4..5a02190a 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -84,6 +84,4 @@ public class StopNode extends CFGNode {
GlobalCodeMotion.buildCFG(this);
return this;
}
-
- @Override public Node getBlockStart() { return this; }
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter17/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 7b1bddd5..8e3e6693 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -42,9 +42,15 @@ public class StoreNode extends MemOpNode {
public Type compute() {
Type val = val()._type;
TypeMem mem = (TypeMem)mem()._type; // Invariant
- Type t = mem._alias==_alias
- ? val.meet(mem._t) // Meet into existing memory
- : Type.BOTTOM;
+ Type t = Type.BOTTOM; // No idea on field contents
+ // Same alias, lift val to the declared type and then meet into other fields
+ if( mem._alias == _alias ) {
+ // Update declared forward ref to the actual
+ if( _declaredType.isFRef() && val instanceof TypeMemPtr tmp && !tmp.isFRef() )
+ _declaredType = tmp;
+ val = val.join(_declaredType);
+ t = val.meet(mem._t);
+ }
return TypeMem.make(_alias,t);
}
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter17/src/main/java/com/seaofnodes/simple/type/Type.java
index 989eff9e..7467be79 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -179,6 +179,9 @@ public class Type {
*/
public Type glb() { return _type==TCTRL ? XCONTROL : BOTTOM; }
+ // Is forward-reference
+ public boolean isFRef() { return false; }
+
// ----------------------------------------------------------
// Size in bits to hold an instance of this type.
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter17/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index ec059b8f..449bf89e 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -57,6 +57,8 @@ public class TypeMemPtr extends Type {
@Override
public TypeMemPtr glb() { return make(_obj.glb(),true); }
+ // Is forward-reference
+ @Override public boolean isFRef() { return _obj.isFRef(); }
@Override public TypeMemPtr makeInit() { return NULLPTR; }
@Override public Type nonZero() { return VOIDPTR; }
diff --git a/chapter17/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter17/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 4e4d1305..39011ce2 100644
--- a/chapter17/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter17/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -36,7 +36,7 @@ public class TypeStruct extends Type {
public static final TypeStruct BOT = make("$BOT",new Field[0]);
public static final TypeStruct TEST = make("test",new Field[]{Field.TEST});
// Forward-ref version
- public static TypeStruct make(String name) { return make(name, (Field[])null); }
+ public static TypeStruct makeFRef(String name) { return make(name, (Field[])null); }
// Array
public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) {
@@ -47,8 +47,8 @@ public class TypeStruct extends Type {
}
// A pair of self-cyclic types
- private static final TypeStruct S1F = make("S1");
- private static final TypeStruct S2F = make("S2");
+ private static final TypeStruct S1F = makeFRef("S1");
+ private static final TypeStruct S2F = makeFRef("S2");
public static final TypeStruct S1 = make("S1", new Field[]{ Field.make("a", TypeInteger.BOT, -1, false), Field.make("s2",TypeMemPtr.make(S2F,false),-2, false) });
private static final TypeStruct S2 = make("S2", new Field[]{ Field.make("b", TypeFloat .BOT, -3, false), Field.make("s1",TypeMemPtr.make(S1F,false),-4, false) });
@@ -89,8 +89,8 @@ public class TypeStruct extends Type {
// And the answer is: "don't ask".
if( !_name.equals(that._name) )
return BOT; // It's a struct; that's about all we know
- if( this._fields==null ) return this;
- if( that._fields==null ) return that;
+ if( this._fields==null ) return that;
+ if( that._fields==null ) return this;
if( _fields.length != that._fields.length ) return BOT;
// Just do field meets
Field[] flds = new Field[_fields.length];
@@ -132,6 +132,9 @@ public class TypeStruct extends Type {
return true;
}
+ // Is forward-reference
+ @Override public boolean isFRef() { return _fields==null; }
+
@Override
boolean eq(Type t) {
TypeStruct ts = (TypeStruct)t; // Invariant
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
index f7c66bda..0a48d2e7 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -12,7 +12,6 @@ import static org.junit.Assert.fail;
public class BrainFuckTest {
- @Ignore
@Test
public void testBrainfuck() {
var program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".getBytes(StandardCharsets.UTF_8);
@@ -32,9 +31,9 @@ u8[] output = new u8[0];
while (pc < program#) {
int command = program[pc];
if (command == 62) {
- d = d + 1;
+ d++;
} else if (command == 60) {
- d = d - 1;
+ d--;
} else if (command == 43) {
data[d] = data[d] + 1;
} else if (command == 45) {
@@ -43,10 +42,8 @@ while (pc < program#) {
u8[] old = output;
output = new u8[output# + 1];
int i = 0;
- while (i < old#) {
- output[i] = old[i];
- i = i + 1;
- }
+ while (i < old#)
+ output[i] = old[i++];
output[i] = data[d];
} else if (command == 44) {
data[d] = 42;
@@ -54,24 +51,24 @@ while (pc < program#) {
if (data[d] == 0) {
int d = 1;
while (d > 0) {
- pc = pc + 1;
+ pc++;
command = program[pc];
- if (command == 91) d = d + 1;
- if (command == 93) d = d - 1;
+ if (command == 91) d++;
+ if (command == 93) d--;
}
}
} else if (command == 93) {
- if (data[d] != 0) {
+ if (data[d]) {
int d = 1;
while (d > 0) {
- pc = pc - 1;
+ pc--;
command = program[pc];
- if (command == 93) d = d + 1;
- if (command == 91) d = d - 1;
+ if (command == 93) d++;
+ if (command == 91) d--;
}
}
}
- pc = pc + 1;
+ pc++;
}
return output;
""");
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index ff1f3251..6308bdea 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -84,7 +84,7 @@ else
b=5;
return b;""", TypeInteger.BOT);
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region38,2,5);", stop.toString());
+ assertEquals("return Phi(Region36,2,5);", stop.toString());
}
@Test
@@ -200,7 +200,7 @@ if( arg ) {
return a+b;
""");
StopNode ret = parser.parse().iterate();
- assertEquals("return Phi(Region35,4,1);", ret.toString());
+ assertEquals("return Phi(Region32,4,1);", ret.toString());
}
@@ -251,7 +251,7 @@ if( arg ) {
return a+b+c;
""");
StopNode ret = parser.parse().iterate();
- assertEquals("return (Phi(Region48,Phi(Region29,2,3),0)+Phi(Region,3,1));", ret.toString());
+ assertEquals("return (Phi(Region45,Phi(Region29,2,3),0)+Phi(Region,3,1));", ret.toString());
}
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java
index b54c24a3..80cb15d4 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter07Test.java
@@ -36,7 +36,7 @@ if(arg){}else{
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region31,1,Phi(Loop18,1,(Phi_a+1)));", stop.toString());
+ assertEquals("return Phi(Region32,1,Phi(Loop18,1,(Phi_a+1)));", stop.toString());
}
@Test
@@ -56,7 +56,7 @@ while(i < arg) {
return sum;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop12,0,Phi(Loop26,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", stop.toString());
+ assertEquals("return Phi(Loop12,0,Phi(Loop27,Phi_sum,(Phi_sum+Phi(Loop,0,(Phi_j+1)))));", stop.toString());
}
@Test
@@ -73,7 +73,7 @@ return b;
""");
Node._disablePeephole = true;
StopNode stop = parser.parse();
- assertEquals("return Phi(Loop12,2,Phi(Region34,Phi_b,4));", stop.toString());
+ assertEquals("return Phi(Loop12,2,Phi(Region35,Phi_b,4));", stop.toString());
assertTrue(stop.ret().ctrl() instanceof CProjNode);
Node._disablePeephole = false;
}
@@ -93,7 +93,7 @@ while(a < 10) {
return b;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop12,2,(Phi(Region34,Phi_b,4)+1));", stop.toString());
+ assertEquals("return Phi(Loop12,2,(Phi(Region35,Phi_b,4)+1));", stop.toString());
assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index c199b78c..5372388b 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -23,7 +23,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region45,Phi(Region32,Phi(Loop10,arg,(Phi_arg+1)),Add),Add);", stop.toString());
+ assertEquals("return Phi(Region46,Phi(Region33,Phi(Loop10,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));
@@ -46,7 +46,7 @@ while(arg < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop11,1,Phi(Region52,Phi_a,(Phi_a+1)));", stop.toString());
+ assertEquals("return Phi(Loop11,1,Phi(Region53,Phi_a,(Phi_a+1)));", stop.toString());
assertTrue(stop.ret().ctrl() instanceof CProjNode);
}
@@ -65,7 +65,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region43,Phi(Loop10,arg,(Phi_arg+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region44,Phi(Loop10,arg,(Phi_arg+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
@@ -81,7 +81,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region32,Phi(Loop10,arg,(Phi_arg+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region33,Phi(Loop10,arg,(Phi_arg+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
@@ -185,7 +185,7 @@ while(arg < 10) {
return a;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Region35,Phi(Loop11,1,(Phi_a+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region36,Phi(Loop11,1,(Phi_a+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java
index b1d9d6e5..04ab8001 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter09Test.java
@@ -115,7 +115,7 @@ while (arg) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop18,arg,(Phi_arg+1));", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
@@ -283,7 +283,7 @@ return v1+v0;
public void testFuzz8() {
Parser parser = new Parser("while(arg) arg = arg - 1; return arg;");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop10,arg,(Phi_arg-1));", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 2a33031d..0bf921f6 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -245,7 +245,7 @@ while(arg) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop16,s0,Phi(Region43,s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop16,s0,Phi(Region45,s0,Phi_ret));", stop.toString());
}
@Test
@@ -263,7 +263,7 @@ while(arg) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop20,s0,Phi(Region44,s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop20,s0,Phi(Region46,s0,Phi_ret));", stop.toString());
}
@@ -280,7 +280,7 @@ while(arg < 10) {
return ret;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop16,s0,Phi(Region41,s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop16,s0,Phi(Region42,s0,Phi_ret));", stop.toString());
}
@Test
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index d6b7bb00..44814d53 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -324,7 +324,7 @@ while( prime <= arg ) {
return primeCount;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("Stop[ return 0; return Phi(Loop23,1,Phi(Region100,Phi_primeCount,Phi(Region93,(Phi_primeCount+1),Phi_primeCount))); ]", stop.toString());
+ assertEquals("Stop[ return 0; return Phi(Loop23,1,Phi(Region102,Phi_primeCount,Phi(Region95,(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
@@ -476,7 +476,7 @@ while(arg) {
return arg;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop20,arg,Phi(Region48,.f,0));", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java
index 7d7bb25a..e72b0c76 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter14Test.java
@@ -59,7 +59,7 @@ while( b ) b = b + 456;// Truncate
return b;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return Phi(Loop11,123,((Phi_b+456)&255));", stop.toString());
+ assertEquals("return 0;", stop.toString());
}
@Test
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index d2d60b34..8161862f 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -50,7 +50,7 @@ return output;
""");
StopNode stop = parser.parse().iterate();
assertEquals("return [u8];", stop.toString());
- assertEquals("Obj<[u8]>{#=1,[]=[1]}", Evaluator.evaluate(stop, 0).toString());
+ assertEquals("\u0001", Evaluator.evaluate(stop, 0).toString());
}
@Test
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java b/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java
index 790f9531..91c275ce 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/Chapter17Test.java
@@ -10,12 +10,63 @@ public class Chapter17Test {
@Test
public void testJig() {
- Parser parser = new Parser(
-"""
+ Parser parser = new Parser("""
return 3.14;
""");
StopNode stop = parser.parse().iterate();
assertEquals("return 3.14;", stop.toString());
assertEquals(3.14, Evaluator.evaluate(stop, 0));
}
+
+ @Test
+ public void testInc0() {
+ Parser parser = new Parser("""
+return arg++;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return arg;", stop.toString());
+ assertEquals(0L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testInc1() {
+ Parser parser = new Parser("""
+return arg+++arg++;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return ((arg*2)+1);", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ }
+
+ @Test
+ public void testInc2() {
+ Parser parser = new Parser("""
+// -(arg--)-(arg--)
+return -arg---arg--;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return (-((arg*2)+-1));", stop.toString());
+ assertEquals(1L, Evaluator.evaluate(stop, 0));
+ }
+
+
+ @Test
+ public void testLinkedList2() {
+ Parser parser = new Parser("""
+struct LLI { LLI? next; int i; };
+LLI? head = null;
+while( arg-- )
+ head = new LLI { next=head; i=arg; };
+int sum=0;
+while( head ) {
+ sum = sum + head.i;
+ head = head.next;
+}
+return sum;
+""");
+ StopNode stop = parser.parse().iterate();
+ assertEquals("return Phi(Loop35,0,(Phi_sum+.i));", stop.toString());
+ assertEquals(45L, Evaluator.evaluate(stop, 10));
+ }
+
}
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java
index 6f7409cd..03b0e31d 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -79,7 +79,7 @@ public class TypeTest {
// Test theoretical properties.
// This is a symmetric complete bounded (ranked) lattice.
- // Also the meet is commutative and associative.
+ // Also, the meet is commutative and associative.
// The lattice has a dual (symmetric), and join is ~(~x meet ~y).
// See https://en.wikipedia.org/wiki/Lattice_(order).
@Test
diff --git a/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index 28896283..c215de00 100644
--- a/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter17/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -100,7 +100,7 @@ public class Evaluator {
}
@Override
public String toString() {
- if (struct._name.equals("u8[]")) {
+ if (struct._name.equals("[u8]")) {
var sb = new StringBuilder();
for (int i=struct._fields.length-1; i<fields.length; i++) {
var v = fields[i];