]> Cypherpunks repositories - gostls13.git/commitdiff
runtime: add async preemption support on ARM
authorCherry Zhang <cherryyz@google.com>
Sun, 20 Oct 2019 21:25:02 +0000 (17:25 -0400)
committerCherry Zhang <cherryyz@google.com>
Tue, 5 Nov 2019 02:49:48 +0000 (02:49 +0000)
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 <cherryyz@google.com>
Reviewed-by: Austin Clements <austin@google.com>
src/cmd/compile/internal/ssa/gen/ARMOps.go
src/runtime/mkpreempt.go
src/runtime/preempt_386.s
src/runtime/preempt_arm.s
src/runtime/signal_arm.go
src/runtime/traceback.go

index bde170864de86e41ef67faf59906f4698dcd0f7e..d10706d634ba3fa9d2bcaf98b5d55ae9a15dc688 100644 (file)
@@ -60,6 +60,8 @@ var regNamesARM = []string{
        "F14",
        "F15", // tmp
 
+       // If you add registers, update asyncPreempt in runtime.
+
        // pseudo-registers
        "SB",
 }
index c28f89581dc7398f01c263100d61e48410469ae4..78b1707f1b29e149c5ac1ee596fc84193ef3fe69 100644 (file)
@@ -78,7 +78,7 @@ var out io.Writer
 var arches = map[string]func(){
        "386":     gen386,
        "amd64":   genAMD64,
-       "arm":     notImplemented,
+       "arm":     genARM,
        "arm64":   notImplemented,
        "mips64x": notImplemented,
        "mipsx":   notImplemented,
@@ -133,9 +133,14 @@ func p(f string, args ...interface{}) {
        fmt.Fprintf(out, "\t%s\n", strings.Replace(fmted, "\n", "\n\t", -1))
 }
 
+func label(l string) {
+       fmt.Fprintf(out, "%s\n", l)
+}
+
 type layout struct {
        stack int
        regs  []regPos
+       sp    string // stack pointer register
 }
 
 type regPos struct {
@@ -165,7 +170,7 @@ func (l *layout) save() {
                if reg.save != "" {
                        p(reg.save, reg.pos)
                } else {
-                       p("%s %s, %d(SP)", reg.op, reg.reg, reg.pos)
+                       p("%s %s, %d(%s)", reg.op, reg.reg, reg.pos, l.sp)
                }
        }
 }
@@ -176,7 +181,7 @@ func (l *layout) restore() {
                if reg.restore != "" {
                        p(reg.restore, reg.pos)
                } else {
-                       p("%s %d(SP), %s", reg.op, reg.pos, reg.reg)
+                       p("%s %d(%s), %s", reg.op, reg.pos, l.sp, reg.reg)
                }
        }
 }
