cmd/compile: move s390x over to new bounds check strategy

Change-Id: I86ed1a60165b729bb88a8a418da0ea1b59b3dc10
Reviewed-on: https://go-review.googlesource.com/c/go/+/682499
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Mauri de Souza Meneguzzo <mauri870@gmail.com>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Michael Munday <mikemndy@gmail.com>
Reviewed-by: Mark Freeman <mark@golang.org>
This commit is contained in:
Keith Randall 2025-06-18 16:55:06 -07:00
parent 95693816a5
commit 7a1679d7ae
6 changed files with 225 additions and 157 deletions

View file

@ -15,6 +15,7 @@ import (
"cmd/compile/internal/types" "cmd/compile/internal/types"
"cmd/internal/obj" "cmd/internal/obj"
"cmd/internal/obj/s390x" "cmd/internal/obj/s390x"
"internal/abi"
) )
// ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags. // ssaMarkMoves marks any MOVXconst ops that need to avoid clobbering flags.
@ -573,12 +574,92 @@ func ssaGenValue(s *ssagen.State, v *ssa.Value) {
p.To.Name = obj.NAME_EXTERN p.To.Name = obj.NAME_EXTERN
// AuxInt encodes how many buffer entries we need. // AuxInt encodes how many buffer entries we need.
p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1] p.To.Sym = ir.Syms.GCWriteBarrier[v.AuxInt-1]
case ssa.OpS390XLoweredPanicBoundsA, ssa.OpS390XLoweredPanicBoundsB, ssa.OpS390XLoweredPanicBoundsC:
p := s.Prog(obj.ACALL) case ssa.OpS390XLoweredPanicBoundsRR, ssa.OpS390XLoweredPanicBoundsRC, ssa.OpS390XLoweredPanicBoundsCR, ssa.OpS390XLoweredPanicBoundsCC:
// Compute the constant we put in the PCData entry for this call.
code, signed := ssa.BoundsKind(v.AuxInt).Code()
xIsReg := false
yIsReg := false
xVal := 0
yVal := 0
switch v.Op {
case ssa.OpS390XLoweredPanicBoundsRR:
xIsReg = true
xVal = int(v.Args[0].Reg() - s390x.REG_R0)
yIsReg = true
yVal = int(v.Args[1].Reg() - s390x.REG_R0)
case ssa.OpS390XLoweredPanicBoundsRC:
xIsReg = true
xVal = int(v.Args[0].Reg() - s390x.REG_R0)
c := v.Aux.(ssa.PanicBoundsC).C
if c >= 0 && c <= abi.BoundsMaxConst {
yVal = int(c)
} else {
// Move constant to a register
yIsReg = true
if yVal == xVal {
yVal = 1
}
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = s390x.REG_R0 + int16(yVal)
}
case ssa.OpS390XLoweredPanicBoundsCR:
yIsReg = true
yVal := int(v.Args[0].Reg() - s390x.REG_R0)
c := v.Aux.(ssa.PanicBoundsC).C
if c >= 0 && c <= abi.BoundsMaxConst {
xVal = int(c)
} else {
// Move constant to a register
if xVal == yVal {
xVal = 1
}
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = s390x.REG_R0 + int16(xVal)
}
case ssa.OpS390XLoweredPanicBoundsCC:
c := v.Aux.(ssa.PanicBoundsCC).Cx
if c >= 0 && c <= abi.BoundsMaxConst {
xVal = int(c)
} else {
// Move constant to a register
xIsReg = true
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = s390x.REG_R0 + int16(xVal)
}
c = v.Aux.(ssa.PanicBoundsCC).Cy
if c >= 0 && c <= abi.BoundsMaxConst {
yVal = int(c)
} else {
// Move constant to a register
yIsReg = true
yVal = 1
p := s.Prog(s390x.AMOVD)
p.From.Type = obj.TYPE_CONST
p.From.Offset = c
p.To.Type = obj.TYPE_REG
p.To.Reg = s390x.REG_R0 + int16(yVal)
}
}
c := abi.BoundsEncode(code, signed, xIsReg, yIsReg, xVal, yVal)
p := s.Prog(obj.APCDATA)
p.From.SetConst(abi.PCDATA_PanicBounds)
p.To.SetConst(int64(c))
p = s.Prog(obj.ACALL)
p.To.Type = obj.TYPE_MEM p.To.Type = obj.TYPE_MEM
p.To.Name = obj.NAME_EXTERN p.To.Name = obj.NAME_EXTERN
p.To.Sym = ssagen.BoundsCheckFunc[v.AuxInt] p.To.Sym = ir.Syms.PanicBounds
s.UseArgs(16) // space used in callee args area by assembly stubs
case ssa.OpS390XFLOGR, ssa.OpS390XPOPCNT, case ssa.OpS390XFLOGR, ssa.OpS390XPOPCNT,
ssa.OpS390XNEG, ssa.OpS390XNEGW, ssa.OpS390XNEG, ssa.OpS390XNEGW,
ssa.OpS390XMOVWBR, ssa.OpS390XMOVDBR: ssa.OpS390XMOVWBR, ssa.OpS390XMOVDBR:

