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  	"internal/race"
     9  	"math/rand"
    10  	"sort"
    11  	"sync"
    12  
    13  	"cmd/compile/internal/base"
    14  	"cmd/compile/internal/ir"
    15  	"cmd/compile/internal/liveness"
    16  	"cmd/compile/internal/objw"
    17  	"cmd/compile/internal/pgoir"
    18  	"cmd/compile/internal/ssagen"
    19  	"cmd/compile/internal/staticinit"
    20  	"cmd/compile/internal/types"
    21  	"cmd/compile/internal/walk"
    22  	"cmd/internal/obj"
    23  )
    24  
    25  // "Portable" code generation.
    26  
    27  var (
    28  	compilequeue []*ir.Func // functions waiting to be compiled
    29  )
    30  
    31  func enqueueFunc(fn *ir.Func) {
    32  	if ir.CurFunc != nil {
    33  		base.FatalfAt(fn.Pos(), "enqueueFunc %v inside %v", fn, ir.CurFunc)
    34  	}
    35  
    36  	if ir.FuncName(fn) == "_" {
    37  		// Skip compiling blank functions.
    38  		// Frontend already reported any spec-mandated errors (#29870).
    39  		return
    40  	}
    41  
    42  	// Don't try compiling dead hidden closure.
    43  	if fn.IsDeadcodeClosure() {
    44  		return
    45  	}
    46  
    47  	if clo := fn.OClosure; clo != nil && !ir.IsTrivialClosure(clo) {
    48  		return // we'll get this as part of its enclosing function
    49  	}
    50  
    51  	if ssagen.CreateWasmImportWrapper(fn) {
    52  		return
    53  	}
    54  
    55  	if len(fn.Body) == 0 {
    56  		// Initialize ABI wrappers if necessary.
    57  		ir.InitLSym(fn, false)
    58  		types.CalcSize(fn.Type())
    59  		a := ssagen.AbiForBodylessFuncStackMap(fn)
    60  		abiInfo := a.ABIAnalyzeFuncType(fn.Type()) // abiInfo has spill/home locations for wrapper
    61  		liveness.WriteFuncMap(fn, abiInfo)
    62  		if fn.ABI == obj.ABI0 {
    63  			x := ssagen.EmitArgInfo(fn, abiInfo)
    64  			objw.Global(x, int32(len(x.P)), obj.RODATA|obj.LOCAL)
    65  		}
    66  		return
    67  	}
    68  
    69  	errorsBefore := base.Errors()
    70  
    71  	todo := []*ir.Func{fn}
    72  	for len(todo) > 0 {
    73  		next := todo[len(todo)-1]
    74  		todo = todo[:len(todo)-1]
    75  
    76  		prepareFunc(next)
    77  		todo = append(todo, next.Closures...)
    78  	}
    79  
    80  	if base.Errors() > errorsBefore {
    81  		return
    82  	}
    83  
    84  	// Enqueue just fn itself. compileFunctions will handle
    85  	// scheduling compilation of its closures after it's done.
    86  	compilequeue = append(compilequeue, fn)
    87  }
    88  
    89  // prepareFunc handles any remaining frontend compilation tasks that
    90  // aren't yet safe to perform concurrently.
    91  func prepareFunc(fn *ir.Func) {
    92  	// Set up the function's LSym early to avoid data races with the assemblers.
    93  	// Do this before walk, as walk needs the LSym to set attributes/relocations
    94  	// (e.g. in MarkTypeUsedInInterface).
    95  	ir.InitLSym(fn, true)
    96  
    97  	// If this function is a compiler-generated outlined global map
    98  	// initializer function, register its LSym for later processing.
    99  	if staticinit.MapInitToVar != nil {
   100  		if _, ok := staticinit.MapInitToVar[fn]; ok {
   101  			ssagen.RegisterMapInitLsym(fn.Linksym())
   102  		}
   103  	}
   104  
   105  	// Calculate parameter offsets.
   106  	types.CalcSize(fn.Type())
   107  
   108  	ir.CurFunc = fn
   109  	walk.Walk(fn)
   110  	ir.CurFunc = nil // enforce no further uses of CurFunc
   111  }
   112  
   113  // compileFunctions compiles all functions in compilequeue.
   114  // It fans out nBackendWorkers to do the work
   115  // and waits for them to complete.
   116  func compileFunctions(profile *pgoir.Profile) {
   117  	if race.Enabled {
   118  		// Randomize compilation order to try to shake out races.
   119  		tmp := make([]*ir.Func, len(compilequeue))
   120  		perm := rand.Perm(len(compilequeue))
   121  		for i, v := range perm {
   122  			tmp[v] = compilequeue[i]
   123  		}
   124  		copy(compilequeue, tmp)
   125  	} else {
   126  		// Compile the longest functions first,
   127  		// since they're most likely to be the slowest.
   128  		// This helps avoid stragglers.
   129  		sort.Slice(compilequeue, func(i, j int) bool {
   130  			return len(compilequeue[i].Body) > len(compilequeue[j].Body)
   131  		})
   132  	}
   133  
   134  	// By default, we perform work right away on the current goroutine
   135  	// as the solo worker.
   136  	queue := func(work func(int)) {
   137  		work(0)
   138  	}
   139  
   140  	if nWorkers := base.Flag.LowerC; nWorkers > 1 {
   141  		// For concurrent builds, we allow the work queue
   142  		// to grow arbitrarily large, but only nWorkers work items
   143  		// can be running concurrently.
   144  		workq := make(chan func(int))
   145  		done := make(chan int)
   146  		go func() {
   147  			ids := make([]int, nWorkers)
   148  			for i := range ids {
   149  				ids[i] = i
   150  			}
   151  			var pending []func(int)
   152  			for {
   153  				select {
   154  				case work := <-workq:
   155  					pending = append(pending, work)
   156  				case id := <-done:
   157  					ids = append(ids, id)
   158  				}
   159  				for len(pending) > 0 && len(ids) > 0 {
   160  					work := pending[len(pending)-1]
   161  					id := ids[len(ids)-1]
   162  					pending = pending[:len(pending)-1]
   163  					ids = ids[:len(ids)-1]
   164  					go func() {
   165  						work(id)
   166  						done <- id
   167  					}()
   168  				}
   169  			}
   170  		}()
   171  		queue = func(work func(int)) {
   172  			workq <- work
   173  		}
   174  	}
   175  
   176  	var wg sync.WaitGroup
   177  	var compile func([]*ir.Func)
   178  	compile = func(fns []*ir.Func) {
   179  		wg.Add(len(fns))
   180  		for _, fn := range fns {
   181  			fn := fn
   182  			queue(func(worker int) {
   183  				ssagen.Compile(fn, worker, profile)
   184  				compile(fn.Closures)
   185  				wg.Done()
   186  			})
   187  		}
   188  	}
   189  
   190  	types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
   191  	base.Ctxt.InParallel = true
   192  
   193  	compile(compilequeue)
   194  	compilequeue = nil
   195  	wg.Wait()
   196  
   197  	base.Ctxt.InParallel = false
   198  	types.CalcSizeDisabled = false
   199  }
   200  

View as plain text