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
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)
}
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 {
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
}
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()
}
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)
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
}
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)
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
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
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)
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
}
*/
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)
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)
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
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)
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
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
}
}
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
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.
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
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.
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
}
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
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
// 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}
}
}
- 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
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.
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
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 {
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
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
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)
q1.To.SetTarget(spill)
}
- return end
+ return end, rg
}
func isR15(r int16) bool {
--- /dev/null
+// 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)
+}