summaryrefslogtreecommitdiff
path: root/chapter15/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2024-10-04 12:58:50 -0700
committerCliff Click <cliffc@acm.org>2024-10-06 08:56:22 -0700
commit9edb193559f48f78f3040d96147d0baea84abde7 (patch)
treeaae8154fdec71a45563d9775dffba861705160cb /chapter15/src
parent37bc8c4cc5269eb4be281025b48583331f3008b1 (diff)
downloadSimple-9edb193559f48f78f3040d96147d0baea84abde7.tar.gz
Simple-9edb193559f48f78f3040d96147d0baea84abde7.zip
Optimize Loads bypassing trivial unrelated array stores.
Fix some graphviz display on array loads & stores Fix bug in GCM, extra code for infinite loops now handled elsewhere. Move BrainFuck out into its own test.
Diffstat (limited to 'chapter15/src')
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java11
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/Parser.java2
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java57
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/MemOpNode.java3
-rw-r--r--chapter15/src/main/java/com/seaofnodes/simple/node/PhiNode.java2
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/BrainFuckTest.java97
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter11Test.java4
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java94
-rw-r--r--chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java2
9 files changed, 161 insertions, 111 deletions
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java b/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
index 3bdfcb2d..2eac6ccc 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/GlobalCodeMotion.java
@@ -69,17 +69,6 @@ public abstract class GlobalCodeMotion {
cfg.loopDepth();
for( Node n : cfg._inputs )
_schedEarly(n,visit);
- // Strictly for dead infinite loops, we can have entire code blocks
- // not reachable from below - so we reach down, from above, one
- // step. Since _schedEarly modifies the output arrays, the normal
- // region._outputs ArrayList iterator throws CME. The extra edges
- // are always *added* after any Phis, so just walk the Phi prefix.
- if( cfg instanceof RegionNode region ) {
- int len = region.nOuts();
- for( int i=0; i<len; i++ )
- if( region.out(i) instanceof PhiNode phi )
- _schedEarly(phi,visit);
- }
}
}
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
index ab0fe427..64cde40f 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/Parser.java
@@ -685,7 +685,7 @@ public class Parser {
* Parse a primary expression:
*
* <pre>
- * primaryExpr : integerLiteral | Identifier | true | false | null | new Identifier | '(' expression ')'
+ * primaryExpr : integerLiteral | Identifier | true | false | null | new Type | '(' expression ')'
* </pre>
* @return a primary {@link Node}, never {@code null}
*/
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 762d8bea..0bdf86f0 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/LoadNode.java
@@ -25,10 +25,10 @@ public class LoadNode extends MemOpNode {
super(name, alias, glb, mem, ptr, off);
}
- // GraphVis DOT code and debugger labels
+ // GraphVis DOT code (must be valid Java identifiers) and debugger labels
@Override public String label() { return "ld_"+mlabel(); }
// GraphVis node-internal labels
- @Override public String glabel() { return _name.equals("[]=") ? "[]=" : "." +_name+"="; }
+ @Override public String glabel() { return "." +_name; }
@Override
StringBuilder _print1(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); }
@@ -46,19 +46,53 @@ public class LoadNode extends MemOpNode {
@Override
public Node idealize() {
+ Node ptr = ptr();
// Simple Load-after-Store on same address.
if( mem() instanceof StoreNode st &&
- ptr() == st.ptr() && off() == st.off() ) { // 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();
}
// Simple Load-after-New on same address.
if( mem() instanceof ProjNode p && p.in(0) instanceof NewNode nnn &&
- ptr() == nnn.proj(1) ) // Must check same object
+ ptr == nnn.proj(1) ) // Must check same object
return new ConstantNode(_declaredType.makeInit());
+ // Load-after-Store on same address, but bypassing provably unrelated
+ // stores. This is a more complex superset of the above two peeps.
+ // "Provably unrelated" is really weak.
+ Node mem = mem();
+ outer:
+ while( true ) {
+ switch( mem ) {
+ case StoreNode st:
+ if( ptr == st.ptr() && off() == st.off() )
+ return st.val(); // Proved equal
+ // Can we prove unequal? Offsets do not overlap?
+ if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap
+ !neverAlias(ptr,st.ptr()) ) // And might alias
+ break outer; // Cannot tell, stop trying
+ // Pointers cannot overlap
+ mem = st.mem(); // Proved never equal
+ break;
+ case PhiNode phi:
+ break outer; // Assume related
+ case ProjNode mproj:
+ if( mproj.in(0) instanceof NewNode nnn1 ) {
+ if( ptr instanceof ProjNode pproj && pproj.in(0) == mproj.in(0) )
+ return new ConstantNode(_declaredType.makeInit()); // Load from a New
+ if( !(ptr instanceof ProjNode pproj && pproj.in(0) instanceof NewNode nnn2) )
+ break outer; // Cannot tell, ptr not related to New
+ mem = nnn1.in(_alias);// Bypass unrelated New
+ break;
+ } else throw Utils.TODO();
+ default:
+ throw Utils.TODO();
+ }
+ }
+
// Push a Load up through a Phi, as long as it collapses on at least
// one arm. If at a Loop, the backedge MUST collapse - else we risk
// spinning the same transform around the loop indefinitely.
@@ -72,14 +106,25 @@ public class LoadNode extends MemOpNode {
if( profit(memphi,2) ||
// Else must not be a loop to count profit on LHS.
(!(memphi.region() instanceof LoopNode) && profit(memphi,1)) ) {
- Node ld1 = new LoadNode(_name,_alias,_declaredType,memphi.in(1),ptr(), off()).peephole();
- Node ld2 = new LoadNode(_name,_alias,_declaredType,memphi.in(2),ptr(), off()).peephole();
+ Node ld1 = ld(1);
+ Node ld2 = ld(2);
return new PhiNode(_name,_declaredType,memphi.region(),ld1,ld2);
}
}
return null;
}
+ private Node ld( int idx ) {
+ Node mem = mem(), ptr = ptr();
+ return new LoadNode(_name,_alias,_declaredType,mem.in(idx),ptr instanceof PhiNode && ptr.in(0)==mem.in(0) ? ptr.in(idx) : ptr, off()).peephole();
+ }
+ private static boolean neverAlias( Node ptr1, Node ptr2 ) {
+ return ptr1.in(0) != ptr2.in(0) &&
+ // Unrelated allocations
+ ptr1 instanceof ProjNode && ptr1.in(0) instanceof NewNode &&
+ ptr2 instanceof ProjNode && ptr2.in(0) instanceof NewNode;
+ }
+
// Profitable if we find a matching Store on this Phi arm.
private boolean profit(PhiNode phi, int idx) {
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/node/MemOpNode.java b/chapter15/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
index 1a819da5..c7a1efec 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/MemOpNode.java
@@ -25,7 +25,8 @@ public abstract class MemOpNode extends Node {
}
//
- String mlabel() { return _name.equals("[]") ? "ary" : (_name.equals("#") ? "len" : _name); }
+ static String mlabel(String name) { return name.equals("[]") ? "ary" : (name.equals("#") ? "len" : name); }
+ String mlabel() { return mlabel(_name); }
public Node mem() { return in(1); }
public Node ptr() { return in(2); }
diff --git a/chapter15/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter15/src/main/java/com/seaofnodes/simple/node/PhiNode.java
index a4fa8e1a..139e80a3 100644
--- a/chapter15/src/main/java/com/seaofnodes/simple/node/PhiNode.java
+++ b/chapter15/src/main/java/com/seaofnodes/simple/node/PhiNode.java
@@ -16,7 +16,7 @@ public class PhiNode extends Node {
public PhiNode(String label, Type declaredType, Node... inputs) { super(inputs); _label = label; assert declaredType!=null; _declaredType = declaredType; }
- @Override public String label() { return "Phi_"+_label; }
+ @Override public String label() { return "Phi_"+MemOpNode.mlabel(_label); }
@Override public String glabel() { return "&phi;_"+_label; }
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/BrainFuckTest.java b/chapter15/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
new file mode 100644
index 00000000..894abbf6
--- /dev/null
+++ b/chapter15/src/test/java/com/seaofnodes/simple/BrainFuckTest.java
@@ -0,0 +1,97 @@
+package com.seaofnodes.simple;
+
+import com.seaofnodes.simple.evaluator.Evaluator;
+import com.seaofnodes.simple.node.StopNode;
+import org.junit.Test;
+import org.junit.Ignore;
+
+import java.nio.charset.StandardCharsets;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class BrainFuckTest {
+
+ @Ignore
+ @Test
+ public void testBrainfuck() {
+ var program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".getBytes(StandardCharsets.UTF_8);
+ var encoded = new StringBuilder("u8[] program = new u8[").append(program.length).append("];");
+ for (int i = 0; i < program.length; i++) {
+ int value = program[i] & 0xFF;
+ encoded.append("program[").append(i).append("] = ").append(value).append(";");
+ }
+
+ Parser parser = new Parser(
+ encoded + """
+int pc = 0;
+int d = 0;
+u8[] data = new u8[10000];
+int i=0;
+while(i < data#) {
+ data[i] = 0;
+ i = i + 1;
+}
+
+u8[] output = new u8[0];
+
+while (pc < program#) {
+ int command = program[pc];
+ if (command == 62) {
+ d = d + 1;
+ } else if (command == 60) {
+ d = d - 1;
+ } else if (command == 43) {
+ data[d] = data[d] + 1;
+ } else if (command == 45) {
+ data[d] = data[d] - 1;
+ } else if (command == 46) {
+ u8[] old = output;
+ output = new u8[output# + 1];
+ i = 0;
+ while (i < old#) {
+ output[i] = old[i];
+ i = i + 1;
+ }
+ output[i] = data[d];
+ } else if (command == 44) {
+ data[d] = 42;
+ } else if (command == 91) {
+ if (data[d] == 0) {
+ int d = 1;
+ while (d > 0) {
+ pc = pc + 1;
+ command = program[pc];
+ if (command == 91) d = d + 1;
+ if (command == 93) d = d - 1;
+ }
+ }
+ } else if (command == 93) {
+ if (data[d] != 0) {
+ int d = 1;
+ while (d > 0) {
+ pc = pc - 1;
+ command = program[pc];
+ if (command == 93) d = d + 1;
+ if (command == 91) d = d - 1;
+ }
+ }
+ }
+ pc = pc + 1;
+}
+int result = 0;
+i = 0;
+while (i < 8) {
+ result = (result << 8) | data[i];
+ i = i + 1;
+}
+return result;
+ """);
+ StopNode stop = parser.parse(false).iterate(false);
+ long expected = 0;
+ for (byte b : "Hello Wo".getBytes(StandardCharsets.UTF_8)) {
+ expected = (expected << 8) | b;
+ }
+ assertEquals(expected, Evaluator.evaluate(stop, 0, 100000));
+ }
+}
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter11Test.java
index 9f5119f5..69e29826 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter11Test.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter11Test.java
@@ -346,7 +346,7 @@ if (arg) v.f=1;
return i;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return .f;", stop.toString());
+ assertEquals("return 2;", stop.toString());
assertEquals(2L, Evaluator.evaluate(stop, 0));
assertEquals(2L, Evaluator.evaluate(stop, 1));
}
@@ -406,7 +406,7 @@ while (arg) v.f = 2;
return i;
""");
StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return .f;", stop.toString());
+ assertEquals("return arg;", stop.toString());
}
@Test
diff --git a/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
index 00ed6c6c..1aee2f0d 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/Chapter15Test.java
@@ -30,7 +30,7 @@ return 3.14;
int[] is = new int[2];
return is[1];
""");
- StopNode stop = parser.parse(false).iterate(true);
+ StopNode stop = parser.parse(false).iterate(false);
assertEquals("return 0;", stop.toString());
assertEquals(0L, Evaluator.evaluate(stop, 0));
}
@@ -43,7 +43,7 @@ int[] is = new int[2];
int[] is2 = new int[2];
return is[1];
""");
- StopNode stop = parser.parse(false).iterate(true);
+ StopNode stop = parser.parse(false).iterate(false);
assertEquals("return 0;", stop.toString());
assertEquals(0L, Evaluator.evaluate(stop, 0));
}
@@ -57,8 +57,8 @@ a[0] = 1;
a[1] = 2;
return a[0];
""");
- StopNode stop = parser.parse(false).iterate(true);
- assertEquals("return .[];", stop.toString());
+ StopNode stop = parser.parse(false).iterate(false);
+ assertEquals("return 1;", stop.toString());
assertEquals(1L, Evaluator.evaluate(stop, 0));
}
@@ -166,7 +166,7 @@ while( i < ary# - 1 ) {
}
return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6
""");
- StopNode stop = parser.parse(false).iterate(true);
+ StopNode stop = parser.parse(false).iterate(false);
assertEquals("return (.[]+(.[]*1000));", stop.toString());
assertEquals(1006L, Evaluator.evaluate(stop, 4));
}
@@ -209,7 +209,7 @@ while( j < nprimes ) {
return rez;
""");
- StopNode stop = parser.parse(false).iterate(true);
+ StopNode stop = parser.parse(false).iterate(false);
assertEquals("return int[];", stop.toString());
Evaluator.Obj obj = (Evaluator.Obj)Evaluator.evaluate(stop, 20);
assertEquals("int[] {\n # :int;\n [] :int;\n}",obj.struct().toString());
@@ -267,86 +267,4 @@ return is[1];
catch( NegativeArraySizeException e ) { assertEquals("-1",e.getMessage()); }
}
- @Test
- public void testBrainfuck() {
- var program = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.".getBytes(StandardCharsets.UTF_8);
- var encoded = new StringBuilder("u8[] program = new u8[").append(program.length).append("];");
- for (int i = 0; i < program.length; i++) {
- int value = program[i] & 0xFF;
- encoded.append("program[").append(i).append("] = ").append(value).append(";");
- }
-
- Parser parser = new Parser(
- encoded + """
-int pc = 0;
-int d = 0;
-u8[] data = new u8[30000];
-int i=0;
-while(i < data#) {
- data[i] = 0;
- i = i + 1;
-}
-
-u8[] output = new u8[0];
-
-while (pc < program#) {
- int command = program[pc];
- if (command == 62) {
- d = d + 1;
- } else if (command == 60) {
- d = d - 1;
- } else if (command == 43) {
- data[d] = data[d] + 1;
- } else if (command == 45) {
- data[d] = data[d] - 1;
- } else if (command == 46) {
- u8[] old = output;
- output = new u8[output# + 1];
- i = 0;
- while (i < old#) {
- output[i] = old[i];
- i = i + 1;
- }
- output[i] = data[d];
- } else if (command == 44) {
- data[d] = 42;
- } else if (command == 91) {
- if (data[d] == 0) {
- int d = 1;
- while (d > 0) {
- pc = pc + 1;
- command = program[pc];
- if (command == 91) d = d + 1;
- if (command == 93) d = d - 1;
- }
- }
- } else if (command == 93) {
- if (data[d] != 0) {
- int d = 1;
- while (d > 0) {
- pc = pc - 1;
- command = program[pc];
- if (command == 93) d = d + 1;
- if (command == 91) d = d - 1;
- }
- }
- }
- pc = pc + 1;
-}
-int result = 0;
-i = 0;
-while (i < 8) {
- result = (result << 8) | data[i];
- i = i + 1;
-}
-return result;
- """);
- StopNode stop = parser.parse(false).iterate(false);
- long expected = 0;
- for (byte b : "Hello Wo".getBytes(StandardCharsets.UTF_8)) {
- expected = (expected << 8) | b;
- }
- assertEquals(expected, Evaluator.evaluate(stop, 1000000));
- }
-
}
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 e59017e7..26860858 100644
--- a/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
+++ b/chapter15/src/test/java/com/seaofnodes/simple/evaluator/Evaluator.java
@@ -115,7 +115,7 @@ public class Evaluator {
int scale = from.struct.aryScale();
long i = ((Long)off - base)>>scale;
if( i < 0 || i >= len )
- throw new ArrayIndexOutOfBoundsException("Array index out of bounds " + off + " <= " + len);
+ throw new ArrayIndexOutOfBoundsException("Array index out of bounds " + i + " < " + len);
return from.fields[(int)i+1];
} else