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