Skip to content

Commit 1b0b980

Browse files
committed
runtime: add async preemption support on ARM64
This CL adds support of call injection and async preemption on ARM64. There seems no way to return from the injected call without clobbering *any* register. So we have to clobber one, which is chosen to be REGTMP. Previous CLs have marked code sequences that use REGTMP async-nonpreemtible. Change-Id: Ieca4e3ba5557adf3d0f5d923bce5f1769b58e30b Reviewed-on: https://go-review.googlesource.com/c/go/+/203461 Run-TryBot: Cherry Zhang <[email protected]> TryBot-Result: Gobot Gobot <[email protected]> Reviewed-by: Keith Randall <[email protected]> Reviewed-by: Austin Clements <[email protected]>
1 parent 4736088 commit 1b0b980

File tree

4 files changed

+205
-5
lines changed

4 files changed

+205
-5
lines changed

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ var regNamesARM64 = []string{
9696
"F30",
9797
"F31",
9898

99+
// If you add registers, update asyncPreempt in runtime.
100+
99101
// pseudo-registers
100102
"SB",
101103
}

src/runtime/mkpreempt.go

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ var arches = map[string]func(){
7979
"386": gen386,
8080
"amd64": genAMD64,
8181
"arm": genARM,
82-
"arm64": notImplemented,
82+
"arm64": genARM64,
8383
"mips64x": notImplemented,
8484
"mipsx": notImplemented,
8585
"ppc64x": notImplemented,
@@ -304,6 +304,58 @@ func genARM() {
304304
p("UNDEF") // shouldn't get here
305305
}
306306

307+
func genARM64() {
308+
// Add integer registers R0-R26
309+
// R27 (REGTMP), R28 (g), R29 (FP), R30 (LR), R31 (SP) are special
310+
// and not saved here.
311+
var l = layout{sp: "RSP", stack: 8} // add slot to save PC of interrupted instruction
312+
for i := 0; i <= 26; i++ {
313+
if i == 18 {
314+
continue // R18 is not used, skip
315+
}
316+
reg := fmt.Sprintf("R%d", i)
317+
l.add("MOVD", reg, 8)
318+
}
319+
// Add flag registers.
320+
l.addSpecial(
321+
"MOVD NZCV, R0\nMOVD R0, %d(RSP)",
322+
"MOVD %d(RSP), R0\nMOVD R0, NZCV",
323+
8)
324+
l.addSpecial(
325+
"MOVD FPSR, R0\nMOVD R0, %d(RSP)",
326+
"MOVD %d(RSP), R0\nMOVD R0, FPSR",
327+
8)
328+
// TODO: FPCR? I don't think we'll change it, so no need to save.
329+
// Add floating point registers F0-F31.
330+
for i := 0; i <= 31; i++ {
331+
reg := fmt.Sprintf("F%d", i)
332+
l.add("FMOVD", reg, 8)
333+
}
334+
if l.stack%16 != 0 {
335+
l.stack += 8 // SP needs 16-byte alignment
336+
}
337+
338+
// allocate frame, save PC of interrupted instruction (in LR)
339+
p("MOVD R30, %d(RSP)", -l.stack)
340+
p("SUB $%d, RSP", l.stack)
341+
p("#ifdef GOOS_linux")
342+
p("MOVD R29, -8(RSP)") // save frame pointer (only used on Linux)
343+
p("SUB $8, RSP, R29") // set up new frame pointer
344+
p("#endif")
345+
346+
l.save()
347+
p("CALL ·asyncPreempt2(SB)")
348+
l.restore()
349+
350+
p("MOVD %d(RSP), R30", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it
351+
p("#ifdef GOOS_linux")
352+
p("MOVD -8(RSP), R29") // restore frame pointer
353+
p("#endif")
354+
p("MOVD (RSP), R27") // load PC to REGTMP
355+
p("ADD $%d, RSP", l.stack+16) // pop frame (including the space pushed by sigctxt.pushCall)
356+
p("JMP (R27)")
357+
}
358+
307359
func genWasm() {
308360
p("// No async preemption on wasm")
309361
p("UNDEF")

src/runtime/preempt_arm64.s

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

66
TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
7-
// Not implemented yet
8-
JMP ·abort(SB)
7+
MOVD R30, -496(RSP)
8+
SUB $496, RSP
9+
#ifdef GOOS_linux
10+
MOVD R29, -8(RSP)
11+
SUB $8, RSP, R29
12+
#endif
13+
MOVD R0, 8(RSP)
14+
MOVD R1, 16(RSP)
15+
MOVD R2, 24(RSP)
16+
MOVD R3, 32(RSP)
17+
MOVD R4, 40(RSP)
18+
MOVD R5, 48(RSP)
19+
MOVD R6, 56(RSP)
20+
MOVD R7, 64(RSP)
21+
MOVD R8, 72(RSP)
22+
MOVD R9, 80(RSP)
23+
MOVD R10, 88(RSP)
24+
MOVD R11, 96(RSP)
25+
MOVD R12, 104(RSP)
26+
MOVD R13, 112(RSP)
27+
MOVD R14, 120(RSP)
28+
MOVD R15, 128(RSP)
29+
MOVD R16, 136(RSP)
30+
MOVD R17, 144(RSP)
31+
MOVD R19, 152(RSP)
32+
MOVD R20, 160(RSP)
33+
MOVD R21, 168(RSP)
34+
MOVD R22, 176(RSP)
35+
MOVD R23, 184(RSP)
36+
MOVD R24, 192(RSP)
37+
MOVD R25, 200(RSP)
38+
MOVD R26, 208(RSP)
39+
MOVD NZCV, R0
40+
MOVD R0, 216(RSP)
41+
MOVD FPSR, R0
42+
MOVD R0, 224(RSP)
43+
FMOVD F0, 232(RSP)
44+
FMOVD F1, 240(RSP)
45+
FMOVD F2, 248(RSP)
46+
FMOVD F3, 256(RSP)
47+
FMOVD F4, 264(RSP)
48+
FMOVD F5, 272(RSP)
49+
FMOVD F6, 280(RSP)
50+
FMOVD F7, 288(RSP)
51+
FMOVD F8, 296(RSP)
52+
FMOVD F9, 304(RSP)
53+
FMOVD F10, 312(RSP)
54+
FMOVD F11, 320(RSP)
55+
FMOVD F12, 328(RSP)
56+
FMOVD F13, 336(RSP)
57+
FMOVD F14, 344(RSP)
58+
FMOVD F15, 352(RSP)
59+
FMOVD F16, 360(RSP)
60+
FMOVD F17, 368(RSP)
61+
FMOVD F18, 376(RSP)
62+
FMOVD F19, 384(RSP)
63+
FMOVD F20, 392(RSP)
64+
FMOVD F21, 400(RSP)
65+
FMOVD F22, 408(RSP)
66+
FMOVD F23, 416(RSP)
67+
FMOVD F24, 424(RSP)
68+
FMOVD F25, 432(RSP)
69+
FMOVD F26, 440(RSP)
70+
FMOVD F27, 448(RSP)
71+
FMOVD F28, 456(RSP)
72+
FMOVD F29, 464(RSP)
73+
FMOVD F30, 472(RSP)
74+
FMOVD F31, 480(RSP)
75+
CALL ·asyncPreempt2(SB)
76+
FMOVD 480(RSP), F31
77+
FMOVD 472(RSP), F30
78+
FMOVD 464(RSP), F29
79+
FMOVD 456(RSP), F28
80+
FMOVD 448(RSP), F27
81+
FMOVD 440(RSP), F26
82+
FMOVD 432(RSP), F25
83+
FMOVD 424(RSP), F24
84+
FMOVD 416(RSP), F23
85+
FMOVD 408(RSP), F22
86+
FMOVD 400(RSP), F21
87+
FMOVD 392(RSP), F20
88+
FMOVD 384(RSP), F19
89+
FMOVD 376(RSP), F18
90+
FMOVD 368(RSP), F17
91+
FMOVD 360(RSP), F16
92+
FMOVD 352(RSP), F15
93+
FMOVD 344(RSP), F14
94+
FMOVD 336(RSP), F13
95+
FMOVD 328(RSP), F12
96+
FMOVD 320(RSP), F11
97+
FMOVD 312(RSP), F10
98+
FMOVD 304(RSP), F9
99+
FMOVD 296(RSP), F8
100+
FMOVD 288(RSP), F7
101+
FMOVD 280(RSP), F6
102+
FMOVD 272(RSP), F5
103+
FMOVD 264(RSP), F4
104+
FMOVD 256(RSP), F3
105+
FMOVD 248(RSP), F2
106+
FMOVD 240(RSP), F1
107+
FMOVD 232(RSP), F0
108+
MOVD 224(RSP), R0
109+
MOVD R0, FPSR
110+
MOVD 216(RSP), R0
111+
MOVD R0, NZCV
112+
MOVD 208(RSP), R26
113+
MOVD 200(RSP), R25
114+
MOVD 192(RSP), R24
115+
MOVD 184(RSP), R23
116+
MOVD 176(RSP), R22
117+
MOVD 168(RSP), R21
118+
MOVD 160(RSP), R20
119+
MOVD 152(RSP), R19
120+
MOVD 144(RSP), R17
121+
MOVD 136(RSP), R16
122+
MOVD 128(RSP), R15
123+
MOVD 120(RSP), R14
124+
MOVD 112(RSP), R13
125+
MOVD 104(RSP), R12
126+
MOVD 96(RSP), R11
127+
MOVD 88(RSP), R10
128+
MOVD 80(RSP), R9
129+
MOVD 72(RSP), R8
130+
MOVD 64(RSP), R7
131+
MOVD 56(RSP), R6
132+
MOVD 48(RSP), R5
133+
MOVD 40(RSP), R4
134+
MOVD 32(RSP), R3
135+
MOVD 24(RSP), R2
136+
MOVD 16(RSP), R1
137+
MOVD 8(RSP), R0
138+
MOVD 496(RSP), R30
139+
#ifdef GOOS_linux
140+
MOVD -8(RSP), R29
141+
#endif
142+
MOVD (RSP), R27
143+
ADD $512, RSP
144+
JMP (R27)

src/runtime/signal_arm64.go

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

82-
const pushCallSupported = false
82+
const pushCallSupported = true
8383

8484
func (c *sigctxt) pushCall(targetPC uintptr) {
85-
throw("not implemented")
85+
// Push the LR to stack, as we'll clobber it in order to
86+
// push the call. The function being pushed is responsible
87+
// for restoring the LR and setting the SP back.
88+
// This extra space is known to gentraceback.
89+
sp := c.sp() - 16 // SP needs 16-byte alignment
90+
c.set_sp(sp)
91+
*(*uint64)(unsafe.Pointer(uintptr(sp))) = c.lr()
92+
// Set up PC and LR to pretend the function being signaled
93+
// calls targetPC at the faulting PC.
94+
c.set_lr(c.pc())
95+
c.set_pc(uint64(targetPC))
8696
}

0 commit comments

Comments
 (0)