summaryrefslogtreecommitdiff
path: root/chapter21/src
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-04-13 10:23:18 -0700
committerCliff Click <cliffc@acm.org>2025-04-13 10:23:18 -0700
commit1e28b27ec2d1129d4a219a57ec0c661c74720493 (patch)
tree74ba9d8cd8081b574b3d177d0d3955fa2dbdb121 /chapter21/src
parent0ac2721f28c729c06c28fce14f8f863f8c57e41b (diff)
downloadSimple-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')
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java41
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/CFGNode.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/IfNode.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/NeverNode.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/BranchARM.java21
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java11
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/BranchRISC.java33
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java11
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java7
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java3
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/JmpX86.java32
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java26
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter11Test.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter18Test.java15
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter19Test.java71
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter20Test.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java13
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Simple.java31
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/TestC.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/TestRisc5.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/fuzzer/FuzzerUtils.java2
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp17
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;