aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/gc/compile.go
blob: 1b3dd672f3f0f8dfc77a387ee8298bf66abe9c2d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gc

import (
	"internal/race"
	"math/rand"
	"sort"
	"sync"

	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/compile/internal/liveness"
	"cmd/compile/internal/reflectdata"
	"cmd/compile/internal/ssagen"
	"cmd/compile/internal/typecheck"
	"cmd/compile/internal/types"
	"cmd/compile/internal/walk"
)

// "Portable" code generation.

var (
	compilequeue []*ir.Func // functions waiting to be compiled
)

func funccompile(fn *ir.Func) {
	if ir.CurFunc != nil {
		base.Fatalf("funccompile %v inside %v", fn.Sym(), ir.CurFunc.Sym())
	}

	if fn.Type() == nil {
		if base.Errors() == 0 {
			base.Fatalf("funccompile missing type")
		}
		return
	}

	// assign parameter offsets
	types.CalcSize(fn.Type())

	if len(fn.Body) == 0 {
		// Initialize ABI wrappers if necessary.
		ssagen.InitLSym(fn, false)
		liveness.WriteFuncMap(fn)
		return
	}

	typecheck.DeclContext = ir.PAUTO
	ir.CurFunc = fn
	compile(fn)
	ir.CurFunc = nil
	typecheck.DeclContext = ir.PEXTERN
}

func compile(fn *ir.Func) {
	// Set up the function's LSym early to avoid data races with the assemblers.
	// Do this before walk, as walk needs the LSym to set attributes/relocations
	// (e.g. in markTypeUsedInInterface).
	ssagen.InitLSym(fn, true)

	errorsBefore := base.Errors()
	walk.Walk(fn)
	if base.Errors() > errorsBefore {
		return
	}

	// From this point, there should be no uses of Curfn. Enforce that.
	ir.CurFunc = nil

	if ir.FuncName(fn) == "_" {
		// We don't need to generate code for this function, just report errors in its body.
		// At this point we've generated any errors needed.
		// (Beyond here we generate only non-spec errors, like "stack frame too large".)
		// See issue 29870.
		return
	}

	// Make sure type syms are declared for all types that might
	// be types of stack objects. We need to do this here
	// because symbols must be allocated before the parallel
	// phase of the compiler.
	for _, n := range fn.Dcl {
		switch n.Class_ {
		case ir.PPARAM, ir.PPARAMOUT, ir.PAUTO:
			if liveness.ShouldTrack(n) && n.Addrtaken() {
				reflectdata.WriteType(n.Type())
				// Also make sure we allocate a linker symbol
				// for the stack object data, for the same reason.
				if fn.LSym.Func().StackObjects == nil {
					fn.LSym.Func().StackObjects = base.Ctxt.Lookup(fn.LSym.Name + ".stkobj")
				}
			}
		}
	}

	if compilenow(fn) {
		ssagen.Compile(fn, 0)
	} else {
		compilequeue = append(compilequeue, fn)
	}
}

// compilenow reports whether to compile immediately.
// If functions are not compiled immediately,
// they are enqueued in compilequeue,
// which is drained by compileFunctions.
func compilenow(fn *ir.Func) bool {
	// Issue 38068: if this function is a method AND an inline
	// candidate AND was not inlined (yet), put it onto the compile
	// queue instead of compiling it immediately. This is in case we
	// wind up inlining it into a method wrapper that is generated by
	// compiling a function later on in the Target.Decls list.
	if ir.IsMethod(fn) && isInlinableButNotInlined(fn) {
		return false
	}
	return base.Flag.LowerC == 1 && base.Debug.CompileLater == 0
}

// compileFunctions compiles all functions in compilequeue.
// It fans out nBackendWorkers to do the work
// and waits for them to complete.
func compileFunctions() {
	if len(compilequeue) != 0 {
		types.CalcSizeDisabled = true // not safe to calculate sizes concurrently
		if race.Enabled {
			// Randomize compilation order to try to shake out races.
			tmp := make([]*ir.Func, len(compilequeue))
			perm := rand.Perm(len(compilequeue))
			for i, v := range perm {
				tmp[v] = compilequeue[i]
			}
			copy(compilequeue, tmp)
		} else {
			// Compile the longest functions first,
			// since they're most likely to be the slowest.
			// This helps avoid stragglers.
			sort.Slice(compilequeue, func(i, j int) bool {
				return len(compilequeue[i].Body) > len(compilequeue[j].Body)
			})
		}
		var wg sync.WaitGroup
		base.Ctxt.InParallel = true
		c := make(chan *ir.Func, base.Flag.LowerC)
		for i := 0; i < base.Flag.LowerC; i++ {
			wg.Add(1)
			go func(worker int) {
				for fn := range c {
					ssagen.Compile(fn, worker)
				}
				wg.Done()
			}(i)
		}
		for _, fn := range compilequeue {
			c <- fn
		}
		close(c)
		compilequeue = nil
		wg.Wait()
		base.Ctxt.InParallel = false
		types.CalcSizeDisabled = false
	}
}

// isInlinableButNotInlined returns true if 'fn' was marked as an
// inline candidate but then never inlined (presumably because we
// found no call sites).
func isInlinableButNotInlined(fn *ir.Func) bool {
	if fn.Inl == nil {
		return false
	}
	if fn.Sym() == nil {
		return true
	}
	return !fn.Linksym().WasInlined()
}