summaryrefslogtreecommitdiff
path: root/chapter24/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2026-10-19 10:40:35 -0700
committerCliff Click <cliffc@acm.org>2026-10-19 10:40:35 -0700
commite7eb2db06d1b9c529f94f2da3a087c83c0498e05 (patch)
tree17a21219c7a28017b361bdeec011dda0afc562dc /chapter24/src
parentef8d9c4630db6af79c25526c8e80c54a48df4e39 (diff)
downloadSimple-e7eb2db06d1b9c529f94f2da3a087c83c0498e05.tar.gz
Simple-e7eb2db06d1b9c529f94f2da3a087c83c0498e05.zip
Turn off Store GVN with explaination
Diffstat (limited to 'chapter24/src')
-rw-r--r--chapter24/src/main/java/com/seaofnodes/simple/node/StoreNode.java9
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java23
-rw-r--r--chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java25
3 files changed, 55 insertions, 2 deletions
diff --git a/chapter24/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter24/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index 4e1d7ce3..47f12d82 100644
--- a/chapter24/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter24/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -171,4 +171,13 @@ public class StoreNode extends MemOpNode {
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
return null;
}
+
+ // Do not GVN Stores. Two equal Stores on two of three paths are the easy
+ // failure: sharing them makes GCM place the Store on all three paths.
+ // Sharing is safe only if every exit path goes through an original Store,
+ // or GCM splits the shared Store to keep it on those paths. Stores must
+ // sink, not run on extra paths. GVN does not check this; GCM does not split.
+ // Identity also disables generic Phi factoring of Stores.
+ // The closed-loop Store sink is a separate peep and still applies.
+ @Override public boolean eq(Node n) { return this == n; }
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java
index 40c69a4f..afe0b141 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter10Test.java
@@ -8,6 +8,24 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
public class Chapter10Test {
+ @Test public void testConditionalStoreSharing() {
+ // Sharing the writes on two of three paths must not write on the third.
+ // Return the object so Load forwarding cannot hide the physical Store.
+ String src = """
+ struct S { int x; };
+ !S !a = new S;
+ a.x = 10;
+ if (arg == 1) a.x = 9;
+ else if (arg == 2) a.x = 9;
+ return a;
+ """;
+ var code = new CodeGen(src).driver(CodeGen.Phase.LocalSched);
+ for( int arg : new int[]{0,1,2,3} ) {
+ long expected = (arg==1 || arg==2) ? 9 : 10;
+ assertEquals("S{x="+expected+"}",Eval2.eval(code,arg));
+ }
+ }
+
@Test public void testDeadReferenceReturn() {
for( String body : new String[] {
"return new S; return 0;",
@@ -548,7 +566,7 @@ return 0;
assertEquals("3",Eval2.eval(code,3));
}
- @Test public void testDropStores() {
+ @Test public void testKeepBranchStores() {
var code = new CodeGen("""
struct S { int x; };
!S !s = new S;
@@ -556,7 +574,8 @@ return 0;
return s;
""").driver(CodeGen.Phase.FinalizeIR);
StopNode stop = code._stop;
- assertEquals(1,countMemoryNodes(stop,StoreNode.class,new BitSet()));
+ // Keep each Store on its own branch.
+ assertEquals(2,countMemoryNodes(stop,StoreNode.class,new BitSet()));
assertEquals("S{x=2}",Eval2.eval(code,0));
assertEquals("S{x=4}",Eval2.eval(code,3));
}
diff --git a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
index 77e75449..d2fa40f3 100644
--- a/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
+++ b/chapter24/src/test/java/com/seaofnodes/simple/Chapter17bTest.java
@@ -8,6 +8,31 @@ import static org.junit.Assert.fail;
public class Chapter17bTest {
+ @Test public void testSinkLoopStores() {
+ var code = new CodeGen("""
+ struct S { int x; };
+ !S !a = new S;
+ a.x = 10;
+ int i = 0;
+ while (i < arg) { a.x = i + 1; i++; }
+ return a;
+ """).driver(CodeGen.Phase.LocalSched);
+ int[] stores = {0};
+ code._stop.walk(n -> {
+ if( n instanceof com.seaofnodes.simple.node.StoreNode st && st._name.equals("x") ) {
+ assertEquals(0,st.cfg0().loopDepth()); // One final Store outside the loop.
+ stores[0]++;
+ }
+ return null;
+ });
+ assertEquals(1,stores[0]);
+ for( int arg : new int[]{-1,0,1,4} ) {
+ long expected = arg<=0 ? 10 : arg;
+ assertEquals("S{x="+expected+"}",Eval2.eval(code,arg));
+ }
+ }
+
+
@Ignore
@Test
public void testJig() {