]> Cypherpunks.ru repositories - gostls13.git/blob - src/cmd/compile/internal/ssagen/pgen.go
ca064a16a7d920e185f82acfaf1aa904381d431f
[gostls13.git] / src / cmd / compile / internal / ssagen / pgen.go
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.
4
5 package ssagen
6
7 import (
8         "fmt"
9         "internal/buildcfg"
10         "os"
11         "sort"
12         "sync"
13
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"
19         "cmd/internal/obj"
20         "cmd/internal/objabi"
21         "cmd/internal/src"
22 )
23
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) {
28                 return needAlloc(b)
29         }
30
31         // If both are non-auto (e.g., parameters, results), then sort by
32         // frame offset (defined by ABI).
33         if !needAlloc(a) {
34                 return a.FrameOffset() < b.FrameOffset()
35         }
36
37         // From here on, a and b are both autos (i.e., local variables).
38
39         // Sort used before unused (so AllocFrame can truncate unused
40         // variables).
41         if a.Used() != b.Used() {
42                 return a.Used()
43         }
44
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()
49         if ap != bp {
50                 return ap
51         }
52
53         // Group variables that need zeroing, so we can efficiently zero
54         // them altogether.
55         ap = a.Needzero()
56         bp = b.Needzero()
57         if ap != bp {
58                 return ap
59         }
60
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()
65         }
66
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()
72         }
73
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
76         // ascending order.
77         //
78         // Their index was saved in FrameOffset in state.openDeferSave.
79         if a.OpenDeferSlot() {
80                 return a.FrameOffset() > b.FrameOffset()
81         }
82
83         // Tie breaker for stable results.
84         return a.Sym().Name < b.Sym().Name
85 }
86
87 // byStackVar implements sort.Interface for []*Node using cmpstackvarlt.
88 type byStackVar []*ir.Name
89
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] }
93
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
96 // the callers frame.
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())
100         }
101
102         switch n.Class {
103         case ir.PAUTO:
104                 return true
105         case ir.PPARAM:
106                 return false
107         case ir.PPARAMOUT:
108                 return n.IsOutputParamInRegisters()
109
110         default:
111                 base.FatalfAt(n.Pos(), "%v has unexpected Class %v", n, n.Class)
112                 return false
113         }
114 }
115
116 func (s *ssafn) AllocFrame(f *ssa.Func) {
117         s.stksize = 0
118         s.stkptrsize = 0
119         s.stkalign = int64(types.RegSize)
120         fn := s.curfn
121
122         // Mark the PAUTO's unused.
123         for _, ln := range fn.Dcl {
124                 if needAlloc(ln) {
125                         ln.SetUsed(false)
126                 }
127         }
128
129         for _, l := range f.RegAlloc {
130                 if ls, ok := l.(ssa.LocalSlot); ok {
131                         ls.N.SetUsed(true)
132                 }
133         }
134
135         for _, b := range f.Blocks {
136                 for _, v := range b.Values {
137                         if n, ok := v.Aux.(*ir.Name); ok {
138                                 switch n.Class {
139                                 case ir.PPARAMOUT:
140                                         if n.IsOutputParamInRegisters() && v.Op == ssa.OpVarDef {
141                                                 // ignore VarDef, look for "real" uses.
142                                                 // TODO: maybe do this for PAUTO as well?
143                                                 continue
144                                         }
145                                         fallthrough
146                                 case ir.PPARAM, ir.PAUTO:
147                                         n.SetUsed(true)
148                                 }
149                         }
150                 }
151         }
152
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))
157
158         // Reassign stack offsets of the locals that are used.
159         lastHasPtr := false
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)
163                         continue
164                 }
165                 if !n.Used() {
166                         fn.DebugInfo.(*ssa.FuncDebug).OptDcl = fn.Dcl[i:]
167                         fn.Dcl = fn.Dcl[:i]
168                         break
169                 }
170
171                 types.CalcSize(n.Type())
172                 w := n.Type().Size()
173                 if w >= types.MaxWidth || w < 0 {
174                         base.Fatalf("bad width")
175                 }
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.
181                         w = 1
182                 }
183                 s.stksize += w
184                 s.stksize = types.RoundUp(s.stksize, n.Type().Alignment())
185                 if n.Type().Alignment() > int64(types.RegSize) {
186                         s.stkalign = n.Type().Alignment()
187                 }
188                 if n.Type().HasPointers() {
189                         s.stkptrsize = s.stksize
190                         lastHasPtr = true
191                 } else {
192                         lastHasPtr = false
193                 }
194                 n.SetFrameOffset(-s.stksize)
195         }
196
197         s.stksize = types.RoundUp(s.stksize, s.stkalign)
198         s.stkptrsize = types.RoundUp(s.stkptrsize, s.stkalign)
199 }
200
201 const maxStackSize = 1 << 30
202
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()
214                 return
215         }
216         pp := objw.NewProgs(fn, worker)
217         defer pp.Free()
218         genssa(f, pp)
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()
230                 return
231         }
232
233         pp.Flush() // assemble, fill in boilerplate, etc.
234
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)
240         }
241
242         // fieldtrack must be called after pp.Flush. See issue 20014.
243         fieldtrack(pp.Text.From.Sym, fn.FieldTrack)
244 }
245
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{}
249
250 // RegisterMapInitLsym records "s" in the set of outlined map initializer
251 // functions.
252 func RegisterMapInitLsym(s *obj.LSym) {
253         if globalMapInitLsyms == nil {
254                 globalMapInitLsyms = make(map[*obj.LSym]struct{})
255         }
256         globalMapInitLsyms[s] = struct{}{}
257 }
258
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 {
265                 return
266         }
267         for i := range fn.LSym.R {
268                 tgt := fn.LSym.R[i].Sym
269                 if tgt == nil {
270                         continue
271                 }
272                 if _, ok := globalMapInitLsyms[tgt]; !ok {
273                         continue
274                 }
275                 if base.Debug.WrapGlobalMapDbg > 1 {
276                         fmt.Fprintf(os.Stderr, "=-= weakify fn %v reloc %d %+v\n", fn, i,
277                                 fn.LSym.R[i])
278                 }
279                 // set the R_WEAK bit, leave rest of reloc type intact
280                 fn.LSym.R[i].Type |= objabi.R_WEAK
281         }
282 }
283
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 {
288         n := slot.N
289         var off int64
290         switch n.Class {
291         case ir.PPARAM, ir.PPARAMOUT:
292                 if !n.IsOutputParamInRegisters() {
293                         off = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
294                         break
295                 }
296                 fallthrough // PPARAMOUT in registers allocates like an AUTO
297         case ir.PAUTO:
298                 off = n.FrameOffset()
299                 if base.Ctxt.Arch.FixedFrameSize == 0 {
300                         off -= int64(types.PtrSize)
301                 }
302                 if buildcfg.FramePointerEnabled {
303                         off -= int64(types.PtrSize)
304                 }
305         }
306         return int32(off + slot.Off)
307 }
308
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{}) {
312         if fnsym == nil {
313                 return
314         }
315         if !buildcfg.Experiment.FieldTrack || len(tracked) == 0 {
316                 return
317         }
318
319         trackSyms := make([]*obj.LSym, 0, len(tracked))
320         for sym := range tracked {
321                 trackSyms = append(trackSyms, sym)
322         }
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)
326                 r.Sym = sym
327                 r.Type = objabi.R_USEFIELD
328         }
329 }
330
331 // largeStack is info about a function whose stack frame is too large (rare).
332 type largeStack struct {
333         locals int64
334         args   int64
335         callee int64
336         pos    src.XPos
337 }
338
339 var (
340         largeStackFramesMu sync.Mutex // protects largeStackFrames
341         largeStackFrames   []largeStack
342 )
343
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)
348         })
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)
352                 } else {
353                         base.ErrorfAt(large.pos, 0, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
354                 }
355         }
356 }