1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
14 "cmd/compile/internal/base"
15 "cmd/compile/internal/ir"
16 "cmd/compile/internal/objw"
17 "cmd/compile/internal/ssa"
18 "cmd/compile/internal/types"
24 // cmpstackvarlt reports whether the stack variable a sorts before b.
25 func cmpstackvarlt(a, b *ir.Name) bool {
26 // Sort non-autos before autos.
27 if needAlloc(a) != needAlloc(b) {
31 // If both are non-auto (e.g., parameters, results), then sort by
32 // frame offset (defined by ABI).
34 return a.FrameOffset() < b.FrameOffset()
37 // From here on, a and b are both autos (i.e., local variables).
39 // Sort used before unused (so AllocFrame can truncate unused
41 if a.Used() != b.Used() {
45 // Sort pointer-typed before non-pointer types.
46 // Keeps the stack's GC bitmap compact.
47 ap := a.Type().HasPointers()
48 bp := b.Type().HasPointers()
53 // Group variables that need zeroing, so we can efficiently zero
61 // Sort variables in descending alignment order, so we can optimally
62 // pack variables into the frame.
63 if a.Type().Alignment() != b.Type().Alignment() {
64 return a.Type().Alignment() > b.Type().Alignment()
67 // Sort normal variables before open-coded-defer slots, so that the
68 // latter are grouped together and near the top of the frame (to
69 // minimize varint encoding of their varp offset).
70 if a.OpenDeferSlot() != b.OpenDeferSlot() {
71 return a.OpenDeferSlot()
74 // If a and b are both open-coded defer slots, then order them by
75 // index in descending order, so they'll be laid out in the frame in
78 // Their index was saved in FrameOffset in state.openDeferSave.
79 if a.OpenDeferSlot() {
80 return a.FrameOffset() > b.FrameOffset()
83 // Tie breaker for stable results.
84 return a.Sym().Name < b.Sym().Name
87 // byStackVar implements sort.Interface for []*Node using cmpstackvarlt.
88 type byStackVar []*ir.Name
90 func (s byStackVar) Len() int { return len(s) }
91 func (s byStackVar) Less(i, j int) bool { return cmpstackvarlt(s[i], s[j]) }
92 func (s byStackVar) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
94 // needAlloc reports whether n is within the current frame, for which we need to
95 // allocate space. In particular, it excludes arguments and results, which are in
97 func needAlloc(n *ir.Name) bool {
98 if n.Op() != ir.ONAME {
99 base.FatalfAt(n.Pos(), "%v has unexpected Op %v", n, n.Op())
108 return n.IsOutputParamInRegisters()
111 base.FatalfAt(n.Pos(), "%v has unexpected Class %v", n, n.Class)
116 func (s *ssafn) AllocFrame(f *ssa.Func) {
119 s.stkalign = int64(types.RegSize)
122 // Mark the PAUTO's unused.
123 for _, ln := range fn.Dcl {
129 for _, l := range f.RegAlloc {
130 if ls, ok := l.(ssa.LocalSlot); ok {
135 for _, b := range f.Blocks {
136 for _, v := range b.Values {
137 if n, ok := v.Aux.(*ir.Name); ok {
140 if n.IsOutputParamInRegisters() && v.Op == ssa.OpVarDef {
141 // ignore VarDef, look for "real" uses.
142 // TODO: maybe do this for PAUTO as well?
146 case ir.PPARAM, ir.PAUTO:
153 // Use sort.Stable instead of sort.Sort so stack layout (and thus
154 // compiler output) is less sensitive to frontend changes that
155 // introduce or remove unused variables.
156 sort.Stable(byStackVar(fn.Dcl))
158 // Reassign stack offsets of the locals that are used.
160 for i, n := range fn.Dcl {
161 if n.Op() != ir.ONAME || n.Class != ir.PAUTO && !(n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters()) {
162 // i.e., stack assign if AUTO, or if PARAMOUT in registers (which has no predefined spill locations)
166 fn.DebugInfo.(*ssa.FuncDebug).OptDcl = fn.Dcl[i:]
171 types.CalcSize(n.Type())
173 if w >= types.MaxWidth || w < 0 {
174 base.Fatalf("bad width")
176 if w == 0 && lastHasPtr {
177 // Pad between a pointer-containing object and a zero-sized object.
178 // This prevents a pointer to the zero-sized object from being interpreted
179 // as a pointer to the pointer-containing object (and causing it
180 // to be scanned when it shouldn't be). See issue 24993.
184 s.stksize = types.RoundUp(s.stksize, n.Type().Alignment())
185 if n.Type().Alignment() > int64(types.RegSize) {
186 s.stkalign = n.Type().Alignment()
188 if n.Type().HasPointers() {
189 s.stkptrsize = s.stksize
194 n.SetFrameOffset(-s.stksize)
197 s.stksize = types.RoundUp(s.stksize, s.stkalign)
198 s.stkptrsize = types.RoundUp(s.stkptrsize, s.stkalign)
201 const maxStackSize = 1 << 30
203 // Compile builds an SSA backend function,
204 // uses it to generate a plist,
205 // and flushes that plist to machine code.
206 // worker indicates which of the backend workers is doing the processing.
207 func Compile(fn *ir.Func, worker int) {
208 f := buildssa(fn, worker)
209 // Note: check arg size to fix issue 25507.
210 if f.Frontend().(*ssafn).stksize >= maxStackSize || f.OwnAux.ArgWidth() >= maxStackSize {
211 largeStackFramesMu.Lock()
212 largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: f.OwnAux.ArgWidth(), pos: fn.Pos()})
213 largeStackFramesMu.Unlock()
216 pp := objw.NewProgs(fn, worker)
219 // Check frame size again.
220 // The check above included only the space needed for local variables.
221 // After genssa, the space needed includes local variables and the callee arg region.
222 // We must do this check prior to calling pp.Flush.
223 // If there are any oversized stack frames,
224 // the assembler may emit inscrutable complaints about invalid instructions.
225 if pp.Text.To.Offset >= maxStackSize {
226 largeStackFramesMu.Lock()
227 locals := f.Frontend().(*ssafn).stksize
228 largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: f.OwnAux.ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
229 largeStackFramesMu.Unlock()
233 pp.Flush() // assemble, fill in boilerplate, etc.
235 // If we're compiling the package init function, search for any
236 // relocations that target global map init outline functions and
237 // turn them into weak relocs.
238 if fn.IsPackageInit() && base.Debug.WrapGlobalMapCtl != 1 {
239 weakenGlobalMapInitRelocs(fn)
242 // fieldtrack must be called after pp.Flush. See issue 20014.
243 fieldtrack(pp.Text.From.Sym, fn.FieldTrack)
246 // globalMapInitLsyms records the LSym of each map.init.NNN outlined
247 // map initializer function created by the compiler.
248 var globalMapInitLsyms map[*obj.LSym]struct{}
250 // RegisterMapInitLsym records "s" in the set of outlined map initializer
252 func RegisterMapInitLsym(s *obj.LSym) {
253 if globalMapInitLsyms == nil {
254 globalMapInitLsyms = make(map[*obj.LSym]struct{})
256 globalMapInitLsyms[s] = struct{}{}
259 // weakenGlobalMapInitRelocs walks through all of the relocations on a
260 // given a package init function "fn" and looks for relocs that target
261 // outlined global map initializer functions; if it finds any such
262 // relocs, it flags them as R_WEAK.
263 func weakenGlobalMapInitRelocs(fn *ir.Func) {
264 if globalMapInitLsyms == nil {
267 for i := range fn.LSym.R {
268 tgt := fn.LSym.R[i].Sym
272 if _, ok := globalMapInitLsyms[tgt]; !ok {
275 if base.Debug.WrapGlobalMapDbg > 1 {
276 fmt.Fprintf(os.Stderr, "=-= weakify fn %v reloc %d %+v\n", fn, i,
279 // set the R_WEAK bit, leave rest of reloc type intact
280 fn.LSym.R[i].Type |= objabi.R_WEAK
284 // StackOffset returns the stack location of a LocalSlot relative to the
285 // stack pointer, suitable for use in a DWARF location entry. This has nothing
286 // to do with its offset in the user variable.
287 func StackOffset(slot ssa.LocalSlot) int32 {
291 case ir.PPARAM, ir.PPARAMOUT:
292 if !n.IsOutputParamInRegisters() {
293 off = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
296 fallthrough // PPARAMOUT in registers allocates like an AUTO
298 off = n.FrameOffset()
299 if base.Ctxt.Arch.FixedFrameSize == 0 {
300 off -= int64(types.PtrSize)
302 if buildcfg.FramePointerEnabled {
303 off -= int64(types.PtrSize)
306 return int32(off + slot.Off)
309 // fieldtrack adds R_USEFIELD relocations to fnsym to record any
310 // struct fields that it used.
311 func fieldtrack(fnsym *obj.LSym, tracked map[*obj.LSym]struct{}) {
315 if !buildcfg.Experiment.FieldTrack || len(tracked) == 0 {
319 trackSyms := make([]*obj.LSym, 0, len(tracked))
320 for sym := range tracked {
321 trackSyms = append(trackSyms, sym)
323 sort.Slice(trackSyms, func(i, j int) bool { return trackSyms[i].Name < trackSyms[j].Name })
324 for _, sym := range trackSyms {
325 r := obj.Addrel(fnsym)
327 r.Type = objabi.R_USEFIELD
331 // largeStack is info about a function whose stack frame is too large (rare).
332 type largeStack struct {
340 largeStackFramesMu sync.Mutex // protects largeStackFrames
341 largeStackFrames []largeStack
344 func CheckLargeStacks() {
345 // Check whether any of the functions we have compiled have gigantic stack frames.
346 sort.Slice(largeStackFrames, func(i, j int) bool {
347 return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
349 for _, large := range largeStackFrames {
350 if large.callee != 0 {
351 base.ErrorfAt(large.pos, 0, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
353 base.ErrorfAt(large.pos, 0, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)