summaryrefslogtreecommitdiff
path: root/chapter21
diff options
context:
space:
mode:
authorCliff Click <cliffc@acm.org>2025-04-29 13:27:32 -0700
committerCliff Click <cliffc@acm.org>2025-04-29 13:27:32 -0700
commit7e1deb4cbd52c7ebbec78e2ceeb35875e83c6e3a (patch)
treec159f8dd2522f6be8e582b8abca0f491d94559d3 /chapter21
parentfe52d80f4a25b2df1954b42395123de652b953df (diff)
downloadSimple-7e1deb4cbd52c7ebbec78e2ceeb35875e83c6e3a.tar.gz
Simple-7e1deb4cbd52c7ebbec78e2ceeb35875e83c6e3a.zip
Minor bugs
When passing AND receiving arguments on the stack (win64 passes only 4 args in registers, and requires backing stack store), the RA math was off. Bug triggered need for a stack-stack spill, now done. Inlining also hides the bug, since there are no large-argument calls afterwards. x86 store-immediate encoded an 8byte imm but the op only takes a 4byte imm. Making a 1-reg regmask with reg>64 math wrong. "popular constant" RA split would trigger when spanning calls; visit pattern for multi-users would then skip a user. Encoding would pad fun headers to 16bytes, then expand x86 short jumps to long jumps, messing up the padding. Call/End/Node visiting in RA would double-visit trailing projections. GCM not treating Calls as memory kills.
Diffstat (limited to 'chapter21')
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java23
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java6
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/StoreNode.java8
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/arm.java4
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRISC.java10
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/riscv.java5
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallX86.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/SplitX86.java11
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/StoreX86.java2
-rw-r--r--chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/x86_64_v2.java18
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java9
-rw-r--r--chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp71
25 files changed, 146 insertions, 69 deletions
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
index a2bb21ed..e9b5eb2e 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/BuildLRG.java
@@ -63,7 +63,7 @@ abstract public class BuildLRG {
}
// MultiNodes have projections which set registers
- if( n instanceof MultiNode )
+ if( n instanceof MultiNode && !(n instanceof CFGNode) )
for( Node proj : n.outs() )
if( proj instanceof MachNode )
defLRG(alloc,proj);
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 d7057839..c35ea6a4 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Encoding.java
@@ -324,7 +324,6 @@ public class Encoding {
if( !(bb instanceof MachNode mach0) )
_opStart[bb._nid] = _bits.size();
else if( bb instanceof FunNode fun ) {
- padN(16,_bits);
_fun = fun; // Currently encoding function
_opStart[bb._nid] = _bits.size();
mach0.encoding( this );
@@ -338,7 +337,6 @@ public class Encoding {
}
}
}
- padN(16,_bits);
}
// --------------------------------------------------
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java b/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
index 289d1786..40f827ec 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/GlobalCodeMotion.java
@@ -167,6 +167,8 @@ public abstract class GlobalCodeMotion {
!(memuse instanceof NewNode) &&
// Load-use directly defines memory
(memuse._type instanceof TypeMem ||
+ // Load-use directly defines memory
+ memuse instanceof CallNode ||
// Load-use indirectly defines memory
(memuse._type instanceof TypeTuple tt && tt._types[ld._alias] instanceof TypeMem)) )
continue outer;
@@ -277,9 +279,9 @@ public abstract class GlobalCodeMotion {
lca = anti_dep( load, late[mem._nid], mem.cfg0(), lca, st );
}
break; // Loads do not cause anti-deps on other loads
- case CallNode st:
- assert late[st._nid]!=null;
- lca = anti_dep(load,late[st._nid],st.cfg0(),lca,st);
+ case CallNode call:
+ assert late[call._nid]!=null;
+ lca = anti_dep(load,late[call._nid],call.cfg0(),lca,call);
break;
case PhiNode phi:
// Repeat anti-dep for matching Phi inputs.
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 81955779..fdd4e2f9 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/IFG.java
@@ -242,14 +242,12 @@ abstract public class IFG {
private static void mergeLiveOut( RegAlloc alloc, CFGNode priorbb, int i ) {
CFGNode bb = priorbb.cfg(i);
if( bb == null ) return; // Start has no prior
- if( !bb.blockHead() ) bb = bb.cfg0();
- //if( i==0 && !(bb instanceof StartNode) ) bb = bb.cfg0();
- assert bb.blockHead();
+ while( !bb.blockHead() ) bb = bb.cfg0();
// Lazy get live-out set for bb
- IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() );
+ IdentityHashMap<LRG, Node> lrgs = BBOUTS.computeIfAbsent( bb, k -> new IdentityHashMap<>() );
- for( LRG lrg : TMP.keySet() ) {
+ for( LRG lrg : TMP.keySet() ) {
Node def = TMP.get(lrg);
// Effective def comes from phi input from prior block
if( def instanceof PhiNode phi && phi.cfg0()==priorbb ) {
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 1c12bfcb..2766414f 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java
@@ -16,8 +16,12 @@ abstract public class Machine {
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);
+ // Call Argument Mask. Passed in the function signature and argument
+ // number (2-based; 0 is for control and 1 for memory). Also passed in a 0
+ // for the function itself, or for *outgoing* calls, the maximum stack slot
+ // given to the incoming function arguments (stack slots reserved for
+ // incoming arguments).
+ public abstract RegMask callArgMask(TypeFunPtr tfp, int arg, int maxArgSlot);
// Return register mask, based on signature (GPR vs FPR)
public abstract RegMask retMask(TypeFunPtr tfp);
// Return PC register
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 49f057a3..3a99c097 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java
@@ -262,7 +262,7 @@ public class RegAlloc {
}
// Single-def live range with an empty mask. There are many single-reg
- // uses. Theory is there's many repeats if the same reg amongst the uses.
+ // uses. Theory is there's many repeats of the same reg amongst the uses.
// In of splitting once per use, start by splitting into groups based on
// required input register.
boolean splitEmptyMaskByUse( byte round, LRG lrg ) {
@@ -271,26 +271,32 @@ public class RegAlloc {
// Look at each use, and break into non-overlapping register classes.
Ary<RegMask> rclass = new Ary<>(RegMask.class);
boolean done=false;
+ int ncalls=0;
while( !done ) {
done = true;
for( Node use : def._outputs )
- if( use instanceof MachNode mach )
+ if( use instanceof MachNode mach ) {
+ if( mach instanceof CallNode ) ncalls++;
for( int i=1; i<use.nIns(); i++ )
if( use.in(i)==def )
done = putIntoRegClass( rclass, mach.regmap(i) );
+ }
}
- // See how many register classes we split into
- if( rclass._len <= 1 ) return false;
+ // See how many register classes we split into. Generally not
+ // productive to split like this across calls, which are going to kill
+ // all registers anyways.
+ if( rclass._len <= 1 || ncalls > 1 ) return false;
- // Split by class
+ // Split by classh
+ Ary<Node> ns = new Ary<>(Node.class);
for( RegMask rmask : rclass ) {
+ ns.addAll(def._outputs);
Node split = makeSplit(def,"popular",round,lrg);
split.insertAfter( def );
if( split.nIns()>1 ) split.setDef(1,def);
// all uses by class to split
- for( int j=0; j < def._outputs._len; j++ ) {
- Node use = def._outputs.at(j);
+ for( Node use : ns ) {
if( use instanceof MachNode mach && use!=split ) {
// Check all use inputs for n, in case there's several
for( int i = 1; i < use.nIns(); i++ )
@@ -299,10 +305,11 @@ public class RegAlloc {
RegMask m = mach.regmap( i );
if( m!=null && mach.regmap( i ).overlap( rmask ) )
// Modify use to use the split version specialized to this rclass
- { use.setDefOrdered( i, split ); j--; break; }
+ use.setDefOrdered( i, split );
}
}
}
+ ns.clear();
}
return true;
}
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 79de3fbf..ec0c4e30 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegMask.java
@@ -4,11 +4,11 @@ import com.seaofnodes.simple.Utils;
import com.seaofnodes.simple.SB;
/** RegMask
- * A "register mask" - 1 bit set for each allowed register. In addition
+ * A "register mask" - 1 bit set for each allowed register. In addition,
* "stack slot" registers may be allowed, effectively making the set infinite.
* <p>
* For smaller and simpler machines it suffices to make such masks an i64 or
- * i128 (64 or 128 bit integers), and this presentation is by far the better
+ * i128 (64- or 128-bit integers), and this presentation is by far the better
* way to go... if all register allocations can fit in this bit limitation.
* The allocator will need bits for stack-based parameters and for splits
* which cannot get a register. For a 32-register machine like the X86, add 1
@@ -28,7 +28,7 @@ public class RegMask {
public RegMask(int bit) {
if( bit < 64 ) _bits0 = 1L<<bit;
- else _bits1 = 1L<<(bit=64);
+ else _bits1 = 1L<<(bit-64);
}
public RegMask(long bits ) { _bits0 = bits; }
public RegMask(long bits0, long bits1 ) { _bits0 = bits0; _bits1 = bits1; }
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 3d4bad5e..5a8c1b6b 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/NewNode.java
@@ -90,8 +90,8 @@ public class NewNode extends Node implements MultiNode {
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);
+ _arg2Reg = code._mach.callArgMask(TypeFunPtr.CALLOC,2,0).firstReg();
+ _arg3Mask = code._mach.callArgMask(TypeFunPtr.CALLOC,3,0);
// 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
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/StoreNode.java b/chapter21/src/main/java/com/seaofnodes/simple/node/StoreNode.java
index b9f99f4e..a6992f04 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/StoreNode.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/StoreNode.java
@@ -84,6 +84,12 @@ public class StoreNode extends MemOpNode {
}
}
+ // Store of zero after alloc
+ if( mem() instanceof ProjNode prj && prj.in(0) instanceof NewNode &&
+ prj.in(0)==ptr().in(0) && // Same NewNode memory & pointer
+ (val()._type==TypeInteger.ZERO || val()._type==Type.NIL ) )
+ return mem();
+
return null;
}
@@ -105,7 +111,7 @@ public class StoreNode extends MemOpNode {
TypeMemPtr tmp = (TypeMemPtr)ptr()._type;
if( tmp._obj.field(_name)._final && !_init )
return Parser.error("Cannot modify final field '"+_name+"'",_loc);
- Type t = val()._type;
+ //Type t = val()._type;
//return _init || t.isa(_declaredType) ? null : Parser.error("Cannot store "+t+" into field "+_declaredType+" "+_name,_loc);
return null;
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
index 63d418ff..7f6fe93c 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallARM.java
@@ -21,7 +21,7 @@ public class CallARM extends CallNode implements MachNode, RIPRelSize {
@Override public String label() { return op(); }
@Override public String name() { return _name; }
@Override public TypeFunPtr tfp() { return _tfp; }
- @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i); }
+ @Override public RegMask regmap(int i) { return arm.callInMask(_tfp,i,fun()._maxArgSlot); }
@Override public RegMask outregmap() { return null; }
@Override public void encoding( Encoding enc ) {
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
index b3f55659..87dde38c 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/CallRRARM.java
@@ -11,7 +11,7 @@ public class CallRRARM extends CallNode implements MachNode {
@Override public RegMask regmap(int i) {
return i==_inputs._len
? arm.RMASK // Function call target
- : arm.callInMask(tfp(),i); // Normal argument
+ : arm.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
}
@Override public RegMask outregmap() { return null; }
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
index 4d0f95ab..b419fe0a 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/arm/ParmARM.java
@@ -9,7 +9,7 @@ public class ParmARM extends ParmNode implements MachNode {
final RegMask _rmask;
ParmARM(ParmNode parm) {
super(parm);
- _rmask = arm.callInMask(fun().sig(),_idx);
+ _rmask = arm.callInMask(fun().sig(),_idx,0);
}
@Override public RegMask regmap(int i) { return null; }
@Override public RegMask outregmap() { return _rmask; }
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 5056bc74..260025d1 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
@@ -563,8 +563,8 @@ 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 ) {
+ @Override public RegMask callArgMask(TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask(TypeFunPtr tfp, int idx, int maxArgSlot ) {
if( idx==0 ) return CodeGen.CODE._rpcMask;
if( idx==1 ) return null;
// Count floats in signature up to index
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 5aff94d3..ae9cdcd9 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,9 +19,9 @@ 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) {
- return riscv.callInMask(_tfp,i);
+ return riscv.callInMask(_tfp,i,fun()._maxArgSlot);
}
- @Override public RegMask outregmap() { return riscv.RPC_MASK; }
+ @Override public RegMask outregmap() { return null; }
@Override public String name() { return _name; }
@Override public TypeFunPtr tfp() { return _tfp; }
@@ -29,8 +29,7 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
// Short form +/-4K: beq r0,r0,imm12
// Long form: auipc rX,imm20/32; jal r0,[rX+imm12/32]
enc.relo(this);
- short rpc = enc.reg(this);
- enc.add4(riscv.j_type(riscv.OP_JAL, rpc, 0));
+ enc.add4(riscv.j_type(riscv.OP_JAL, riscv.RPC, 0));
}
// Delta is from opcode start
@@ -42,9 +41,8 @@ public class CallRISC extends CallNode implements MachNode, RIPRelSize {
// Delta is from opcode start
@Override public void patch( Encoding enc, int opStart, int opLen, int delta ) {
- short rpc = enc.reg(this);
if( opLen==4 ) {
- enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, rpc, delta));
+ enc.patch4(opStart,riscv.j_type(riscv.OP_JAL, riscv.RPC, delta));
} else {
throw Utils.TODO();
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
index 2afbbfa9..dcc53cbf 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/CallRRISC.java
@@ -11,7 +11,7 @@ public class CallRRISC extends CallNode implements MachNode {
@Override public RegMask regmap(int i) {
return i==_inputs._len-1
? riscv.RMASK // Function call target
- : riscv.callInMask(tfp(),i); // Normal argument
+ : riscv.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
}
@Override public RegMask outregmap() { return null; }
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
index 02d53560..0f8db4e5 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/riscv/ParmRISC.java
@@ -9,7 +9,7 @@ public class ParmRISC extends ParmNode implements MachNode {
final RegMask _rmask;
ParmRISC(ParmNode parm) {
super(parm);
- _rmask = riscv.callInMask(fun().sig(),_idx);
+ _rmask = riscv.callInMask(fun().sig(),_idx,0);
}
@Override public RegMask regmap(int i) { return null; }
@Override public RegMask outregmap() { return _rmask; }
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 47d1db75..54c6b828 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
@@ -253,8 +253,8 @@ public class riscv extends Machine {
};
}
- @Override public RegMask callArgMask( TypeFunPtr tfp, int idx ) { return callInMask(tfp,idx); }
- static RegMask callInMask( TypeFunPtr tfp, int idx ) {
+ @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
if( idx==0 ) return RPC_MASK;
if( idx==1 ) return null;
// Count floats in signature up to index
@@ -272,6 +272,7 @@ public class riscv extends Machine {
return cargs[idx-2-fcnt];
}
// Pass on stack slot(8 and higher)
+ if( maxArgSlot>0 ) throw Utils.TODO();
return new RegMask(MAX_REG + 1 + (idx - 2));
}
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
index b9d4fa8d..1a74f817 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/CallRX86.java
@@ -12,7 +12,7 @@ public class CallRX86 extends CallNode implements MachNode {
@Override public RegMask regmap(int i) {
return i==_inputs._len
? x86_64_v2.WMASK // Function call target
- : x86_64_v2.callInMask(tfp(),i); // Normal argument
+ : x86_64_v2.callInMask(tfp(),i,fun()._maxArgSlot); // Normal argument
}
@Override public RegMask outregmap() { return null; }
@Override public void encoding( Encoding enc ) {
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 f6e67fb4..c97037e2 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
@@ -20,7 +20,7 @@ public class CallX86 extends CallNode implements MachNode, RIPRelSize {
@Override public String label() { return op(); }
@Override public String name() { return _name; }
@Override public TypeFunPtr tfp() { return _tfp; }
- @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i); }
+ @Override public RegMask regmap(int i) { return x86_64_v2.callInMask(_tfp,i,fun()._maxArgSlot); }
@Override public RegMask outregmap() { return null; }
@Override public int nargs() { return nIns()-2; } // Minus control, memory, fptr
diff --git a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
index 7a6dbb13..5cfdd804 100644
--- a/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
+++ b/chapter21/src/main/java/com/seaofnodes/simple/node/cpus/x86_64_v2/ParmX86.java
@@ -9,7 +9,7 @@ public class ParmX86 extends ParmNode implements MachNode {
final RegMask _rmask;
ParmX86( ParmNode parm ) {
super(parm);
- _rmask = x86_64_v2.callInMask(fun().sig(),_idx);
+ _rmask = x86_64_v2.callInMask(fun().sig(),_idx,1/*RPC*/);
}
@Override public RegMask regmap(int i) { return null; }
@Override public RegMask outregmap() { return _rmask; }
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 f965157b..960ea6f8 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
@@ -49,9 +49,16 @@ public class SplitX86 extends SplitNode {
// Stack spills
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.computeStackOffset(enc._code,dst);
+ if( src >= x86_64_v2.MAX_REG ) {
+ // Rare stack-stack move. push [RSP+soff]; pop [RSP+doff]
+ int soff = enc._fun.computeStackOffset(enc._code,src);
+ enc.add1(0xFF);
+ x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, soff, 6, enc);
+ enc.add1(0x8F);
+ x86_64_v2.indirectAdr(0, (short)-1/*index*/, (short)x86_64_v2.RSP, off, 0, enc);
+ return;
+ }
StoreX86.encVal(enc, srcX ? TypeFloat.F64 : TypeInteger.BOT, (short)x86_64_v2.RSP, (short)-1/*index*/, src, 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 c906c3f2..76fb4e8d 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
@@ -44,7 +44,7 @@ public class StoreX86 extends MemOpX86 {
case 0: enc.add1(_imm); break;
case 1: enc.add2(_imm); break;
case 2: enc.add4(_imm); break;
- case 3: enc.add8(_imm); break;
+ case 3: enc.add4(_imm); break; // Limit of a 4 byte immediate
}
} else {
encVal(enc,_declaredType,ptr,idx,src,_off,_scale);
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 23c12ae7..2b7fddb1 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
@@ -185,13 +185,13 @@ 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 ) {
+ @Override public RegMask callArgMask( TypeFunPtr tfp, int idx, int maxArgSlot ) { return callInMask(tfp,idx,maxArgSlot); }
+ static RegMask callInMask( TypeFunPtr tfp, int idx, int maxArgSlot ) {
if( idx==0 ) return RPC_MASK;
if( idx==1 ) return null;
return switch( CodeGen.CODE._callingConv ) {
- case "SystemV" -> callSys5 (tfp,idx);
- case "Win64" -> callWin64(tfp,idx);
+ case "SystemV" -> callSys5 (tfp,idx,maxArgSlot);
+ case "Win64" -> callWin64(tfp,idx,maxArgSlot);
default -> throw Utils.TODO();
};
}
@@ -230,11 +230,11 @@ public class x86_64_v2 extends Machine {
R09_MASK,
};
- static RegMask callWin64(TypeFunPtr tfp, int idx ) {
+ static RegMask callWin64(TypeFunPtr tfp, int idx, int maxArgSlot ) {
// idx 2,3,4,5 passed in registers, with stack slot mirrors.
// idx >= 6 passed on stack, starting at slot#1 (#0 reserved for RPC).
if( idx >= 6 )
- return new RegMask(MAX_REG+1/*RPC*/+(idx-2));
+ return new RegMask(MAX_REG+maxArgSlot+(idx-2));
return tfp.arg(idx-2) instanceof TypeFloat
? XMMS8 [idx-2]
: WIN64_CALL[idx-2];
@@ -271,7 +271,7 @@ public class x86_64_v2 extends Machine {
R09_MASK,
};
- static RegMask callSys5(TypeFunPtr tfp, int idx ) {
+ static RegMask callSys5(TypeFunPtr tfp, int idx, int maxArgSlot ) {
// First 6 integers passed in registers: rdi,rsi,rdx,rcx,r08,r09
// First 8 floats passed in registers: xmm0-xmm7
int icnt=0, fcnt=0; // Count of ints, floats
@@ -281,8 +281,8 @@ public class x86_64_v2 extends Machine {
}
int nstk = Math.max(icnt-6,0)+Math.max(fcnt-8,0);
return tfp.arg(idx-2) instanceof TypeFloat
- ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+1/*RPC*/+nstk)
- : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+1/*RPC*/+nstk);
+ ? fcnt<8 ? XMMS8 [fcnt] : new RegMask(MAX_REG+maxArgSlot+nstk)
+ : icnt<6 ? SYS5_CALL[icnt] : new RegMask(MAX_REG+maxArgSlot+nstk);
}
static short maxArgSlotSys5(TypeFunPtr tfp) {
int icnt=0, fcnt=0; // Count of ints, floats
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
index ea23728c..c626c14e 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
+++ b/chapter21/src/test/java/com/seaofnodes/simple/Chapter21Test.java
@@ -15,9 +15,10 @@ public class Chapter21Test {
@Test
public void testJig() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/jig.smp"));
- testCPU(src,"x86_64_v2", "Win64" ,-1,null);
- testCPU(src,"riscv" , "SystemV",-1,null);
- testCPU(src,"arm" , "SystemV",-1,null);
+ CodeGen code = new CodeGen(src).driver("x86_64_v2","Win64","build/objs/jigS.o");
+ //testCPU(src,"x86_64_v2", "Win64" ,-1,null);
+ //testCPU(src,"riscv" , "SystemV",-1,null);
+ //testCPU(src,"arm" , "SystemV",-1,null);
}
static void testCPU( String src, String cpu, String os, int spills, String stop ) {
@@ -64,7 +65,7 @@ public class Chapter21Test {
@Test
public void testString() throws IOException {
String src = Files.readString(Path.of("src/test/java/com/seaofnodes/simple/progs/stringHash.smp"));
- testCPU(src,"x86_64_v2", "SystemV", 9,null);
+ //testCPU(src,"x86_64_v2", "SystemV", 9,null);
testCPU(src,"riscv" , "SystemV", 5,null);
testCPU(src,"arm" , "SystemV", 6,null);
}
diff --git a/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp
index 00c2a161..b82b3f42 100644
--- a/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp
+++ b/chapter21/src/test/java/com/seaofnodes/simple/progs/jig.smp
@@ -1,9 +1,64 @@
// -*- mode: java; -*-
-struct s0 {
- i16 v1;
- u8 v2;
-};
-s0 !l4Hi9BCnsd = new s0;
-if( new s0.v2+arg )
- while(1) l4Hi9BCnsd=new s0;
-while(new s0.v2+new s0.v1<=0) {}
+// based on the top-down version from https://en.wikipedia.org/wiki/Merge_sort
+
+val merge_sort = { int[] a, int[] b, int n ->
+ copy_array (a, 0, n, b);
+ split_merge(a, 0, n, b);
+};
+
+val split_merge = { int[] b, int begin, int end, int[] a ->
+ if (end - begin <= 1)
+ return 0;
+ int middle = (end + begin) / 2;
+ split_merge(a, begin, middle, b);
+ split_merge(a, middle, end, b);
+ merge(b, begin, middle, end, a);
+ return 0;
+};
+
+val merge = { int[] b, int begin, int middle, int end, int[] a ->
+ int i = begin, j = middle;
+
+ for (int k = begin; k < end; k++) {
+ // && and ||
+ bool cond = false;
+ if (i < middle) {
+ if (j >= end) cond = true;
+ else if (a[i] <= a[j]) cond = true;
+ }
+ if (cond) b[k] = a[i++];
+ else b[k] = a[j++];
+ }
+};
+
+val copy_array = { int[] a, int begin, int end, int[] b ->
+ for (int k = begin; k < end; k++)
+ b[k] = a[k];
+};
+
+val eq = { int[] a, int[] b, int n ->
+ int result = 1;
+ int i = 0;
+ while (i < n) {
+ if (a[i] != b[i]) {
+ result = 0;
+ break;
+ }
+ i = i + 1;
+ }
+ return result;
+};
+
+val main = { ->
+ int[] !a = new int[10];
+ a[0] = 10; a[1] = 9; a[2] = 8; a[3] = 7; a[4] = 6;
+ a[5] = 5; a[6] = 4; a[7] = 3; a[8] = 2; a[9] = 1;
+ int[] !b = new int[10];
+ b[0] = 0; b[1] = 0; b[2] = 0; b[3] = 0; b[4] = 0;
+ b[5] = 0; b[6] = 0; b[7] = 0; b[8] = 0; b[9] = 0;
+ int[] !expect = new int[10];
+ expect[0] = 1; expect[1] = 2; expect[2] = 3; expect[3] = 4; expect[4] = 5;
+ expect[5] = 6; expect[6] = 7; expect[7] = 8; expect[8] = 9; expect[9] = 10;
+ merge_sort(a, b, 10);
+ return eq(a,expect,10);
+};