summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-03-21 15:12:05 -0700
committerCliff Click <cliffc@acm.org>2025-03-21 15:12:05 -0700
commitde2d08cfaa659d1c22f2cbbfa84748bd5c307ac4 (patch)
tree6e7e9dbcc5182deb0f47aad6b7080f3f75251069
parent442b5060a7f5857cfdedcc69384d90fd5fed9036 (diff)
downloadSimple-de2d08cfaa659d1c22f2cbbfa84748bd5c307ac4.tar.gz
Simple-de2d08cfaa659d1c22f2cbbfa84748bd5c307ac4.zip
Stack slot updates
Spill code work
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java38
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java28
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java22
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java34
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/MachNode.java3
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java6
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java25
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/RetRISC.java13
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java39
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java28
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/FunX86.java33
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/RetX86.java17
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java30
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java53
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java13
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java1
16 files changed, 334 insertions, 49 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
index e227eaff..67eeb3c2 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -231,12 +231,19 @@ public class Encoding {
// Write encoding bits in order into a big byte array.
// Record opcode start and length.
+ public FunNode _fun; // Currently encoding function
private void writeEncodings() {
_opStart= new int [_code.UID()];
_opLen = new byte[_code.UID()];
for( CFGNode bb : _code._cfg ) {
- if( !(bb instanceof MachNode) || bb instanceof FunNode )
+ if( !(bb instanceof MachNode mach0) )
_opStart[bb._nid] = _bits.size();
+ else if( bb instanceof FunNode fun ) {
+ _fun = fun; // Currently encoding function
+ _opStart[bb._nid] = _bits.size();
+ mach0.encoding( this );
+ _opLen[bb._nid] = (byte) (_bits.size() - _opStart[bb._nid]);
+ }
for( Node n : bb._outputs ) {
if( n instanceof MachNode mach && !(n instanceof FunNode) ) {
_opStart[n._nid] = _bits.size();
@@ -365,4 +372,33 @@ public class Encoding {
((RIPRelSize)src).patch(this, start, _opLen[src._nid], target - start);
}
}
+
+
+ // 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 |
+ // +--------+
+ private void frameSize() {
+ for( Node n : _code._start.outs() ) {
+ if( n instanceof FunNode fun ) {
+ throw Utils.TODO();
+ }
+ }
+ }
}
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 fbc3f6c2..295eab0a 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java
@@ -5,8 +5,6 @@ import com.seaofnodes.simple.node.*;
abstract public class Machine {
// Human readable machine name. Something like "x86-64" or "arm" or "risc5"
public abstract String name();
- // Human-readable name for a register number, e.g. "RAX" or "R0"
- public abstract String reg( int reg );
// Default opcode size for printing; 4 bytes for most 32-bit risc chips
public int defaultOpSize() { return 4; }
// Create a split op; any register to any register, including stack slots
@@ -21,4 +19,30 @@ abstract public class Machine {
public abstract IfNode never( CFGNode ctrl );
// Instruction select from ideal nodes
public abstract Node instSelect( Node n );
+
+ // Convert a register to a zero-based stack *slot*, or -1.
+ // Stack slots are assumed 8 bytes each.
+ // Actual stack layout is up to each CPU.
+ // X86, with too many args & spills:
+ // | CALLER |
+ // | argN | // slot 1, required by callER
+ // +--------+
+ // | RPC | // slot 0, required by callER
+ // | callee | // slot 3, callEE
+ // | callee | // slot 2, callEE
+ // | PAD16 |
+ // +--------+
+
+ // RISC/ARM, with too many args & spills:
+ // | CALLER |
+ // | argN | // slot 0, required by callER
+ // +--------+
+ // | callee | // slot 3, callEE: might be RPC
+ // | callee | // slot 2, callEE
+ // | callee | // slot 1, callEE
+ // | PAD16 |
+ // +--------+
+ public abstract int stackSlot( int reg );
+ // Human-readable name for a register number, e.g. "RAX" or "R0"
+ public abstract String reg( int reg );
}
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 4b3557cd..f323ea22 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -141,9 +141,15 @@ public class RegAlloc {
public void regAlloc() {
// Insert callee-save registers
- for( CFGNode bb : _code._cfg )
+ FunNode lastFun=null;
+ for( CFGNode bb : _code._cfg ) {
if( bb instanceof FunNode fun )
- insertCalleeSave(fun);
+ insertCalleeSave(lastFun=fun);
+ // Leaf routine, or not?
+ // X86 requires 16b RSP aligned if NOT leaf
+ if( bb instanceof CallNode ) lastFun._hasCalls = true;
+ }
+
// Top driver: repeated rounds of coloring and splitting.
byte round=0;
@@ -460,11 +466,21 @@ public class RegAlloc {
// -----------------------
// POST PASS: Remove empty spills that biased-coloring made
private void postColor() {
+ int maxSlot = -1;
for( CFGNode bb : _code._cfg ) { // For all ops
+ if( bb instanceof FunNode )
+ maxSlot = -1;
+ if( bb instanceof ReturnNode ret )
+ ret.fun()._maxSlot = (short)maxSlot;
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);
+ }
+
if( !(n instanceof SplitNode lo) ) continue;
- int defreg = lrg(n )._reg;
+ int defreg = lrg(n )._reg;
int usereg = lrg(n.in(1))._reg;
// Attempt to bypass split
if( defreg != usereg && splitBypass(bb,j,n,defreg) )
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 38aa83a0..8db30e7d 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/FunNode.java
@@ -161,4 +161,38 @@ public class FunNode extends RegionNode {
if( in ) body.set(n._nid);
return in;
}
+
+
+ // | 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.
+
+ // 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;
+
+ // Max observed spill slot, set during RA postcolor
+ public short _maxSlot = -1;
+ // Max argument slot, set during early Encoding.
+ public short _maxArgSlot = -1;
+ // Frame adjust, including padding, st during early Encoding
+ public short _frameAdjust;
+
+
+ // 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;
+ }
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/MachNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/MachNode.java
index 3fc6e031..5486b1c1 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/MachNode.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/MachNode.java
@@ -52,9 +52,6 @@ public interface MachNode {
// Just something like "R17=[R18+12] // Load array base".
// General form: "dst=src+src".
default void asm(CodeGen code, SB sb) { throw Utils.TODO(); }
- // Multi-op encodings, printing 1 op per line. Template-style,
- // no RA, no scheduling.
- default void asm(CodeGen code, SB sb, int multiN) { }
// Human readable opcode; e.g. "ld4" or "call".
default String op() { throw Utils.TODO(); }
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 92638562..b35f600a 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,7 +42,11 @@ public class arm extends Machine {
"flags"
};
@Override public String reg( int reg ) {
- return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
+ }
+ // Stack slots, in units of 8 bytes.
+ @Override public int stackSlot( int reg ) {
+ return reg < REGS.length ? -1 : reg-REGS.length;
}
// from (x0-x30)
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 6f1e4b6c..3bd2570f 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/FunRISC.java
@@ -1,13 +1,36 @@
package com.seaofnodes.simple.node.cpus.riscv;
import com.seaofnodes.simple.codegen.*;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.node.FunNode;
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 encoding( Encoding enc ) { }
@Override public void postSelect(CodeGen code) { code.link(this); }
+
+ // Actual stack layout is up to each CPU.
+ // X86, with too many args & spills:
+ // | CALLER |
+ // | argN | // slot 1, required by callER
+ // +--------+
+ // | RPC | // slot 0, required by callER
+ // | callee | // slot 3, callEE
+ // | callee | // slot 2, callEE
+ // | PAD16 |
+ // +--------+
+ @Override public void encoding( Encoding enc ) {
+ // Stack frame size: _maxSlot - max(arg), padded to 16.
+ _maxArgSlot = riscv.maxSlot(enc._fun.sig());
+ _frameAdjust = (short) (_maxSlot - _maxArgSlot);
+ if( _frameAdjust == 0 ) return; // Skip if no frame adjust
+ enc.add4(riscv.i_type(riscv.I_TYPE, riscv.SP, 0, riscv.SP, (_frameAdjust*8) & 0xFFF));
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p("rsp += #").p(_frameAdjust*8);
+ }
}
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 181e9ac0..1e501411 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
@@ -10,7 +10,9 @@ import com.seaofnodes.simple.node.MachNode;
public class RetRISC extends ReturnNode implements MachNode{
RetRISC(ReturnNode ret, FunNode fun) { super(ret, fun); fun.setRet(this); }
- @Override public String op() { return "ret"; }
+ @Override public String op() {
+ return ((FunRISC)fun())._frameAdjust > 0 ? "addi" : "ret";
+ }
// Correct Nodes outside the normal edges
@Override public void postSelect(CodeGen code) {
FunNode fun = (FunNode)rpc().in(0);
@@ -20,12 +22,17 @@ public class RetRISC extends ReturnNode implements MachNode{
@Override public RegMask regmap(int i) { return riscv.retMask(_fun.sig(),i); }
@Override public RegMask outregmap() { return null; }
@Override public void encoding( Encoding enc ) {
+ int frameAdjust = ((FunRISC)fun())._frameAdjust;
+ if( frameAdjust > 0 )
+ enc.add4(riscv.i_type(riscv.I_TYPE, riscv.SP, 0, riscv.SP, (frameAdjust*-8) & 0xFFF));
short rpc = enc.reg(rpc());
- int body = riscv.i_type(0x67, riscv.ZERO, 0, rpc, 0);
- enc.add4(body);
+ enc.add4(riscv.i_type(0x67, riscv.ZERO, 0, rpc, 0));
}
@Override public void asm(CodeGen code, SB sb) {
+ int frameAdjust = ((FunRISC)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
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 54a6db38..c6a31bdb 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/SplitRISC.java
@@ -1,5 +1,6 @@
package com.seaofnodes.simple.node.cpus.riscv;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.SplitNode;
@@ -9,9 +10,39 @@ public class SplitRISC extends SplitNode {
@Override public RegMask regmap(int i) { return riscv.SPLIT_MASK; }
@Override public RegMask outregmap() { return riscv.SPLIT_MASK; }
@Override public void encoding( Encoding enc ) {
- short dst = enc.reg(this );
- short src1 = enc.reg(in(1));
- int body = riscv.r_type(riscv.OP,dst,0,src1,riscv.ZERO,0);
- enc.add4(body);
+ short dst = enc.reg(this );
+ short src = enc.reg(in(1));
+
+ // Flag for being either XMM or stack
+ boolean dstX = dst >= riscv.F_OFFSET;
+ boolean srcX = src >= riscv.F_OFFSET;
+
+ if( dst >= riscv.MAX_REG ) {
+ // Store to SP
+ if( src >= riscv.MAX_REG )
+ throw Utils.TODO(); // Very rare stack-stack move
+ int off = enc._fun.computeStackSlot(dst - riscv.MAX_REG)*8;
+ throw Utils.TODO();
+ }
+ if( src >= riscv.MAX_REG ) {
+ // Load from SP
+ int off = enc._fun.computeStackSlot(src - riscv.MAX_REG)*8;
+ throw Utils.TODO();
+ }
+ // pick opcode based on regs
+ if( !dstX && !srcX ) {
+ // GPR->GPR
+ enc.add4(riscv.r_type(riscv.OP,dst,0,src,riscv.ZERO,0));
+ } else if( dstX && srcX ) {
+ // FPR->FPR
+ throw Utils.TODO();
+ } else if( dstX && !srcX ) {
+ //GPR->FPR
+ throw Utils.TODO();
+ } else if( !dstX && srcX ) {
+ //FPR->GPR
+ throw Utils.TODO();
+ }
+
}
}
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 dceef330..72ca0beb 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
@@ -28,9 +28,9 @@ public class riscv extends Machine {
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 final int MAX_REG = 61;
+ static final int MAX_REG = 64;
- static final int F_OFFSET = 31;
+ static final int F_OFFSET = 32;
static final String[] REGS = new String[] {
"zero","rpc" , "sp" , "gp" , "tp" , "t0" , "t1" , "t2" ,
@@ -245,6 +245,21 @@ public class riscv extends Machine {
throw Utils.TODO(); // Pass on stack slot
}
+ // Return the max stack slot used by this signature, or 0
+ static short maxSlot( 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 ) |
@@ -289,9 +304,16 @@ public class riscv extends Machine {
}
@Override public String reg( int reg ) {
- return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
+ }
+
+ // Stack slots, in units of 8 bytes.
+ @Override public int stackSlot( int reg ) {
+ return reg < REGS.length ? -1 : reg-REGS.length;
}
+
+
// Return a MachNode unconditional branch
@Override public CFGNode jump() {
return new UJmpRISC();
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 22cb8698..55b2b8af 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,13 +1,44 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
+import com.seaofnodes.simple.SB;
import com.seaofnodes.simple.codegen.*;
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 "addi"; }
@Override public RegMask regmap(int i) { return null; }
@Override public RegMask outregmap() { return null; }
- @Override public void encoding( Encoding enc ) { }
@Override public void postSelect(CodeGen code) { code.link(this); }
+
+ // Actual stack layout is up to each CPU.
+ // X86, with too many args & spills:
+ // | CALLER |
+ // | argN | // slot 1, required by callER
+ // +--------+
+ // | RPC | // slot 0, required by callER
+ // | callee | // slot 3, callEE
+ // | callee | // slot 2, callEE
+ // | PAD16 |
+ // +--------+
+ @Override public void encoding( Encoding enc ) {
+ // Stack frame size: _maxSlot - max(arg), padded to 16.
+ _maxArgSlot = x86_64_v2.maxSlot(enc._fun.sig());
+ int sz = _maxSlot - _maxArgSlot;
+ // If non-leaf function, pad to 16b
+ if( _hasCalls ) sz = ((sz+1) & -2)+1;
+ assert x86_64_v2.imm8(sz*8);
+ _frameAdjust = (short)sz;
+ if( sz == 0 ) return; // Skip if no frame adjust
+
+ // opcode: 0x83, addi rsp with immediate 8
+ enc.add1( 0x83 );
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) );
+ enc.add1(sz*8);
+ }
+
+ @Override public void asm(CodeGen code, SB sb) {
+ sb.p("rsp += #").p(_frameAdjust*8);
+ }
}
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 34458e8f..bc4d5805 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,7 +6,9 @@ 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 "ret"; }
+ @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);
@@ -15,9 +17,20 @@ public class RetX86 extends ReturnNode implements MachNode {
}
@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 ) { enc.add1(0xC3); }
+ @Override public void encoding( Encoding enc ) {
+ int frameAdjust = ((FunX86)fun())._frameAdjust;
+ if( frameAdjust > 0 ) {
+ enc.add1( 0x83 );
+ enc.add1( x86_64_v2.modrm(x86_64_v2.MOD.DIRECT, 0, x86_64_v2.RSP) );
+ enc.add1(frameAdjust*-8);
+ }
+ 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
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 21c20b81..15657de0 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
@@ -1,6 +1,7 @@
package com.seaofnodes.simple.node.cpus.x86_64_v2;
import com.seaofnodes.simple.SB;
+import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.codegen.*;
import com.seaofnodes.simple.node.Node;
import com.seaofnodes.simple.node.SplitNode;
@@ -13,7 +14,6 @@ public class SplitX86 extends SplitNode {
// Need to handle 8 cases: . reg->reg, reg->xmm, reg->flags, xmm->reg, xmm->xmm, xmm->flags, flags->reg, flags->xmm,
// flags->flags.
- // Currently not handling flags
// Encoding is appended into the byte array; size is returned
@Override public void encoding( Encoding enc ) {
// REX.W + 8B /r MOV r64, r/m64
@@ -39,10 +39,34 @@ public class SplitX86 extends SplitNode {
return;
}
+ // Flag for being either XMM or stack
boolean dstX = dst >= x86_64_v2.XMM_OFFSET;
boolean srcX = src >= x86_64_v2.XMM_OFFSET;
- if( dstX ) dst -= x86_64_v2.XMM_OFFSET;
- if( srcX ) src -= x86_64_v2.XMM_OFFSET;
+
+ // Stack spills
+ if( dst >= x86_64_v2.MAX_REG ) {
+ if( src >= x86_64_v2.MAX_REG )
+ throw Utils.TODO(); // Very rare stack-stack move
+ // TODO: Missing FP 0x66 prefix
+ if( srcX ) { src -= (short)x86_64_v2.XMM_OFFSET; enc.add1(0x66); }
+ int off = enc._fun.computeStackSlot(dst - x86_64_v2.MAX_REG)*8;
+ enc.add1(x86_64_v2.rex(src, x86_64_v2.RSP, -1));
+ enc.add1( 0x89 );
+ x86_64_v2.indirectAdr(0, (short)-1, (short)x86_64_v2.RSP, off, src, enc);
+ return;
+ }
+ if( src >= x86_64_v2.MAX_REG ) {
+ if( dstX ) { dst -= (short)x86_64_v2.XMM_OFFSET; enc.add1(0x66); }
+ int off = enc._fun.computeStackSlot(src - x86_64_v2.MAX_REG)*8;
+ enc.add1(x86_64_v2.rex(dst, x86_64_v2.RSP, -1));
+ enc.add1( 0x8B );
+ x86_64_v2.indirectAdr(0, (short)-1, (short)x86_64_v2.RSP, off, dst, enc);
+ return;
+ }
+
+ // reg-reg move. Adjust numbering for GPR vs FPR reg set
+ if( dstX ) dst -= (short) x86_64_v2.XMM_OFFSET;
+ if( srcX ) src -= (short) x86_64_v2.XMM_OFFSET;
// 0x66 if moving between register classes
if( dstX ^ srcX ) enc.add1(0x66);
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 dc3b86a8..9d7b1a26 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
@@ -13,8 +13,8 @@ public class x86_64_v2 extends Machine {
@Override public String name() { return "x86_64_v2"; }
@Override public int defaultOpSize() { return 5; }
- public static int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7;
- public static int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
+ public static final int RAX = 0, RCX = 1, RDX = 2, RBX = 3, RSP = 4, RBP = 5, RSI = 6, RDI = 7;
+ public static final int R08 = 8, R09 = 9, R10 = 10, R11 = 11, R12 = 12, R13 = 13, R14 = 14, R15 = 15;
public static final int XMM0 = 16, XMM1 = 17, XMM2 = 18, XMM3 = 19, XMM4 = 20, XMM5 = 21, XMM6 = 22, XMM7 = 23;
public static final int XMM8 = 24, XMM9 = 25, XMM10 = 26, XMM11 = 27, XMM12 = 28, XMM13 = 29, XMM14 = 30, XMM15 = 31;
@@ -118,7 +118,7 @@ public class x86_64_v2 extends Machine {
// don't need to use REX if 0x40.
public static byte rexF(int reg, int ptr, int idx, boolean wide, Encoding enc) {
int rex = rex(reg, ptr, idx, wide);
- if (rex == 0x40) return 0;
+ if (rex == REX) return 0;
enc.add1(rex);
return 1;
}
@@ -172,12 +172,18 @@ public class x86_64_v2 extends Machine {
"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", "[rsp-4]",
+ "flags",
};
@Override public String reg( int reg ) {
- return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*4+"]";
+ return reg < REGS.length ? REGS[reg] : "[rsp+"+(reg-REGS.length)*8+"]";
}
+ // Stack slots, in units of 8 bytes.
+ @Override public int stackSlot( int reg ) {
+ return reg < REGS.length ? -1 : reg-REGS.length;
+ }
+
+
// WIN64(param passing)
static RegMask[] CALLINMASK_WIN64 = new RegMask[] {
RCX_MASK,
@@ -227,6 +233,25 @@ public class x86_64_v2 extends Machine {
throw Utils.TODO(); // Pass on stack slot
}
+ // Return the max stack slot used by this signature, or 0
+ static short maxSlot( 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 >= XMMS4.length )
+ throw Utils.TODO();
+ RegMask[] cargs = switch( CodeGen.CODE._callingConv ) {
+ case "SystemV" -> CALLINMASK_SYSTEMV;
+ case "Win64" -> CALLINMASK_WIN64;
+ default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv);
+ };
+ if( tfp.nargs()-fcnt >= cargs.length )
+ throw Utils.TODO();
+ return 0; // No stack args
+ }
+
// Return single int/ptr register. Used by CallEnd output and Return input.
static RegMask retMask( TypeFunPtr tfp ) {
return tfp.ret() instanceof TypeFloat ? XMM0_MASK : RAX_MASK;
@@ -236,9 +261,9 @@ public class x86_64_v2 extends Machine {
// caller saved(systemv)
static final long SYSTEM5_CALLER_SAVE =
(1L<< RAX) | (1L<< RCX) | (1L<< RDX) |
- (1L << RDI) | (1L << RSI) |
- (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11) |
- (1L << FLAGS) | // Flags are killed
+ (1L << RDI) | (1L << RSI) |
+ (1L << R08) | (1L << R09) | (1L << R10) | (1L << R11) |
+ (1L << FLAGS) | // Flags are killed
// All FP regs are killed
FP_BITS;
static final RegMask SYSTEM5_CALLER_SAVE_MASK = new RegMask(SYSTEM5_CALLER_SAVE);
@@ -255,16 +280,13 @@ public class x86_64_v2 extends Machine {
static RegMask x86CallerSave() {
return switch (CodeGen.CODE._callingConv) {
- case "SystemV" -> SYSTEM5_CALLER_SAVE_MASK;
- case "Win64" -> WIN64_CALLER_SAVE_MASK;
- default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv);
+ case "SystemV" -> SYSTEM5_CALLER_SAVE_MASK;
+ case "Win64" -> WIN64_CALLER_SAVE_MASK;
+ default -> throw new IllegalArgumentException("Unknown calling convention: " + CodeGen.CODE._callingConv);
};
}
- @Override
- public RegMask callerSave() {
- return x86CallerSave();
- }
+ @Override public RegMask callerSave() { return x86CallerSave(); }
static final RegMask SYSTEM5_CALLEE_SAVE_MASK;
static final RegMask WIN64_CALLEE_SAVE_MASK;
@@ -293,7 +315,6 @@ public class x86_64_v2 extends Machine {
}
@Override public RegMask calleeSave() { return x86CalleeSave(); }
-
static final RegMask[] WIN64_RET_MASKS, SYS5_RET_MASKS;
static {
WIN64_RET_MASKS = makeRetMasks( WIN64_CALLEE_SAVE_MASK);
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java b/chapter21/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
index ab997ab9..efa94b8b 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/print/ASMPrinter.java
@@ -56,7 +56,8 @@ public abstract class ASMPrinter {
fun.sig().print(sb);
sb.p("---------------------------").nl();
-
+ if( fun._frameAdjust != 0 )
+ iadr = doInst(iadr,sb,code,cfgidx,fun,true,true);
while( !(code._cfg.at(cfgidx) instanceof ReturnNode) )
iadr = doBlock(iadr,sb,code,fun,cfgidx++);
@@ -113,7 +114,8 @@ public abstract class ASMPrinter {
if( postAlloc && n instanceof CalleeSaveNode ) return iadr;
if( postEncode && n instanceof ProjNode ) return iadr;
if( n instanceof MemMergeNode ) return iadr;
- final int encWidth = code._mach.defaultOpSize()*2;
+ final int dopz = code._mach.defaultOpSize();
+ final int encWidth = dopz*2;
// All blocks ending in a Region will need to either fall into or jump
// to this block. Until the post-reg-alloc block layout cleanup, we
@@ -192,13 +194,14 @@ public abstract class ASMPrinter {
// Multiple ops, template style, no RA, no scheduling. Print out
// one-line-per-newline, with encoding bits up front.
int size = code._encoding._opLen[n._nid];
- int off = code._mach.defaultOpSize();
+ int off = Math.min(size,dopz);
while( isMultiOp!=null ) {
sb.hex2(iadr).p(" ");
- int len = Math.min(off,size);
+ int len = Math.min(size-off,dopz);
for( int i=0; i<len; i++ )
sb.hex1(code._encoding._bits.buf()[iadr++]);
- off += code._mach.defaultOpSize();
+ off += len;
+ for( int i=size-off; i<dopz; i++ ) sb.p(" ");
sb.p(" ");
int x = isMultiOp.indexOf('\n');
if( x== -1 ) { // Last line
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index 431d0627..06988ee8 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -23,7 +23,6 @@ public class Chapter21Test {
static void testCPU( String src, String cpu, String os, int spills, String stop ) {
CodeGen code = new CodeGen(src);
code.parse().opto().typeCheck().instSelect(PORTS,cpu,os).GCM().localSched().regAlloc().encode();
- code.asm();
int delta = spills>>3;
if( delta==0 ) delta = 1;
assertEquals("Expect spills:",spills,code._regAlloc._spillScaled,delta);