Source file src/cmd/compile/internal/gc/compile.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 gc
     6  
     7  import (
     8  	"cmp"
     9  	"internal/race"
    10  	"math/rand"
    11  	"slices"
    12  	"sync"
    13  
    14  	"cmd/compile/internal/base"
    15  	"cmd/compile/internal/ir"
    16  	"cmd/compile/internal/liveness"
    17  	"cmd/compile/internal/objw"
    18  	"cmd/compile/internal/pgoir"
    19  	"cmd/compile/internal/ssa"
    20  	"cmd/compile/internal/ssagen"
    21  	"cmd/compile/internal/staticinit"
    22  	"cmd/compile/internal/types"
    23  	"cmd/compile/internal/walk"
    24  	"cmd/internal/obj"
    25  )
    26  
    27  // "Portable" code generation.
    28  
    29  var (
    30  	compilequeue []*ir.Func // functions waiting to be compiled
    31  )
    32  
    33  func enqueueFunc(fn *ir.Func, symABIs *ssagen.SymABIs) {
    34  	if ir.CurFunc != nil {
    35  		base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
    36  	}
    37  
    38  	if ir.FuncName(fn) == "_" {
    39  		// Skip compiling blank functions.
    40  		// Frontend already reported any spec-mandated errors (#29870).
    41  		return
    42  	}
    43  
    44  	if fn.IsClosure() {
    45  		return // we'll get this as part of its enclosing function
    46  	}
    47  
    48  	if ssagen.CreateWasmImportWrapper(fn) {
    49  		return
    50  	}
    51  
    52  	if len(fn.Body) == 0 {
    53  		if ir.IsIntrinsicSym(fn.Sym()) && fn.Sym().Linkname == "" && !symABIs.HasDef(fn.Sym()) {
    54  			// Generate the function body for a bodyless intrinsic, in case it
    55  			// is used in a non-call context (e.g. as a function pointer).
    56  			// We skip functions defined in assembly, or has a linkname (which
    57  			// could be defined in another package).
    58  			ssagen.GenIntrinsicBody(fn)
    59  		} else {
    60  			// Initialize ABI wrappers if necessary.
    61  			ir.InitLSym(fn, false)
    62  			types.CalcSize(fn.Type())
    63  			a := ssagen.AbiForBodylessFuncStackMap(fn)
    64  			abiInfo := a.ABIAnalyzeFuncType(fn.Type()) // abiInfo has spill/home locations for wrapper
    65  			if fn.ABI == obj.ABI0 {
    66  				// The current args_stackmap generation assumes the function
    67  				// is ABI0, and only ABI0 assembly function can have a FUNCDATA
    68  				// reference to args_stackmap (see cmd/internal/obj/plist.go:Flushplist).
    69  				// So avoid introducing an args_stackmap if the func is not ABI0.
    70  				liveness.WriteFuncMap(fn, abiInfo)
    71  
    72  				x := ssagen.EmitArgInfo(fn, abiInfo)
    73  				objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
    74  			}
    75  			return
    76  		}
    77  	}
    78  
    79  	errorsBefore := base.Errors()
    80  
    81  	todo := []*ir.Func{fn}
    82  	for len(todo) > 0 {
    83  		next := todo[len(todo)-1]
    84  		todo = todo[:len(todo)-1]
    85  
    86  		prepareFunc(next)
    87  		todo = append(todo, next.Closures...)
    88  	}
    89  
    90  	if base.Errors() > errorsBefore {
    91  		return
    92  	}
    93  
    94  	// Enqueue just fn itself. compileFunctions will handle
    95  	// scheduling compilation of its closures after it's done.
    96  	compilequeue = append(compilequeue, fn)
    97  }
    98  
    99  // prepareFunc handles any remaining frontend compilation tasks that
   100  // aren't yet safe to perform concurrently.
   101  func prepareFunc(fn *ir.Func) {
   102  	// Set up the function's LSym early to avoid data races with the assemblers.
   103  	// Do this before walk, as walk needs the LSym to set attributes/relocations
   104  	// (e.g. in MarkTypeUsedInInterface).
   105  	ir.InitLSym(fn, true)
   106  
   107  	// If this function is a compiler-generated outlined global map
   108  	// initializer function, register its LSym for later processing.
   109  	if staticinit.MapInitToVar != nil {
   110  		if _, ok := staticinit.MapInitToVar[fn]; ok {
   111  			ssagen.RegisterMapInitLsym(fn.Linksym())
   112  		}
   113  	}
   114  
   115  	// Calculate parameter offsets.
   116  	types.CalcSize(fn.Type())
   117  
   118  	// Generate wrappers between Go ABI and Wasm ABI, for a wasmexport
   119  	// function.
   120  	// Must be done after InitLSym and CalcSize.
   121  	ssagen.GenWasmExportWrapper(fn)
   122  
   123  	ir.CurFunc = fn
   124  	walk.Walk(fn)
   125  	if ir.MatchAstDump(fn, "walk") {
   126  		ir.AstDump(fn, "walk, "+ir.FuncName(fn))
   127  	}
   128  	ir.CurFunc = nil // enforce no further uses of CurFunc
   129  
   130  	base.Ctxt.DwTextCount++
   131  }
   132  
   133  // compileFunctions compiles all functions in compilequeue.
   134  // It fans out nBackendWorkers to do the work
   135  // and waits for them to complete.
   136  func compileFunctions(profile *pgoir.Profile) {
   137  	if race.Enabled {
   138  		// Randomize compilation order to try to shake out races.
   139  		tmp := make([]*ir.Func, len(compilequeue))
   140  		perm := rand.Perm(len(compilequeue))
   141  		for i, v := range perm {
   142  			tmp[v] = compilequeue[i]
   143  		}
   144  		copy(compilequeue, tmp)
   145  	} else {
   146  		// Compile the longest functions first,
   147  		// since they're most likely to be the slowest.
   148  		// This helps avoid stragglers.
   149  		// Since we remove from the end of the slice queue,
   150  		// that means shortest to longest.
   151  		slices.SortFunc(compilequeue, func(a, b *ir.Func) int {
   152  			return cmp.Compare(a.NumPreWalkNodes, b.NumPreWalkNodes)
   153  		})
   154  	}
   155  
   156  	var mu sync.Mutex
   157  	var wg sync.WaitGroup
   158  	mu.Lock()
   159  
   160  	for workerId := range base.Flag.LowerC {
   161  		// TODO: replace with wg.Go when the oldest bootstrap has it.
   162  		// With the current policy, that'd be go1.27.
   163  		wg.Add(1)
   164  		go func() {
   165  			defer wg.Done()
   166  			var closures []*ir.Func
   167  			for {
   168  				mu.Lock()
   169  				compilequeue = append(compilequeue, closures...)
   170  				remaining := len(compilequeue)
   171  				if remaining == 0 {
   172  					mu.Unlock()
   173  					return
   174  				}
   175  				fn := compilequeue[len(compilequeue)-1]
   176  				compilequeue = compilequeue[:len(compilequeue)-1]
   177  				mu.Unlock()
   178  				ssagen.Compile(fn, workerId, profile)
   179  				closures = fn.Closures
   180  			}
   181  		}()
   182  	}
   183  
   184  	types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
   185  	base.Ctxt.InParallel = true
   186  
   187  	mu.Unlock()
   188  	wg.Wait()
   189  	compilequeue = nil
   190  
   191  	base.Ctxt.InParallel = false
   192  	types.CalcSizeDisabled = false
   193  
   194  	ssa.PostCompile()
   195  }
   196  

View as plain text