1
2
3
4
5 package ssagen
6
7 import (
8 "fmt"
9 "internal/buildcfg"
10 "os"
11 "slices"
12 "sort"
13 "strings"
14 "sync"
15
16 "cmd/compile/internal/base"
17 "cmd/compile/internal/inline"
18 "cmd/compile/internal/ir"
19 "cmd/compile/internal/liveness"
20 "cmd/compile/internal/objw"
21 "cmd/compile/internal/pgoir"
22 "cmd/compile/internal/ssa"
23 "cmd/compile/internal/types"
24 "cmd/internal/obj"
25 "cmd/internal/objabi"
26 "cmd/internal/src"
27 )
28
29
30 func cmpstackvarlt(a, b *ir.Name, mls *liveness.MergeLocalsState) bool {
31
32 if needAlloc(a) != needAlloc(b) {
33 return needAlloc(b)
34 }
35
36
37
38 if !needAlloc(a) {
39 return a.FrameOffset() < b.FrameOffset()
40 }
41
42
43
44
45 if mls != nil {
46 aFollow := mls.Subsumed(a)
47 bFollow := mls.Subsumed(b)
48 if aFollow != bFollow {
49 return bFollow
50 }
51 }
52
53
54
55 if a.Used() != b.Used() {
56 return a.Used()
57 }
58
59
60
61 ap := a.Type().HasPointers()
62 bp := b.Type().HasPointers()
63 if ap != bp {
64 return ap
65 }
66
67
68
69 ap = a.Needzero()
70 bp = b.Needzero()
71 if ap != bp {
72 return ap
73 }
74
75
76
77 if a.Type().Alignment() != b.Type().Alignment() {
78 return a.Type().Alignment() > b.Type().Alignment()
79 }
80
81
82
83
84 if a.OpenDeferSlot() != b.OpenDeferSlot() {
85 return a.OpenDeferSlot()
86 }
87
88
89
90
91
92
93 if a.OpenDeferSlot() {
94 return a.FrameOffset() > b.FrameOffset()
95 }
96
97
98 return a.Sym().Name < b.Sym().Name
99 }
100
101
102
103
104 func needAlloc(n *ir.Name) bool {
105 if n.Op() != ir.ONAME {
106 base.FatalfAt(n.Pos(), "%v has unexpected Op %v", n, n.Op())
107 }
108
109 switch n.Class {
110 case ir.PAUTO:
111 return true
112 case ir.PPARAM:
113 return false
114 case ir.PPARAMOUT:
115 return n.IsOutputParamInRegisters()
116
117 default:
118 base.FatalfAt(n.Pos(), "%v has unexpected Class %v", n, n.Class)
119 return false
120 }
121 }
122
123 func (s *ssafn) AllocFrame(f *ssa.Func) {
124 s.stksize = 0
125 s.stkptrsize = 0
126 s.stkalign = int64(types.RegSize)
127 fn := s.curfn
128
129
130 for _, ln := range fn.Dcl {
131 if ln.OpenDeferSlot() {
132
133
134
135
136 continue
137 }
138
139 if needAlloc(ln) {
140 ln.SetUsed(false)
141 }
142 }
143
144 for _, l := range f.RegAlloc {
145 if ls, ok := l.(ssa.LocalSlot); ok {
146 ls.N.SetUsed(true)
147 }
148 }
149
150 for _, b := range f.Blocks {
151 for _, v := range b.Values {
152 if n, ok := v.Aux.(*ir.Name); ok {
153 switch n.Class {
154 case ir.PPARAMOUT:
155 if n.IsOutputParamInRegisters() && v.Op == ssa.OpVarDef {
156
157
158 continue
159 }
160 fallthrough
161 case ir.PPARAM, ir.PAUTO:
162 n.SetUsed(true)
163 }
164 }
165 }
166 }
167
168 var mls *liveness.MergeLocalsState
169 var leaders map[*ir.Name]int64
170 if base.Debug.MergeLocals != 0 {
171 mls = liveness.MergeLocals(fn, f)
172 if base.Debug.MergeLocalsTrace > 0 && mls != nil {
173 savedNP, savedP := mls.EstSavings()
174 fmt.Fprintf(os.Stderr, "%s: %d bytes of stack space saved via stack slot merging (%d nonpointer %d pointer)\n", ir.FuncName(fn), savedNP+savedP, savedNP, savedP)
175 if base.Debug.MergeLocalsTrace > 1 {
176 fmt.Fprintf(os.Stderr, "=-= merge locals state for %v:\n%v",
177 fn, mls)
178 }
179 }
180 leaders = make(map[*ir.Name]int64)
181 }
182
183
184
185
186 sort.SliceStable(fn.Dcl, func(i, j int) bool {
187 return cmpstackvarlt(fn.Dcl[i], fn.Dcl[j], mls)
188 })
189
190 if mls != nil {
191
192
193 followers := []*ir.Name{}
194 newdcl := make([]*ir.Name, 0, len(fn.Dcl))
195 for i := 0; i < len(fn.Dcl); i++ {
196 n := fn.Dcl[i]
197 if mls.Subsumed(n) {
198 continue
199 }
200 newdcl = append(newdcl, n)
201 if mls.IsLeader(n) {
202 followers = mls.Followers(n, followers)
203
204 newdcl = append(newdcl, followers...)
205 }
206 }
207 fn.Dcl = newdcl
208 }
209
210 if base.Debug.MergeLocalsTrace > 1 && mls != nil {
211 fmt.Fprintf(os.Stderr, "=-= sorted DCL for %v:\n", fn)
212 for i, v := range fn.Dcl {
213 if !ssa.IsMergeCandidate(v) {
214 continue
215 }
216 fmt.Fprintf(os.Stderr, " %d: %q isleader=%v subsumed=%v used=%v sz=%d align=%d t=%s\n", i, v.Sym().Name, mls.IsLeader(v), mls.Subsumed(v), v.Used(), v.Type().Size(), v.Type().Alignment(), v.Type().String())
217 }
218 }
219
220
221 lastHasPtr := false
222 for i, n := range fn.Dcl {
223 if n.Op() != ir.ONAME || n.Class != ir.PAUTO && !(n.Class == ir.PPARAMOUT && n.IsOutputParamInRegisters()) {
224
225 continue
226 }
227 if mls != nil && mls.Subsumed(n) {
228 continue
229 }
230 if !n.Used() {
231 fn.DebugInfo.(*ssa.FuncDebug).OptDcl = fn.Dcl[i:]
232 fn.Dcl = fn.Dcl[:i]
233 break
234 }
235 types.CalcSize(n.Type())
236 w := n.Type().Size()
237 if w >= types.MaxWidth || w < 0 {
238 base.Fatalf("bad width")
239 }
240 if w == 0 && lastHasPtr {
241
242
243
244
245 w = 1
246 }
247 s.stksize += w
248 s.stksize = types.RoundUp(s.stksize, n.Type().Alignment())
249 if n.Type().Alignment() > int64(types.RegSize) {
250 s.stkalign = n.Type().Alignment()
251 }
252 if n.Type().HasPointers() {
253 s.stkptrsize = s.stksize
254 lastHasPtr = true
255 } else {
256 lastHasPtr = false
257 }
258 n.SetFrameOffset(-s.stksize)
259 if mls != nil && mls.IsLeader(n) {
260 leaders[n] = -s.stksize
261 }
262 }
263
264 if mls != nil {
265
266
267 for i := 0; i < len(fn.Dcl); i++ {
268 n := fn.Dcl[i]
269 if !mls.Subsumed(n) {
270 continue
271 }
272 leader := mls.Leader(n)
273 off, ok := leaders[leader]
274 if !ok {
275 panic("internal error missing leader")
276 }
277
278
279 n.SetFrameOffset(off)
280 }
281
282 if base.Debug.MergeLocalsTrace > 1 {
283 fmt.Fprintf(os.Stderr, "=-= stack layout for %v:\n", fn)
284 for i, v := range fn.Dcl {
285 if v.Op() != ir.ONAME || (v.Class != ir.PAUTO && !(v.Class == ir.PPARAMOUT && v.IsOutputParamInRegisters())) {
286 continue
287 }
288 fmt.Fprintf(os.Stderr, " %d: %q frameoff %d isleader=%v subsumed=%v sz=%d align=%d t=%s\n", i, v.Sym().Name, v.FrameOffset(), mls.IsLeader(v), mls.Subsumed(v), v.Type().Size(), v.Type().Alignment(), v.Type().String())
289 }
290 }
291 }
292
293 s.stksize = types.RoundUp(s.stksize, s.stkalign)
294 s.stkptrsize = types.RoundUp(s.stkptrsize, s.stkalign)
295 }
296
297 const maxStackSize = 1 << 30
298
299
300
301
302
303 func Compile(fn *ir.Func, worker int, profile *pgoir.Profile) {
304 f := buildssa(fn, worker, inline.IsPgoHotFunc(fn, profile) || inline.HasPgoHotInline(fn))
305
306 if f.Frontend().(*ssafn).stksize >= maxStackSize || f.OwnAux.ArgWidth() >= maxStackSize {
307 largeStackFramesMu.Lock()
308 largeStackFrames = append(largeStackFrames, largeStack{locals: f.Frontend().(*ssafn).stksize, args: f.OwnAux.ArgWidth(), pos: fn.Pos()})
309 largeStackFramesMu.Unlock()
310 return
311 }
312 pp := objw.NewProgs(fn, worker)
313 defer pp.Free()
314 genssa(f, pp)
315
316
317
318
319
320
321 if pp.Text.To.Offset >= maxStackSize {
322 largeStackFramesMu.Lock()
323 locals := f.Frontend().(*ssafn).stksize
324 largeStackFrames = append(largeStackFrames, largeStack{locals: locals, args: f.OwnAux.ArgWidth(), callee: pp.Text.To.Offset - locals, pos: fn.Pos()})
325 largeStackFramesMu.Unlock()
326 return
327 }
328
329 pp.Flush()
330
331
332
333
334 if fn.IsPackageInit() && base.Debug.WrapGlobalMapCtl != 1 {
335 weakenGlobalMapInitRelocs(fn)
336 }
337
338
339 fieldtrack(pp.Text.From.Sym, fn.FieldTrack)
340 }
341
342
343
344 var globalMapInitLsyms map[*obj.LSym]struct{}
345
346
347
348 func RegisterMapInitLsym(s *obj.LSym) {
349 if globalMapInitLsyms == nil {
350 globalMapInitLsyms = make(map[*obj.LSym]struct{})
351 }
352 globalMapInitLsyms[s] = struct{}{}
353 }
354
355
356
357
358
359 func weakenGlobalMapInitRelocs(fn *ir.Func) {
360 if globalMapInitLsyms == nil {
361 return
362 }
363 for i := range fn.LSym.R {
364 tgt := fn.LSym.R[i].Sym
365 if tgt == nil {
366 continue
367 }
368 if _, ok := globalMapInitLsyms[tgt]; !ok {
369 continue
370 }
371 if base.Debug.WrapGlobalMapDbg > 1 {
372 fmt.Fprintf(os.Stderr, "=-= weakify fn %v reloc %d %+v\n", fn, i,
373 fn.LSym.R[i])
374 }
375
376 fn.LSym.R[i].Type |= objabi.R_WEAK
377 }
378 }
379
380
381
382
383 func StackOffset(slot ssa.LocalSlot) int32 {
384 n := slot.N
385 var off int64
386 switch n.Class {
387 case ir.PPARAM, ir.PPARAMOUT:
388 if !n.IsOutputParamInRegisters() {
389 off = n.FrameOffset() + base.Ctxt.Arch.FixedFrameSize
390 break
391 }
392 fallthrough
393 case ir.PAUTO:
394 off = n.FrameOffset()
395 if base.Ctxt.Arch.FixedFrameSize == 0 {
396 off -= int64(types.PtrSize)
397 }
398 if buildcfg.FramePointerEnabled {
399 off -= int64(types.PtrSize)
400 }
401 }
402 return int32(off + slot.Off)
403 }
404
405
406
407 func fieldtrack(fnsym *obj.LSym, tracked map[*obj.LSym]struct{}) {
408 if fnsym == nil {
409 return
410 }
411 if !buildcfg.Experiment.FieldTrack || len(tracked) == 0 {
412 return
413 }
414
415 trackSyms := make([]*obj.LSym, 0, len(tracked))
416 for sym := range tracked {
417 trackSyms = append(trackSyms, sym)
418 }
419 slices.SortFunc(trackSyms, func(a, b *obj.LSym) int { return strings.Compare(a.Name, b.Name) })
420 for _, sym := range trackSyms {
421 fnsym.AddRel(base.Ctxt, obj.Reloc{Type: objabi.R_USEFIELD, Sym: sym})
422 }
423 }
424
425
426 type largeStack struct {
427 locals int64
428 args int64
429 callee int64
430 pos src.XPos
431 }
432
433 var (
434 largeStackFramesMu sync.Mutex
435 largeStackFrames []largeStack
436 )
437
438 func CheckLargeStacks() {
439
440 sort.Slice(largeStackFrames, func(i, j int) bool {
441 return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
442 })
443 for _, large := range largeStackFrames {
444 if large.callee != 0 {
445 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)
446 } else {
447 base.ErrorfAt(large.pos, 0, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
448 }
449 }
450 }
451
View as plain text