diff options
| author | Cliff Click <cliffc@acm.org> | 2025-04-15 12:58:13 -0700 |
|---|---|---|
| committer | Cliff Click <cliffc@acm.org> | 2025-04-15 12:58:13 -0700 |
| commit | d4d894407b9cbf83217da3bde1ecbd6d783297e4 (patch) | |
| tree | 638126cb17485b9086c9ac8146fcdbad4dbc2435 /chapter21/src/main | |
| parent | 5e7223504d356cbb48c397bed0cd0012eb075935 (diff) | |
| download | Simple-d4d894407b9cbf83217da3bde1ecbd6d783297e4.tar.gz Simple-d4d894407b9cbf83217da3bde1ecbd6d783297e4.zip | |
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.
Diffstat (limited to 'chapter21/src/main')
35 files changed, 462 insertions, 686 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java index 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<Machine>) 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<maxReg; reg++ ) + if( !_callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) + _retMasks[i++] = new RegMask(reg); + _rpcMask = new RegMask(_mach.rpc()); + _retMasks[3] = _rpcMask; + + // Convert to machine ops long t0 = System.currentTimeMillis(); _uid = 1; // All new machine nodes reset numbering @@ -340,6 +373,7 @@ public class CodeGen { return "N"+ n._nid; } + // --------------------------- // Encoding public int _tEncode; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java index c1b9d3e4..81955779 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java @@ -453,8 +453,8 @@ abstract public class IFG { // 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/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java index 220ce7a4..1c12bfcb 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -8,45 +8,86 @@ abstract public class Machine { 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(TypeFunPtr tfp, int arg); + // Return register mask, based on signature (GPR vs FPR) + public abstract RegMask retMask(TypeFunPtr 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 ); - // Maximum stack slot (or 0) for the args in this TFP. + /** 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(TypeFunPtr tfp); - // 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 ); } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java index 642befa5..e1051fc6 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -51,29 +51,6 @@ public class RegAlloc { // - If color fails: // - - Split uncolorable LRGs - // 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 & printing, stack slots start again at slot#0 - but - // during RA they are actually biased by CPU.MAX_REG. - - // Stack Layout during Reg Alloc: - // - // Stack - // Num -- Caller ------ - // N+1 argN - // ... - // 1 arg0 - // -------- Callee ------- - // 0 RPC - // N+M PAD - // N+ Spills - // N+2 Spills - // Top-level program graph structure final CodeGen _code; @@ -160,11 +137,11 @@ public class RegAlloc { // No register yet, use LRG if( lrg._reg == -1 ) return "V"+lrg._lrg; // Chosen machine register unless stack-slot and past RA - int slot = _code._mach.stackSlot(lrg._reg); - if( slot == -1 || _code._phase.ordinal() <= CodeGen.Phase.RegAlloc.ordinal() || fun==null ) - return _code._mach.reg(lrg._reg); + 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.computeStackSlot(slot)*8+"]"; + return "[rsp+"+fun.computeStackOffset(_code,lrg._reg)+"]"; } // ----------------------- @@ -172,9 +149,24 @@ public class RegAlloc { public void regAlloc() { // Insert callee-save registers + String[] regs = _code._mach.regs(); + long neverSave= _code._mach.neverSave(); for( CFGNode bb : _code._cfg ) - if( bb instanceof FunNode fun ) - insertCalleeSave(fun); + if( bb instanceof FunNode fun ) { + ReturnNode ret = fun.ret(); + int len = Math.min(regs.length,64); + for( int reg=0; reg<len; reg++ ) + if( !_code._callerSave.test(reg) && ((1L<<reg)&neverSave)==0 ) { + ret.addDef(new CalleeSaveNode(fun,reg,regs[reg])); + assert ret.regmap(ret.nIns()-1).firstReg()==reg; + } + } + // Cache reg masks for New and Call + for( CFGNode bb : _code._cfg ) { + if( bb instanceof CallEndNode cend ) cend.cacheRegs(_code); + for( Node n : bb._outputs ) + if( n instanceof NewNode nnn ) nnn.cacheRegs(_code); + } // Top driver: repeated rounds of coloring and splitting. byte round=0; @@ -204,20 +196,6 @@ public class RegAlloc { IFG.color(round,this); // If colorable } - // Insert callee-save registers. Walk the callee-save RegMask ignoring any - // Parms, then insert a Parm and an edge from the Ret to the Parm with the - // callee-save register. - private void insertCalleeSave( FunNode fun ) { - RegMask saves = _code._mach.calleeSave(); - ReturnNode ret = fun.ret(); - - for( short reg = saves.firstReg(); reg != -1; reg = saves.nextReg(reg) ) { - ret.addDef(new CalleeSaveNode(fun,reg,_code._mach.reg(reg))); - assert ((MachNode)ret).regmap(ret.nIns()-1).firstReg()==reg; - } - } - - // ----------------------- // Split conflicted live ranges. void split(byte round) { @@ -565,22 +543,26 @@ public class RegAlloc { // ----------------------- // POST PASS: Remove empty spills that biased-coloring made private void postColor() { - int maxSlot = -1; + int maxReg = -1; for( CFGNode bb : _code._cfg ) { // For all ops - if( bb instanceof FunNode fun ) { - fun._maxArgSlot = _code._mach.maxArgSlot(fun.sig()); - maxSlot = -1; // Reset for new function - } - if( bb instanceof ReturnNode ret ) // Capture max seen - ret.fun()._maxSlot = (short)maxSlot; + if( bb instanceof FunNode fun ) + maxReg = -1; // Reset for new function + // Compute frame size, based on arguments and largest reg seen + if( bb instanceof ReturnNode ret ) + ret.fun().computeFrameAdjust(_code,maxReg); + // Raise frame size by max stack args passed, even if ignored + if( bb instanceof CallEndNode cend ) + maxReg = Math.max(maxReg,cend._xslot); + for( int j=0; j<bb.nOuts(); j++ ) { Node n = bb.out(j); - if( lrg(n)!=null ) { - int slot = _code._mach.stackSlot(lrg(n)._reg); - maxSlot = Math.max(maxSlot,slot+1); - } + if( lrg(n)!=null ) + maxReg = Math.max(maxReg,lrg(n)._reg+1); + // Raise frame size by max stack args passed to New + if( n instanceof NewNode nnn ) + maxReg = Math.max(maxReg,nnn._xslot); - if( !(n instanceof SplitNode lo) ) continue; + if( !(n instanceof SplitNode ) ) continue; int defreg = lrg(n )._reg; int usereg = lrg(n.in(1))._reg; // Attempt to bypass split diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java index 12ab222c..79de3fbf 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java @@ -110,13 +110,17 @@ public class RegMask { Machine mach = CodeGen.CODE._mach; if( _bits0==0 && _bits1==0 ) return sb.p("[]"); sb.p("["); + String[] regs = mach.regs(); for( int i=0; i<64; i++ ) if( ((_bits0 >> 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<regs.length ? regs[i] : "[stk#"+i+"]"; + } +} diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java index 6fa5344f..1da9a3b1 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java @@ -9,6 +9,11 @@ public class RegMaskRW extends RegMask { else _bits1 &= ~(1L<<(r-64)); return _bits0!=0 || _bits1!=0; } + // sets bit at position r. + public void set(int r) { + if( r < 64 ) _bits0 |= (1L<<(r )); + else _bits1 |= (1L<<(r-64)); + } @Override RegMaskRW and( RegMask mask ) { if( mask==null ) return this; _bits0 &= mask._bits0; diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/CallEndNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/CallEndNode.java index c98b2ff9..e1f64ca0 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/CallEndNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/CallEndNode.java @@ -1,7 +1,7 @@ package com.seaofnodes.simple.node; import com.seaofnodes.simple.*; -import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.type.*; import java.util.BitSet; @@ -97,4 +97,34 @@ public class CallEndNode extends CFGNode implements MultiNode { @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<xslot; i++ ) + kills.set((maxReg+fslot)+i); + _kills = kills; + } + public String op() { return "cend"; } + public RegMask regmap(int i) { return null; } + public RegMask outregmap() { return null; } + public RegMask outregmap(int idx) { return idx==2 ? _retMask : null; } + public RegMask killmap() { return _kills; } + public void encoding( Encoding enc ) { } + public void asm(CodeGen code, SB sb) { } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java index 0562b5b8..c477e125 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java @@ -1,10 +1,13 @@ package com.seaofnodes.simple.node; + import com.seaofnodes.simple.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; +import com.seaofnodes.simple.codegen.RegMask; import com.seaofnodes.simple.type.Type; import com.seaofnodes.simple.type.TypeFunPtr; import com.seaofnodes.simple.type.TypeTuple; - import java.util.BitSet; import static com.seaofnodes.simple.codegen.CodeGen.CODE; @@ -163,36 +166,51 @@ public class FunNode extends RegionNode { } - // | CALLER | - // | | - // | argN+1 | // slot 1, callER +32 - // | argN | // slot 0, callER +24 - // +--------+ // OLD FRAME - // | PAD | // Alignment pad +16 - // | callee | // slot 3, callEE + 8 - // | callee | // slot 2, callEE + 0 - // +--------+ // RSP - - // TODO: lower this field into a generic FunMachNode. - // Replace all FunXXX variants with FunMachNode. + // ------------ + // MachNode specifics, shared across all CPUs // Function may call other functions? X86 requires 16b aligned SP on // outbound calls, but not leaf. Set during an unrelated RA scan. public boolean _hasCalls; - // One more than largest spill slot, set during RA postcolor. - public short _maxSlot = -1; - // Max argument slot, set during early Encoding. - public short _maxArgSlot = -1; + // Maximum incoming argument slot, based on function type + public int _maxArgSlot; + // Frame adjust, including padding, set during early Encoding - public short _frameAdjust; + public int _frameAdjust; + public String op() { return _frameAdjust==0 ? "entry" : "subi"; } + public void postSelect(CodeGen code) { + code.link(this); + // Max slot seen past space reserved for incoming arguments + _maxArgSlot = code._mach.maxArgSlot(sig()); + } + public RegMask regmap(int i) { return null; } + public RegMask outregmap() { return null; } + public void encoding( Encoding enc ) { throw Utils.TODO(); } + public void asm(CodeGen code, SB sb) { + if( _frameAdjust!=0 ) + sb.p("rsp -= #").p(_frameAdjust); + } + + public void computeFrameAdjust(CodeGen code, int maxReg) { + // Max slot seen, or 0. + int maxSlot = Math.max(maxReg - code._mach.regs().length,_maxArgSlot); + // Frame adjust before rounding, can be negative for no spills + int size = maxSlot - _maxArgSlot; + _frameAdjust = size*8; + } - // Convert a stack slot, based on the frame, into an offset. Stack layout - // is the same for all CPUs; X86 will pre-spill the RPC. - public final int computeStackSlot(int slotN) { - return slotN < _maxArgSlot - ? slotN + _frameAdjust - : slotN - _maxArgSlot; + // Given a high register number and during encoding compute the stack + // pointer offset. Pre-RA we lack the fun._frameAdjust. We also need a + // FunNode. + public int computeStackOffset(CodeGen code, int reg) { + String[] regs = code._mach.regs(); + int stackSlot = reg - regs.length; + int slotRotate = stackSlot < _maxArgSlot + ? stackSlot + (_frameAdjust>>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<xslot; i++ ) + kills.set((maxReg+fslot)+i); + _kills = kills; + } + public String op() { return "alloc"; } + public RegMask regmap(int i) { return i==1 ? _arg3Mask : null; } + public RegMask outregmap() { return null; } + public RegMask outregmap(int idx) { return idx==1 ? _retMask : null; } + public RegMask killmap() { return _kills; } + 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/PhiNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java index 975492bf..2747b9d1 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java @@ -119,8 +119,8 @@ public class PhiNode extends Node { private boolean same_op() { for( int i=1; i<nIns(); i++ ) { Node op = in(i); - if( in(1).getClass() != op.getClass() || op.in(0)!=null ) - return false; // Wrong class or CFG bound + if( in(1).getClass() != op.getClass() || op.in(0)!=null || in(i).nIns() != op.nIns() ) + return false; // Wrong class or CFG bound or mismatched inputs if( op.nOuts() > 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<nIns(); i++ ) + sb.p(code.reg(in(i),fun())).p(" "); + // If we did not get the expected rpc, print which one we got + else if( code._regAlloc.regnum(rpc()) != code._mach.rpc() ) + sb.p("[").p(code.reg(rpc())).p("]"); + } + } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java index 0d9663c4..b3c2f6c2 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java @@ -1,24 +1,8 @@ package com.seaofnodes.simple.node.cpus.arm; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFunPtr; public class CallEndARM extends CallEndNode implements MachNode { - final TypeFunPtr _tfp; - CallEndARM( CallEndNode cend ) { - super(cend); - _tfp = (TypeFunPtr)(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/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java index 028688b7..32777f03 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java @@ -8,34 +8,15 @@ import com.seaofnodes.simple.node.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 computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); + } @Override public void encoding( Encoding enc ) { - // Size of local stack frame. - // Can be negative if the stack args passed are never referenced - int sz = Math.max(_maxSlot - _maxArgSlot,0); - sz = ((sz+1) & -2)+1; // Pad to 16 - _frameAdjust = (short)sz; + int sz = _frameAdjust; if( sz == 0 ) return; // Skip if no frame adjust - if( sz*-8 < -1L<<12 ) throw Utils.TODO(); - enc.add4(arm.imm_inst(arm.OPI_ADD, (_frameAdjust*-8)&0xFFF, arm.RSP, arm.RSP)); - } - - @Override public void asm(CodeGen code, SB sb) { - if( _frameAdjust != 0 ) - sb.p("rsp -= #").p(_frameAdjust*8); + if( sz >= 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<nIns(); i++ ) - sb.p(code.reg(in(i))).p(" "); - - // Post-allocation, if we did not get the expected return register, print what we got - else if( code._regAlloc.regnum(rpc()) != arm.X30 ) - sb.p("[").p(code.reg(rpc())).p("]"); - } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java index 50ba2274..ba39eead 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java @@ -25,7 +25,7 @@ public class SplitARM extends SplitNode { if(src >= 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<tfp.nargs(); i++ ) - if( tfp.arg(i) instanceof TypeFloat ) - fcnt++; - if( fcnt >= 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<<X19 | - 1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 | - 1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 | - 1L<<X28 | - 1L<<D9 | 1L<<D10 | 1L<<D11 | - 1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15; - - static final RegMask CALLER_SAVE_MASK; - static { - long caller = ~CALLEE_SAVE; - caller &= ~(1L<<RSP); - CALLER_SAVE_MASK = new RegMask(caller,1L<<(FLAGS-64)); - } - static RegMask armCallerSave() { return CALLER_SAVE_MASK; } - @Override public RegMask callerSave() { return armCallerSave(); } - - static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE); - static RegMask armCalleeSave() { return CALLEE_SAVE_MASK; } - @Override public RegMask calleeSave() { return armCalleeSave(); } - - - // Return single int/ptr register. Used by CallEnd output and Return input. - static final RegMask[] RET_MASKS; - static { - int nSaves = CALLEE_SAVE_MASK.size(); - RET_MASKS = new RegMask[4 + nSaves]; - RET_MASKS[0] = null; - RET_MASKS[1] = null; // Memory - RET_MASKS[2] = null; // Varies, either XMM0 or RAX - RET_MASKS[3] = RMASK; // Expected R30 but could be any register - short reg = CALLEE_SAVE_MASK.firstReg(); - for( int i=0; i<nSaves; i++ ) { - RET_MASKS[i+4] = new RegMask(reg); - reg = CALLEE_SAVE_MASK.nextReg(reg); + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; } + int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0); + return (short)nstk; } - static RegMask retMask( TypeFunPtr tfp, int i ) { - return i==2 - ? (tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK) - : RET_MASKS[i]; + + private static final long CALLEE_SAVE = + 1L<<X19 | + 1L<<X20 | 1L<<X21 | 1L<<X22 | 1L<<X23 | + 1L<<X24 | 1L<<X25 | 1L<<X26 | 1L<<X27 | + 1L<<X28 | + 1L<<D9 | 1L<<D10 | 1L<<D11 | + 1L<<D12 | 1L<<D13 | 1L<<D14 | 1L<<D15; + static final long CALLER_SAVE = ~CALLEE_SAVE & ~(1L<<RSP); + @Override public long callerSave() { return CALLER_SAVE; } + @Override public long neverSave() { return 1L<<RSP; } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? D0_MASK : X0_MASK; } + @Override public int rpc() { return X30; } // Create a split op; any register to any register, including stack slots @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitARM(kind,round); } @@ -607,11 +570,6 @@ public class arm extends Machine { return new UJmpARM(); } - // Break an infinite loop - @Override public NeverNode never(CFGNode ctrl ) { - throw Utils.TODO(); - } - // Instruction selection @Override public Node instSelect(Node n ) { return switch( n ) { diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java index 3dca37b8..7df68e77 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java @@ -1,23 +1,8 @@ package com.seaofnodes.simple.node.cpus.riscv; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFunPtr; public class CallEndRISC extends CallEndNode implements MachNode { - final TypeFunPtr _tfp; - CallEndRISC( CallEndNode cend ) { - super(cend); - _tfp = (TypeFunPtr)(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/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java index ce478761..5aff94d3 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/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/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java index 43a9439e..84f0f4c7 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java @@ -8,34 +8,15 @@ import com.seaofnodes.simple.node.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 computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8) & -16); + } @Override public void encoding( Encoding enc ) { - // Size of local stack frame. - // Can be negative if the stack args passed are never referenced - int sz = Math.max(_maxSlot - _maxArgSlot,0); - sz = ((sz+1) & -2)+1; // Pad to 16 - _frameAdjust = (short)sz; + int sz = _frameAdjust; if( sz == 0 ) return; // Skip if no frame adjust - if( sz*-8 < -1L<<12 ) throw Utils.TODO(); - enc.add4(riscv.i_type(riscv.OP_IMM, riscv.SP, 0, riscv.SP, (_frameAdjust*-8) & 0xFFF)); - } - - @Override public void asm(CodeGen code, SB sb) { - if( _frameAdjust != 0 ) - sb.p("rsp -= #").p(_frameAdjust*8); + 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/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<nIns(); i++ ) - sb.p(in(i)==null ? "---" : code.reg(in(i))).p(" "); - - // Post-allocation, if we did not get the expected return register, print what we got - else if( code._regAlloc.regnum(rpc()) != riscv.RPC ) - sb.p("[").p(code.reg(rpc())).p("]"); - } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java index bd50842c..569fa2e0 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java @@ -24,18 +24,18 @@ public class SplitRISC extends SplitNode { if( src >= 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<<F0; @@ -92,14 +94,14 @@ public class riscv extends Machine { }; static RegMask[] XMMS = new RegMask[] { - FA0_MASK, - FA1_MASK, - FA2_MASK, - FA3_MASK, - FA4_MASK, - FA5_MASK, - FA6_MASK, - FA7_MASK + FA0_MASK, + FA1_MASK, + FA2_MASK, + FA3_MASK, + FA4_MASK, + FA5_MASK, + FA6_MASK, + FA7_MASK }; // major opcode: OP @@ -241,6 +243,7 @@ public class riscv extends Machine { }; } + @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; @@ -258,89 +261,42 @@ public class riscv extends Machine { 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)); } @Override public short maxArgSlot( TypeFunPtr tfp ) { - // Count floats in signature up to index - int fcnt=0; - for( int i=0; i<tfp.nargs(); i++ ) - if( tfp.arg(i) instanceof TypeFloat ) - fcnt++; - if( fcnt >= 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<<S12) | (1L<<S13) | - (1L<<FS0) | (1L<<FS1) | (1L<<FS2 ) | (1L<<FS3 ) | - (1L<<FS4) | (1L<<FS5) | (1L<<FS6 ) | (1L<<FS7 ) | - (1L<<FS8) | (1L<<FS9) | (1L<<FS10) | (1L<<FS11); - - static final RegMask CALLER_SAVE_MASK; - static { - long caller = ~CALLEE_SAVE; - caller &= ~(1L<<SP); - CALLER_SAVE_MASK = new RegMask(caller,0); - } - static RegMask riscCallerSave() { return CALLER_SAVE_MASK; } - @Override public RegMask callerSave() { return riscCallerSave(); } - - static final RegMask CALLEE_SAVE_MASK = new RegMask(CALLEE_SAVE); - static RegMask riscCalleeSave() { return CALLEE_SAVE_MASK; } - @Override public RegMask calleeSave() { return riscCalleeSave(); } - - // Return single int/ptr register. Used by CallEnd output and Return input. - static final RegMask[] RET_MASKS; - static { - int nSaves = CALLEE_SAVE_MASK.size(); - RET_MASKS = new RegMask[4 + nSaves]; - RET_MASKS[0] = null; - RET_MASKS[1] = null; // Memory - RET_MASKS[2] = null; // Varies, either XMM0 or RAX - RET_MASKS[3] = RMASK; // Expected R1 but could be any register really - short reg = CALLEE_SAVE_MASK.firstReg(); - for( int i=0; i<nSaves; i++ ) { - RET_MASKS[i+4] = new RegMask(reg); - reg = CALLEE_SAVE_MASK.nextReg(reg); + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i<tfp.nargs(); i++ ) { + if( tfp.arg(i) instanceof TypeFloat ) fcnt++; + else icnt++; } - } - static RegMask retMask( TypeFunPtr tfp, int i ) { - return i==2 - ? (tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK) - : RET_MASKS[i]; + int nstk = Math.max(icnt-8,0)+Math.max(fcnt-8,0); + return (short)nstk; } - @Override public String reg( int reg ) { - return reg < REGS.length ? REGS[reg] : "[stk#"+(reg-REGS.length)+"]"; - } + static final long CALLER_SAVE = + (1L<<RPC) | + (1L<<A0 ) | (1L<<A1 ) | (1L<<A2 ) | (1L<<A3 ) | (1L<<A4 ) | (1L<<A5 ) | (1L<<A6 ) | (1L<<A7 ) | + (1L<<T0 ) | (1L<<T1 ) | (1L<<T2 ) | (1L<<T3 ) | (1L<<T4 ) | (1L<<T5 ) | (1L<<T6 ) | + (1L<<FA0) | (1L<<FA1) | (1L<<FA2) | (1L<<FA3) | (1L<<FA4) | (1L<<FA5) | (1L<<FA6) | (1L<<FA7) | + (1L<<F0 ) | (1L<<F1 ) | (1L<<F2 ) | (1L<<F3 ) | (1L<<F4 ) | (1L<<F5 ) | (1L<<F6 ) | (1L<<F7 ) | + (1L<<F8 ) | (1L<<F9 ) | (1L<<F10) | (1L<<F11) ; - // Stack slots, in units of 8 bytes. - @Override public int stackSlot( int reg ) { - return reg < REGS.length ? -1 : reg-REGS.length; + @Override public long callerSave() { return CALLER_SAVE; } + @Override public long neverSave() { return (1L<<RSP) | (1L<<ZERO); } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? FA0_MASK : A0_MASK; } - + @Override public int rpc() { return RPC; } // Return a MachNode unconditional branch - @Override public CFGNode jump() { - return new UJmpRISC(); - } + @Override public CFGNode jump() { return new UJmpRISC(); } // Create a split op; any register to any register, including stack slots @Override public SplitNode split(String kind, byte round, LRG lrg) { return new SplitRISC(kind,round); } - // Break an infinite loop - @Override public NeverNode never( CFGNode ctrl ) { return new NJmpRISC(ctrl);} - // True if signed 12-bit immediate public static boolean imm12(TypeInteger ti) { // 52 = 64-12 diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java index 2bb589a1..ce8b993f 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java @@ -1,23 +1,8 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; -import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.*; import com.seaofnodes.simple.node.CallEndNode; import com.seaofnodes.simple.node.MachNode; -import com.seaofnodes.simple.type.TypeFunPtr; public class CallEndX86 extends CallEndNode implements MachNode { - final TypeFunPtr _tfp; - CallEndX86( CallEndNode cend ) { - super(cend); - _tfp = (TypeFunPtr)(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/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java index f4e243bc..f6e67fb4 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/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<nargs(); i++ ) diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java index 9f6fc715..07418eb0 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java @@ -1,46 +1,34 @@ package com.seaofnodes.simple.node.cpus.x86_64_v2; import com.seaofnodes.simple.SB; -import com.seaofnodes.simple.codegen.*; +import com.seaofnodes.simple.codegen.CodeGen; +import com.seaofnodes.simple.codegen.Encoding; import com.seaofnodes.simple.node.FunNode; import com.seaofnodes.simple.node.MachNode; public class FunX86 extends FunNode implements MachNode { FunX86( FunNode fun ) { super(fun); } - @Override public String op() { return "subi"; } - @Override public RegMask regmap(int i) { return null; } - @Override public RegMask outregmap() { return null; } - @Override public void postSelect(CodeGen code) { code.link(this); } + @Override public void postSelect(CodeGen code) { + super.postSelect(code); + // One more for RPC pre-pushed; X86 special + _maxArgSlot++; + } + + @Override public void computeFrameAdjust(CodeGen code, int maxReg) { + super.computeFrameAdjust(code,maxReg); + // Alignment, but off by RPC + if( _hasCalls ) // If non-leaf, pad to 16b + _frameAdjust = ((_frameAdjust+8+8) & -16)-8; + } - // Actual stack layout is up to each CPU. - // X86, with too many args & spills: - // | CALLER | - // | argN | // slot 1, required by callER - // +--------+ // Old frame - // | RPC | // slot 0, required by callER - // +--------+ // New frame top - // | PAD16 | - // | callee | // slot 3, callEE - // | callee | // slot 2, callEE - // +--------+ @Override public void encoding( Encoding enc ) { - // Stack frame size: _maxSlot - max(arg). - // Can be negative if the stack args passed are never referenced - int sz = Math.max(_maxSlot - _maxArgSlot,0); - // If non-leaf function, pad to 16b - if( _hasCalls ) sz = ((sz+1) & -2)+1; - _frameAdjust = (short)sz; + int sz = _frameAdjust; if( sz == 0 ) return; // Skip if no frame adjust - // opcode: 0x83, addi rsp with immediate 8 - 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, 0b101, 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); } - @Override public void asm(CodeGen code, SB sb) { - if( _frameAdjust!=0 ) - sb.p("rsp -= #").p(_frameAdjust*8); - } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java index aee543ca..97586d87 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java @@ -66,7 +66,9 @@ 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 + 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 } 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<nIns(); i++ ) - sb.p(code.reg(in(i))).p(" "); - } } diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java index 35e58e85..f965157b 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java @@ -51,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 ? 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<<FLAGS)| // Flags are killed // Only XMM0-XMM5 are killed; XMM6-XMM15 are preserved (1L<<XMM0) | (1L<<XMM1) | (1L<<XMM2) | (1L<<XMM3) | - (1L<<XMM4) | (1L<<XMM5) | - // Shadow arg space is killed by caller: 1st 4 stack slots past RPC - (1L<<(MAX_REG+1)) | - (1L<<(MAX_REG+2)) | - (1L<<(MAX_REG+3)) | - (1L<<(MAX_REG+4)) ; - static final RegMask WIN64_CALLER_SAVE_MASK = new RegMask(WIN64_CALLER_SAVE); - - static RegMask x86CallerSave() { + (1L<<XMM4) | (1L<<XMM5) ; + + @Override public long callerSave() { return switch (CodeGen.CODE._callingConv) { - case "SystemV" -> 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<<MAX_REG)-1; - callee &= ~(1L<<FLAGS); - callee &= ~(1L<<RSP); - SYSTEM5_CALLEE_SAVE_MASK = new RegMask(callee); - - callee = ~WIN64_CALLER_SAVE; - // Remove the spills - callee &= (1L<<MAX_REG)-1; - callee &= ~(1L<<FLAGS); - callee &= ~(1L<<RSP); - WIN64_CALLEE_SAVE_MASK = new RegMask(callee); - } - static RegMask x86CalleeSave() { - return switch( CodeGen.CODE._callingConv ) { - case "SystemV" -> 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<nSaves; i++ ) { - masks[i+4] = new RegMask(reg); - reg = mask.nextReg(reg); - } - return masks; + @Override public long neverSave() { return 1L<<RSP; } + @Override public RegMask retMask( TypeFunPtr tfp ) { + return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK; } + @Override public int rpc() { return RPC; } - // Return single int/ptr register. Used by CallEnd output and Return input. - static RegMask retMask( TypeFunPtr tfp, int i ) { - if( i==2 ) - return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK; - RegMask[] masks = switch (CodeGen.CODE._callingConv) { - case "SystemV" -> 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) { |
