diff options
author | Cherry Zhang <cherryyz@google.com> | 2019-10-26 22:54:28 -0400 |
---|---|---|
committer | Cherry Zhang <cherryyz@google.com> | 2019-11-07 20:59:14 +0000 |
commit | a930fede7386fd3583553b523fd6f7fa5fef1244 (patch) | |
tree | 73081ff2104bd49d8b6cfed4e54f57d08c533d92 /src/runtime/mkpreempt.go | |
parent | a120cc8b365be33d3f82bbf8b79584e0e3439b9b (diff) | |
download | go-a930fede7386fd3583553b523fd6f7fa5fef1244.tar.gz go-a930fede7386fd3583553b523fd6f7fa5fef1244.zip |
runtime: add async preemption support on MIPS and MIPS64
This CL adds support of call injection and async preemption on
MIPS and MIPS64.
Like ARM64, we need to clobber one register (REGTMP) for
returning from the injected call. Previous CLs have marked code
sequences that use REGTMP async-nonpreemtible.
It seems on MIPS/MIPS64, a CALL instruction is not "atomic" (!).
If a signal is delivered right at the CALL instruction, we may
see an updated LR with a not-yet-updated PC. In some cases this
may lead to failed stack unwinding. Don't preempt in this case.
Change-Id: I99437b2d05869ded5c0c8cb55265dbfc933aedab
Reviewed-on: https://go-review.googlesource.com/c/go/+/203720
Reviewed-by: Keith Randall <khr@golang.org>
Diffstat (limited to 'src/runtime/mkpreempt.go')
-rw-r--r-- | src/runtime/mkpreempt.go | 65 |
1 files changed, 63 insertions, 2 deletions
diff --git a/src/runtime/mkpreempt.go b/src/runtime/mkpreempt.go index 2f022971fd..e9e82b8c43 100644 --- a/src/runtime/mkpreempt.go +++ b/src/runtime/mkpreempt.go @@ -80,8 +80,8 @@ var arches = map[string]func(){ "amd64": genAMD64, "arm": genARM, "arm64": genARM64, - "mips64x": notImplemented, - "mipsx": notImplemented, + "mips64x": func() { genMIPS(true) }, + "mipsx": func() { genMIPS(false) }, "ppc64x": notImplemented, "s390x": genS390X, "wasm": genWasm, @@ -356,6 +356,67 @@ func genARM64() { p("JMP (R27)") } +func genMIPS(_64bit bool) { + mov := "MOVW" + movf := "MOVF" + add := "ADD" + sub := "SUB" + r28 := "R28" + regsize := 4 + if _64bit { + mov = "MOVV" + movf = "MOVD" + add = "ADDV" + sub = "SUBV" + r28 = "RSB" + regsize = 8 + } + + // Add integer registers R1-R22, R24-R25, R28 + // R0 (zero), R23 (REGTMP), R29 (SP), R30 (g), R31 (LR) are special, + // and not saved here. R26 and R27 are reserved by kernel and not used. + var l = layout{sp: "R29", stack: regsize} // add slot to save PC of interrupted instruction (in LR) + for i := 1; i <= 25; i++ { + if i == 23 { + continue // R23 is REGTMP + } + reg := fmt.Sprintf("R%d", i) + l.add(mov, reg, regsize) + } + l.add(mov, r28, regsize) + l.addSpecial( + mov+" HI, R1\n"+mov+" R1, %d(R29)", + mov+" %d(R29), R1\n"+mov+" R1, HI", + regsize) + l.addSpecial( + mov+" LO, R1\n"+mov+" R1, %d(R29)", + mov+" %d(R29), R1\n"+mov+" R1, LO", + regsize) + // Add floating point control/status register FCR31 (FCR0-FCR30 are irrelevant) + l.addSpecial( + mov+" FCR31, R1\n"+mov+" R1, %d(R29)", + mov+" %d(R29), R1\n"+mov+" R1, FCR31", + regsize) + // Add floating point registers F0-F31. + for i := 0; i <= 31; i++ { + reg := fmt.Sprintf("F%d", i) + l.add(movf, reg, regsize) + } + + // allocate frame, save PC of interrupted instruction (in LR) + p(mov+" R31, -%d(R29)", l.stack) + p(sub+" $%d, R29", l.stack) + + l.save() + p("CALL ·asyncPreempt2(SB)") + l.restore() + + p(mov+" %d(R29), R31", l.stack) // sigctxt.pushCall has pushed LR (at interrupt) on stack, restore it + p(mov + " (R29), R23") // load PC to REGTMP + p(add+" $%d, R29", l.stack+regsize) // pop frame (including the space pushed by sigctxt.pushCall) + p("JMP (R23)") +} + func genS390X() { // Add integer registers R0-R12 // R13 (g), R14 (LR), R15 (SP) are special, and not saved here. |