[dev.simd] all: merge master (02d1f3a) into dev.simd

Merge List:

+ 2025-11-24 02d1f3a06b runtime: respect GOTRACEBACK for user-triggered runtime panics
+ 2025-11-24 a593ca9d65 runtime/cgo: add support for `any` param and return type
+ 2025-11-24 89552911b3 cmd/compile, internal/buildcfg: enable regABI on s390x, and add s390x
+ 2025-11-24 2fe0ba8d52 internal/bytealg: port bytealg functions to reg ABI on s390x
+ 2025-11-24 4529c8fba6 runtime: port memmove, memclr to register ABI on s390x
+ 2025-11-24 58a48a3e3b internal/runtime/syscall: Syscall changes for s390x regabi
+ 2025-11-24 2a185fae7e reflect, runtime: add reflect support for regabi on s390x
+ 2025-11-24 e92d2964fa runtime: mark race functions on s390x as ABIInternal
+ 2025-11-24 41af98eb83 runtime: add runtime changes for register ABI on s390x
+ 2025-11-24 85e6080089 cmd/internal/obj: set morestack arg spilling and regabi prologue on s390x
+ 2025-11-24 24697419c5 cmd/compile: update s390x CALL* ops
+ 2025-11-24 81242d034c cmd/compile/internal/s390x: add initial spill support
+ 2025-11-24 73b6aa0fec cmd/compile/internal: add register ABI information for s390x
+ 2025-11-24 1036f6f485 internal/abi: define s390x ABI constants
+ 2025-11-24 2e5d12a277 cmd/compile: document register-based ABI for s390x

Change-Id: I57b4ae6f9b65d99958b9fe5974205770e18f7788
This commit is contained in:
Cherry Mui 2025-11-24 15:29:27 -05:00
commit afd1721fc5
33 changed files with 715 additions and 251 deletions

View file

@ -1121,6 +1121,9 @@ func (p *Package) hasPointer(f *File, t ast.Expr, top bool) bool {
if t.Name == "error" { if t.Name == "error" {
return true return true
} }
if t.Name == "any" {
return true
}
if goTypes[t.Name] != nil { if goTypes[t.Name] != nil {
return false return false
} }

View file

@ -106,6 +106,7 @@ func TestSetEnv(t *testing.T) { testSetEnv(t) }
func TestThreadLock(t *testing.T) { testThreadLockFunc(t) } func TestThreadLock(t *testing.T) { testThreadLockFunc(t) }
func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) } func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) }
func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) } func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) }
func Test76340(t *testing.T) { test76340(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) } func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
func BenchmarkGoString(b *testing.B) { benchGoString(b) } func BenchmarkGoString(b *testing.B) { benchGoString(b) }

View file

@ -959,6 +959,18 @@ char * const issue75751p = &issue75751v;
#define issue75751m issue75751p #define issue75751m issue75751p
char * const volatile issue75751p2 = &issue75751v; char * const volatile issue75751p2 = &issue75751v;
#define issue75751m2 issue75751p2 #define issue75751m2 issue75751p2
typedef struct { void *t; void *v; } GoInterface;
extern int exportAny76340Param(GoInterface);
extern GoInterface exportAny76340Return(int);
int issue76340testFromC(GoInterface obj) {
return exportAny76340Param(obj);
}
GoInterface issue76340returnFromC(int val) {
return exportAny76340Return(val);
}
*/ */
import "C" import "C"
@ -2407,3 +2419,22 @@ func test69086(t *testing.T) {
func test75751() int { func test75751() int {
return int(*C.issue75751m) + int(*C.issue75751m2) return int(*C.issue75751m) + int(*C.issue75751m2)
} }
// Issue 76340.
func test76340(t *testing.T) {
var emptyInterface C.GoInterface
r1 := C.issue76340testFromC(emptyInterface)
if r1 != 0 {
t.Errorf("issue76340testFromC with nil interface: got %d, want 0", r1)
}
r2 := C.issue76340returnFromC(42)
if r2.t == nil && r2.v == nil {
t.Error("issue76340returnFromC(42) returned nil interface")
}
r3 := C.issue76340returnFromC(0)
if r3.t != nil || r3.v != nil {
t.Errorf("issue76340returnFromC(0) returned non-nil interface: got %v, want nil", r3)
}
}

View file

@ -595,3 +595,21 @@ func test49633(t *testing.T) {
t.Errorf("msg = %q, want 'hello'", v.msg) t.Errorf("msg = %q, want 'hello'", v.msg)
} }
} }
//export exportAny76340Param
func exportAny76340Param(obj any) C.int {
if obj == nil {
return 0
}
return 1
}
//export exportAny76340Return
func exportAny76340Return(val C.int) any {
if val == 0 {
return nil
}
return int(val)
}

View file

@ -1558,6 +1558,9 @@ func (p *Package) doCgoType(e ast.Expr, m map[ast.Expr]bool) *Type {
if t.Name == "error" { if t.Name == "error" {
return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")} return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
} }
if t.Name == "any" {
return &Type{Size: 2 * p.PtrSize, Align: p.PtrSize, C: c("GoInterface")}
}
if r, ok := goTypes[t.Name]; ok { if r, ok := goTypes[t.Name]; ok {
return goTypesFixup(r) return goTypesFixup(r)
} }

View file

@ -833,6 +833,51 @@ The riscv64 has Zicsr extension for control and status register (CSR) and
treated as scratch register. treated as scratch register.
All bits in CSR are system flags and are not modified by Go. All bits in CSR are system flags and are not modified by Go.
### s390x architecture
The s390x architecture uses R2 R9 for integer arguments and integer results.
It uses F0 F15 for floating-point arguments and results.
Special-purpose registers used within Go generated code and Go assembly code
are as follows:
| Register | Call meaning | Return meaning | Body meaning |
| --- | --- | --- | --- |
| R0 | Zero value | Same | Same |
| R1 | Scratch | Scratch | Scratch |
| R10, R11 | used by the assembler | Same | Same |
| R12 | Closure context pointer | Same | Same |
| R13 | Current goroutine | Same | Same |
| R14 | Link register | Link register | Scratch |
| R15 | Stack pointer | Same | Same |
*Rationale*: These register meanings are compatible with Gos stack-based
calling convention.
#### Stack layout
The stack pointer, R15, grows down and is aligned to 8 bytes.
A function's stack frame, after the frame is created, is laid out as
follows:
+------------------------------+
| ... locals ... |
| ... outgoing arguments ... |
| return PC | ← R15 points to
+------------------------------+ ↓ lower addresses
This stack layout is used by both register-based (ABIInternal) and
stack-based (ABI0) calling conventions.
The "return PC" is loaded to the link register R14, as part of the
s390x `BL` operation.
#### Flags
The s390x architecture maintains a single condition code (CC) field in the Program Status Word (PSW).
Go-generated code sets and tests this condition code to control conditional branches.
## Future directions ## Future directions
### Spill path improvements ### Spill path improvements

View file

@ -20,4 +20,6 @@ func Init(arch *ssagen.ArchInfo) {
arch.SSAMarkMoves = ssaMarkMoves arch.SSAMarkMoves = ssaMarkMoves
arch.SSAGenValue = ssaGenValue arch.SSAGenValue = ssaGenValue
arch.SSAGenBlock = ssaGenBlock arch.SSAGenBlock = ssaGenBlock
arch.LoadRegResult = loadRegResult
arch.SpillArgReg = spillArgReg
} }

View file

