From 798bd959410eb6c2e0b9f7ae1222181ac2b295d4 Mon Sep 17 00:00:00 2001 From: Darrick Wiebe Date: Sat, 12 Apr 2025 13:40:20 -0400 Subject: MemStore interface (#201) * Hook for pre-encoding work needed by M2 * Add MemStore interface * Add both MemLoad and MemStore interfaces * Fix NPE printing StopNode * Attach merged mem to return when forcing a loop exit * Made requested changes. This breaks a few tests, possibly because... ...I am not sure whether some X86 ops are loads or stores. * Mach ops need anti-deps too MemOp subclasses are either "Load-like" or "Store-like" based on a flag, and not on subclassing LoadNode nor StoreNode. Mach ops pick up anti-deps, no reg mask for those. x86 bug: do not allow "dst = [mem] + #imm", no such instruction Empty merge-mem folds away. x86 add-to-mem works both ways --------- Co-authored-by: Cliff Click --- .../com/seaofnodes/simple/codegen/BuildLRG.java | 5 ++- .../simple/codegen/GlobalCodeMotion.java | 38 ++++++++++------------ .../java/com/seaofnodes/simple/codegen/IFG.java | 2 +- .../java/com/seaofnodes/simple/codegen/LRG.java | 5 --- .../java/com/seaofnodes/simple/node/LoadNode.java | 2 +- .../java/com/seaofnodes/simple/node/LoopNode.java | 20 ++++++++++-- .../com/seaofnodes/simple/node/MemMergeNode.java | 9 ++++- .../java/com/seaofnodes/simple/node/MemOpNode.java | 28 +++++++++++++--- .../java/com/seaofnodes/simple/node/StopNode.java | 3 +- .../java/com/seaofnodes/simple/node/StoreNode.java | 2 +- .../seaofnodes/simple/node/cpus/arm/LoadARM.java | 4 +-- .../seaofnodes/simple/node/cpus/arm/MemOpARM.java | 2 +- .../simple/node/cpus/riscv/StoreRISC.java | 2 +- .../simple/node/cpus/x86_64_v2/AddMemX86.java | 5 ++- .../simple/node/cpus/x86_64_v2/MemOpX86.java | 7 ++-- .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 30 +++++++++-------- 16 files changed, 102 insertions(+), 62 deletions(-) (limited to 'chapter21/src/main') diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java index ca5157c7..a2bb21ed 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java @@ -1,6 +1,5 @@ package com.seaofnodes.simple.codegen; -import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.TypeMem; @@ -55,8 +54,8 @@ abstract public class BuildLRG { if( n.in(i)!=null ) { LRG lrg2 = alloc.lrg(n.in(i)); if( lrg2 != null ) { // Anti-dep or other, no LRG - RegMask use_mask = mach.regmap(i); - if( !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) + RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep + if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) alloc.fail(lrg2); // Empty register mask, must split } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index ee8c0259..e904dc66 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -160,7 +160,7 @@ public abstract class GlobalCodeMotion { continue outer; // Nope, await all uses done // Loads need their memory inputs' uses also done - if( n instanceof LoadNode ld ) + if( n instanceof MemOpNode ld && ld._isLoad ) for( Node memuse : ld.mem()._outputs ) if( late[memuse._nid]==null && // New makes new memory, never crushes load memory @@ -180,8 +180,8 @@ public abstract class GlobalCodeMotion { 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 ) + for( Node out : def._outputs ) + if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) work.push(ld); } } @@ -197,7 +197,7 @@ public abstract class GlobalCodeMotion { lca = use_block(n,use, late)._idom(lca,null); // Loads may need anti-dependencies, raising their LCA - if( n instanceof LoadNode load ) + if( n instanceof MemOpNode load && load._isLoad ) lca = find_anti_dep(lca,load,early,late); // Walk up from the LCA to the early, looking for best place. This is @@ -235,7 +235,7 @@ public abstract class GlobalCodeMotion { (lca.idepth() > best.idepth() || best instanceof IfNode); } - private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) { + private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) { // We could skip final-field loads here. // Walk LCA->early, flagging Load's block location choices for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) @@ -243,10 +243,12 @@ public abstract class GlobalCodeMotion { // Walk load->mem uses, looking for Stores causing an anti-dep 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 MemOpNode st: + if( !st._isLoad ) { + assert late[mem._nid] != null; + lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st ); + } + break; // Loads do not cause anti-deps on other loads case CallNode st: assert late[st._nid]!=null; lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); @@ -259,7 +261,6 @@ public abstract class GlobalCodeMotion { lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null); break; case NewNode st: break; - case LoadNode ld: break; // Loads do not cause anti-deps on other loads case ReturnNode ret: break; // Load must already be ahead of Return case MemMergeNode ret: break; // Mem uses now on ScopeMin case NeverNode never: break; @@ -270,16 +271,13 @@ public abstract class GlobalCodeMotion { } // - private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { - // Walk store blocks "reach" from its scheduled location to its earliest - for( ; stblk != defblk.idom(); stblk = stblk.idom() ) { - // Store and Load overlap, need anti-dependence - if( stblk._anti==load._nid ) { - lca = stblk._idom(lca,null); // Raise Loads LCA - if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, - st.addDef(load); // Add anti-dep as well - return lca; // Cap this stores' anti-dep to here - } + private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { + // Store and Load overlap, need anti-dependence + if( stblk._anti==load._nid ) { + lca = stblk._idom(lca,null); // Raise Loads LCA + if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, + st.addDef(load); // Add anti-dep as well + return lca; // Cap this stores' anti-dep to here } return lca; } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java index 34b755b0..c1b9d3e4 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java @@ -174,7 +174,7 @@ abstract public class IFG { // Look for a must-use single register conflicting with some other must-def. if( n instanceof MachNode m ) { RegMask ni_mask = m.regmap(i); - if( ni_mask.size1() ) { // Must-use single register + if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register // Search all current live for( LRG tlrg : TMP.keySet() ) { assert tlrg.leader(); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java index 018f18ab..ec254a2e 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/LRG.java @@ -45,11 +45,6 @@ public class LRG { public MachNode _machDef, _machUse; short _uidx; // _machUse input - // Mask set to empty via a kill-mask. This is usually a capacity kill, - // which means we need to split and spill into memory - which means even if - // the def-side has many registers, it MUST spill. - boolean _killed; - // Some splits used in biased coloring MachConcreteNode _splitDef, _splitUse; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/LoadNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/LoadNode.java index e5174823..b8ddeca4 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/LoadNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/LoadNode.java @@ -20,7 +20,7 @@ public class LoadNode extends MemOpNode { * @param off The offset inside the struct base */ public LoadNode(Parser.Lexer loc, String name, int alias, Type glb, Node mem, Node ptr, Node off) { - super(loc, name, alias, glb, mem, ptr, off); + super(loc, name, alias, true, glb, mem, ptr, off); } // GraphVis DOT code (must be valid Java identifiers) and debugger labels diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java index 3a57fde7..778f2a8d 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -59,6 +59,22 @@ public class LoopNode extends RegionNode { // Now fold control into the exit. Might have 1 valid exit, or an // XCtrl or a bunch of prior NeverNode exits. Node top = new ConstantNode(Type.TOP).peephole(); + Node memout = new MemMergeNode(false); + memout.addDef(null); // placeholder for control + for( Node u : _outputs ) + if( u instanceof PhiNode phi && phi._type.isa(TypeMem.BOT) ) + memout.addDef(phi); + if( memout.nIns()>2 ) + memout.setDef(0, ret.cfg0()); + else { + if( memout.nIns()==1 ) memout=top; + else { + Node tmp=memout; + memout=memout.in(1); + tmp.setDef(1, null); + } + } + Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr(); if( ctrl!=null && ctrl._type != Type.XCONTROL ) { // Perfect aligned exit? @@ -73,11 +89,11 @@ public class LoopNode extends RegionNode { } // Append new Never exit ctrl.addDef(f ); - mem .addDef(top); + mem .addDef(memout); expr.addDef(top); } else { ctrl = f; - mem = top; + mem = memout; expr = top; } ret.setDef(0,ctrl); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java index 40859eea..32c25a8b 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/MemMergeNode.java @@ -65,11 +65,18 @@ public class MemMergeNode extends Node { if( inProgress() ) return null; // If not merging any memory (all memory is just the default) - if( nIns()==2 ) + if( allDefault() ) return in(1); // Become default memory return null; } + private boolean allDefault() { + for( int i=2; i= 3) - return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val); + return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs); // if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) { // throw Utils.TODO(); // Swap load sides @@ -476,7 +476,7 @@ public class x86_64_v2 extends Machine { // Attempt a full LEA-style break down. // Returns one of AddX86, AddIX86, LeaX86, or LHS - if( rhs instanceof ConstantNode off && off._con instanceof TypeInteger toff ) { + if( rhs instanceof ConstantNode off2 && off2._con instanceof TypeInteger toff ) { long imm = toff.value(); assert imm!=0; // Folded in peeps if( (int)imm != imm ) // Full 64bit immediate @@ -639,21 +639,25 @@ public class x86_64_v2 extends Machine { return new ShrX86(shr); } - private Node st (StoreNode st ){ + private Node st( StoreNode st ) { // Look for "*ptr op= val" Node op = st.val(); - if(op instanceof AddNode) { - if(op.in(1) instanceof LoadNode ld && - ld.in(0) == st.in(0) && - ld.mem() == st.mem() && - ld.ptr() == st.ptr() && - ld.off() == st.off()) { - return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); - } + if( op instanceof AddNode ) { + if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); + if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val); } - return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val); } + private static boolean stld_match(StoreNode st, LoadNode ld ) { + return + ld.in(0) == st.in(0) && + ld.mem() == st.mem() && + ld.ptr() == st.ptr() && + ld.off() == st.off(); + } + private Node sub (SubNode sub ){ return sub.in(2) instanceof ConstantNode con && con._con instanceof TypeInteger ti @@ -699,7 +703,7 @@ public class x86_64_v2 extends Machine { return mop; } - private int imm (Node xval ){ + private int imm( Node xval ) { assert val == null && imm == 0; if( xval instanceof ConstantNode con && con._con instanceof TypeInteger ti) { val = null; -- cgit v1.2.3