summaryrefslogtreecommitdiff
path: root/chapter21/src/main/java
diff options
context:
space:
mode:
Diffstat (limited to 'chapter21/src/main/java')
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java34
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java105
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java92
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java10
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMaskRW.java5
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/CallEndNode.java32
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java66
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/LoopNode.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java38
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/PhiNode.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/ReturnNode.java38
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallEndARM.java18
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/FunARM.java35
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/NewARM.java10
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/RetARM.java28
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/SplitARM.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java92
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallEndRISC.java17
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java35
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NJmpRISC.java46
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/NewRISC.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java39
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java120
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallEndX86.java17
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java1
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java48
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/MemOpX86.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/NewX86.java17
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java31
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java10
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java122
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) {