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

View as plain text