@ -10,6 +10,7 @@ import (
"cmd/compile/internal/base" "cmd/compile/internal/base"
"cmd/compile/internal/ir" "cmd/compile/internal/ir"
"cmd/compile/internal/logopt" "cmd/compile/internal/logopt"
"cmd/compile/internal/objw"
"cmd/compile/internal/ssa" "cmd/compile/internal/ssa"
"cmd/compile/internal/ssagen" "cmd/compile/internal/ssagen"
"cmd/compile/internal/types" "cmd/compile/internal/types"
@ -540,6 +541,19 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = v.Args[0].Reg() p.From.Reg = v.Args[0].Reg()
ssagen.AddrAuto(&p.To, v) ssagen.AddrAuto(&p.To, v)
case ssa.OpArgIntReg, ssa.OpArgFloatReg:
// The assembler needs to wrap the entry safepoint/stack growth code with spill/unspill
// The loop only runs once.
for _, a := range v.Block.Func.RegArgs {
// Pass the spill/unspill information along to the assembler, offset by size of
// the saved LR slot.
addr := ssagen.SpillSlotAddr(a, s390x.REGSP, base.Ctxt.Arch.FixedFrameSize)
s.FuncInfo().AddSpill(
obj.RegSpill{Reg: a.Reg, Addr: addr, Unspill: loadByType(a.Type), Spill: storeByType(a.Type)})
}
v.Block.Func.RegArgs = nil
ssagen.CheckArgReg(v)
case ssa.OpS390XLoweredGetClosurePtr: case ssa.OpS390XLoweredGetClosurePtr:
// Closure pointer is R12 (already) // Closure pointer is R12 (already)
ssagen.CheckLoweredGetClosurePtr(v) ssagen.CheckLoweredGetClosurePtr(v)
@ -1029,3 +1043,22 @@ func ssaGenBlock(s *ssagen.State, b, next *ssa.Block) {
s.Br(s390x.ABR, succs[1]) s.Br(s390x.ABR, succs[1])
} }
} }
func loadRegResult(s *ssagen.State, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
p := s.Prog(loadByType(t))
p.From.Type = obj.TYPE_MEM
p.From.Name = obj.NAME_AUTO
p.From.Sym = n.Linksym()
p.From.Offset = n.FrameOffset() + off
p.To.Type = obj.TYPE_REG
p.To.Reg = reg
return p
}
func spillArgReg(pp *objw.Progs, p *obj.Prog, f *ssa.Func, t *types.Type, reg int16, n *ir.Name, off int64) *obj.Prog {
p = pp.Append(p, storeByType(t), obj.TYPE_REG, reg, 0, obj.TYPE_MEM, 0, n.FrameOffset()+off)
p.To.Name = obj.NAME_PARAM
p.To.Sym = n.Linksym()
p.Pos = p.Pos.WithNotStmt()
return p
}

View file

