From 9fff7d0803c4759c2f95f010c3a52f531822d1f9 Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Sat, 26 Apr 2025 00:34:04 +0100 Subject: [PATCH 1/5] Sync with final ch21 --- .../compiler/nodes/cpus/arm/AddARM.java | 2 +- .../compiler/nodes/cpus/arm/AddFARM.java | 2 +- .../compiler/nodes/cpus/arm/AddIARM.java | 2 +- .../compiler/nodes/cpus/arm/AndARM.java | 2 +- .../compiler/nodes/cpus/arm/AndIARM.java | 4 +- .../compiler/nodes/cpus/arm/AsrARM.java | 2 +- .../compiler/nodes/cpus/arm/AsrIARM.java | 2 +- .../compiler/nodes/cpus/arm/BranchARM.java | 23 +- .../compiler/nodes/cpus/arm/CallEndARM.java | 18 +- .../compiler/nodes/cpus/arm/CastARM.java | 18 + .../compiler/nodes/cpus/arm/CmpARM.java | 2 +- .../compiler/nodes/cpus/arm/CmpIARM.java | 2 +- .../compiler/nodes/cpus/arm/DivARM.java | 2 +- .../compiler/nodes/cpus/arm/FloatARM.java | 2 +- .../compiler/nodes/cpus/arm/FunARM.java | 36 +- .../compiler/nodes/cpus/arm/IntARM.java | 3 +- .../compiler/nodes/cpus/arm/LoadARM.java | 20 +- .../compiler/nodes/cpus/arm/LslARM.java | 2 +- .../compiler/nodes/cpus/arm/LslIARM.java | 2 +- .../compiler/nodes/cpus/arm/LsrARM.java | 2 +- .../compiler/nodes/cpus/arm/LsrIARM.java | 2 +- .../compiler/nodes/cpus/arm/MemOpARM.java | 10 +- .../compiler/nodes/cpus/arm/MulARM.java | 2 +- .../compiler/nodes/cpus/arm/NegARM.java | 25 ++ .../compiler/nodes/cpus/arm/NewARM.java | 32 +- .../compiler/nodes/cpus/arm/NotARM.java | 2 +- .../ezlang/compiler/nodes/cpus/arm/OrARM.java | 2 +- .../compiler/nodes/cpus/arm/OrIARM.java | 2 +- .../compiler/nodes/cpus/arm/RetARM.java | 28 -- .../compiler/nodes/cpus/arm/SetARM.java | 2 +- .../compiler/nodes/cpus/arm/SplitARM.java | 16 +- .../compiler/nodes/cpus/arm/StoreARM.java | 16 +- .../compiler/nodes/cpus/arm/SubARM.java | 2 +- .../compiler/nodes/cpus/arm/SubIARM.java | 6 +- .../compiler/nodes/cpus/arm/UJmpARM.java | 2 +- .../compiler/nodes/cpus/arm/XorARM.java | 2 +- .../compiler/nodes/cpus/arm/XorIARM.java | 2 +- .../ezlang/compiler/nodes/cpus/arm/arm.java | 375 ++++++++-------- .../compiler/nodes/cpus/riscv/BranchRISC.java | 45 +- .../nodes/cpus/riscv/CallEndRISC.java | 17 +- .../compiler/nodes/cpus/riscv/CallRISC.java | 2 - .../compiler/nodes/cpus/riscv/FltRISC.java | 6 +- .../compiler/nodes/cpus/riscv/FunRISC.java | 36 +- .../compiler/nodes/cpus/riscv/ImmRISC.java | 2 +- .../compiler/nodes/cpus/riscv/IntRISC.java | 5 +- .../compiler/nodes/cpus/riscv/MemOpRISC.java | 2 +- .../compiler/nodes/cpus/riscv/NJmpRISC.java | 46 -- .../compiler/nodes/cpus/riscv/NegRISC.java | 20 + .../compiler/nodes/cpus/riscv/NewRISC.java | 39 +- .../compiler/nodes/cpus/riscv/NotRISC.java | 18 +- .../compiler/nodes/cpus/riscv/RetRISC.java | 39 +- .../compiler/nodes/cpus/riscv/SetFRISC.java | 2 +- .../compiler/nodes/cpus/riscv/SplitRISC.java | 15 +- .../compiler/nodes/cpus/riscv/StoreRISC.java | 2 +- .../compiler/nodes/cpus/riscv/UJmpRISC.java | 2 +- .../compiler/nodes/cpus/riscv/riscv.java | 236 +++++----- .../nodes/cpus/x86_64_v2/AddMemX86.java | 5 +- .../nodes/cpus/x86_64_v2/CallEndX86.java | 17 +- .../nodes/cpus/x86_64_v2/CallX86.java | 1 - .../nodes/cpus/x86_64_v2/CmpIX86.java | 9 +- .../nodes/cpus/x86_64_v2/CmpMemX86.java | 57 ++- .../compiler/nodes/cpus/x86_64_v2/FunX86.java | 48 +-- .../compiler/nodes/cpus/x86_64_v2/JmpX86.java | 34 +- .../nodes/cpus/x86_64_v2/LoadX86.java | 20 +- .../nodes/cpus/x86_64_v2/MemAddX86.java | 2 +- .../nodes/cpus/x86_64_v2/MemOpX86.java | 11 +- .../compiler/nodes/cpus/x86_64_v2/NegX86.java | 21 + .../compiler/nodes/cpus/x86_64_v2/NewX86.java | 17 +- .../compiler/nodes/cpus/x86_64_v2/RetX86.java | 34 +- .../nodes/cpus/x86_64_v2/SarIX86.java | 1 + .../compiler/nodes/cpus/x86_64_v2/SetX86.java | 1 + .../nodes/cpus/x86_64_v2/ShlIX86.java | 1 + .../nodes/cpus/x86_64_v2/ShrIX86.java | 1 + .../nodes/cpus/x86_64_v2/SplitX86.java | 15 +- .../nodes/cpus/x86_64_v2/StoreX86.java | 46 +- .../nodes/cpus/x86_64_v2/UJmpX86.java | 2 +- .../nodes/cpus/x86_64_v2/x86_64_v2.java | 407 ++++++++---------- 77 files changed, 974 insertions(+), 986 deletions(-) create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java delete mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java create mode 100644 seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java index 9fc20ac..ee8f426 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddARM.java @@ -8,7 +8,7 @@ public class AddARM extends MachConcreteNode implements MachNode { AddARM( Node add) { super(add); } @Override public String op() { return "add"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } // ADD (shifted register) @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_ADD); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java index 807c0a4..4701712 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddFARM.java @@ -11,7 +11,7 @@ public class AddFARM extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return arm.DMASK; } //FADD (scalar) - @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_ADD); } + @Override public void encoding( Encoding enc ) { arm.f_scalar(enc,this,arm.OPF_OP_ADD);} // Default on double precision for now(64 bits) // General form: "addf rd = src1 + src2 diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java index 4c2b94a..e85f38a 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AddIARM.java @@ -15,7 +15,7 @@ public class AddIARM extends MachConcreteNode implements MachNode { return _imm == 1 ? "inc" : (_imm == -1 ? "dec" : "addi"); } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } //ADD (immediate) @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this, in(1), arm.OPI_ADD,_imm); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java index 94ee434..d932154 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndARM.java @@ -11,7 +11,7 @@ public class AndARM extends MachConcreteNode implements MachNode { @Override public String op() { return "and"; } @Override public String glabel() { return "&"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } // AND (shifted register) @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_AND); } // General form: #rd = rs1 & rs2 diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java index 96b4485..4961645 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AndIARM.java @@ -13,12 +13,12 @@ public class AndIARM extends MachConcreteNode implements MachNode { } @Override public String op() { return "andi"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_AND,_imm); } // General form: "andi rd = rs1 & imm" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(_imm); + sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" & #").p(arm.decodeImm12(_imm)); } } \ No newline at end of file diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java index 7bd9418..0ca9280 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrARM.java @@ -9,7 +9,7 @@ public class AsrARM extends MachConcreteNode implements MachNode { AsrARM(Node asr) {super(asr);} @Override public String op() { return "sar"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.DMASK; } @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_ASR); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))).p(" >> ").p(code.reg(in(2))); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java index 3f903b3..5f78666 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/AsrIARM.java @@ -17,7 +17,7 @@ public class AsrIARM extends MachConcreteNode implements MachNode { } @Override public String op() { return "asri"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { short rd = enc.reg(this); short rn = enc.reg(in(1)); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java index 8494f39..8bb1ad6 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/BranchARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/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) @@ -26,18 +27,24 @@ public class BranchARM extends IfNode implements MachNode, RIPRelSize { @Override public RegMask regmap(int i) { assert i==1; return arm.FLAGS_MASK; } @Override public RegMask outregmap() { return null; } - @Override public void invert() { _bop = invert(_bop); } + @Override public void negate() { _bop = negate(_bop); } @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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java index af4d96c..eb868c1 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CallEndARM.java @@ -1,24 +1,8 @@ package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm; -import com.compilerprogramming.ezlang.compiler.SB; -import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; -import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr; public class CallEndARM extends CallEndNode implements MachNode { - final SONTypeFunPtr _tfp; - CallEndARM( CallEndNode cend ) { - super(cend); - _tfp = (SONTypeFunPtr)(cend.call().fptr()._type); - } - - @Override public String op() { return "cend"; } - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask outregmap(int idx) { return idx == 2 ? arm.retMask(_tfp,2) : null; } - @Override public RegMask killmap() { return arm.armCallerSave(); } - @Override public void encoding( Encoding enc ) { } - @Override public void asm(CodeGen code, SB sb) { } + CallEndARM( CallEndNode cend ) { super(cend); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java new file mode 100644 index 0000000..432a5b8 --- /dev/null +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CastARM.java @@ -0,0 +1,18 @@ +package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm; + +import com.compilerprogramming.ezlang.compiler.SB; +import com.compilerprogramming.ezlang.compiler.codegen.CodeGen; +import com.compilerprogramming.ezlang.compiler.codegen.Encoding; +import com.compilerprogramming.ezlang.compiler.codegen.RegMask; +import com.compilerprogramming.ezlang.compiler.nodes.CastNode; +import com.compilerprogramming.ezlang.compiler.nodes.MachNode; + +public class CastARM extends CastNode implements MachNode { + public CastARM( CastNode cast ) { super(cast); } + @Override public String op() { return label(); } + @Override public RegMask regmap(int i) { assert i==1; return RegMask.FULL; } + @Override public RegMask outregmap() { return RegMask.FULL; } + @Override public int twoAddress( ) { return 1; } + @Override public void encoding( Encoding enc ) { } + @Override public void asm(CodeGen code, SB sb) { _t.print(sb.p(code.reg(in(1))).p(" isa ")); } +} diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java index b5dba20..cdf80f2 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpARM.java @@ -11,7 +11,7 @@ public class CmpARM extends MachConcreteNode implements MachNode { @Override public RegMask outregmap() { return arm.FLAGS_MASK; } // SUBS (shifted register) - @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_CMP); } + @Override public void encoding( Encoding enc ) { arm.r_reg_subs(enc,this,arm.OP_CMP); } // General form: "cmp rs1, rs2" @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java index c9b85f4..3dce7f2 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/CmpIARM.java @@ -28,7 +28,7 @@ public class CmpIARM extends MachConcreteNode implements MachNode { @Override public boolean isClone() { return true; } @Override public Node copy() { return new CmpIARM(this); } - @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,in(1),in(1), arm.OPI_CMP,_imm); } + @Override public void encoding( Encoding enc ) { arm.imm_inst_subs(enc,in(1),in(1), arm.OPI_CMP,_imm); } // General form: "cmp rs1, 1" @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java index e9c8dbc..0c4c17e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/DivARM.java @@ -8,7 +8,7 @@ public class DivARM extends MachConcreteNode implements MachNode { DivARM( Node div ) { super(div); } @Override public String op() { return "div"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } // SDIV @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_DIV,3); } // General form: "div dst /= src" diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java index b8f41b1..db04638 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FloatARM.java @@ -31,7 +31,7 @@ public class FloatARM extends ConstantNode implements MachNode, RIPRelSize { // Delta is from opcode start @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { short dst = (short)(enc.reg(this) - arm.D_OFFSET); - enc.add4(arm.load_pc(arm.OPF_ARM, delta, dst)); + enc.patch4(opStart, arm.load_pc(arm.OPF_ARM, delta, dst)); } // Human-readable form appended to the SB. Things like the encoding, diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java index a270595..382543c 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/FunARM.java @@ -1,36 +1,22 @@ package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm; -import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.SB; +import com.compilerprogramming.ezlang.compiler.Utils; +import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.FunNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; public class FunARM extends FunNode implements MachNode { FunARM(FunNode fun) { super(fun); } - @Override public String op() { return "addi"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public void postSelect(CodeGen code) { code.link(this); } - - // Actual stack layout is up to each CPU. - // X86, with too many args & spills: - // | CALLER | - // | argN | // slot 1, required by callER - // +--------+ - // | RPC | // slot 0, required by callER - // | callee | // slot 3, callEE - // | callee | // slot 2, callEE - // | PAD16 | - // +--------+ - @Override public void encoding( Encoding enc ) { - // Stack frame size: _maxSlot - max(arg), padded to 16. - _maxArgSlot = arm.maxSlot(enc._fun.sig()); - _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot); - if( _frameAdjust == 0 ) return; // Skip if no frame adjust - enc.add4(arm.imm_inst(arm.OPI_ADD, (_frameAdjust*8)&0xFFF, arm.RSP, arm.RSP)); + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); } - - @Override public void asm(CodeGen code, SB sb) { - sb.p("rsp += #").p(_frameAdjust*8); + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz >= 1L<<12 ) throw Utils.TODO(); + enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP)); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java index 495559b..9e00093 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/IntARM.java @@ -4,6 +4,7 @@ import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; +import com.compilerprogramming.ezlang.compiler.sontypes.SONType; import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger; // Integer constants @@ -19,7 +20,7 @@ public class IntARM extends ConstantNode implements MachNode { @Override public void encoding( Encoding enc ) { short self = enc.reg(this); - long x = ((SONTypeInteger)_con).value(); + long x = _con==SONType.NIL ? 0 : ((SONTypeInteger)_con).value(); int nb0 = 0; int nb1 = 0; // Count number of 0000 and FFFF blocks diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java index 2700fdb..eff01e0 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LoadARM.java @@ -12,19 +12,25 @@ // idx = null // off = off - offset added to base -public class LoadARM extends MemOpARM{ - LoadARM(LoadNode ld,Node base, Node idx, int off) { +public class LoadARM extends MemOpARM { + LoadARM(LoadNode ld, Node base, Node idx, int off) { super(ld, base, idx, off, 0); } @Override public String op() { return "ld"+_sz; } @Override public RegMask outregmap() { return arm.MEM_MASK; } + + private static final int[] OP_LOADS = new int[]{ arm.OP_LOAD_IMM_8, arm.OP_LOAD_IMM_16, arm.OP_LOAD_IMM_32, arm.OP_LOAD_IMM_64, }; + private int imm_op() { + return _declaredType == SONTypeFloat.F32 ? arm.OPF_LOAD_IMM_32 + : _declaredType == SONTypeFloat.F64 ? arm.OPF_LOAD_IMM_64 + : OP_LOADS[_declaredType.log_size()]; + } + + private static final int[] OP_LOAD_RS = new int[]{ arm.OP_LOAD_R_8 , arm.OP_LOAD_R_16 , arm.OP_LOAD_R_32 , arm.OP_LOAD_R_64, }; + // ldr(immediate - unsigned offset) | ldr(register) @Override public void encoding( Encoding enc ) { - if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) { - ldst_encode(enc, arm.OPF_LOAD_IMM, arm.OPF_LOAD_R, this, true); - } else { - ldst_encode(enc, arm.OP_LOAD_IMM, arm.OP_LOAD_R, this, false); - } + ldst_encode(enc, imm_op(), OP_LOAD_RS[_declaredType.log_size()], this, size()); } @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)).p(","); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java index 594e0e4..d7630fb 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslARM.java @@ -9,7 +9,7 @@ public class LslARM extends MachConcreteNode implements MachNode { LslARM(Node asr) {super(asr);} @Override public String op() { return "shr"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSL); } @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java index a82fbf4..65de773 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LslIARM.java @@ -17,7 +17,7 @@ public class LslIARM extends MachConcreteNode implements MachNode { } @Override public String op() { return "lsli"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { short rd = enc.reg(this); short rn = enc.reg(in(1)); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java index af4763b..34aaac4 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrARM.java @@ -9,7 +9,7 @@ public class LsrARM extends MachConcreteNode implements MachNode { LsrARM(Node asr) {super(asr);} @Override public String op() { return "shr"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.shift_reg(enc,this,arm.OP_LSR); } @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java index f1fff80..2251f74 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/LsrIARM.java @@ -15,7 +15,7 @@ public class LsrIARM extends MachConcreteNode implements MachNode { } @Override public String op() { return "lsri"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { short rd = enc.reg(this); short rn = enc.reg(in(1)); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java index 70dfdee..fcb5986 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MemOpARM.java @@ -29,7 +29,7 @@ public abstract class MemOpARM extends MemOpNode implements MachNode { _inputs.setX(4,val); } - @Override public String label() { return op(); } + @Override public String label() { return op();} Node val() { return in(4); } // Only for stores @Override public StringBuilder _printMach(StringBuilder sb, BitSet visited) { return sb.append(".").append(_name); } @@ -37,6 +37,8 @@ public abstract class MemOpARM extends MemOpNode implements MachNode { @Override public SONType compute() { throw Utils.TODO(); } @Override public Node idealize() { throw Utils.TODO(); } + int size() { return 1<<_declaredType.log_size(); } + // Wider mask to store both GPRs and FPRs @Override public RegMask regmap(int i) { // 0 - ctrl @@ -44,16 +46,16 @@ public abstract class MemOpARM extends MemOpNode implements MachNode { if( i==2 ) return arm.RMASK; // ptr/base if( i==3 ) return arm.RMASK; // off/index if( i==4 ) return arm.RMASK; // value - throw Utils.TODO(); + return null; // Anti-dependence } // Shared encoding for loads and stores(int and float) - public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, boolean float_type ) { + public void ldst_encode( Encoding enc, int opcode_imm, int opcode_reg, Node xval, int size) { short ptr = enc.reg(ptr()); short off = enc.reg(off()); short val = enc.reg(xval); int body = off() == null - ? arm.load_str_imm(opcode_imm, _off, ptr, val) + ? arm.load_str_imm(opcode_imm, _off, ptr, val, size) : arm.indr_adr(opcode_reg, off, arm.STORE_LOAD_OPTION.SXTX, 0, ptr, val); enc.add4(body); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java index 1f69b83..82121df 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/MulARM.java @@ -9,7 +9,7 @@ public class MulARM extends MachConcreteNode implements MachNode { MulARM(Node mul) {super(mul);} @Override public String op() { return "mul"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.madd(enc,this,arm.OP_MUL,31); } // General form: "rd = rs1 * rs2" diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java new file mode 100644 index 0000000..fa76b32 --- /dev/null +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NegARM.java @@ -0,0 +1,25 @@ +package com.compilerprogramming.ezlang.compiler.nodes.cpus.arm; + +import com.compilerprogramming.ezlang.compiler.*; +import com.compilerprogramming.ezlang.compiler.codegen.*; +import com.compilerprogramming.ezlang.compiler.nodes.*; +import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode; +import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger; + +public class NegARM extends MachConcreteNode implements MachNode { + NegARM(Node sub) { + super(sub); + //_inputs.pop(); + } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } + @Override public void encoding( Encoding enc ) { + // reverse subtract with immediate 0 + arm.imm_inst(enc,this, in(1), 0b00100110, 0); + } + // General form: "subi rd = rs1 - imm" + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); + } +} diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java index e867fed..5781c00 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NewARM.java @@ -4,21 +4,33 @@ import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.*; -public class NewARM extends NewNode implements MachNode { +public class NewARM extends NewNode implements MachNode, RIPRelSize { // A pre-zeroed chunk of memory. NewARM(NewNode nnn) { super(nnn); } - @Override public String op() { return "alloc"; } - @Override public RegMask regmap(int i) { return i == 1 ? arm. X0_MASK : null; } - @Override public RegMask outregmap(int i) { return i == 1 ? arm. X0_MASK : null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask killmap() { return arm.armCallerSave(); } - @Override public void encoding( Encoding enc ) { - // bl - enc.external(this,"calloc").add4(arm.b(arm.OP_CALL, 0)); + // BL(branch with link) + enc.external(this,"calloc"). + add4(arm.mov(arm.OP_MOVZ, 0, 1, 0)). // movz x0,#1 + add4(arm.b(arm.OP_CALL, 0)); } + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public byte encSize(int delta) { return 4*2; } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public void patch(Encoding enc, int opStart, int opLen, int delta ) { + // Negative patches are JIT emulator targets. + if( opStart+delta < 0 ) { + enc.patch4(opStart+4, arm.b_calloc(arm.OP_CALL, delta-4)); + } else { + throw Utils.TODO(); + } + } + // General form: "alloc #bytes PC" @Override public void asm(CodeGen code, SB sb) { - sb.p("#calloc, ").p(code.reg(size())); + sb.p("ldi x0=#1\n"); + sb.p("call #calloc"); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java index 19d0408..8fa9118 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/NotARM.java @@ -10,7 +10,7 @@ public class NotARM extends MachConcreteNode implements MachNode{ NotARM(NotNode not) {super(not);} @Override public String op() { return "not"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public RegMask killmap() { return arm.FLAGS_MASK; } @Override public void encoding( Encoding enc ) { // subs xzr, rs, #0 diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java index 7208ea9..9b0e1b2 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrARM.java @@ -10,7 +10,7 @@ public class OrARM extends MachConcreteNode implements MachNode { @Override public String op() { return "or"; } @Override public String glabel() { return "|"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_OR); } // General form: #rd = rs1 & rs2 @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java index c33de85..5f73fb5 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/OrIARM.java @@ -13,7 +13,7 @@ public class OrIARM extends MachConcreteNode implements MachNode { } @Override public String op() { return "ori"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc, this, in(1), arm.OPI_OR, _imm); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java index 7497cb4..2c88f3b 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/RetARM.java @@ -6,38 +6,10 @@ public class RetARM extends ReturnNode implements MachNode { RetARM(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } - @Override public String op() { - return ((FunARM)fun())._frameAdjust > 0 ? "addi" : "ret"; - } - // Correct Nodes outside the normal edges - @Override public void postSelect(CodeGen code) { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - @Override public RegMask regmap(int i) { return arm.retMask(_fun.sig(),i); } - @Override public RegMask outregmap() { return null; } - // RET @Override public void encoding( Encoding enc ) { int frameAdjust = ((FunARM)fun())._frameAdjust; if( frameAdjust > 0 ) enc.add4(arm.imm_inst(arm.OPI_ADD, (frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP)); enc.add4(arm.ret(arm.OP_RET)); } - - @Override public void asm(CodeGen code, SB sb) { - int frameAdjust = ((FunARM)fun())._frameAdjust; - if( frameAdjust>0 ) - sb.p("rsp += #").p(frameAdjust*-8).p("\nret"); - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either X0 or D0), RPC (always R30) and - // then the callee-save registers. - for( int i=2; i= arm.MAX_REG) { throw Utils.TODO(); } - int off = enc._fun.computeStackSlot(dst - arm.MAX_REG)*8; + int off = enc._fun.computeStackOffset(enc._code,dst); if( srcX ) src -= arm.D_OFFSET; - enc.add4(arm.load_str_imm(arm.OP_STORE_IMM, off, arm.RSP, src)); + enc.add4(arm.load_str_imm(arm.OP_STORE_IMM_64, off, arm.RSP, src, 8)); return; } if(src >= arm.MAX_REG) { // Load from SP - int off = enc._fun.computeStackSlot(src - arm.MAX_REG) * 8; + int off = enc._fun.computeStackOffset(enc._code,src); if( dstX ) dst -= arm.D_OFFSET; - enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM, off, arm.RSP, dst)); + enc.add4(arm.load_str_imm(arm.OP_LOAD_IMM_64, off, arm.RSP, dst, 8)); return; } @@ -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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java index 2bab0ee..283263b 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/StoreARM.java @@ -22,12 +22,18 @@ public class StoreARM extends MemOpARM { } @Override public String op() { return "st"+_sz; } @Override public RegMask outregmap() { return null; } + + private static final int[] OP_STORES = new int[]{ arm.OP_STORE_IMM_8, arm.OP_STORE_IMM_16, arm.OP_STORE_IMM_32, arm.OP_STORE_IMM_64, }; + private int imm_op() { + return _declaredType == SONTypeFloat.F32 ? arm.OPF_STORE_IMM_32 + : _declaredType == SONTypeFloat.F64 ? arm.OPF_STORE_IMM_64 + : OP_STORES[_declaredType.log_size()]; + } + + private static final int[] OP_STORE_RS = new int[]{ arm.OP_STORE_R_8, arm.OP_STORE_R_16, arm.OP_STORE_R_32, arm.OP_STORE_R_64, }; + @Override public void encoding( Encoding enc ) { - if(_declaredType == SONTypeFloat.F32 || _declaredType == SONTypeFloat.F64) { - ldst_encode(enc, arm.OPF_STORE_IMM,arm.OPF_STORE_R, val(), true); - } else { - ldst_encode(enc, arm.OP_STORE_IMM, arm.OP_STORE_R, val(), false); - } + ldst_encode(enc, imm_op(), OP_STORE_RS[_declaredType.log_size()], val(), size()); } @Override public void asm(CodeGen code, SB sb) { asm_address(code,sb).p(","); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java index 43e71fb..7cafadf 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubARM.java @@ -8,7 +8,7 @@ public class SubARM extends MachConcreteNode implements MachNode { SubARM(Node sub) { super(sub); } @Override public String op() { return "sub"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_SUB); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java index b7ff3a9..2102c14 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/SubIARM.java @@ -16,9 +16,11 @@ public class SubIARM extends MachConcreteNode implements MachNode { } @Override public String op() { return _imm == -1 ? "dec" : "subi"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } - @Override public void encoding( Encoding enc ) { arm.imm_inst(enc,this, in(1), 0b1101000100,_imm); } + @Override public void encoding( Encoding enc ) { + arm.imm_inst(enc,this, in(1), arm.OPI_SUB,_imm); + } // General form: "subi rd = rs1 - imm" @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java index 65e314f..6405475 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/UJmpARM.java @@ -42,7 +42,7 @@ public class UJmpARM extends CFGNode implements MachNode, RIPRelSize { @Override public void asm(CodeGen code, SB sb) { CFGNode target = uctrl(); - assert target.nOuts() > 1; // Should optimize jmp to empty targets + //assert target.nOuts() > 1; // Should optimize jmp to empty targets sb.p(label(target)); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java index 0132936..b8b5574 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorARM.java @@ -9,7 +9,7 @@ public class XorARM extends MachConcreteNode implements MachNode{ @Override public String op() { return "xor"; } @Override public String glabel() { return "^"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } @Override public void encoding( Encoding enc ) { arm.r_reg(enc,this,arm.OP_XOR); } // General form: "rd = x1 ^ x2" @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java index cb3a28d..cd72fbf 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/XorIARM.java @@ -17,7 +17,7 @@ public class XorIARM extends MachConcreteNode implements MachNode { @Override public String op() { return "xori"; } @Override public RegMask regmap(int i) { return arm.RMASK; } - @Override public RegMask outregmap() { return arm.RMASK; } + @Override public RegMask outregmap() { return arm.WMASK; } // General form: "xori rd = rs1 ^ imm" @Override public void encoding( Encoding enc ) { arm.imm_inst_n(enc,this, in(1), arm.OPI_XOR,_imm); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java index 34db62e..928d2e6 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/arm/arm.java @@ -4,7 +4,6 @@ import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.*; import com.compilerprogramming.ezlang.compiler.sontypes.*; -import java.io.ByteArrayOutputStream; public class arm extends Machine { public arm( CodeGen code ) { @@ -16,39 +15,33 @@ public arm( CodeGen code ) { @Override public String name() { return "arm"; } // GPR(S) - static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7; - static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15; - static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23; - static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31; + public static final int X0 = 0, X1 = 1, X2 = 2, X3 = 3, X4 = 4, X5 = 5, X6 = 6, X7 = 7; + public static final int X8 = 8, X9 = 9, X10 = 10, X11 = 11, X12 = 12, X13 = 13, X14 = 14, X15 = 15; + public static final int X16 = 16, X17 = 17, X18 = 18, X19 = 19, X20 = 20, X21 = 21, X22 = 22, X23 = 23; + public static final int X24 = 24, X25 = 25, X26 = 26, X27 = 27, X28 = 28, X29 = 29, X30 = 30, RSP = 31; // Floating point registers - static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39; - static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47; - static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55; - static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63; + public static final int D0 = 32, D1 = 33, D2 = 34, D3 = 35, D4 = 36, D5 = 37, D6 = 38, D7 = 39; + public static final int D8 = 40, D9 = 41, D10 = 42, D11 = 43, D12 = 44, D13 = 45, D14 = 46, D15 = 47; + public static final int D16 = 48, D17 = 49, D18 = 50, D19 = 51, D20 = 52, D21 = 53, D22 = 54, D23 = 55; + public static final int D24 = 56, D25 = 57, D26 = 58, D27 = 59, D28 = 60, D29 = 61, D30 = 62, D31 = 63; - static final int MAX_REG = 64; - static final int D_OFFSET = 31; static final int FLAGS = 64; + static final int MAX_REG = 65; + public static final int D_OFFSET = 32; static final String[] REGS = new String[] { - "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", - "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", - "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", - "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP", - "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", - "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", - "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23", - "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", - "flags" + "X0", "X1", "X2", "X3", "X4", "X5", "X6", "X7", + "X8", "X9", "X10", "X11", "X12", "X13", "X14", "X15", + "X16", "X17", "X18", "X19", "X20", "X21", "X22", "X23", + "X24", "X25", "X26", "X27", "X28", "X29", "RPC", "RSP", + "D0", "D1", "D2", "D3", "D4", "D5", "D6", "D7", + "D8", "D9", "D10", "D11", "D12", "D13", "D14", "D15", + "D16", "D17", "D18", "D19", "D20", "D21", "D22", "D23", + "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", + "flags" }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]"; - } - // Stack slots, in units of 8 bytes. - @Override public int stackSlot( int reg ) { - return reg < REGS.length ? -1 : reg-REGS.length; - } + @Override public String[] regs() { return REGS; } // from (x0-x30) // General purpose register mask: pointers and ints, not floats @@ -64,11 +57,9 @@ public arm( CodeGen code ) { // Load/store mask; both GPR and FPR static final RegMask MEM_MASK = new RegMask(WR_BITS | FP_BITS); - static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -1L); + static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS, -2L/*skip flags*/); static final RegMask FLAGS_MASK = new RegMask(FLAGS); - // x30 (LR): Procedure link register, used to return from subroutines. - static final RegMask RPC_MASK = new RegMask(1L << X30); // Arguments masks static final RegMask X0_MASK = new RegMask(X0); @@ -93,9 +84,11 @@ public arm( CodeGen code ) { // major opcode: OP public static int OP_ADD = 0b10_001_011; public static int OPF_ADD = 0b00_011_110; + public static int OPF_OP_ADD = 0b00_101_0; public static int OPI_ADD = 0b10_010_00100; - public static int OP_UJMP = 0b01010100; + public static int OPI_SUB = 0b11_010_00100; + public static int OP_UJMP = 0b000101; public static int OP_ADRP = 0b10000; @@ -105,7 +98,6 @@ public arm( CodeGen code ) { public static int OP_LSL = 0b1000; public static int OPI_LSL =0b1101001101; - public static int OP_LSR = 0b1001; public static int OPI_LSR = 0b1101001101; @@ -147,17 +139,39 @@ public arm( CodeGen code ) { public static int OP_RET = 0b1101011001011111000000; // Store/load opcodes - public static int OP_LOAD_R = 0b11111000011; - public static int OP_LOAD_IMM = 0b1111100101; + public static int OP_LOAD_R_64 = 0b11111000011; + public static int OP_LOAD_R_32 = 0b10111000011; + public static int OP_LOAD_R_16 = 0b01111000010; + public static int OP_LOAD_R_8 = 0b00111000011; + + public static int OP_LOAD_IMM_64 = 0b1111100101; + public static int OP_LOAD_IMM_32 = 0b1011100101; + public static int OP_LOAD_IMM_16 = 0b0111100101; + public static int OP_LOAD_IMM_8 = 0b0011100101; + + // load + public static int OPF_LOAD_R_64 = 0b11111100011; + public static int OPF_LOAD_R_32 = 0b10111100011; + + public static int OPF_LOAD_IMM_64 = 0b1111110101; + public static int OPF_LOAD_IMM_32 = 0b1011110101; - public static int OPF_LOAD_R = 0b11111100011; - public static int OPF_LOAD_IMM = 0b1111110101; + public static int OP_STORE_R_64 = 0b11111000001; + public static int OP_STORE_R_32 = 0b10111000001; + public static int OP_STORE_R_16 = 0b01111000001; + public static int OP_STORE_R_8 = 0b00111000001; - public static int OP_STORE_R = 0b11111000001; - public static int OP_STORE_IMM = 0b1111100100; + public static int OP_STORE_IMM_64 = 0b1111100100; + public static int OP_STORE_IMM_32 = 0b1011100100; + public static int OP_STORE_IMM_16 = 0b0111100100; + public static int OP_STORE_IMM_8 = 0b0011100100; + + public static int OPF_STORE_R_64 = 0b11111100001; + public static int OPF_STORE_R_32 = 0b10111100001; + + public static int OPF_STORE_IMM_32 = 0b1111110100; + public static int OPF_STORE_IMM_64 = 0b1111110100; - public static int OPF_STORE_R = 0b11111100001; - public static int OPF_STORE_IMM = 0b1111110100; public static int OP_FMOV = 0b10011110; public static int OP_FMOV_REG = 0b00011110; @@ -167,24 +181,24 @@ public arm( CodeGen code ) { // for incoming argument idx. // index 0 for control, 1 for memory, real args start at index 2 static final RegMask[] CALLINMASK = new RegMask[] { - X0_MASK, - X1_MASK, - X2_MASK, - X3_MASK, - X4_MASK, - X5_MASK, - X6_MASK, - X7_MASK, + X0_MASK, + X1_MASK, + X2_MASK, + X3_MASK, + X4_MASK, + X5_MASK, + X6_MASK, + X7_MASK, }; static final RegMask[] XMMS = new RegMask[] { - D0_MASK, - D1_MASK, - D2_MASK, - D3_MASK, - D4_MASK, - D5_MASK, - D6_MASK, - D7_MASK, + D0_MASK, + D1_MASK, + D2_MASK, + D3_MASK, + D4_MASK, + D5_MASK, + D6_MASK, + D7_MASK, }; // ARM ENCODING @@ -218,17 +232,6 @@ public enum STORE_LOAD_OPTION { SXTX, // base+ s64 index [<< logsize] } - static public String invert(String op) { - return switch (op) { - case "==" -> "!="; - case "!=" -> "=="; - case ">" -> "<="; - case "<" -> ">="; - case ">=" -> "<"; - case "<=" -> ">"; - default -> throw new IllegalStateException("Unexpected value: " + op); - }; - } public enum COND { EQ, NE, @@ -269,10 +272,16 @@ private static int imm12Logical(SONTypeInteger ti) { if (val == 0 || val == -1) return -1; // Special cases are not allowed int immr = 0; // Rotate until we have 0[...]1 + + // Rotate until: + // The number is negative (MSB is 1) + // Or the LSB is not 1 while (val < 0 || (val & 1)==0) { + // circular rotation val = (val >>> 63) | (val << 1); immr++; } + // Start by assuming that val might be made of two 32-bit chunks that repeat. int size = 32; long pattern = val; // Is upper half of pattern the same as the lower? @@ -290,7 +299,7 @@ private static int imm12Logical(SONTypeInteger ti) { return (32-size)<<1 | immr << 6 | imms; } - // sh is encoded in opcdoe + // sh is encoded in opcode public static int imm_inst(int opcode, int imm12, int rn, int rd) { assert 0 <= rn && rn < 32; assert 0 <= rd && rd < 32; @@ -307,10 +316,18 @@ public static int imm_shift(int opcode, int imm, int imms, int rn, int rd) { public static void imm_inst(Encoding enc, Node n,Node n2, int opcode, int imm12) { short self = enc.reg(n); short reg1 = enc.reg(n2); + int body = imm_inst(opcode, imm12&0xFFF, reg1, self); enc.add4(body); } + public static void imm_inst_subs(Encoding enc, Node n,Node n2, int opcode, int imm12) { + short reg1 = enc.reg(n2); + // 31 = 11111 + int body = imm_inst(opcode, imm12&0xFFF, reg1, 31); + enc.add4(body); + } + // for cases where rs1 and dst are the same, eg add x0, x0, 1 public static void imm_inst(Encoding enc, int opcode, int imm12, int self) { int body = imm_inst(opcode, imm12&0xFFF, self, self); @@ -320,7 +337,6 @@ public static void imm_inst(Encoding enc, int opcode, int imm12, int self) { public static void imm_inst_n(Encoding enc, Node n, Node n2, int opcode, int imm13) { short self = enc.reg(n); short reg1 = enc.reg(n2); - int body = imm_inst_n(opcode, imm13, reg1, self); enc.add4(body); } @@ -336,7 +352,7 @@ public static int imm_inst_n(int opcode, int imm13, int rn, int rd) { public static int imm_inst_l(int opcode, int imm12, int self) { int body = imm_inst(opcode, imm12&0xFFF, self, self); return body; - } + } // for cases where rs1 and dst are the same, eg add x0, x0, 1 public static int imm_inst_l(Encoding enc, Node n, int opcode, int imm12) { @@ -352,14 +368,22 @@ public static int r_reg(int opcode, int shift, int rm, int imm6, int rn, int rd) assert 0 <= rm && rm < 32; assert 0 <= rn && rn < 32; assert 0 <= rd && rd < 32; - return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) << (rn << 5) | rd; - } - public static void r_reg(Encoding enc, Node n, int opcode) { - short self = enc.reg(n); - short reg1 = enc.reg(n.in(1)); - short reg2 = enc.reg(n.in(2)); - int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self); - enc.add4(body); + return (opcode << 24) | (shift << 21) | (rm << 16) | (imm6 << 10) | (rn << 5) | rd; + } + public static void r_reg(Encoding enc, Node n, int opcode) { + short self = enc.reg(n); + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + int body = r_reg(opcode, 0, reg2, 0, reg1, self >= 32 ? reg1: self); + enc.add4(body); + } + + public static void r_reg_subs(Encoding enc, Node n, int opcode) { + short reg1 = enc.reg(n.in(1)); + short reg2 = enc.reg(n.in(2)); + // 31 = 11111 + int body = r_reg(opcode, 0, reg2, 0, reg1, 31); + enc.add4(body); } public static int shift_reg(int opcode, int rm, int op2, int rn, int rd) { @@ -434,6 +458,7 @@ public static void f_scalar(Encoding enc, Node n, int op ) { // share same encoding with LDR (literal, SIMD&FP) flavour public static int load_pc(int opcode, int offset, int rt) { + offset>>=2; return (opcode << 24) | (offset << 5) | rt; } // int l @@ -449,10 +474,15 @@ public static int indr_adr(int opcode, int off, STORE_LOAD_OPTION option, int s, return (opcode << 21) | (off << 16) | (option.ordinal() << 13) | (s << 12) | (2 << 10) | (ptr << 5) | rt; } // [Rptr+imm9] - public static int load_str_imm(int opcode, int imm12, int ptr, int rt) { + public static int load_str_imm(int opcode, int imm12, int ptr, int rt, int size) { assert 0 <= ptr && ptr < 32; assert 0 <= rt && rt < 32; - return (opcode << 22) | (imm12 << 10) |(ptr << 5) | rt; + + if(size == 8) imm12 = imm12 >> 3; + if(size == 4) imm12 = imm12 >> 2; + if(size == 2) imm12 = imm12 >> 1; + // size == 1 imm12 = imm12 + return (opcode << 22) | ((imm12) << 10) | (ptr << 5) | rt; } // encoding for vcvt, size is encoded in operand @@ -476,7 +506,8 @@ public static int float_cast(int opcode, int ftype, int rn, int rd) { public static int f_cmp(int opcode, int ftype, int rm, int rn) { assert 0 <= rn && rn < 32; assert 0 <= rm && rm < 32; - return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5) | 8; + // Todo: |8 is not needed + return (opcode << 24) | (ftype << 21) | (rm << 16) | (8 << 10) | (rn << 5); } public static void f_cmp(Encoding enc, Node n) { short reg1 = (short)(enc.reg(n.in(1))-D_OFFSET); @@ -489,19 +520,21 @@ public static COND make_condition(String bop) { return switch (bop) { case "==" -> COND.EQ; case "!=" -> COND.NE; - case "<" -> COND.LE; - case "<=" -> COND.LT; - case ">=" -> COND.GT; - case ">" -> COND.GE; + case "<" -> COND.LT; + case "<=" -> COND.LE; + case ">=" -> COND.GE; + case ">" -> COND.GT; default -> throw Utils.TODO(); }; } - // Todo: maybe missing zero here after delta << 5 + public static int b_cond(int opcode, int delta, COND cond) { // 24-5 == 19bits offset range assert -(1<<19) <= delta && delta < (1<<19); + assert (delta&3)==0; + delta>>=2; delta &= (1L<<19)-1; // Zero extend - return (opcode << 24) | (delta << 5) | cond.ordinal(); + return (opcode << 24) | ((delta)<< 5) | cond.ordinal(); } public static int cond_set(int opcode, int rm, COND cond, int rn, int rd) { @@ -517,12 +550,22 @@ public static int blr(int opcode, int rd) { } public static int b(int opcode, int delta) { assert -(1<<26) <= delta && delta < (1<<26); + assert (delta&3)==0; + delta>>=2; + delta &= (1L<<26)-1; // Zero extend + return (opcode << 26) | delta; + } + // no aligned assert(assert (delta&3)==0;) + public static int b_calloc(int opcode, int delta) { + assert -(1<<26) <= delta && delta < (1<<26); + delta>>=2; delta &= (1L<<26)-1; // Zero extend return (opcode << 26) | delta; } + @Override public RegMask callArgMask(SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } static RegMask callInMask(SONTypeFunPtr tfp, int idx ) { - if( idx==0 ) return RPC_MASK; + if( idx==0 ) return CodeGen.CODE._rpcMask; if( idx==1 ) return null; // Count floats in signature up to index int fcnt=0; @@ -540,63 +583,55 @@ static RegMask callInMask(SONTypeFunPtr tfp, int idx ) { } throw Utils.TODO(); // Pass on stack slot } + + public static long decodeImm12(int imm12) { + int immr = (imm12 >> 6) & 0x3F; + int imms = imm12 & 0x3F; + int size; + if ((imm12 & 0x1000) != 0) { + size = 64; + } else { + size = 31-(imms >> 1); + size |= size >> 1; + size |= size >> 2; + size |= size >> 4; + size++; + imms &= ~((32-size) << 1); + } + long val = (2L << imms)-1; + while (size < 64) { + val |= val << size; + size <<= 1; + } + val = (val >>> immr) | val << (64-immr); + return val; + } + // Return the max stack slot used by this signature, or 0 - static short maxSlot( SONTypeFunPtr tfp ) { - // Count floats in signature up to index - int fcnt=0; - for( int i=0; i= XMMS.length ) - throw Utils.TODO(); - RegMask[] cargs = CALLINMASK; - if( tfp.nargs()-fcnt >= cargs.length ) - throw Utils.TODO(); - return 0; // No stack args - } - - static final long CALLEE_SAVE = - 1L< and(and); case BoolNode bool -> cmp(bool); case CallNode call -> call(call); - case CastNode cast -> new CastNode(cast); + case CastNode cast -> new CastARM(cast); case CallEndNode cend -> new CallEndARM(cend); case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); @@ -629,6 +659,7 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) { case IfNode iff -> jmp(iff); case LoadNode ld -> ld(ld); case MemMergeNode mem -> new MemMergeNode(mem); + case MinusNode neg -> new NegARM(neg); case MulFNode mulf -> new MulFARM(mulf); case MulNode mul -> new MulARM(mul); case NewNode nnn -> new NewARM(nnn); @@ -658,16 +689,14 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) { private Node cmp(BoolNode bool){ Node cmp = _cmp(bool); - return new SetARM(cmp, invert(bool.op())); + return new SetARM(cmp, IfNode.negate(bool.op())); } private Node _cmp(BoolNode bool) { - if( bool instanceof BoolNode.EQF || - bool instanceof BoolNode.LTF || - bool instanceof BoolNode.LEF ) + if( bool.isFloat() ) return new CmpFARM(bool); return bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti - ? new CmpIARM(bool, (int)ti.value()) - : new CmpARM(bool); + ? new CmpIARM(bool, (int)ti.value()) + : new CmpARM(bool); } private Node ld(LoadNode ld) { @@ -678,47 +707,49 @@ private Node jmp(IfNode iff) { // 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(SONTypeInteger.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(SONTypeInteger.ZERO))); + return new BranchARM(iff, op); } private Node add(AddNode add) { return add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti) - ? new AddIARM(add, (int)ti.value()) - : new AddARM(add); + ? new AddIARM(add, (int)ti.value()) + : new AddARM(add); } private Node sub(SubNode sub) { return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti) - ? new AddIARM(sub, (int)(-ti.value())) - : new SubARM(sub); + ? new SubIARM(sub, (int)(ti.value())) + : new SubARM(sub); } - private Node con(ConstantNode con) { + private Node con( ConstantNode con ) { if( !con._con.isConstant() ) return new ConstantNode( con ); // Default unknown caller inputs return switch( con._con ) { - case SONTypeInteger ti -> new IntARM(con); - case SONTypeFloat tf -> new FloatARM(con); - case SONTypeFunPtr tfp -> new TFPARM(con); - case SONTypeMemPtr tmp -> new ConstantNode(con); - case SONTypeNil tn -> throw Utils.TODO(); - // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. - case SONType t -> new ConstantNode(con); + case SONTypeInteger ti -> new IntARM(con); + case SONTypeFloat tf -> new FloatARM(con); + case SONTypeFunPtr tfp -> new TFPARM(con); + case SONTypeMemPtr tmp -> new ConstantNode(con); + case SONTypeNil tn -> throw Utils.TODO(); + // TOP, BOTTOM, XCtrl, Ctrl, etc. Never any executable code. + case SONType t -> t==SONType.NIL ? new IntARM(con) : new ConstantNode(con); }; } private Node call(CallNode call){ return call.fptr() instanceof ConstantNode con && con._con instanceof SONTypeFunPtr tfp - ? new CallARM(call, tfp) - : new CallRRARM(call); + ? new CallARM(call, tfp) + : new CallRRARM(call); } private Node or(OrNode or) { int imm12; return or.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && (imm12 = imm12Logical(ti)) != -1 - ? new OrIARM(or, imm12) - : new OrARM(or); + ? new OrIARM(or, imm12) + : new OrARM(or); } private Node xor(XorNode xor) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java index 3fe6397..6c8c325 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/BranchRISC.java @@ -21,11 +21,13 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { @Override public String comment() { return "L"+cproj(1)._nid; } @Override public RegMask regmap(int i) { return riscv.RMASK; } @Override public RegMask outregmap() { return null; } - @Override public void invert() { - if( _bop.equals("<") || _bop.equals("<=") ) - swap12(); // Cannot invert the test, so swap the operands - else - _bop = invert(_bop); + @Override public void negate() { + _bop = negate(_bop); + // Cannot encode ">" or "<=", so flip these + if( _bop.equals(">") || _bop.equals("<=") ) { + swap12(); // Cannot negate the test, so swap the operands + _bop = swap(_bop); // Swap test to match + } } @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { @@ -39,15 +41,21 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { } @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 +63,25 @@ public BranchRISC( IfNode iff, String bop, Node n1, Node n2 ) { // 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java index 32cbb5b..12f2b81 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallEndRISC.java @@ -1,23 +1,8 @@ package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv; -import com.compilerprogramming.ezlang.compiler.SB; -import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; -import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr; public class CallEndRISC extends CallEndNode implements MachNode { - final SONTypeFunPtr _tfp; - CallEndRISC( CallEndNode cend ) { - super(cend); - _tfp = (SONTypeFunPtr)(cend.call().fptr()._type); - } - @Override public String op() { return "cend"; } - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask outregmap(int idx) { return idx == 2 ? riscv.retMask(_tfp,2) : null; } - @Override public RegMask killmap() { return riscv.riscCallerSave(); } - @Override public void encoding( Encoding enc ) { } - @Override public void asm(CodeGen code, SB sb) { } + CallEndRISC( CallEndNode cend ) { super(cend); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java index 5b4512f..292cf6a 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/CallRISC.java @@ -19,8 +19,6 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize { @Override public String op() { return "call"; } @Override public String label() { return op(); } @Override public RegMask regmap(int i) { - // Last call input is AUIPC - if( i == nIns()-1 ) return riscv.RMASK; return riscv.callInMask(_tfp,i); } @Override public RegMask outregmap() { return riscv.RPC_MASK; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java index 32e205d..f9dd6e6 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FltRISC.java @@ -22,7 +22,7 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize { // AUIPC dst,#hi20_constant_pool enc.add4(riscv.u_type(riscv.OP_AUIPC, tmp, 0)); // Load dst,[dst+#low12_constant_pool] - enc.add4(riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, 0)); + enc.add4(riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, 0)); } @Override public RegMask killmap() { return new RegMask(riscv.T6); } @@ -35,9 +35,9 @@ public class FltRISC extends ConstantNode implements MachNode, RIPRelSize { short dst = (short)(enc.reg(this) - riscv.F_OFFSET); short tmp = (short)riscv.T6; // AUIPC dst,#hi20_constant_pool - enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta)); + enc.patch4(opStart , riscv.u_type(riscv.OP_AUIPC, tmp, delta>>12)); // Load dst,[dst+#low12_constant_pool] - enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOAD, dst, 0b11, tmp, delta)); + enc.patch4(opStart+4, riscv.i_type(riscv.OP_LOADFP, dst, 0b11, tmp, delta & 0xFFF)); } @Override public void asm(CodeGen code, SB sb) { diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java index c06a40c..b9c50ec 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/FunRISC.java @@ -1,36 +1,22 @@ package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv; -import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.SB; +import com.compilerprogramming.ezlang.compiler.Utils; +import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.FunNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; public class FunRISC extends FunNode implements MachNode { FunRISC(FunNode fun) {super(fun);} - @Override public String op() { return "addi"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public void postSelect(CodeGen code) { code.link(this); } - - // Actual stack layout is up to each CPU. - // X86, with too many args & spills: - // | CALLER | - // | argN | // slot 1, required by callER - // +--------+ - // | RPC | // slot 0, required by callER - // | callee | // slot 3, callEE - // | callee | // slot 2, callEE - // | PAD16 | - // +--------+ - @Override public void encoding( Encoding enc ) { - // Stack frame size: _maxSlot - max(arg), padded to 16. - _maxArgSlot = riscv.maxSlot(enc._fun.sig()); - _frameAdjust = (short) (_maxSlot+1 - _maxArgSlot); - if( _frameAdjust == 0 ) return; // Skip if no frame adjust - enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (_frameAdjust*8) & 0xFFF)); + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); } - - @Override public void asm(CodeGen code, SB sb) { - sb.p("rsp += #").p(_frameAdjust*8); + @Override public void encoding( Encoding enc ) { + int sz = _frameAdjust; + if( sz == 0 ) return; // Skip if no frame adjust + if( sz >= 1L<<12 ) throw Utils.TODO(); + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF)); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java index 9257b4d..150a8b9 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/ImmRISC.java @@ -11,7 +11,7 @@ abstract public class ImmRISC extends MachConcreteNode implements MachNode { ImmRISC( Node add, int imm12, boolean pop ) { super(add); _imm12 = imm12; - // If pop, we are moving from a ideal Node to MachNode; the ideal Node + // If pop, we are moving from an ideal Node to MachNode; the ideal Node // inputs got copied here, but we are absorbing the immediate into the // MachNode and so do not want the constant input. if( pop ) _inputs.pop(); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java index c230467..d01a11d 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/IntRISC.java @@ -4,6 +4,7 @@ import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.ConstantNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; +import com.compilerprogramming.ezlang.compiler.sontypes.SONType; import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeInteger; // 12-bit integer constant. Larger constants are made up in the instruction @@ -17,10 +18,10 @@ public class IntRISC extends ConstantNode implements MachNode { @Override public IntRISC copy() { return new IntRISC(this); } @Override public void encoding( Encoding enc ) { short dst = enc.reg(this); - SONTypeInteger ti = (SONTypeInteger)_con; + int val = _con==SONType.NIL ? 0 : (int)(((SONTypeInteger)_con).value() & 0xFFF); // Explicit truncation of larger immediates; this will sign-extend on // load and this is handled during instruction selection. - enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, (int)(ti.value() & 0xFFF))); + enc.add4(riscv.i_type(riscv.OP_IMM, dst, 0, riscv.ZERO, val)); } @Override public void asm(CodeGen code, SB sb) { String reg = code.reg(this); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java index c66f079..90b6a49 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/MemOpRISC.java @@ -52,7 +52,7 @@ int func3() { // float if(_declaredType == SONTypeFloat.F32) func3 = 2; // fLW fSW if(_declaredType == SONTypeFloat.F64) func3 = 3; // fLD fSD - if( _declaredType instanceof SONTypeMemPtr ) func3=6; // 4 byte pointers, assumed unsigned? + if( _declaredType instanceof SONTypeMemPtr ) func3=3; // 8 byte pointers if( func3 == -1 ) throw Utils.TODO(); return func3; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java deleted file mode 100644 index d7d6e2e..0000000 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NJmpRISC.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv; - -import com.compilerprogramming.ezlang.compiler.*; -import com.compilerprogramming.ezlang.compiler.codegen.*; -import com.compilerprogramming.ezlang.compiler.nodes.*; -import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr; -import java.util.BitSet; - -public class NJmpRISC extends NeverNode implements MachNode, RIPRelSize { - // label is obtained implicitly - public NJmpRISC( CFGNode cfg ) { super(cfg); } - - @Override public String op() { return "jmp"; } - @Override public String label() { return op(); } - @Override public String comment() { return "L"+cproj(1)._nid; } - @Override public StringBuilder _print1( StringBuilder sb, BitSet visited ) { return sb.append("jmp "); } - @Override public RegMask regmap(int i) { throw Utils.TODO(); } - @Override public RegMask outregmap() { return null; } - - @Override public void encoding( Encoding enc ) { - // Short form +/-4K: beq r0,r0,imm12 - // Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32] - enc.jump(this,cproj(0)); - enc.add4(riscv.j_type(riscv.OP_JAL, 0, 0)); - } - - // Delta is from opcode start - @Override public byte encSize(int delta) { - if( -(1L<<20) <= delta && delta < (1L<<20) ) return 4; - // 2 word encoding needs a tmp register, must teach RA - throw Utils.TODO(); - } - - // Delta is from opcode start - @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { - if( opLen==4 ) { - enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, 0, delta)); - } else { - throw Utils.TODO(); - } - } - - @Override public void asm(CodeGen code, SB sb) { - sb.p(label(cproj(0).uctrl())); - } -} diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java new file mode 100644 index 0000000..32178d6 --- /dev/null +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NegRISC.java @@ -0,0 +1,20 @@ +package com.compilerprogramming.ezlang.compiler.nodes.cpus.riscv; + +import com.compilerprogramming.ezlang.compiler.SB; +import com.compilerprogramming.ezlang.compiler.codegen.*; +import com.compilerprogramming.ezlang.compiler.nodes.*; + +public class NegRISC extends MachConcreteNode implements MachNode { + public NegRISC( Node neg ) { super(neg); } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { assert i==1; return riscv.RMASK; } + @Override public RegMask outregmap() { return riscv.WMASK; } + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + short src = enc.reg(in(1)); + riscv.r_type(riscv.OP,dst,0,0,src,0x20); + } + @Override public void asm(CodeGen code, SB sb) { + sb.p(code.reg(this)).p(" = -").p(code.reg(in(1))); + } +} diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java index 8c3fbeb..71a9bce 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NewRISC.java @@ -5,29 +5,42 @@ import com.compilerprogramming.ezlang.compiler.nodes.*; import com.compilerprogramming.ezlang.compiler.print.ASMPrinter; -public class NewRISC extends NewNode implements MachNode { +public class NewRISC extends NewNode implements MachNode, RIPRelSize { // A pre-zeroed chunk of memory. NewRISC( NewNode nnn ) { super(nnn); } - @Override public String op() { return "alloc"; } - @Override public RegMask regmap(int i) { - return i==1 ? riscv.A0_MASK : null; // Size input or mem aliases - } - @Override public RegMask outregmap(int i) { return i == 1 ? riscv.A0_MASK : null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask killmap() { return riscv.riscCallerSave(); } - @Override public void encoding( Encoding enc ) { // Generic external encoding; 2 ops. enc.external(this,"calloc"); // A1 is a caller-save, allowed to crush building external address // auipc - enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A1, 0)); - enc.add4(riscv.i_type(riscv.OP_JALR, riscv.RPC, 0, riscv.A1, 0)); + enc.add4(riscv.i_type(riscv.OP_IMM , _arg2Reg , 0, riscv.ZERO, 1)); + enc.add4(riscv.u_type(riscv.OP_AUIPC, riscv.A2 , 0)); + enc.add4(riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, 0)); + } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public byte encSize(int delta) { return 4*3; } + + // Patch is for running "new" in a JIT. + // Delta is from opcode start + @Override public void patch( Encoding enc, int opStart, int opLen, int delta ) { + // Negative patches are JIT emulator targets. + if( opStart+delta < 0 ) { + delta -= 4; // Offset from the AUIPC start + int imm20 = delta>>12; + if( ((delta>>11)&1)==1 ) imm20++; // Correct accidental sign extension + int imm12 = delta&0xFFF; + enc.patch4(opStart+4,riscv.u_type(riscv.OP_AUIPC, riscv.A2 , imm20)); + enc.patch4(opStart+8,riscv.i_type(riscv.OP_JALR , riscv.RPC, 0, riscv.A2, imm12)); + } else + throw Utils.TODO(); } // General form: "alloc #bytes PC" @Override public void asm(CodeGen code, SB sb) { - sb.p("auipc a1=#calloc\n"); - sb.p("call a1+#calloc, a0"); + sb.p("ldi a1=#1\n"); + sb.p("auipc a2=#calloc\n"); + sb.p("call a2+#calloc, a0"); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java index 81b568a..5b705dc 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/NotRISC.java @@ -2,15 +2,19 @@ import com.compilerprogramming.ezlang.compiler.SB; import com.compilerprogramming.ezlang.compiler.codegen.*; +import com.compilerprogramming.ezlang.compiler.nodes.MachConcreteNode; +import com.compilerprogramming.ezlang.compiler.nodes.MachNode; import com.compilerprogramming.ezlang.compiler.nodes.NotNode; -public class NotRISC extends ImmRISC { - NotRISC(NotNode not) { super(not,1); } - @Override public RegMask regmap(int i) { return riscv.RMASK; } - @Override public RegMask outregmap() { return riscv.RMASK; } +public class NotRISC extends MachConcreteNode implements MachNode { + public NotRISC(NotNode not) { super(not); } @Override public String op() { return "not"; } - // sltiu: x 0 ? "addi" : "ret"; - } - // Correct Nodes outside the normal edges - @Override public void postSelect(CodeGen code) { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - @Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); } - @Override public RegMask outregmap() { return null; } + public RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); } @Override public void encoding( Encoding enc ) { - int frameAdjust = fun()._frameAdjust; - if( frameAdjust > 0 ) - enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (frameAdjust*-8) & 0xFFF)); + int sz = fun()._frameAdjust; + if( sz >= 1L<<12 ) throw Utils.TODO(); + if( sz != 0 ) + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, sz & 0xFFF)); short rpc = enc.reg(rpc()); enc.add4(riscv.i_type(riscv.OP_JALR, riscv.ZERO, 0, rpc, 0)); } - - @Override public void asm(CodeGen code, SB sb) { - int frameAdjust = fun()._frameAdjust; - if( frameAdjust>0 ) - sb.p("rsp += #").p(frameAdjust*-8).p("\nret"); - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either A0 or FA0), RPC (always R1) and then - // the callee-save registers. - for( int i=2; i= riscv.MAX_REG ) { throw Utils.TODO(); // Very rare stack-stack move } - int off = enc._fun.computeStackSlot(dst - riscv.MAX_REG)*8; + int off = enc._fun.computeStackOffset(enc._code,dst); int op = srcX ? riscv.OP_STOREFP : riscv.OP_STORE; if( srcX ) src -= riscv.F_OFFSET; - enc.add4(riscv.s_type(op, 0b011, riscv.SP, src, off)); + enc.add4(riscv.s_type(op, 0b011, riscv.RSP, src, off)); return; } if( src >= riscv.MAX_REG ) { // Load from SP - int off = enc._fun.computeStackSlot(src - riscv.MAX_REG)*8; + int off = enc._fun.computeStackOffset(enc._code,src); int op = dstX ? riscv.OP_LOADFP : riscv.OP_LOAD; if( dstX ) dst -= riscv.F_OFFSET; - enc.add4(riscv.i_type(op, dst, 0b011, riscv.SP, off)); + enc.add4(riscv.i_type(op, dst, 0b011, riscv.RSP, off)); return; } // pick opcode based on regs @@ -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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java index 7a76f55..d732933 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/StoreRISC.java @@ -27,7 +27,7 @@ public class StoreRISC extends MemOpRISC { if( i==2 ) return riscv.RMASK; // ptr // 2 - index if( i==4 ) return riscv.MEM_MASK; // Wide mask to store GPR and FPR - throw Utils.TODO(); + return null; // Anti-dependence } @Override public RegMask outregmap() { return null; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java index 07959cf..7ddb3aa 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/UJmpRISC.java @@ -40,7 +40,7 @@ public class UJmpRISC extends CFGNode implements MachNode, RIPRelSize { @Override public void asm(CodeGen code, SB sb) { CFGNode target = uctrl(); - assert target.nOuts() > 1; // Should optimize jmp to empty targets + //assert target.nOuts() > 1; // Should optimize jmp to empty targets sb.p(label(target)); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java index c9adfeb..eb10fab 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/riscv/riscv.java @@ -13,39 +13,51 @@ public riscv(CodeGen code) { } @Override public String name() {return "riscv";} + //@Override public int maxReg() { return MAX_REG; } // Using ABI names instead of register names - public static int ZERO = 0, RPC= 1, SP = 2, S12= 3, S13= 4, T0 = 5, T1 = 6, T2 = 7; + public static int ZERO = 0, RPC= 1, RSP= 2, S12= 3, S13= 4, T0 = 5, T1 = 6, T2 = 7; public static int S0 = 8, S1 = 9, A0 = 10, A1 = 11, A2 = 12, A3 = 13, A4 = 14, A5 = 15; public static int A6 = 16, A7 = 17, S2 = 18, S3 = 19, S4 = 20, S5 = 21, S6 = 22, S7 = 23; public static int S8 = 24, S9 = 25, S10= 26, S11= 27, T3 = 28, T4 = 29, T5 = 30, T6 = 31; // FP registers - static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; - static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; - static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; - static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; + public static int F0 = 32, F1 = 33, F2 = 34, F3 = 35, F4 = 36, F5 = 37, F6 = 38, F7 = 39; + public static int FS0 = 40, FS1 = 41, FA0 = 42, FA1 = 43, FA2 = 44, FA3 = 45, FA4 = 46, FA5 = 47; + public static int FA6 = 48, FA7 = 49, FS2 = 50, FS3 = 51, FS4 = 52, FS5 = 53, FS6 = 54, FS7 = 55; + public static int FS8 = 56, FS9 = 57, FS10 = 58, FS11 = 59, FT8 = 60, FT9 = 61, FT10 = 62, FT11 = 63; static final int MAX_REG = 64; - static final int F_OFFSET = 32; + public static final int F_OFFSET = 32; static final String[] REGS = new String[] { - "zero","rpc" , "sp" , "s12" , "s13" , "t0" , "t1" , "t2" , - "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , - "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" , - "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" , - "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , - "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , - "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , - "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" + "zero","rpc" , "rsp" , "s12" , "s13" , "t0" , "t1" , "t2" , + "s0" , "s1" , "a0" , "a1" , "a2" , "a3" , "a4" , "a5" , + "a6" , "a7" , "s2" , "s3" , "s4" , "s5" , "s6" , "s7" , + "s8" , "s9" , "s10" , "s11" , "t3" , "t4" , "t5" , "t6" , + "f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , + "fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , + "fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , + "fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" + // Register-based naming hella easier to debug than "official" register + // names when single-stepping in debugger. + //"zero", "r1" , "rsp" , "r3" , "r4" , "r5" , "r6" , "r7" , + //"r8" , "r9" , "r10" , "r11", "r12", "r13", "r14", "r15", + //"r16" , "r17" , "r18" , "r19", "r20", "r21", "r22", "r23", + //"r24" , "r25" , "r26" , "r27", "r28", "r29", "r30", "r31", + //"f0" , "f1" , "f2" , "f3" , "f4" , "f5" , "f6" , "f7" , + //"fs0" , "fs1" , "fa0" , "fa1" , "fa2" , "fa3" , "fa4" , "fa5" , + //"fa6" , "fa7" , "fs2" , "fs3" , "fs4" , "fs5" , "fs6" , "fs7" , + //"fs8" , "fs9" , "fs10", "fs11", "ft8" , "ft9" , "ft10", "ft11" }; + @Override public String[] regs() { return REGS; } // General purpose register mask: pointers and ints, not floats static final long RD_BITS = 0b11111111111111111111111111111111L; // All the GPRs static final RegMask RMASK = new RegMask(RD_BITS); - static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and SP + static final long WR_BITS = 0b11111111111111111111111111111010L; // All the GPRs, minus ZERO and RSP static final RegMask WMASK = new RegMask(WR_BITS); // Float mask from(f0-ft10). TODO: ft10,ft11 needs a larger RegMask static final long FP_BITS = 0b11111111111111111111111111111111L< 0x0; case "!=" -> 0x1; case "<" -> 0x4; - case "<=" -> 0x5; + case ">=" -> 0x5; case "u<" -> 0x6; case "u<=" -> 0x7; default -> throw Utils.TODO(); @@ -241,6 +253,7 @@ static public int fsetop(String op) { }; } + @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } static RegMask callInMask( SONTypeFunPtr tfp, int idx ) { if( idx==0 ) return RPC_MASK; if( idx==1 ) return null; @@ -258,90 +271,42 @@ static RegMask callInMask( SONTypeFunPtr tfp, int idx ) { if( idx-2-fcnt < cargs.length ) return cargs[idx-2-fcnt]; } - throw Utils.TODO(); // Pass on stack slot + // Pass on stack slot(8 and higher) + return new RegMask(MAX_REG + 1 + (idx - 2)); } - // Return the max stack slot used by this signature, or 0 - static short maxSlot( SONTypeFunPtr tfp ) { - // Count floats in signature up to index - int fcnt=0; - for( int i=0; i= XMMS.length ) - throw Utils.TODO(); - RegMask[] cargs = CALLINMASK; - if( tfp.nargs()-fcnt >= cargs.length ) - throw Utils.TODO(); - return 0; // No stack args - } - - // callee saved(riscv) - static final long CALLEE_SAVE = - (1L<< S0) | (1L<< S1) | (1L<< S2 ) | (1L<< S3 ) | - (1L<< S4) | (1L<< S5) | (1L<< S6 ) | (1L<< S7 ) | - (1L<< S8) | (1L<< S9) | (1L<< S10) | (1L<< S11) | - (1L< addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); - case CallNode call -> call(call); - case CastNode cast -> new CastRISC(cast); + case AddFNode addf -> addf(addf); + case AddNode add -> add(add); + case AndNode and -> and(and); + case BoolNode bool -> cmp(bool); + case CallNode call -> call(call); + case CastNode cast -> new CastRISC(cast); case CallEndNode cend -> new CallEndRISC(cend); - case CProjNode c -> new CProjNode(c); + case CProjNode c -> new CProjNode(c); case ConstantNode con -> con(con); - case DivFNode divf -> new DivFRISC(divf); - case DivNode div -> new DivRISC(div); - case FunNode fun -> new FunRISC(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); + case DivFNode divf -> new DivFRISC(divf); + case DivNode div -> new DivRISC(div); + case FunNode fun -> new FunRISC(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); case MemMergeNode mem -> new MemMergeNode(mem); - case MulFNode mulf -> new MulFRISC(mulf); - case MulNode mul -> new MulRISC(mul); - case NewNode nnn -> nnn(nnn); - case NotNode not -> new NotRISC(not); - case OrNode or -> or(or); - case ParmNode parm -> new ParmRISC(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetRISC(ret, ret.fun()); - case SarNode sar -> sra(sar); - case ShlNode shl -> sll(shl); - case ShrNode shr -> srl(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFRISC(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); - case XorNode xor -> xor(xor); + case MinusNode neg -> new NegRISC(neg); + case MulFNode mulf -> new MulFRISC(mulf); + case MulNode mul -> new MulRISC(mul); + case NewNode nnn -> nnn(nnn); + case NotNode not -> new NotRISC(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmRISC(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read-> new ReadOnlyNode(read); + case ReturnNode ret -> new RetRISC(ret, ret.fun()); + case SarNode sar -> sra(sar); + case ShlNode shl -> sll(shl); + case ShrNode shr -> srl(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFRISC(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XorNode xor -> xor(xor); case LoopNode loop -> new LoopNode(loop); case RegionNode region -> new RegionNode(region); @@ -404,7 +370,7 @@ private Node addf(AddFNode addf) { } private Node add(AddNode add) { - if( add.in(2) instanceof ConstantNode off && off._con instanceof SONTypeInteger ti && imm12(ti) ) + if( add.in(2) instanceof ConstantNode off2 && off2._con instanceof SONTypeInteger ti && imm12(ti) ) return new AddIRISC(add, (int)ti.value(),true); return new AddRISC(add); } @@ -437,11 +403,11 @@ private Node cmp(BoolNode bool) { // Only < and y - swap; y < x - // x >= y - swap and invert; !(x < y); `slt tmp=y,x;` then NOT. + // x >= y - swap and negate; !(x < y); `slt tmp=y,x;` then NOT. // x != y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` then NOT. // The ">", ">=" and "!=" in Simple include a NotNode, which can be @@ -451,8 +417,8 @@ private Node cmp(BoolNode bool) { return switch( bool.op() ) { case "<" -> bool.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm12(ti) ? new SetIRISC(bool, (int)ti.value(),false) - : new SetRISC(bool, false); - // x <= y - flip and invert; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` + : new SetRISC(bool, false); + // x <= y - flip and negate; !(y < x); `slt tmp=y,x; xori dst=tmp,#1` case "<=" -> new XorIRISC(new SetRISC(bool.swap12(), false),1); // x == y - sub and vs0 == `sub tmp=x-y; sltu dst=tmp,#1` case "==" -> new SetIRISC(new SubRISC(bool),1,true); @@ -492,12 +458,12 @@ private Node jmp( IfNode iff ) { if( iff.in(1) instanceof BoolNode bool && !bool.isFloat() ) { // if less than or equal switch inputs String bop = bool.op(); - if( bop.equals(">=") || bop.equals(">") ) - return new BranchRISC(iff, IfNode.invert(bop), bool.in(2), bool.in(1)); + if( bop.equals("<=") || bop.equals(">") ) + return new BranchRISC(iff, IfNode.swap(bop), bool.in(2), bool.in(1)); 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java index 89dbcd9..3d51d5a 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/AddMemX86.java @@ -26,10 +26,9 @@ public class AddMemX86 extends MemOpX86 { x86_64_v2.indirectAdr(_scale, idx, ptr, _off, dst, enc); } - // General form: "add dst = src + [base + idx<<2 + 12]" + // General form: "add dst += [base + idx<<2 + 12]" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = "); - sb.p(val()==null ? "#"+_imm : code.reg(val())).p(" + "); + sb.p(code.reg(this)).p(" += "); asm_address(code,sb); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java index 4cf1f22..1dfff37 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallEndX86.java @@ -1,23 +1,8 @@ package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2; -import com.compilerprogramming.ezlang.compiler.SB; -import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.CallEndNode; import com.compilerprogramming.ezlang.compiler.nodes.MachNode; -import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr; public class CallEndX86 extends CallEndNode implements MachNode { - final SONTypeFunPtr _tfp; - CallEndX86( CallEndNode cend ) { - super(cend); - _tfp = (SONTypeFunPtr)(cend.call().fptr()._type); - } - @Override public String op() { return "cend"; } - @Override public String label() { return op(); } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask outregmap(int idx) { return idx == 2 ? x86_64_v2.retMask(_tfp,2) : null; } - @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); } - @Override public void encoding( Encoding enc ) { } - @Override public void asm(CodeGen code, SB sb) { } + CallEndX86( CallEndNode cend ) { super(cend); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java index c18e77f..362f54e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/CallX86.java @@ -36,7 +36,6 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize { enc.patch4(opStart+1, delta-5); } - @Override public void asm(CodeGen code, SB sb) { sb.p(_name).p(" "); for( int i=0; i=2 ) enc.add1(0x8B); // zero extend: 8B /r MOV r32, r/m32 else throw Utils.TODO(); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java index 1096341..a0edf38 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemAddX86.java @@ -23,7 +23,7 @@ public class MemAddX86 extends MemOpX86 { enc.add1(x86_64_v2.rex(src, ptr, idx)); // opcode - enc.add1( src == -1 ? 0x01 : 0x81 ); + enc.add1( src == -1 ? 0x81: 0x01); // includes modrm x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src == -1 ? 0 : src, enc); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java index dde6bca..e6e064d 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/MemOpX86.java @@ -23,13 +23,13 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { final int _off; // Limit 32 bits final int _scale; // Limit 0,1,2,3 final int _imm; // Limit 32 bits - final char _sz = (char)('0'+(1<<_declaredType.log_size())); + final char _sz; // Handy print name MemOpX86( Node op, MemOpNode mop, Node base, Node idx, int off, int scale, int imm ) { super(op,mop); assert base._type instanceof SONTypeMemPtr && !(base instanceof AddNode); assert (idx==null && scale==0) || (idx!=null && 0<= scale && scale<=3); - // Copy memory parts from eg the LoadNode over the opcode, e.g. an Add + // Copy memory parts from e.g. the LoadNode over the opcode, e.g. an Add if( op != mop ) { _inputs.set(0,mop.in(0)); // Control from mem op _inputs.set(1,mop.in(1)); // Memory from mem op @@ -41,6 +41,7 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { _off = off; _scale = scale; _imm = imm; + _sz = (char)('0'+(1<<_declaredType.log_size())); } // Store-based flavors have a value edge @@ -65,8 +66,10 @@ public abstract class MemOpX86 extends MemOpNode implements MachNode { if( i==1 ) return null; // Memory if( i==2 ) return x86_64_v2.RMASK; // base in GPR if( i==3 ) return x86_64_v2.RMASK; // index in GPR - if( i==4 ) return x86_64_v2.MEM_MASK; // value in GPR or XMM - throw Utils.TODO(); + if( i==4 ) return _sz >= 2 + ? x86_64_v2.MEM_MASK // value in GPR or XMM + : x86_64_v2.RMASK; // Bytes and shorts in GPR only + return null; // Anti-dependence } // "[base + idx<<2 + 12]" diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java new file mode 100644 index 0000000..68be129 --- /dev/null +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NegX86.java @@ -0,0 +1,21 @@ +package com.compilerprogramming.ezlang.compiler.nodes.cpus.x86_64_v2; + +import com.compilerprogramming.ezlang.compiler.SB; +import com.compilerprogramming.ezlang.compiler.codegen.*; +import com.compilerprogramming.ezlang.compiler.nodes.*; + +public class NegX86 extends MachConcreteNode implements MachNode { + NegX86(MinusNode not) { super(not); } + @Override public String op() { return "neg"; } + @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } + @Override public RegMask outregmap() { return x86_64_v2.RMASK; } + @Override public RegMask killmap() { return x86_64_v2.FLAGS_MASK; } + + @Override public void encoding( Encoding enc ) { + short dst = enc.reg(this ); + enc.add1(x86_64_v2.rex(0, dst, 0)); + enc.add1(0xF7); // opcode + enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 3, dst)); + } + @Override public void asm(CodeGen code, SB sb) { sb.p(code.reg(this)); } +} diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java index 6d5639b..aa81518 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/NewX86.java @@ -3,28 +3,23 @@ import com.compilerprogramming.ezlang.compiler.*; import com.compilerprogramming.ezlang.compiler.codegen.*; import com.compilerprogramming.ezlang.compiler.nodes.*; +import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr; public class NewX86 extends NewNode implements MachNode { // A pre-zeroed chunk of memory. NewX86( NewNode nnn ) { super(nnn); } - @Override public String op() { return "alloc"; } - // Size and pointer result in standard calling convention; null for all the - // memory aliases edges - @Override public RegMask regmap(int i) { return i == 1 ? x86_64_v2.RDI_MASK : null; } - @Override public RegMask outregmap(int i) { return i == 1 ? x86_64_v2.RAX_MASK : null; } - @Override public RegMask outregmap() { return null; } - @Override public RegMask killmap() { return x86_64_v2.x86CallerSave(); } - @Override public void encoding( Encoding enc ) { enc.external(this,"calloc"); + // ldi rcx,#1 // number of elements to calloc + enc.add1(0xB8 + _arg2Reg).add4(1); // E8 cd CALL rel32; enc.add1(0xE8); enc.add4(0); // offset } - - // General form: "alloc #bytes" + // General form: "alloc #bytes PC" @Override public void asm(CodeGen code, SB sb) { - sb.p("#calloc, ").p(code.reg(size())); + sb.p("ldi rcx = #1\n"); + sb.p("call #calloc"); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java index ce4d36f..5591405 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/RetX86.java @@ -6,36 +6,14 @@ public class RetX86 extends ReturnNode implements MachNode { RetX86( ReturnNode ret, FunNode fun ) { super(ret, fun); fun.setRet(this); } - @Override public String op() { - return ((FunX86)fun())._frameAdjust > 0 ? "addi" : "ret"; - } - // Correct Nodes outside the normal edges - @Override public void postSelect(CodeGen code) { - FunNode fun = (FunNode)rpc().in(0); - _fun = fun; - fun.setRet(this); - } - @Override public RegMask regmap(int i) { return x86_64_v2.retMask(_fun.sig(),i); } - @Override public RegMask outregmap() { return null; } @Override public void encoding( Encoding enc ) { - int frameAdjust = ((FunX86)fun())._frameAdjust; - if( frameAdjust > 0 ) { - enc.add1( 0x83 ); - enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) ); - enc.add1(frameAdjust*-8); + int sz = fun()._frameAdjust; + if( sz != 0 ) { + enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz) ? 0x83 : 0x81 ); + enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0b000, x86_64_v2.RSP) ); + if( x86_64_v2.imm8(sz) ) enc.add1(sz); + else enc.add4(sz); } enc.add1(0xC3); } - - @Override public void asm(CodeGen code, SB sb) { - int frameAdjust = ((FunX86)fun())._frameAdjust; - if( frameAdjust>0 ) - sb.p("rsp += #").p(frameAdjust*-8).p("\nret"); - // Post code-gen, just print the "ret" - if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) - // Prints return reg (either RAX or XMM0), RPC (always [rsp-4]) and - // then the callee-save registers. - for( int i=2; i 0x94; // SETE case "<" -> _unsigned ? 0x92 : 0x9C; // SETB /SETL case "<=" -> _unsigned ? 0x96 : 0x9E; // SETBE/SETLE + case ">=" -> _unsigned ? 0x93 : 0x9D; // SETAE/SETGE default -> throw Utils.TODO(); }); enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, dst)); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java index 319a303..26c5c6e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShlIX86.java @@ -19,6 +19,7 @@ public class ShlIX86 extends MachConcreteNode implements MachNode { @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } @Override public String op() { return "shli"; } @Override public String glabel() { return "<<"; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java index dd0f9cb..44d0caa 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/ShrIX86.java @@ -16,6 +16,7 @@ public class ShrIX86 extends MachConcreteNode implements MachNode { @Override public RegMask regmap(int i) { return x86_64_v2.RMASK; } @Override public RegMask outregmap() { return x86_64_v2.WMASK; } + @Override public int twoAddress() { return 1; } @Override public void encoding(Encoding enc) { short dst = enc.reg(this); // Also src1 diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java index fefefc3..058df1e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/SplitX86.java @@ -3,6 +3,7 @@ import com.compilerprogramming.ezlang.compiler.SB; import com.compilerprogramming.ezlang.compiler.Utils; import com.compilerprogramming.ezlang.compiler.codegen.*; +import com.compilerprogramming.ezlang.compiler.nodes.FunNode; import com.compilerprogramming.ezlang.compiler.nodes.Node; import com.compilerprogramming.ezlang.compiler.nodes.SplitNode; import com.compilerprogramming.ezlang.compiler.sontypes.SONType; @@ -26,7 +27,7 @@ public class SplitX86 extends SplitNode { // push rcx // popf (Pop the top of the stack into the FLAGS register) // 50+rd PUSH r64 - if( src >= 8 ) x86_64_v2.rex(0,src,0,true); + x86_64_v2.rexF(0,src,0,false,enc); enc.add1(0x50 + src); // popf enc.add1(0x9D); @@ -37,7 +38,7 @@ public class SplitX86 extends SplitNode { // pushf; pop reg enc.add1(0x9C); // 58+ rd POP r64 - if( dst >= 8 ) x86_64_v2.rex(0,dst,0,true); + x86_64_v2.rexF(0,dst,0,false, enc); enc.add1(0x58 + dst); return; } @@ -50,12 +51,12 @@ public class SplitX86 extends SplitNode { if( dst >= x86_64_v2.MAX_REG ) { if( src >= x86_64_v2.MAX_REG ) throw Utils.TODO(); // Very rare stack-stack move - int off = enc._fun.computeStackSlot(dst - x86_64_v2.MAX_REG)*8; + int off = enc._fun.computeStackOffset(enc._code,dst); StoreX86.encVal(enc, srcX ? SONTypeFloat.F64 : SONTypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, off, 0); return; } if( src >= x86_64_v2.MAX_REG ) { - int off = enc._fun.computeStackSlot(src - x86_64_v2.MAX_REG)*8; + int off = enc._fun.computeStackOffset(enc._code,src); LoadX86.enc(enc, dstX ? SONTypeFloat.F64 : SONTypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0); return; } @@ -66,6 +67,8 @@ public class SplitX86 extends SplitNode { // 0x66 if moving between register classes if( dstX ^ srcX ) enc.add1(0x66); + if( !dstX && srcX ) + { short tmp=src; src=dst; dst=tmp; } enc.add1(x86_64_v2.rex(dst, src, 0)); // pick opcode based on regs @@ -84,7 +87,6 @@ public class SplitX86 extends SplitNode { // xmm->reg(66 0F 7E /r MOVQ r/m64, xmm) enc.add1(0x0F); enc.add1(0x7E); - short tmp=src; src=dst; dst=tmp; } enc.add1(x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, dst, src)); @@ -92,6 +94,7 @@ public class SplitX86 extends SplitNode { // General form: "mov dst = src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + 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/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java index 5728951..49ec2bb 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/StoreX86.java @@ -31,16 +31,20 @@ public class StoreX86 extends MemOpX86 { short src = enc.reg(val()); if( src == -1 ) { - // return opcode for optimised immediate store - if( x86_64_v2.imm8(_imm) ) enc.add1(0xC6); - else if( x86_64_v2.imm32(_imm) ) enc.add1(0xC7); - else enc.add1(x86_64_v2.rex(-1, ptr, idx)).add1(0xC7); + int log = _declaredType.log_size(); + x86_64_v2.rexF(-1, ptr, idx, log==3, enc); + switch( log ) { + case 0: enc .add1(0xC6); break; + case 1: enc.add1(0x66).add1(0xC7); break; + case 2: enc .add1(0xC7); break; + case 3: enc .add1(0xC7); break; + } x86_64_v2.indirectAdr(_scale, idx, ptr, _off, src, enc); - switch (x86_64_v2.imm_size(_imm)) { - case 8: enc.add1(_imm); break; - case 16: enc.add2(_imm); break; - case 32: enc.add4(_imm); break; - case 64: enc.add8(_imm); break; + switch( log ) { + case 0: enc.add1(_imm); break; + case 1: enc.add2(_imm); break; + case 2: enc.add4(_imm); break; + case 3: enc.add8(_imm); break; } } else { encVal(enc,_declaredType,ptr,idx,src,_off,_scale); @@ -49,15 +53,25 @@ public class StoreX86 extends MemOpX86 { // Non-immediate encoding static void encVal( Encoding enc, SONType decl, short ptr, short idx, short src, int off, int scale ) { - if( decl instanceof SONTypeFloat ) { + int log = decl.log_size(); + // Float reg being stored + if( src >= x86_64_v2.XMM_OFFSET ) { src -= (short)x86_64_v2.XMM_OFFSET; - enc.add1( decl==SONTypeFloat.F32 ? 0xF3 : 0xF2 ).add1(0x0F).add1(0x11); + assert log == 2 || log == 3; + enc.add1( log==2 ? 0xF3 : 0xF2 ); + x86_64_v2.rexF(src,ptr,idx,false,enc); + enc.add1(0x0F).add1(0x11); + } else { + // byte stores from sil, dil, bpl, spl need a rex + if( log == 0 && src >= x86_64_v2.RSP ) enc.add1(x86_64_v2.rex(src,ptr,idx,false)); + else x86_64_v2.rexF(src,ptr,idx,log==3,enc); + switch( log ) { + case 0: enc .add1(0x88); break; + case 1: enc.add1(0x66).add1(0x89); break; + case 2: enc .add1(0x89); break; + case 3: enc .add1(0x89); break; + } } - else if( decl.log_size() == 0 ) enc.add1(0x88); - else if( decl.log_size() == 1 ) enc.add1(0x89); - else if( decl.log_size() == 2 ) enc.add1(0x89); - else if( decl.log_size() == 3 ) enc.add1(x86_64_v2.rex(src, ptr, idx)).add1(0x89); - x86_64_v2.indirectAdr(scale, idx, ptr, off, src, enc); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java index c70a884..d404bb5 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/UJmpX86.java @@ -47,7 +47,7 @@ public class UJmpX86 extends CFGNode implements MachNode, RIPRelSize { @Override public void asm(CodeGen code, SB sb) { CFGNode target = uctrl(); - assert target.nOuts() > 1; // Should optimize jmp to empty targets + //assert target.nOuts() > 1; // Should optimize jmp to empty targets sb.p(label(target)); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java index 61035af..bee5d2e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/cpus/x86_64_v2/x86_64_v2.java @@ -5,8 +5,6 @@ import com.compilerprogramming.ezlang.compiler.nodes.*; import com.compilerprogramming.ezlang.compiler.sontypes.*; -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. @@ -22,6 +20,17 @@ public x86_64_v2( CodeGen code ) {} public static final int MAX_REG = 33; public static final int RPC = 33; + // Human-readable name for a register number, e.g. "RAX". + public static final String[] REGS = new String[]{ + "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", + "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", + "xmm0", "xmm1", "xmm2" , "xmm3" , "xmm4" , "xmm5" , "xmm6" , "xmm7" , + "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", + "flags", + }; + // Hard crash for bad register number, fix yer bugs! + @Override public String[] regs() { return REGS; } + public static int XMM_OFFSET = 16; // General purpose register mask: pointers and ints, not floats static final long RD_BITS = 0b1111111111111111L; // All the GPRs @@ -36,7 +45,7 @@ public x86_64_v2( CodeGen code ) {} static RegMask RPC_MASK = new RegMask(RPC); static final long SPILLS = -(1L << MAX_REG); - static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | (1L<" -> 0x8F; case "<" -> 0x8C; case "<=" -> 0x8E; - case ">=" -> 0X8D; + case ">=" -> 0x8D; default -> throw new IllegalArgumentException("Too many arguments"); }; } @@ -119,14 +128,6 @@ public static int rex(int reg, int ptr, int idx, boolean wide) { 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); } @@ -175,41 +176,93 @@ public static void indirectAdr( int scale, short index, short base, int offset, } } - // Calling conv metadata - public int GPR_COUNT_CONV_WIN64 = 4; // RCX, RDX, R9, R9 - public int XMM_COUNT_CONV_WIN64 = 4; // XMM0L, XMM1L, XMM2L, XMM3L - public int GPR_COUNT_CONV_SYSTEM_V = 6; // RDI, RSI, RDX, RCX, R8, R9 - public int XMM_COUNT_CONV_SYSTEM_V = 4; // XMM0, XMM1, XMM2, XMM3 .... - // Human-readable name for a register number, e.g. "RAX". - // Hard crash for bad register number, fix yer bugs! - public static final String[] REGS = new String[]{ - "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi", - "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15", - "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7", - "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15", - "flags", + // Limit of float args passed in registers + static RegMask[] XMMS8 = new RegMask[]{ + new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), + new RegMask(XMM4), new RegMask(XMM5), new RegMask(XMM6), new RegMask(XMM7), }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]"; - } - // Stack slots, in units of 8 bytes. - @Override public int stackSlot( int reg ) { - return reg < REGS.length ? -1 : reg-REGS.length; + // Map from function signature and argument index to register. + // Used to set input registers to CallNodes, and ParmNode outputs. + @Override public RegMask callArgMask( SONTypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } + static RegMask callInMask( SONTypeFunPtr tfp, int idx ) { + if( idx==0 ) return RPC_MASK; + if( idx==1 ) return null; + return switch( CodeGen.CODE._callingConv ) { + case "SystemV" -> callSys5 (tfp,idx); + case "Win64" -> callWin64(tfp,idx); + default -> throw Utils.TODO(); + }; } + // Maximum stack args used by this signature + @Override public short maxArgSlot( SONTypeFunPtr tfp ) { + return switch( CodeGen.CODE._callingConv ) { + case "SystemV" -> maxArgSlotSys5 (tfp); + case "Win64" -> maxArgSlotWin64(tfp); + default -> throw Utils.TODO(); + }; + } - // WIN64(param passing) - static RegMask[] CALLINMASK_WIN64 = new RegMask[] { + // Win64 - max *4* args in registers of all kinds; after that on stack. + // Stack args land in increasing memory order with empty/mirror slots for + // every register. + // + // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) + // + // -- Prior Frame -- + // i6 + // f5 + // i4 + // empty mirror, XMM3= f3 + // empty mirror, R08 = i2 + // empty mirror, XMM1= f1 + // empty mirror, RCX = i0 + // -- Caller Frame; 16b align -- + // RPC + // PAD/ALIGN + // -- Callee Frame; 16b align -- + static RegMask[] WIN64_CALL = new RegMask[] { RCX_MASK, RDX_MASK, R08_MASK, R09_MASK, }; + static RegMask callWin64(SONTypeFunPtr tfp, int idx ) { + // idx 2,3,4,5 passed in registers, with stack slot mirrors. + // idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC). + if( idx >= 6 ) + return new RegMask(MAX_REG+1/*RPC*/+(idx-2)); + return tfp.arg(idx-2) instanceof SONTypeFloat + ? XMMS8 [idx-2] + : WIN64_CALL[idx-2]; + } + static short maxArgSlotWin64(SONTypeFunPtr tfp) { + return (short)tfp.nargs(); + } + + // Sys5: max 6 GPRs and 8 FPRS filled first. Extra args land in increasing + // memory order as needed - no mirror space. + // + // foo( int i0, flt f1, int i2, flt f3, int i4, flt f5, int i6, ... ) + // RDI =i0, RSI =i2, RDX =i4, RCX =i6, R08 =i8, R09 =i10 + // XMM0=f1, XMM1=f3, XMM2=f5, XMM3=f7, XMM4=f9, XMM5=f11, XMM6=f13, XMM7=f15 + // + // -- Prior Frame -- + // f18 + // i18 + // f17 + // i16 + // i14 + // i12 + // -- Caller Frame; 16b align -- + // RPC + // PAD/ALIGN + // -- Callee Frame; 16b align -- // SystemV(param passing) - static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] { + static RegMask[] SYS5_CALL = new RegMask[] { RDI_MASK, RSI_MASK, RDX_MASK, @@ -218,63 +271,29 @@ public static void indirectAdr( int scale, short index, short base, int offset, R09_MASK, }; - // Limit of float args passed in registers - static RegMask[] XMMS4 = new RegMask[]{ - new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), - }; - - // Map from function signature and argument index to register. - // Used to set input registers to CallNodes, and ParmNode outputs. - static RegMask callInMask( SONTypeFunPtr tfp, int idx ) { - if( idx==0 ) return RPC_MASK; - if( idx==1 ) return null; - // Count floats in signature up to index - int fcnt=0; - for( int i=2; i CALLINMASK_SYSTEMV; - case "Win64" -> CALLINMASK_WIN64; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - if( idx-2-fcnt < cargs.length ) - return cargs[idx-2-fcnt]; + static RegMask callSys5(SONTypeFunPtr tfp, int idx ) { + // First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09 + // First 8 floats passed in registers: xmm0-xmm7 + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=2; i= XMMS4.length ) - throw Utils.TODO(); - RegMask[] cargs = switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> CALLINMASK_SYSTEMV; - case "Win64" -> CALLINMASK_WIN64; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - if( tfp.nargs()-fcnt >= cargs.length ) - throw Utils.TODO(); - return 0; // No stack args - } - - // Return single int/ptr register. Used by CallEnd output and Return input. - static RegMask retMask( SONTypeFunPtr tfp ) { - return tfp.ret() instanceof SONTypeFloat ? XMM0_MASK : RAX_MASK; + int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); + return tfp.arg(idx-2) instanceof SONTypeFloat + ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+1/*RPC*/+nstk) + : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+1/*RPC*/+nstk); + } + static short maxArgSlotSys5(SONTypeFunPtr tfp) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i SYSTEM5_CALLER_SAVE_MASK; - case "Win64" -> WIN64_CALLER_SAVE_MASK; + case "SystemV" -> SYSTEM5_CALLER_SAVE; + case "Win64" -> WIN64_CALLER_SAVE; default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv); }; } - - @Override public RegMask callerSave() { return x86CallerSave(); } - - static final RegMask SYSTEM5_CALLEE_SAVE_MASK; - static final RegMask WIN64_CALLEE_SAVE_MASK; - - static { - long callee = ~SYSTEM5_CALLER_SAVE; - // Remove the spills - callee &= (1L< SYSTEM5_CALLEE_SAVE_MASK; - case "Win64" -> WIN64_CALLEE_SAVE_MASK; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - } - @Override public RegMask calleeSave() { return x86CalleeSave(); } - - static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS; - static { - WIN64_RET_MASKS = makeRetMasks( WIN64_CALLEE_SAVE_MASK); - SYS5_RET_MASKS = makeRetMasks(SYSTEM5_CALLEE_SAVE_MASK); - } - private static RegMask[] makeRetMasks(RegMask mask) { - int nSaves = mask.size(); - RegMask[] masks = new RegMask[4 + nSaves]; - masks[0] = null; - masks[1] = null; // Memory - masks[2] = null; // Varies, either XMM0 or RAX - masks[3] = RPC_MASK; - short reg = mask.firstReg(); - for( int i=0; i SYS5_RET_MASKS; - case "Win64" -> WIN64_RET_MASKS; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - return masks[i]; - } // Create a split op; any register to any register, including stack slots @Override public SplitNode split(String kind, byte round, LRG lrg) { @@ -373,50 +334,47 @@ static RegMask retMask( SONTypeFunPtr tfp, int i ) { return new UJmpX86(); } - // Break an infinite loop - @Override public NeverNode never(CFGNode ctrl) { - throw Utils.TODO(); - } - // Instruction selection @Override public Node instSelect(Node n) { return switch (n) { - case AddFNode addf -> addf(addf); - case AddNode add -> add(add); - case AndNode and -> and(and); - case BoolNode bool -> cmp(bool); + case AddFNode addf -> addf(addf); + 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 CallNode call -> call(call); + case CastNode cast -> new CastX86(cast); case ConstantNode con -> con(con); - case DivFNode divf -> new DivFX86(divf); - case DivNode div -> new DivX86(div); - case FunNode fun -> new FunX86(fun); - case IfNode iff -> jmp(iff); - case LoadNode ld -> ld(ld); + case DivFNode divf -> new DivFX86(divf); + case DivNode div -> new DivX86(div); + case FunNode fun -> new FunX86(fun); + case IfNode iff -> jmp(iff); + case LoadNode ld -> ld(ld); case MemMergeNode mem -> new MemMergeNode(mem); - case MulFNode mulf -> new MulFX86(mulf); - case MulNode mul -> mul(mul); - case NewNode nnn -> new NewX86(nnn); - case NotNode not -> new NotX86(not); - case OrNode or -> or(or); - case ParmNode parm -> new ParmX86(parm); - case PhiNode phi -> new PhiNode(phi); - case ProjNode prj -> prj(prj); - case ReadOnlyNode read -> new ReadOnlyNode(read); - case ReturnNode ret -> new RetX86(ret, ret.fun()); - case SarNode sar -> sar(sar); - case ShlNode shl -> shl(shl); - case ShrNode shr -> shr(shr); - case StartNode start -> new StartNode(start); - case StopNode stop -> new StopNode(stop); - case StoreNode st -> st(st); - case SubFNode subf -> new SubFX86(subf); - case SubNode sub -> sub(sub); - case ToFloatNode tfn -> i2f8(tfn); - case XorNode xor -> xor(xor); + case MinusNode neg -> new NegX86(neg); + case MulFNode mulf -> new MulFX86(mulf); + case MulNode mul -> mul(mul); + case NewNode nnn -> new NewX86(nnn); + case NotNode not -> new NotX86(not); + case OrNode or -> or(or); + case ParmNode parm -> new ParmX86(parm); + case PhiNode phi -> new PhiNode(phi); + case ProjNode prj -> prj(prj); + case ReadOnlyNode read-> new ReadOnlyNode(read); + case ReturnNode ret -> new RetX86(ret, ret.fun()); + case SarNode sar -> sar(sar); + case ShlNode shl -> shl(shl); + case ShrNode shr -> shr(shr); + case StartNode start -> new StartNode(start); + case StopNode stop -> new StopNode(stop); + case StoreNode st -> st(st); + case SubFNode subf -> new SubFX86(subf); + case SubNode sub -> sub(sub); + case ToFloatNode tfn -> i2f8(tfn); + case XCtrlNode x -> new ConstantNode(SONType.XCONTROL); + case XorNode xor -> xor(xor); case LoopNode loop -> new LoopNode(loop); case RegionNode region -> new RegionNode(region); @@ -425,7 +383,7 @@ public Node instSelect(Node n) { } 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. @@ -433,7 +391,7 @@ private Node add(AddNode add) { Node lhs = add.in(1); Node rhs = add.in(2); if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) - return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val); + return new AddMemX86(add, address(ld), ld.ptr(), idx, off, scale, 0, rhs); // if(rhs instanceof LoadNode ld && ld.nOuts() == 1 && ld._declaredType.log_size() >= 3) { // throw Utils.TODO(); // Swap load sides @@ -441,7 +399,7 @@ private Node add(AddNode add) { // Attempt a full LEA-style break down. // Returns one of AddX86, AddIX86, LeaX86, or LHS - if( rhs instanceof ConstantNode off && off._con instanceof SONTypeInteger toff ) { + if( rhs instanceof ConstantNode off2 && off2._con instanceof SONTypeInteger toff ) { long imm = toff.value(); assert imm!=0; // Folded in peeps if( (int)imm != imm ) // Full 64bit immediate @@ -518,27 +476,29 @@ private Node _cmp(BoolNode bool) { Node lhs = bool.in(1); Node rhs = bool.in(2); + // Since cmp does not record a BOP, SetX/Jmp need to know if the CMP is swapped. + // Vs memory - if( lhs instanceof LoadNode ld && ld.nOuts() == 1 ) + if( lhs instanceof LoadNode ld && ld.nOuts() == 1 && rhs._type.isa(ld._declaredType) ) return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(rhs), val, false); - if( rhs instanceof LoadNode ld && ld.nOuts() == 1 ) + // Operands swap in the encoding directly, no need for Set/Jmp to swap `bop` + if( rhs instanceof LoadNode ld && ld.nOuts() == 1 && lhs._type.isa(ld._declaredType)) return new CmpMemX86(bool, address(ld), ld.ptr(), idx, off, scale, imm(lhs), val, true ); // Vs immediate if( rhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) ) - return new CmpIX86(bool, (int)ti.value()); + return new CmpIX86(bool, (int)ti.value(), false); - if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) ) { - swap = true; - return new CmpIX86(bool, (int)ti.value(), 0.5); - } + // Operand swap compare; Set and Jmp need to swap `bop` + if( lhs instanceof ConstantNode con && con._con instanceof SONTypeInteger ti && imm32(ti.value()) ) + return new CmpIX86(bool, (int)ti.value(), swap=true); // x vs y return new CmpX86(bool); } - private Node con(ConstantNode con) { + private Node con( ConstantNode con ) { if(!con._con.isConstant()) return new ConstantNode(con); // Default unknown caller inputs return switch (con._con) { case SONTypeInteger ti -> new IntX86(con); @@ -560,10 +520,11 @@ private Node jmp(IfNode iff) { // 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(SONTypeInteger.ZERO))); - return new JmpX86(iff, bool.op()); + String op = "!="; + if( iff.in(1) instanceof BoolNode bool ) op = swap ? IfNode.swap(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(SONTypeInteger.ZERO))); + return new JmpX86(iff, op); } private Node ld(LoadNode ld) { @@ -604,21 +565,25 @@ private Node shr( ShrNode shr ) { return new ShrX86(shr); } - private Node st (StoreNode st ){ + private Node st( StoreNode st ) { // Look for "*ptr op= val" Node op = st.val(); - if(op instanceof AddNode) { - if(op.in(1) instanceof LoadNode ld && - ld.in(0) == st.in(0) && - ld.mem() == st.mem() && - ld.ptr() == st.ptr() && - ld.off() == st.off()) { - return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); - } + if( op instanceof AddNode ) { + if( op.in(1) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(2)), val); + if( op.in(2) instanceof LoadNode ld && stld_match(st,ld) ) + return new MemAddX86(address(st), st.ptr(), idx, off, scale, imm(op.in(1)), val); } - return new StoreX86(address(st), st.ptr(), idx, off, scale, imm(st.val()), val); } + private static boolean stld_match(StoreNode st, LoadNode ld ) { + return + ld.in(0) == st.in(0) && + ld.mem() == st.mem() && + ld.ptr() == st.ptr() && + ld.off() == st.off(); + } + private Node sub (SubNode sub ){ return sub.in(2) instanceof ConstantNode con && con._con instanceof SONTypeInteger ti @@ -664,7 +629,7 @@ private N address(N mop) { return mop; } - private int imm (Node xval ){ + private int imm( Node xval ) { assert val == null && imm == 0; if( xval instanceof ConstantNode con && con._con instanceof SONTypeInteger ti) { val = null; From 741396ff5d53713bc482fdbdb56b8d9701aacd84 Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Sat, 26 Apr 2025 00:34:33 +0100 Subject: [PATCH 2/5] Sync with final ch21 --- .../ezlang/compiler/sontypes/SONTypeFunPtr.java | 1 + .../ezlang/compiler/sontypes/SONTypeTuple.java | 1 + 2 files changed, 2 insertions(+) diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java index 0b41493..07b06df 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeFunPtr.java @@ -44,6 +44,7 @@ public SONTypeFunPtr(byte nil, SONTypeTuple sig, SONType ret, long fidxs) { // public static SONTypeFunPtr TEST = make((byte)2, SONTypeTuple.TEST, SONTypeInteger.BOT,1); // public static SONTypeFunPtr TEST0 = make((byte)3, SONTypeTuple.TEST, SONTypeInteger.BOT,3); public static SONTypeFunPtr MAIN = make((byte)3, SONTypeTuple.MAIN, SONTypeInteger.BOT,-1); + public static SONTypeFunPtr CALLOC= make((byte)3,SONTypeTuple.CALLOC,SONTypeMemPtr.BOT,-1); public static void gather(ArrayList ts) { /* ts.add(TEST); ts.add(TEST0); */ ts.add(BOT); ts.add(MAIN); } @Override diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java index ecb14ef..73ea8f4 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/sontypes/SONTypeTuple.java @@ -17,6 +17,7 @@ public class SONTypeTuple extends SONType { public static final SONTypeTuple START= make(SONType.CONTROL, SONTypeMem.TOP, SONTypeInteger.BOT); public static final SONTypeTuple MAIN = make(SONTypeInteger.BOT); public static final SONTypeTuple RET = make(SONType.CONTROL, SONTypeMem.BOT, SONType.BOTTOM); + public static final SONTypeTuple CALLOC = make(SONTypeInteger.BOT,SONTypeInteger.BOT); public static final SONTypeTuple IF_BOTH = make(new SONType[]{SONType. CONTROL, SONType. CONTROL}); public static final SONTypeTuple IF_NEITHER = make(new SONType[]{SONType.XCONTROL, SONType.XCONTROL}); From 044886d01037ea4c18892822121afb87b42de6e2 Mon Sep 17 00:00:00 2001 From: dibyendumajumdar Date: Sat, 26 Apr 2025 00:38:21 +0100 Subject: [PATCH 3/5] Sync with final ch21 --- .../ezlang/compiler/codegen/BuildLRG.java | 4 +- .../ezlang/compiler/codegen/CodeGen.java | 105 ++++++-- .../ezlang/compiler/codegen/Encoding.java | 237 +++++++++++------- .../compiler/codegen/GlobalCodeMotion.java | 38 ++- .../ezlang/compiler/codegen/IFG.java | 6 +- .../ezlang/compiler/codegen/LRG.java | 5 - .../ezlang/compiler/codegen/LinkMem.java | 7 +- .../compiler/codegen/ListScheduler.java | 3 + .../ezlang/compiler/codegen/Machine.java | 107 +++++--- .../ezlang/compiler/codegen/RegAlloc.java | 71 +++--- .../ezlang/compiler/codegen/RegMask.java | 10 +- .../ezlang/compiler/codegen/RegMaskRW.java | 5 + 12 files changed, 394 insertions(+), 204 deletions(-) diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java index 99c3fbd..003d26e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/BuildLRG.java @@ -55,8 +55,8 @@ public static boolean run(int round, RegAlloc alloc) { if( n.in(i)!=null ) { LRG lrg2 = alloc.lrg(n.in(i)); if( lrg2 != null ) { // Anti-dep or other, no LRG - RegMask use_mask = mach.regmap(i); - if( !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) + RegMask use_mask = mach.regmap(i); // use_mask is also null for anti-dep + if( use_mask!=null && !lrg2.machUse(mach,(short)i,use_mask.size1()).and(use_mask) ) alloc.fail(lrg2); // Empty register mask, must split } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java index 22e05c3..d7eaa3a 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/CodeGen.java @@ -22,6 +22,7 @@ public enum Phase { 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 @@ -52,6 +53,27 @@ public CodeGen( String src, SONTypeInteger arg, long workListSeed ) { } + // 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 @@ -125,6 +147,7 @@ public CodeGen parse() { assert _phase == null; _phase = Phase.Parse; long t0 = System.currentTimeMillis(); + P.parse(); _tParse = (int)(System.currentTimeMillis() - t0); return this; @@ -180,18 +203,42 @@ public CodeGen typeCheck() { 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; + // Callee save registers + public RegMask _callerSave; + + // All returns have the following inputs: + // 0 - ctrl + // 1 - memory + // 2 - varies with returning GPR,FPR + // 3 - RPC + // 4+- Caller save registers + public RegMask[] _retMasks; + // Return Program Counter + public RegMask _rpcMask; // 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; @@ -203,6 +250,27 @@ public CodeGen instSelect( String cpu, String callingConv, String base ) { try { _mach = ((Class) Class.forName( clzFile )).getDeclaredConstructor(new Class[]{CodeGen.class}).newInstance(this); } catch( Exception e ) { throw new RuntimeException(e); } + // Build global copies of common register masks. + long callerSave = _mach.callerSave(); + long neverSave = _mach. neverSave(); + int maxReg = Math.min(64,_mach.regs().length); + assert maxReg>=64 || (-1L << maxReg & callerSave)==0; // No stack slots in callerSave + _callerSave = new RegMask(callerSave); + + // Build a Return RegMask array. All returns have the following inputs: + // 0 - ctrl + // 1 - memory + // 2 - varies with returning GPR,FPR + // 3 - RPC + // 4+- Caller save registers + _retMasks = new RegMask[(maxReg-_callerSave.size()-Long.bitCount( neverSave ))+4]; + for( int reg=0, i=4; reg _cfg = new Ary<>(CFGNode.class); @@ -269,8 +337,6 @@ public CodeGen GCM( boolean show) { _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 ) @@ -305,34 +371,32 @@ public CodeGen regAlloc() { return this; } - public String reg(Node n) { + // Human readable register name + public String reg(Node n) { return reg(n,null); } + public String reg(Node n, FunNode fun) { if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { - String s = _regAlloc.reg(n); + String s = _regAlloc.reg(n,fun); if( s!=null ) return s; } return "N"+ n._nid; } + // --------------------------- // Encoding public int _tEncode; - public boolean _JIT; - public Encoding _encoding; - public CodeGen encode(boolean jit) { + public Encoding _encoding; // Encoding object + public void preEncode() { } // overridden by alternative ports + public CodeGen encode() { assert _phase == Phase.RegAlloc; _phase = Phase.Encoding; long t0 = System.currentTimeMillis(); _encoding = new Encoding(this); - _JIT = jit; + preEncode(); _encoding.encode(); _tEncode = (int)(System.currentTimeMillis() - t0); return this; } - public CodeGen encode() { - return encode(false); - } - // Encoded binary, no relocation info - public byte[] binary() { return _encoding.bits(); } // --------------------------- // Exporting to external formats @@ -365,6 +429,15 @@ public CodeGen exportELF(String fname) throws IOException { // Debugging helper public Node f(int idx) { return _stop.find(idx); } + + String printCFG() { + if( _cfg==null ) return "no CFG"; + SB sb = new SB(); + for( CFGNode cfg : _cfg ) + IRPrinter.printLine(cfg,sb); + return sb.toString(); + } + public static void print_as_hex(Encoding enc) { for (byte b : enc._bits.toByteArray()) { System.out.print(String.format("%02X", b)); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java index 6696e1c..53f20ed 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Encoding.java @@ -21,7 +21,7 @@ public class Encoding { // Top-level program graph structure - final CodeGen _code; + public final CodeGen _code; // Instruction bytes. The registers are encoded already. Relocatable data // is in a fixed format depending on the kind of relocation. @@ -153,11 +153,13 @@ public void largeConstant( Node relo, SONType t, int off, int elf ) { _bigCons.put(relo,new Relo(relo,t,(byte)off,(byte)elf)); } + // -------------------------------------------------- void encode() { - // Basic block layout: invert branches to keep blocks in-order; insert + // Basic block layout: negate branches to keep blocks in-order; insert // unconditional jumps. Attempt to keep backwards branches taken, // forwards not-taken (this is the default prediction on most - // hardware). Layout is still RPO but with more restrictions. + // hardware). Layout is still Reverse Post Order but with more + // restrictions. basicBlockLayout(); // Write encoding bits in order into a big byte array. @@ -170,20 +172,25 @@ void encode() { // Patch RIP-relative and local encodings now. patchLocalRelocations(); - } - // Basic block layout: invert branches to keep blocks in-order; insert - // unconditional jumps. Attempt to keep backwards branches taken, - // forwards not-taken (this is the default prediction on most - // hardware). Layout is still RPO but with more restrictions. + // -------------------------------------------------- + // Basic block layout: negate branches to keep blocks in-order; insert + // unconditional jumps. Attempt to keep backwards branches taken, forwards + // not-taken (this is the default prediction on most hardware). Layout is + // still Reverse Post Order but with more restrictions. private void basicBlockLayout() { + IdentityHashMap> rpos = new IdentityHashMap<>(); Ary rpo = new Ary<>(CFGNode.class); + rpos.put(_code._start.loop(),rpo); BitSet visit = _code.visit(); + //IdentityHashMap looptail = new IdentityHashMap<>(); + rpo.add(_code._stop); for( Node n : _code._start._outputs ) if( n instanceof FunNode fun ) { int x = rpo._len; - _rpo_cfg(fun, visit, rpo); + //_rpo_cfg2(fun, visit, rpo, looptail); + _rpo_cfg(fun, visit, rpos ); assert rpo.at(x) instanceof ReturnNode; } rpo.add(_code._start); @@ -195,64 +202,37 @@ private void basicBlockLayout() { _code._cfg = rpo; // Save the new ordering } - // Basic block layout. Now that RegAlloc is finished, no more spill code - // will appear. We can change our BB layout from RPO to something that - // minimizes actual branches, takes advantage of fall-through edges, and - // tries to help simple branch predictions: back branches are predicted - // taken, forward not-taken. - private void _rpo_cfg(CFGNode bb, BitSet visit, Ary rpo) { - if( visit.get(bb._nid) ) return; + + private void _rpo_cfg(CFGNode bb, BitSet visit, IdentityHashMap> rpos ) { + if( bb==null || visit.get(bb._nid) ) return; visit.set(bb._nid); - if( bb.nOuts()==0 ) return; // StopNode - if( !(bb instanceof IfNode iff) ) { - CFGNode next = bb instanceof ReturnNode ? (CFGNode)bb.out(bb.nOuts()-1) : bb.uctrl(); - // If the *next* BB has already been visited, we may need an - // unconditional jump here - if( visit.get(next._nid) && !(next instanceof StopNode) ) { - boolean needJump = next instanceof LoopNode - // If backwards to a loop, and the block has statements, - // will need a jump. Empty blocks can just backwards branch. - ? (bb.nOuts()>1) - // Forwards jump. If all the blocks, in RPO order, to our - // target are empty, we will fall in and not need a jump. - : !isEmptyBackwardsScan(rpo,next); - if( needJump ) { - CFGNode jmp = _code._mach.jump(); - jmp.setDefX(0,bb); - next.setDef(next._inputs.find(bb),jmp); - rpo.add(jmp); - } - } - _rpo_cfg(next,visit,rpo); - } else { - boolean invert = false; + CFGNode next = bb.uctrl(); + + // Loops run an inner "rpo_cfg", then append the entire loop body in + // place. This keeps loop bodies completely contained, although if + // Some Day Later we have real profile data we ought to arrange the + // branch orderings based on frequency. + if( bb instanceof LoopNode loop ) { + Ary body = new Ary<>(CFGNode.class); // Private RPO for the loop + rpos.put(loop.loop(),body); // Find it via loop tree + _rpo_cfg(next,visit,rpos); // RPO the loop + body.add(loop); // Include loop last (first in RPO) + Ary outer = rpos.get(loop.cfg(1).loop()); + outer.addAll(body); // Append to original CFG + return; + } + + // IfNodes visit 2 sides, and may choose to reorder them in the RPO + if( bb instanceof IfNode iff ) { // Pick out the T/F projections CProjNode t = iff.cproj(0); CProjNode f = iff.cproj(1); - int tld = t.loopDepth(), fld = f.loopDepth(), bld = bb.loopDepth(); - // Decide entering or exiting a loop - if( tld==bld ) { - // if T is empty, keep - if( t.nOuts()>1 && - // Else swap so T is forward and exits, while F falls into next loop block - ((fld bld ) { // True enters a deeper loop - throw Utils.TODO(); - } else if( fld == bld && f.out(0) instanceof LoopNode ) { // Else True exits a loop - // if false is the loop backedge, make sure its true/taken - invert = true; - } // always forward and good - // Invert test and Proj fields - if( invert ) { - iff.invert(); + // Invert the branch or not + if( shouldInvert(t,f,iff.loopDepth()) ) { + iff.negate(); t.invert(); f.invert(); CProjNode tmp=f; f=t; t=tmp; // Swap t/f - int d=tld; tld=fld; fld=d; // Swap depth } // Whichever side is visited first becomes last in the RPO. With @@ -260,29 +240,79 @@ private void _rpo_cfg(CFGNode bb, BitSet visit, Ary rpo) { // when the False RPO visit returns, the IF is immediately next. // When the RPO is reversed, the fall-through path will always be // following the IF. + _rpo_cfg(t,visit,rpos); // True side first + next = f; // False side last + } - // If loops are involved, attempt to keep them in a line. Visit - // the exits first, so they follow the loop body when the order gets - // reversed. - if( fld < tld || (fld==tld && f.nOuts()==1) ) { - _rpo_cfg(f,visit,rpo); - _rpo_cfg(t,visit,rpo); - } else { - _rpo_cfg(t,visit,rpo); - _rpo_cfg(f,visit,rpo); + // If the *next* BB has already been visited, and we are not already a + // jump, we may need an unconditional forwards jump here + Ary rpo = rpos.get(bb.loop()); + if( next!=null && visit.get(next._nid) && !(bb instanceof IfNode) ) { + boolean needJump = next instanceof LoopNode + // Empty blocks from an IF will invert the IF and backwards + // branch to the loop head. If not empty, or not an IF + // will need a jump. + ? (bb.nOuts()>1 || !(bb.cfg0() instanceof IfNode) ) + // Forwards jump. If all the blocks, in RPO order, to our + // target are empty, we will fall in and not need a jump. + : !backwardsEmptyScan(rpo,next); + if( needJump ) { + CFGNode jmp = _code._mach.jump(); + jmp._ltree = bb._ltree; + jmp.setDefX(0,bb); + next.setDef(next._inputs.find(bb),jmp); + rpo.add(jmp); } } - assert rpo._len>0 || bb instanceof ReturnNode; + + _rpo_cfg(next,visit,rpos); rpo.add(bb); } - private static boolean isEmptyBackwardsScan(Ary rpo, CFGNode next) { + // Should this test be inverted? + private static boolean shouldInvert(CFGNode t, CFGNode f, int bld) { + int tld = t.loopDepth(), fld = f.loopDepth(); + // These next two are symmetric and can happen in any order; if `tld < + // bld` is true, the `fld < bld` must be false, or else both directions + // exit the loop... and the IF test would not be in the loop. + + // true to exit a loop usually (and false falls into Yet Another Loop + // Block), invert if false is taking an empty backwards branch. + if( tld < bld ) return forwardsEmptyScan(f,bld); + // false to exit a loop, normally invert to true (except empty back) + if( fld < bld ) return !forwardsEmptyScan(t,bld); + + // Not exiting the loop; staying at this depth (or going deeper loop). + + // Jump/true to an empty backwards block + if( forwardsEmptyScan(t,bld) ) return false; + if( forwardsEmptyScan(f,bld) ) return true ; + + // Fall/false into a full block, Jump/true to an empty block. + if( f.nOuts()>1 && t.nOuts()==1 ) return false; + if( t.nOuts()>1 && f.nOuts()==1 ) return true ; + // Everything else equal, use pre-order + return t._pre > f._pre; + } + + // Do we continue forwards from 'c' to the Loop back edge, without doing + // anything else? Then this branch can directly jump to the loop head. + private static boolean forwardsEmptyScan( CFGNode c, int bld ) { + if( c.nOuts()!=1 || c.loopDepth()!=bld ) return false; + return c.uctrl() instanceof RegionNode cfg && + (cfg instanceof LoopNode || forwardsEmptyScan(cfg,bld)); + } + + // Is the CFG from "next" to the end empty? This means jumping to "next" + // will naturally fall into the end. + private static boolean backwardsEmptyScan(Ary rpo, CFGNode next) { for( int i=rpo._len-1; rpo.at(i)!=next; i-- ) if( rpo.at(i).nOuts()!=1 ) return false; return true; } + // -------------------------------------------------- // Write encoding bits in order into a big byte array. // Record opcode start and length. public FunNode _fun; // Currently encoding function @@ -310,6 +340,7 @@ else if( bb instanceof FunNode fun ) { padN(16,_bits); } + // -------------------------------------------------- // Short-form RIP-relative support: replace short encodings with long // encodings and expand the code, changing all the offsets. private void compactShortForm() { @@ -337,12 +368,15 @@ private void compactShortForm() { slide += (newStart+15 & -16)-newStart; } _opStart[bb._nid] += slide; + // Slide down all other (non-CFG) ops in the block + for( Node n : bb._outputs ) + if( n instanceof MachNode && !(n instanceof CFGNode) ) + _opStart[n._nid] += slide; if( bb instanceof RIPRelSize riprel ) { - CFGNode target = ((CFGNode)bb.out(0)).uctrlSkipEmpty(); + CFGNode target = (bb instanceof IfNode iff ? iff.cproj(0) : (CFGNode)bb.out(0)).uctrlSkipEmpty(); // Delta is from opStart to opStart. X86 at least counts // the delta from the opEnd, but we don't have the end until // we decide the size - so the encSize has to deal - assert _opStart[target._nid] > 0; int delta = _opStart[target._nid] - _opStart[bb._nid]; byte opLen = riprel.encSize(delta); // Recorded size is smaller than the current size? @@ -374,16 +408,25 @@ private void compactShortForm() { } + // -------------------------------------------------- + // Patch local encodings now + void patchLocalRelocations() { + // Walk the local code-address relocations + for( Node src : _internals.keySet() ) { + int start = _opStart[src._nid]; + Node dst = _internals.get(src); + int target = _opStart[dst._nid]; + ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); + } + } + + // -------------------------------------------------- // Write the constant pool into the BAOS and optionally patch locally void writeConstantPool( BAOS bits, boolean patch ) { - HashSet ts = new HashSet<>(); - for( Relo t : _bigCons.values() ) { - if( ts.contains(t) ) - throw Utils.TODO(); // Dup! Compress! - ts.add(t); - } padN(16,bits); + HashMap targets = new HashMap<>(); + // By log size for( int log = 3; log >= 0; log-- ) { // Write the 8-byte constants @@ -391,30 +434,40 @@ void writeConstantPool( BAOS bits, boolean patch ) { Relo relo = _bigCons.get(op); if( relo._t.log_size()==log ) { // Map from relo to constant start and patch - relo._target = bits.size(); + Integer target = targets.get(relo._t); + if( target==null ) { + targets.put(relo._t,target = bits.size()); + // Put constant into code space. + if( relo._t instanceof SONTypeTuple tt ) // Constant tuples put all entries + for( SONType tx : tt._types ) + addN(log,tx,bits); + else + addN(log,relo._t,bits); + } + relo._target = target; relo._opStart= _opStart[op._nid]; // Go ahead and locally patch in-memory if( patch ) ((RIPRelSize)op).patch(this, relo._opStart, _opLen[op._nid], relo._target - relo._opStart); - // Put constant into code space. - if( relo._t instanceof SONTypeTuple tt ) // Constant tuples put all entries - for( SONType tx : tt._types ) - addN(log,tx,bits); - else - addN(log,relo._t,bits); } } } } - // Patch local encodings now - void patchLocalRelocations() { - // Walk the local code-address relocations - for( Node src : _internals.keySet() ) { - Node dst = _internals.get(src); - int target = _opStart[dst._nid]; + // A series of libc/external calls that Simple can link against in a JIT. + // Since no runtime in the JVM process, using magic numbers for the CPU + // emulators to pick up on. + public static int SENTINAL_CALLOC = -4; + + void patchGlobalRelocations() { + for( Node src : _externals.keySet() ) { int start = _opStart[src._nid]; + String dst = _externals.get(src); + int target = switch( dst ) { + case "calloc" -> SENTINAL_CALLOC; + default -> throw Utils.TODO(); + }; ((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java index 86712da..deba31f 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/GlobalCodeMotion.java @@ -160,7 +160,7 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) { continue outer; // Nope, await all uses done // Loads need their memory inputs' uses also done - if( n instanceof LoadNode ld ) + if( n instanceof MemOpNode ld && ld._isLoad ) for( Node memuse : ld.mem()._outputs ) if( late[memuse._nid]==null && // New makes new memory, never crushes load memory @@ -180,8 +180,8 @@ private static void breadth(Node stop, Node[] ns, CFGNode[] late) { if( def!=null && late[def._nid]==null ) { work.push(def); // if the def has a load use, maybe the load can fire - for( Node ld : def._outputs ) - if( ld instanceof LoadNode && late[ld._nid]==null ) + for( Node out : def._outputs ) + if( out instanceof MemOpNode ld && ld._isLoad && late[ld._nid]==null ) work.push(ld); } } @@ -197,7 +197,7 @@ private static void _doSchedLate(Node n, Node[] ns, CFGNode[] late) { lca = use_block(n,use, late)._idom(lca,null); // Loads may need anti-dependencies, raising their LCA - if( n instanceof LoadNode load ) + if( n instanceof MemOpNode load && load._isLoad ) lca = find_anti_dep(lca,load,early,late); // Walk up from the LCA to the early, looking for best place. This is @@ -235,7 +235,7 @@ private static boolean better( CFGNode lca, CFGNode best ) { (lca.idepth() > best.idepth() || best instanceof IfNode); } - private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, CFGNode[] late) { + private static CFGNode find_anti_dep(CFGNode lca, MemOpNode load, CFGNode early, CFGNode[] late) { // We could skip final-field loads here. // Walk LCA->early, flagging Load's block location choices for( CFGNode cfg=lca; early!=null && cfg!=early.idom(); cfg = cfg.idom() ) @@ -243,10 +243,12 @@ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, // Walk load->mem uses, looking for Stores causing an anti-dep for( Node mem : load.mem()._outputs ) { switch( mem ) { - case StoreNode st: - assert late[st._nid]!=null; - lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); - break; + case MemOpNode st: + if( !st._isLoad ) { + assert late[mem._nid] != null; + lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st ); + } + break; // Loads do not cause anti-deps on other loads case CallNode st: assert late[st._nid]!=null; lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st); @@ -259,7 +261,6 @@ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, lca = anti_dep(load,phi.region().cfg(i),load.mem().cfg0(),lca,null); break; case NewNode st: break; - case LoadNode ld: break; // Loads do not cause anti-deps on other loads case ReturnNode ret: break; // Load must already be ahead of Return case MemMergeNode ret: break; // Mem uses now on ScopeMin case NeverNode never: break; @@ -270,16 +271,13 @@ private static CFGNode find_anti_dep(CFGNode lca, LoadNode load, CFGNode early, } // - private static CFGNode anti_dep( LoadNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { - // Walk store blocks "reach" from its scheduled location to its earliest - for( ; stblk != defblk.idom(); stblk = stblk.idom() ) { - // Store and Load overlap, need anti-dependence - if( stblk._anti==load._nid ) { - lca = stblk._idom(lca,null); // Raise Loads LCA - if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, - st.addDef(load); // Add anti-dep as well - return lca; // Cap this stores' anti-dep to here - } + private static CFGNode anti_dep( MemOpNode load, CFGNode stblk, CFGNode defblk, CFGNode lca, Node st ) { + // Store and Load overlap, need anti-dependence + if( stblk._anti==load._nid ) { + lca = stblk._idom(lca,null); // Raise Loads LCA + if( lca == stblk && st != null && st._inputs.find(load) == -1 ) // And if something moved, + st.addDef(load); // Add anti-dep as well + return lca; // Cap this stores' anti-dep to here } return lca; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java index a67cf16..37005a7 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/IFG.java @@ -173,7 +173,7 @@ private static void do_node(RegAlloc alloc, Node n) { // Look for a must-use single register conflicting with some other must-def. if( n instanceof MachNode m ) { RegMask ni_mask = m.regmap(i); - if( ni_mask.size1() ) { // Must-use single register + if( ni_mask!=null && ni_mask.size1() ) { // Must-use single register // Search all current live for( LRG tlrg : TMP.keySet() ) { assert tlrg.leader(); @@ -452,8 +452,8 @@ private static int pickRiskyScore( LRG lrg ) { // Always pick callee-save registers as being very large area recovered // and very cheap to spill. if( lrg._machDef instanceof CalleeSaveNode ) - return 1000000-2; - if( lrg._splitDef != null && lrg._splitDef.in(1) instanceof CalleeSaveNode && + return 1000000-2-lrg._mask.firstReg(); + if( lrg._splitDef != null && lrg._splitDef. in(1) instanceof CalleeSaveNode && lrg._splitUse != null && lrg._splitUse.out(0) instanceof ReturnNode ) return 1000000-1; diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java index 79b2ad7..f71851b 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LRG.java @@ -44,11 +44,6 @@ public class LRG { public MachNode _machDef, _machUse; short _uidx; // _machUse input - // Mask set to empty via a kill-mask. This is usually a capacity kill, - // which means we need to split and spill into memory - which means even if - // the def-side has many registers, it MUST spill. - boolean _killed; - // Some splits used in biased coloring MachConcreteNode _splitDef, _splitUse; diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java index 39f3dcb..53cf805 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/LinkMem.java @@ -6,9 +6,14 @@ public class LinkMem { LinkMem( CodeGen code ) { _code = code; } public CodeGen link() { + Encoding enc = _code._encoding; + + // Patch external calls internally + enc.patchGlobalRelocations(); + // Write any large constants into a constant pool; they // are accessed by RIP-relative addressing. - _code._encoding.writeConstantPool(_code._encoding._bits,true); + enc.writeConstantPool(enc._bits,true); return _code; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java index 220e7fb..5516492 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/ListScheduler.java @@ -48,6 +48,9 @@ private XSched init(CFGNode bb, Node n) { computeSingleRDef(bb,use); else computeSingleRDef(bb,n); + + if( n instanceof NewNode || n instanceof CallNode ) + bb.fun()._hasCalls = true; return this; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java index 73acfd1..b04ddbe 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/Machine.java @@ -1,48 +1,93 @@ package com.compilerprogramming.ezlang.compiler.codegen; import com.compilerprogramming.ezlang.compiler.nodes.*; +import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeFunPtr; abstract public class Machine { // Human readable machine name. Something like "x86-64" or "arm" or "risc5" public abstract String name(); // Default opcode size for printing; 4 bytes for most 32-bit risc chips public int defaultOpSize() { return 4; } + // Human-readable name for a register number, e.g. "RAX" or "R0" + public abstract String[] regs(); // Create a split op; any register to any register, including stack slots public abstract SplitNode split( String kind, byte round, LRG lrg); - // List of caller-save registers - public abstract RegMask callerSave(); - // List of callee-save registers - public abstract RegMask calleeSave(); + // List of caller-save registers, as a 64bit mask + public abstract long callerSave(); + // List of never-save registers, e.g. RSP or a ZERO register if you have one + public abstract long neverSave(); + // Call Argument Mask + public abstract RegMask callArgMask(SONTypeFunPtr tfp, int arg); + // Return register mask, based on signature (GPR vs FPR) + public abstract RegMask retMask(SONTypeFunPtr tfp); + // Return PC register + public abstract int rpc(); // Return a MachNode unconditional branch public abstract CFGNode jump(); - // Break an infinite loop - public abstract NeverNode never( CFGNode ctrl ); // Instruction select from ideal nodes public abstract Node instSelect( Node n ); - // Convert a register to a zero-based stack *slot*, or -1. - // Stack slots are assumed 8 bytes each. - // Actual stack layout is up to each CPU. - // X86, with too many args & spills: - // | CALLER | - // | argN | // slot 1, required by callER - // +--------+ - // | RPC | // slot 0, required by callER - // | callee | // slot 3, callEE - // | callee | // slot 2, callEE - // | PAD16 | - // +--------+ - - // RISC/ARM, with too many args & spills: - // | CALLER | - // | argN | // slot 0, required by callER - // +--------+ - // | callee | // slot 3, callEE: might be RPC - // | callee | // slot 2, callEE - // | callee | // slot 1, callEE - // | PAD16 | - // +--------+ - public abstract int stackSlot( int reg ); - // Human-readable name for a register number, e.g. "RAX" or "R0" - public abstract String reg( int reg ); + /** Stack Slot Numbering + +RA gives all registers a number, starting at 0. Stack slots continue this +numbering going up from the last register number. Common register numbers are +0-15 for GPRs, 16-31 for FPRs, 32 for flags, and stack slots starting at +register#33 going up. These numbers are machine-specific, YMMV, etc; e.g. RPC +is only in stack slot 0 on X86; other cpus start with the rpc in a register +which may spill into any generic spill slot. + +For ease of reading and printing, stack slots start again at slot#0 - but +during RA they are actually biased by CPU.MAX_REG. + +Stack Layout during Reg Alloc: + + Slot Value Description + ---- ------- ----------- +MAXREG+ R+1 argR maxArgSlot(calleR tfp), can be 0 + ... +MAXREG+ 1 arg0 + ---- -- Caller ------ +MAXREG+ 0 RPC +MAXREG+ R+E+N PAD optional alignment + +MAXREG+ + Spills Caller Frame Space; N spills +MAXREG+ R+E+1 Spills + +MAXREG+ R+E argE maxArgSlot(calleE tfp), can be 0 + ... +MAXREG+ R+2 arg0 + ---- -- Callee ------- + ... + +Slots defined by maxArgSlot do not have to contain the actual passed argument; +they can be "shadow slots". If they exist, they are property of the frame / +FunNode where the TFP comes from. + +For a given calleR, there are generally many calleE's and they all share the +low-range stack slots. + +Example:, Win64, main(int arg) { calloc(1,size); } + + Post-Warp: SP+56 shadow-main-arg +Pre-alloc Post-Alloc SP+48 RPC +--- ------- ---------- ------------- +40 spill#3 SP+40 SP+40 spill#3 +39 spill#2 SP+32 "" +38 spill#1 SP+24 "" +37 spill#0 SP+16 "" spill#0 +36 shadow-calloc#size SP+ 8 SP+ 8 shadow-calloc#size +35 shadow-calloc#1 SP+ 0 SP+ 0 shadow-calloc#1 +--- --- FRAME BASE ---------- ---- FRAME BASE ------- +34 shadow-main-arg SP+56 +33 RPC SP+48 + + + */ + + + // Maximum stack slot (or 0) for the args in this TFP. This will include + // shadow slots if defined in the ABI, even if all arguments are passed in + // registers. + public abstract short maxArgSlot(SONTypeFunPtr tfp); + } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java index 09d6c6b..5c01edf 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/codegen/RegAlloc.java @@ -129,11 +129,18 @@ public short regnum( Node n ) { } // Printable register number for node n - String reg( Node n ) { + String reg( Node n ) { return reg(n,null); } + String reg( Node n, FunNode fun ) { LRG lrg = lrg(n); if( lrg==null ) return null; + // No register yet, use LRG if( lrg._reg == -1 ) return "V"+lrg._lrg; - return _code._mach.reg(lrg._reg); + // Chosen machine register unless stack-slot and past RA + String[] regs = _code._mach.regs(); + if( lrg._reg < regs.length || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || fun==null ) + return RegMask.reg(regs,lrg._reg); + // Stack-slot past RA uses the frame layout logic + return "[rsp+"+fun.computeStackOffset(_code,lrg._reg)+"]"; } // ----------------------- @@ -141,13 +148,23 @@ String reg( Node n ) { public void regAlloc() { // Insert callee-save registers - FunNode lastFun=null; + String[] regs = _code._mach.regs(); + long neverSave= _code._mach.neverSave(); + for( CFGNode bb : _code._cfg ) + if( bb instanceof FunNode fun ) { + ReturnNode ret = fun.ret(); + int len = Math.min(regs.length,64); + for( int reg=0; reg> i)&1) != 0 ) - sb.p(mach.reg(i)).p(","); + sb.p(reg(regs,i)).p(","); for( int i=0; i<64; i++ ) if( ((_bits1 >> i)&1) != 0 ) - sb.p(mach.reg(i+64)).p(","); + sb.p(reg(regs,i+64)).p(","); return sb.unchar().p("]"); } -} + public static String reg(String[] regs, int i) { + return i Date: Sat, 26 Apr 2025 00:43:54 +0100 Subject: [PATCH 4/5] Sync with final ch21 --- .../ezlang/compiler/IterPeeps.java | 2 - .../ezlang/compiler/SB.java | 23 +- .../ezlang/compiler/nodes/AddNode.java | 2 +- .../ezlang/compiler/nodes/CFGNode.java | 8 +- .../ezlang/compiler/nodes/CProjNode.java | 4 +- .../ezlang/compiler/nodes/CallEndNode.java | 40 +++- .../ezlang/compiler/nodes/CastNode.java | 2 +- .../ezlang/compiler/nodes/ConstantNode.java | 4 +- .../ezlang/compiler/nodes/FunNode.java | 66 +++--- .../ezlang/compiler/nodes/IfNode.java | 12 +- .../ezlang/compiler/nodes/LoadNode.java | 8 +- .../ezlang/compiler/nodes/LoopNode.java | 23 +- .../ezlang/compiler/nodes/MemMergeNode.java | 11 +- .../ezlang/compiler/nodes/MemOpNode.java | 39 +++- .../ezlang/compiler/nodes/NeverNode.java | 3 +- .../ezlang/compiler/nodes/NewNode.java | 40 +++- .../ezlang/compiler/nodes/Node.java | 41 ++-- .../ezlang/compiler/nodes/PhiNode.java | 87 +++++--- .../ezlang/compiler/nodes/ProjNode.java | 2 +- .../ezlang/compiler/nodes/RegionNode.java | 44 +++- .../ezlang/compiler/nodes/ReturnNode.java | 41 +++- .../ezlang/compiler/nodes/ShlNode.java | 2 +- .../ezlang/compiler/nodes/SplitNode.java | 5 +- .../ezlang/compiler/nodes/StopNode.java | 3 +- .../ezlang/compiler/nodes/StoreNode.java | 4 +- .../ezlang/compiler/nodes/StructNode.java | 2 +- .../ezlang/compiler/print/ASMPrinter.java | 204 ++++-------------- .../ezlang/compiler/print/IRPrinter.java | 1 - 28 files changed, 423 insertions(+), 300 deletions(-) diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java index c18aa90..f58ae9f 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/IterPeeps.java @@ -49,7 +49,6 @@ public class IterPeeps { public IterPeeps( long seed ) { _work = new WorkList<>(seed); } - @SuppressWarnings("unchecked") public N add( N n ) { return (N)_work.push(n); } public void addAll( Ary ary ) { _work.addAll(ary); } @@ -127,7 +126,6 @@ private boolean progressOnList(CodeGen code) { * Classic WorkList, with a fast add/remove, dup removal, random pull. * The Node's nid is used to check membership in the worklist. */ - @SuppressWarnings("unchecked") public static class WorkList { private Node[] _es; diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java index 0ed652d..fe7f97d 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/SB.java @@ -29,28 +29,21 @@ else if( Double.isInfinite(s) ) { public SB p( int s ) { _sb.append(s); return this; } public SB p( long s ) { _sb.append(s); return this; } public SB p( boolean s) { _sb.append(s); return this; } - // 1 byte, 2 hex digits, 8 bits - public SB hex1(int s) { - int digit = (s>>4) & 0xf; - _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); - digit = s & 0xf; - _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); + // 4 hex digits + public SB hex4(int s) { + assert (s>>4*4)==0; // Fits in 16 bits + for( int i=0; i<4; i++ ) { + int digit = (s>>((3-i)*4)) & 0xf; + _sb.append((char)((digit <= 9 ? '0' : ('A'-10))+digit)); + } return this; } - // 2 bytes, 4 hex digits, 16 bits, Big Endian - public SB hex2(int s) { return hex1(s>> 8).hex1(s); } - // 4 bytes, 8 hex digits, 32 bits, Big Endian - public SB hex4(int s) { return hex2(s>>16).hex2(s); } - // 8 bytes, 16 hex digits, 64 bits, Big Endian - public SB hex8(long s) { return hex4((int)(s>>32)).hex4((int)s); } - // Fixed width field public SB fix( int sz, String s ) { for( int i=0; i= 0 ) lhs = ((CFGNode)lhs.addDep(dep)).idom(); - if( comp <= 0 ) rhs = ((CFGNode)rhs.addDep(dep)).idom(); + if( comp >= 0 ) lhs = (dep==null ? lhs : dep.addDep(lhs)).idom(); + if( comp <= 0 ) rhs = (dep==null ? rhs : dep.addDep(rhs)).idom(); } return lhs; } @@ -102,7 +102,7 @@ public FunNode fun() { public LoopNode loop() { return _ltree._head; } public int loopDepth() { return _ltree==null ? 0 : _ltree.depth(); } - LoopTree _ltree; + public LoopTree _ltree; public int _pre; // Pre-order numbers for loop tree finding static class LoopTree { LoopTree _par; diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java index 9e2f384..9c18492 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CProjNode.java @@ -44,7 +44,7 @@ public Node idealize() { } // Flip a negating if-test, to remove the not - if( ctrl() instanceof IfNode iff && iff.pred().addDep(this) instanceof NotNode not ) + if( ctrl() instanceof IfNode iff && addDep(iff.pred()) instanceof NotNode not ) return new CProjNode(new IfNode(iff.ctrl(),not.in(1)).peephole(),1-_idx,_idx==0 ? "False" : "True"); // Copy of some other input @@ -58,7 +58,7 @@ public void invert() { } @Override - boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; } + public boolean eq( Node n ) { return _idx == ((CProjNode)n)._idx; } @Override int hash() { return _idx; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java index c705f53..07c2aa8 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/CallEndNode.java @@ -1,7 +1,11 @@ package com.compilerprogramming.ezlang.compiler.nodes; import com.compilerprogramming.ezlang.compiler.Compiler; +import com.compilerprogramming.ezlang.compiler.SB; import com.compilerprogramming.ezlang.compiler.codegen.CodeGen; +import com.compilerprogramming.ezlang.compiler.codegen.Encoding; +import com.compilerprogramming.ezlang.compiler.codegen.RegMask; +import com.compilerprogramming.ezlang.compiler.codegen.RegMaskRW; import com.compilerprogramming.ezlang.compiler.sontypes.*; import java.util.BitSet; @@ -38,7 +42,7 @@ public SONType compute() { return SONTypeTuple.RET.dual(); SONType ret = SONType.BOTTOM; SONTypeMem mem = SONTypeMem.BOT; - if( call.fptr().addDep(this)._type instanceof SONTypeFunPtr tfp ) { + if( addDep(call.fptr())._type instanceof SONTypeFunPtr tfp ) { ret = tfp.ret(); // Here, if I can figure out I've found *all* callers, then I can meet // across the linked returns and join with the function return type. @@ -84,10 +88,10 @@ public Node idealize() { return this; } } else { - fun.addDep(this); + addDep(fun); } } else { // Function ptr has multiple users (so maybe multiple call sites) - fptr.addDep(this); + addDep(fptr); } } @@ -97,4 +101,34 @@ public Node idealize() { @Override public Node pcopy(int idx) { return _folding ? in(1).in(idx) : null; } + + // ------------ + // MachNode specifics, shared across all CPUs + public int _xslot; + private RegMask _retMask; + private RegMask _kills; + public void cacheRegs(CodeGen code) { + // Return mask depends on TFP (either GPR or FPR) + _retMask = code._mach.retMask(call().tfp()); + // Kill mask is all caller-saves, and any mirror stack slots for args + // in registers. + RegMaskRW kills = code._callerSave.copy(); + // Start of stack slots + int maxReg = code._mach.regs().length; + // Incoming function arg slots, all low numbered in the RA + int fslot = fun()._maxArgSlot; + // Killed slots for this calls outgoing args + int xslot = code._mach.maxArgSlot(call().tfp()); + _xslot = (maxReg+fslot)+xslot; + for( int i=0; i>3) + : stackSlot - _maxArgSlot; + return slotRotate*8; + } + } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java index b9475bd..862c8b6 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/IfNode.java @@ -20,7 +20,9 @@ public IfNode(Node ctrl, Node pred) { @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); } @@ -57,19 +59,19 @@ public Node idealize() { // test on either the true or false branch, that side wins. if( !pred()._type.isHighOrConst() ) for( CFGNode dom = idom(), prior=this; dom!=null; prior = dom, dom = dom.idom() ) - if( dom.addDep(this) instanceof IfNode iff && iff.pred().addDep(this)==pred() && prior instanceof CProjNode prj ) { + if( addDep(dom) instanceof IfNode iff && addDep(iff.pred())==pred() && prior instanceof CProjNode prj ) { setDef(1,con( prj._idx==0 ? 1 : 0 )); return this; } return null; } - // MachNode variants need to support this and invert the conditional test. + // MachNode variants need to support this and negate the conditional test. // The following CProjs will be inverted by the caller. - public void invert() { throw Utils.TODO(); } + public void negate() { throw Utils.TODO(); } // Negate the sense of a test - public static String invert( String bop ) { + public static String negate( String bop ) { return switch( bop ) { case "<" -> ">="; case "<=" -> ">" ; diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java index fdec37a..4607e8e 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoadNode.java @@ -19,7 +19,7 @@ public class LoadNode extends MemOpNode { * @param off The offset inside the struct base */ public LoadNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off) { - super(name, alias, glb, mem, ptr, off); + super(name, alias, true, glb, mem, ptr, off); } // GraphVis DOT code (must be valid Java identifiers) and debugger labels @@ -76,7 +76,7 @@ public Node idealize() { while( true ) { switch( mem ) { case StoreNode st: - if( ptr == st.ptr().addDep(this) && off() == st.off() ) + if( ptr == addDep(st.ptr()) && off() == st.off() ) return extend(castRO(st.val())); // Proved equal // Can we prove unequal? Offsets do not overlap? if( !off()._type.join(st.off()._type).isHigh() && // Offsets overlap @@ -87,7 +87,7 @@ public Node idealize() { break; case PhiNode phi: // Assume related - phi.addDep(this); + addDep(phi); break outer; case ConstantNode top: break outer; // Assume shortly dead case ProjNode mproj: // Memory projection @@ -181,7 +181,7 @@ private boolean profit(PhiNode phi, int idx) { if( px==null ) return false; if( px._type instanceof SONTypeMem mem && mem._t.isHighOrConst() ) return true; if( px instanceof StoreNode st1 && ptr()==st1.ptr() && off()==st1.off() ) return true; - px.addDep(this); + addDep(px); return false; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java index e3f63f3..4ff533c 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/LoopNode.java @@ -2,6 +2,7 @@ import com.compilerprogramming.ezlang.compiler.codegen.CodeGen; import com.compilerprogramming.ezlang.compiler.sontypes.SONType; +import com.compilerprogramming.ezlang.compiler.sontypes.SONTypeMem; public class LoopNode extends RegionNode { public LoopNode( Node entry ) { super(null,entry,null); } @@ -40,19 +41,23 @@ public StopNode forceExit( FunNode fun, StopNode stop ) { x = x.idom(); } // Found a no-exit loop. Insert an exit - NeverNode iff = CodeGen.CODE._mach == null - ? new NeverNode(back()) // Ideal never-branch - : CodeGen.CODE._mach.never(back()); // Machine never-branch + NeverNode iff = new NeverNode(back()); // Ideal never-branch CProjNode t = new CProjNode(iff,0,"True" ).init(); CProjNode f = new CProjNode(iff,1,"False").init(); - setDef(2,t); - iff._ltree = t._ltree = _ltree; + setDef(2,t); // True continues loop, False (never) exits loop ReturnNode ret = fun.ret(); - f._ltree = ret._ltree; + iff._ltree = t._ltree = _ltree; + ret._ltree = f._ltree = stop._ltree; // Now fold control into the exit. Might have 1 valid exit, or an // XCtrl or a bunch of prior NeverNode exits. Node top = new ConstantNode(SONType.TOP).peephole(); + Node memout = new MemMergeNode(false); + memout.addDef(f); // placeholder for control + for( Node u : _outputs ) + if( u instanceof PhiNode phi && phi._type.isa(SONTypeMem.BOT) ) + memout.addDef(phi); + Node ctrl = ret.ctrl(), mem = ret.mem(), expr = ret.expr(); if( ctrl!=null && ctrl._type != SONType.XCONTROL ) { // Perfect aligned exit? @@ -61,17 +66,17 @@ public StopNode forceExit( FunNode fun, StopNode stop ) { expr instanceof PhiNode prez && prez.region()==r ) ) { // Nope, insert an aligned exit layer RegionNode r = new RegionNode((Node)null,ctrl).init(); - ctrl = r; r._ltree = _ltree; + ctrl = r; r._ltree = stop._ltree; mem = new PhiNode(r,mem ).init(); expr = new PhiNode(r,expr).init(); } // Append new Never exit ctrl.addDef(f ); - mem .addDef(top); + mem .addDef(memout); expr.addDef(top); } else { ctrl = f; - mem = top; + mem = memout; expr = top; } ret.setDef(0,ctrl); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java index 09487c9..6a36e74 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/MemMergeNode.java @@ -65,11 +65,18 @@ public Node merge() { if( inProgress() ) return null; // If not merging any memory (all memory is just the default) - if( nIns()==2 ) + if( allDefault() ) return in(1); // Become default memory return null; } + private boolean allDefault() { + for( int i=2; i _inputs; + public Ary _inputs; /** * Outputs reference Nodes that are not null and have this Node as an @@ -44,7 +44,7 @@ public abstract class Node { * walked in either direction. These outputs are typically used for * efficient optimizations but otherwise have no semantics meaning. */ - public final Ary _outputs; + public Ary _outputs; /** @@ -550,17 +550,16 @@ public SONType setType(SONType type) { * or output of this node, that is, it is at least one step away. The node * being added must benefit from this node being peepholed. */ - Node addDep( Node dep ) { + N addDep( N dep ) { // Running peepholes during the big assert cannot have side effects // like adding dependencies. - if( CODE._midAssert ) return this; - if( dep == null ) return this; - if( _deps==null ) _deps = new Ary<>(Node.class); - if( _deps .find(dep) != -1 ) return this; // Already on list - if( _inputs .find(dep) != -1 ) return this; // No need for deps on immediate neighbors - if( _outputs.find(dep) != -1 ) return this; - _deps.add(dep); - return this; + if( CODE._midAssert ) return dep; + if( dep._deps==null ) dep._deps = new Ary<>(Node.class); + if( dep._deps .find(this) != -1 ) return dep; // Already on list + if( dep._inputs .find(this) != -1 ) return dep; // No need for deps on immediate neighbors + if( dep._outputs.find(this) != -1 ) return dep; + dep._deps.add(this); + return dep; } // Move the dependents onto a worklist, and clear for future dependents. @@ -586,7 +585,7 @@ public void moveDepsToWorklist( ) { } // Subclasses add extra checks (such as ConstantNodes have same constant), // and can assume "this!=n" and has the same Java class. - boolean eq( Node n ) { return true; } + public boolean eq( Node n ) { return true; } // Cached hash. If zero, then not computed AND this Node is NOT in the GVN @@ -668,7 +667,7 @@ public Node swap12() { boolean allCons(Node dep) { for( int i=1; i(Node.class); + n._outputs = new Ary<>(Node.class); + n._deps = null; + n._hash = 0; + return n; + } + // Report any post-optimize errors public CompilerException err() { return null; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java index 5523b17..903f792 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/PhiNode.java @@ -59,7 +59,7 @@ public SONType compute() { for (int i = 1; i < nIns(); i++) // If the region's control input is live, add this as a dependency // to the control because we can be peeped should it become dead. - if( r.in(i).addDep(this)._type != SONType.XCONTROL ) + if( addDep(r.in(i))._type != SONType.XCONTROL ) t = t.meet(in(i)._type); return t; } @@ -81,37 +81,15 @@ public Node idealize() { if( r.in(i)._type == SONType.XCONTROL ) return null; - // Pull "down" a common data op. One less op in the world. One more - // Phi, but Phis do not make code. - // Phi(op(A,B),op(Q,R),op(X,Y)) becomes - // op(Phi(A,Q,X), Phi(B,R,Y)). - Node op = in(1); - if( !isMem() && op.nIns()==3 && op.in(0)==null && same_op() ) { - assert !(op instanceof CFGNode); - Node[] lhss = new Node[nIns()]; - Node[] rhss = new Node[nIns()]; - lhss[0] = rhss[0] = in(0); // Set Region - for( int i=1; i 0 && idom.in(0) != iff ) idom = idom.idom(); @@ -137,13 +115,52 @@ public Node idealize() { return null; } + // Same op on all Phi paths; all ops have only the Phi as a use. + // None have a control input. private boolean same_op() { - for( int i=2; i 1 ) { // Too many users, but addDep in case lose users + for( Node out : op._outputs ) + if( out!=null && out!=this ) + addDep(out); return false; + } + } return true; } + private Node drop_same_op() { + assert !(in(1) instanceof CFGNode); + Node op = in(1); + Node cp = op.copy(); + cp._type = null; // Fresh type + cp.addDef(null); // No control + + for( int j=1; j2 && !hasMidUser(region) ) { + assert !region.inProgress(); + // Fold Phis + for( Node use : _outputs ) { + if( use instanceof PhiNode phi ) { + PhiNode phi2 = phi.in(i) instanceof PhiNode phi2x && phi2x.region()==region ? phi2x : null; + for( int j=1; j1 && in(nIns()-1) == null; } // Never equal if inProgress - @Override boolean eq( Node n ) { return !inProgress(); } + @Override public boolean eq( Node n ) { return !inProgress(); } } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java index b44328e..065bdcb 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/ReturnNode.java @@ -2,6 +2,10 @@ import com.compilerprogramming.ezlang.compiler.Compiler; import com.compilerprogramming.ezlang.compiler.SB; +import com.compilerprogramming.ezlang.compiler.Utils; +import com.compilerprogramming.ezlang.compiler.codegen.CodeGen; +import com.compilerprogramming.ezlang.compiler.codegen.Encoding; +import com.compilerprogramming.ezlang.compiler.codegen.RegMask; import com.compilerprogramming.ezlang.compiler.sontypes.*; import com.compilerprogramming.ezlang.exceptions.CompilerException; @@ -30,7 +34,7 @@ public ReturnNode(Node ctrl, Node mem, Node data, Node rpc, FunNode fun ) { public Node mem () { return in(1); } public Node expr() { return in(2); } public Node rpc () { return in(3); } - public FunNode fun() { return _fun; } + @Override public FunNode fun() { return _fun; } @Override public String label() { return "Return"; } @@ -128,4 +132,39 @@ static CompilerException mixerr( boolean ti, boolean tf, boolean tp, boolean tn) if( tp || tn ) sb.p("reference and "); return Compiler.error(sb.unchar(5).toString()); } + + + // ------------ + // MachNode specifics, shared across all CPUs + public String op() { + return _fun._frameAdjust > 0 ? "addi" : "ret"; + } + // Correct Nodes outside the normal edges + public void postSelect(CodeGen code) { + FunNode fun = (FunNode)rpc().in(0); + _fun = fun; + fun.setRet(this); + } + public RegMask regmap(int i) { + return i==2 + ? CodeGen.CODE._mach.retMask(_fun.sig()) + : CodeGen.CODE._retMasks[i]; + } + public RegMask outregmap() { return null; } + public void encoding( Encoding enc ) { throw Utils.TODO(); } + public void asm(CodeGen code, SB sb) { + int frameAdjust = fun()._frameAdjust; + if( frameAdjust>0 ) + sb.p("rsp += #").p(frameAdjust).p("\nret"); + // Post code-gen, just print the "ret" + if( code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() ) + // Prints return reg (either RAX or XMM0), RPC and then the + // callee-save registers. + for( int i=2; i (x << i) + (c << i) - if( lhs instanceof AddNode add && add.addDep(this).in(2)._type instanceof SONTypeInteger c && c.isConstant() ) { + if( lhs instanceof AddNode add && addDep(add).in(2)._type instanceof SONTypeInteger c && c.isConstant() ) { long sum = c.value() << shl.value(); if( Integer.MIN_VALUE <= sum && sum <= Integer.MAX_VALUE ) return new AddNode(new ShlNode(add.in(1),rhs).peephole(), Compiler.con(sum) ); diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java index 9a980ec..0040146 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/SplitNode.java @@ -11,7 +11,10 @@ public abstract class SplitNode extends MachConcreteNode { public SplitNode(String kind, byte round, Node[] nodes) { super(nodes); _kind = kind; _round = round; } @Override public String op() { return "mov"; } @Override public StringBuilder _print1(StringBuilder sb, BitSet visited) { - return in(1)._print0(sb.append("mov("),visited).append(")"); + sb.append("mov("); + if( in(1) == null ) sb.append("---"); + else in(1)._print0(sb,visited); + return sb.append(")"); } @Override public SONType compute() { return in(0)._type; } @Override public Node idealize() { return null; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java index 1d13a7f..f0fbdfa 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StopNode.java @@ -25,7 +25,8 @@ public StringBuilder _print1(StringBuilder sb, BitSet visited) { if( ret()!=null ) return ret()._print0(sb,visited); sb.append("Stop[ "); for( Node ret : _inputs ) - ret._print0(sb, visited).append(" "); + if( ret!=null ) + ret._print0(sb, visited).append(" "); return sb.append("]"); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java index ca4a712..8d2c679 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StoreNode.java @@ -22,7 +22,7 @@ public class StoreNode extends MemOpNode { * @param value Value to be stored */ public StoreNode(String name, int alias, SONType glb, Node mem, Node ptr, Node off, Node value, boolean init) { - super(name, alias, glb, mem, ptr, off, value); + super(name, alias, false, glb, mem, ptr, off, value); _init = init; } @@ -97,7 +97,7 @@ private boolean checkOnlyUse(Node mem) { // when the other uses go away we can retry. for( Node use : mem._outputs ) if( use != this ) - use.addDep(this); + addDep(use); return false; } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java index 2d0779e..71f3bb2 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/nodes/StructNode.java @@ -39,7 +39,7 @@ public SONTypeStruct compute() { public Node idealize() { return null; } @Override - boolean eq(Node n) { return _ts == ((StructNode)n)._ts; } + public boolean eq(Node n) { return _ts == ((StructNode)n)._ts; } @Override int hash() { return _ts==null ? 0 : _ts.hashCode(); } diff --git a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java index 646c7e0..f620244 100644 --- a/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java +++ b/seaofnodes/src/main/java/com/compilerprogramming/ezlang/compiler/print/ASMPrinter.java @@ -17,48 +17,6 @@ public static SB print(SB sb, CodeGen code) { for( int i=0; i iadr ) - iadr++; - - // constant pool - Encoding enc = code._encoding; - if( enc!=null && !enc._bigCons.isEmpty() ) { - sb.p("--- Constant Pool ------").nl(); - // By log size - for( int log = 3; log >= 0; log-- ) { - for( Node op : enc._bigCons.keySet() ) { - Encoding.Relo relo = enc._bigCons.get(op); - if( relo._t.log_size()==log ) { - sb.hex2(iadr).p(" "); - if( relo._t instanceof SONTypeTuple tt ) { - for( SONType tx : tt._types ) { - switch( log ) { - case 0: sb.hex1(enc.read1(iadr)); break; - case 1: sb.hex2(enc.read2(iadr)); break; - case 2: sb.hex4(enc.read4(iadr)); break; - case 3: sb.hex8(enc.read8(iadr)); break; - } - iadr += (1< CodeGen.Phase.RegAlloc.ordinal(); - final boolean postEncode= code._phase.ordinal() >=CodeGen.Phase.Encoding.ordinal(); - boolean once=false; - for( Node n : bb.outs() ) { - if( !(n instanceof PhiNode phi) ) continue; - if( phi._type instanceof SONTypeMem || phi._type instanceof SONTypeRPC ) continue; // Nothing for the hidden ones - // Post-RegAlloc phi prints all on one line + // Count Phis + int nPhi=0; + for( ; nPhi0 ) { + // Post-alloc phi prints all on one line if( postAlloc ) { - if( !once ) { once=true; sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); } - sb.p(phi._label).p(':').p(code.reg(phi)).p(','); + sb.fix(4," ").p(" ").fix(encWidth,"").p(" "); + for( int i=0; i