diff options
| author | Cliff Click <cliffc@acm.org> | 2025-05-08 14:44:38 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-05-08 14:44:38 -0700 |
| commit | 30c60eeea76687bcf267937e50cc0cc944f420c1 (patch) | |
| tree | 1e786ac2b006c85063c26603639903bc121b3732 | |
| parent | f1c1c070f0491cba8b18f3169e72e9a1df8f1a2a (diff) | |
| download | Simple-30c60eeea76687bcf267937e50cc0cc944f420c1.tar.gz Simple-30c60eeea76687bcf267937e50cc0cc944f420c1.zip | |
Missed updates from ch21 forward ported to ch22
38 files changed, 166 insertions, 145 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java index 3a99c097..bd4cbf35 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -288,7 +288,7 @@ public class RegAlloc { // all registers anyways. if( rclass._len <= 1 || ncalls > 1 ) return false; - // Split by classh + // Split by class Ary<Node> ns = new Ary<>(Node.class); for( RegMask rmask : rclass ) { ns.addAll(def._outputs); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index ae9cdcd9..7a0f3313 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -21,7 +21,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize { @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i,fun()._maxArgSlot); } - @Override public RegMask outregmap() { return null; } + @Override public RegMask outregmap() { return riscv.RPC_MASK; } @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java index c626c14e..d8a5cebc 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -12,13 +12,12 @@ import static org.junit.Assert.*; public class Chapter21Test { - @Test + @Test @Ignore public void testJig() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); - CodeGen code = new CodeGen(src).driver("x86_64_v2","Win64","build/objs/jigS.o"); - //testCPU(src,"x86_64_v2", "Win64" ,-1,null); - //testCPU(src,"riscv" , "SystemV",-1,null); - //testCPU(src,"arm" , "SystemV",-1,null); + testCPU(src,"x86_64_v2", "Win64" ,-1,null); + testCPU(src,"riscv" , "SystemV",-1,null); + testCPU(src,"arm" , "SystemV",-1,null); } static void testCPU( String src, String cpu, String os, int spills, String stop ) { @@ -65,7 +64,7 @@ public class Chapter21Test { @Test public void testString() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp")); - //testCPU(src,"x86_64_v2", "SystemV", 9,null); + testCPU(src,"x86_64_v2", "SystemV", 9,null); testCPU(src,"riscv" , "SystemV", 5,null); testCPU(src,"arm" , "SystemV", 6,null); } @@ -180,7 +179,7 @@ public class Chapter21Test { int p2 = ps+4*8+2*8; EvalRisc5 R5 = TestRisc5.build("person", ps, 0, false); R5.regs[riscv.A1] = 1; // Index 1 - R5.st4(ps,3); // Length + R5.st8(ps,3); // Length R5.st8(ps+1*8,p0); R5.st8(ps+2*8,p1); R5.st8(ps+3*8,p2); @@ -196,7 +195,7 @@ public class Chapter21Test { EvalArm64 A5 = TestArm64.build("person", ps, 0, false); A5.regs[arm.X1] = 1; // Index 1 - A5.st4(ps, 3); + A5.st8(ps, 3); A5.st8(ps+1*8,p0); A5.st8(ps+2*8,p1); A5.st8(ps+3*8,p2); diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp index b82b3f42..1daeee09 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp @@ -1,64 +1,2 @@ // -*- mode: java; -*- -// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort - -val merge_sort = { int[] a, int[] b, int n -> - copy_array (a, 0, n, b); - split_merge(a, 0, n, b); -}; - -val split_merge = { int[] b, int begin, int end, int[] a -> - if (end - begin <= 1) - return 0; - int middle = (end + begin) / 2; - split_merge(a, begin, middle, b); - split_merge(a, middle, end, b); - merge(b, begin, middle, end, a); - return 0; -}; - -val merge = { int[] b, int begin, int middle, int end, int[] a -> - int i = begin, j = middle; - - for (int k = begin; k < end; k++) { - // && and || - bool cond = false; - if (i < middle) { - if (j >= end) cond = true; - else if (a[i] <= a[j]) cond = true; - } - if (cond) b[k] = a[i++]; - else b[k] = a[j++]; - } -}; - -val copy_array = { int[] a, int begin, int end, int[] b -> - for (int k = begin; k < end; k++) - b[k] = a[k]; -}; - -val eq = { int[] a, int[] b, int n -> - int result = 1; - int i = 0; - while (i < n) { - if (a[i] != b[i]) { - result = 0; - break; - } - i = i + 1; - } - return result; -}; - -val main = { -> - int[] !a = new int[10]; - a[0] = 10; a[1] = 9; a[2] = 8; a[3] = 7; a[4] = 6; - a[5] = 5; a[6] = 4; a[7] = 3; a[8] = 2; a[9] = 1; - int[] !b = new int[10]; - b[0] = 0; b[1] = 0; b[2] = 0; b[3] = 0; b[4] = 0; - b[5] = 0; b[6] = 0; b[7] = 0; b[8] = 0; b[9] = 0; - int[] !expect = new int[10]; - expect[0] = 1; expect[1] = 2; expect[2] = 3; expect[3] = 4; expect[4] = 5; - expect[5] = 6; expect[6] = 7; expect[7] = 8; expect[8] = 9; expect[9] = 10; - merge_sort(a, b, 10); - return eq(a,expect,10); -}; +return 0; diff --git a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java index 21d6c359..07fbdd5b 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/Parser.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/Parser.java @@ -653,7 +653,8 @@ public class Parser { // Make finals deep; widen ints to floats; narrow wide int types. // Early error if types do not match variable. private Node liftExpr( Node expr, Type t, boolean xfinal ) { - assert !(expr._type instanceof TypeMemPtr tmp) || !tmp.isFRef(); + if( expr._type instanceof TypeMemPtr tmp && tmp.isFRef() ) + throw error("Must define forward ref "+tmp._obj._name); // Final is deep on ptrs if( xfinal && t instanceof TypeMemPtr tmp ) { t = tmp.makeRO(); diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java index a2bb21ed..e9b5eb2e 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java @@ -63,7 +63,7 @@ abstract public class BuildLRG { } // MultiNodes have projections which set registers - if( n instanceof MultiNode ) + if( n instanceof MultiNode && !(n instanceof CFGNode) ) for( Node proj : n.outs() ) if( proj instanceof MachNode ) defLRG(alloc,proj); diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 74fa1f71..874f850b 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -89,6 +89,24 @@ public class CodeGen { public int getALIAS() { return _alias++; } + // idepths are cached and valid until *inserting* CFG edges (deleting is + // OK). This happens with inlining, which bumps the version to bulk + // invalidate the idepth caches. + private int _iDepthVersion = 0; + public void invalidateIDepthCaches() { _iDepthVersion++; } + public boolean validIDepth(int idepth) { + if( idepth==0 ) return false; + if( _iDepthVersion==0 ) return true; + return (idepth%100)==_iDepthVersion; + } + public int iDepthAt(int idepth) { + return 100*idepth+_iDepthVersion; + } + public int iDepthFrom(int idepth) { + assert idepth==0 || validIDepth(idepth); + return idepth+100; + } + // Popular visit bitset, declared here, so it gets reused all over public final BitSet _visit = new BitSet(); public BitSet visit() { assert _visit.isEmpty(); return _visit; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java index f4b2ab69..7a43f099 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -322,7 +322,6 @@ public class Encoding { if( !(bb instanceof MachNode mach0) ) _opStart[bb._nid] = _bits.size(); else if( bb instanceof FunNode fun ) { - padN(16,_bits); _fun = fun; // Currently encoding function _opStart[bb._nid] = _bits.size(); mach0.encoding( this ); @@ -336,7 +335,6 @@ public class Encoding { } } } - padN(16,_bits); } // -------------------------------------------------- diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java index f76b35ef..3de66626 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java @@ -166,10 +166,10 @@ public abstract class GlobalCodeMotion { !(memuse instanceof NewNode) && // Load-use directly defines memory (memuse._type instanceof TypeMem || + // Load-use directly defines memory + memuse instanceof CallNode || // Load-use indirectly defines memory - (memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem) || - // Load-use use/defs memory - memuse instanceof CallNode) ) + (memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) ) continue outer; // All uses done, schedule @@ -201,6 +201,33 @@ public abstract class GlobalCodeMotion { if( n instanceof MemOpNode load && load._isLoad ) lca = find_anti_dep(lca,load,early,late); + + // Nodes setting a single register and getting killed will stay close + // to the uses, since they will be forced to spill anyway. The kill + // check is very weak, and some may be hoisted only to spill in the RA. + if( n instanceof MachNode mach ) { + RegMask out = mach.outregmap(); + if( out!=null && out.size1() ) { + int reg = mach.outregmap().firstReg(); + // Look for kills + outer: + for( CFGNode lca2=lca; lca2 != early; lca2 = lca2.idom() ) { + if( lca2 instanceof MachNode mach2 ) { + for( int i=1; i<lca2.nIns(); i++ ) { + RegMask mask = mach2.regmap(i); + if( mask!=null && mask.test(reg) ) { + early = lca2 instanceof IfNode ? lca2.idom() : lca2; + break outer; + } + } + RegMask kill = mach2.killmap(); + if( kill != null ) + throw Utils.TODO(); + } + } + } + } + // Walk up from the LCA to the early, looking for best place. This is // the lowest execution frequency, approximated by least loop depth and // deepest control flow. @@ -232,8 +259,9 @@ public abstract class GlobalCodeMotion { // Least loop depth first, then largest idepth private static boolean better( CFGNode lca, CFGNode best ) { return lca.loopDepth() < best.loopDepth() || - lca instanceof NeverNode || - (lca.idepth() > best.idepth() || best instanceof IfNode); + lca instanceof NeverNode || + lca.idepth() > best.idepth() || + best instanceof IfNode; } private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) { @@ -250,9 +278,9 @@ public abstract class GlobalCodeMotion { 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); + case CallNode call: + assert late[call._nid]!=null; + lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call); break; case PhiNode phi: // Repeat anti-dep for matching Phi inputs. diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/IFG.java index 81955779..fdd4e2f9 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/IFG.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/IFG.java @@ -242,14 +242,12 @@ abstract public class IFG { private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) { CFGNode bb = priorbb.cfg(i); if( bb == null ) return; // Start has no prior - if( !bb.blockHead() ) bb = bb.cfg0(); - //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0(); - assert bb.blockHead(); + while( !bb.blockHead() ) bb = bb.cfg0(); // Lazy get live-out set for bb - IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() ); + IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() ); - for( LRG lrg : TMP.keySet() ) { + for( LRG lrg : TMP.keySet() ) { Node def = TMP.get(lrg); // Effective def comes from phi input from prior block if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/Machine.java index 1c12bfcb..2766414f 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/Machine.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -16,8 +16,12 @@ abstract public class Machine { public abstract long callerSave(); // List of never-save registers, e.g. RSP or a ZERO register if you have one public abstract long neverSave(); - // Call Argument Mask - public abstract RegMask callArgMask(TypeFunPtr tfp, int arg); + // Call Argument Mask. Passed in the function signature and argument + // number (2-based; 0 is for control and 1 for memory). Also passed in a 0 + // for the function itself, or for *outgoing* calls, the maximum stack slot + // given to the incoming function arguments (stack slots reserved for + // incoming arguments). + public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot); // Return register mask, based on signature (GPR vs FPR) public abstract RegMask retMask(TypeFunPtr tfp); // Return PC register diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java index e4a48750..f68caf91 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -262,7 +262,7 @@ public class RegAlloc { } // Single-def live range with an empty mask. There are many single-reg - // uses. Theory is there's many repeats if the same reg amongst the uses. + // uses. Theory is there's many repeats of the same reg amongst the uses. // In of splitting once per use, start by splitting into groups based on // required input register. boolean splitEmptyMaskByUse( byte round, LRG lrg ) { @@ -271,20 +271,27 @@ public class RegAlloc { // Look at each use, and break into non-overlapping register classes. Ary<RegMask> rclass = new Ary<>(RegMask.class); boolean done=false; + int ncalls=0; while( !done ) { done = true; for( Node use : def._outputs ) - if( use instanceof MachNode mach ) + if( use instanceof MachNode mach ) { + if( mach instanceof CallNode ) ncalls++; for( int i=1; i<use.nIns(); i++ ) if( use.in(i)==def ) done = putIntoRegClass( rclass, mach.regmap(i) ); + } } - // See how many register classes we split into - if( rclass._len <= 1 ) return false; + // See how many register classes we split into. Generally not + // productive to split like this across calls, which are going to kill + // all registers anyways. + if( rclass._len <= 1 || ncalls > 1 ) return false; // Split by class + Ary<Node> ns = new Ary<>(Node.class); for( RegMask rmask : rclass ) { + ns.addAll(def._outputs); Node split = makeSplit(def,"popular",round,lrg,rmask); split.insertAfter( def ); if( split.nIns()>1 ) split.setDef(1,def); @@ -295,11 +302,15 @@ public class RegAlloc { // Check all use inputs for n, in case there's several for( int i = 1; i < use.nIns(); i++ ) // Find a def input, and check register class - if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) ) - // Modify use to use the split version specialized to this rclass - { use.setDef( i, split ); j--; break; } + if( use.in( i ) == def && mach.regmap( i ).overlap( rmask ) ) { + RegMask m = mach.regmap( i ); + if( m!=null && mach.regmap( i ).overlap( rmask ) ) + // Modify use to use the split version specialized to this rclass + { use.setDefOrdered( i, split ); j--; break; } + } } } + ns.clear(); } return true; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegMask.java index 79de3fbf..ec0c4e30 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegMask.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/codegen/RegMask.java @@ -4,11 +4,11 @@ import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.SB; /** RegMask - * A "register mask" - 1 bit set for each allowed register. In addition + * A "register mask" - 1 bit set for each allowed register. In addition, * "stack slot" registers may be allowed, effectively making the set infinite. * <p> * For smaller and simpler machines it suffices to make such masks an i64 or - * i128 (64 or 128 bit integers), and this presentation is by far the better + * i128 (64- or 128-bit integers), and this presentation is by far the better * way to go... if all register allocations can fit in this bit limitation. * The allocator will need bits for stack-based parameters and for splits * which cannot get a register. For a 32-register machine like the X86, add 1 @@ -28,7 +28,7 @@ public class RegMask { public RegMask(int bit) { if( bit < 64 ) _bits0 = 1L<<bit; - else _bits1 = 1L<<(bit=64); + else _bits1 = 1L<<(bit-64); } public RegMask(long bits ) { _bits0 = bits; } public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/CFGNode.java index 96597648..1dddc171 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -3,6 +3,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Ary; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.*; import java.util.BitSet; import java.util.HashMap; @@ -72,7 +73,9 @@ public abstract class CFGNode extends Node { * See {@link <a href="https://en.wikipedia.org/wiki/Dominator_(graph_theory)">...</a>} */ public int _idepth; - public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; } + public int idepth() { + return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); + } // Return the immediate dominator of this Node and compute dom tree depth. public CFGNode idom(Node dep) { return cfg(0); } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index e1d8aee5..7a331deb 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -69,8 +69,9 @@ public class CallEndNode extends CFGNode implements MultiNode { assert fun.in(1) instanceof StartNode && fun.in(2)==call; // Disallow self-recursive inlining (loop unrolling by another name) CFGNode idom = call; - while( !(idom instanceof FunNode fun2) ) + while( !(idom instanceof FunNode) ) idom = idom.idom(); + // Inline? if( idom != fun ) { // Trivial inline: rewrite _folding = true; @@ -80,6 +81,9 @@ public class CallEndNode extends CFGNode implements MultiNode { fun.setDef(2,call.ctrl()); // Bypass the Call; fun.ret().setDef(3,null); // Return is folding also CodeGen.CODE.addAll(fun._outputs); + // Inlining immediately blows all cache idepth fields past the inline point. + // Bump the global version number invalidating them en-masse. + CodeGen.CODE.invalidateIDepthCaches(); return this; } } else { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/FunNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/FunNode.java index c469fc96..f19e1cd6 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -119,8 +119,11 @@ public class FunNode extends RegionNode { return null; } - // Bypass Region idom, always assume depth == 1, one more than Start - @Override public int idepth() { return (_idepth=1); } + // Bypass Region idom, always assume depth == 1, one more than Start, + // unless folding then just a ID on input#1 + @Override public int idepth() { + return _folding ? super.idepth() : CodeGen.CODE.iDepthAt(1); + } // Bypass Region idom, always assume idom is Start @Override public CFGNode idom(Node dep) { return cfg(1); } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/LoopNode.java index cacf9c99..ec9055dc 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -26,7 +26,9 @@ public class LoopNode extends RegionNode { } // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) - @Override public int idepth() { return _idepth==0 ? (_idepth=idom().idepth()+1) : _idepth; } + @Override public int idepth() { + return CodeGen.CODE.validIDepth(_idepth) ? _idepth : (_idepth=CodeGen.CODE.iDepthFrom(idom().idepth())); + } // Bypass Region idom, same as the default idom() using use in(1) instead of in(0) @Override public CFGNode idom(Node dep) { return entry(); } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/NewNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/NewNode.java index 3d4bad5e..5a8c1b6b 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -90,8 +90,8 @@ public class NewNode extends Node implements MultiNode { private RegMask _retMask; private RegMask _kills; public void cacheRegs(CodeGen code) { - _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2).firstReg(); - _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3); + _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0).firstReg(); + _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0); // Return mask depends on TFP (either GPR or FPR) _retMask = code._mach.retMask(TypeFunPtr.CALLOC); // Kill mask is all caller-saves, and any mirror stack slots for args diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/RegionNode.java index 8c56feff..8f65d0e5 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -141,12 +141,13 @@ public class RegionNode extends CFGNode { // Immediate dominator of Region is a little more complicated. @Override public int idepth() { - if( _idepth!=0 ) return _idepth; + if( CodeGen.CODE.validIDepth(_idepth) ) + return _idepth; int d=0; for( Node n : _inputs ) if( n!=null ) - d = Math.max(d,((CFGNode)n).idepth()+1); - return _idepth=d; + d = Math.max(d,CodeGen.CODE.iDepthFrom(((CFGNode)n).idepth())); + return _idepth = d; } @Override public CFGNode idom(Node dep) { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/StartNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/StartNode.java index 4bd76f50..f40203b4 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/StartNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/StartNode.java @@ -51,7 +51,7 @@ public class StartNode extends LoopNode implements MultiNode { @Override public Node idealize() { return null; } // No immediate dominator, and idepth==0 - @Override public int idepth() { return 0; } + @Override public int idepth() { return CodeGen.CODE.iDepthAt(0); } @Override public CFGNode idom(Node dep) { return null; } } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter22/src/main/java/com/seaofnodes/simple/node/StoreNode.java index 0beef65c..4b73ac1b 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/StoreNode.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/StoreNode.java @@ -84,6 +84,12 @@ public class StoreNode extends MemOpNode { } } + // Store of zero after alloc + if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode && + prj.in(0)==ptr().in(0) && // Same NewNode memory & pointer + (val()._type==TypeInteger.ZERO || val()._type==Type.NIL ) ) + return mem(); + // TODO: Needs more love: the shift amount depends on the store size //if( val() instanceof SarNode shr && // shr.in(1) instanceof ShlNode shl && @@ -114,8 +120,6 @@ public class StoreNode extends MemOpNode { TypeMemPtr tmp = (TypeMemPtr)ptr()._type; if( tmp._obj.field(_name)._final && !_init ) return Parser.error("Cannot modify final field '"+_name+"'",_loc); - Type t = val()._type; - //return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc); return null; } } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java index d5e12b7f..989bd366 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java @@ -21,7 +21,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize { @Override public String label() { return op(); } @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } - @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i); } + @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); } @Override public RegMask outregmap() { return null; } @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java index b3f55659..87dde38c 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java @@ -11,7 +11,7 @@ public class CallRRARM extends CallNode implements MachNode { @Override public RegMask regmap(int i) { return i==_inputs._len ? arm.RMASK // Function call target - : arm.callInMask(tfp(),i); // Normal argument + : arm.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument } @Override public RegMask outregmap() { return null; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java index 4d0f95ab..b419fe0a 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java @@ -9,7 +9,7 @@ public class ParmARM extends ParmNode implements MachNode { final RegMask _rmask; ParmARM(ParmNode parm) { super(parm); - _rmask = arm.callInMask(fun().sig(),_idx); + _rmask = arm.callInMask(fun().sig(),_idx,0); } @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return _rmask; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index 88a2e8c5..e6f64158 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -563,10 +563,11 @@ public class arm extends Machine { return (opcode << 26) | delta; } - @Override public RegMask callArgMask(TypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } - static RegMask callInMask(TypeFunPtr tfp, int idx ) { + @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { if( idx==0 ) return CodeGen.CODE._rpcMask; if( idx==1 ) return null; + // Count floats in signature up to index if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence // Count floats in signature up to index int fcnt=0; @@ -789,8 +790,7 @@ public class arm extends Machine { private static int off; private static Node idx; private Node st(StoreNode st) { - Node xval = st.val() instanceof ConstantNode con && con._con == TypeInteger.ZERO ? null : st.val(); - return new StoreARM(address(st),st.ptr(),idx,off,xval); + return new StoreARM(address(st),st.ptr(),idx,off,st.val()); } // Gather addressing mode bits prior to constructing. This is a builder diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index daef60f2..89b2c865 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java @@ -21,7 +21,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize { @Override public String label() { return op(); } @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } - @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i); } + @Override public RegMask regmap(int i) { return riscv.callInMask(_tfp,i,fun()._maxArgSlot); } @Override public RegMask outregmap() { return riscv.RPC_MASK; } @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java index 2afbbfa9..dcc53cbf 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java @@ -11,7 +11,7 @@ public class CallRRISC extends CallNode implements MachNode { @Override public RegMask regmap(int i) { return i==_inputs._len-1 ? riscv.RMASK // Function call target - : riscv.callInMask(tfp(),i); // Normal argument + : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument } @Override public RegMask outregmap() { return null; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java index 02d53560..0f8db4e5 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java @@ -9,7 +9,7 @@ public class ParmRISC extends ParmNode implements MachNode { final RegMask _rmask; ParmRISC(ParmNode parm) { super(parm); - _rmask = riscv.callInMask(fun().sig(),_idx); + _rmask = riscv.callInMask(fun().sig(),_idx,0); } @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return _rmask; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java index d34d94fc..7305c4d9 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/StoreRISC.java @@ -36,7 +36,7 @@ public class StoreRISC extends MemOpRISC { short ptr = enc.reg(ptr()); int op = val >= riscv.F_OFFSET ? riscv.OP_STOREFP : riscv.OP_STORE; if( val >= riscv.F_OFFSET ) val -= riscv.F_OFFSET; - enc.add4(riscv.s_type(op, func3()&7, ptr, val, _off)); + enc.add4(riscv.s_type(op, func3()&7, ptr, val == -1 ? riscv.ZERO : val, _off)); } @Override public void asm(CodeGen code, SB sb) { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index c1ad1647..8e722563 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -252,8 +252,8 @@ public class riscv extends Machine { }; } - @Override public RegMask callArgMask( TypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } - static RegMask callInMask( TypeFunPtr tfp, int idx ) { + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { if( idx==0 ) return RPC_MASK; if( idx==1 ) return null; if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence @@ -272,6 +272,7 @@ public class riscv extends Machine { return cargs[idx-2-fcnt]; } // Pass on stack slot(8 and higher) + if( maxArgSlot>0 ) throw Utils.TODO(); return new RegMask(MAX_REG + 1 + (idx - 2)); } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java index b9d4fa8d..1a74f817 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java @@ -12,7 +12,7 @@ public class CallRX86 extends CallNode implements MachNode { @Override public RegMask regmap(int i) { return i==_inputs._len ? x86_64_v2.WMASK // Function call target - : x86_64_v2.callInMask(tfp(),i); // Normal argument + : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument } @Override public RegMask outregmap() { return null; } @Override public void encoding( Encoding enc ) { diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java index 3110dbb4..4696c859 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java @@ -20,7 +20,7 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize { @Override public String label() { return op(); } @Override public String name() { return _name; } @Override public TypeFunPtr tfp() { return _tfp; } - @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i); } + @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot); } @Override public RegMask outregmap() { return null; } @Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java index 7a6dbb13..5cfdd804 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java @@ -9,7 +9,7 @@ public class ParmX86 extends ParmNode implements MachNode { final RegMask _rmask; ParmX86( ParmNode parm ) { super(parm); - _rmask = x86_64_v2.callInMask(fun().sig(),_idx); + _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/); } @Override public RegMask regmap(int i) { return null; } @Override public RegMask outregmap() { return _rmask; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java index f965157b..960ea6f8 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java @@ -49,9 +49,16 @@ public class SplitX86 extends SplitNode { // Stack spills if( dst >= x86_64_v2.MAX_REG ) { - if( src >= x86_64_v2.MAX_REG ) - throw Utils.TODO(); // Very rare stack-stack move int off = enc._fun.computeStackOffset(enc._code,dst); + if( src >= x86_64_v2.MAX_REG ) { + // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff] + int soff = enc._fun.computeStackOffset(enc._code,src); + enc.add1(0xFF); + x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc); + enc.add1(0x8F); + x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, off, 0, enc); + return; + } StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0); return; } diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java index c906c3f2..76fb4e8d 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -44,7 +44,7 @@ public class StoreX86 extends MemOpX86 { case 0: enc.add1(_imm); break; case 1: enc.add2(_imm); break; case 2: enc.add4(_imm); break; - case 3: enc.add8(_imm); break; + case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate } } else { encVal(enc,_declaredType,ptr,idx,src,_off,_scale); diff --git a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index 35541346..60e10d79 100644 --- a/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/chapter22/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -185,14 +185,14 @@ public class x86_64_v2 extends Machine { // Map from function signature and argument index to register. // Used to set input registers to CallNodes, and ParmNode outputs. - @Override public RegMask callArgMask( TypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } - static RegMask callInMask( TypeFunPtr tfp, int idx ) { + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); } + static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { if( idx==0 ) return RPC_MASK; if( idx==1 ) return null; if( idx-2 >= tfp.nargs() ) return null; // Anti-dependence return switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> callSys5 (tfp,idx); - case "Win64" -> callWin64(tfp,idx); + case "SystemV" -> callSys5 (tfp,idx,maxArgSlot); + case "Win64" -> callWin64(tfp,idx,maxArgSlot); default -> throw Utils.TODO(); }; } @@ -231,11 +231,11 @@ public class x86_64_v2 extends Machine { R09_MASK, }; - static RegMask callWin64(TypeFunPtr tfp, int idx ) { + static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) { // idx 2,3,4,5 passed in registers, with stack slot mirrors. // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC). if( idx >= 6 ) - return new RegMask(MAX_REG+1/*RPC*/+(idx-2)); + return new RegMask(MAX_REG+maxArgSlot+(idx-2)); return tfp.arg(idx-2) instanceof TypeFloat ? XMMS8 [idx-2] : WIN64_CALL[idx-2]; @@ -272,7 +272,7 @@ public class x86_64_v2 extends Machine { R09_MASK, }; - static RegMask callSys5(TypeFunPtr tfp, int idx ) { + static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) { // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09 // First 8 floats passed in registers: xmm0-xmm7 int icnt=0, fcnt=0; // Count of ints, floats @@ -282,8 +282,8 @@ public class x86_64_v2 extends Machine { } int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); return tfp.arg(idx-2) instanceof TypeFloat - ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+1/*RPC*/+nstk) - : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+1/*RPC*/+nstk); + ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk) + : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk); } static short maxArgSlotSys5(TypeFunPtr tfp) { int icnt=0, fcnt=0; // Count of ints, floats diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java index 96e6c5ff..e589d5a4 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -6,12 +6,13 @@ import com.seaofnodes.simple.node.cpus.riscv.riscv; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import org.junit.Ignore; import org.junit.Test; import static org.junit.Assert.*; public class Chapter21Test { - @Test + @Test @Ignore public void testJig() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); testCPU(src,"x86_64_v2", "Win64" ,-1,null); diff --git a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java index baca507c..4b15124c 100644 --- a/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java +++ b/chapter22/src/test/java/com/seaofnodes/simple/Chapter22Test.java @@ -13,7 +13,7 @@ import static org.junit.Assert.*; public class Chapter22Test { - @Test + @Test @Ignore public void testJig() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp")); testCPU(src,"x86_64_v2", "Win64" ,-1,null); |
