From d4d894407b9cbf83217da3bde1ecbd6d783297e4 Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Tue, 15 Apr 2025 12:58:13 -0700 Subject: Revamp calling convention math Handles win64 shadow stack space correctly. Unifies most of the caller-save mask, return mask, arg mask computations. Unifies most of the frame-size math across all chips. Large documentation comment in Machine about how stack math is computed. --- .../com/seaofnodes/simple/codegen/CodeGen.java | 34 ++++++ .../java/com/seaofnodes/simple/codegen/IFG.java | 4 +- .../com/seaofnodes/simple/codegen/Machine.java | 105 ++++++++++++------ .../com/seaofnodes/simple/codegen/RegAlloc.java | 92 +++++++--------- .../com/seaofnodes/simple/codegen/RegMask.java | 10 +- .../com/seaofnodes/simple/codegen/RegMaskRW.java | 5 + .../com/seaofnodes/simple/node/CallEndNode.java | 32 +++++- .../java/com/seaofnodes/simple/node/FunNode.java | 66 +++++++---- .../java/com/seaofnodes/simple/node/LoopNode.java | 4 +- .../java/com/seaofnodes/simple/node/NewNode.java | 38 ++++++- .../java/com/seaofnodes/simple/node/PhiNode.java | 4 +- .../com/seaofnodes/simple/node/ReturnNode.java | 38 +++++++ .../simple/node/cpus/arm/CallEndARM.java | 18 +-- .../seaofnodes/simple/node/cpus/arm/FunARM.java | 35 ++---- .../seaofnodes/simple/node/cpus/arm/NewARM.java | 10 -- .../seaofnodes/simple/node/cpus/arm/RetARM.java | 28 ----- .../seaofnodes/simple/node/cpus/arm/SplitARM.java | 4 +- .../com/seaofnodes/simple/node/cpus/arm/arm.java | 92 +++++----------- .../simple/node/cpus/riscv/CallEndRISC.java | 17 +-- .../simple/node/cpus/riscv/CallRISC.java | 2 - .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 35 ++---- .../simple/node/cpus/riscv/NJmpRISC.java | 46 -------- .../seaofnodes/simple/node/cpus/riscv/NewRISC.java | 8 +- .../seaofnodes/simple/node/cpus/riscv/RetRISC.java | 39 +------ .../simple/node/cpus/riscv/SplitRISC.java | 8 +- .../seaofnodes/simple/node/cpus/riscv/riscv.java | 120 +++++++------------- .../simple/node/cpus/x86_64_v2/CallEndX86.java | 17 +-- .../simple/node/cpus/x86_64_v2/CallX86.java | 1 - .../simple/node/cpus/x86_64_v2/FunX86.java | 48 +++----- .../simple/node/cpus/x86_64_v2/MemOpX86.java | 4 +- .../simple/node/cpus/x86_64_v2/NewX86.java | 17 +-- .../simple/node/cpus/x86_64_v2/RetX86.java | 31 +----- .../simple/node/cpus/x86_64_v2/SplitX86.java | 4 +- .../simple/node/cpus/x86_64_v2/StoreX86.java | 10 +- .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 122 ++++----------------- 35 files changed, 462 insertions(+), 686 deletions(-) delete mode 100644 chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NJmpRISC.java (limited to 'chapter21/src/main') diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 8f0a97c8..146c016a 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -213,6 +213,18 @@ public class CodeGen { 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; @@ -230,6 +242,27 @@ public class CodeGen { 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> 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>3) + : stackSlot - _maxArgSlot; + return slotRotate*8; } + } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java index 847a4aa0..cc11d811 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java @@ -46,9 +46,7 @@ public class LoopNode extends RegionNode { 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); // True continues loop, False (never) exits loop diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java index 6a7b16ee..3d4bad5e 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java @@ -1,7 +1,9 @@ package com.seaofnodes.simple.node; -import com.seaofnodes.simple.type.*; +import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.type.*; import java.util.BitSet; /** @@ -80,4 +82,38 @@ public class NewNode extends Node implements MultiNode { @Override int hash() { return _ptr.hashCode(); } + + // ------------ + // MachNode specifics, shared across all CPUs + public int _arg2Reg, _xslot; + private RegMask _arg3Mask; + private RegMask _retMask; + private RegMask _kills; + public void cacheRegs(CodeGen code) { + _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2).firstReg(); + _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3); + // Return mask depends on TFP (either GPR or FPR) + _retMask = code._mach.retMask(TypeFunPtr.CALLOC); + // Kill mask is all caller-saves, and any mirror stack slots for args + // 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 = cfg0().fun()._maxArgSlot; + // Killed slots for this calls outgoing args + int xslot = code._mach.maxArgSlot(TypeFunPtr.CALLOC); + _xslot = (maxReg+fslot)+xslot; + for( int i=0; i 1 ) { // Too many users, but addDep in case lose users for( Node out : op._outputs ) if( out!=null ) out.addDep(this); return false; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/ReturnNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/ReturnNode.java index 6fa76ef8..317f4ce8 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/ReturnNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/ReturnNode.java @@ -3,6 +3,9 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.Parser; import com.seaofnodes.simple.SB; import com.seaofnodes.simple.Utils; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.RegMask; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -127,4 +130,39 @@ public class ReturnNode extends CFGNode { if( tp || tn ) sb.p("reference and "); return Parser.error(sb.unchar(5).toString(),loc); } + + + // ------------ + // 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= 1L<<12 ) throw Utils.TODO(); + enc.add4(arm.imm_inst(arm.OPI_ADD, -sz&0xFFF, arm.RSP, arm.RSP)); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java index face3cdd..f2804437 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java @@ -7,18 +7,8 @@ import com.seaofnodes.simple.node.*; public class NewARM extends NewNode implements MachNode { // 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)); } - - @Override public void asm(CodeGen code, SB sb) { - sb.p("#calloc, ").p(code.reg(size())); - } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java index ec32c1bc..2136ebf5 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java @@ -6,38 +6,10 @@ import com.seaofnodes.simple.node.*; 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)); return; @@ -33,7 +33,7 @@ public class SplitARM extends SplitNode { 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)); return; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java index ac134ab4..a36cdd51 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java @@ -42,13 +42,7 @@ public class arm extends Machine { "D24", "D25", "D26", "D27", "D28", "D29", "D30", "D31", "flags" }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[stk#"+(reg-REGS.length)+"]"; - } - // 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 +58,11 @@ public class arm extends Machine { // 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); + //static final RegMask RPC_MASK = new RegMask(1L << X30); // Arguments masks static final RegMask X0_MASK = new RegMask(X0); @@ -522,8 +516,9 @@ public class arm extends Machine { return (opcode << 26) | delta; } + @Override public RegMask callArgMask(TypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } static RegMask callInMask(TypeFunPtr tfp, int idx ) { - 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; @@ -543,61 +538,29 @@ public class arm extends Machine { } // Return the max stack slot used by this signature, or 0 @Override public short maxArgSlot( TypeFunPtr 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<= 1L<<12 ) throw Utils.TODO(); + enc.add4(riscv.i_type(riscv.OP_IMM, riscv.RSP, 0, riscv.RSP, -sz & 0xFFF)); } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NJmpRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NJmpRISC.java deleted file mode 100644 index 39e9052b..00000000 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NJmpRISC.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.seaofnodes.simple.node.cpus.riscv; - -import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.*; -import com.seaofnodes.simple.node.*; -import com.seaofnodes.simple.type.TypeFunPtr; -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/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java index 0ca76d3f..3b0018c2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java @@ -8,18 +8,12 @@ import com.seaofnodes.simple.print.ASMPrinter; 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.A1_MASK : null; } - @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.i_type(riscv.OP_IMM , riscv.A0 , 0, riscv.ZERO, 1)); + 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)); } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java index 2edc61c7..8a8186cd 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java @@ -9,42 +9,13 @@ import com.seaofnodes.simple.node.ReturnNode; import com.seaofnodes.simple.node.MachNode; public class RetRISC extends ReturnNode implements MachNode { - public RetRISC(ReturnNode ret, FunNode fun) { - super(ret, fun); - if( fun != null ) fun.setRet(this); - } - @Override public String op() { - return 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 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 diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java index c86b30f6..6a8f34b1 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java @@ -13,18 +13,19 @@ public class riscv extends Machine { } @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; + 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, F8 = 60, F9 = 61, F10 = 62, F11 = 63; static final int MAX_REG = 64; @@ -40,12 +41,13 @@ public class riscv extends Machine { "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<= 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<= 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 } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java index e751fa23..170fc8c5 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java @@ -8,29 +8,14 @@ import com.seaofnodes.simple.type.TypeFunPtr; 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.callInMask(TypeFunPtr.CALLOC,2) : 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"); // This has to call the *native* ABI, regardless of how Simple is // being compiled, because it links against the native calloc. // ldi rcx,#1 // number of elements to calloc - RegMask ARG3 = x86_64_v2.callInMask(TypeFunPtr.CALLOC,3); - enc.add1(0xB8 + ARG3.firstReg()).add4(1); + enc.add1(0xB8 + _arg2Reg).add4(1); // E8 cd CALL rel32; enc.add1(0xE8); enc.add4(0); // offset } - - // General form: "alloc #bytes" - @Override public void asm(CodeGen code, SB sb) { - sb.p("#calloc, ").p(code.reg(size())); - } - } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java index 0b8f88a3..a35d43a2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java @@ -6,37 +6,14 @@ import com.seaofnodes.simple.node.*; 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 sz = ((FunX86)fun())._frameAdjust; + int sz = fun()._frameAdjust; if( sz != 0 ) { - enc.add1( x86_64_v2.REX_W ).add1( x86_64_v2.imm8(sz*8) ? 0x83 : 0x81 ); + 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*8) ) enc.add1(sz*8); - else enc.add4(sz*8); + 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= 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 ? TypeFloat.F64 : TypeInteger.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 ? TypeFloat.F64 : TypeInteger.BOT, dst, (short)x86_64_v2.RSP, (short)-1, off, 0); return; } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java index ac5de47d..c906c3f2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java @@ -53,12 +53,16 @@ public class StoreX86 extends MemOpX86 { // Non-immediate encoding static void encVal( Encoding enc, Type decl, short ptr, short idx, short src, int off, int scale ) { - if( decl instanceof TypeFloat ) { + 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==TypeFloat.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 - int log = decl.log_size(); 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 ) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java index b8688f53..e6390386 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java @@ -20,6 +20,17 @@ public class x86_64_v2 extends Machine { 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 @@ -165,23 +176,6 @@ public class x86_64_v2 extends Machine { } } - // 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", - }; - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[stk#"+(reg-REGS.length)+"]"; - } - - // Stack slots, in units of 8 bytes. - @Override public int stackSlot( int reg ) { - return reg < REGS.length ? -1 : reg-REGS.length; - } // Limit of float args passed in registers static RegMask[] XMMS8 = new RegMask[]{ @@ -191,6 +185,7 @@ public class x86_64_v2 extends Machine { // Map from function signature and argument index to register. // Used to set input registers to CallNodes, and ParmNode outputs. + @Override public RegMask callArgMask( TypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); } static RegMask callInMask( TypeFunPtr tfp, int idx ) { if( idx==0 ) return RPC_MASK; if( idx==1 ) return null; @@ -201,7 +196,7 @@ public class x86_64_v2 extends Machine { }; } - // Maximum stack args used by this signature, +1 for RPC + // Maximum stack args used by this signature @Override public short maxArgSlot( TypeFunPtr tfp ) { return switch( CodeGen.CODE._callingConv ) { case "SystemV" -> maxArgSlotSys5 (tfp); @@ -245,7 +240,7 @@ public class x86_64_v2 extends Machine { : WIN64_CALL[idx-2]; } static short maxArgSlotWin64(TypeFunPtr tfp) { - return (short)(1/*RPC*/+ tfp.nargs()); + return (short)tfp.nargs(); } // Sys5: max 6 GPRs and 8 FPRS filled first. Extra args land in increasing @@ -296,10 +291,9 @@ public class x86_64_v2 extends Machine { else icnt++; } int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0); - return (short)(1/*RPC*/+nstk); + return (short)nstk; } - // caller saved(systemv) static final long SYSTEM5_CALLER_SAVE = (1L << RAX) | (1L << RCX) | (1L << RDX) | (1L << RDI) | (1L << RSI) | @@ -307,91 +301,28 @@ public class x86_64_v2 extends Machine { (1L << FLAGS) | // Flags are killed // All FP regs are killed FP_BITS; - static final RegMask SYSTEM5_CALLER_SAVE_MASK = new RegMask(SYSTEM5_CALLER_SAVE); - // caller saved(win64) static final long WIN64_CALLER_SAVE = (1L<< RAX) | (1L<< RCX) | (1L<< RDX) | (1L<< R08) | (1L<< R09) | (1L<< R10) | (1L<< R11) | (1L< 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) { @@ -403,11 +334,6 @@ public class x86_64_v2 extends Machine { return new UJmpX86(); } - // Break an infinite loop - @Override public NeverNode never(CFGNode ctrl) { - throw Utils.TODO(); - } - // Instruction selection @Override public Node instSelect(Node n) { -- cgit v1.2.3