View file

@ -458,9 +458,11 @@
// Write barrier. // Write barrier.
(WB ...) => (LoweredWB ...) (WB ...) => (LoweredWB ...)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 0 => (LoweredPanicBoundsA [kind] x y mem) (PanicBounds ...) => (LoweredPanicBoundsRR ...)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 1 => (LoweredPanicBoundsB [kind] x y mem) (LoweredPanicBoundsRR [kind] x (MOVDconst [c]) mem) => (LoweredPanicBoundsRC [kind] x {PanicBoundsC{C:c}} mem)
(PanicBounds [kind] x y mem) && boundsABI(kind) == 2 => (LoweredPanicBoundsC [kind] x y mem) (LoweredPanicBoundsRR [kind] (MOVDconst [c]) y mem) => (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:c}} y mem)
(LoweredPanicBoundsRC [kind] {p} (MOVDconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:c, Cy:p.C}} mem)
(LoweredPanicBoundsCR [kind] {p} (MOVDconst [c]) mem) => (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:p.C, Cy:c}} mem)
// *************************** // ***************************
// Above: lowering rules // Above: lowering rules

View file

@ -114,6 +114,7 @@ func init() {
sb = buildReg("SB") sb = buildReg("SB")
r0 = buildReg("R0") r0 = buildReg("R0")
tmp = buildReg("R11") // R11 is used as a temporary in a small number of instructions. tmp = buildReg("R11") // R11 is used as a temporary in a small number of instructions.
lr = buildReg("R14")
// R10 is reserved by the assembler. // R10 is reserved by the assembler.
gp = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14") gp = buildReg("R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12 R14")
@ -518,12 +519,15 @@ func init() {
// Returns a pointer to a write barrier buffer in R9. // Returns a pointer to a write barrier buffer in R9.
{name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R14") | r1, outputs: []regMask{r9}}, clobberFlags: true, aux: "Int64"}, {name: "LoweredWB", argLength: 1, reg: regInfo{clobbers: (callerSave &^ gpg) | buildReg("R14") | r1, outputs: []regMask{r9}}, clobberFlags: true, aux: "Int64"},
// There are three of these functions so that they can have three different register inputs. // LoweredPanicBoundsRR takes x and y, two values that caused a bounds check to fail.
// When we check 0 <= c <= cap (A), then 0 <= b <= c (B), then 0 <= a <= b (C), we want the // the RC and CR versions are used when one of the arguments is a constant. CC is used
// default registers to match so we don't need to copy registers around unnecessarily. // when both are constant (normally both 0, as prove derives the fact that a [0] bounds
{name: "LoweredPanicBoundsA", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r2, r3}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go). // failure means the length must have also been 0).
{name: "LoweredPanicBoundsB", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r1, r2}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go). // AuxInt contains a report code (see PanicBounds in genericOps.go).
{name: "LoweredPanicBoundsC", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{r0, r1}}, typ: "Mem", call: true}, // arg0=idx, arg1=len, arg2=mem, returns memory. AuxInt contains report code (see PanicBounds in generic.go). {name: "LoweredPanicBoundsRR", argLength: 3, aux: "Int64", reg: regInfo{inputs: []regMask{gp &^ lr, gp &^ lr}}, typ: "Mem", call: true}, // arg0=x, arg1=y, arg2=mem, returns memory.
{name: "LoweredPanicBoundsRC", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp &^ lr}}, typ: "Mem", call: true}, // arg0=x, arg1=mem, returns memory.
{name: "LoweredPanicBoundsCR", argLength: 2, aux: "PanicBoundsC", reg: regInfo{inputs: []regMask{gp &^ lr}}, typ: "Mem", call: true}, // arg0=y, arg1=mem, returns memory.
{name: "LoweredPanicBoundsCC", argLength: 1, aux: "PanicBoundsCC", reg: regInfo{}, typ: "Mem", call: true}, // arg0=mem, returns memory.
// Constant condition code values. The condition code can be 0, 1, 2 or 3. // Constant condition code values. The condition code can be 0, 1, 2 or 3.
{name: "FlagEQ"}, // CC=0 (equal) {name: "FlagEQ"}, // CC=0 (equal)

