runtime: enable framepointer on all arm64

Frame pointers were already enabled on linux, darwin, ios,
but not freebsd, android, openbsd, netbsd.

But the space was reserved on all platforms, leading to
two different arm64 framepointer conditions in different
parts of the code, one of which had no name
(framepointer_enabled || GOARCH == "arm64",
which might have been "framepointer_space_reserved").

So on the disabled systems, the stack layouts were still
set up for frame pointers and the only difference was not
actually maintaining the FP register in the generated code.

Reduce complexity by just enabling the frame pointer
completely on all the arm64 systems.

This commit passes on freebsd, android, netbsd.
I have not been able to try it on openbsd.

This CL is part of a stack adding windows/arm64
support (#36439), intended to land in the Go 1.17 cycle.
This CL is, however, not windows/arm64-specific.
It is cleanup meant to make the port (and future ports) easier.

Change-Id: I83bd23369d24b76db4c6a648fa74f6917819a093
Reviewed-on: https://go-review.googlesource.com/c/go/+/288814
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Cherry Zhang <cherryyz@google.com>
This commit is contained in:
Russ Cox 2021-01-27 11:34:42 -05:00
parent b19e7b518e
commit 09e059afb1
7 changed files with 164 additions and 136 deletions

View file

@ -271,8 +271,7 @@ func createSimpleVar(fnsym *obj.LSym, n *ir.Name) *dwarf.Var {
if base.Ctxt.FixedFrameSize() == 0 { if base.Ctxt.FixedFrameSize() == 0 {
offs -= int64(types.PtrSize) offs -= int64(types.PtrSize)
} }
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" { if objabi.Framepointer_enabled {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
offs -= int64(types.PtrSize) offs -= int64(types.PtrSize)
} }

View file