@@ -185,7 +190,7 @@ func gen386() {
        p("PUSHFL")
 
        // Save general purpose registers.
-       var l layout
+       var l = layout{sp: "SP"}
        for _, reg := range regNames386 {
                if reg == "SP" || strings.HasPrefix(reg, "X") {
                        continue
@@ -200,7 +205,7 @@ func gen386() {
                108)
 
        // Save SSE state only if supported.
-       lSSE := layout{stack: l.stack}
+       lSSE := layout{stack: l.stack, sp: "SP"}
        for i := 0; i < 8; i++ {
                lSSE.add("MOVUPS", fmt.Sprintf("X%d", i), 16)
        }
@@ -210,11 +215,11 @@ func gen386() {
        l.save()
        p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse")
        lSSE.save()
-       p("nosse:")
+       label("nosse:")
        p("CALL ·asyncPreempt2(SB)")
        p("CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1\nJNE nosse2")
        lSSE.restore()
-       p("nosse2:")
+       label("nosse2:")
        l.restore()
        p("ADJSP $%d", -lSSE.stack)
 
@@ -224,7 +229,7 @@ func gen386() {
 
 func genAMD64() {
        // Assign stack offsets.
-       var l layout
+       var l = layout{sp: "SP"}
        for _, reg := range regNamesAMD64 {
                if reg == "SP" || reg == "BP" {
                        continue
@@ -255,6 +260,50 @@ func genAMD64() {
        p("RET")
 }
 
+func genARM() {
+       // Add integer registers R0-R12.
+       // R13 (SP), R14 (LR), R15 (PC) are special and not saved here.
+       var l = layout{sp: "R13", stack: 4} // add LR slot
+       for i := 0; i <= 12; i++ {
+               reg := fmt.Sprintf("R%d", i)
+               if i == 10 {
+                       continue // R10 is g register, no need to save/restore
+               }
+               l.add("MOVW", reg, 4)
+       }
+       // Add flag register.
+       l.addSpecial(
+               "MOVW CPSR, R0\nMOVW R0, %d(R13)",
+               "MOVW %d(R13), R0\nMOVW R0, CPSR",
+               4)
+
+       // Add floating point registers F0-F15 and flag register.
+       var lfp = layout{stack: l.stack, sp: "R13"}
+       lfp.addSpecial(
+               "MOVW FPCR, R0\nMOVW R0, %d(R13)",
+               "MOVW %d(R13), R0\nMOVW R0, FPCR",
+               4)
+       for i := 0; i <= 15; i++ {
+               reg := fmt.Sprintf("F%d", i)
+               lfp.add("MOVD", reg, 8)
+       }
+
+       p("MOVW.W R14, -%d(R13)", lfp.stack) // allocate frame, save LR
+       l.save()
+       p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp") // test goarm, and skip FP registers if goarm=5.
+       lfp.save()
+       label("nofp:")
+       p("CALL ·asyncPreempt2(SB)")
+       p("MOVB ·goarm(SB), R0\nCMP $6, R0\nBLT nofp2") // test goarm, and skip FP registers if goarm=5.
+       lfp.restore()
+       label("nofp2:")
+       l.restore()
+
+       p("MOVW %d(R13), R14", lfp.stack)     // sigctxt.pushCall pushes LR on stack, restore it
+       p("MOVW.P %d(R13), R15", lfp.stack+4) // load PC, pop frame (including the space pushed by sigctxt.pushCall)
+       p("UNDEF")                            // shouldn't get here
+}
+
 func genWasm() {
        p("// No async preemption on wasm")
        p("UNDEF")
index a7961e02ce6ea31e71a5ef177c5a395abee5dc9f..a00ac8f385488fa9f52dbda89087b422bd5de98a 100644 (file)
@@ -26,7 +26,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
        MOVUPS X5, 216(SP)
        MOVUPS X6, 232(SP)
        MOVUPS X7, 248(SP)
-       nosse:
+nosse:
        CALL ·asyncPreempt2(SB)
        CMPB internal∕cpu·X86+const_offsetX86HasSSE2(SB), $1
        JNE nosse2
@@ -38,7 +38,7 @@ TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
        MOVUPS 168(SP), X2
        MOVUPS 152(SP), X1
        MOVUPS 136(SP), X0
-       nosse2:
+nosse2:
        FRSTOR 28(SP)
        MOVL 24(SP), DI
        MOVL 20(SP), SI
index 5697268ce1612e5a8a0b19e4d86d4c924b8e63d4..8f243c0dcd608191507494e86bac08e921241c3b 100644 (file)
@@ -4,5 +4,80 @@
 #include "textflag.h"
 
 TEXT ·asyncPreempt(SB),NOSPLIT|NOFRAME,$0-0
-       // Not implemented yet
-       JMP ·abort(SB)
+       MOVW.W R14, -188(R13)
+       MOVW R0, 4(R13)
+       MOVW R1, 8(R13)
+       MOVW R2, 12(R13)
+       MOVW R3, 16(R13)
+       MOVW R4, 20(R13)
+       MOVW R5, 24(R13)
+       MOVW R6, 28(R13)
+       MOVW R7, 32(R13)
+       MOVW R8, 36(R13)
+       MOVW R9, 40(R13)
+       MOVW R11, 44(R13)
+       MOVW R12, 48(R13)
+       MOVW CPSR, R0
+       MOVW R0, 52(R13)
+       MOVB ·goarm(SB), R0
+       CMP $6, R0
+       BLT nofp
+       MOVW FPCR, R0
+       MOVW R0, 56(R13)
+       MOVD F0, 60(R13)
+       MOVD F1, 68(R13)
+       MOVD F2, 76(R13)
+       MOVD F3, 84(R13)
+       MOVD F4, 92(R13)
+       MOVD F5, 100(R13)
+       MOVD F6, 108(R13)
+       MOVD F7, 116(R13)
+       MOVD F8, 124(R13)
+       MOVD F9, 132(R13)
+       MOVD F10, 140(R13)
+       MOVD F11, 148(R13)
+       MOVD F12, 156(R13)
+       MOVD F13, 164(R13)
+       MOVD F14, 172(R13)
+       MOVD F15, 180(R13)
+nofp:
+       CALL ·asyncPreempt2(SB)
+       MOVB ·goarm(SB), R0
+       CMP $6, R0
+       BLT nofp2
+       MOVD 180(R13), F15
+       MOVD 172(R13), F14
+       MOVD 164(R13), F13
+       MOVD 156(R13), F12
+       MOVD 148(R13), F11
+       MOVD 140(R13), F10
+       MOVD 132(R13), F9
+       MOVD 124(R13), F8
+       MOVD 116(R13), F7
+       MOVD 108(R13), F6
+       MOVD 100(R13), F5
+       MOVD 92(R13), F4
+       MOVD 84(R13), F3
+       MOVD 76(R13), F2
+       MOVD 68(R13), F1
+       MOVD 60(R13), F0
+       MOVW 56(R13), R0
+       MOVW R0, FPCR
+nofp2:
+       MOVW 52(R13), R0
+       MOVW R0, CPSR
+       MOVW 48(R13), R12
+       MOVW 44(R13), R11
+       MOVW 40(R13), R9
+       MOVW 36(R13), R8
+       MOVW 32(R13), R7
+       MOVW 28(R13), R6
+       MOVW 24(R13), R5
+       MOVW 20(R13), R4
+       MOVW 16(R13), R3
+       MOVW 12(R13), R2
+       MOVW 8(R13), R1
+       MOVW 4(R13), R0
+       MOVW 188(R13), R14
+       MOVW.P 192(R13), R15
+       UNDEF
index 1b3e53d01c13324bf2d1f4460c25b47292140666..ff952b8b60c22cc25ebf2af7d750203f4a707aea 100644 (file)
@@ -63,8 +63,18 @@ func (c *sigctxt) preparePanic(sig uint32, gp *g) {
        c.set_pc(uint32(funcPC(sigpanic)))
 }
 
-const pushCallSupported = false
+const pushCallSupported = true
 
 func (c *sigctxt) pushCall(targetPC uintptr) {
-       throw("not implemented")
+       // Push the LR to stack, as we'll clobber it in order to
+       // push the call. The function being pushed is responsible
+       // for restoring the LR and setting the SP back.
+       // This extra slot is known to gentraceback.
+       sp := c.sp() - 4
+       c.set_sp(sp)
+       *(*uint32)(unsafe.Pointer(uintptr(sp))) = c.lr()
+       // Set up PC and LR to pretend the function being signaled
+       // calls targetPC at the faulting PC.
+       c.set_lr(c.pc())
+       c.set_pc(uint32(targetPC))
 }
index 9be7d739d17a2b68f3999bc54b49d790e0120d76..dc2a7a36935aae06294ad8c80c1d03eb3c830450 100644 (file)
@@ -462,6 +462,7 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                }
 
                waspanic = f.funcID == funcID_sigpanic
+               injectedCall := waspanic || f.funcID == funcID_asyncPreempt
 
                // Do not unwind past the bottom of the stack.
                if !flr.valid() {
@@ -477,8 +478,8 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
                frame.argmap = nil
 
                // On link register architectures, sighandler saves the LR on stack
-               // before faking a call to sigpanic.
-               if usesLR && waspanic {
+               // before faking a call.
+               if usesLR && injectedCall {
                        x := *(*uintptr)(unsafe.Pointer(frame.sp))
                        frame.sp += sys.MinFrameSize
                        if GOARCH == "arm64" {