diff options
| author | Cliff Click <cliffc@acm.org> | 2024-10-04 12:58:50 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2024-10-06 08:56:22 -0700 |
| commit | 9edb193559f48f78f3040d96147d0baea84abde7 (patch) | |
| tree | aae8154fdec71a45563d9775dffba861705160cb /chapter15 | |
| parent | 37bc8c4cc5269eb4be281025b48583331f3008b1 (diff) | |
| download | Simple-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')
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 "φ_"+_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 |
