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) {
    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  		// Initialize ABI wrappers if necessary.
    53  		ir.InitLSym(fn, false)
    54  		types.CalcSize(fn.Type())
    55  		a := ssagen.AbiForBodylessFuncStackMap(fn)
    56  		abiInfo := a.ABIAnalyzeFuncType(fn.Type()) // abiInfo has spill/home locations for wrapper
    57  		if fn.ABI == obj.ABI0 {
    58  			// The current args_stackmap generation assumes the function
    59  			// is ABI0, and only ABI0 assembly function can have a FUNCDATA
    60  			// reference to args_stackmap (see cmd/internal/obj/plist.go:Flushplist).
    61  			// So avoid introducing an args_stackmap if the func is not ABI0.
    62  			liveness.WriteFuncMap(fn, abiInfo)
    63  
    64  			x := ssagen.EmitArgInfo(fn, abiInfo)
    65  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
    66  		}
    67  		return
    68  	}
    69  
    70  	errorsBefore := base.Errors()
    71  
    72  	todo := []*ir.Func{fn}
    73  	for len(todo) > 0 {
    74  		next := todo[len(todo)-1]
    75  		todo = todo[:len(todo)-1]
    76  
    77  		prepareFunc(next)
    78  		todo = append(todo, next.Closures...)
    79  	}
    80  
    81  	if base.Errors() > errorsBefore {
    82  		return
    83  	}
    84  
    85  	// Enqueue just fn itself. compileFunctions will handle
    86  	// scheduling compilation of its closures after it's done.
    87  	compilequeue = append(compilequeue, fn)
    88  }
    89  
    90  // prepareFunc handles any remaining frontend compilation tasks that
    91  // aren't yet safe to perform concurrently.
    92  func prepareFunc(fn *ir.Func) {
    93  	// Set up the function's LSym early to avoid data races with the assemblers.
    94  	// Do this before walk, as walk needs the LSym to set attributes/relocations
    95  	// (e.g. in MarkTypeUsedInInterface).
    96  	ir.InitLSym(fn, true)
    97  
    98  	// If this function is a compiler-generated outlined global map
    99  	// initializer function, register its LSym for later processing.
   100  	if staticinit.MapInitToVar != nil {
   101  		if _, ok := staticinit.MapInitToVar[fn]; ok {
   102  			ssagen.RegisterMapInitLsym(fn.Linksym())
   103  		}
   104  	}
   105  
   106  	// Calculate parameter offsets.
   107  	types.CalcSize(fn.Type())
   108  
   109  	// Generate wrappers between Go ABI and Wasm ABI, for a wasmexport
   110  	// function.
   111  	// Must be done after InitLSym and CalcSize.
   112  	ssagen.GenWasmExportWrapper(fn)
   113  
   114  	ir.CurFunc = fn
   115  	walk.Walk(fn)
   116  	ir.CurFunc = nil // enforce no further uses of CurFunc
   117  
   118  	base.Ctxt.DwTextCount++
   119  }
   120  
   121  // compileFunctions compiles all functions in compilequeue.
   122  // It fans out nBackendWorkers to do the work
   123  // and waits for them to complete.
   124  func compileFunctions(profile *pgoir.Profile) {
   125  	if race.Enabled {
   126  		// Randomize compilation order to try to shake out races.
   127  		tmp := make([]*ir.Func, len(compilequeue))
   128  		perm := rand.Perm(len(compilequeue))
   129  		for i, v := range perm {
   130  			tmp[v] = compilequeue[i]
   131  		}
   132  		copy(compilequeue, tmp)
   133  	} else {
   134  		// Compile the longest functions first,
   135  		// since they're most likely to be the slowest.
   136  		// This helps avoid stragglers.
   137  		slices.SortFunc(compilequeue, func(a, b *ir.Func) int {
   138  			return cmp.Compare(len(b.Body), len(a.Body))
   139  		})
   140  	}
   141  
   142  	// By default, we perform work right away on the current goroutine
   143  	// as the solo worker.
   144  	queue := func(work func(int)) {
   145  		work(0)
   146  	}
   147  
   148  	if nWorkers := base.Flag.LowerC; nWorkers > 1 {
   149  		// For concurrent builds, we allow the work queue
   150  		// to grow arbitrarily large, but only nWorkers work items
   151  		// can be running concurrently.
   152  		workq := make(chan func(int))
   153  		done := make(chan int)
   154  		go func() {
   155  			ids := make([]int, nWorkers)
   156  			for i := range ids {
   157  				ids[i] = i
   158  			}
   159  			var pending []func(int)
   160  			for {
   161  				select {
   162  				case work := <-workq:
   163  					pending = append(pending, work)
   164  				case id := <-done:
   165  					ids = append(ids, id)
   166  				}
   167  				for len(pending) > 0 && len(ids) > 0 {
   168  					work := pending[len(pending)-1]
   169  					id := ids[len(ids)-1]
   170  					pending = pending[:len(pending)-1]
   171  					ids = ids[:len(ids)-1]
   172  					go func() {
   173  						work(id)
   174  						done <- id
   175  					}()
   176  				}
   177  			}
   178  		}()
   179  		queue = func(work func(int)) {
   180  			workq <- work
   181  		}
   182  	}
   183  
   184  	var wg sync.WaitGroup
   185  	var compile func([]*ir.Func)
   186  	compile = func(fns []*ir.Func) {
   187  		wg.Add(len(fns))
   188  		for _, fn := range fns {
   189  			fn := fn
   190  			queue(func(worker int) {
   191  				ssagen.Compile(fn, worker, profile)
   192  				compile(fn.Closures)
   193  				wg.Done()
   194  			})
   195  		}
   196  	}
   197  
   198  	types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
   199  	base.Ctxt.InParallel = true
   200  
   201  	compile(compilequeue)
   202  	compilequeue = nil
   203  	wg.Wait()
   204  
   205  	base.Ctxt.InParallel = false
   206  	types.CalcSizeDisabled = false
   207  }
   208  

View as plain text