@ -484,10 +484,10 @@ func init() {
{name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"}, {name: "CLEAR", argLength: 2, reg: regInfo{inputs: []regMask{ptr, 0}}, asm: "CLEAR", aux: "SymValAndOff", typ: "Mem", clobberFlags: true, faultOnNilArg0: true, symEffect: "Write"},
{name: "CALLstatic", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLstatic", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
{name: "CALLtail", argLength: 1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). arg0=mem, auxint=argsize, returns mem {name: "CALLtail", argLength: -1, reg: regInfo{clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true, tailCall: true}, // tail call static function aux.(*obj.LSym). last arg=mem, auxint=argsize, returns mem
{name: "CALLclosure", argLength: 3, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, arg2=mem, auxint=argsize, returns mem {name: "CALLclosure", argLength: -1, reg: regInfo{inputs: []regMask{ptrsp, buildReg("R12"), 0}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call function via closure. arg0=codeptr, arg1=closure, last arg=mem, auxint=argsize, returns mem
{name: "CALLinter", argLength: 2, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, arg1=mem, auxint=argsize, returns mem {name: "CALLinter", argLength: -1, reg: regInfo{inputs: []regMask{ptr}, clobbers: callerSave}, aux: "CallOff", clobberFlags: true, call: true}, // call fn by pointer. arg0=codeptr, last arg=mem, auxint=argsize, returns mem
// (InvertFlags (CMP a b)) == (CMP b a) // (InvertFlags (CMP a b)) == (CMP b a)
// InvertFlags is a pseudo-op which can't appear in assembly output. // InvertFlags is a pseudo-op which can't appear in assembly output.
@ -818,6 +818,8 @@ func init() {
ops: S390Xops, ops: S390Xops,
blocks: S390Xblocks, blocks: S390Xblocks,
regnames: regNamesS390X, regnames: regNamesS390X,
ParamIntRegNames: "R2 R3 R4 R5 R6 R7 R8 R9",
ParamFloatRegNames: "F0 F1 F2 F3 F4 F5 F6 F7 F8 F9 F10 F11 F12 F13 F14 F15",
gpregmask: gp, gpregmask: gp,
fpregmask: fp, fpregmask: fp,
framepointerreg: -1, // not used framepointerreg: -1, // not used

View file

@ -313,6 +313,8 @@ func NewConfig(arch string, types Types, ctxt *obj.Link, optimize, softfloat boo
c.registers = registersS390X[:] c.registers = registersS390X[:]
c.gpRegMask = gpRegMaskS390X c.gpRegMask = gpRegMaskS390X
c.fpRegMask = fpRegMaskS390X c.fpRegMask = fpRegMaskS390X
c.intParamRegs = paramIntRegS390X
c.floatParamRegs = paramFloatRegS390X
c.FPReg = framepointerRegS390X c.FPReg = framepointerRegS390X
c.LinkReg = linkRegS390X c.LinkReg = linkRegS390X
c.hasGReg = true c.hasGReg = true

View file

@ -45,7 +45,7 @@ func testGoArch() string {
func hasRegisterABI() bool { func hasRegisterABI() bool {
switch testGoArch() { switch testGoArch() {
case "amd64", "arm64", "loong64", "ppc64", "ppc64le", "riscv": case "amd64", "arm64", "loong64", "ppc64", "ppc64le", "riscv", "s390x":
return true return true
} }
return false return false

View file

@ -81087,7 +81087,7 @@ var opcodeTable = [...]opInfo{
{ {
name: "CALLstatic", name: "CALLstatic",
auxType: auxCallOff, auxType: auxCallOff,
argLen: 1, argLen: -1,
clobberFlags: true, clobberFlags: true,
call: true, call: true,
reg: regInfo{ reg: regInfo{
@ -81097,7 +81097,7 @@ var opcodeTable = [...]opInfo{
{ {
name: "CALLtail", name: "CALLtail",
auxType: auxCallOff, auxType: auxCallOff,
argLen: 1, argLen: -1,
clobberFlags: true, clobberFlags: true,
call: true, call: true,
tailCall: true, tailCall: true,
@ -81108,7 +81108,7 @@ var opcodeTable = [...]opInfo{
{ {
name: "CALLclosure", name: "CALLclosure",
auxType: auxCallOff, auxType: auxCallOff,
argLen: 3, argLen: -1,
clobberFlags: true, clobberFlags: true,
call: true, call: true,
reg: regInfo{ reg: regInfo{
@ -81122,7 +81122,7 @@ var opcodeTable = [...]opInfo{
{ {
name: "CALLinter", name: "CALLinter",
auxType: auxCallOff, auxType: auxCallOff,
argLen: 2, argLen: -1,
clobberFlags: true, clobberFlags: true,
call: true, call: true,
reg: regInfo{ reg: regInfo{
@ -93219,8 +93219,8 @@ var registersS390X = [...]Register{
{31, s390x.REG_F15, "F15"}, {31, s390x.REG_F15, "F15"},
{32, 0, "SB"}, {32, 0, "SB"},
} }
var paramIntRegS390X = []int8(nil) var paramIntRegS390X = []int8{2, 3, 4, 5, 6, 7, 8, 9}
var paramFloatRegS390X = []int8(nil) var paramFloatRegS390X = []int8{16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31}
var gpRegMaskS390X = regMask(23551) var gpRegMaskS390X = regMask(23551)
var fpRegMaskS390X = regMask(4294901760) var fpRegMaskS390X = regMask(4294901760)
var specialRegMaskS390X = regMask(0) var specialRegMaskS390X = regMask(0)

View file

@ -139,8 +139,8 @@ const (
REG_RESERVED // end of allocated registers REG_RESERVED // end of allocated registers
REGARG = -1 // -1 disables passing the first argument in register REGARG = -1 // -1 disables passing the first argument in register
REGRT1 = REG_R3 // used during zeroing of the stack - not reserved REGRT1 = REG_R1 // used during zeroing of the stack - not reserved
REGRT2 = REG_R4 // used during zeroing of the stack - not reserved REGRT2 = REG_R10 // used during zeroing of the stack - not reserved
REGTMP = REG_R10 // scratch register used in the assembler and linker REGTMP = REG_R10 // scratch register used in the assembler and linker
REGTMP2 = REG_R11 // scratch register used in the assembler and linker REGTMP2 = REG_R11 // scratch register used in the assembler and linker
REGCTXT = REG_R12 // context for closures REGCTXT = REG_R12 // context for closures

View file

@ -506,7 +506,13 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCh
// Save LR and REGCTXT // Save LR and REGCTXT
const frameSize = 16 const frameSize = 16
p = c.ctxt.StartUnsafePoint(p, c.newprog) p = c.ctxt.StartUnsafePoint(p, c.newprog)
// Spill arguments. This has to happen before we open
// any more frame space.
p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
// MOVD LR, -16(SP) // MOVD LR, -16(SP)
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
p.As = AMOVD p.As = AMOVD
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR} p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
@ -549,10 +555,12 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCh
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP} p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
p.Spadj = -frameSize p.Spadj = -frameSize
// Unspill arguments
p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
} }
// MOVD g_stackguard(g), R3 // MOVD g_stackguard(g), R10
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
// Jump back to here after morestack returns. // Jump back to here after morestack returns.
pCheck = p pCheck = p
@ -565,7 +573,7 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCh
p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1 p.From.Offset = 3 * int64(c.ctxt.Arch.PtrSize) // G.stackguard1
} }
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R3 p.To.Reg = REG_R10
// Mark the stack bound check and morestack call async nonpreemptible. // Mark the stack bound check and morestack call async nonpreemptible.
// If we get preempted here, when resumed the preemption request is // If we get preempted here, when resumed the preemption request is
@ -579,7 +587,7 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCh
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = REG_R3 p.From.Reg = REG_R10
p.Reg = REGSP p.Reg = REGSP
p.As = ACMPUBGE p.As = ACMPUBGE
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
@ -598,40 +606,40 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCh
// stack guard to incorrectly succeed. We explicitly // stack guard to incorrectly succeed. We explicitly
// guard against underflow. // guard against underflow.
// //
// MOVD $(framesize-StackSmall), R4 // MOVD $(framesize-StackSmall), R11
// CMPUBLT SP, R4, label-of-call-to-morestack // CMPUBLT SP, R11, label-of-call-to-morestack
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
p.As = AMOVD p.As = AMOVD
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = offset p.From.Offset = offset
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R4 p.To.Reg = REG_R11
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
pPreempt = p pPreempt = p
p.As = ACMPUBLT p.As = ACMPUBLT
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = REGSP p.From.Reg = REGSP
p.Reg = REG_R4 p.Reg = REG_R11
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
} }
// Check against the stack guard. We've ensured this won't underflow. // Check against the stack guard. We've ensured this won't underflow.
// ADD $-(framesize-StackSmall), SP, R4 // ADD $-(framesize-StackSmall), SP, R11
// CMPUBGE stackguard, R4, label-of-call-to-morestack // CMPUBGE stackguard, R11, label-of-call-to-morestack
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
p.As = AADD p.As = AADD
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = -offset p.From.Offset = -offset
p.Reg = REGSP p.Reg = REGSP
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R4 p.To.Reg = REG_R11
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = REG_R3 p.From.Reg = REG_R10
p.Reg = REG_R4 p.Reg = REG_R11
p.As = ACMPUBGE p.As = ACMPUBGE
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH
@ -654,18 +662,22 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, fr
pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog) pcdata := c.ctxt.EmitEntryStackMap(c.cursym, spfix, c.newprog)
pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog) pcdata = c.ctxt.StartUnsafePoint(pcdata, c.newprog)
if pPreempt != nil {
pPreempt.To.SetTarget(pcdata)
}
pPre.To.SetTarget(pcdata)
// Spill the register args that could be clobbered by the
// morestack code.
spill := c.cursym.Func().SpillRegisterArgs(pcdata, c.newprog)
// MOVD LR, R5 // MOVD LR, R5
p = obj.Appendp(pcdata, c.newprog) p = obj.Appendp(spill, c.newprog)
pPre.To.SetTarget(p)
p.As = AMOVD p.As = AMOVD
p.From.Type = obj.TYPE_REG p.From.Type = obj.TYPE_REG
p.From.Reg = REG_LR p.From.Reg = REG_LR
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = REG_R5 p.To.Reg = REG_R5
if pPreempt != nil {
pPreempt.To.SetTarget(p)
}
// BL runtime.morestack(SB) // BL runtime.morestack(SB)
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
@ -680,10 +692,12 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, fr
p.To.Sym = c.ctxt.Lookup("runtime.morestack") p.To.Sym = c.ctxt.Lookup("runtime.morestack")
} }
// The instructions which unspill regs should be preemptible.
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1) p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
unspill := c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
// BR pCheck // BR pCheck
p = obj.Appendp(p, c.newprog) p = obj.Appendp(unspill, c.newprog)
p.As = ABR p.As = ABR
p.To.Type = obj.TYPE_BRANCH p.To.Type = obj.TYPE_BRANCH

View file

@ -0,0 +1,19 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build goexperiment.regabiargs
package abi
const (
// See abi_generic.go.
// R2 - R9.
IntArgRegs = 8
// F0 - F15
FloatArgRegs = 16
EffectiveFloatRegSize = 8
)

View file

@ -65,6 +65,8 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
case "amd64", "arm64", "loong64", "ppc64le", "ppc64", "riscv64": case "amd64", "arm64", "loong64", "ppc64le", "ppc64", "riscv64":
regabiAlwaysOn = true regabiAlwaysOn = true
regabiSupported = true regabiSupported = true
case "s390x":
regabiSupported = true
} }
// Older versions (anything before V16) of dsymutil don't handle // Older versions (anything before V16) of dsymutil don't handle
@ -141,7 +143,7 @@ func ParseGOEXPERIMENT(goos, goarch, goexp string) (*ExperimentFlags, error) {
flags.RegabiWrappers = true flags.RegabiWrappers = true
flags.RegabiArgs = true flags.RegabiArgs = true
} }
// regabi is only supported on amd64, arm64, loong64, riscv64, ppc64 and ppc64le. // regabi is only supported on amd64, arm64, loong64, riscv64, s390x, ppc64 and ppc64le.
if !regabiSupported { if !regabiSupported {
flags.RegabiWrappers = false flags.RegabiWrappers = false
flags.RegabiArgs = false flags.RegabiArgs = false

View file

@ -5,65 +5,93 @@
#include "go_asm.h" #include "go_asm.h"
#include "textflag.h" #include "textflag.h"
TEXT ·Compare(SB),NOSPLIT|NOFRAME,$0-56 TEXT ·Compare<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-56
MOVD a_base+0(FP), R3 #ifndef GOEXPERIMENT_regabiargs
MOVD a_len+8(FP), R4 MOVD a_base+0(FP), R2
MOVD b_base+24(FP), R5 MOVD a_len+8(FP), R3
MOVD b_len+32(FP), R6 MOVD b_base+24(FP), R4
LA ret+48(FP), R7 MOVD b_len+32(FP), R5
LA ret+48(FP), R6
#else
// R2 = a_base
// R3 = a_len
// R4 = a_cap (unused)
// R5 = b_base (want in R4)
// R6 = b_len (want in R5)
// R7 = b_cap (unused)
MOVD R5, R4
MOVD R6, R5
#endif
BR cmpbody<>(SB) BR cmpbody<>(SB)
TEXT runtime·cmpstring(SB),NOSPLIT|NOFRAME,$0-40 TEXT runtime·cmpstring<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40
MOVD a_base+0(FP), R3 #ifndef GOEXPERIMENT_regabiargs
MOVD a_len+8(FP), R4 MOVD a_base+0(FP), R2
MOVD b_base+16(FP), R5 MOVD a_len+8(FP), R3
MOVD b_len+24(FP), R6 MOVD b_base+16(FP), R4
LA ret+32(FP), R7 MOVD b_len+24(FP), R5
LA ret+32(FP), R6
#endif
// R2 = a_base
// R3 = a_len
// R4 = b_base
// R5 = b_len
BR cmpbody<>(SB) BR cmpbody<>(SB)
// input: // input:
// R3 = a // R2 = a
// R4 = alen // R3 = alen
// R5 = b // R4 = b
// R6 = blen // R5 = blen
// R7 = address of output word (stores -1/0/1 here) // For regabiargs output value( -1/0/1 ) stored in R2
// For !regabiargs address of output word( stores -1/0/1 ) stored in R6
TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0 TEXT cmpbody<>(SB),NOSPLIT|NOFRAME,$0-0
CMPBEQ R3, R5, cmplengths CMPBEQ R2, R4, cmplengths
MOVD R4, R8 MOVD R3, R7
CMPBLE R4, R6, amin CMPBLE R3, R5, amin
MOVD R6, R8 MOVD R5, R7
amin: amin:
CMPBEQ R8, $0, cmplengths CMPBEQ R7, $0, cmplengths
CMP R8, $256 CMP R7, $256
BLE tail BLE tail
loop: loop:
CLC $256, 0(R3), 0(R5) CLC $256, 0(R2), 0(R4)
BGT gt BGT gt
BLT lt BLT lt
SUB $256, R8 SUB $256, R7
MOVD $256(R3), R3 MOVD $256(R2), R2
MOVD $256(R5), R5 MOVD $256(R4), R4
CMP R8, $256 CMP R7, $256
BGT loop BGT loop
tail: tail:
SUB $1, R8 SUB $1, R7
EXRL $cmpbodyclc<>(SB), R8 EXRL $cmpbodyclc<>(SB), R7
BGT gt BGT gt
BLT lt BLT lt
cmplengths: cmplengths:
CMP R4, R6 CMP R3, R5
BEQ eq BEQ eq
BLT lt BLT lt
gt: gt:
MOVD $1, 0(R7) MOVD $1, R2
#ifndef GOEXPERIMENT_regabiargs
MOVD R2, 0(R6)
#endif
RET RET
lt: lt:
MOVD $-1, 0(R7) MOVD $-1, R2
#ifndef GOEXPERIMENT_regabiargs
MOVD R2, 0(R6)
#endif
RET RET
eq: eq:
MOVD $0, 0(R7) MOVD $0, R2
#ifndef GOEXPERIMENT_regabiargs
MOVD R2, 0(R6)
#endif
RET RET
TEXT cmpbodyclc<>(SB),NOSPLIT|NOFRAME,$0-0 TEXT cmpbodyclc<>(SB),NOSPLIT|NOFRAME,$0-0
CLC $1, 0(R3), 0(R5) CLC $1, 0(R2), 0(R4)
RET RET

View file

@ -6,80 +6,92 @@
#include "textflag.h" #include "textflag.h"
// memequal(a, b unsafe.Pointer, size uintptr) bool // memequal(a, b unsafe.Pointer, size uintptr) bool
TEXT runtime·memequal(SB),NOSPLIT|NOFRAME,$0-25 TEXT runtime·memequal<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-25
MOVD a+0(FP), R3 #ifndef GOEXPERIMENT_regabiargs
MOVD b+8(FP), R5 MOVD a+0(FP), R2
MOVD size+16(FP), R6 MOVD b+8(FP), R3
LA ret+24(FP), R7 MOVD size+16(FP), R4
LA ret+24(FP), R5
#endif
BR memeqbody<>(SB) BR memeqbody<>(SB)
// memequal_varlen(a, b unsafe.Pointer) bool // memequal_varlen(a, b unsafe.Pointer) bool
TEXT runtime·memequal_varlen(SB),NOSPLIT|NOFRAME,$0-17 TEXT runtime·memequal_varlen<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-17
MOVD a+0(FP), R3 #ifndef GOEXPERIMENT_regabiargs
MOVD b+8(FP), R5 MOVD a+0(FP), R2
MOVD 8(R12), R6 // compiler stores size at offset 8 in the closure MOVD b+8(FP), R3
LA ret+16(FP), R7 LA ret+16(FP), R5
#endif
MOVD 8(R12), R4 // compiler stores size at offset 8 in the closure
BR memeqbody<>(SB) BR memeqbody<>(SB)
// input: // input:
// R3 = a // R2 = a
// R5 = b // R3 = b
// R6 = len // R4 = len
// R7 = address of output byte (stores 0 or 1 here) // For regabiargs output value( 0/1 ) stored in R2
// For !regabiargs address of output byte( stores 0/1 ) stored in R5
// a and b have the same length // a and b have the same length
TEXT memeqbody<>(SB),NOSPLIT|NOFRAME,$0-0 TEXT memeqbody<>(SB),NOSPLIT|NOFRAME,$0-0
CMPBEQ R3, R5, equal CMPBEQ R2, R3, equal
loop: loop:
CMPBEQ R6, $0, equal CMPBEQ R4, $0, equal
CMPBLT R6, $32, tiny CMPBLT R4, $32, tiny
CMP R6, $256 CMP R4, $256
BLT tail BLT tail
CLC $256, 0(R3), 0(R5) CLC $256, 0(R2), 0(R3)
BNE notequal BNE notequal
SUB $256, R6 SUB $256, R4
LA 256(R2), R2
LA 256(R3), R3 LA 256(R3), R3
LA 256(R5), R5
BR loop BR loop
tail: tail:
SUB $1, R6, R8 SUB $1, R4, R8
EXRL $memeqbodyclc<>(SB), R8 EXRL $memeqbodyclc<>(SB), R8
BEQ equal BEQ equal
notequal: notequal:
MOVB $0, 0(R7) MOVD $0, R2
#ifndef GOEXPERIMENT_regabiargs
MOVB R2, 0(R5)
#endif
RET RET
equal: equal:
MOVB $1, 0(R7) MOVD $1, R2
#ifndef GOEXPERIMENT_regabiargs
MOVB R2, 0(R5)
#endif
RET RET
tiny: tiny:
MOVD $0, R2 MOVD $0, R1
CMPBLT R6, $16, lt16 CMPBLT R4, $16, lt16
MOVD 0(R3), R8 MOVD 0(R2), R8
MOVD 0(R5), R9 MOVD 0(R3), R9
CMPBNE R8, R9, notequal CMPBNE R8, R9, notequal
MOVD 8(R3), R8 MOVD 8(R2), R8
MOVD 8(R5), R9 MOVD 8(R3), R9
CMPBNE R8, R9, notequal CMPBNE R8, R9, notequal
LA 16(R2), R2 LA 16(R1), R1
SUB $16, R6 SUB $16, R4
lt16: lt16:
CMPBLT R6, $8, lt8 CMPBLT R4, $8, lt8
MOVD 0(R3)(R2*1), R8 MOVD 0(R2)(R1*1), R8
MOVD 0(R5)(R2*1), R9 MOVD 0(R3)(R1*1), R9
CMPBNE R8, R9, notequal CMPBNE R8, R9, notequal
LA 8(R2), R2 LA 8(R1), R1
SUB $8, R6 SUB $8, R4
lt8: lt8:
CMPBLT R6, $4, lt4 CMPBLT R4, $4, lt4
MOVWZ 0(R3)(R2*1), R8 MOVWZ 0(R2)(R1*1), R8
MOVWZ 0(R5)(R2*1), R9 MOVWZ 0(R3)(R1*1), R9
CMPBNE R8, R9, notequal CMPBNE R8, R9, notequal
LA 4(R2), R2 LA 4(R1), R1
SUB $4, R6 SUB $4, R4
lt4: lt4:
#define CHECK(n) \ #define CHECK(n) \
CMPBEQ R6, $n, equal \ CMPBEQ R4, $n, equal \
MOVB n(R3)(R2*1), R8 \ MOVB n(R2)(R1*1), R8 \
MOVB n(R5)(R2*1), R9 \ MOVB n(R3)(R1*1), R9 \
CMPBNE R8, R9, notequal CMPBNE R8, R9, notequal
CHECK(0) CHECK(0)
CHECK(1) CHECK(1)
@ -88,5 +100,5 @@ lt4:
BR equal BR equal
TEXT memeqbodyclc<>(SB),NOSPLIT|NOFRAME,$0-0 TEXT memeqbodyclc<>(SB),NOSPLIT|NOFRAME,$0-0
CLC $1, 0(R3), 0(R5) CLC $1, 0(R2), 0(R3)
RET RET

View file

@ -5,46 +5,63 @@
#include "go_asm.h" #include "go_asm.h"
#include "textflag.h" #include "textflag.h"
TEXT ·IndexByte(SB),NOSPLIT|NOFRAME,$0-40
MOVD b_base+0(FP), R3// b_base => R3 TEXT ·IndexByte<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-40
MOVD b_len+8(FP), R4 // b_len => R4 #ifndef GOEXPERIMENT_regabiargs
MOVBZ c+24(FP), R5 // c => R5 MOVD b_base+0(FP), R2// b_base => R2
MOVD $ret+32(FP), R2 // &ret => R9 MOVD b_len+8(FP), R3 // b_len => R3
MOVBZ c+24(FP), R4 // c => R4
MOVD $ret+32(FP), R5 // &ret => R5
#else
MOVD R5, R4
AND $0xff, R4
#endif
BR indexbytebody<>(SB) BR indexbytebody<>(SB)
TEXT ·IndexByteString(SB),NOSPLIT|NOFRAME,$0-32 TEXT ·IndexByteString<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-32
MOVD s_base+0(FP), R3// s_base => R3 #ifndef GOEXPERIMENT_regabiargs
MOVD s_len+8(FP), R4 // s_len => R4 MOVD s_base+0(FP), R2 // s_base => R2
MOVBZ c+16(FP), R5 // c => R5 MOVD s_len+8(FP), R3 // s_len => R3
MOVD $ret+24(FP), R2 // &ret => R9 MOVBZ c+16(FP), R4 // c => R4
MOVD $ret+24(FP), R5 // &ret => R5
#else
AND $0xff, R4
#endif
BR indexbytebody<>(SB) BR indexbytebody<>(SB)
// input: // input:
// R3: s // R2: s
// R4: s_len // R3: s_len
// R5: c -- byte sought // R4: c -- byte sought
// R2: &ret -- address to put index into // For regabiargs output value(index) stored in R2
// For !regabiargs address of output value(index) stored in R5
TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0 TEXT indexbytebody<>(SB),NOSPLIT|NOFRAME,$0
CMPBEQ R4, $0, notfound CMPBEQ R3, $0, notfound
MOVD R3, R6 // store base for later MOVD R2, R6 // store base for later
ADD R3, R4, R8 // the address after the end of the string ADD R2, R3, R8 // the address after the end of the string
//if the length is small, use loop; otherwise, use vector or srst search //if the length is small, use loop; otherwise, use vector or srst search
CMPBGE R4, $16, large CMPBGE R3, $16, large
residual: residual:
CMPBEQ R3, R8, notfound CMPBEQ R2, R8, notfound
MOVBZ 0(R3), R7 MOVBZ 0(R2), R7
LA 1(R3), R3 LA 1(R2), R2
CMPBNE R7, R5, residual CMPBNE R7, R4, residual
found: found:
SUB R6, R3 SUB R6, R2
SUB $1, R3 SUB $1, R2
MOVD R3, 0(R2) #ifndef GOEXPERIMENT_regabiargs
MOVD R2, 0(R5)
#endif
RET RET
notfound: notfound:
MOVD $-1, 0(R2) #ifndef GOEXPERIMENT_regabiargs
MOVD $-1, 0(R5)
#else
MOVD $-1, R2
#endif
RET RET
large: large:
@ -52,57 +69,70 @@ large:
CMPBNE R1, $0, vectorimpl CMPBNE R1, $0, vectorimpl
srstimpl: // no vector facility srstimpl: // no vector facility
MOVBZ R5, R0 // c needs to be in R0, leave until last minute as currently R0 is expected to be 0 MOVBZ R4, R0 // c needs to be in R0, leave until last minute as currently R0 is expected to be 0
srstloop: srstloop:
WORD $0xB25E0083 // srst %r8, %r3 (search the range [R3, R8)) WORD $0xB25E0082 // srst %r8, %r2 (search the range [R2, R8))
BVS srstloop // interrupted - continue BVS srstloop // interrupted - continue
BGT notfoundr0 BGT notfoundr0
foundr0: foundr0:
XOR R0, R0 // reset R0 XOR R0, R0 // reset R0
SUB R6, R8 // remove base SUB R6, R8 // remove base
MOVD R8, 0(R2) #ifndef GOEXPERIMENT_regabiargs
MOVD R8, 0(R5)
#else
MOVD R8, R2
#endif
RET RET
notfoundr0: notfoundr0:
XOR R0, R0 // reset R0 XOR R0, R0 // reset R0
MOVD $-1, 0(R2) #ifndef GOEXPERIMENT_regabiargs
MOVD $-1, 0(R5)
#else
MOVD $-1, R2
#endif
RET RET
vectorimpl: vectorimpl:
//if the address is not 16byte aligned, use loop for the header //if the address is not 16byte aligned, use loop for the header
MOVD R3, R8 MOVD R2, R8
AND $15, R8 AND $15, R8
CMPBGT R8, $0, notaligned CMPBGT R8, $0, notaligned
aligned: aligned:
ADD R6, R4, R8 ADD R6, R3, R8
MOVD R8, R7 MOVD R8, R7
AND $-16, R7 AND $-16, R7
// replicate c across V17 // replicate c across V17
VLVGB $0, R5, V19 VLVGB $0, R4, V19
VREPB $0, V19, V17 VREPB $0, V19, V17
vectorloop: vectorloop:
CMPBGE R3, R7, residual CMPBGE R2, R7, residual
VL 0(R3), V16 // load string to be searched into V16 VL 0(R2), V16 // load string to be searched into V16
ADD $16, R3 ADD $16, R2
VFEEBS V16, V17, V18 // search V17 in V16 and set conditional code accordingly VFEEBS V16, V17, V18 // search V17 in V16 and set conditional code accordingly
BVS vectorloop BVS vectorloop
// when vector search found c in the string // when vector search found c in the string
VLGVB $7, V18, R7 // load 7th element of V18 containing index into R7 VLGVB $7, V18, R7 // load 7th element of V18 containing index into R7
SUB $16, R3 SUB $16, R2
SUB R6, R3 SUB R6, R2
ADD R3, R7 ADD R2, R7
MOVD R7, 0(R2) #ifndef GOEXPERIMENT_regabiargs
MOVD R7, 0(R5)
#else
MOVD R7, R2
#endif
RET RET
notaligned: notaligned:
MOVD R3, R8 MOVD R2, R8
AND $-16, R8 AND $-16, R8
ADD $16, R8 ADD $16, R8
notalignedloop: notalignedloop:
CMPBEQ R3, R8, aligned CMPBEQ R2, R8, aligned
MOVBZ 0(R3), R7 MOVBZ 0(R2), R7
LA 1(R3), R3 LA 1(R2), R2
CMPBNE R7, R5, notalignedloop CMPBNE R7, R4, notalignedloop
BR found BR found

View file

@ -5,7 +5,16 @@
#include "textflag.h" #include "textflag.h"
// func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr) // func Syscall6(num, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, errno uintptr)
TEXT ·Syscall6(SB),NOSPLIT,$0-80 TEXT ·Syscall6<ABIInternal>(SB),NOSPLIT,$0-80
#ifdef GOEXPERIMENT_regabiargs
MOVD R2, R1
MOVD R3, R2
MOVD R4, R3
MOVD R5, R4
MOVD R6, R5
MOVD R7, R6
MOVD R8, R7
#else
MOVD num+0(FP), R1 // syscall entry MOVD num+0(FP), R1 // syscall entry
MOVD a1+8(FP), R2 MOVD a1+8(FP), R2
MOVD a2+16(FP), R3 MOVD a2+16(FP), R3
@ -13,16 +22,27 @@ TEXT ·Syscall6(SB),NOSPLIT,$0-80
MOVD a4+32(FP), R5 MOVD a4+32(FP), R5
MOVD a5+40(FP), R6 MOVD a5+40(FP), R6
MOVD a6+48(FP), R7 MOVD a6+48(FP), R7
#endif
SYSCALL SYSCALL
MOVD $0xfffffffffffff001, R8 MOVD $0xfffffffffffff001, R8
CMPUBLT R2, R8, ok CMPUBLT R2, R8, ok
#ifdef GOEXPERIMENT_regabiargs
MOVD $0, R3
NEG R2, R4
MOVD $-1, R2
#else
MOVD $-1, r1+56(FP) MOVD $-1, r1+56(FP)
MOVD $0, r2+64(FP) MOVD $0, r2+64(FP)
NEG R2, R2 NEG R2, R2
MOVD R2, errno+72(FP) MOVD R2, errno+72(FP)
#endif
RET RET
ok: ok:
#ifdef GOEXPERIMENT_regabiargs
MOVD $0, R4
#else
MOVD R2, r1+56(FP) MOVD R2, r1+56(FP)
MOVD R3, r2+64(FP) MOVD R3, r2+64(FP)
MOVD $0, errno+72(FP) MOVD $0, errno+72(FP)
#endif
RET RET

View file

@ -5,34 +5,82 @@
#include "textflag.h" #include "textflag.h"
#include "funcdata.h" #include "funcdata.h"
// The frames of each of the two functions below contain two locals, at offsets
// that are known to the runtime.
//
// The first local is a bool called retValid with a whole pointer-word reserved
// for it on the stack. The purpose of this word is so that the runtime knows
// whether the stack-allocated return space contains valid values for stack
// scanning.
//
// The second local is an abi.RegArgs value whose offset is also known to the
// runtime, so that a stack map for it can be constructed, since it contains
// pointers visible to the GC.
#define LOCAL_RETVALID 40
#define LOCAL_REGARGS 48
// The frame size of the functions below is
// 32 (args of callReflect/callMethod) + 8 (bool + padding) + 264 (abi.RegArgs) = 304.
// makeFuncStub is the code half of the function returned by MakeFunc. // makeFuncStub is the code half of the function returned by MakeFunc.
// See the comment on the declaration of makeFuncStub in makefunc.go // See the comment on the declaration of makeFuncStub in makefunc.go
// for more details. // for more details.
// No arg size here, runtime pulls arg map out of the func value. // No arg size here, runtime pulls arg map out of the func value.
TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$40 TEXT ·makeFuncStub(SB),(NOSPLIT|WRAPPER),$304
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
ADD $LOCAL_REGARGS, R15, R10 // spillArgs using R10
BL runtime·spillArgs(SB)
MOVD R12, 32(R15) // save context reg R12 > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
#ifdef GOEXPERIMENT_regabiargs
MOVD R12, R2
MOVD R10, R3
#else
MOVD R12, 8(R15) MOVD R12, 8(R15)
MOVD $argframe+0(FP), R3 MOVD R10, 16(R15)
MOVD R3, 16(R15) #endif
MOVB $0, 40(R15) BL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
ADD $40, R15, R3 MOVD 32(R15), R12 // restore context reg R12
MOVD R3, 24(R15) MOVD R12, 8(R15)
MOVD $0, 32(R15) MOVD $argframe+0(FP), R1
MOVD R1, 16(R15)
MOVB $0, LOCAL_RETVALID(R15)
ADD $LOCAL_RETVALID, R15, R1
MOVD R1, 24(R15)
ADD $LOCAL_REGARGS, R15, R1
MOVD R1, 32(R15)
BL ·callReflect(SB) BL ·callReflect(SB)
ADD $LOCAL_REGARGS, R15, R10 // unspillArgs using R10
BL runtime·unspillArgs(SB)
RET RET
// methodValueCall is the code half of the function returned by makeMethodValue. // methodValueCall is the code half of the function returned by makeMethodValue.
// See the comment on the declaration of methodValueCall in makefunc.go // See the comment on the declaration of methodValueCall in makefunc.go
// for more details. // for more details.
// No arg size here; runtime pulls arg map out of the func value. // No arg size here; runtime pulls arg map out of the func value.
TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$40 TEXT ·methodValueCall(SB),(NOSPLIT|WRAPPER),$304
NO_LOCAL_POINTERS NO_LOCAL_POINTERS
ADD $LOCAL_REGARGS, R15, R10 // spillArgs using R10
BL runtime·spillArgs(SB)
MOVD R12, 32(R15) // save context reg R12 > args of moveMakeFuncArgPtrs < LOCAL_REGARGS
#ifdef GOEXPERIMENT_regabiargs
MOVD R12, R2
MOVD R10, R3
#else
MOVD R12, 8(R15) MOVD R12, 8(R15)
MOVD $argframe+0(FP), R3 MOVD R10, 16(R15)
MOVD R3, 16(R15) #endif
MOVB $0, 40(R15) BL ·moveMakeFuncArgPtrs<ABIInternal>(SB)
ADD $40, R15, R3 MOVD 32(R15), R12 // restore context reg R12
MOVD R3, 24(R15) MOVD R12, 8(R15)
MOVD $0, 32(R15) MOVD $argframe+0(FP), R1
MOVD R1, 16(R15)
MOVB $0, LOCAL_RETVALID(R15)
ADD $LOCAL_RETVALID, R15, R1
MOVD R1, 24(R15)
ADD $LOCAL_REGARGS, R15, R1
MOVD R1, 32(R15)
BL ·callMethod(SB) BL ·callMethod(SB)
ADD $LOCAL_REGARGS, R15, R10 // unspillArgs using R10
BL runtime·unspillArgs(SB)
RET RET

View file

@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style // Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file. // license that can be found in the LICENSE file.
//go:build !ppc64 && !ppc64le && !riscv64 //go:build !ppc64 && !ppc64le && !riscv64 && !s390x
package reflect package reflect

View file

@ -0,0 +1,30 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build s390x
#include "textflag.h"
// On s390x, the float32 becomes a float64
// when loaded in a register, different from
// other platforms. These functions are
// needed to ensure correct conversions on s390x.
// Convert float32->uint64
TEXT ·archFloat32ToReg(SB),NOSPLIT,$0-16
FMOVS val+0(FP), F1
FMOVD F1, ret+8(FP)
RET
// Convert uint64->float32
TEXT ·archFloat32FromReg(SB),NOSPLIT,$0-12
FMOVD reg+0(FP), F1
// Normally a float64->float32 conversion
// would need rounding, but that is not needed
// here since the uint64 was originally converted
// from float32, and should be avoided to
// preserve SNaN values.
FMOVS F1, ret+8(FP)
RET

View file

@ -0,0 +1,10 @@
// Copyright 2025 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build s390x
package reflect
func archFloat32FromReg(reg uint64) float32
func archFloat32ToReg(val float32) uint64

View file

@ -160,7 +160,7 @@ nocgo:
MOVD $0, 1(R0) MOVD $0, 1(R0)
RET RET
DATA runtime·mainPC+0(SB)/8,$runtime·main(SB) DATA runtime·mainPC+0(SB)/8,$runtime·main<ABIInternal>(SB)
GLOBL runtime·mainPC(SB),RODATA,$8 GLOBL runtime·mainPC(SB),RODATA,$8
TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·breakpoint(SB),NOSPLIT|NOFRAME,$0-0
@ -205,25 +205,29 @@ TEXT gogo<>(SB), NOSPLIT|NOFRAME, $0
// Switch to m->g0's stack, call fn(g). // Switch to m->g0's stack, call fn(g).
// Fn must never return. It should gogo(&g->sched) // Fn must never return. It should gogo(&g->sched)
// to keep running g. // to keep running g.
TEXT runtime·mcall(SB), NOSPLIT, $-8-8 TEXT runtime·mcall<ABIInternal>(SB), NOSPLIT, $-8-8
#ifdef GOEXPERIMENT_regabiargs
MOVD R2, R12 // context
#else
MOVD fn+0(FP), R12 // context
#endif
// Save caller state in g->sched // Save caller state in g->sched
MOVD R15, (g_sched+gobuf_sp)(g) MOVD R15, (g_sched+gobuf_sp)(g)
MOVD LR, (g_sched+gobuf_pc)(g) MOVD LR, (g_sched+gobuf_pc)(g)
MOVD $0, (g_sched+gobuf_lr)(g) MOVD $0, (g_sched+gobuf_lr)(g)
// Switch to m->g0 & its stack, call fn. // Switch to m->g0 & its stack, call fn.
MOVD g, R3 MOVD g, R2
MOVD g_m(g), R8 MOVD g_m(g), R4
MOVD m_g0(R8), g MOVD m_g0(R4), g
BL runtime·save_g(SB) BL runtime·save_g(SB)
CMP g, R3 CMP g, R2
BNE 2(PC) BNE 2(PC)
BR runtime·badmcall(SB) BR runtime·badmcall(SB)
MOVD fn+0(FP), R12 // context
MOVD 0(R12), R4 // code pointer MOVD 0(R12), R4 // code pointer
MOVD (g_sched+gobuf_sp)(g), R15 // sp = m->g0->sched.sp MOVD (g_sched+gobuf_sp)(g), R15 // sp = m->g0->sched.sp
SUB $16, R15 SUB $16, R15
MOVD R3, 8(R15) MOVD R2, 8(R15)
MOVD $0, 0(R15) MOVD $0, 0(R15)
BL (R4) BL (R4)
BR runtime·badmcall2(SB) BR runtime·badmcall2(SB)
@ -292,18 +296,18 @@ noswitch:
// func switchToCrashStack0(fn func()) // func switchToCrashStack0(fn func())
TEXT runtime·switchToCrashStack0<ABIInternal>(SB), NOSPLIT, $0-8 TEXT runtime·switchToCrashStack0<ABIInternal>(SB), NOSPLIT, $0-8
MOVD fn+0(FP), R12 // context MOVD R2, R12 // context
MOVD g_m(g), R4 // curm MOVD g_m(g), R2 // curm
// set g to gcrash // set g to gcrash
MOVD $runtime·gcrash(SB), g // g = &gcrash MOVD $runtime·gcrash(SB), g // g = &gcrash
BL runtime·save_g(SB) BL runtime·save_g(SB)
MOVD R4, g_m(g) // g.m = curm MOVD R2, g_m(g) // g.m = curm
MOVD g, m_g0(R4) // curm.g0 = g MOVD g, m_g0(R2) // curm.g0 = g
// switch to crashstack // switch to crashstack
MOVD (g_stack+stack_hi)(g), R4 MOVD (g_stack+stack_hi)(g), R2
ADD $(-4*8), R4, R15 ADD $(-4*8), R2, R15
// call target function // call target function
MOVD 0(R12), R3 // code pointer MOVD 0(R12), R3 // code pointer
@ -446,10 +450,14 @@ tailArgs: /* copy remaining bytes */ \
EXRL $callfnMVC<>(SB), R5; \ EXRL $callfnMVC<>(SB), R5; \
callFunction: \ callFunction: \
MOVD f+8(FP), R12; \ MOVD f+8(FP), R12; \
MOVD (R12), R8; \ MOVD regArgs+40(FP), R10; \
BL ·unspillArgs(SB); \
MOVD (R12), R10; \
PCDATA $PCDATA_StackMapIndex, $0; \ PCDATA $PCDATA_StackMapIndex, $0; \
BL (R8); \ BL (R10); \
/* copy return values back */ \ /* copy return values back */ \
MOVD regArgs+40(FP), R10; \
BL ·spillArgs(SB); \
MOVD stackArgsType+0(FP), R7; \ MOVD stackArgsType+0(FP), R7; \
MOVD stackArgs+16(FP), R6; \ MOVD stackArgs+16(FP), R6; \
MOVWZ stackArgsSize+24(FP), R5; \ MOVWZ stackArgsSize+24(FP), R5; \
@ -466,11 +474,12 @@ callFunction: \
// to reflectcallmove. It does not follow the Go ABI; it expects its // to reflectcallmove. It does not follow the Go ABI; it expects its
// arguments in registers. // arguments in registers.
TEXT callRet<>(SB), NOSPLIT, $40-0 TEXT callRet<>(SB), NOSPLIT, $40-0
NO_LOCAL_POINTERS;
MOVD R7, 8(R15) MOVD R7, 8(R15)
MOVD R6, 16(R15) MOVD R6, 16(R15)
MOVD R4, 24(R15) MOVD R4, 24(R15)
MOVD R5, 32(R15) MOVD R5, 32(R15)
MOVD $0, 40(R15) MOVD R10, 40(R15)
BL runtime·reflectcallmove(SB) BL runtime·reflectcallmove(SB)
RET RET
@ -754,15 +763,80 @@ TEXT runtime·cputicks(SB),NOSPLIT,$0-8
MOVD R3, ret+0(FP) MOVD R3, ret+0(FP)
RET RET
#ifdef GOEXPERIMENT_regabiargs
// spillArgs stores return values from registers to a *internal/abi.RegArgs in R10.
TEXT runtime·spillArgs(SB),NOSPLIT,$0-0
MOVD R2, 0(R10)
MOVD R3, 8(R10)
MOVD R4, 16(R10)
MOVD R5, 24(R10)
MOVD R6, 32(R10)
MOVD R7, 40(R10)
MOVD R8, 48(R10)
MOVD R9, 56(R10)
FMOVD F0, 64(R10)
FMOVD F1, 72(R10)
FMOVD F2, 80(R10)
FMOVD F3, 88(R10)
FMOVD F4, 96(R10)
FMOVD F5, 104(R10)
FMOVD F6, 112(R10)
FMOVD F7, 120(R10)
FMOVD F8, 128(R10)
FMOVD F9, 136(R10)
FMOVD F10, 144(R10)
FMOVD F11, 152(R10)
FMOVD F12, 160(R10)
FMOVD F13, 168(R10)
FMOVD F14, 176(R10)
FMOVD F15, 184(R10)
RET
// unspillArgs loads args into registers from a *internal/abi.RegArgs in R10.
TEXT runtime·unspillArgs(SB),NOSPLIT,$0-0
MOVD 0(R10), R2
MOVD 8(R10), R3
MOVD 16(R10), R4
MOVD 24(R10), R5
MOVD 32(R10), R6
MOVD 40(R10), R7
MOVD 48(R10), R8
MOVD 56(R10), R9
FMOVD 64(R10), F0
FMOVD 72(R10), F1
FMOVD 80(R10), F2
FMOVD 88(R10), F3
FMOVD 96(R10), F4
FMOVD 104(R10), F5
FMOVD 112(R10), F6
FMOVD 120(R10), F7
FMOVD 128(R10), F8
FMOVD 136(R10), F9
FMOVD 144(R10), F10
FMOVD 152(R10), F11
FMOVD 160(R10), F12
FMOVD 168(R10), F13
FMOVD 176(R10), F14
FMOVD 184(R10), F15
RET
#else
TEXT runtime·spillArgs(SB),NOSPLIT,$0-0
RET
TEXT runtime·unspillArgs(SB),NOSPLIT,$0-0
RET
#endif
// AES hashing not implemented for s390x // AES hashing not implemented for s390x
TEXT runtime·memhash(SB),NOSPLIT|NOFRAME,$0-32 TEXT runtime·memhash<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-32
JMP runtime·memhashFallback(SB) JMP runtime·memhashFallback<ABIInternal>(SB)
TEXT runtime·strhash(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·strhash<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
JMP runtime·strhashFallback(SB) JMP runtime·strhashFallback<ABIInternal>(SB)
TEXT runtime·memhash32(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash32<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
JMP runtime·memhash32Fallback(SB) JMP runtime·memhash32Fallback<ABIInternal>(SB)
TEXT runtime·memhash64(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memhash64<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
JMP runtime·memhash64Fallback(SB) JMP runtime·memhash64Fallback<ABIInternal>(SB)
// Called from cgo wrappers, this function returns g->m->curg.stack.hi. // Called from cgo wrappers, this function returns g->m->curg.stack.hi.
// Must obey the gcc calling convention. // Must obey the gcc calling convention.
@ -902,8 +976,7 @@ TEXT runtime·panicBounds<ABIInternal>(SB),NOSPLIT,$144-0
// skip R14 aka LR @ 136 // skip R14 aka LR @ 136
// skip R15 aka SP @ 144 // skip R15 aka SP @ 144
MOVD R14, 8(R15) // PC immediately after call to panicBounds MOVD R14, R2 // PC immediately after call to panicBounds
ADD $24, R15, R0 // pointer to save area ADD $24, R15, R3 // pointer to save area
MOVD R0, 16(R15)
CALL runtime·panicBounds64<ABIInternal>(SB) CALL runtime·panicBounds64<ABIInternal>(SB)
RET RET

View file

@ -796,6 +796,9 @@ func cgoCheckResult(val any) {
ep := efaceOf(&val) ep := efaceOf(&val)
t := ep._type t := ep._type
if t == nil {
return
}
cgoCheckArg(t, ep.data, !t.IsDirectIface(), false, cgoResultFail) cgoCheckArg(t, ep.data, !t.IsDirectIface(), false, cgoResultFail)
} }

View file

@ -7,10 +7,14 @@
// See memclrNoHeapPointers Go doc for important implementation constraints. // See memclrNoHeapPointers Go doc for important implementation constraints.
// func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr) // func memclrNoHeapPointers(ptr unsafe.Pointer, n uintptr)
TEXT runtime·memclrNoHeapPointers(SB),NOSPLIT|NOFRAME,$0-16 TEXT runtime·memclrNoHeapPointers<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-16
#ifndef GOEXPERIMENT_regabiargs
MOVD ptr+0(FP), R4 MOVD ptr+0(FP), R4
MOVD n+8(FP), R5 MOVD n+8(FP), R5
#else
MOVD R2, R4
MOVD R3, R5
#endif
CMPBGE R5, $32, clearge32 CMPBGE R5, $32, clearge32
start: start:

View file

@ -7,10 +7,16 @@
// See memmove Go doc for important implementation constraints. // See memmove Go doc for important implementation constraints.
// func memmove(to, from unsafe.Pointer, n uintptr) // func memmove(to, from unsafe.Pointer, n uintptr)
TEXT runtime·memmove(SB),NOSPLIT|NOFRAME,$0-24 TEXT runtime·memmove<ABIInternal>(SB),NOSPLIT|NOFRAME,$0-24
#ifndef GOEXPERIMENT_regabiargs
MOVD to+0(FP), R6 MOVD to+0(FP), R6
MOVD from+8(FP), R4 MOVD from+8(FP), R4
MOVD n+16(FP), R5 MOVD n+16(FP), R5
#else
MOVD R4, R5
MOVD R3, R4
MOVD R2, R6
#endif
CMPBEQ R6, R4, done CMPBEQ R6, R4, done

View file

@ -25,10 +25,14 @@
// func runtime·raceread(addr uintptr) // func runtime·raceread(addr uintptr)
// Called from instrumented code. // Called from instrumented code.
TEXT runtime·raceread(SB), NOSPLIT, $0-8 TEXT runtime·raceread<ABIInternal>(SB), NOSPLIT, $0-8
// void __tsan_read(ThreadState *thr, void *addr, void *pc); // void __tsan_read(ThreadState *thr, void *addr, void *pc);
MOVD $__tsan_read(SB), R1 MOVD $__tsan_read(SB), R1
#ifndef GOEXPERIMENT_regabiargs
MOVD addr+0(FP), R3 MOVD addr+0(FP), R3
#else
MOVD R2, R3
#endif
MOVD R14, R4 MOVD R14, R4
JMP racecalladdr<>(SB) JMP racecalladdr<>(SB)
@ -46,10 +50,14 @@ TEXT runtime·racereadpc(SB), NOSPLIT, $0-24
// func runtime·racewrite(addr uintptr) // func runtime·racewrite(addr uintptr)
// Called from instrumented code. // Called from instrumented code.
TEXT runtime·racewrite(SB), NOSPLIT, $0-8 TEXT runtime·racewrite<ABIInternal>(SB), NOSPLIT, $0-8
// void __tsan_write(ThreadState *thr, void *addr, void *pc); // void __tsan_write(ThreadState *thr, void *addr, void *pc);
MOVD $__tsan_write(SB), R1 MOVD $__tsan_write(SB), R1
#ifndef GOEXPERIMENT_regabiargs
MOVD addr+0(FP), R3 MOVD addr+0(FP), R3
#else
MOVD R2, R3
#endif
MOVD R14, R4 MOVD R14, R4
JMP racecalladdr<>(SB) JMP racecalladdr<>(SB)
@ -67,10 +75,15 @@ TEXT runtime·racewritepc(SB), NOSPLIT, $0-24
// func runtime·racereadrange(addr, size uintptr) // func runtime·racereadrange(addr, size uintptr)
// Called from instrumented code. // Called from instrumented code.
TEXT runtime·racereadrange(SB), NOSPLIT, $0-16 TEXT runtime·racereadrange<ABIInternal>(SB), NOSPLIT, $0-16
// void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc); // void __tsan_read_range(ThreadState *thr, void *addr, uintptr size, void *pc);
MOVD $__tsan_read_range(SB), R1 MOVD $__tsan_read_range(SB), R1
#ifndef GOEXPERIMENT_regabiargs
LMG addr+0(FP), R3, R4 LMG addr+0(FP), R3, R4
#else
MOVD R3, R4
MOVD R2, R3
#endif
MOVD R14, R5 MOVD R14, R5
JMP racecalladdr<>(SB) JMP racecalladdr<>(SB)
@ -91,10 +104,15 @@ TEXT runtime·racereadrangepc1(SB), NOSPLIT, $0-24
// func runtime·racewriterange(addr, size uintptr) // func runtime·racewriterange(addr, size uintptr)
// Called from instrumented code. // Called from instrumented code.
TEXT runtime·racewriterange(SB), NOSPLIT, $0-16 TEXT runtime·racewriterange<ABIInternal>(SB), NOSPLIT, $0-16
// void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc); // void __tsan_write_range(ThreadState *thr, void *addr, uintptr size, void *pc);
MOVD $__tsan_write_range(SB), R1 MOVD $__tsan_write_range(SB), R1
#ifndef GOEXPERIMENT_regabiargs
LMG addr+0(FP), R3, R4 LMG addr+0(FP), R3, R4
#else
MOVD R3, R4
MOVD R2, R3
#endif
MOVD R14, R5 MOVD R14, R5
JMP racecalladdr<>(SB) JMP racecalladdr<>(SB)

View file

@ -39,7 +39,7 @@ func gotraceback() (level int32, all, crash bool) {
gp := getg() gp := getg()
t := atomic.Load(&traceback_cache) t := atomic.Load(&traceback_cache)
crash = t&tracebackCrash != 0 crash = t&tracebackCrash != 0
all = gp.m.throwing >= throwTypeUser || t&tracebackAll != 0 all = gp.m.throwing > throwTypeUser || t&tracebackAll != 0
if gp.m.traceback != 0 { if gp.m.traceback != 0 {
level = int32(gp.m.traceback) level = int32(gp.m.traceback)
} else if gp.m.throwing >= throwTypeRuntime { } else if gp.m.throwing >= throwTypeRuntime {

View file

@ -234,7 +234,7 @@ func (frame *stkframe) getStackMap(debug bool) (locals, args bitvector, objs []s
} }
// stack objects. // stack objects.
if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "loong64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64") && if (GOARCH == "amd64" || GOARCH == "arm64" || GOARCH == "loong64" || GOARCH == "ppc64" || GOARCH == "ppc64le" || GOARCH == "riscv64" || GOARCH == "s390x") &&
unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect { unsafe.Sizeof(abi.RegArgs{}) > 0 && isReflect {
// For reflect.makeFuncStub and reflect.methodValueCall, // For reflect.makeFuncStub and reflect.methodValueCall,
// we need to fake the stack object record. // we need to fake the stack object record.

View file

@ -8,6 +8,13 @@ package runtime
func load_g() func load_g()
func save_g() func save_g()
// Used by reflectcall and the reflect package.
//
// Spills/loads arguments in registers to/from an internal/abi.RegArgs
// respectively. Does not follow the Go ABI.
func spillArgs()
func unspillArgs()
// getfp returns the frame pointer register of its caller or 0 if not implemented. // getfp returns the frame pointer register of its caller or 0 if not implemented.
// TODO: Make this a compiler intrinsic // TODO: Make this a compiler intrinsic
func getfp() uintptr { return 0 } func getfp() uintptr { return 0 }

View file

@ -19,7 +19,7 @@
// //
// If !iscgo, this is a no-op. // If !iscgo, this is a no-op.
// //
// NOTE: setg_gcc<> assume this clobbers only R10 and R11. // NOTE: setg_gcc<> and mcall assume this clobbers only R10 and R11.
TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0 TEXT runtime·save_g(SB),NOSPLIT|NOFRAME,$0-0
MOVB runtime·iscgo(SB), R10 MOVB runtime·iscgo(SB), R10
CMPBEQ R10, $0, nocgo CMPBEQ R10, $0, nocgo