mirror of
https://github.com/golang/go
synced 2024-11-11 18:51:37 -07:00
cmd/{asm,compile,internal/obj}: add "maymorestack" support
This adds a debugging hook for optionally calling a "maymorestack" function in the prologue of any function that might call morestack (whether it does at run time or not). The maymorestack function will let us improve lock checking and add debugging modes that stress function preemption and stack growth. Passes toolstash-check -all (except on js/wasm, where toolstash appears to be broken) Fixes #48297. Change-Id: I27197947482b329af75dafb9971fc0d3a52eaf31 Reviewed-on: https://go-review.googlesource.com/c/go/+/359795 Trust: Austin Clements <austin@google.com> Run-TryBot: Austin Clements <austin@google.com> Reviewed-by: Cherry Mui <cherryyz@google.com>
This commit is contained in:
parent
1c4cfd8010
commit
3839b60014
@ -28,6 +28,10 @@ var (
|
||||
CompilingRuntime = flag.Bool("compiling-runtime", false, "source to be compiled is part of the Go runtime")
|
||||
)
|
||||
|
||||
var DebugFlags struct {
|
||||
MayMoreStack string `help:"call named function before all stack growth checks"`
|
||||
}
|
||||
|
||||
var (
|
||||
D MultiFlag
|
||||
I MultiFlag
|
||||
@ -39,6 +43,7 @@ func init() {
|
||||
flag.Var(&D, "D", "predefined symbol with optional simple value -D=identifier=value; can be set multiple times")
|
||||
flag.Var(&I, "I", "include directory; can be set multiple times")
|
||||
flag.BoolVar(&DebugV, "v", false, "print debug output")
|
||||
flag.Var(objabi.NewDebugFlag(&DebugFlags, nil), "d", "enable debugging settings; try -d help")
|
||||
objabi.AddVersionFlag() // -V
|
||||
objabi.Flagcount("S", "print assembly and machine code", &PrintOut)
|
||||
}
|
||||
|
@ -42,6 +42,7 @@ func main() {
|
||||
ctxt.Flag_dynlink = *flags.Dynlink
|
||||
ctxt.Flag_linkshared = *flags.Linkshared
|
||||
ctxt.Flag_shared = *flags.Shared || *flags.Dynlink
|
||||
ctxt.Flag_maymorestack = flags.DebugFlags.MayMoreStack
|
||||
ctxt.IsAsm = true
|
||||
ctxt.Pkgpath = *flags.Importpath
|
||||
switch *flags.Spectre {
|
||||
|
@ -42,6 +42,7 @@ type DebugFlags struct {
|
||||
UnifiedQuirks int `help:"enable unified IR construction's quirks mode"`
|
||||
WB int `help:"print information about write barriers"`
|
||||
ABIWrap int `help:"print information about ABI wrapper generation"`
|
||||
MayMoreStack string `help:"call named function before all stack growth checks"`
|
||||
|
||||
Any bool // set when any of the debug flags have been set
|
||||
}
|
||||
|
@ -192,6 +192,7 @@ func ParseFlags() {
|
||||
Ctxt.Flag_shared = Ctxt.Flag_dynlink || Ctxt.Flag_shared
|
||||
Ctxt.Flag_optimize = Flag.N == 0
|
||||
Ctxt.Debugasm = int(Flag.S)
|
||||
Ctxt.Flag_maymorestack = Debug.MayMoreStack
|
||||
|
||||
if flag.NArg() < 1 {
|
||||
usage()
|
||||
|
@ -634,6 +634,61 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
}
|
||||
|
||||
func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
if c.ctxt.Flag_maymorestack != "" {
|
||||
// Save LR and make room for REGCTXT.
|
||||
const frameSize = 8
|
||||
// MOVW.W R14,$-8(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVW
|
||||
p.Scond |= C_WBIT
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGLINK
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Offset = -frameSize
|
||||
p.To.Reg = REGSP
|
||||
p.Spadj = frameSize
|
||||
|
||||
// MOVW REGCTXT, 4(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVW
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGCTXT
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Offset = 4
|
||||
p.To.Reg = REGSP
|
||||
|
||||
// CALL maymorestack
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = obj.ACALL
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
// See ../x86/obj6.go
|
||||
p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
|
||||
|
||||
// Restore REGCTXT and LR.
|
||||
|
||||
// MOVW 4(SP), REGCTXT
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVW
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Offset = 4
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGCTXT
|
||||
|
||||
// MOVW.P 8(SP), R14
|
||||
p.As = AMOVW
|
||||
p.Scond |= C_PBIT
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Offset = frameSize
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGLINK
|
||||
p.Spadj = -frameSize
|
||||
}
|
||||
|
||||
// Jump back to here after morestack returns.
|
||||
startPred := p
|
||||
|
||||
// MOVW g_stackguard(g), R1
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
@ -761,7 +816,7 @@ func (c *ctxt5) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
b := obj.Appendp(pcdata, c.newprog)
|
||||
b.As = obj.AJMP
|
||||
b.To.Type = obj.TYPE_BRANCH
|
||||
b.To.SetTarget(c.cursym.Func().Text.Link)
|
||||
b.To.SetTarget(startPred.Link)
|
||||
b.Spadj = +framesize
|
||||
|
||||
return end
|
||||
|
@ -58,6 +58,91 @@ var noZRreplace = map[obj.As]bool{
|
||||
}
|
||||
|
||||
func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
if c.ctxt.Flag_maymorestack != "" {
|
||||
p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
|
||||
|
||||
// Save LR and make room for FP, REGCTXT. Leave room
|
||||
// for caller's saved FP.
|
||||
const frameSize = 32
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGLINK
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.Scond = C_XPRE
|
||||
p.To.Offset = -frameSize
|
||||
p.To.Reg = REGSP
|
||||
p.Spadj = frameSize
|
||||
|
||||
// Save FP.
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGFP
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = REGSP
|
||||
p.To.Offset = -8
|
||||
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ASUB
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = 8
|
||||
p.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGFP
|
||||
|
||||
// Save REGCTXT (for simplicity we do this whether or
|
||||
// not we need it.)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGCTXT
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Reg = REGSP
|
||||
p.To.Offset = 8
|
||||
|
||||
// BL maymorestack
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ABL
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
// See ../x86/obj6.go
|
||||
p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
|
||||
|
||||
// Restore REGCTXT.
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Reg = REGSP
|
||||
p.From.Offset = 8
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGCTXT
|
||||
|
||||
// Restore FP.
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Reg = REGSP
|
||||
p.From.Offset = -8
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGFP
|
||||
|
||||
// Restore LR and SP.
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.Scond = C_XPOST
|
||||
p.From.Offset = frameSize
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGLINK
|
||||
p.Spadj = -frameSize
|
||||
|
||||
p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
|
||||
}
|
||||
|
||||
// Jump back to here after morestack returns.
|
||||
startPred := p
|
||||
|
||||
// MOV g_stackguard(g), RT1
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
@ -212,7 +297,7 @@ func (c *ctxt7) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
jmp := obj.Appendp(pcdata, c.newprog)
|
||||
jmp.As = AB
|
||||
jmp.To.Type = obj.TYPE_BRANCH
|
||||
jmp.To.SetTarget(c.cursym.Func().Text.Link)
|
||||
jmp.To.SetTarget(startPred.Link)
|
||||
jmp.Spadj = +framesize
|
||||
|
||||
return end
|
||||
|
@ -880,7 +880,8 @@ type Link struct {
|
||||
Flag_linkshared bool
|
||||
Flag_optimize bool
|
||||
Flag_locationlists bool
|
||||
Retpoline bool // emit use of retpoline stubs for indirect jmp/call
|
||||
Retpoline bool // emit use of retpoline stubs for indirect jmp/call
|
||||
Flag_maymorestack string // If not "", call this function before stack checks
|
||||
Bso *bufio.Writer
|
||||
Pathname string
|
||||
Pkgpath string // the current package's import path, "" if unknown
|
||||
|
@ -658,6 +658,82 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
mov = AMOVW
|
||||
}
|
||||
|
||||
if c.ctxt.Flag_maymorestack != "" {
|
||||
// Save LR and REGCTXT.
|
||||
frameSize := 2 * c.ctxt.Arch.PtrSize
|
||||
|
||||
p = c.ctxt.StartUnsafePoint(p, c.newprog)
|
||||
|
||||
// MOV REGLINK, -8/-16(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = mov
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGLINK
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Offset = int64(-frameSize)
|
||||
p.To.Reg = REGSP
|
||||
|
||||
// MOV REGCTXT, -4/-8(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = mov
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGCTXT
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Offset = -int64(c.ctxt.Arch.PtrSize)
|
||||
p.To.Reg = REGSP
|
||||
|
||||
// ADD $-8/$-16, SP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = add
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = int64(-frameSize)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGSP
|
||||
p.Spadj = int32(frameSize)
|
||||
|
||||
// JAL maymorestack
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AJAL
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
// See ../x86/obj6.go
|
||||
p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
|
||||
p.Mark |= BRANCH
|
||||
|
||||
// Restore LR and REGCTXT.
|
||||
|
||||
// MOV 0(SP), REGLINK
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = mov
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Offset = 0
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGLINK
|
||||
|
||||
// MOV 4/8(SP), REGCTXT
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = mov
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Offset = int64(c.ctxt.Arch.PtrSize)
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGCTXT
|
||||
|
||||
// ADD $8/$16, SP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = add
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = int64(frameSize)
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGSP
|
||||
p.Spadj = int32(-frameSize)
|
||||
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
}
|
||||
|
||||
// Jump back to here after morestack returns.
|
||||
startPred := p
|
||||
|
||||
// MOV g_stackguard(g), R1
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
@ -787,7 +863,8 @@ func (c *ctxt0) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
|
||||
p.As = AJMP
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
p.To.SetTarget(c.cursym.Func().Text.Link)
|
||||
p.To.SetTarget(startPred.Link)
|
||||
startPred.Link.Mark |= LABEL
|
||||
p.Mark |= BRANCH
|
||||
|
||||
// placeholder for q1's jump target
|
||||
|
@ -1048,7 +1048,96 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
}
|
||||
*/
|
||||
func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
p0 := p // save entry point, but skipping the two instructions setting R2 in shared mode
|
||||
if c.ctxt.Flag_maymorestack != "" {
|
||||
if c.ctxt.Flag_shared || c.ctxt.Flag_dynlink {
|
||||
// See the call to morestack for why these are
|
||||
// complicated to support.
|
||||
c.ctxt.Diag("maymorestack with -shared or -dynlink is not supported")
|
||||
}
|
||||
|
||||
// Spill arguments. This has to happen before we open
|
||||
// any more frame space.
|
||||
p = c.cursym.Func().SpillRegisterArgs(p, c.newprog)
|
||||
|
||||
// Save LR and REGCTXT
|
||||
frameSize := 8 + c.ctxt.FixedFrameSize()
|
||||
|
||||
// MOVD LR, REGTMP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REG_LR
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGTMP
|
||||
// MOVDU REGTMP, -16(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVDU
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGTMP
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Offset = -frameSize
|
||||
p.To.Reg = REGSP
|
||||
p.Spadj = int32(frameSize)
|
||||
|
||||
// MOVD REGCTXT, 8(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGCTXT
|
||||
p.To.Type = obj.TYPE_MEM
|
||||
p.To.Offset = 8
|
||||
p.To.Reg = REGSP
|
||||
|
||||
// BL maymorestack
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ABL
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
// See ../x86/obj6.go
|
||||
p.To.Sym = c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
|
||||
|
||||
// Restore LR and REGCTXT
|
||||
|
||||
// MOVD 8(SP), REGCTXT
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Offset = 8
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGCTXT
|
||||
|
||||
// MOVD 0(SP), REGTMP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
p.From.Offset = 0
|
||||
p.From.Reg = REGSP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGTMP
|
||||
|
||||
// MOVD REGTMP, LR
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGTMP
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REG_LR
|
||||
|
||||
// ADD $16, SP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AADD
|
||||
p.From.Type = obj.TYPE_CONST
|
||||
p.From.Offset = frameSize
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGSP
|
||||
p.Spadj = -int32(frameSize)
|
||||
|
||||
// Unspill arguments.
|
||||
p = c.cursym.Func().UnspillRegisterArgs(p, c.newprog)
|
||||
}
|
||||
|
||||
// save entry point, but skipping the two instructions setting R2 in shared mode and maymorestack
|
||||
startPred := p
|
||||
|
||||
// MOVD g_stackguard(g), R22
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
@ -1262,7 +1351,7 @@ func (c *ctxt9) stacksplit(p *obj.Prog, framesize int32) *obj.Prog {
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ABR
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
p.To.SetTarget(p0.Link)
|
||||
p.To.SetTarget(startPred.Link)
|
||||
|
||||
// placeholder for q1's jump target
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
@ -722,6 +722,62 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
|
||||
return p
|
||||
}
|
||||
|
||||
if ctxt.Flag_maymorestack != "" {
|
||||
// Save LR and REGCTXT
|
||||
const frameSize = 16
|
||||
p = ctxt.StartUnsafePoint(p, newprog)
|
||||
// MOV LR, -16(SP)
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AMOV
|
||||
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: -frameSize}
|
||||
// ADDI $-16, SP
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AADDI
|
||||
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: -frameSize}
|
||||
p.Reg = REG_SP
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
|
||||
p.Spadj = frameSize
|
||||
// MOV REGCTXT, 8(SP)
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AMOV
|
||||
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_CTXT}
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 8}
|
||||
|
||||
// CALL maymorestack
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = obj.ACALL
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
// See ../x86/obj6.go
|
||||
p.To.Sym = ctxt.LookupABI(ctxt.Flag_maymorestack, cursym.ABI())
|
||||
jalToSym(ctxt, p, REG_X5)
|
||||
|
||||
// Restore LR and REGCTXT
|
||||
|
||||
// MOV 8(SP), REGCTXT
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AMOV
|
||||
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 8}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_CTXT}
|
||||
// MOV (SP), LR
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AMOV
|
||||
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REG_SP, Offset: 0}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
||||
// ADDI $16, SP
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AADDI
|
||||
p.From = obj.Addr{Type: obj.TYPE_CONST, Offset: frameSize}
|
||||
p.Reg = REG_SP
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_SP}
|
||||
p.Spadj = -frameSize
|
||||
|
||||
p = ctxt.EndUnsafePoint(p, newprog, -1)
|
||||
}
|
||||
|
||||
// Jump back to here after morestack returns.
|
||||
startPred := p
|
||||
|
||||
// MOV g_stackguard(g), X10
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AMOV
|
||||
@ -821,7 +877,7 @@ func stacksplit(ctxt *obj.Link, p *obj.Prog, cursym *obj.LSym, newprog obj.ProgA
|
||||
p.As = AJAL
|
||||
p.To = obj.Addr{Type: obj.TYPE_BRANCH}
|
||||
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_ZERO}
|
||||
p.To.SetTarget(cursym.Func().Text.Link)
|
||||
p.To.SetTarget(startPred.Link)
|
||||
|
||||
// placeholder for to_done's jump target
|
||||
p = obj.Appendp(p, newprog)
|
||||
|
@ -294,6 +294,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
var pLast *obj.Prog
|
||||
var pPre *obj.Prog
|
||||
var pPreempt *obj.Prog
|
||||
var pCheck *obj.Prog
|
||||
wasSplit := false
|
||||
for p := c.cursym.Func().Text; p != nil; p = p.Link {
|
||||
pLast = p
|
||||
@ -323,7 +324,7 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
q := p
|
||||
|
||||
if !p.From.Sym.NoSplit() {
|
||||
p, pPreempt = c.stacksplitPre(p, autosize) // emit pre part of split check
|
||||
p, pPreempt, pCheck = c.stacksplitPre(p, autosize) // emit pre part of split check
|
||||
pPre = p
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
wasSplit = true //need post part of split
|
||||
@ -563,14 +564,69 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
}
|
||||
}
|
||||
if wasSplit {
|
||||
c.stacksplitPost(pLast, pPre, pPreempt, autosize) // emit post part of split check
|
||||
c.stacksplitPost(pLast, pPre, pPreempt, pCheck, autosize) // emit post part of split check
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Prog) {
|
||||
// stacksplitPre generates the function stack check prologue following
|
||||
// Prog p (which should be the TEXT Prog). It returns one or two
|
||||
// branch Progs that must be patched to jump to the morestack epilogue,
|
||||
// and the Prog that starts the morestack check.
|
||||
func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (pPre, pPreempt, pCheck *obj.Prog) {
|
||||
if c.ctxt.Flag_maymorestack != "" {
|
||||
// Save LR and REGCTXT
|
||||
const frameSize = 16
|
||||
p = c.ctxt.StartUnsafePoint(p, c.newprog)
|
||||
// MOVD LR, -16(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: -frameSize}
|
||||
// MOVD $-16(SP), SP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From = obj.Addr{Type: obj.TYPE_ADDR, Offset: -frameSize, Reg: REGSP}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
|
||||
p.Spadj = frameSize
|
||||
// MOVD REGCTXT, 8(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT}
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8}
|
||||
|
||||
// BL maymorestack
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = ABL
|
||||
// See ../x86/obj6.go
|
||||
sym := c.ctxt.LookupABI(c.ctxt.Flag_maymorestack, c.cursym.ABI())
|
||||
p.To = obj.Addr{Type: obj.TYPE_BRANCH, Sym: sym}
|
||||
|
||||
// Restore LR and REGCTXT
|
||||
|
||||
// MOVD REGCTXT, 8(SP)
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 8}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGCTXT}
|
||||
// MOVD (SP), LR
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From = obj.Addr{Type: obj.TYPE_MEM, Reg: REGSP, Offset: 0}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REG_LR}
|
||||
// MOVD $16(SP), SP
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
p.As = AMOVD
|
||||
p.From = obj.Addr{Type: obj.TYPE_CONST, Reg: REGSP, Offset: frameSize}
|
||||
p.To = obj.Addr{Type: obj.TYPE_REG, Reg: REGSP}
|
||||
p.Spadj = -frameSize
|
||||
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
}
|
||||
|
||||
// MOVD g_stackguard(g), R3
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
// Jump back to here after morestack returns.
|
||||
pCheck = p
|
||||
|
||||
p.As = AMOVD
|
||||
p.From.Type = obj.TYPE_MEM
|
||||
@ -599,12 +655,11 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Pro
|
||||
p.As = ACMPUBGE
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
|
||||
return p, nil
|
||||
return p, nil, pCheck
|
||||
}
|
||||
|
||||
// large stack: SP-framesize < stackguard-StackSmall
|
||||
|
||||
var q *obj.Prog
|
||||
offset := int64(framesize) - objabi.StackSmall
|
||||
if framesize > objabi.StackBig {
|
||||
// Such a large stack we need to protect against underflow.
|
||||
@ -625,7 +680,7 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Pro
|
||||
p.To.Reg = REG_R4
|
||||
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
q = p
|
||||
pPreempt = p
|
||||
p.As = ACMPUBLT
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGSP
|
||||
@ -651,10 +706,16 @@ func (c *ctxtz) stacksplitPre(p *obj.Prog, framesize int32) (*obj.Prog, *obj.Pro
|
||||
p.As = ACMPUBGE
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
|
||||
return p, q
|
||||
return p, pPreempt, pCheck
|
||||
}
|
||||
|
||||
func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog, framesize int32) *obj.Prog {
|
||||
// stacksplitPost generates the function epilogue that calls morestack
|
||||
// and returns the new last instruction in the function.
|
||||
//
|
||||
// p is the last Prog in the function. pPre and pPreempt, if non-nil,
|
||||
// are the instructions that branch to the epilogue. This will fill in
|
||||
// their branch targets. pCheck is the Prog that begins the stack check.
|
||||
func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre, pPreempt, pCheck *obj.Prog, framesize int32) *obj.Prog {
|
||||
// Now we are at the end of the function, but logically
|
||||
// we are still in function prologue. We need to fix the
|
||||
// SP data and PCDATA.
|
||||
@ -692,12 +753,12 @@ func (c *ctxtz) stacksplitPost(p *obj.Prog, pPre *obj.Prog, pPreempt *obj.Prog,
|
||||
|
||||
p = c.ctxt.EndUnsafePoint(p, c.newprog, -1)
|
||||
|
||||
// BR start
|
||||
// BR pCheck
|
||||
p = obj.Appendp(p, c.newprog)
|
||||
|
||||
p.As = ABR
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
p.To.SetTarget(c.cursym.Func().Text.Link)
|
||||
p.To.SetTarget(pCheck)
|
||||
return p
|
||||
}
|
||||
|
||||
|
@ -243,6 +243,51 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
||||
p.Spadj = int32(framesize)
|
||||
}
|
||||
|
||||
needMoreStack := !s.Func().Text.From.Sym.NoSplit()
|
||||
|
||||
// If the maymorestack debug option is enabled, insert the
|
||||
// call to maymorestack *before* processing resume points so
|
||||
// we can construct a resume point after maymorestack for
|
||||
// morestack to resume at.
|
||||
var pMorestack = s.Func().Text
|
||||
if needMoreStack && ctxt.Flag_maymorestack != "" {
|
||||
p := pMorestack
|
||||
|
||||
// Save REGCTXT on the stack.
|
||||
const tempFrame = 8
|
||||
p = appendp(p, AGet, regAddr(REG_SP))
|
||||
p = appendp(p, AI32Const, constAddr(tempFrame))
|
||||
p = appendp(p, AI32Sub)
|
||||
p = appendp(p, ASet, regAddr(REG_SP))
|
||||
p.Spadj = tempFrame
|
||||
ctxtp := obj.Addr{
|
||||
Type: obj.TYPE_MEM,
|
||||
Reg: REG_SP,
|
||||
Offset: 0,
|
||||
}
|
||||
p = appendp(p, AMOVD, regAddr(REGCTXT), ctxtp)
|
||||
|
||||
// maymorestack must not itself preempt because we
|
||||
// don't have full stack information, so this can be
|
||||
// ACALLNORESUME.
|
||||
p = appendp(p, ACALLNORESUME, constAddr(0))
|
||||
// See ../x86/obj6.go
|
||||
sym := ctxt.LookupABI(ctxt.Flag_maymorestack, s.ABI())
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: sym}
|
||||
|
||||
// Restore REGCTXT.
|
||||
p = appendp(p, AMOVD, ctxtp, regAddr(REGCTXT))
|
||||
p = appendp(p, AGet, regAddr(REG_SP))
|
||||
p = appendp(p, AI32Const, constAddr(tempFrame))
|
||||
p = appendp(p, AI32Add)
|
||||
p = appendp(p, ASet, regAddr(REG_SP))
|
||||
p.Spadj = -tempFrame
|
||||
|
||||
// Add an explicit ARESUMEPOINT after maymorestack for
|
||||
// morestack to resume at.
|
||||
pMorestack = appendp(p, ARESUMEPOINT)
|
||||
}
|
||||
|
||||
// Introduce resume points for CALL instructions
|
||||
// and collect other explicit resume points.
|
||||
numResumePoints := 0
|
||||
@ -303,8 +348,8 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
||||
tableIdxs = append(tableIdxs, uint64(numResumePoints))
|
||||
s.Size = pc + 1
|
||||
|
||||
if !s.Func().Text.From.Sym.NoSplit() {
|
||||
p := s.Func().Text
|
||||
if needMoreStack {
|
||||
p := pMorestack
|
||||
|
||||
if framesize <= objabi.StackSmall {
|
||||
// small stack: SP <= stackguard
|
||||
@ -341,6 +386,13 @@ func preprocess(ctxt *obj.Link, s *obj.LSym, newprog obj.ProgAlloc) {
|
||||
// TODO(neelance): handle wraparound case
|
||||
|
||||
p = appendp(p, AIf)
|
||||
// This CALL does *not* have a resume point after it
|
||||
// (we already inserted all of the resume points). As
|
||||
// a result, morestack will resume at the *previous*
|
||||
// resume point (typically, the beginning of the
|
||||
// function) and perform the morestack check again.
|
||||
// This is why we don't need an explicit loop like
|
||||
// other architectures.
|
||||
p = appendp(p, obj.ACALL, constAddr(0))
|
||||
if s.Func().Text.From.Sym.NeedCtxt() {
|
||||
p.To = obj.Addr{Type: obj.TYPE_MEM, Name: obj.NAME_EXTERN, Sym: morestack}
|
||||
|
@ -644,19 +644,6 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
}
|
||||
}
|
||||
|
||||
var regg int16
|
||||
if !p.From.Sym.NoSplit() || p.From.Sym.Wrapper() {
|
||||
if ctxt.Arch.Family == sys.AMD64 && cursym.ABI() == obj.ABIInternal {
|
||||
regg = REGG // use the g register directly in ABIInternal
|
||||
} else {
|
||||
p = obj.Appendp(p, newprog)
|
||||
regg = REG_CX
|
||||
if ctxt.Arch.Family == sys.AMD64 {
|
||||
regg = REGG // == REG_R14
|
||||
}
|
||||
p = load_g(ctxt, p, newprog, regg) // load g into regg
|
||||
}
|
||||
}
|
||||
var regEntryTmp0, regEntryTmp1 int16
|
||||
if ctxt.Arch.Family == sys.AMD64 {
|
||||
regEntryTmp0, regEntryTmp1 = REGENTRYTMP0, REGENTRYTMP1
|
||||
@ -664,8 +651,13 @@ func preprocess(ctxt *obj.Link, cursym *obj.LSym, newprog obj.ProgAlloc) {
|
||||
regEntryTmp0, regEntryTmp1 = REG_BX, REG_DI
|
||||
}
|
||||
|
||||
if !cursym.Func().Text.From.Sym.NoSplit() {
|
||||
p = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg), regg) // emit split check
|
||||
var regg int16
|
||||
if !p.From.Sym.NoSplit() {
|
||||
// Emit split check and load G register
|
||||
p, regg = stacksplit(ctxt, cursym, p, newprog, autoffset, int32(textarg))
|
||||
} else if p.From.Sym.Wrapper() {
|
||||
// Load G register for the wrapper code
|
||||
p, regg = loadG(ctxt, cursym, p, newprog)
|
||||
}
|
||||
|
||||
// Delve debugger would like the next instruction to be noted as the end of the function prologue.
|
||||
@ -973,12 +965,21 @@ func indir_cx(ctxt *obj.Link, a *obj.Addr) {
|
||||
a.Reg = REG_CX
|
||||
}
|
||||
|
||||
// Append code to p to load g into cx.
|
||||
// Overwrites p with the first instruction (no first appendp).
|
||||
// Overwriting p is unusual but it lets use this in both the
|
||||
// prologue (caller must call appendp first) and in the epilogue.
|
||||
// Returns last new instruction.
|
||||
func load_g(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, rg int16) *obj.Prog {
|
||||
// loadG ensures the G is loaded into a register (either CX or REGG),
|
||||
// appending instructions to p if necessary. It returns the new last
|
||||
// instruction and the G register.
|
||||
func loadG(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc) (*obj.Prog, int16) {
|
||||
if ctxt.Arch.Family == sys.AMD64 && cursym.ABI() == obj.ABIInternal {
|
||||
// Use the G register directly in ABIInternal
|
||||
return p, REGG
|
||||
}
|
||||
|
||||
var regg int16 = REG_CX
|
||||
if ctxt.Arch.Family == sys.AMD64 {
|
||||
regg = REGG // == REG_R14
|
||||
}
|
||||
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = AMOVQ
|
||||
if ctxt.Arch.PtrSize == 4 {
|
||||
p.As = AMOVL
|
||||
@ -987,8 +988,9 @@ func load_g(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, rg int16) *obj.P
|
||||
p.From.Reg = REG_TLS
|
||||
p.From.Offset = 0
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = rg
|
||||
p.To.Reg = regg
|
||||
|
||||
// Rewrite TLS instruction if necessary.
|
||||
next := p.Link
|
||||
progedit(ctxt, p, newprog)
|
||||
for p.Link != next {
|
||||
@ -1000,24 +1002,26 @@ func load_g(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc, rg int16) *obj.P
|
||||
p.From.Scale = 2
|
||||
}
|
||||
|
||||
return p
|
||||
return p, regg
|
||||
}
|
||||
|
||||
// Append code to p to check for stack split.
|
||||
// Appends to (does not overwrite) p.
|
||||
// Assumes g is in rg.
|
||||
// Returns last new instruction.
|
||||
func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32, rg int16) *obj.Prog {
|
||||
// Returns last new instruction and G register.
|
||||
func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgAlloc, framesize int32, textarg int32) (*obj.Prog, int16) {
|
||||
cmp := ACMPQ
|
||||
lea := ALEAQ
|
||||
mov := AMOVQ
|
||||
sub := ASUBQ
|
||||
push, pop := APUSHQ, APOPQ
|
||||
|
||||
if ctxt.Arch.Family == sys.I386 {
|
||||
cmp = ACMPL
|
||||
lea = ALEAL
|
||||
mov = AMOVL
|
||||
sub = ASUBL
|
||||
push, pop = APUSHL, APOPL
|
||||
}
|
||||
|
||||
tmp := int16(REG_AX) // use AX for 32-bit
|
||||
@ -1026,6 +1030,45 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
|
||||
tmp = int16(REGENTRYTMP0)
|
||||
}
|
||||
|
||||
if ctxt.Flag_maymorestack != "" {
|
||||
p = cursym.Func().SpillRegisterArgs(p, newprog)
|
||||
|
||||
if cursym.Func().Text.From.Sym.NeedCtxt() {
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = push
|
||||
p.From.Type = obj.TYPE_REG
|
||||
p.From.Reg = REGCTXT
|
||||
}
|
||||
|
||||
// We call maymorestack with an ABI matching the
|
||||
// caller's ABI. Since this is the first thing that
|
||||
// happens in the function, we have to be consistent
|
||||
// with the caller about CPU state (notably,
|
||||
// fixed-meaning registers).
|
||||
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = obj.ACALL
|
||||
p.To.Type = obj.TYPE_BRANCH
|
||||
p.To.Name = obj.NAME_EXTERN
|
||||
p.To.Sym = ctxt.LookupABI(ctxt.Flag_maymorestack, cursym.ABI())
|
||||
|
||||
if cursym.Func().Text.From.Sym.NeedCtxt() {
|
||||
p = obj.Appendp(p, newprog)
|
||||
p.As = pop
|
||||
p.To.Type = obj.TYPE_REG
|
||||
p.To.Reg = REGCTXT
|
||||
}
|
||||
|
||||
p = cursym.Func().UnspillRegisterArgs(p, newprog)
|
||||
}
|
||||
|
||||
// Jump back to here after morestack returns.
|
||||
startPred := p
|
||||
|
||||
// Load G register
|
||||
var rg int16
|
||||
p, rg = loadG(ctxt, cursym, p, newprog)
|
||||
|
||||
var q1 *obj.Prog
|
||||
if framesize <= objabi.StackSmall {
|
||||
// small stack: SP <= stackguard
|
||||
@ -1171,7 +1214,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
|
||||
jmp := obj.Appendp(pcdata, newprog)
|
||||
jmp.As = obj.AJMP
|
||||
jmp.To.Type = obj.TYPE_BRANCH
|
||||
jmp.To.SetTarget(cursym.Func().Text.Link)
|
||||
jmp.To.SetTarget(startPred.Link)
|
||||
jmp.Spadj = +framesize
|
||||
|
||||
jls.To.SetTarget(spill)
|
||||
@ -1179,7 +1222,7 @@ func stacksplit(ctxt *obj.Link, cursym *obj.LSym, p *obj.Prog, newprog obj.ProgA
|
||||
q1.To.SetTarget(spill)
|
||||
}
|
||||
|
||||
return end
|
||||
return end, rg
|
||||
}
|
||||
|
||||
func isR15(r int16) bool {
|
||||
|
47
test/maymorestack.go
Normal file
47
test/maymorestack.go
Normal file
@ -0,0 +1,47 @@
|
||||
// run -gcflags=-d=maymorestack=main.mayMoreStack
|
||||
|
||||
// Copyright 2021 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// Test the maymorestack testing hook by injecting a hook that counts
|
||||
// how many times it is called and checking that count.
|
||||
|
||||
package main
|
||||
|
||||
import "runtime"
|
||||
|
||||
var count uint32
|
||||
|
||||
//go:nosplit
|
||||
func mayMoreStack() {
|
||||
count++
|
||||
}
|
||||
|
||||
func main() {
|
||||
const wantCount = 128
|
||||
|
||||
anotherFunc(wantCount - 1) // -1 because the call to main already counted
|
||||
|
||||
if count == 0 {
|
||||
panic("mayMoreStack not called")
|
||||
} else if count != wantCount {
|
||||
println(count, "!=", wantCount)
|
||||
panic("wrong number of calls to mayMoreStack")
|
||||
}
|
||||
}
|
||||
|
||||
//go:noinline
|
||||
func anotherFunc(n int) {
|
||||
// Trigger a stack growth on at least some calls to
|
||||
// anotherFunc to test that mayMoreStack is called outside the
|
||||
// morestack loop. It's also important that it is called
|
||||
// before (not after) morestack, but that's hard to test.
|
||||
var x [1 << 10]byte
|
||||
|
||||
if n > 1 {
|
||||
anotherFunc(n - 1)
|
||||
}
|
||||
|
||||
runtime.KeepAlive(x)
|
||||
}
|
Loading…
Reference in New Issue
Block a user