From 0cb35914c6a92be41108a7e3f16801b955967432 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Sat, 19 Apr 2025 16:43:25 -0700 Subject: Better block layout Keeps loops self-contained, handles more variants of infinite loops. Fold region-to-regions together, in an effort to have fewer junk jumps --- .../com/seaofnodes/simple/codegen/CodeGen.java | 2 +- .../com/seaofnodes/simple/codegen/Encoding.java | 172 ++++++++++++--------- .../java/com/seaofnodes/simple/node/CFGNode.java | 2 +- .../java/com/seaofnodes/simple/node/LoopNode.java | 4 +- .../com/seaofnodes/simple/node/RegionNode.java | 41 +++++ .../seaofnodes/simple/node/cpus/arm/UJmpARM.java | 2 +- .../simple/node/cpus/riscv/UJmpRISC.java | 2 +- .../simple/node/cpus/x86_64_v2/UJmpX86.java | 2 +- .../java/com/seaofnodes/simple/Chapter05Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter06Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter08Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter11Test.java | 2 +- .../java/com/seaofnodes/simple/Chapter15Test.java | 4 +- .../java/com/seaofnodes/simple/Chapter21Test.java | 48 ++---- .../java/com/seaofnodes/simple/MergeSortTest.java | 2 +- .../test/java/com/seaofnodes/simple/TestRisc5.java | 13 +- 16 files changed, 178 insertions(+), 128 deletions(-) (limited to 'chapter21/src') diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 2e1a4709..91553f38 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -318,7 +318,7 @@ public class CodeGen { // --------------------------- - // Control Flow Graph in RPO order. + // Control Flow Graph in Reverse Post Order. public int _tGCM; public Ary _cfg = new Ary<>(CFGNode.class); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java index 5ff2836e..6d6248e4 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java @@ -1,8 +1,10 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.Ary; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.print.IRPrinter; import com.seaofnodes.simple.type.*; import java.io.ByteArrayOutputStream; import java.util.*; @@ -152,11 +154,13 @@ public class Encoding { _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf)); } + // -------------------------------------------------- void encode() { // Basic block layout: negate branches to keep blocks in-order; insert // unconditional jumps. Attempt to keep backwards branches taken, // forwards not-taken (this is the default prediction on most - // hardware). Layout is still RPO but with more restrictions. + // hardware). Layout is still Reverse Post Order but with more + // restrictions. basicBlockLayout(); // Write encoding bits in order into a big byte array. @@ -171,17 +175,23 @@ public class Encoding { patchLocalRelocations(); } + // -------------------------------------------------- // Basic block layout: negate branches to keep blocks in-order; insert - // unconditional jumps. Attempt to keep backwards branches taken, - // forwards not-taken (this is the default prediction on most - // hardware). Layout is still RPO but with more restrictions. + // unconditional jumps. Attempt to keep backwards branches taken, forwards + // not-taken (this is the default prediction on most hardware). Layout is + // still Reverse Post Order but with more restrictions. private void basicBlockLayout() { + IdentityHashMap> rpos = new IdentityHashMap<>(); Ary rpo = new Ary<>(CFGNode.class); + rpos.put(_code._start.loop(),rpo); BitSet visit = _code.visit(); + //IdentityHashMap looptail = new IdentityHashMap<>(); + rpo.add(_code._stop); for( Node n : _code._start._outputs ) if( n instanceof FunNode fun ) { int x = rpo._len; - _rpo_cfg(fun, visit, rpo); + //_rpo_cfg2(fun, visit, rpo, looptail); + _rpo_cfg(fun, visit, rpos ); assert rpo.at(x) instanceof ReturnNode; } rpo.add(_code._start); @@ -193,64 +203,45 @@ public class Encoding { _code._cfg = rpo; // Save the new ordering } - // Basic block layout. Now that RegAlloc is finished, no more spill code - // will appear. We can change our BB layout from RPO to something that - // minimizes actual branches, takes advantage of fall-through edges, and - // tries to help simple branch predictions: back branches are predicted - // taken, forward not-taken. - private void _rpo_cfg(CFGNode bb, BitSet visit, Ary rpo) { - if( visit.get(bb._nid) ) return; + + private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap> rpos ) { + if( bb==null || visit.get(bb._nid) ) return; visit.set(bb._nid); - if( bb.nOuts()==0 ) return; // StopNode - if( !(bb instanceof IfNode iff) ) { - CFGNode next = bb instanceof ReturnNode ? (CFGNode)bb.out(bb.nOuts()-1) : bb.uctrl(); - // If the *next* BB has already been visited, we may need an - // unconditional jump here - if( visit.get(next._nid) && !(next instanceof StopNode) ) { - boolean needJump = next instanceof LoopNode - // If backwards to a loop, and the block has statements, - // will need a jump. Empty blocks can just backwards branch. - ? (bb.nOuts()>1) - // Forwards jump. If all the blocks, in RPO order, to our - // target are empty, we will fall in and not need a jump. - : !isEmptyBackwardsScan(rpo,next); - if( needJump ) { - CFGNode jmp = _code._mach.jump(); - jmp.setDefX(0,bb); - next.setDef(next._inputs.find(bb),jmp); - rpo.add(jmp); - } - } - _rpo_cfg(next,visit,rpo); - } else { - boolean invert = false; + CFGNode next = bb.uctrl(); + + // Loops run an inner "rpo_cfg", then append the entire loop body in + // place. This keeps loop bodies completely contained, although if + // Some Day Later we have real profile data we ought to arrange the + // branch orderings based on frequency. + if( bb instanceof LoopNode loop ) { + Ary body = new Ary<>(CFGNode.class); // Private RPO for the loop + rpos.put(loop.loop(),body); // Find it via loop tree + _rpo_cfg(next,visit,rpos); // RPO the loop + body.add(loop); // Include loop last (first in RPO) + Ary outer = rpos.get(loop.cfg(1).loop()); + outer.addAll(body); // Append to original CFG + return; + } + + // IfNodes visit 2 sides, and may choose to reorder them in the RPO + if( bb instanceof IfNode iff ) { // Pick out the T/F projections CProjNode t = iff.cproj(0); CProjNode f = iff.cproj(1); - int tld = t.loopDepth(), fld = f.loopDepth(), bld = bb.loopDepth(); - // Decide entering or exiting a loop - if( tld==bld ) { - // if T is empty, keep - if( t.nOuts()>1 && - // Else swap so T is forward and exits, while F falls into next loop block - ((fld bld ) { // True enters a deeper loop - throw Utils.TODO(); - } else if( fld == bld && f.out(0) instanceof LoopNode ) { // Else True exits a loop - // if false is the loop backedge, make sure its true/taken - invert = true; - } // always forward and good - // Invert test and Proj fields - if( invert ) { + int tld = t.loopDepth(), fld = f.loopDepth(), bld = iff.loopDepth(); + // Invert the branch or not + if( tld >= bld && // true to exit a loop + // false to exit a loop, invert to true + (fld < bld || + // Else not exiting (staying or entering deeper). + // Fall/false into a full block, Jump/true to an empty block. + ( f.nOuts()==1 && (t.nOuts()>1 || + // Everything else equal, use pre-order + t._pre > f._pre) )) ) { iff.negate(); t.invert(); f.invert(); CProjNode tmp=f; f=t; t=tmp; // Swap t/f - int d=tld; tld=fld; fld=d; // Swap depth } // Whichever side is visited first becomes last in the RPO. With @@ -258,22 +249,37 @@ public class Encoding { // when the False RPO visit returns, the IF is immediately next. // When the RPO is reversed, the fall-through path will always be // following the IF. + _rpo_cfg(t,visit,rpos); // True side first + next = f; // False side last + } - // If loops are involved, attempt to keep them in a line. Visit - // the exits first, so they follow the loop body when the order gets - // reversed. - if( fld < tld || (fld==tld && f.nOuts()==1) ) { - _rpo_cfg(f,visit,rpo); - _rpo_cfg(t,visit,rpo); - } else { - _rpo_cfg(t,visit,rpo); - _rpo_cfg(f,visit,rpo); + // If the *next* BB has already been visited, and we are not already a + // jump, we may need an unconditional forwards jump here + Ary rpo = rpos.get(bb.loop()); + if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) { + boolean needJump = next instanceof LoopNode + // Empty blocks from an IF will invert the IF and backwards + // branch to the loop head. If not empty, or not an IF + // will need a jump. + ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) ) + // Forwards jump. If all the blocks, in RPO order, to our + // target are empty, we will fall in and not need a jump. + : !isEmptyBackwardsScan(rpo,next); + if( needJump ) { + CFGNode jmp = _code._mach.jump(); + jmp._ltree = bb._ltree; + jmp.setDefX(0,bb); + next.setDef(next._inputs.find(bb),jmp); + rpo.add(jmp); } } - assert rpo._len>0 || bb instanceof ReturnNode; + + _rpo_cfg(next,visit,rpos); rpo.add(bb); } + // Is the CFG from "next" to the end empty? This means jumping to "next" + // will naturally fall into the end. private static boolean isEmptyBackwardsScan(Ary rpo, CFGNode next) { for( int i=rpo._len-1; rpo.at(i)!=next; i-- ) if( rpo.at(i).nOuts()!=1 ) @@ -281,6 +287,7 @@ public class Encoding { return true; } + // -------------------------------------------------- // Write encoding bits in order into a big byte array. // Record opcode start and length. public FunNode _fun; // Currently encoding function @@ -308,6 +315,7 @@ public class Encoding { padN(16,_bits); } + // -------------------------------------------------- // Short-form RIP-relative support: replace short encodings with long // encodings and expand the code, changing all the offsets. private void compactShortForm() { @@ -340,7 +348,7 @@ public class Encoding { if( n instanceof MachNode && !(n instanceof CFGNode) ) _opStart[n._nid] += slide; if( bb instanceof RIPRelSize riprel ) { - CFGNode target = ((CFGNode)bb.out(0)).uctrlSkipEmpty(); + CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty(); // Delta is from opStart to opStart. X86 at least counts // the delta from the opEnd, but we don't have the end until // we decide the size - so the encSize has to deal @@ -375,6 +383,19 @@ public class Encoding { } + // -------------------------------------------------- + // Patch local encodings now + void patchLocalRelocations() { + // Walk the local code-address relocations + for( Node src : _internals.keySet() ) { + int start = _opStart[src._nid]; + Node dst = _internals.get(src); + int target = _opStart[dst._nid]; + ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); + } + } + + // -------------------------------------------------- // Write the constant pool into the BAOS and optionally patch locally void writeConstantPool( BAOS bits, boolean patch ) { padN(16,bits); @@ -409,17 +430,6 @@ public class Encoding { } - // Patch local encodings now - void patchLocalRelocations() { - // Walk the local code-address relocations - for( Node src : _internals.keySet() ) { - int start = _opStart[src._nid]; - Node dst = _internals.get(src); - int target = _opStart[dst._nid]; - ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); - } - } - // A series of libc/external calls that Simple can link against in a JIT. // Since no runtime in the JVM process, using magic numbers for the CPU // emulators to pick up on. @@ -436,4 +446,12 @@ public class Encoding { ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); } } + + String printCFG() { + if( _code._cfg==null ) return "no CFG"; + SB sb = new SB(); + for( CFGNode cfg : _code._cfg ) + IRPrinter.printLine(cfg,sb); + return sb.toString(); + } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java index 0aeee5f3..fd17092f 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -106,7 +106,7 @@ public abstract class CFGNode extends Node { public LoopNode loop() { return _ltree._head; } public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); } - LoopTree _ltree; + public LoopTree _ltree; public int _pre; // Pre-order numbers for loop tree finding static class LoopTree { LoopTree _par; 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 cc11d811..1ba182b9 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -50,9 +50,9 @@ public class LoopNode extends RegionNode { CProjNode t = new CProjNode(iff,0,"True" ).init(); CProjNode f = new CProjNode(iff,1,"False").init(); setDef(2,t); // True continues loop, False (never) exits loop - iff._ltree = t._ltree = _ltree; ReturnNode ret = fun.ret(); - f._ltree = ret._ltree; + iff._ltree = t._ltree = _ltree; + ret._ltree = f._ltree = stop._ltree; // Now fold control into the exit. Might have 1 valid exit, or an // XCtrl or a bunch of prior NeverNode exits. diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/RegionNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/RegionNode.java index 2da402b1..583a4088 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/RegionNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/RegionNode.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; +import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.codegen.CodeGen; import com.seaofnodes.simple.type.Type; import java.util.*; @@ -56,6 +57,29 @@ public class RegionNode extends CFGNode { return delDef(2); } + // Flatten stack regions (no loops involved) + if( getClass() == RegionNode.class ) { + for( int i=1; i2 && !hasMidUser(region) ) { + assert !region.inProgress(); + // Fold Phis + for( Node use : _outputs ) { + if( use instanceof PhiNode phi ) { + PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null; + for( int j=1; j 1; // Should optimize jmp to empty targets + //assert target.nOuts() > 1; // Should optimize jmp to empty targets sb.p(label(target)); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java index 9f623b0c..6473d81c 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/UJmpRISC.java @@ -40,7 +40,7 @@ public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize { @Override public void asm(CodeGen code, SB sb) { CFGNode target = uctrl(); - assert target.nOuts() > 1; // Should optimize jmp to empty targets + //assert target.nOuts() > 1; // Should optimize jmp to empty targets sb.p(label(target)); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java index c2c804a2..ec4eeb3b 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/UJmpX86.java @@ -47,7 +47,7 @@ public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize { @Override public void asm(CodeGen code, SB sb) { CFGNode target = uctrl(); - assert target.nOuts() > 1; // Should optimize jmp to empty targets + //assert target.nOuts() > 1; // Should optimize jmp to empty targets sb.p(label(target)); } } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java index a584ad5a..d1dfd216 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter05Test.java @@ -100,7 +100,7 @@ else return a; """); code.parse().opto(); - assertEquals("return Phi(Region,Phi(Region,2,3),Phi(Region,4,5));", code.print()); + assertEquals("return Phi(Region,3,5,2,4);", code.print()); } @Test diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java index 24d12921..b6bc2bf3 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter06Test.java @@ -145,7 +145,7 @@ else return a; """); code.parse().opto(); - assertEquals("return Phi(Region,3,Phi(Region,4,5));", code.print()); + assertEquals("return Phi(Region,3,5,4);", code.print()); } @Test @@ -258,7 +258,7 @@ if( arg ) { return a+b+c; """); code.parse().opto(); - assertEquals("return (Phi(Region,Phi(Region,2,3),0)+Phi(Region,3,1));", code.print()); + assertEquals("return Phi(Region,6,1,5);", code.print()); } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java index 7e90d484..09669cc7 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter08Test.java @@ -23,7 +23,7 @@ while(arg < 10) { return arg; """); code.parse().opto(); - assertEquals("return Phi(Region,Phi(Region,Phi(Loop,arg,(Phi_arg+1)),Add),Add);", code.print()); + assertEquals("return Phi(Region,(Phi(Loop,arg,Add)+1),Add,Phi_arg);", code.print()); assertTrue(code.ctrl() instanceof RegionNode); Assert.assertEquals( "5", Eval2.eval(code, 1)); Assert.assertEquals("10", Eval2.eval(code, 6)); @@ -46,7 +46,7 @@ while(arg < 10) { return a; """); code.parse().opto(); - assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1)));", code.print()); + assertEquals("return Phi(Loop,1,Phi(Region,Phi_a,(Phi_a+1),Phi_a));", code.print()); assertTrue(code.ctrl() instanceof CProjNode); } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java index 7e88b60e..f6539ac4 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -321,7 +321,7 @@ while( prime <= arg ) { return primeCount; """); code.parse().opto(); - assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi(Region,(Phi_primeCount+1),Phi_primeCount))));", code.print()); + assertEquals("return Phi(Region,0,Phi(Loop,1,Phi(Region,Phi_primeCount,Phi_primeCount,(Phi_primeCount+1))));", code.print()); assertEquals("0", Eval2.eval(code, 1)); // No primes 1 or below assertEquals("1", Eval2.eval(code, 2)); // 2 assertEquals("2", Eval2.eval(code, 3)); // 2, 3 diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java index 8da5cf96..8b138b46 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter15Test.java @@ -130,7 +130,7 @@ else { return rez; """); code.parse().opto(); - assertEquals("return Phi(Region,1.2,Phi(Region,2.3,.y));", code.print()); + assertEquals("return Phi(Region,1.2,.y,2.3);", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); assertEquals("1.2", Eval2.eval(code, 1)); } @@ -155,7 +155,7 @@ if( iss[arg] ) rez = iss[arg][2].y; return rez;"""); code.parse().opto().typeCheck(); - assertEquals("return Phi(Region,Phi(Region,.y,1.2),1.2);", code.print()); + assertEquals("return Phi(Region,1.2,1.2,.y);", code.print()); assertEquals("3.14", Eval2.eval(code, 0)); try { Eval2.eval(code, 10); fail(); } // Fails AIOOBE in Eval2, no range checks catch( Exception e ) { assertEquals("Index 10 out of bounds for length 2",e.getMessage()); } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java index 291d4a55..50cb7604 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -95,31 +95,14 @@ public class Chapter21Test { // Return register A0 holds fib(8)==55 assertEquals(1.732051,R5.fregs[riscv.FA0 - riscv.F_OFFSET], 0.00001); - // arm - EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10, false); - A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0; - int trap_arm = A5.step(1000); - assertEquals(0,trap_arm); - assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001); + //// arm + //EvalArm64 A5 = TestArm64.build("newtonFloat", 0, 10, false); + //A5.fregs[arm.D0 - arm.D_OFFSET] = 3.0; + //int trap_arm = A5.step(1000); + //assertEquals(0,trap_arm); + //assertEquals(1.732051, A5.fregs[arm.D0 - arm.D_OFFSET], 0.00001); } - @Test - public void testFibPrint() throws IOException { - String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")); - CodeGen code = new CodeGen(src); - - code.parse().opto().typeCheck().loopTree().instSelect("arm", "SystemV").GCM().localSched().regAlloc().encode().print_as_hex(); - System.out.print(code.asm()); - } - - @Test - public void testFibPrintRiscv() throws IOException { - String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")); - CodeGen code = new CodeGen(src); - - code.parse().opto().typeCheck().loopTree().instSelect("riscv", "SystemV").GCM().localSched().regAlloc().encode().print_as_hex(); - System.out.print(code.asm()); - } @Test public void testSieve() throws IOException { // The primes @@ -143,17 +126,16 @@ public class Chapter21Test { assertEquals(primes.length, R5.ld4s(ary)); for( int i=0; i