From 074f718a104dbbb8b1b0a2205c176659a27dfd9f Mon Sep 17 00:00:00 2001 From: Cliff Click Date: Wed, 9 Apr 2025 17:12:56 -0700 Subject: Handle large arg count function 2 print modes for stack slots - slot# or stack offset, only available after encoding. Compute maxArgSlot in one place on all archs. X86 yank flags from Splits - lest we split a XMM into the flags??? --- .../com/seaofnodes/simple/codegen/CodeGen.java | 6 +- .../com/seaofnodes/simple/codegen/Machine.java | 4 + .../com/seaofnodes/simple/codegen/RegAlloc.java | 42 ++++++- .../java/com/seaofnodes/simple/node/FunNode.java | 2 +- .../seaofnodes/simple/node/cpus/arm/FunARM.java | 14 ++- .../com/seaofnodes/simple/node/cpus/arm/arm.java | 8 +- .../seaofnodes/simple/node/cpus/riscv/FunRISC.java | 7 +- .../seaofnodes/simple/node/cpus/riscv/riscv.java | 5 +- .../simple/node/cpus/x86_64_v2/FunX86.java | 6 +- .../simple/node/cpus/x86_64_v2/RetX86.java | 2 +- .../simple/node/cpus/x86_64_v2/SplitX86.java | 4 +- .../simple/node/cpus/x86_64_v2/x86_64_v2.java | 131 ++++++++++++--------- .../com/seaofnodes/simple/print/ASMPrinter.java | 17 +-- 13 files changed, 155 insertions(+), 93 deletions(-) (limited to 'chapter21/src/main/java') 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 f5d32dd2..515e2c10 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/CodeGen.java @@ -297,9 +297,11 @@ public class CodeGen { return this; } - public String reg(Node n) { + // Human readable register name + public String reg(Node n) { return reg(n,null); } + public String reg(Node n, FunNode fun) { if( _phase.ordinal() >= Phase.RegAlloc.ordinal() ) { - String s = _regAlloc.reg(n); + String s = _regAlloc.reg(n,fun); if( s!=null ) return s; } return "N"+ n._nid; 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 bf3bfdb9..220ce7a4 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/Machine.java @@ -1,6 +1,7 @@ package com.seaofnodes.simple.codegen; import com.seaofnodes.simple.node.*; +import com.seaofnodes.simple.type.TypeFunPtr; abstract public class Machine { // Human readable machine name. Something like "x86-64" or "arm" or "risc5" @@ -20,6 +21,9 @@ abstract public class Machine { // Instruction select from ideal nodes public abstract Node instSelect( Node n ); + // Maximum stack slot (or 0) for the args in this TFP. + 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. 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 c84e6011..0f480a33 100644 --- a/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java +++ b/chapter21/src/main/java/com/seaofnodes/simple/codegen/RegAlloc.java @@ -51,6 +51,29 @@ 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; @@ -130,11 +153,18 @@ public class RegAlloc { } // Printable register number for node n - String reg( Node n ) { + String reg( Node n ) { return reg(n,null); } + String reg( Node n, FunNode fun ) { LRG lrg = lrg(n); if( lrg==null ) return null; + // No register yet, use LRG if( lrg._reg == -1 ) return "V"+lrg._lrg; - return _code._mach.reg(lrg._reg); + // 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); + // Stack-slot past RA uses the frame layout logic + return "[rsp+"+fun.computeStackSlot(slot)*8+"]"; } // ----------------------- @@ -542,9 +572,11 @@ public class RegAlloc { 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 ) + 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; for( int j=0; j0 ) - sb.p("rsp += #").p(frameAdjust*-8).p("\nret"); + 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 8e3112cd..c7685ae1 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 @@ -3,6 +3,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.FunNode; import com.seaofnodes.simple.node.Node; import com.seaofnodes.simple.node.SplitNode; import com.seaofnodes.simple.type.Type; @@ -92,6 +93,7 @@ public class SplitX86 extends SplitNode { // General form: "mov dst = src" @Override public void asm(CodeGen code, SB sb) { - sb.p(code.reg(this)).p(" = ").p(code.reg(in(1))); + FunNode fun = code._encoding._fun; + sb.p(code.reg(this,fun)).p(" = ").p(code.reg(in(1),fun)); } } 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 ea0ab27b..2f70dc0c 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 @@ -36,7 +36,7 @@ public class x86_64_v2 extends Machine { static RegMask RPC_MASK = new RegMask(RPC); static final long SPILLS = -(1L << MAX_REG); - static final RegMask SPLIT_MASK = new RegMask(WR_BITS | FP_BITS | (1L< callSys5 (tfp,idx); + case "Win64" -> callWin64(tfp,idx); + default -> throw Utils.TODO(); + }; + } + + // Maximum stack args used by this signature, +1 for RPC + @Override public short maxArgSlot( TypeFunPtr tfp ) { + return switch( CodeGen.CODE._callingConv ) { + case "SystemV" -> maxArgSlotSys5 (tfp); + case "Win64" -> maxArgSlotWin64(tfp); + default -> throw Utils.TODO(); + }; + } - // WIN64(param passing) + // Win64 - max *4* args in registers of all kinds; after that on stack. + // Stack args land in increasing memory order. + // foo( int arg0, int arg1, int arg2, int arg3, int stk1, flt stk2, int stk3, ... ) + // + // -- Prior Frame -- + // RPC + // stk3 + // stk2 + // stk1 + // -- Caller Frame; 16b align -- + // RPC + // PAD + // -- Callee Frame; 16b align -- static RegMask[] CALLINMASK_WIN64 = new RegMask[] { RCX_MASK, RDX_MASK, @@ -208,6 +240,19 @@ public class x86_64_v2 extends Machine { R09_MASK, }; + private static RegMask callWin64(TypeFunPtr tfp, int idx ) { + // 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 tfp.arg(idx-2) instanceof TypeFloat + ? XMMS8[idx-2] + : CALLINMASK_WIN64[idx-2]; + } + static short maxArgSlotWin64(TypeFunPtr tfp) { + return (short)(1/*RPC*/+ tfp.nargs()); + } + // SystemV(param passing) static RegMask[] CALLINMASK_SYSTEMV = new RegMask[] { RDI_MASK, @@ -218,57 +263,29 @@ public class x86_64_v2 extends Machine { R09_MASK, }; - // Limit of float args passed in registers - static RegMask[] XMMS4 = new RegMask[]{ - new RegMask(XMM0), new RegMask(XMM1), new RegMask(XMM2), new RegMask(XMM3), - }; - - // Map from function signature and argument index to register. - // Used to set input registers to CallNodes, and ParmNode outputs. - static RegMask callInMask( TypeFunPtr tfp, int idx ) { - if( idx==0 ) return RPC_MASK; - if( idx==1 ) return null; - // Count floats in signature up to index - int fcnt=0; - for( int i=2; i CALLINMASK_SYSTEMV; - case "Win64" -> CALLINMASK_WIN64; - default -> throw new IllegalArgumentException("Unknown calling convention: "+CodeGen.CODE._callingConv); - }; - if( idx-2-fcnt < cargs.length ) - return cargs[idx-2-fcnt]; + private static RegMask callSys5(TypeFunPtr tfp, int idx ) { + // 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 + for( int i=2; i= 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 1; // No stack args, plus RPC + 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+nstk) + : icnt<6 ? CALLINMASK_SYSTEMV[icnt] : new RegMask(MAX_REG+nstk); + } + static short maxArgSlotSys5(TypeFunPtr tfp) { + int icnt=0, fcnt=0; // Count of ints, floats + for( int i=0; i