summaryrefslogtreecommitdiff
path: root/chapter15/src
diff options
context:
space:
mode:
Diffstat (limited to 'chapter15/src')
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/Parser.java59
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java7
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/NewNode.java2
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/type/TypeArray.java37
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java50
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java26
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java39
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java9
8 files changed, 150 insertions, 79 deletions
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
index 65bc82ea..b3b1f3c5 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
@@ -489,8 +489,13 @@ public class Parser {
// Convert the type name to a type.
Type t0 = TYPES.get(tname);
Type t1 = t0==null ? TypeStruct.BOT : t0; // Null: assume a forward ref type
- //while( match("[]") )
- // t1 = TypeArray.make(t1);
+ while( match("[]") ) {
+ Type t2 = TYPES.get("[]"+tname);
+ if( t2==null ) {
+ TYPES.put("[]"+tname,t1 = TypeStruct.makeAry(TypeInteger.BOT,ALIAS++,t1,ALIAS++));
+ START.addMemProj((TypeStruct)t1, _scope); // Insert memory edges
+ }
+ }
Type t2 = t1 instanceof TypeStruct obj ? TypeMemPtr.make(obj,match("?")) : t1;
// Check no forward ref
@@ -668,12 +673,12 @@ public class Parser {
String typeName = _lexer.matchId();
if( typeName==null ) throw error("Expected a type");
Type t = TYPES.get(typeName);
- //if( match("[") )
- // return require(newArray(t),"]");
+ if( match("[") )
+ return require(newArray(t),"]");
if( t instanceof TypeStruct obj ) {
if( obj._fields==null )
throw error("Unknown struct type '" + typeName + "'");
- return newStruct(obj);
+ return newStruct(obj,con(obj.offset(obj._fields.length)));
}
throw error("Cannot allocate a "+t);
}
@@ -687,11 +692,11 @@ public class Parser {
/**
* Return a NewNode with pre-zeroed memory
*/
- private Node newStruct(TypeStruct obj) {
+ private Node newStruct(TypeStruct obj, Node size) {
Field[] fs = obj._fields;
Node[] ns = new Node[2+fs.length];
ns[0] = ctrl();
- ns[1] = con(obj.offsets()[fs.length]);
+ ns[1] = size;
for( int i = 0; i < fs.length; i++ )
ns[i+2] = memAlias(fs[i]._alias);
Node nnn = new NewNode(TypeMemPtr.make(obj), ns).peephole();
@@ -701,10 +706,15 @@ public class Parser {
}
private Node newArray(Type t) {
- Node len = parseExpression();
- //TypeArray tary = TypeArray.make(t,TypeInteger.BOT);
- //return new NewArrayNode(tary,ctrl(),len).peephole();
- throw Utils.TODO();
+ Node len = parseExpression().keep();
+ TypeStruct ary = (TypeStruct)TYPES.get("[]"+t.str());
+ int base = ary.aryBase ();
+ int scale= ary.aryScale();
+ Node size = new AddNode(con(base),new ShlNode(len,con(scale)).peephole()).peephole();
+ Node ptr = newStruct(ary,size);
+ int alias = ary._fields[0]._alias; // Length alias
+ memAlias(alias,new StoreNode("#",alias,TypeInteger.BOT,memAlias(alias),ptr,con( ary.offset(0) ), len.unkeep(), true ).peephole());
+ return ptr;
}
// We set up memory aliases by inserting special vars in the scope these
// variables are prefixed by $ so they cannot be referenced in Simple code.
@@ -720,13 +730,16 @@ public class Parser {
*
* <pre>
* expr ('.' IDENTIFIER)* [ = expr ]
+ * expr #
* expr ('[' expr ']')* = [ = expr ]
* </pre>
*/
private Node parsePostfix(Node expr) {
- String name = "[]";
- if( match(".") ) name = requireId().intern();
- else if( !match("[") ) return expr; // No postfix
+ String name = null;
+ if( match(".") ) name = requireId().intern();
+ else if( match("#") ) name = "#";
+ else if( match("[") ) name = "[]";
+ else return expr; // No postfix
// Sanity check expr for being a reference
if( !(expr._type instanceof TypeMemPtr ptr) )
@@ -736,14 +749,20 @@ public class Parser {
// Sanity check field name for existing
TypeStruct base = (TypeStruct)TYPES.get(ptr._obj._name);
if( base == null ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
- int idx = base.find(name);
- if( idx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+ int fidx = base.find(name);
+ if( fidx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
// Get field type and layout offset
- Field f = base._fields[idx];
- Node off = con(base.offsets()[idx]);
- if( name.equals("[]") )
- throw Utils.TODO(); // Array index math
+ Field f = base._fields[fidx];
+ Node off;
+ if( name.equals("[]") ) {
+ // Array index math
+ Node idx = require(parseExpression(),"]");
+ off = new AddNode(con(base.aryBase()),new ShlNode(idx,con(base.aryScale())).peephole()).peephole();
+ } else {
+ // Hardwired field offset
+ off = con(base.offset(fidx));
+ }
if( match("=") ) {
// Disambiguate "obj.fld==x" boolean test from "obj.fld=x" field assignment
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 0567db6e..3048e2d9 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -47,7 +47,7 @@ public class LoadNode extends MemOpNode {
// Simple Load-after-Store on same address.
if( mem() instanceof StoreNode st &&
- ptr() == st.ptr() ) { // Must check same object
+ ptr() == st.ptr() && off() == st.off() ) { // Must check same object
assert _name.equals(st._name); // Equiv class aliasing is perfect
return st.val();
}
@@ -64,7 +64,8 @@ public class LoadNode extends MemOpNode {
// if( pred ) ptr.x = e0; val = pred ? e0
// else ptr.x = e1; : e1;
// val = ptr.x; ptr.x = val;
- if( mem() instanceof PhiNode memphi && memphi.region()._type == Type.CONTROL && memphi.nIns()== 3 ) {
+ if( mem() instanceof PhiNode memphi && memphi.region()._type == Type.CONTROL && memphi.nIns()== 3 &&
+ off() instanceof ConstantNode ) {
// Profit on RHS/Loop backedge
if( profit(memphi,2) ||
// Else must not be a loop to count profit on LHS.
@@ -83,7 +84,7 @@ public class LoadNode extends MemOpNode {
Node px = phi.in(idx);
if( px==null ) return false;
if( px._type instanceof TypeMem mem && mem._t.isHighOrConst() ) { px.addDep(this); return true; }
- if( px instanceof StoreNode st1 && ptr()==st1.ptr() ) { px.addDep(this); return true; }
+ if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) { px.addDep(this); return true; }
return false;
}
}
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/node/NewNode.java b/chapter15/src/main/java/com/seaofnodes/simple/node/NewNode.java
index f1e6e84b..75db67fd 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -11,7 +11,7 @@ import java.util.BitSet;
*/
public class NewNode extends Node implements MultiNode {
- TypeMemPtr _ptr;
+ public TypeMemPtr _ptr;
public NewNode(TypeMemPtr ptr, Node... nodes) {
super(nodes);
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/type/TypeArray.java b/chapter15/src/main/java/com/seaofnodes/simple/type/TypeArray.java
deleted file mode 100644
index 8bfbff30..00000000
--- a/chapter15/src/main/java/com/seaofnodes/simple/type/TypeArray.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package com.seaofnodes.simple.type;
-
-import com.seaofnodes.simple.Utils;
-import java.util.ArrayList;
-
-/**
- * Represents an array type
- */
-public class TypeArray extends TypeStruct {
-
- // An Array has a type and a size; the size is any integer type.
- // The type can be any scalar type, so not e.g. TypeMem.
-
- private TypeArray(TypeInteger len, int lenAlias, Type body, int bodyAlias) {
- super(TARRAY, "array", new Field[]{Field.make("#",lenAlias,len), Field.make("[]",bodyAlias,body)});
- }
-
- // All fields directly listed
- public static TypeArray make(TypeInteger len, int lenAlias, Type body, int bodyAlias) { return new TypeArray(len,lenAlias,body,bodyAlias).intern(); }
- public static TypeArray make(int lenAlias, Type body, int bodyAlias) { return make(TypeInteger.BOT,lenAlias,body,bodyAlias); }
-
- //public static final TypeArray BOT = make(Type.BOTTOM,TypeInteger.BOT);
- //public static final TypeArray FLTS = make(TypeFloat.BOT,TypeInteger.U32);
- //
- //public static void gather(ArrayList<Type> ts) { ts.add(BOT); ts.add(FLTS); }
-
- //@Override
- //public StringBuilder print(StringBuilder sb) {
- // _type.print(sb).append("[");
- // _len .print(sb).append("]");
- // return sb;
- //}
- //
- //@Override public String str() {
- // return _type.str() + "[" + _len.str() + "]";
- //}
-}
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index e7794e14..9e38e025 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -24,26 +24,37 @@ public class TypeStruct extends Type {
public final String _name;
public final Field[] _fields;
- TypeStruct(byte t, String name, Field[] fields) {
- super(t);
+ TypeStruct(String name, Field[] fields) {
+ super(TSTRUCT);
_name = name;
_fields = fields;
}
// All fields directly listed
- public static TypeStruct make(String name, Field[] fields) { return new TypeStruct(TSTRUCT, name, fields).intern(); }
+ public static TypeStruct make(String name, Field... fields) { return new TypeStruct(name, fields).intern(); }
public static final TypeStruct TOP = make("$TOP",new Field[0]);
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, null); }
+ public static TypeStruct make(String name) { return make(name, (Field[])null); }
+
+ // Array
+ public static TypeStruct makeAry(TypeInteger len, int lenAlias, Type body, int bodyAlias) {
+ String name = "[]" + body.str();
+ return make("[]" + body.str(),
+ Field.make("#" , lenAlias,len ),
+ Field.make("[]",bodyAlias,body));
+ }
// A pair of self-cyclic types
private static final TypeStruct S1F = make("S1");
private static final TypeStruct S2F = make("S2");
public static final TypeStruct S1 = make("S1", new Field[]{ Field.make("a", -1, TypeInteger.BOT), Field.make("s2",-2,TypeMemPtr.make(S2F,false)) });
private static final TypeStruct S2 = make("S2", new Field[]{ Field.make("b", -3, TypeFloat .BOT), Field.make("s1",-4,TypeMemPtr.make(S1F,false)) });
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); }
+
+ private static final TypeStruct ARY = makeAry(TypeInteger.BOT,-1,TypeFloat.BOT,-2);
+
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); ts.add(ARY); }
public int find(String fname) {
for( int i=0; i<_fields.length; i++ )
@@ -150,11 +161,26 @@ public class TypeStruct extends Type {
@Override public String str() { return _name; }
+ public boolean isAry() { return _fields.length==2 && _fields[1]._fname.equals("[]"); }
+
+ public int aryBase() {
+ assert isAry();
+ if( _offs==null ) _offs = offsets();
+ return _offs[1];
+ }
+ public int aryScale() {
+ assert isAry();
+ return _fields[1]._type.log_size();
+ }
+
+
// Field offsets as packed byte offsets
private int[] _offs; // Lazily computed and not part of the type semantics
- public int[] offsets() { // Field byte offsets
- if( _offs != null ) return _offs;
-
+ public int offset(int idx) {
+ if( _offs==null ) _offs = offsets();
+ return _offs[idx];
+ }
+ private int[] offsets() { // Field byte offsets
// Compute a layout for a collection of fields
assert _fields != null; // No forward refs
// Compute a layout
@@ -166,7 +192,7 @@ public class TypeStruct extends Type {
int[] offs = new int[4];
for( int i=3; i>=0; i-- ) {
offs[i] = off;
- off += cnts[i]<<(i+3);
+ off += cnts[i]<<i;
}
// Assign offsets to all fields.
// Really a hidden radix sort.
@@ -174,10 +200,10 @@ public class TypeStruct extends Type {
for( Field f : _fields ) {
int log = f._type.log_size();
_offs[idx++] = offs[log]; // Field offset
- offs[log] += 1<<(log+3); // Next field offset at same alignment
- cnts[log]--; // Count down, should be all zero at end
+ offs[log] += 1<<log; // Next field offset at same alignment
+ cnts[log]--; // Count down, should be all zero at end
}
- _offs[_fields.length] = (off+63)& ~63; // Round out to max alignment
+ _offs[_fields.length] = (off+7)& ~7; // Round out to max alignment
return _offs;
}
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index f884f18d..15465677 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -21,7 +21,7 @@ return 3.14;
assertEquals(3.14, Evaluator.evaluate(stop, 0));
}
- @Ignore @Test
+ @Test
public void testBasic() {
Parser parser = new Parser(
"""
@@ -34,6 +34,30 @@ return is[1];
}
@Test
+ public void testRollingSum() {
+ Parser parser = new Parser(
+"""
+int[] ary = new int[arg];
+// Fill [0,1,2,3,4,...]
+int i=0;
+while( i < ary# ) {
+ ary[i] = i;
+ i = i+1;
+}
+// Fill [0,1,3,6,10,...]
+i=0;
+while( i < ary# - 1 ) {
+ ary[i+1] = ary[i+1] + ary[i];
+ i = i+1;
+}
+return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
+""");
+ StopNode stop = parser.parse(false).iterate(true);
+ assertEquals("return (.[]+(.[]*1000));", stop.toString());
+ assertEquals(1006L, Evaluator.evaluate(stop, 4));
+ }
+
+ @Test
public void testBad0() {
Parser parser = new Parser(
"""
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
index b897af7d..40d73b19 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -85,8 +85,21 @@ public class Evaluator {
private Object alloc(NewNode alloc) {
TypeTuple tt = alloc.compute();
TypeStruct type = ((TypeMemPtr)tt._types[1])._obj;
+ Object[] body=null;
+ if( type.isAry() ) {
+ long sz = (Long)val(alloc.in(1));
+ int base = alloc._ptr._obj.aryBase();
+ int scale = alloc._ptr._obj.aryScale();
+ long n = (sz-base)>>scale;
+ body = new Object[(int)n+1]; // Array body
+ body[0] = n; // Array length
+ } else {
+ body = new Object[type._fields.length];
+ }
Object[] mems = new Object[type._fields.length+2];
- mems[1] = new Obj(type,new Object[type._fields.length]);
+ // mems[0] is control
+ mems[1] = new Obj(type,body); // the ref
+ // mems[2+...] are memory aliases
return mems;
}
@@ -94,7 +107,17 @@ public class Evaluator {
var from = (Obj)val(load.ptr());
var off = val(load.off());
var idx = getFieldIndex(from.struct, load, (Long)off);
- return from.fields[idx];
+ if( idx==1 && from.struct.isAry() ) {
+ long len = (Long)from.fields[0];
+ int base = from.struct.aryBase();
+ int scale = from.struct.aryScale();
+ long i = ((Long)off - base)>>scale;
+ if( i < 0 || i >= len )
+ throw new RuntimeException("Array index out of bounds " + off + " <= " + len);
+ return from.fields[(int)i+1];
+
+ } else
+ return from.fields[idx];
}
private Object store(StoreNode store) {
@@ -102,7 +125,17 @@ public class Evaluator {
var off = val(store.off());
var val = val(store.val());
var idx = getFieldIndex(to.struct, store, (Long)off);
- to.fields[idx] = val;
+ if( idx==1 && to.struct.isAry() ) {
+ long len = (Long)to.fields[0];
+ int base = to.struct.aryBase();
+ int scale = to.struct.aryScale();
+ long i = ((Long)off - base)>>scale;
+ if( i < 0 || i >= len )
+ throw new RuntimeException("Array index out of bounds " + off + " <= " + len);
+ to.fields[(int)i+1] = val;
+
+ } else
+ to.fields[idx] = val;
return null;
}
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
index fd8fdb8d..59c15d3f 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Scheduler.java
@@ -155,7 +155,10 @@ public class Scheduler {
* @return true if all placed inputs are before this node.
*/
private boolean isValid(NodeData data) {
- return data.node._inputs.stream().map(i->i==null?null:d(i)).map(d->d==null||d.users>0?null:d.block).allMatch(d->d==null||dom(d, data.block)==d);
+ return data.node._inputs.stream()
+ .map(i->i==null?null:d(i))
+ .map(d->d==null||d.users>0?null:d.block)
+ .allMatch(d->d==null||dom(d, data.block)==d);
}
/**
@@ -399,7 +402,9 @@ public class Scheduler {
}
}
if (!(node instanceof ReturnNode))
- for (Node n:node._outputs) if (n!=null && n instanceof CFGNode && isCFGNodeReady(n) && d(n).block == null) queue.push(d(n));
+ for (Node n : node._outputs)
+ if(n instanceof CFGNode && isCFGNodeReady(n) && d(n).block == null)
+ queue.push(d(n));
var b = block;
for(var in:data.node._inputs) od(in).ifPresent(d->update(d, b));
}