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