diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-13 10:23:18 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-04-13 10:23:18 -0700 |
| commit | 1e28b27ec2d1129d4a219a57ec0c661c74720493 (patch) | |
| tree | 74ba9d8cd8081b574b3d177d0d3955fa2dbdb121 /chapter21/src | |
| parent | 0ac2721f28c729c06c28fce14f8f863f8c57e41b (diff) | |
| download | Simple-1e28b27ec2d1129d4a219a57ec0c661c74720493.tar.gz Simple-1e28b27ec2d1129d4a219a57ec0c661c74720493.zip | |
Handle infinite loops
Loop tree building (and breaking infinite loops) needs to happen *before* instruction selection, which is optional. Best done as a new compiler phase... which now wants a driver.
Add a compiler driver; adjust tests to use.
Add "never-branch" handling to the 3 CPU port main branch ops.
Diffstat (limited to 'chapter21/src')
27 files changed, 246 insertions, 124 deletions
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 114d0b5c..fb46a6a2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -20,6 +20,7 @@ public class CodeGen { Parse, // Parse ASCII text into Sea-of-Nodes IR Opto, // Run ideal optimizations TypeCheck, // Last check for bad programs + LoopTree, // Build a loop tree; break infinite loops InstSelect, // Convert to target hardware nodes Schedule, // Global schedule (code motion) nodes LocalSched, // Local schedule @@ -49,6 +50,28 @@ public class CodeGen { P = new Parser(this,arg); } + + // All passes up to Phase, except ELF + public CodeGen driver( Phase phase ) { return driver(phase,null,null); } + public CodeGen driver( Phase phase, String cpu, String callingConv ) { + if( _phase==null ) parse(); + if( _phase.ordinal() < phase.ordinal() ) opto(); + if( _phase.ordinal() < phase.ordinal() ) typeCheck(); + if( _phase.ordinal() < phase.ordinal() ) loopTree(); + if( _phase.ordinal() < phase.ordinal() && cpu != null ) instSelect(cpu,callingConv); + if( _phase.ordinal() < phase.ordinal() ) GCM(); + if( _phase.ordinal() < phase.ordinal() ) localSched(); + if( _phase.ordinal() < phase.ordinal() ) regAlloc(); + if( _phase.ordinal() < phase.ordinal() ) encode(); + return this; + } + + // Run all the phases through final ELF emission + public CodeGen driver( String cpu, String callingConv, String obj ) throws IOException { + return driver(Phase.Encoding,cpu,callingConv).exportELF(obj); + } + + // --------------------------- /** * A counter, for unique node id generation. Starting with value 1, to @@ -172,18 +195,30 @@ public class CodeGen { return this; } + // --------------------------- + // Build the loop tree; break never-exit loops + public int _tLoopTree; + public CodeGen loopTree() { + assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); + _phase = Phase.LoopTree; + long t0 = System.currentTimeMillis(); + // Build the loop tree, fix never-exit loops + _start.buildLoopTree(_stop); + _tLoopTree = (int)(System.currentTimeMillis() - t0); + return this; + } // --------------------------- // Code generation CPU target public Machine _mach; - // + // Chosen calling convention (usually either Win64 or SystemV) public String _callingConv; // Convert to target hardware nodes public int _tInsSel; public CodeGen instSelect( String cpu, String callingConv ) { return instSelect(cpu,callingConv,PORTS); } public CodeGen instSelect( String cpu, String callingConv, String base ) { - assert _phase.ordinal() <= Phase.TypeCheck.ordinal(); + assert _phase.ordinal() == Phase.LoopTree.ordinal(); _phase = Phase.InstSelect; _callingConv = callingConv; @@ -261,8 +296,6 @@ public class CodeGen { _phase = Phase.Schedule; long t0 = System.currentTimeMillis(); - // Build the loop tree, fix never-exit loops - _start.buildLoopTree(_stop); GlobalCodeMotion.buildCFG(this); _tGCM = (int)(System.currentTimeMillis() - t0); if( show ) 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 0b97c574..0aeee5f3 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java @@ -57,7 +57,7 @@ public abstract class CFGNode extends Node { // Used by the encoding / final BB layout public CFGNode uctrlSkipEmpty() { - CFGNode x = this, y=null; + CFGNode x = this, y; while( x.nOuts() == 1 && (y=x.uctrl())!=null ) // Skip empty blocks x = y; return x; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java index f00f4f9f..9bb269d5 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java @@ -21,7 +21,9 @@ public class IfNode extends CFGNode implements MultiNode { @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { sb.append("if( "); - return in(1)._print0(sb, visited).append(" )"); + if( in(1)==null ) sb.append("never"); + else in(1)._print0(sb, visited); + return sb.append(" )"); } public Node ctrl() { return in(0); } 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 778f2a8d..847a4aa0 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -51,7 +51,7 @@ public class LoopNode extends RegionNode { : CodeGen.CODE._mach.never(back()); // Machine never-branch CProjNode t = new CProjNode(iff,0,"True" ).init(); CProjNode f = new CProjNode(iff,1,"False").init(); - setDef(2,t); + setDef(2,t); // True continues loop, False (never) exits loop iff._ltree = t._ltree = _ltree; ReturnNode ret = fun.ret(); f._ltree = ret._ltree; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java index 1dcad1db..516f218b 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java @@ -9,6 +9,7 @@ import java.util.BitSet; // "Never true" for infinite loop exits public class NeverNode extends IfNode { + public NeverNode(NeverNode ctrl) { super(ctrl); } public NeverNode(Node ctrl) { super(ctrl,null); } @Override public String label() { return "Never"; } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java index be31dd38..f0bf1166 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -90,6 +90,7 @@ public class PhiNode extends Node { { solo=false; break; } if( solo ) { Node cp = op.copy(); + cp._type = null; // Fresh type cp.addDef(null); // No control for( int j=1; j<op.nIns(); j++ ) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java index d07c5cef..4b8bd687 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java @@ -16,6 +16,7 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize { @Override public void postSelect(CodeGen code) { Node set = in(1); + if( set==null ) return; // Never-node cutout Node cmp = set.in(1); // Bypass an expected Set and just reference the cmp directly if( set instanceof SetARM) @@ -31,13 +32,19 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize { @Override public void encoding( Encoding enc ) { // Assuming that condition flags are already set. These flags are set // by comparison (or sub). No need for regs because it uses flags + if( in(1)!=null ) { + // B.cond + enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) ); + } else { + if( _bop=="!=" ) return; // Inverted, no code + enc.add4(arm.b(arm.OP_UJMP, 0)); + } enc.jump(this,cproj(0)); - // B.cond - enc.add4( arm.b_cond(arm.OP_BRANCH, 0, arm.make_condition(_bop)) ); } // Delta is from opcode start @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code if( -(1<<19) <= delta && delta < (1<<19) ) return 4; // 2 word encoding needs a tmp register, must teach RA throw Utils.TODO(); @@ -45,6 +52,7 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize { // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch if( opLen==4 ) { enc.patch4(opStart,arm.b_cond(arm.OP_BRANCH, delta, arm.make_condition(_bop))); } else { @@ -53,8 +61,13 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize { } @Override public void asm(CodeGen code, SB sb) { - String src = code.reg(in(1)); - if( src!="flags" ) sb.p(src).p(" "); + if( in(1)!=null ) { // Never-node + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(src).p(" "); + } else if( _bop=="!=" ) { + sb.p("never"); + return; + } CFGNode prj = cproj(0).uctrlSkipEmpty(); if( !prj.blockHead() ) prj = prj.cfg0(); sb.p(label(prj)); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java index b919df66..028688b7 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java @@ -1,7 +1,8 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.MachNode; @@ -27,12 +28,14 @@ public class FunARM extends FunNode implements MachNode { // Can be negative if the stack args passed are never referenced int sz = Math.max(_maxSlot - _maxArgSlot,0); sz = ((sz+1) & -2)+1; // Pad to 16 - if( _frameAdjust != 0 ) - enc.add4(arm.imm_inst(arm.OPI_ADD, (_frameAdjust*8)&0xFFF, arm.RSP, arm.RSP)); + _frameAdjust = (short)sz; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz*-8 < -1L<<12 ) throw Utils.TODO(); + enc.add4(arm.imm_inst(arm.OPI_ADD, (_frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP)); } @Override public void asm(CodeGen code, SB sb) { if( _frameAdjust != 0 ) - sb.p("rsp += #").p(_frameAdjust*8); + sb.p("rsp -= #").p(_frameAdjust*8); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java index 684cc167..50ba2274 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java @@ -1,7 +1,9 @@ package com.seaofnodes.simple.node.cpus.arm; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.SplitNode; import com.seaofnodes.simple.node.cpus.riscv.riscv; @@ -55,4 +57,10 @@ public class SplitARM extends SplitNode { enc.add4(arm.f_mov_general(arm.OP_FMOV, 0b01, 0, 0b110, src - arm.D_OFFSET, dst)); } } + + // General form: "mov dst = src" + @Override public void asm(CodeGen code, SB sb) { + FunNode fun = code._encoding==null ? null : code._encoding._fun; + sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); + } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index bedda4e2..97c0cf15 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -679,9 +679,11 @@ public class arm extends Machine { // If/Bool combos will match to a Cmp/Set which sets flags. // Most general arith ops will also set flags, which the Jmp needs directly. // Loads do not set the flags, and will need an explicit TEST - if( !(iff.in(1) instanceof BoolNode) ) - iff.setDef(1,new BoolNode.EQ(iff.in(1),new ConstantNode(TypeInteger.ZERO))); - return new BranchARM(iff, invert(((BoolNode)iff.in(1)).op())); + String op = "!="; + if( iff.in(1) instanceof BoolNode bool ) op = bool.op(); + else if( iff.in(1)==null ) op = "=="; // Never-node cutout + else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); + return new BranchARM(iff, op); } private Node add(AddNode add) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java index 438ba760..6b1568f2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java @@ -39,15 +39,21 @@ public class BranchRISC extends IfNode implements MachNode, RIPRelSize { } @Override public void encoding( Encoding enc ) { + if( in(1)==null && _bop=="!=" ) return; // Inverted never-node, no code enc.jump(this,cproj(0)); - // Todo: relocs (for offset - immf) - short src1 = enc.reg(in(1)); - short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); - enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0)); + if( in(1)==null ) // Never node + enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); + else { + // Todo: relocs (for offset - immf) + short src1 = enc.reg(in(1)); + short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); + enc.add4(riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, 0)); + } } // Delta is from opcode start @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code if( -4*1024 <= delta && delta < 4*1024 ) return 4; // 2 word encoding needs a tmp register, must teach RA throw Utils.TODO(); @@ -55,16 +61,25 @@ public class BranchRISC extends IfNode implements MachNode, RIPRelSize { // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - short src1 = enc.reg(in(1)); - short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); - if( opLen==4 ) { - enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta)); + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch + if( in(1)==null ) { // Never node + enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); } else { - throw Utils.TODO(); + short src1 = enc.reg(in(1)); + short src2 = in(2)==null ? (short)riscv.ZERO : enc.reg(in(2)); + if( opLen==4 ) { + enc.patch4(opStart,riscv.b_type(riscv.OP_BRANCH, riscv.jumpop(_bop), src1, src2, delta)); + } else { + throw Utils.TODO(); + } } } @Override public void asm(CodeGen code, SB sb) { + if( in(1)==null && _bop=="!=" ) { + sb.p("never"); + return; + } String src1 = in(1)==null ? "#0" : code.reg(in(1)); String src2 = in(2)==null ? "#0" : code.reg(in(2)); sb.p(src1).p(" ").p(_bop).p(" ").p(src2).p(" "); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java index 5f888806..43a9439e 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java @@ -1,7 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.SB; +import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.MachNode; @@ -27,12 +28,14 @@ public class FunRISC extends FunNode implements MachNode { // Can be negative if the stack args passed are never referenced int sz = Math.max(_maxSlot - _maxArgSlot,0); sz = ((sz+1) & -2)+1; // Pad to 16 - if( _frameAdjust != 0 ) - enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (_frameAdjust*-8) & 0xFFF)); + _frameAdjust = (short)sz; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz*-8 < -1L<<12 ) throw Utils.TODO(); + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (_frameAdjust*-8) & 0xFFF)); } @Override public void asm(CodeGen code, SB sb) { if( _frameAdjust != 0 ) - sb.p("rsp += #").p(_frameAdjust*-8); + sb.p("rsp -= #").p(_frameAdjust*8); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java index ebf2d5b9..bd50842c 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java @@ -1,7 +1,9 @@ package com.seaofnodes.simple.node.cpus.riscv; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.SplitNode; @@ -54,6 +56,11 @@ public class SplitRISC extends SplitNode { //fmv.x.d enc.add4(riscv.r_type(riscv.OP_FP, dst, 0, src - riscv.F_OFFSET, 0, 0b1110001)); } + } + // General form: "mov dst = src" + @Override public void asm(CodeGen code, SB sb) { + FunNode fun = code._encoding==null ? null : code._encoding._fun; + sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index 5b2ea9b9..e526252e 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -496,7 +496,7 @@ public class riscv extends Machine { return new BranchRISC(iff, bop, bool.in(1), bool.in(2)); } // Vs zero - return new BranchRISC(iff, "!=", iff.in(1), null); + return new BranchRISC(iff, iff.in(1)==null ? "==" : "!=", iff.in(1), null); } private Node or(OrNode or) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java index a0747025..9f6fc715 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java @@ -40,6 +40,7 @@ public class FunX86 extends FunNode implements MachNode { } @Override public void asm(CodeGen code, SB sb) { - sb.p("rsp -= #").p(_frameAdjust*8); + if( _frameAdjust!=0 ) + sb.p("rsp -= #").p(_frameAdjust*8); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java index d77d4acd..51e7f061 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java @@ -15,6 +15,7 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize { @Override public String label() { return op(); } @Override public void postSelect(CodeGen code) { Node set = in(1); + if( set==null ) return; // Never-node cutout Node cmp = set.in(1); // Bypass an expected Set and just reference the cmp directly if( set instanceof SetX86 setx && (cmp instanceof CmpX86 || cmp instanceof CmpIX86 || cmp instanceof CmpMemX86 || cmp instanceof CmpFX86) ) { @@ -28,35 +29,48 @@ public class JmpX86 extends IfNode implements MachNode, RIPRelSize { @Override public void invert() { _bop = invert(_bop); } @Override public void encoding( Encoding enc ) { + if( in(1)!=null ) { + int op = x86_64_v2.jumpop(_bop); + enc.add1(op-16).add1(0); // Short form jump + } else { + if( _bop=="!=" ) return; // Inverted, no code + enc.add1(0xEB).add1(0); // Never-node + } enc.jump(this,cproj(0)); - int op = x86_64_v2.jumpop(_bop); - enc.add1(op-16); // Short form jump - enc.add1(0); // Offset } // Delta is from opcode start, but X86 measures from the end of the 2-byte encoding - @Override public byte encSize(int delta) { return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); } + @Override public byte encSize(int delta) { + if( in(1)==null && _bop=="!=" ) return 0; // Inverted never-node, no code + return (byte)(x86_64_v2.imm8(delta-2) ? 2 : 6); + } // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + assert !( in(1)==null && _bop=="!=" ); // Inverted never-node, no code no patch byte[] bits = enc.bits(); if( opLen==2 ) { - assert bits[opStart] == x86_64_v2.jumpop(_bop)-16; + assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; delta -= 2; // Offset from opcode END assert (byte)delta==delta; bits[opStart+1] = (byte)delta; } else { - assert bits[opStart] == x86_64_v2.jumpop(_bop)-16; + assert in(1)==null || bits[opStart] == x86_64_v2.jumpop(_bop)-16; delta -= 6; // Offset from opcode END bits[opStart] = 0x0F; - bits[opStart+1] = (byte)x86_64_v2.jumpop(_bop); + bits[opStart+1] = (byte)(in(1)==null ? 0xE9 : x86_64_v2.jumpop(_bop)); enc.patch4(opStart+2,delta); } } @Override public void asm(CodeGen code, SB sb) { - String src = code.reg(in(1)); - if( src!="flags" ) sb.p(src).p(" "); + if( in(1)!=null ) { // Never-node + String src = code.reg(in(1)); + if( src!="flags" ) sb.p(src).p(" "); + } else if( _bop=="!=" ) { + sb.p("never"); + return; + } CFGNode prj = cproj(0).uctrlSkipEmpty(); // 0 is True is jump target if( !prj.blockHead() ) prj = prj.cfg0(); sb.p(label(prj)); diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index 12b3a12c..401a9b2c 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -5,8 +5,6 @@ import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.*; import com.seaofnodes.simple.type.*; -import java.io.ByteArrayOutputStream; - public class x86_64_v2 extends Machine { public x86_64_v2( CodeGen code ) {} // X86-64 V2. Includes e.g. SSE4.2 and POPCNT. @@ -79,7 +77,7 @@ public class x86_64_v2 extends Machine { case ">" -> 0x8F; case "<" -> 0x8C; case "<=" -> 0x8E; - case ">=" -> 0X8D; + case ">=" -> 0x8D; default -> throw new IllegalArgumentException("Too many arguments"); }; } @@ -119,14 +117,6 @@ public class x86_64_v2 extends Machine { return rex; } - // return the size of the immediate - public static int imm_size(long imm) { - if(imm8(imm)) return 8; - if(imm16(imm)) return 16; - if(imm32(imm)) return 32; - return 64; - } - public static int rex(int reg, int ptr, int idx) { return rex(reg, ptr, idx, true); } @@ -421,10 +411,10 @@ public class x86_64_v2 extends Machine { case AddNode add -> add(add); case AndNode and -> and(and); case BoolNode bool -> cmp(bool); + case CProjNode c -> new CProjNode(c); case CallEndNode cend -> new CallEndX86(cend); case CallNode call -> call(call); case CastNode cast -> new CastX86(cast); - case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); case DivFNode divf -> new DivFX86(divf); case DivNode div -> new DivX86(div); @@ -451,6 +441,7 @@ public class x86_64_v2 extends Machine { case SubFNode subf -> new SubFX86(subf); case SubNode sub -> sub(sub); case ToFloatNode tfn -> i2f8(tfn); + case XCtrlNode x -> new ConstantNode(Type.XCONTROL); case XorNode xor -> xor(xor); case LoopNode loop -> new LoopNode(loop); @@ -460,7 +451,7 @@ public class x86_64_v2 extends Machine { } public static boolean imm8( long imm ) { return -128 <= imm && imm <= 127; } - public static boolean imm16(long imm) {return -32768 <= imm && imm <= 32767; } + public static boolean imm32( long imm ) { return (int)imm==imm; } // Attempt a full LEA-style break down. @@ -595,10 +586,11 @@ public class x86_64_v2 extends Machine { // If/Bool combos will match to a Cmp/Set which sets flags. // Most general arith ops will also set flags, which the Jmp needs directly. // Loads do not set the flags, and will need an explicit TEST - BoolNode bool; - if( iff.in(1) instanceof BoolNode bool0 ) bool = bool0; - else iff.setDef(1, bool=new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); - return new JmpX86(iff, bool.op()); + String op = "!="; + if( iff.in(1) instanceof BoolNode bool ) op = bool.op(); + else if( iff.in(1)==null ) op = "=="; // Never-node cutout + else iff.setDef(1, new BoolNode.NE(iff.in(1), new ConstantNode(TypeInteger.ZERO))); + return new JmpX86(iff, op); } private Node ld(LoadNode ld) { diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java index c2b9a86d..7e88b60e 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java @@ -289,7 +289,7 @@ else { } """); code.parse().opto().typeCheck().GCM(); - assertEquals("return Phi(Region,9,Top,Top,Top,Top);", code.print()); + assertEquals("return 9;", code.print()); } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java index 99daae7d..c0e28412 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.CodeGen.Phase; import com.seaofnodes.simple.codegen.CodeGen; import org.junit.Ignore; import org.junit.Test; @@ -14,7 +15,7 @@ public class Chapter18Test { """ return 0; """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return 0;", code._stop.toString()); assertEquals("0", Eval2.eval(code, 2)); } @@ -76,7 +77,7 @@ var sq = { int x -> }; return sq(arg)+sq(3); """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("Stop[ return (#2+#2); return (Parm_x(sq,int)*x); ]", code._stop.toString()); assertEquals("13", Eval2.eval(code, 2)); } @@ -174,7 +175,7 @@ for(;;) { i2i = id(x); } """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("Stop[ return #2; return Parm_i(x,int); ]", code._stop.toString()); assertEquals("3", Eval2.eval(code, 0)); } @@ -190,7 +191,7 @@ for(;;) { arg = x(3); } """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return Top;", code._stop.toString()); assertEquals("null", Eval2.eval(code, 0)); } @@ -215,7 +216,7 @@ ps[0] = new Person; ps[1] = new Person; fcn(ps,1); """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return 0;", code._stop.toString()); assertEquals("0", Eval2.eval(code, 0)); } @@ -345,7 +346,7 @@ if (arg) i2i = null; if (i2i) return i2i(arg); return f2f(o)(1); """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("Stop[ return Phi(Region,#2,#2); return Parm_i(o,int); ]", code._stop.toString()); assertEquals("1", Eval2.eval(code, 2)); } @@ -361,7 +362,7 @@ Person !p = new Person; p.coffee_count += 1; return p.coffee_count; """); - code.parse().opto().typeCheck().GCM().localSched(); + code.driver(Phase.LocalSched); assertEquals("return 1;", code._stop.toString()); assertEquals("1", Eval2.eval(code, 2)); } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java index 9ba8e972..787dfd4c 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java @@ -1,5 +1,6 @@ package com.seaofnodes.simple; +import com.seaofnodes.simple.codegen.CodeGen.Phase; import com.seaofnodes.simple.codegen.CodeGen; import java.io.IOException; import java.nio.file.Files; @@ -32,98 +33,98 @@ return 0; @Test public void testBasic0() { - CodeGen code = new CodeGen("return 0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return 0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return 0;", code._stop.toString()); } @Test public void testBasic1() { - CodeGen code = new CodeGen("return arg+1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg+1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (inc,arg);", code._stop.toString()); } @Test public void testBasic2() { - CodeGen code = new CodeGen("return -17;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return -17;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return -17;", code._stop.toString()); } @Test public void testBasic3() { - CodeGen code = new CodeGen("return arg==1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg==1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (set==,(cmp,arg));", code._stop.toString()); } @Test public void testBasic4() { - CodeGen code = new CodeGen("return arg<<1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg<<1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (shli,arg);", code._stop.toString()); } @Test public void testBasic5() { - CodeGen code = new CodeGen("return arg >> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg >> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (sari,arg);", code._stop.toString()); } @Test public void testBasic6() { - CodeGen code = new CodeGen("return arg >>> 1;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg >>> 1;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (shri,arg);", code._stop.toString()); } @Test public void testBasic7() { - CodeGen code = new CodeGen("return arg / 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg / 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (div,arg,2);", code._stop.toString()); } @Test public void testBasic8() { - CodeGen code = new CodeGen("return arg * 6;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg * 6;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (muli,arg);", code._stop.toString()); } @Test public void testBasic9() { - CodeGen code = new CodeGen("return arg & 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg & 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (andi,arg);", code._stop.toString()); } @Test public void testBasic10() { - CodeGen code = new CodeGen("return arg | 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg | 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (ori,arg);", code._stop.toString()); } @Test public void testBasic11() { - CodeGen code = new CodeGen("return arg ^ 2;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg ^ 2;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (xori,arg);", code._stop.toString()); } @Test public void testBasic12() { - CodeGen code = new CodeGen("return arg + 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg + 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test public void testBasic13() { - CodeGen code = new CodeGen("return arg - 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg - 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (subf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test public void testBasic14() { - CodeGen code = new CodeGen("return arg * 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg * 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (mulf,(cvtf,arg),2.0f);", code._stop.toString()); } @Test public void testBasic15() { - CodeGen code = new CodeGen("return arg / 2.0;").parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen("return arg / 2.0;").driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (mulf,(cvtf,arg),0.5f);", code._stop.toString()); } @@ -133,7 +134,7 @@ return 0; """ int arg1 = arg + 1; return arg1 / arg;"""); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (div,(inc,arg),arg);", code._stop.toString()); } @@ -144,7 +145,7 @@ return arg1 / arg;"""); int arg1 = arg + 1; return arg1 * arg; """); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (mul,(inc,arg),arg);", code._stop.toString()); } @@ -154,7 +155,7 @@ return arg1 * arg; int a = arg; return a + 2.0; """ - ).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + ).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (addf,(cvtf,arg),2.0f);", code._stop.toString()); } @@ -169,7 +170,7 @@ else { a = arg-3; } return a;"""); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return Phi(Region,(addi,arg),(addi,arg));", code.print()); } @@ -184,7 +185,7 @@ if( arg==1 ) else a=b+1; return a+b;"""); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (add,(add,Phi(Region,(shli,arg),arg),arg),Phi(Region,4,5));", code.print()); } @@ -196,7 +197,7 @@ int sum=0; for( int i=0; i<arg; i++ ) sum += i; return sum;"""); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return Phi(Loop,0,(add,Phi_sum,Phi(Loop,0,(inc,Phi_i))));", code.print()); } @@ -206,42 +207,42 @@ return sum;"""); """ struct S { int a; S? c; }; return new S;"""); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return S;", code.print()); } @Test public void testLea1() { CodeGen code = new CodeGen("int x = arg/3; return arg+x+7;"); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (lea,arg,(div,arg,3));", code.print()); } @Test public void testLea2() { CodeGen code = new CodeGen("int x = arg/3; return arg+x*4+7;"); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (lea,arg,(div,arg,3));", code.print()); } @Test public void testLea3() { CodeGen code = new CodeGen("int x = arg/3; return x*4+arg;"); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return (lea,arg,(div,arg,3));", code.print()); } @Test public void testAlloc2() { CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1];"); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @Test public void testAlloc3() { CodeGen code = new CodeGen("int[] !xs = new int[3]; xs[arg]=1; return xs[arg&1]+3;"); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @@ -258,7 +259,7 @@ for( int i=0; i<ary#-1; i++ ) ary[i+1] += ary[i]; return ary[1] * 1000 + ary[3]; // 1 * 1000 + 6 """); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @@ -273,7 +274,7 @@ for( int i=0; i<A#; i++ ) for( int i=0; i<A#; i++ ) B[i] += A[i]; """); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return 0;", code.print()); } @@ -286,7 +287,7 @@ for( int i=0; i<A#; i++ ) A[i]++; return A[1]; """); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return .[];", code.print()); } @@ -294,7 +295,7 @@ return A[1]; public void testNewton() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/newtonFloat.smp")) + "flt farg = arg; return test_sqrt(farg);"; - CodeGen code = new CodeGen(src).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return Phi(Loop,(cvtf,arg),(mulf,(addf,(divf,cvtf,Phi_guess),Phi_guess),0.5f));", code.print()); }; @@ -302,7 +303,7 @@ return A[1]; @Test public void sieveOfEratosthenes() throws IOException { String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/sieve.smp")); - CodeGen code = new CodeGen(src).parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + CodeGen code = new CodeGen(src).driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("return [u32];", code.print()); //assertEquals("u32[ 2,3,5,7,11,13,17,19]",Eval2.eval(code, 20)); } @@ -315,7 +316,7 @@ return A[1]; val fcn = arg ? { int x -> x*x; } : { int x -> x+x; }; return fcn(2)*10 + fcn(3); """); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("Stop[ return (add,#2,(muli,#2)); return (mul,Parm_x($fun1,int),x); return (shli,Parm_x($fun2,int)); ]", code.print()); } @@ -326,7 +327,7 @@ return fcn(2)*10 + fcn(3); val sq = { int x -> x*x; }; return sq(arg) + sq(3); """); - code.parse().opto().typeCheck().instSelect("x86_64_v2", "SystemV").GCM().localSched(); + code.driver(Phase.LocalSched,"x86_64_v2", "SystemV"); assertEquals("Stop[ return (add,#2,#2); return (mul,Parm_x(sq,int),x); ]", code.print()); } } diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java index 8b7136ab..664620a8 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java @@ -23,7 +23,7 @@ public class Chapter20Test { static void testCPU( String src, String cpu, String os, int spills, String stop ) { CodeGen code = new CodeGen(src); - code.parse().opto().typeCheck().instSelect(cpu,os).GCM().localSched().regAlloc(); + code.driver(CodeGen.Phase.RegAlloc,cpu,os); int delta = spills>>3; if( delta==0 ) delta = 1; if( spills != -1 ) diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java index e584f9bc..259cbb4f 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java @@ -13,16 +13,15 @@ import static org.junit.Assert.*; public class Chapter21Test { @Test - public void testJig() { - CodeGen code = new CodeGen("return 0;"); - code.parse().opto().typeCheck().GCM().localSched(); - assertEquals("return 0;", code._stop.toString()); - assertEquals("0", Eval2.eval(code, 2)); + 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", "SystemV",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 ) { - CodeGen code = new CodeGen(src); - code.parse().opto().typeCheck().instSelect(cpu,os).GCM().localSched().regAlloc().encode(); + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,cpu,os); int delta = spills>>3; if( delta==0 ) delta = 1; if( spills != -1 ) diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Simple.java b/chapter21/src/test/java/com/seaofnodes/simple/Simple.java index 56761e7a..9b1e2827 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/Simple.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/Simple.java @@ -16,11 +16,12 @@ public class Simple { static final int DUMP_AFTER_PARSE = 1<<0; static final int DUMP_AFTER_OPTO = 1<<1; static final int DUMP_AFTER_TYPE_CHECK = 1<<2; - static final int DUMP_AFTER_INSTR_SELECT = 1<<3; - static final int DUMP_AFTER_GCM = 1<<4; - static final int DUMP_AFTER_LOCAL_SCHED = 1<<5; - static final int DUMP_AFTER_REG_ALLOC = 1<<6; - static final int DUMP_AFTER_ENCODE = 1<<7; + static final int DUMP_AFTER_LOOP_TREE = 1<<3; + static final int DUMP_AFTER_INSTR_SELECT = 1<<4; + static final int DUMP_AFTER_GCM = 1<<5; + static final int DUMP_AFTER_LOCAL_SCHED = 1<<6; + static final int DUMP_AFTER_REG_ALLOC = 1<<7; + static final int DUMP_AFTER_ENCODE = 1<<8; static final int DUMP_FINAL = 1<<16; @@ -88,12 +89,13 @@ Options: case DUMP_AFTER_PARSE -> "01-parse.dot"; case DUMP_AFTER_OPTO -> "02-opto.dot"; case DUMP_AFTER_TYPE_CHECK -> "03-type_check.dot"; - case DUMP_AFTER_INSTR_SELECT -> "04-instr_select.dot"; - case DUMP_AFTER_GCM -> "05-gcm.dot"; - case DUMP_AFTER_LOCAL_SCHED -> "06-local_sched.dot"; - case DUMP_AFTER_REG_ALLOC -> "07-reg_allos.dot"; - case DUMP_AFTER_ENCODE -> "08-local_sched.dot"; - case DUMP_FINAL -> "09-final.dot"; + case DUMP_AFTER_LOOP_TREE -> "04-loop_tree.dot"; + case DUMP_AFTER_INSTR_SELECT -> "05-instr_select.dot"; + case DUMP_AFTER_GCM -> "06-gcm.dot"; + case DUMP_AFTER_LOCAL_SCHED -> "07-local_sched.dot"; + case DUMP_AFTER_REG_ALLOC -> "08-reg_allos.dot"; + case DUMP_AFTER_ENCODE -> "09-local_sched.dot"; + case DUMP_FINAL -> "10-final.dot"; default -> throw Utils.TODO(); }; @@ -110,6 +112,7 @@ Options: case DUMP_AFTER_PARSE -> "After Parse:"; case DUMP_AFTER_OPTO -> "After OPTO:"; case DUMP_AFTER_TYPE_CHECK -> "After Type Check:"; + case DUMP_AFTER_LOOP_TREE -> "After Loop Tree:"; case DUMP_AFTER_INSTR_SELECT -> "After Instruction Selection:"; case DUMP_AFTER_GCM -> "After GCM:"; case DUMP_AFTER_LOCAL_SCHED -> "After Local Scheduling:"; @@ -134,6 +137,8 @@ Options: System.out.println(String.format("Optimization Time: %.3f sec", t)); total += t = code._tTypeCheck / 1e3; System.out.println(String.format("Type Checking Time: %.3f sec", t)); + total += t = code._tLoopTree / 1e3; + System.out.println(String.format("Loop Tree Time: %.3f sec", t)); total += t = code._tInsSel / 1e3; System.out.println(String.format("Instruction Selection Time: %.3f sec", t)); total += t = code._tGCM / 1e3; @@ -180,6 +185,7 @@ loop: for (int i = 0; i < args.length; i++) { case "--dump-after-parse": dump |= DUMP_AFTER_PARSE; break; case "--dump-after-opto": dump |= DUMP_AFTER_OPTO; break; case "--dump-after-type-check": dump |= DUMP_AFTER_TYPE_CHECK; break; + case "--dump-after-loop-tree": dump |= DUMP_AFTER_LOOP_TREE; break; case "--dump-after-instr-select": dump |= DUMP_AFTER_INSTR_SELECT; break; case "--dump-after-gcm": dump |= DUMP_AFTER_GCM; break; case "--dump-after-local-sched": dump |= DUMP_AFTER_LOCAL_SCHED; break; @@ -250,6 +256,9 @@ loop: for (int i = 0; i < args.length; i++) { code.typeCheck(); dump(code, dump, DUMP_AFTER_TYPE_CHECK); + code.loopTree(); + dump(code, dump, DUMP_AFTER_LOOP_TREE); + if (do_codegen) { code.instSelect(PORTS, cpu, abi); dump(code, dump, DUMP_AFTER_INSTR_SELECT); diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestC.java b/chapter21/src/test/java/com/seaofnodes/simple/TestC.java index 2d908699..c6e0b196 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/TestC.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestC.java @@ -49,7 +49,7 @@ public abstract class TestC { String bin = efile+xtn; String obj = bin+".o"; // Compile simple, emit ELF - CodeGen code = new CodeGen(src).parse().opto().typeCheck().instSelect( CPU_PORT, simple_conv).GCM().localSched().regAlloc().encode().exportELF(obj); + CodeGen code = new CodeGen(src).driver( CPU_PORT, simple_conv, obj); // Compile the C program var params = new String[] { diff --git a/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java b/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java index 275cdebb..9c64fe9f 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java @@ -18,7 +18,7 @@ public abstract class TestRisc5 { public static EvalRisc5 build( String dir, String file, int arg ) throws IOException { // Compile and export Simple String src = Files.readString(Path.of(dir+"/"+file+".smp")); - CodeGen code = new CodeGen(src).parse().opto().typeCheck().instSelect( "riscv", "SystemV").GCM().localSched().regAlloc().encode(); + CodeGen code = new CodeGen(src).driver(CodeGen.Phase.Encoding,"riscv", "SystemV"); // Image byte[] image = new byte[1<<20]; // A megabyte diff --git a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java index 669a1e6a..b90b663d 100644 --- a/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java +++ b/chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java @@ -101,7 +101,7 @@ class FuzzerUtils { System.setErr(NULL_PRINT_STREAM); System.setOut(NULL_PRINT_STREAM); var code = new CodeGen(script, TypeInteger.BOT, workListSeed); - return code.parse().opto().typeCheck().GCM().localSched(); + return code.driver(CodeGen.Phase.LocalSched); } finally { System.setErr(err); System.setOut(out); diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp new file mode 100644 index 00000000..47aeb2e5 --- /dev/null +++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp @@ -0,0 +1,17 @@ +// -*- mode: java; -*- +struct s0 { + bool v0; + u8 v2; + i16 v1; + byte v3; + int v4; + i16 v5; + int v6; +}; + while(new s0.v1) {} + if(new s0.v6) return new s0.v5; + return new s0.v2; +struct s1 { + int v7; +}; + while(new s1.v7) arg=new s0.v3; |
