summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2024-08-05 11:46:17 -0700
committerDibyendu Majumdar <dibyendumajumdar@users.noreply.github.com>2024-08-06 00:50:58 +0100
commit0fb76e75be3a35d6b4741e9ae4378fa04868a840 (patch)
tree4f531ef7ccfd3f032687177c057628bb64ad7c10
parent5dad6618f075aac78023183354f0456e5dcc1d54 (diff)
downloadSimple-0fb76e75be3a35d6b4741e9ae4378fa04868a840.tar.gz
Simple-0fb76e75be3a35d6b4741e9ae4378fa04868a840.zip
Ref types working
GCM uses a breadth-first pass for schedule-late instead of a post-order; the post-order walk depends on StoreNodes being pinned, which is true in C2 but not Simple. Move GCM call out of IterPeeps. Struct field init is "makeInit()" not integer zero. Rename Type._print to print. Store,Load prints include field name.
-rw-r--r--chapter11/src/test/java/com/seaofnodes/simple/Chapter10Test.java5
-rw-r--r--chapter13/README.md25
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java88
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/IterPeeps.java7
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/Parser.java44
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/node/CastNode.java3
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/node/ConstantNode.java2
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/node/LoadNode.java6
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/node/StopNode.java14
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/node/StoreNode.java4
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/Field.java4
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/Type.java24
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/TypeFloat.java2
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/TypeInteger.java2
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/TypeMem.java9
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java7
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/TypeStruct.java49
-rw-r--r--chapter13/src/main/java/com/seaofnodes/simple/type/TypeTuple.java11
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/Chapter05Test.java6
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/Chapter06Test.java6
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/Chapter08Test.java6
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/Chapter10Test.java19
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/Chapter11Test.java6
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/Chapter12Test.java45
-rw-r--r--chapter13/src/test/java/com/seaofnodes/simple/TypeTest.java28
25 files changed, 273 insertions, 149 deletions
diff --git a/chapter11/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter11/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 06065702..33f561e9 100644
--- a/chapter11/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter11/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -1,12 +1,7 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StopNode;
-import com.seaofnodes.simple.type.*;
import org.junit.Test;
-
-import java.util.Arrays;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
diff --git a/chapter13/README.md b/chapter13/README.md
index e381f1dc..49737663 100644
--- a/chapter13/README.md
+++ b/chapter13/README.md
@@ -1,9 +1,9 @@
-# Chapter 12: Floats, Arrays, Refs
-
-## Floats
+# Chapter 12: Floats, References and Arrays
In this chapter we add a series of new types and some modest syntax.
+## Floats
+
Floating point values start with the `flt` type and compute IEEE754 64-bit arithmetic.
Integer expressions auto-widen to floats when involved in a float expression.
@@ -45,3 +45,22 @@ Within the Type Lattice, we now add the following domain:
with a TOP, BOT and constants
+## References
+
+Structs can reference other structs; references can be mutually recursive.
+Any reference field can allow null or not, same as a normal variable.
+
+```java
+struct Person {
+ String name; // No string type, yet
+ int age;
+ FamilyTree tree?; // A family tree, or not
+}
+struct FamilyTree {
+ Person father?;
+ Person mother?;
+ Person []kids?;
+}
+```
+
+Normal field syntax works: `person.tree.father = new Person;` or `person.tree.father.name = "Dad";`.
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 4e0a31d5..8a96e69c 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple;
+import com.seaofnodes.simple.IterPeeps.WorkList;
import com.seaofnodes.simple.node.*;
import com.seaofnodes.simple.type.*;
import java.util.*;
@@ -12,14 +13,14 @@ public abstract class GlobalCodeMotion {
public static void buildCFG( StopNode stop ) {
schedEarly();
Parser.SCHEDULED = true;
- schedLate(Parser.START);
+ schedLate( stop);
}
// ------------------------------------------------------------------------
// Backwards walk on the CFG only, looking for unreachable code - which has
// to be an infinite loop. Insert a bogus never-taken exit to Stop, so the
// loop becomes reachable. Also, set loop nesting depth
- static void fixLoops(StopNode stop) {
+ public static void fixLoops(StopNode stop) {
// Backwards walk from Stop, looking for unreachable code
BitSet visit = new BitSet();
HashSet<CFGNode> unreach = new HashSet<>();
@@ -54,6 +55,10 @@ public abstract class GlobalCodeMotion {
}
// ------------------------------------------------------------------------
+ // Visit all nodes in CFG Reverse Post-Order, essentially defs before uses
+ // (except at loops). Since defs are visited first - and hoisted as early
+ // as possible, when we come to a use we place it just after its deepest
+ // input.
private static void schedEarly() {
ArrayList<CFGNode> rpo = new ArrayList<>();
BitSet visit = new BitSet();
@@ -110,38 +115,60 @@ public abstract class GlobalCodeMotion {
}
// ------------------------------------------------------------------------
- private static void schedLate(StartNode start) {
+ private static void schedLate( StopNode stop) {
CFGNode[] late = new CFGNode[Node.UID()];
Node[] ns = new Node[Node.UID()];
- _schedLate(start,ns,late);
+ // Breadth-first scheduling
+ breadth(stop,ns,late);
+
+ // Copy the best placement choice into the control slot
for( int i=0; i<late.length; i++ )
if( ns[i] != null )
ns[i].setDef(0,late[i]);
}
- // Forwards post-order pass. Schedule all outputs first, then draw a
- // idom-tree line from the LCA of uses to the early schedule. Schedule is
- // legal anywhere on this line; pick the most control-dependent (largest
- // idepth) in the shallowest loop nest.
- private static void _schedLate(Node n, Node[] ns, CFGNode[] late) {
- if( late[n._nid]!=null ) return; // Been there, done that
- // These I know the late schedule of, and need to set early for loops
- if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0);
- if( n instanceof PhiNode phi ) late[n._nid] = phi.region();
-
- // Walk Stores before Loads, so we can get the anti-deps right
- for( Node use : n._outputs )
- if( isForwardsEdge(use,n) &&
- use._type instanceof TypeMem )
- _schedLate(use,ns,late);
- // Walk everybody now
- for( Node use : n._outputs )
- if( isForwardsEdge(use,n) )
- _schedLate(use,ns,late);
- // Already implicitly scheduled
- if( n.isPinned() ) return;
- // Need to schedule n
+ private static void breadth(Node stop, Node[] ns, CFGNode[] late) {
+ // Things on the worklist have some (but perhaps not all) uses done.
+ WorkList<Node> work = new WorkList<>();
+ work.push(stop);
+ Node n;
+ outer:
+ while( (n = work.pop()) != null ) {
+ assert late[n._nid]==null; // No double visit
+ // These I know the late schedule of, and need to set early for loops
+ if( n instanceof CFGNode cfg ) late[n._nid] = cfg.blockHead() ? cfg : cfg.cfg(0);
+ else if( n instanceof PhiNode phi ) late[n._nid] = phi.region();
+ else if( n.isPinned() ) late[n._nid] = n.cfg0();
+ else {
+
+ // All uses done?
+ for( Node use : n._outputs )
+ if( late[use._nid]==null )
+ continue outer; // Nope, await all uses done
+
+ // Loads need their memory inputs' uses also done
+ if( n instanceof LoadNode ld )
+ for( Node memuse : ld.mem()._outputs )
+ if( memuse._type instanceof TypeMem && late[memuse._nid]==null )
+ continue outer;
+
+ // All uses done, schedule
+ _doSchedLate(n,ns,late);
+ }
+
+ // Walk all inputs and put on worklist, as their last-use might now be done
+ for( Node def : n._inputs )
+ if( def!=null && late[def._nid]==null ) {
+ work.push(def);
+ // if the def has a load use, maybe the load can fire
+ for( Node ld : def._outputs )
+ if( ld instanceof LoadNode && late[ld._nid]==null )
+ work.push(ld);
+ }
+ }
+ }
+ private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late) {
// Walk uses, gathering the LCA (Least Common Ancestor) of uses
CFGNode early = (CFGNode)n.in(0);
assert early != null;
@@ -154,7 +181,7 @@ public abstract class GlobalCodeMotion {
lca = find_anti_dep(lca,load,early,late);
// Walk up from the LCA to the early, looking for best place. This is
- // lowest execution frequency, approximated by least loop depth and
+ // the lowest execution frequency, approximated by least loop depth and
// deepest control flow.
CFGNode best = lca;
lca = lca.idom(); // Already found best for starting LCA
@@ -187,12 +214,6 @@ public abstract class GlobalCodeMotion {
(lca.idepth() > best.idepth() || best instanceof IfNode);
}
- // Skip iteration if a backedge
- private static boolean isForwardsEdge(Node use, Node def) {
- return use != null && def != null &&
- !(use.nIns()>2 && use.in(2)==def && (use instanceof LoopNode || (use instanceof PhiNode phi && phi.region() instanceof LoopNode)));
- }
-
private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) {
// We could skip final-field loads here.
// Walk LCA->early, flagging Load's block location choices
@@ -202,6 +223,7 @@ public abstract class GlobalCodeMotion {
for( Node mem : load.mem()._outputs ) {
switch( mem ) {
case StoreNode st:
+ assert late[st._nid]!=null;
lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
break;
case PhiNode phi:
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/IterPeeps.java b/chapter13/src/main/java/com/seaofnodes/simple/IterPeeps.java
index 121d0233..783fc4f1 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/IterPeeps.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/IterPeeps.java
@@ -57,7 +57,7 @@ public abstract class IterPeeps {
/**
* Iterate peepholes to a fixed point
*/
- public static StopNode iterate(StopNode stop, boolean show) {
+ public static StopNode iterate(StopNode stop) {
assert progressOnList(stop);
int cnt=0;
@@ -92,11 +92,6 @@ public abstract class IterPeeps {
n.kill(); // Just plain dead
}
- // Break infinite loops, forcing a Never-branch to exit
- GlobalCodeMotion.fixLoops(stop);
- if( show )
- System.out.println(new GraphVisualizer().generateDotOutput(stop,null,null));
- GlobalCodeMotion.buildCFG(stop);
return stop;
}
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/Parser.java b/chapter13/src/main/java/com/seaofnodes/simple/Parser.java
index 9182a74c..56c8e955 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/Parser.java
@@ -178,7 +178,7 @@ public class Parser {
* type IDENTIFIER ;
* </pre>
*/
- private Field parseField(String sname) {
+ private Field parseField() {
Type t = type();
if( t==null )
throw errorSyntax("Requires a field type, found '"+_lexer.getAnyNextToken()+"'");
@@ -199,14 +199,14 @@ public class Parser {
ArrayList<Field> fields = new ArrayList<>();
require("{");
while (!peek('}') && !_lexer.isEOF()) {
- Field field = parseField(typeName);
+ Field field = parseField();
if (fields.contains(field)) throw errorSyntax("Field '" + field + "' already defined in struct '" + typeName + "'");
fields.add(field);
}
require("}");
// Build and install the TypeStruct
TypeStruct ts = TypeStruct.make(typeName, fields);
- TYPES.put(typeName, ts); // Insert the struct name in the collection of all struct names
+ TYPES.put(typeName, ts);
START.addMemProj(ts, _scope); // Insert memory edges
return parseStatement();
}
@@ -349,8 +349,8 @@ public class Parser {
// Parse the false side
_scope = fScope; // Restore scope, then parse else block if any
ctrl(ifF.unkeep()); // Ctrl token is now set to ifFalse projection
+ _scope.upcast(ifF,pred,true); // Up-cast predicate
if (matchx("else")) {
- _scope.upcast(ifF,pred,true); // Up-cast predicate
parseStatement();
fScope = _scope;
}
@@ -432,24 +432,36 @@ public class Parser {
// Auto-widen int to float
if( expr._type instanceof TypeInteger && t instanceof TypeFloat )
expr = new ToFloatNode(expr).peephole();
+ // Auto-deepen forward ref types
+ Type e = expr._type;
+ if( e instanceof TypeMemPtr tmp && tmp._obj._fields==null )
+ e = tmp.make_from((TypeStruct)TYPES.get(tmp._obj._name));
// Type is sane
- if( !expr._type.isa(t) )
- throw error("Type " + expr._type.str() + " is not of declared type " + t.str());
+ if( !e.isa(t) )
+ throw error("Type " + e.str() + " is not of declared type " + t.str());
return _scope.update(name,expr);
}
- // Parse a type-or-null
+ // Parse a "type id" and return "type" (and re-parse "id" in caller) or
+ // return null.
private Type type() {
int old = _lexer._position;
String tname = _lexer.matchId();
if( tname==null ) return null;
+ boolean nullable = match("?");
Type t = TYPES.get(tname);
+ // Assume a forward-reference type
if( t == null ) {
- // Not a type; unwind the parse
- _lexer._position = old;
- return null;
+ int old2 = _lexer._position;
+ String id = _lexer.matchId();
+ if( id==null ) {
+ _lexer._position = old;
+ return null;
+ }
+ TYPES.put(tname,t = TypeStruct.make(tname));
+ _lexer._position = old2; // Reparse ID in caller
}
- return t instanceof TypeStruct obj ? TypeMemPtr.make(obj,match("?")) : t;
+ return t instanceof TypeStruct obj ? TypeMemPtr.make(obj,nullable) : t;
}
@@ -583,7 +595,7 @@ public class Parser {
Node n = new NewNode(TypeMemPtr.make(obj), ctrl()).peephole().keep();
int alias = START._aliasStarts.get(obj._name);
for( Field field : obj._fields ) {
- memAlias(alias, new StoreNode(field._fname, alias, memAlias(alias), n, ZERO).peephole());
+ memAlias(alias, new StoreNode(field._fname, alias, memAlias(alias), n, new ConstantNode(field._type.makeInit()).peephole()).peephole());
alias++;
}
return n.unkeep();
@@ -613,7 +625,9 @@ public class Parser {
String name = requireId().intern();
if( expr._type == TypeMemPtr.TOP ) throw error("Accessing field '" + name + "' from null");
- int idx = ptr._obj==null ? -1 : ptr._obj.find(name);
+ if( ptr._obj == null ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
+ TypeStruct base = (TypeStruct)TYPES.get(ptr._obj._name);
+ int idx = base==null ? -1 : base.find(name);
if( idx == -1 ) throw error("Accessing unknown field '" + name + "' from '" + ptr.str() + "'");
int alias = START._aliasStarts.get(ptr._obj._name)+idx;
@@ -627,7 +641,7 @@ public class Parser {
}
}
- Type declaredType = ptr._obj._fields[idx]._type;
+ Type declaredType = base._fields[idx]._type;
return parsePostfix(new LoadNode(name, alias, declaredType, memAlias(alias), expr).peephole());
}
@@ -636,7 +650,7 @@ public class Parser {
*
* <pre>
* integerLiteral: [1-9][0-9]* | [0]
- * floatLiteral: [1-9][0-9]* | [0]
+ * floatLiteral: [digits].[digits]?[e [digits]]?
* </pre>
*/
private ConstantNode parseLiteral() {
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/node/CastNode.java b/chapter13/src/main/java/com/seaofnodes/simple/node/CastNode.java
index fec540f7..fbf0e518 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/node/CastNode.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/node/CastNode.java
@@ -22,6 +22,9 @@ public class CastNode extends Node {
public String uniqueName() { return "Cast_" + _nid; }
@Override
+ public boolean isPinned() { return true; }
+
+ @Override
StringBuilder _print1(StringBuilder sb, BitSet visited) {
return in(1)._print0(sb.append(label()), visited);
}
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/node/ConstantNode.java b/chapter13/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
index cf1a76b0..016b0b9b 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/node/ConstantNode.java
@@ -32,7 +32,7 @@ public class ConstantNode extends Node {
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) {
- return _con._print(sb);
+ return _con.print(sb);
}
@Override public boolean isMultiTail() { return true; }
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter13/src/main/java/com/seaofnodes/simple/node/LoadNode.java
index 95d49662..e235cc36 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -2,7 +2,6 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.type.Type;
-import com.seaofnodes.simple.type.Field;
import java.util.BitSet;
@@ -26,10 +25,7 @@ public class LoadNode extends MemOpNode {
}
@Override
- public String label() { return "Load"; }
-
- @Override
- public String glabel() { return "."+_name; }
+ public String label() { return "."+_name; }
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/node/StopNode.java b/chapter13/src/main/java/com/seaofnodes/simple/node/StopNode.java
index ab567bae..0eacc1f4 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/node/StopNode.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/node/StopNode.java
@@ -2,6 +2,8 @@ package com.seaofnodes.simple.node;
import com.seaofnodes.simple.type.Type;
import com.seaofnodes.simple.IterPeeps;
+import com.seaofnodes.simple.GlobalCodeMotion;
+import com.seaofnodes.simple.GraphVisualizer;
import java.util.BitSet;
@@ -67,13 +69,21 @@ public class StopNode extends CFGNode {
return addDef(node);
}
- public StopNode iterate( ) { return IterPeeps.iterate(this,false).typeCheck(); }
- public StopNode iterate(boolean show) { return IterPeeps.iterate(this,show ).typeCheck(); }
+ public StopNode iterate( ) { return IterPeeps.iterate(this).typeCheck().GCM(false); }
+ public StopNode iterate(boolean show) { return IterPeeps.iterate(this).typeCheck().GCM(show ); }
StopNode typeCheck() {
String err = walk( Node::err );
if( err != null ) throw new RuntimeException(err);
return this;
}
+ StopNode GCM(boolean show) {
+ // Break infinite loops, forcing a Never-branch to exit
+ GlobalCodeMotion.fixLoops(this);
+ if( show )
+ System.out.println(new GraphVisualizer().generateDotOutput(this,null,null));
+ GlobalCodeMotion.buildCFG(this);
+ return this;
+ }
@Override public Node getBlockStart() { return this; }
}
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter13/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 7c41e767..2b4a46dd 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -22,9 +22,7 @@ public class StoreNode extends MemOpNode {
}
@Override
- public String label() { return "Store"; }
- @Override
- public String glabel() { return "."+_name+" ="; }
+ public String label() { return "."+_name+"="; }
@Override
public boolean isMem() { return true; }
public Node val() { return in(3); }
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/Field.java b/chapter13/src/main/java/com/seaofnodes/simple/type/Field.java
index e1c337d4..b2c006dd 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/Field.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/Field.java
@@ -52,8 +52,8 @@ public class Field extends Type {
@Override
- public StringBuilder _print( StringBuilder sb ) {
- return _type._print(sb.append(_fname).append(":"));
+ public StringBuilder print( StringBuilder sb ) {
+ return _type.print(sb.append(_fname).append(" :"));
}
@Override public String str() { return _fname; }
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/Type.java b/chapter13/src/main/java/com/seaofnodes/simple/type/Type.java
index f3152719..1f211430 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/Type.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/Type.java
@@ -76,20 +76,22 @@ public class Type {
/**
* Display Type name in a format that's good for IR printer
*/
- public StringBuilder typeName( StringBuilder sb) { return _print(sb); }
+ public StringBuilder typeName( StringBuilder sb) { return print(sb); }
public Type makeInit() { return null; }
// ----------------------------------------------------------
- // Notes on Type interning.
- // At the moment it is not easy to reset the interned types
- // because we hold static references to several types and these are scattered
- // around. This means the INTERN cache will retain all types from
- // every run of the Parser. For this to work correctly types must be
- // rigorous about defining when they are the same. Also types need to be
- // immutable once defined.
- // The rationale for interning is performance.
+ // Notes on Type interning: At the moment it is not easy to reset the
+ // interned types because we hold static references to several types and
+ // these are scattered around. This means the INTERN cache will retain all
+ // types from every run of the Parser. For this to work correctly types
+ // must be rigorous about defining when they are the same. Also types need
+ // to be immutable once defined. The rationale for interning is
+ // *correctness* with cyclic type definitions. Simple structural recursive
+ // checks go exponential with merely sharing, but with cycles they will
+ // stack overflow and crash. Intering means we do not need to have sharing
+ // checks with every type compare, only during interning.
// Factory method which interns "this"
public <T extends Type> T intern() {
@@ -178,10 +180,10 @@ public class Type {
// This is a more verbose dev-friendly print.
@Override
public final String toString() {
- return _print(new StringBuilder()).toString();
+ return print(new StringBuilder()).toString();
}
- public StringBuilder _print(StringBuilder sb) { return is_simple() ? sb.append(STRS[_type]) : sb;}
+ public StringBuilder print(StringBuilder sb) { return is_simple() ? sb.append(STRS[_type]) : sb;}
// This is used by error messages, and is a shorted print.
public String str() { return STRS[_type]; }
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeFloat.java b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
index 36bc61f5..738aebc9 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeFloat.java
@@ -35,7 +35,7 @@ public class TypeFloat extends Type {
// FIXME this display format is problematic
// In visualizer '#' gets prepended if its a constant
@Override
- public StringBuilder _print(StringBuilder sb) {
+ public StringBuilder print(StringBuilder sb) {
if( this==TOP ) return sb.append("FltTop");
if( this==BOT ) return sb.append("FltBot");
return sb.append(_con);
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeInteger.java b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
index f2a93a3a..8c77672b 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeInteger.java
@@ -35,7 +35,7 @@ public class TypeInteger extends Type {
// FIXME this display format is problematic
// In visualizer '#' gets prepended if its a constant
@Override
- public StringBuilder _print(StringBuilder sb) {
+ public StringBuilder print(StringBuilder sb) {
if( this==TOP ) return sb.append("IntTop");
if( this==BOT ) return sb.append("IntBot");
return sb.append(_con);
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMem.java b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMem.java
index 198d49ee..b2d081f4 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMem.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMem.java
@@ -15,7 +15,7 @@ public class TypeMem extends Type {
// 0 means TOP, no slice.
// -1 means BOT, all memory.
// N means slice#N.
- private final int _alias;
+ public final int _alias;
private TypeMem(int alias) { super(TMEM); _alias = alias; }
@@ -23,7 +23,6 @@ public class TypeMem extends Type {
public static final TypeMem TOP = make( 0);
public static final TypeMem BOT = make(-1);
-
public static void gather(ArrayList<Type> ts) { ts.add(make(1)); ts.add(BOT); }
@Override
@@ -40,7 +39,7 @@ public class TypeMem extends Type {
}
@Override
- public Type glb() { return TypeMem.BOT; }
+ public Type glb() { return make(_alias); }
@Override
int hash() { return 9876543 + _alias; }
@@ -52,7 +51,7 @@ public class TypeMem extends Type {
}
@Override
- public StringBuilder _print(StringBuilder sb) {
+ public StringBuilder print(StringBuilder sb) {
return sb.append("MEM#").append( switch(_alias) {
case 0 -> "TOP";
case -1 -> "BOT";
@@ -60,5 +59,5 @@ public class TypeMem extends Type {
});
}
- @Override public String str() { return _print(new StringBuilder()).toString(); }
+ @Override public String str() { return toString(); }
}
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
index 65352402..1ad65611 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeMemPtr.java
@@ -27,7 +27,8 @@ public class TypeMemPtr extends Type {
_nil = nil;
}
public static TypeMemPtr make(TypeStruct obj, boolean nil) { return new TypeMemPtr(obj, nil).intern(); }
- public static TypeMemPtr make(TypeStruct obj) { return TypeMemPtr.make(obj, false); }
+ public static TypeMemPtr make(TypeStruct obj) { return make(obj, false); }
+ public TypeMemPtr make_from(TypeStruct obj) { return make(obj, _nil); }
public static TypeMemPtr BOT = make(TypeStruct.BOT,true);
public static TypeMemPtr TOP = BOT.dual();
@@ -63,10 +64,10 @@ public class TypeMemPtr extends Type {
// [void,name,MANY]*[,?]
@Override
- public StringBuilder _print(StringBuilder sb) {
+ public StringBuilder print(StringBuilder sb) {
if( this== NULLPTR) return sb.append("null");
if( this== VOIDPTR) return sb.append("*void");
- return _obj._print(sb.append("*")).append(_nil ? "?" : "");
+ return _obj.print(sb.append("*")).append(_nil ? "?" : "");
}
@Override public String str() {
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeStruct.java b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
index 29102675..e2489d6f 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeStruct.java
@@ -8,9 +8,14 @@ import java.util.ArrayList;
*/
public class TypeStruct extends Type {
- // For now in terms of the lattice
- // a struct type just stays as is
-
+ // A Struct has a name and a set of fields; the fields themselves have
+ // names and types. Briefly during parsing its allowed to have a
+ // forward-ref to a Struct; in this case the _fields array is null.
+ // Its illegal to attempt to load a field from a forward-ref struct.
+ //
+ // During the normal optimization run, struct types "bottom out" at further
+ // struct references, so we don't have to handle e.g. cyclic types. The
+ // "bottom out" is again the forward-ref struct.
public final String _name;
public final Field[] _fields;
@@ -20,13 +25,23 @@ public class TypeStruct extends Type {
_fields = fields;
}
+ // Forward-ref version
+ public static TypeStruct make(String name) { return new TypeStruct(name, null).intern(); }
+ // All fields directly listed
public static TypeStruct make(String name, Field[] fields) { return new TypeStruct(name, fields).intern(); }
+ // Parser uses an ArrayList to gather the fields
public static TypeStruct make(String name, ArrayList<Field> fields) { return make(name,fields.toArray(new Field[fields.size()])); }
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});
- public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); }
+
+ // 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", TypeInteger.BOT), Field.make("s2",TypeMemPtr.make(S2F,false)) });
+ private static final TypeStruct S2 = make("S2", new Field[]{ Field.make("b", TypeFloat .BOT), Field.make("s1",TypeMemPtr.make(S1F,false)) });
+ public static void gather(ArrayList<Type> ts) { ts.add(TEST); ts.add(BOT); ts.add(S1); ts.add(S2); }
public int find(String fname) {
for( int i=0; i<_fields.length; i++ )
@@ -51,6 +66,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;
// Now all fields should be the same, so just do field meets
assert _fields.length == that._fields.length;
Field[] flds = new Field[_fields.length];
@@ -63,6 +80,7 @@ public class TypeStruct extends Type {
public TypeStruct dual() {
if( this==TOP ) return BOT;
if( this==BOT ) return TOP;
+ if( _fields == null ) return this;
Field[] flds = new Field[_fields.length];
for( int i=0; i<_fields.length; i++ )
flds[i] = _fields[i].dual();
@@ -80,9 +98,10 @@ public class TypeStruct extends Type {
return make(_name,flds);
}
private boolean _glb() {
- for( Field f : _fields )
- if( f.glb() != f )
- return false;
+ if( _fields!=null )
+ for( Field f : _fields )
+ if( f.glb() != f )
+ return false;
return true;
}
@@ -91,6 +110,7 @@ public class TypeStruct extends Type {
TypeStruct ts = (TypeStruct)t; // Invariant
if( !_name.equals(ts._name) )
return false;
+ if( _fields == ts._fields ) return true;
if( _fields.length!=ts._fields.length )
return false;
for( int i = 0; i < _fields.length; i++ )
@@ -102,17 +122,20 @@ public class TypeStruct extends Type {
@Override
int hash() {
long hash = _name.hashCode();
- for( Field f : _fields ) hash = Utils.rot(hash,13) ^ f.hashCode();
+ if( _fields != null )
+ for( Field f : _fields )
+ hash = Utils.rot(hash,13) ^ f.hashCode();
return Utils.fold(hash);
}
@Override
- public StringBuilder _print(StringBuilder sb) {
- sb.append(_name).append(" {\n");
+ public StringBuilder print(StringBuilder sb) {
+ sb.append(_name);
+ if( _fields == null ) return sb; // Forward reference struct, just print the name
+ sb.append(" {\n");
for( Field f : _fields ) {
- sb.append(" ").append(f._fname).append(":");
- f._type._print(sb);
- sb.append(";\n");
+ sb.append(" ").append(f._fname).append(" :");
+ f._type.print(sb).append(";\n");
}
return sb.append("}");
}
diff --git a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeTuple.java b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
index 01bcbe9f..783f3f60 100644
--- a/chapter13/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
+++ b/chapter13/src/main/java/com/seaofnodes/simple/type/TypeTuple.java
@@ -37,17 +37,16 @@ public class TypeTuple extends Type {
return make(ts);
}
- @Override
- public StringBuilder _print(StringBuilder sb) {
- sb.append("[");
+ @Override public StringBuilder print(StringBuilder sb) {
+ sb.append("[ ");
for( Type t : _types )
- t._print(sb).append(",");
- sb.setLength(sb.length()-1);
+ t.print(sb).append(", ");
+ sb.setLength(sb.length()-2);
sb.append("]");
return sb;
}
- @Override public String str() { return _print(new StringBuilder()).toString(); }
+ @Override public String str() { return print(new StringBuilder()).toString(); }
/**
* Display Type name in a format that's good for IR printer
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter13/src/test/java/com/seaofnodes/simple/Chapter05Test.java
index 64f3a1d9..d48bde7f 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/Chapter05Test.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/Chapter05Test.java
@@ -37,7 +37,7 @@ if (arg == 1) {
}
return c;""", TypeInteger.BOT);
StopNode ret = parser.parse().iterate(true);
- assertEquals("return Phi(Region18,4,3);", ret.toString());
+ assertEquals("return Phi(Region19,4,3);", ret.toString());
}
@Test
@@ -116,7 +116,7 @@ if( arg==0 )
return arg+a+b;
#showGraph;""", TypeInteger.BOT);
StopNode stop = parser.parse();
- assertEquals("return ((arg+Phi(Region15,1,0))+Phi(Region30,2,0));", stop.toString());
+ assertEquals("return ((arg+Phi(Region17,1,0))+Phi(Region33,2,0));", stop.toString());
}
@Test
@@ -130,7 +130,7 @@ if( arg==1 )
}
return a;""");
StopNode ret = parser.parse().iterate();
- assertEquals("return (arg==Phi(Region18,3,2));", ret.toString());
+ assertEquals("return (arg==Phi(Region19,3,2));", ret.toString());
}
@Test
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter13/src/test/java/com/seaofnodes/simple/Chapter06Test.java
index fed34465..472e2db3 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/Chapter06Test.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/Chapter06Test.java
@@ -30,7 +30,7 @@ if (arg)
return (arg < a) < 3;
""");
StopNode stop = parser.parse().iterate();
- assertEquals("return ((arg<Phi(Region14,2,1))<3);", stop.toString());
+ assertEquals("return ((arg<Phi(Region16,2,1))<3);", stop.toString());
}
@Test
@@ -200,7 +200,7 @@ if( arg ) {
return a+b;
""");
StopNode ret = parser.parse().iterate(true);
- assertEquals("return Phi(Region26,4,1);", ret.toString());
+ assertEquals("return Phi(Region28,4,1);", ret.toString());
}
@@ -251,7 +251,7 @@ if( arg ) {
return a+b+c;
""");
StopNode ret = parser.parse().iterate(true);
- assertEquals("return (Phi(Region38,Phi(Region25,2,3),0)+Phi(Region,3,1));", ret.toString());
+ assertEquals("return (Phi(Region40,Phi(Region25,2,3),0)+Phi(Region,3,1));", ret.toString());
}
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter13/src/test/java/com/seaofnodes/simple/Chapter08Test.java
index ac891050..aa650022 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/Chapter08Test.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/Chapter08Test.java
@@ -23,7 +23,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate(true);
- assertEquals("return Phi(Region36,Phi(Region26,Phi(Loop8,arg,(Phi_arg+1)),Add),Add);", stop.toString());
+ assertEquals("return Phi(Region37,Phi(Region26,Phi(Loop8,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(true);
- assertEquals("return Phi(Loop9,1,Phi(Region41,Phi_a,(Phi_a+1)));", stop.toString());
+ assertEquals("return Phi(Loop9,1,Phi(Region43,Phi_a,(Phi_a+1)));", stop.toString());
assertTrue(stop.ret().ctrl() instanceof CProjNode);
System.out.println(IRPrinter.prettyPrint(stop,99));
}
@@ -66,7 +66,7 @@ while(arg < 10) {
return arg;
""");
StopNode stop = parser.parse().iterate(true);
- assertEquals("return Phi(Region34,Phi(Loop8,arg,(Phi_arg+1)),Add);", stop.toString());
+ assertEquals("return Phi(Region35,Phi(Loop8,arg,(Phi_arg+1)),Add);", stop.toString());
assertTrue(stop.ret().ctrl() instanceof RegionNode);
}
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter13/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 06065702..65bd1170 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -1,12 +1,7 @@
package com.seaofnodes.simple;
-import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.StopNode;
-import com.seaofnodes.simple.type.*;
import org.junit.Test;
-
-import java.util.Arrays;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -110,7 +105,7 @@ while (arg) {
return bar.a;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return Phi(Loop11,0,(Phi_a+2));", stop.toString());
+ assertEquals("return Phi(Loop12,0,(Phi_a+2));", stop.toString());
}
@Test
@@ -162,7 +157,7 @@ if( bar ) bar.a = 1;
return bar;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return Phi(Region16,null,new Bar);", stop.toString());
+ assertEquals("return Phi(Region31,(*void)Phi(Region19,null,new Bar),null);", stop.toString());
}
@Test
@@ -178,7 +173,7 @@ else bar.a = 1;
return rez;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return Phi(Region33,4,3);", stop.toString());
+ assertEquals("return Phi(Region37,4,3);", stop.toString());
}
@Test
@@ -232,7 +227,7 @@ while( i.x < i.len ) {
return sum;
""");
StopNode stop = parser.parse().iterate(true);
- assertEquals("return Phi(Loop15,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", stop.toString());
+ assertEquals("return Phi(Loop17,0,(Phi(Loop,0,(Phi_x+1))+Phi_sum));", stop.toString());
}
@@ -254,7 +249,7 @@ return ret;
""");
StopNode stop = parser.parse(true).iterate(true);
System.out.println(IRPrinter.prettyPrint(stop, 99, true));
- assertEquals("return Phi(Loop11,new s0,Phi(Region31,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop12,new s0,Phi(Region34,new s0,Phi_ret));", stop.toString());
}
@Test
@@ -273,7 +268,7 @@ return ret;
""");
StopNode stop = parser.parse(true).iterate(true);
System.out.println(IRPrinter.prettyPrint(stop, 99, true));
- assertEquals("return Phi(Loop13,new s0,Phi(Region32,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop15,new s0,Phi(Region35,new s0,Phi_ret));", stop.toString());
}
@@ -291,7 +286,7 @@ return ret;
""");
StopNode stop = parser.parse(true).iterate(true);
System.out.println(IRPrinter.prettyPrint(stop, 99, true));
- assertEquals("return Phi(Loop11,new s0,Phi(Region30,new s0,Phi_ret));", stop.toString());
+ assertEquals("return Phi(Loop12,new s0,Phi(Region33,new s0,Phi_ret));", stop.toString());
}
@Test
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter13/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 6c1f7938..e367184c 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -324,7 +324,7 @@ while( prime <= arg ) {
return primeCount;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("Stop[ return 0; return Phi(Loop19,1,Phi(Region81,Phi_primeCount,Phi(Region76,(Phi_primeCount+1),Phi_primeCount))); ]", stop.toString());
+ assertEquals("Stop[ return 0; return Phi(Loop20,1,Phi(Region87,Phi_primeCount,Phi(Region81,(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
@@ -366,7 +366,7 @@ if (arg) {
return i;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return Phi(Region31,.f,0);", stop.toString());
+ assertEquals("return Phi(Region34,.f,0);", stop.toString());
}
@Test
@@ -472,7 +472,7 @@ while(arg) {
return arg;
""");
StopNode stop = parser.parse(false).iterate(false);
- assertEquals("return Phi(Loop13,arg,Phi(Region35,.f,0));", stop.toString());
+ assertEquals("return Phi(Loop15,arg,Phi(Region37,.f,0));", stop.toString());
}
@Test
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/Chapter12Test.java b/chapter13/src/test/java/com/seaofnodes/simple/Chapter12Test.java
index af8fdfe9..903fe676 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/Chapter12Test.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/Chapter12Test.java
@@ -2,9 +2,8 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.evaluator.Evaluator;
import com.seaofnodes.simple.node.StopNode;
+import org.junit.Ignore;
import org.junit.Test;
-
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
@@ -61,4 +60,46 @@ return x+1==x;
assertEquals(0L, Evaluator.evaluate(stop, 1));
}
+
+ @Test
+ public void testLinkedList0() {
+ Parser parser = new Parser(
+"""
+struct LLI { LLI? next; int i; }
+LLI? head = null;
+while( arg ) {
+ LLI x = new LLI;
+ x.next = head;
+ x.i = arg;
+ head = x;
+ arg = arg-1;
+}
+return head.next.i;
+""");
+ try { parser.parse(true).iterate(true); fail(); }
+ catch( Exception e ) { assertEquals("Might be null accessing 'i'",e.getMessage()); }
+ }
+
+ @Test
+ public void testLinkedList1() {
+ Parser parser = new Parser(
+"""
+struct LLI { LLI? next; int i; }
+LLI? head = null;
+while( arg ) {
+ LLI x = new LLI;
+ x.next = head;
+ x.i = arg;
+ head = x;
+ arg = arg-1;
+}
+if( !head ) return 0;
+LLI? next = head.next;
+if( next==null ) return 1;
+return next.i;
+""");
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("Stop[ return 0; return 1; return .i; ]", stop.toString());
+ assertEquals(2L, Evaluator.evaluate(stop, 3));
+ }
}
diff --git a/chapter13/src/test/java/com/seaofnodes/simple/TypeTest.java b/chapter13/src/test/java/com/seaofnodes/simple/TypeTest.java
index f9c580cc..9e8a1418 100644
--- a/chapter13/src/test/java/com/seaofnodes/simple/TypeTest.java
+++ b/chapter13/src/test/java/com/seaofnodes/simple/TypeTest.java
@@ -2,8 +2,8 @@ package com.seaofnodes.simple;
import com.seaofnodes.simple.type.*;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
-
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertSame;
@@ -36,9 +36,9 @@ public class TypeTest {
Assert.assertEquals(TypeMem .BOT, m2.meet(m3));
Assert.assertEquals(TypeMem .BOT, m3.meet(m4));
- Assert.assertEquals(TypeMem.BOT, m1.glb());
- Assert.assertEquals(m1, m1.dual());
- Assert.assertEquals(TypeMem.TOP, m1.glb().dual());
+ Assert.assertEquals(m1, m1.glb());
+ Assert.assertEquals(TypeMem.make(1), m1.dual());
+ Assert.assertEquals(m1.dual(), m1.glb().dual());
TypeMemPtr ptr1 = TypeMemPtr.make(s1);
Assert.assertEquals(s1, ptr1._obj);
@@ -128,10 +128,13 @@ public class TypeTest {
private static void check_symmetric( Type t0, Type t1 ) {
if( t1==t0 ) return;
Type mt = t0.meet(t1);
- Type ta = mt.dual().meet(t1.dual());
- Type tb = mt.dual().meet(t0.dual());
- assertSame(ta,t1.dual());
- assertSame(tb,t0.dual());
+ Type dm = mt.dual();
+ Type d0 = t0.dual();
+ Type d1 = t1.dual();
+ Type ta = dm.meet(d1);
+ Type tb = dm.meet(d0);
+ assertSame(ta,d1);
+ assertSame(tb,d0);
}
private static void assoc( Type t0, Type t1, Type t2 ) {
@@ -142,4 +145,13 @@ public class TypeTest {
assertSame(t01_2,t0_12);
}
+ // Test cyclic types and meets
+ @Test
+ public void testCyclic0() {
+ Type d0 = TypeStruct.S1.dual();
+ Type d1 = d0.dual();
+ assertSame(TypeStruct.S1,d1);
+ }
+
+
}