View file

@ -2843,9 +2843,10 @@ const (
OpS390XLoweredRound32F OpS390XLoweredRound32F
OpS390XLoweredRound64F OpS390XLoweredRound64F
OpS390XLoweredWB OpS390XLoweredWB
OpS390XLoweredPanicBoundsA OpS390XLoweredPanicBoundsRR
OpS390XLoweredPanicBoundsB OpS390XLoweredPanicBoundsRC
OpS390XLoweredPanicBoundsC OpS390XLoweredPanicBoundsCR
OpS390XLoweredPanicBoundsCC
OpS390XFlagEQ OpS390XFlagEQ
OpS390XFlagLT OpS390XFlagLT
OpS390XFlagGT OpS390XFlagGT
@ -38525,41 +38526,46 @@ var opcodeTable = [...]opInfo{
}, },
}, },
{ {
name: "LoweredPanicBoundsA", name: "LoweredPanicBoundsRR",
auxType: auxInt64, auxType: auxInt64,
argLen: 3, argLen: 3,
call: true, call: true,
reg: regInfo{ reg: regInfo{
inputs: []inputInfo{ inputs: []inputInfo{
{0, 4}, // R2 {0, 7167}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12
{1, 8}, // R3 {1, 7167}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12
}, },
}, },
}, },
{ {
name: "LoweredPanicBoundsB", name: "LoweredPanicBoundsRC",
auxType: auxInt64, auxType: auxPanicBoundsC,
argLen: 3, argLen: 2,
call: true, call: true,
reg: regInfo{ reg: regInfo{
inputs: []inputInfo{ inputs: []inputInfo{
{0, 2}, // R1 {0, 7167}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12
{1, 4}, // R2
}, },
}, },
}, },
{ {
name: "LoweredPanicBoundsC", name: "LoweredPanicBoundsCR",
auxType: auxInt64, auxType: auxPanicBoundsC,
argLen: 3, argLen: 2,
call: true, call: true,
reg: regInfo{ reg: regInfo{
inputs: []inputInfo{ inputs: []inputInfo{
{0, 1}, // R0 {0, 7167}, // R0 R1 R2 R3 R4 R5 R6 R7 R8 R9 R11 R12
{1, 2}, // R1
}, },
}, },
}, },
{
name: "LoweredPanicBoundsCC",
auxType: auxPanicBoundsCC,
argLen: 1,
call: true,
reg: regInfo{},
},
{ {
name: "FlagEQ", name: "FlagEQ",
argLen: 0, argLen: 0,

View file

@ -477,7 +477,8 @@ func rewriteValueS390X(v *Value) bool {
v.Op = OpS390XORW v.Op = OpS390XORW
return true return true
case OpPanicBounds: case OpPanicBounds:
return rewriteValueS390X_OpPanicBounds(v) v.Op = OpS390XLoweredPanicBoundsRR
return true
case OpPopCount16: case OpPopCount16:
return rewriteValueS390X_OpPopCount16(v) return rewriteValueS390X_OpPopCount16(v)
case OpPopCount32: case OpPopCount32:
@ -644,6 +645,12 @@ func rewriteValueS390X(v *Value) bool {
return rewriteValueS390X_OpS390XLTDBR(v) return rewriteValueS390X_OpS390XLTDBR(v)
case OpS390XLTEBR: case OpS390XLTEBR:
return rewriteValueS390X_OpS390XLTEBR(v) return rewriteValueS390X_OpS390XLTEBR(v)
case OpS390XLoweredPanicBoundsCR:
return rewriteValueS390X_OpS390XLoweredPanicBoundsCR(v)
case OpS390XLoweredPanicBoundsRC:
return rewriteValueS390X_OpS390XLoweredPanicBoundsRC(v)
case OpS390XLoweredPanicBoundsRR:
return rewriteValueS390X_OpS390XLoweredPanicBoundsRR(v)
case OpS390XLoweredRound32F: case OpS390XLoweredRound32F:
return rewriteValueS390X_OpS390XLoweredRound32F(v) return rewriteValueS390X_OpS390XLoweredRound32F(v)
case OpS390XLoweredRound64F: case OpS390XLoweredRound64F:
@ -3971,60 +3978,6 @@ func rewriteValueS390X_OpOffPtr(v *Value) bool {
return true return true
} }
} }
func rewriteValueS390X_OpPanicBounds(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (PanicBounds [kind] x y mem)
// cond: boundsABI(kind) == 0
// result: (LoweredPanicBoundsA [kind] x y mem)
for {
kind := auxIntToInt64(v.AuxInt)
x := v_0
y := v_1
mem := v_2
if !(boundsABI(kind) == 0) {
break
}
v.reset(OpS390XLoweredPanicBoundsA)
v.AuxInt = int64ToAuxInt(kind)
v.AddArg3(x, y, mem)
return true
}
// match: (PanicBounds [kind] x y mem)
// cond: boundsABI(kind) == 1
// result: (LoweredPanicBoundsB [kind] x y mem)
for {
kind := auxIntToInt64(v.AuxInt)
x := v_0
y := v_1
mem := v_2
if !(boundsABI(kind) == 1) {
break
}
v.reset(OpS390XLoweredPanicBoundsB)
v.AuxInt = int64ToAuxInt(kind)
v.AddArg3(x, y, mem)
return true
}
// match: (PanicBounds [kind] x y mem)
// cond: boundsABI(kind) == 2
// result: (LoweredPanicBoundsC [kind] x y mem)
for {
kind := auxIntToInt64(v.AuxInt)
x := v_0
y := v_1
mem := v_2
if !(boundsABI(kind) == 2) {
break
}
v.reset(OpS390XLoweredPanicBoundsC)
v.AuxInt = int64ToAuxInt(kind)
v.AddArg3(x, y, mem)
return true
}
return false
}
func rewriteValueS390X_OpPopCount16(v *Value) bool { func rewriteValueS390X_OpPopCount16(v *Value) bool {
v_0 := v.Args[0] v_0 := v.Args[0]
b := v.Block b := v.Block
@ -8147,6 +8100,86 @@ func rewriteValueS390X_OpS390XLTEBR(v *Value) bool {
} }
return false return false
} }
func rewriteValueS390X_OpS390XLoweredPanicBoundsCR(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (LoweredPanicBoundsCR [kind] {p} (MOVDconst [c]) mem)
// result: (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:p.C, Cy:c}} mem)
for {
kind := auxIntToInt64(v.AuxInt)
p := auxToPanicBoundsC(v.Aux)
if v_0.Op != OpS390XMOVDconst {
break
}
c := auxIntToInt64(v_0.AuxInt)
mem := v_1
v.reset(OpS390XLoweredPanicBoundsCC)
v.AuxInt = int64ToAuxInt(kind)
v.Aux = panicBoundsCCToAux(PanicBoundsCC{Cx: p.C, Cy: c})
v.AddArg(mem)
return true
}
return false
}
func rewriteValueS390X_OpS390XLoweredPanicBoundsRC(v *Value) bool {
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (LoweredPanicBoundsRC [kind] {p} (MOVDconst [c]) mem)
// result: (LoweredPanicBoundsCC [kind] {PanicBoundsCC{Cx:c, Cy:p.C}} mem)
for {
kind := auxIntToInt64(v.AuxInt)
p := auxToPanicBoundsC(v.Aux)
if v_0.Op != OpS390XMOVDconst {
break
}
c := auxIntToInt64(v_0.AuxInt)
mem := v_1
v.reset(OpS390XLoweredPanicBoundsCC)
v.AuxInt = int64ToAuxInt(kind)
v.Aux = panicBoundsCCToAux(PanicBoundsCC{Cx: c, Cy: p.C})
v.AddArg(mem)
return true
}
return false
}
func rewriteValueS390X_OpS390XLoweredPanicBoundsRR(v *Value) bool {
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
// match: (LoweredPanicBoundsRR [kind] x (MOVDconst [c]) mem)
// result: (LoweredPanicBoundsRC [kind] x {PanicBoundsC{C:c}} mem)
for {
kind := auxIntToInt64(v.AuxInt)
x := v_0
if v_1.Op != OpS390XMOVDconst {
break
}
c := auxIntToInt64(v_1.AuxInt)
mem := v_2
v.reset(OpS390XLoweredPanicBoundsRC)
v.AuxInt = int64ToAuxInt(kind)
v.Aux = panicBoundsCToAux(PanicBoundsC{C: c})
v.AddArg2(x, mem)
return true
}
// match: (LoweredPanicBoundsRR [kind] (MOVDconst [c]) y mem)
// result: (LoweredPanicBoundsCR [kind] {PanicBoundsC{C:c}} y mem)
for {
kind := auxIntToInt64(v.AuxInt)
if v_0.Op != OpS390XMOVDconst {
break
}
c := auxIntToInt64(v_0.AuxInt)
y := v_1
mem := v_2
v.reset(OpS390XLoweredPanicBoundsCR)
v.AuxInt = int64ToAuxInt(kind)
v.Aux = panicBoundsCToAux(PanicBoundsC{C: c})
v.AddArg2(y, mem)
return true
}
return false
}
func rewriteValueS390X_OpS390XLoweredRound32F(v *Value) bool { func rewriteValueS390X_OpS390XLoweredRound32F(v *Value) bool {
v_0 := v.Args[0] v_0 := v.Args[0]
// match: (LoweredRound32F x:(FMOVSconst)) // match: (LoweredRound32F x:(FMOVSconst))

View file

@ -892,76 +892,18 @@ TEXT runtime·gcWriteBarrier8<ABIInternal>(SB),NOSPLIT,$0
MOVD $64, R9 MOVD $64, R9
JMP gcWriteBarrier<>(SB) JMP gcWriteBarrier<>(SB)
// Note: these functions use a special calling convention to save generated code space. TEXT runtime·panicBounds<ABIInternal>(SB),NOSPLIT,$144-0
// Arguments are passed in registers, but the space for those arguments are allocated NO_LOCAL_POINTERS
// in the caller's stack frame. These stubs write the args into that stack space and // Save all 16 int registers that could have an index in them.
// then tail call to the corresponding runtime handler. // They may be pointers, but if they are they are dead.
// The tail call makes these stubs disappear in backtraces. STMG R0, R12, 24(R15)
TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 // Note that R10 @ 104 is not needed, it is an assembler temp
MOVD R0, x+0(FP) // skip R13 aka G @ 128
MOVD R1, y+8(FP) // skip R14 aka LR @ 136
JMP runtime·goPanicIndex(SB) // skip R15 aka SP @ 144
TEXT runtime·panicIndexU(SB),NOSPLIT,$0-16
MOVD R0, x+0(FP) MOVD R14, 8(R15) // PC immediately after call to panicBounds
MOVD R1, y+8(FP) ADD $24, R15, R0 // pointer to save area
JMP runtime·goPanicIndexU(SB) MOVD R0, 16(R15)
TEXT runtime·panicSliceAlen(SB),NOSPLIT,$0-16 CALL runtime·panicBounds64<ABIInternal>(SB)
MOVD R1, x+0(FP) RET
MOVD R2, y+8(FP)
JMP runtime·goPanicSliceAlen(SB)
TEXT runtime·panicSliceAlenU(SB),NOSPLIT,$0-16
MOVD R1, x+0(FP)
MOVD R2, y+8(FP)
JMP runtime·goPanicSliceAlenU(SB)
TEXT runtime·panicSliceAcap(SB),NOSPLIT,$0-16
MOVD R1, x+0(FP)
MOVD R2, y+8(FP)
JMP runtime·goPanicSliceAcap(SB)
TEXT runtime·panicSliceAcapU(SB),NOSPLIT,$0-16
MOVD R1, x+0(FP)
MOVD R2, y+8(FP)
JMP runtime·goPanicSliceAcapU(SB)
TEXT runtime·panicSliceB(SB),NOSPLIT,$0-16
MOVD R0, x+0(FP)
MOVD R1, y+8(FP)
JMP runtime·goPanicSliceB(SB)
TEXT runtime·panicSliceBU(SB),NOSPLIT,$0-16
MOVD R0, x+0(FP)
MOVD R1, y+8(FP)
JMP runtime·goPanicSliceBU(SB)
TEXT runtime·panicSlice3Alen(SB),NOSPLIT,$0-16
MOVD R2, x+0(FP)
MOVD R3, y+8(FP)
JMP runtime·goPanicSlice3Alen(SB)
TEXT runtime·panicSlice3AlenU(SB),NOSPLIT,$0-16
MOVD R2, x+0(FP)
MOVD R3, y+8(FP)
JMP runtime·goPanicSlice3AlenU(SB)
TEXT runtime·panicSlice3Acap(SB),NOSPLIT,$0-16
MOVD R2, x+0(FP)
MOVD R3, y+8(FP)
JMP runtime·goPanicSlice3Acap(SB)
TEXT runtime·panicSlice3AcapU(SB),NOSPLIT,$0-16
MOVD R2, x+0(FP)
MOVD R3, y+8(FP)
JMP runtime·goPanicSlice3AcapU(SB)
TEXT runtime·panicSlice3B(SB),NOSPLIT,$0-16
MOVD R1, x+0(FP)
MOVD R2, y+8(FP)
JMP runtime·goPanicSlice3B(SB)
TEXT runtime·panicSlice3BU(SB),NOSPLIT,$0-16
MOVD R1, x+0(FP)
MOVD R2, y+8(FP)
JMP runtime·goPanicSlice3BU(SB)
TEXT runtime·panicSlice3C(SB),NOSPLIT,$0-16
MOVD R0, x+0(FP)
MOVD R1, y+8(FP)
JMP runtime·goPanicSlice3C(SB)
TEXT runtime·panicSlice3CU(SB),NOSPLIT,$0-16
MOVD R0, x+0(FP)
MOVD R1, y+8(FP)
JMP runtime·goPanicSlice3CU(SB)
TEXT runtime·panicSliceConvert(SB),NOSPLIT,$0-16
MOVD R2, x+0(FP)
MOVD R3, y+8(FP)
JMP runtime·goPanicSliceConvert(SB)