Skip to content

Commit 2ff746d

Browse files
committed
runtime: add async preemption support on ARM
This CL adds support of call injection and async preemption on ARM. Injected call, like sigpanic, has special frame layout. Teach traceback to handle it. Change-Id: I887e90134fbf8a676b73c26321c50b3c4762dba4 Reviewed-on: https://go-review.googlesource.com/c/go/+/202338 Run-TryBot: Cherry Zhang <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 376c4ce commit 2ff746d

File tree

6 files changed

+153
-16
lines changed

6 files changed

+153
-16
lines changed

src/cmd/compile/internal/ssa/gen/ARMOps.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,8 @@ var regNamesARM = []string{
6060
"F14",
6161
"F15", // tmp
6262

63+
// If you add registers, update asyncPreempt in runtime.
64+
6365
// pseudo-registers
6466
"SB",
6567
}

src/runtime/mkpreempt.go

Lines changed: 57 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ var out io.Writer
7878
var arches = map[string]func(){
7979
"386": gen386,
8080
"amd64": genAMD64,
81-
"arm": notImplemented,
81+
"arm": genARM,
8282
"arm64": notImplemented,
8383
"mips64x": notImplemented,
8484
"mipsx": notImplemented,
@@ -133,9 +133,14 @@ func p(f string, args ...interface{}) {
133133
fmt.Fprintf(out, "\t%s\n", strings.Replace(fmted, "\n", "\n\t", -1))
134134
}
135135

136+
func label(l string) {
137+
fmt.Fprintf(out, "%s\n", l)
138+
}
139+
136140
type layout struct {
137141
stack int
138142
regs []regPos
143+
sp string // stack pointer register
139144
}
140145

141146
type regPos struct {
@@ -165,7 +170,7 @@ func (l *layout) save() {
165170
if reg.save != "" {
166171
p(reg.save, reg.pos)
167172
} else {
168-
p("%s %s, %d(SP)", reg.op, reg.reg, reg.pos)
173+
p("%s %s, %d(%s)", reg.op, reg.reg, reg.pos, l.sp)
169174
}
170175
}
171176
}
@@ -176,7 +181,7 @@ func (l *layout) restore() {
176181
if reg.restore != "" {
177182
p(reg.restore, reg.pos)
178183
} else {
179-
p("%s %d(SP), %s", reg.op, reg.pos, reg.reg)
184+
p("%s %d(%s), %s", reg.op, reg.pos, l.sp, reg.reg)
180185
}
181186
}
182187
}
@@ -185,7 +190,7 @@ func gen386() {
185190
p("PUSHFL")
186191

187192
// Save general purpose registers.
188-
var l layout
193+
var l = layout{sp: "SP"}
189194
for _, reg := range regNames386 {
190195
if reg == "SP" || strings.HasPrefix(reg, "X") {
191196
continue
@@ -200,7 +205,7 @@ func gen386() {
200205
108)
201206

202207
// Save SSE state only if supported.
203-
lSSE := layout{stack: l.stack}
208+
lSSE := layout{stack: l.stack, sp: "SP"}
204209
for i := 0; i < 8; i++ {
205210
lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
206211
}
@@ -210,11 +215,11 @@ func gen386() {
210215
l.save()
211216
p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse")
212217
lSSE.save()
213-
p("nosse:")
218+
label("nosse:")
214219
p("CALL ·asyncPreempt2(SB)")
215220
p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2")
216221
lSSE.restore()
217-
p("nosse2:")
222+
label("nosse2:")
218223
l.restore()
219224
p("ADJSP $%d", -lSSE.stack)
220225

@@ -224,7 +229,7 @@ func gen386() {
224229

225230
func genAMD64() {
226231
// Assign stack offsets.
227-
var l layout
232+
var l = layout{sp: "SP"}
228233
for _, reg := range regNamesAMD64 {
229234
if reg == "SP" || reg == "BP" {
230235
continue
@@ -255,6 +260,50 @@ func genAMD64() {
255260
p("RET")
256261
}
257262

263+
func genARM() {
264+
// Add integer registers R0-R12.
265+
// R13 (SP), R14 (LR), R15 (PC) are special and not saved here.
266+
var l = layout{sp: "R13", stack: 4} // add LR slot
267+
for i := 0; i <= 12; i++ {
268+
reg := fmt.Sprintf("R%d", i)
269+
if i == 10 {
270+
continue // R10 is g register, no need to save/restore
271+
}
272+
l.add("MOVW", reg, 4)
273+
}
274+
// Add flag register.
275+
l.addSpecial(
276+
"MOVW CPSR, R0\nMOVW R0, %d(R13)",
277+
"MOVW %d(R13), R0\nMOVW R0, CPSR",
278+
4)
279+
280+
// Add floating point registers F0-F15 and flag register.
281+
var lfp = layout{stack: l.stack, sp: "R13"}
282+
lfp.addSpecial(
283+
"MOVW FPCR, R0\nMOVW R0, %d(R13)",
284+
"MOVW %d(R13), R0\nMOVW R0, FPCR",
285+
4)
286+
for i := 0; i <= 15; i++ {
287+
reg := fmt.Sprintf("F%d", i)
288+
lfp.add("MOVD", reg, 8)
289+
}
290+
291+
p("MOVW.W R14, -%d(R13)", lfp.stack) // allocate frame, save LR
292+
l.save()
293+
p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp") // test goarm, and skip FP registers if goarm=5.
294+
lfp.save()
295+
label("nofp:")
296+
p("CALL ·asyncPreempt2(SB)")
297+
p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp2") // test goarm, and skip FP registers if goarm=5.
298+
lfp.restore()
299+
label("nofp2:")
300+
l.restore()
301+
302+
p("MOVW %d(R13), R14", lfp.stack) // sigctxt.pushCall pushes LR on stack, restore it
303+
p("MOVW.P %d(R13), R15", lfp.stack+4) // load PC, pop frame (including the space pushed by sigctxt.pushCall)
304+
p("UNDEF") // shouldn't get here
305+
}
306+
258307
func genWasm() {
259308
p("// No async preemption on wasm")
260309
p("UNDEF")

src/runtime/preempt_386.s

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
2626
MOVUPS X5, 216(SP)
2727
MOVUPS X6, 232(SP)
2828
MOVUPS X7, 248(SP)
29-
nosse:
29+
nosse:
3030
CALL ·asyncPreempt2(SB)
3131
CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1
3232
JNE nosse2
@@ -38,7 +38,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
3838
MOVUPS 168(SP), X2
3939
MOVUPS 152(SP), X1
4040
MOVUPS 136(SP), X0
41-
nosse2:
41+
nosse2:
4242
FRSTOR 28(SP)
4343
MOVL 24(SP), DI
4444
MOVL 20(SP), SI

src/runtime/preempt_arm.s

Lines changed: 77 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,80 @@
44
#include "textflag.h"
55

66
TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
7-
// Not implemented yet
8-
JMP ·abort(SB)
7+
MOVW.W R14, -188(R13)
8+
MOVW R0, 4(R13)
9+
MOVW R1, 8(R13)
10+
MOVW R2, 12(R13)
11+
MOVW R3, 16(R13)
12+
MOVW R4, 20(R13)
13+
MOVW R5, 24(R13)
14+
MOVW R6, 28(R13)
15+
MOVW R7, 32(R13)
16+
MOVW R8, 36(R13)
17+
MOVW R9, 40(R13)
18+
MOVW R11, 44(R13)
19+
MOVW R12, 48(R13)
20+
MOVW CPSR, R0
21+
MOVW R0, 52(R13)
22+
MOVB ·goarm(SB), R0
23+
CMP $6, R0
24+
BLT nofp
25+
MOVW FPCR, R0
26+
MOVW R0, 56(R13)
27+
MOVD F0, 60(R13)
28+
MOVD F1, 68(R13)
29+
MOVD F2, 76(R13)
30+
MOVD F3, 84(R13)
31+
MOVD F4, 92(R13)
32+
MOVD F5, 100(R13)
33+
MOVD F6, 108(R13)
34+
MOVD F7, 116(R13)
35+
MOVD F8, 124(R13)
36+
MOVD F9, 132(R13)
37+
MOVD F10, 140(R13)
38+
MOVD F11, 148(R13)
39+
MOVD F12, 156(R13)
40+
MOVD F13, 164(R13)
41+
MOVD F14, 172(R13)
42+
MOVD F15, 180(R13)
43+
nofp:
44+
CALL ·asyncPreempt2(SB)
45+
MOVB ·goarm(SB), R0
46+
CMP $6, R0
47+
BLT nofp2
48+
MOVD 180(R13), F15
49+
MOVD 172(R13), F14
50+
MOVD 164(R13), F13
51+
MOVD 156(R13), F12
52+
MOVD 148(R13), F11
53+
MOVD 140(R13), F10
54+
MOVD 132(R13), F9
55+
MOVD 124(R13), F8
56+
MOVD 116(R13), F7
57+
MOVD 108(R13), F6
58+
MOVD 100(R13), F5
59+
MOVD 92(R13), F4
60+
MOVD 84(R13), F3
61+
MOVD 76(R13), F2
62+
MOVD 68(R13), F1
63+
MOVD 60(R13), F0
64+
MOVW 56(R13), R0
65+
MOVW R0, FPCR
66+
nofp2:
67+
MOVW 52(R13), R0
68+
MOVW R0, CPSR
69+
MOVW 48(R13), R12
70+
MOVW 44(R13), R11
71+
MOVW 40(R13), R9
72+
MOVW 36(R13), R8
73+
MOVW 32(R13), R7
74+
MOVW 28(R13), R6
75+
MOVW 24(R13), R5
76+
MOVW 20(R13), R4
77+
MOVW 16(R13), R3
78+
MOVW 12(R13), R2
79+
MOVW 8(R13), R1
80+
MOVW 4(R13), R0
81+
MOVW 188(R13), R14
82+
MOVW.P 192(R13), R15
83+
UNDEF

src/runtime/signal_arm.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,18 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
6363
c.set_pc(uint32(funcPC(sigpanic)))
6464
}
6565

66-
const pushCallSupported = false
66+
const pushCallSupported = true
6767

6868
func (c *sigctxt) pushCall(targetPC uintptr) {
69-
throw("not implemented")
69+
// Push the LR to stack, as we'll clobber it in order to
70+
// push the call. The function being pushed is responsible
71+
// for restoring the LR and setting the SP back.
72+
// This extra slot is known to gentraceback.
73+
sp := c.sp() - 4
74+
c.set_sp(sp)
75+
*(*uint32)(unsafe.Pointer(uintptr(sp))) = c.lr()
76+
// Set up PC and LR to pretend the function being signaled
77+
// calls targetPC at the faulting PC.
78+
c.set_lr(c.pc())
79+
c.set_pc(uint32(targetPC))
7080
}

src/runtime/traceback.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -462,6 +462,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
462462
}
463463

464464
waspanic = f.funcID == funcID_sigpanic
465+
injectedCall := waspanic || f.funcID == funcID_asyncPreempt
465466

466467
// Do not unwind past the bottom of the stack.
467468
if !flr.valid() {
@@ -477,8 +478,8 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
477478
frame.argmap = nil
478479

479480
// On link register architectures, sighandler saves the LR on stack
480-
// before faking a call to sigpanic.
481-
if usesLR && waspanic {
481+
// before faking a call.
482+
if usesLR && injectedCall {
482483
x := *(*uintptr)(unsafe.Pointer(frame.sp))
483484
frame.sp += sys.MinFrameSize
484485
if GOARCH == "arm64" {

0 commit comments

Comments
 (0)