From 81b654d76444d747fdf1d053760e507b50f26e18 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Mon, 5 Oct 2026 15:38:06 -0700 Subject: Small peep to drop simple store-updates out of loops --- .../com/seaofnodes/simple/node/MemMergeNode.java | 5 ++- .../com/seaofnodes/simple/node/MemPhiNode.java | 5 +++ .../java/com/seaofnodes/simple/node/StoreNode.java | 40 ++++++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) (limited to 'chapter25/src/main/java/com') diff --git a/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java index 3e618472..dfe8794d 100644 --- a/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java +++ b/chapter25/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java @@ -104,6 +104,9 @@ public class MemMergeNode extends Node { // Collapse stacked merged-mem if( in(1) instanceof MemMergeNode mem ) { + // A dead memory cycle can lose its last use when we bypass mem. + // Keep the peephole's result alive until the worklist sees it. + keep(); // Goal is to swap my default mem with mem's default mem for( int i=2; i Store(mem,ptr,off,Phi(load(mem),val)). + // With no observers of the backedge store, only the last value matters. + // The entry load preserves the field even when the loop takes zero trips. + Node sink(MemPhiNode phi) { + phi.addDepForwards(this); + if( nOuts()!=1 || _alias==1 || _size==0 || _init ) return null; + phi.addDep(ptr()); + // Start with a fixed field of an allocation dominating the loop. + // In particular, do not sink stores through a loop-varying address. + if( !(ptr() instanceof ProjNode p) || !(p.in(0) instanceof NewNode obj) || + !(ptr()._type instanceof TypeMemPtr tmp) || tmp._obj.isAry() || + !(off() instanceof ConstantNode) || err()!=null ) return null; + LoopNode loop = (LoopNode)phi.region(); + // Bulk splitting identifies precise slices by Region/alias. Finish it + // before replacing this memory point with a Store. + for( Node use : loop._outputs ) + if( use instanceof BulkMemPhiNode ) { + phi.addDepForwards(use); + return null; + } + phi.addDep(obj); + CFGNode ctrl = loop.entry(); + while( ctrl!=null && ctrl!=obj.cfg0() ) { + phi.addDep(ctrl); + ctrl=ctrl.idom(phi); + } + if( ctrl==null ) return null; + + Node mem = phi.in(1); + Node init = new LoadNode(_loc,_name,_alias,null,mem,ptr(),off()).peephole(); + Node value = CodeGen.CODE.add(new PhiNode(_name,loop,init,val()).peephole()); + Node sink = new StoreNode(_loc,_name,_alias,_con,null,mem,ptr(),off(),value,_init,_size); + // Break the memory cycle, killing the old store. Keep the Phi alive + // even if that store was its last use, until our caller replaces it. + phi.keep(); + phi.setDef(2,mem); + phi.unkeep(); + return sink; + } + // Check that "mem" has no uses except "this" private boolean checkOnlyUse(Node mem) { if( mem.nOuts()==1 ) return true; -- cgit v1.2.3