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