From 300d2a9c3672acacc8bdd8d5370a424651a00571 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sun, 18 Oct 2026 22:09:58 -0700 Subject: Cleanup & backport The phase rework, made new phases explicit and add them to the CodeGen driver. Fix the flag-spilling on X86 and ARM. Fix the GCM conservative anti-deps and backport. --- .../com/seaofnodes/simple/codegen/CodeGen.java | 233 +++++++-------------- .../simple/codegen/GlobalCodeMotion.java | 11 +- .../com/seaofnodes/simple/codegen/InstSelect.java | 69 ++++++ .../java/com/seaofnodes/simple/codegen/LRG.java | 2 + .../com/seaofnodes/simple/codegen/RegAlloc.java | 6 +- .../com/seaofnodes/simple/codegen/RegMask.java | 2 +- .../com/seaofnodes/simple/codegen/TypeCheck.java | 15 ++ .../java/com/seaofnodes/simple/node/SplitNode.java | 2 + .../simple/node/cpus/arm/SplitFlagsARM.java | 13 ++ .../simple/node/cpus/arm/SplitGPRARM.java | 11 + .../com/seaofnodes/simple/node/cpus/arm/arm.java | 13 +- .../simple/node/cpus/x86_64_v2/SplitFlagsX86.java | 12 ++ .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 8 +- .../com/seaofnodes/simple/print/ASMPrinter.java | 2 +- .../simple/print/SimpleGraphObserver.java | 2 +- 15 files changed, 226 insertions(+), 175 deletions(-) create mode 100644 chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java create mode 100644 chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java create mode 100644 chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java create mode 100644 chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java create mode 100644 chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java (limited to 'chapter20/src/main/java') diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 78cf4c06..09ac516b 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -8,7 +8,6 @@ import com.seaofnodes.simple.print.*; import com.seaofnodes.simple.type.*; import java.util.BitSet; import java.util.HashMap; -import java.util.IdentityHashMap; public class CodeGen { public GraphObserver _obs; @@ -27,17 +26,59 @@ public class CodeGen { public static CodeGen CODE; public enum Phase { - Parse, // Parse ASCII text into Sea-of-Nodes IR - Opto, // Run ideal optimizations - TypeCheck, // Last check for bad programs - InstSelect, // Convert to target hardware nodes - Schedule, // Global schedule (code motion) nodes - LocalSched, // Local schedule - RegAlloc, // Register allocation - Encoding, // Encoding + Parse, // Build ideal IR from source + Iter, // Pessimistic peepholes + TypeCheck, // Check the surviving ideal graph + LoopTree, // Discover loops and connect nonreturning paths + InstSelect, // Select machine instructions + Schedule, // Global code motion + LocalSched, // Order instructions within blocks + RegAlloc; // Assign registers and stack slots + } public Phase _phase; + public static final String PORTS = "com.seaofnodes.simple.node.cpus"; + + // Run through target, skipping phases already completed. + public CodeGen driver(Phase target) { return driver(target,null,null); } + public CodeGen driver(Phase target, String cpu, String callingConv) { return driver(target,cpu,callingConv,0); } + public CodeGen driver(Phase target, String cpu, String callingConv, int dump) { + if( target.ordinal()>=Phase.RegAlloc.ordinal() && cpu==null && _mach==null ) + throw new IllegalArgumentException("Machine code requires a CPU target"); + step(target, Phase.Parse, this::parse, dump); + step(target, Phase.Iter, this::iter, dump); + step(target, Phase.TypeCheck, this::typeCheck, dump); + step(target, Phase.LoopTree, this::loopTree, dump); + step(target, Phase.InstSelect, () -> instSelect(PORTS,cpu,callingConv), dump); + step(target, Phase.Schedule, this::GCM, dump); + step(target, Phase.LocalSched, this::localSched, dump); + step(target, Phase.RegAlloc, this::regAlloc, dump); + return this; + } + + private void step(Phase target, Phase next, Runnable work, int dump) { + if( next.ordinal()>target.ordinal() || (_phase!=null && _phase.ordinal()>=next.ordinal()) ) return; + Phase prior = _phase; + work.run(); + if( _phase!=prior && (dump & (1<<_phase.ordinal()))!=0 ) { + if( (dump & (1<<30))!=0 ) System.err.println("After "+_phase+":"); + System.err.println(this); + } + } + + public final long[] _times = new long[Phase.values().length]; + private CodeGen phase(Phase next, boolean ordered, Runnable work) { + assert ordered : "Cannot run "+next+" after "+_phase; + _phase = next; + long t0 = System.currentTimeMillis(); + try { work.run(); } + finally { _times[next.ordinal()] = System.currentTimeMillis()-t0; } + if( _obs!=null ) _obs.phase(next.name()); + return this; + } + + // --------------------------- // Compilation source code public final String _src; @@ -120,16 +161,10 @@ public class CodeGen { public final Parser P; // Parse ASCII text into Sea-of-Nodes IR - public int _tParse; public CodeGen parse() { - assert _phase == null; - _phase = Phase.Parse; - long t0 = System.currentTimeMillis(); - - P.parse(); - _tParse = (int)(System.currentTimeMillis() - t0); - if( _obs != null ) _obs.phase("Parse"); - return this; + return phase(Phase.Parse, _phase==null, () -> { + P.parse(); + }); } @@ -144,49 +179,15 @@ public class CodeGen { public void iterCnt() { if( !_midAssert ) _iter_cnt++; } public void iterNop() { if( !_midAssert ) _iter_nop_cnt++; } - // Run ideal optimizations - public int _tOpto; - public CodeGen opto() { - assert _phase == Phase.Parse; - _phase = Phase.Opto; - long t0 = System.currentTimeMillis(); - - // Pessimistic peephole optimization on a worklist - _iter.iterate(this); - _tOpto = (int)(System.currentTimeMillis() - t0); - - // TODO: - // Optimistic - // TODO: - // loop unroll, peel, RCE, etc - if( _obs != null ) _obs.phase("Opto"); - return this; - } + // Pessimistic peepholes reach a fixed point after parsing. + public CodeGen iter() { return phase(Phase.Iter, _phase==Phase.Parse, () -> _iter.iterate(this)); } public N add( N n ) { return (N)_iter.add(n); } public void addAll( Ary ary ) { _iter.addAll(ary); } // --------------------------- // Last check for bad programs - int _tTypeCheck; - public CodeGen typeCheck() { - // Demand phase Opto for cleaning up dead control flow at least, - // required for the following GCM. - assert _phase.ordinal() <= Phase.Opto.ordinal(); - _phase = Phase.TypeCheck; - long t0 = System.currentTimeMillis(); - - Parser.ParseException err = _stop.walk(n -> { - Parser.ParseException msg = n.err(); - if( msg != null && _obs != null ) _obs.error(n, msg.getMessage()); - return msg; - }); - _tTypeCheck = (int)(System.currentTimeMillis() - t0); - if( err != null ) - throw err; - if( _obs != null ) _obs.phase("TypeCheck"); - return this; - } + public CodeGen typeCheck() { return phase(Phase.TypeCheck, _phase==Phase.Iter, () -> TypeCheck.run(this)); } // --------------------------- @@ -196,118 +197,44 @@ public class CodeGen { public String _callingConv; // Convert to target hardware nodes - public int _tInsSel; public CodeGen instSelect( String base, String cpu, String callingConv ) { - assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); - _phase = Phase.InstSelect; - - _callingConv = callingConv; - - // Not timing the class load... - // Look for CPU in fixed named place: - // com.seaofnodes.simple.node.cpus."cpu"."cpu.class" - String clzFile = base+"."+cpu+"."+cpu; - try { _mach = ((Class) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); } - catch( Exception e ) { throw new RuntimeException(e); } - - // Convert to machine ops - long t0 = System.currentTimeMillis(); - _uid = 1; // All new machine nodes reset numbering - var map = new IdentityHashMap(); - _instSelect( _stop, map ); - _stop = ( StopNode)map.get(_stop ); - _start = (StartNode)map.get(_start); - _instOuts(_stop,visit()); - _visit.clear(); - _tInsSel = (int)(System.currentTimeMillis() - t0); - return this; - } - - // Walk all ideal nodes, recursively mapping ideal to machine nodes, then - // make a machine node for "this". - private Node _instSelect( Node n, IdentityHashMap map ) { - if( n==null ) return null; - Node x = map.get(n); - if( x !=null ) return x; // Been there, done that - - // If n is a MachNode already, then its part of a multi-node expansion. - // It does not need instruction selection (already selected!) - // but it does need its inputs walked. - if( n instanceof MachNode ) { - for( int i=0; i < n.nIns(); i++ ) - n._inputs.set(i, _instSelect(n.in(i),map) ); - return n; - } - - // Produce a machine node from n; map it to flag as done so stops cycles. - map.put(n, x=_mach.instSelect(n) ); - // Walk machine op and replace inputs with mapped inputs - for( int i=0; i < x.nIns(); i++ ) - x._inputs.set(i, _instSelect(x.in(i),map) ); - if( x instanceof MachNode mach ) - mach.postSelect(); // Post selection action - - // Updates forward edges only. - n._outputs.clear(); - return x; - } - - // Walk all machine Nodes, and set their output edges - private void _instOuts( Node n, BitSet visit ) { - if( visit.get(n._nid) ) return; - visit.set(n._nid); - for( Node in : n._inputs ) - if( in!=null ) { - in._outputs.push(n); - _instOuts(in,visit); - } + if( cpu==null ) return this; + return phase(Phase.InstSelect, _phase==Phase.LoopTree, + () -> new InstSelect(this).run(cpu,callingConv,base)); } // --------------------------- // Control Flow Graph in RPO order. - int _tGCM; public Ary _cfg = new Ary<>(CFGNode.class); // Global schedule (code motion) nodes - public CodeGen GCM() { - assert _phase.ordinal() <= Phase.InstSelect.ordinal(); - _phase = Phase.Schedule; - long t0 = System.currentTimeMillis(); + public CodeGen loopTree() { + return phase(Phase.LoopTree, _phase==Phase.TypeCheck, () -> _start.buildLoopTree(_stop)); + } - // Build the loop tree, fix never-exit loops - _start.buildLoopTree(_stop); - GlobalCodeMotion.buildCFG(this); - _tGCM = (int)(System.currentTimeMillis() - t0); + public CodeGen GCM() { + return phase(Phase.Schedule, (_phase==Phase.LoopTree || _phase==Phase.InstSelect), () -> { + GlobalCodeMotion.buildCFG(this); - return this; + }); } // --------------------------- // Local (basic block) scheduler phase, a classic list scheduler - int _tLocal; public CodeGen localSched() { - assert _phase == Phase.Schedule; - _phase = Phase.LocalSched; - long t0 = System.currentTimeMillis(); - ListScheduler.sched(this); - _tLocal = (int)(System.currentTimeMillis() - t0); - return this; - } + return phase(Phase.LocalSched, _phase==Phase.Schedule, () -> ListScheduler.sched(this)); + } // --------------------------- // Register Allocation - int _tRegAlloc; public RegAlloc _regAlloc; public CodeGen regAlloc() { - assert _phase == Phase.LocalSched; - _phase = Phase.RegAlloc; - long t0 = System.currentTimeMillis(); - _regAlloc = new RegAlloc(this); - _regAlloc.regAlloc(); - _tRegAlloc = (int)(System.currentTimeMillis() - t0); - return this; + return phase(Phase.RegAlloc, _phase==Phase.LocalSched, () -> { + _regAlloc = new RegAlloc(this); + _regAlloc.regAlloc(); + }); } public String reg(Node n) { if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { @@ -317,22 +244,6 @@ public class CodeGen { return "N"+ n._nid; } - // --------------------------- - // Encoding - int _tEncode; - public CodeGen encode() { - assert _phase == Phase.RegAlloc; - _phase = Phase.Encoding; - long t0 = System.currentTimeMillis(); - - - //_tEncode = (int)(System.currentTimeMillis() - t0); - //throw Utils.TODO(); - return this; - } - // Encoded binary, no relocation info - public byte[] binary() { throw Utils.TODO(); } - // --------------------------- SB asm(SB sb) { return ASMPrinter.print(sb,this); } public String asm() { return asm(new SB()).toString(); } diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index 52906afb..225e5fb8 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -273,10 +273,11 @@ public abstract class GlobalCodeMotion { lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st,anti); break; case PhiNode phi: - // Repeat anti-dep for matching Phi inputs. - // No anti-dep edges but may raise the LCA. + // Inputs carrying different memory might overwrite this alias. + // Raise the LCA for those paths; unchanged memory does not + // clobber the load. No scheduling edge to the Phi. for( int i=1; i= defblk.idepth(); stblk = stblk.idom() ) { // Store and Load overlap, need anti-dependence if( anti[stblk._nid]==load._nid ) { lca = stblk.domLCA(lca,null); // Raise Loads LCA diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java new file mode 100644 index 00000000..074349e6 --- /dev/null +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/InstSelect.java @@ -0,0 +1,69 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.node.*; +import java.util.BitSet; +import java.util.IdentityHashMap; + +// Replace ideal nodes with machine nodes, then rebuild the copied graph's uses. +class InstSelect { + private final CodeGen _code; + InstSelect(CodeGen code) { _code=code; } + + @SuppressWarnings("unchecked") + void run(String cpu, String callingConv, String base) { + _code._callingConv = callingConv; + + // Look for CPU in fixed named place: + // com.seaofnodes.simple.node.cpus."cpu"."cpu.class" + String clzFile = base+"."+cpu+"."+cpu; + try { _code._mach = ((Class) Class.forName( clzFile )).getDeclaredConstructor().newInstance(); } + catch( Exception e ) { throw new RuntimeException(e); } + + // Convert to machine ops + _code._uid = 1; // All new machine nodes reset numbering + var map = new IdentityHashMap(); + _instSelect( _code._stop, map ); + _code._stop = ( StopNode)map.get(_code._stop ); + _code._start = (StartNode)map.get(_code._start); + _instOuts(_code._stop,_code.visit()); + _code._visit.clear(); + } + + private Node _instSelect( Node n, IdentityHashMap map ) { + if( n==null ) return null; + Node x = map.get(n); + if( x !=null ) return x; // Been there, done that + + // If n is a MachNode already, then its part of a multi-node expansion. + // It does not need instruction selection (already selected!) + // but it does need its inputs walked. + if( n instanceof MachNode ) { + for( int i=0; i < n.nIns(); i++ ) + n._inputs.set(i, _instSelect(n.in(i),map) ); + return n; + } + + // Produce a machine node from n; map it to flag as done so stops cycles. + map.put(n, x=_code._mach.instSelect(n) ); + // Walk machine op and replace inputs with mapped inputs + for( int i=0; i < x.nIns(); i++ ) + x._inputs.set(i, _instSelect(x.in(i),map) ); + if( x instanceof MachNode mach ) + mach.postSelect(); // Post selection action + + // Updates forward edges only. + n._outputs.clear(); + return x; + } + + private void _instOuts( Node n, BitSet visit ) { + if( visit.get(n._nid) ) return; + visit.set(n._nid); + for( Node in : n._inputs ) + if( in!=null ) { + in._outputs.push(n); + _instOuts(in,visit); + } + } + +} diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java index 5803cad8..8ccb5b34 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/LRG.java @@ -54,6 +54,8 @@ public class LRG { // Adjacent Live Range neighbors. Only valid during coloring Ary _adj; + public RegMask useMask() { return _machUse==null ? null : _machUse.regmap(_uidx); } + void addNeighbor(LRG lrg) { if( _adj==null ) _adj = new Ary<>(LRG.class); _adj.push(lrg); diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java index af28cbb3..ddf7b697 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -346,7 +346,7 @@ public class RegAlloc { // If the minLoopDepth is less than the maxLoopDepth: for-all defs and // uses, if at minLoopDepth or lower, split after def and before use. for( Node n : _ns ) { - if( n instanceof SplitNode ) continue; // Ignoring splits; since spilling need to split in a deeper loop + if( n instanceof SplitNode split && !split.isNonStackSplit() ) continue; // Ordinary splits already reach stack storage. if( n.isDead() ) continue; // Some Clonable went dead by other spill changes // If this is a 2-address commutable op (e.g. AddX86, MulX86) and the rhs has only a single user, // commute the inputs... which chops the LHS live ranges' upper bound to just the RHS. @@ -397,8 +397,8 @@ public class RegAlloc { } private static long ldepth( long ld, Node n, CFGNode cfg ) { - // Do not count splits - if( n instanceof SplitNode ) return ld; + // Ordinary splits already reach stack storage; register-only transfers do not. + if( n instanceof SplitNode split && !split.isNonStackSplit() ) return ld; // Collect min/max loop depth int min = (int)ld; int max = (int)(ld>>32); diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java index 5227c11e..80dcde61 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/RegMask.java @@ -81,7 +81,7 @@ public class RegMask { boolean isEmpty() { return _bits0==0 && _bits1==0; } - boolean test( int reg ) { + public boolean test( int reg ) { return ((reg<64 ? (_bits0 >> reg) : (_bits1 >> (reg-64))) & 1) != 0; } diff --git a/chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java b/chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java new file mode 100644 index 00000000..0997fd2f --- /dev/null +++ b/chapter20/src/main/java/com/seaofnodes/simple/codegen/TypeCheck.java @@ -0,0 +1,15 @@ +package com.seaofnodes.simple.codegen; + +import com.seaofnodes.simple.Parser; + +abstract class TypeCheck { + static void run(CodeGen code) { + Parser.ParseException err = code._stop.walk(n -> { + Parser.ParseException msg = n.err(); + if( msg != null && code._obs != null ) code._obs.error(n, msg.getMessage()); + return msg; + }); + if( err != null ) + throw err; + } +} diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java b/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java index 07166a7c..74639df5 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/node/SplitNode.java @@ -10,6 +10,8 @@ public abstract class SplitNode extends MachConcreteNode { public final String _kind; // Kind of split public final byte _round; public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; } + // A register-only transfer may need another split to reach stack storage. + public boolean isNonStackSplit() { return false; } @Override public String op() { return "mov"; } @Override protected ExprPrinter _print1(ExprPrinter p) { p.p("mov("); diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java new file mode 100644 index 00000000..6783d126 --- /dev/null +++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitFlagsARM.java @@ -0,0 +1,13 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; + +// NZCV transfers require a GPR. Further splits can spill that GPR to the stack. +public class SplitFlagsARM extends SplitARM { + private static final RegMask MASK = new RegMask(arm.WR_BITS,1L); + SplitFlagsARM(String kind,byte round) { super(kind,round); } + @Override public boolean isNonStackSplit() { return true; } + @Override public RegMask regmap(int i) { return MASK; } + @Override public RegMask outregmap() { return MASK; } + +} diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java new file mode 100644 index 00000000..46eff4ca --- /dev/null +++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitGPRARM.java @@ -0,0 +1,11 @@ +package com.seaofnodes.simple.node.cpus.arm; + +import com.seaofnodes.simple.codegen.*; + +// Keep a saved NZCV value in GPRs and stack slots through subsequent spills. +public class SplitGPRARM extends SplitARM { + private static final RegMask MASK = new RegMask(arm.WR_BITS,-2L); + SplitGPRARM(String kind,byte round) { super(kind,round); } + @Override public RegMask regmap(int i) { return MASK; } + @Override public RegMask outregmap() { return MASK; } +} diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index 72254310..543e6636 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -55,7 +55,7 @@ public class arm extends Machine { // Load/store mask; both GPR and FPR static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); - static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -1L); + static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/); static final RegMask FLAGS_MASK = new RegMask(FLAGS); // x30 (LR): Procedure link register, used to return from subroutines. @@ -169,7 +169,16 @@ public class arm extends Machine { } // Create a split op; any register to any register, including stack slots - @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); } + @Override public SplitNode split(String kind, byte round, LRG lrg) { + RegMask def = lrg._machDef==null ? null : lrg._machDef.outregmap(), use = lrg.useMask(); + if( (def!=null && def.size1() && def.test(FLAGS)) || + (use!=null && use.size1() && use.test(FLAGS)) ) + return new SplitFlagsARM(kind,round); + // Spilling the saved flags requires a GPR between NZCV and the stack. + if( lrg._machDef instanceof SplitFlagsARM || lrg._machDef instanceof SplitGPRARM ) + return new SplitGPRARM(kind,round); + return new SplitARM(kind,round); + } // Return a MachNode unconditional branch @Override public CFGNode jump() { diff --git a/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java new file mode 100644 index 00000000..3785bf3f --- /dev/null +++ b/chapter20/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitFlagsX86.java @@ -0,0 +1,12 @@ +package com.seaofnodes.simple.node.cpus.x86_64_v2; + +import com.seaofnodes.simple.codegen.*; + +// Flag snapshots can use GPRs or stack slots, but never XMM registers. +public class SplitFlagsX86 extends SplitX86 { + private static final RegMask MASK = new RegMask(x86_64_v2.WR_BITS | (1L< { if( n instanceof MemMergeNode ) return true; return n.getClass()==ConstantNode.class; } - @Override public boolean postAlloc() { return _code._phase.ordinal()>CodeGen.Phase.RegAlloc.ordinal(); } + @Override public boolean postAlloc() { return _code._phase.ordinal()>=CodeGen.Phase.RegAlloc.ordinal(); } } diff --git a/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java b/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java index 9dce0b75..a6418589 100644 --- a/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java +++ b/chapter20/src/main/java/com/seaofnodes/simple/print/SimpleGraphObserver.java @@ -17,7 +17,7 @@ public class SimpleGraphObserver extends GraphCapture { @Override protected void compile(String src) { _code = new CodeGen(src); _code._obs = this; - _code.parse().opto().typeCheck(); + _code.driver(CodeGen.Phase.TypeCheck); } @Override protected String phase() { return _code._phase.name(); } @Override protected int pos() { return _code._phase == CodeGen.Phase.Parse ? _code.P.pos() : -1; } -- cgit v1.2.3