@ -213,8 +213,7 @@ func StackOffset(slot ssa.LocalSlot) int32 {
if base.Ctxt.FixedFrameSize() == 0 { if base.Ctxt.FixedFrameSize() == 0 {
off -= int64(types.PtrSize) off -= int64(types.PtrSize)
} }
if objabi.Framepointer_enabled || objabi.GOARCH == "arm64" { if objabi.Framepointer_enabled {
// There is a word space for FP on ARM64 even if the frame pointer is disabled
off -= int64(types.PtrSize) off -= int64(types.PtrSize)
} }
case ir.PPARAM, ir.PPARAMOUT: case ir.PPARAM, ir.PPARAMOUT:

View file

@ -239,7 +239,7 @@ const (
REGCTXT = REG_R26 // environment for closures REGCTXT = REG_R26 // environment for closures
REGTMP = REG_R27 // reserved for liblink REGTMP = REG_R27 // reserved for liblink
REGG = REG_R28 // G REGG = REG_R28 // G
REGFP = REG_R29 // frame pointer, unused in the Go toolchain REGFP = REG_R29 // frame pointer
REGLINK = REG_R30 REGLINK = REG_R30
// ARM64 uses R31 as both stack pointer and zero register, // ARM64 uses R31 as both stack pointer and zero register,

View file

@ -622,25 +622,24 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd) prologueEnd.Pos = prologueEnd.Pos.WithXlogue(src.PosPrologueEnd)
if objabi.Framepointer_enabled { // Frame pointer.
q1 = obj.Appendp(q1, c.newprog) q1 = obj.Appendp(q1, c.newprog)
q1.Pos = p.Pos q1.Pos = p.Pos
q1.As = AMOVD q1.As = AMOVD
q1.From.Type = obj.TYPE_REG q1.From.Type = obj.TYPE_REG
q1.From.Reg = REGFP q1.From.Reg = REGFP
q1.To.Type = obj.TYPE_MEM q1.To.Type = obj.TYPE_MEM
q1.To.Reg = REGSP q1.To.Reg = REGSP
q1.To.Offset = -8 q1.To.Offset = -8
q1 = obj.Appendp(q1, c.newprog) q1 = obj.Appendp(q1, c.newprog)
q1.Pos = p.Pos q1.Pos = p.Pos
q1.As = ASUB q1.As = ASUB
q1.From.Type = obj.TYPE_CONST q1.From.Type = obj.TYPE_CONST
q1.From.Offset = 8 q1.From.Offset = 8
q1.Reg = REGSP q1.Reg = REGSP
q1.To.Type = obj.TYPE_REG q1.To.Type = obj.TYPE_REG
q1.To.Reg = REGFP q1.To.Reg = REGFP
}
if c.cursym.Func().Text.From.Sym.Wrapper() { if c.cursym.Func().Text.From.Sym.Wrapper() {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame // if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
@ -765,28 +764,26 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
p.To.Reg = REGSP p.To.Reg = REGSP
p.Spadj = -c.autosize p.Spadj = -c.autosize
if objabi.Framepointer_enabled { // Frame pointer.
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
p.As = ASUB p.As = ASUB
p.From.Type = obj.TYPE_CONST p.From.Type = obj.TYPE_CONST
p.From.Offset = 8 p.From.Offset = 8
p.Reg = REGSP p.Reg = REGSP
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = REGFP p.To.Reg = REGFP
}
} }
} else { } else {
/* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/ /* want write-back pre-indexed SP+autosize -> SP, loading REGLINK*/
if objabi.Framepointer_enabled { // Frame pointer.
p.As = AMOVD p.As = AMOVD
p.From.Type = obj.TYPE_MEM p.From.Type = obj.TYPE_MEM
p.From.Reg = REGSP p.From.Reg = REGSP
p.From.Offset = -8 p.From.Offset = -8
p.To.Type = obj.TYPE_REG p.To.Type = obj.TYPE_REG
p.To.Reg = REGFP p.To.Reg = REGFP
p = obj.Appendp(p, c.newprog) p = obj.Appendp(p, c.newprog)
}
aoffset := c.autosize aoffset := c.autosize
@ -821,6 +818,28 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
} }
} }
// If enabled, this code emits 'MOV PC, R27' before every 'MOV LR, PC',
// so that if you are debugging a low-level crash where PC and LR are zero,
// you can look at R27 to see what jumped to the zero.
// This is useful when bringing up Go on a new system.
// (There is similar code in ../ppc64/obj9.go:/if.false.)
const debugRETZERO = false
if debugRETZERO {
if p.As != obj.ARET {
q = newprog()
q.Pos = p.Pos
q.Link = p.Link
p.Link = q
p = q
}
p.As = AADR
p.From.Type = obj.TYPE_BRANCH
p.From.Offset = 0
p.To.Type = obj.TYPE_REG
p.To.Reg = REGTMP
}
if p.As != obj.ARET { if p.As != obj.ARET {
q = newprog() q = newprog()
q.Pos = p.Pos q.Pos = p.Pos
@ -866,110 +885,106 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
} }
case obj.ADUFFCOPY: case obj.ADUFFCOPY:
if objabi.Framepointer_enabled { // ADR ret_addr, R27
// ADR ret_addr, R27 // STP (FP, R27), -24(SP)
// STP (FP, R27), -24(SP) // SUB 24, SP, FP
// SUB 24, SP, FP // DUFFCOPY
// DUFFCOPY // ret_addr:
// ret_addr: // SUB 8, SP, FP
// SUB 8, SP, FP
q1 := p q1 := p
// copy DUFFCOPY from q1 to q4 // copy DUFFCOPY from q1 to q4
q4 := obj.Appendp(p, c.newprog) q4 := obj.Appendp(p, c.newprog)
q4.Pos = p.Pos q4.Pos = p.Pos
q4.As = obj.ADUFFCOPY q4.As = obj.ADUFFCOPY
q4.To = p.To q4.To = p.To
q1.As = AADR q1.As = AADR
q1.From.Type = obj.TYPE_BRANCH q1.From.Type = obj.TYPE_BRANCH
q1.To.Type = obj.TYPE_REG q1.To.Type = obj.TYPE_REG
q1.To.Reg = REG_R27 q1.To.Reg = REG_R27
q2 := obj.Appendp(q1, c.newprog) q2 := obj.Appendp(q1, c.newprog)
q2.Pos = p.Pos q2.Pos = p.Pos
q2.As = ASTP q2.As = ASTP
q2.From.Type = obj.TYPE_REGREG q2.From.Type = obj.TYPE_REGREG
q2.From.Reg = REGFP q2.From.Reg = REGFP
q2.From.Offset = int64(REG_R27) q2.From.Offset = int64(REG_R27)
q2.To.Type = obj.TYPE_MEM q2.To.Type = obj.TYPE_MEM
q2.To.Reg = REGSP q2.To.Reg = REGSP
q2.To.Offset = -24 q2.To.Offset = -24
// maintaine FP for DUFFCOPY // maintain FP for DUFFCOPY
q3 := obj.Appendp(q2, c.newprog) q3 := obj.Appendp(q2, c.newprog)
q3.Pos = p.Pos q3.Pos = p.Pos
q3.As = ASUB q3.As = ASUB
q3.From.Type = obj.TYPE_CONST q3.From.Type = obj.TYPE_CONST
q3.From.Offset = 24 q3.From.Offset = 24
q3.Reg = REGSP q3.Reg = REGSP
q3.To.Type = obj.TYPE_REG q3.To.Type = obj.TYPE_REG
q3.To.Reg = REGFP q3.To.Reg = REGFP
q5 := obj.Appendp(q4, c.newprog) q5 := obj.Appendp(q4, c.newprog)
q5.Pos = p.Pos q5.Pos = p.Pos
q5.As = ASUB q5.As = ASUB
q5.From.Type = obj.TYPE_CONST q5.From.Type = obj.TYPE_CONST
q5.From.Offset = 8 q5.From.Offset = 8
q5.Reg = REGSP q5.Reg = REGSP
q5.To.Type = obj.TYPE_REG q5.To.Type = obj.TYPE_REG
q5.To.Reg = REGFP q5.To.Reg = REGFP
q1.From.SetTarget(q5) q1.From.SetTarget(q5)
p = q5 p = q5
}
case obj.ADUFFZERO: case obj.ADUFFZERO:
if objabi.Framepointer_enabled { // ADR ret_addr, R27
// ADR ret_addr, R27 // STP (FP, R27), -24(SP)
// STP (FP, R27), -24(SP) // SUB 24, SP, FP
// SUB 24, SP, FP // DUFFZERO
// DUFFZERO // ret_addr:
// ret_addr: // SUB 8, SP, FP
// SUB 8, SP, FP
q1 := p q1 := p
// copy DUFFZERO from q1 to q4 // copy DUFFZERO from q1 to q4
q4 := obj.Appendp(p, c.newprog) q4 := obj.Appendp(p, c.newprog)
q4.Pos = p.Pos q4.Pos = p.Pos
q4.As = obj.ADUFFZERO q4.As = obj.ADUFFZERO
q4.To = p.To q4.To = p.To
q1.As = AADR q1.As = AADR
q1.From.Type = obj.TYPE_BRANCH q1.From.Type = obj.TYPE_BRANCH
q1.To.Type = obj.TYPE_REG q1.To.Type = obj.TYPE_REG
q1.To.Reg = REG_R27 q1.To.Reg = REG_R27
q2 := obj.Appendp(q1, c.newprog) q2 := obj.Appendp(q1, c.newprog)
q2.Pos = p.Pos q2.Pos = p.Pos
q2.As = ASTP q2.As = ASTP
q2.From.Type = obj.TYPE_REGREG q2.From.Type = obj.TYPE_REGREG
q2.From.Reg = REGFP q2.From.Reg = REGFP
q2.From.Offset = int64(REG_R27) q2.From.Offset = int64(REG_R27)
q2.To.Type = obj.TYPE_MEM q2.To.Type = obj.TYPE_MEM
q2.To.Reg = REGSP q2.To.Reg = REGSP
q2.To.Offset = -24 q2.To.Offset = -24
// maintaine FP for DUFFZERO // maintain FP for DUFFZERO
q3 := obj.Appendp(q2, c.newprog) q3 := obj.Appendp(q2, c.newprog)
q3.Pos = p.Pos q3.Pos = p.Pos
q3.As = ASUB q3.As = ASUB
q3.From.Type = obj.TYPE_CONST q3.From.Type = obj.TYPE_CONST
q3.From.Offset = 24 q3.From.Offset = 24
q3.Reg = REGSP q3.Reg = REGSP
q3.To.Type = obj.TYPE_REG q3.To.Type = obj.TYPE_REG
q3.To.Reg = REGFP q3.To.Reg = REGFP
q5 := obj.Appendp(q4, c.newprog) q5 := obj.Appendp(q4, c.newprog)
q5.Pos = p.Pos q5.Pos = p.Pos
q5.As = ASUB q5.As = ASUB
q5.From.Type = obj.TYPE_CONST q5.From.Type = obj.TYPE_CONST
q5.From.Offset = 8 q5.From.Offset = 8
q5.Reg = REGSP q5.Reg = REGSP
q5.To.Type = obj.TYPE_REG q5.To.Type = obj.TYPE_REG
q5.To.Reg = REGFP q5.To.Reg = REGFP
q1.From.SetTarget(q5) q1.From.SetTarget(q5)
p = q5 p = q5
}
} }
if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 { if p.To.Type == obj.TYPE_REG && p.To.Reg == REGSP && p.Spadj == 0 {

View file

@ -137,7 +137,7 @@ func init() {
} }
// Note: must agree with runtime.framepointer_enabled. // Note: must agree with runtime.framepointer_enabled.
var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" && (GOOS == "linux" || GOOS == "darwin" || GOOS == "ios") var Framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64"
func addexp(s string) { func addexp(s string) {
// Could do general integer parsing here, but the runtime copy doesn't yet. // Could do general integer parsing here, but the runtime copy doesn't yet.

View file

@ -1106,4 +1106,4 @@ var (
) )
// Must agree with cmd/internal/objabi.Framepointer_enabled. // Must agree with cmd/internal/objabi.Framepointer_enabled.
const framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64" && (GOOS == "linux" || GOOS == "darwin" || GOOS == "ios") const framepointer_enabled = GOARCH == "amd64" || GOARCH == "arm64"

View file

@ -275,7 +275,22 @@ func gentraceback(pc0, sp0, lr0 uintptr, gp *g, skip int, pcbuf *uintptr, max in
// For architectures with frame pointers, if there's // For architectures with frame pointers, if there's
// a frame, then there's a saved frame pointer here. // a frame, then there's a saved frame pointer here.
if frame.varp > frame.sp && (GOARCH == "amd64" || GOARCH == "arm64") { //
// NOTE: This code is not as general as it looks.
// On x86, the ABI is to save the frame pointer word at the
// top of the stack frame, so we have to back down over it.
// On arm64, the frame pointer should be at the bottom of
// the stack (with R29 (aka FP) = RSP), in which case we would
// not want to do the subtraction here. But we started out without
// any frame pointer, and when we wanted to add it, we didn't
// want to break all the assembly doing direct writes to 8(RSP)
// to set the first parameter to a called function.
// So we decided to write the FP link *below* the stack pointer
// (with R29 = RSP - 8 in Go functions).
// This is technically ABI-compatible but not standard.
// And it happens to end up mimicking the x86 layout.
// Other architectures may make different decisions.
if frame.varp > frame.sp && framepointer_enabled {
frame.varp -= sys.PtrSize frame.varp -= sys.PtrSize
} }