Skip to content

Commit b3e0236

Browse files
committed
go/ssa: Track created functions in the builder.
Changes the builder to be stateful and track the Functions that were created while building the current Package [or MethodValue]. Additional bookkeeping is now done whenever a synthetic functions is created. Adds an additional state to Function to track if it is done with the BUILD phase. Moves (*Program).needsMethods from emitConv to a postprocessing phase in package BUILD phase. Clarifies that anonymous functions are done at the same time as their parent. Updates golang/go#48525 Change-Id: I09d1ed2abc95d252029917f49f783a3cfd9f0a78 Reviewed-on: https://go-review.googlesource.com/c/tools/+/396876 Reviewed-by: Robert Findley <[email protected]> Trust: Tim King <[email protected]> Run-TryBot: Tim King <[email protected]> gopls-CI: kokoro <[email protected]>
1 parent 6731659 commit b3e0236

File tree

8 files changed

+222
-61
lines changed

8 files changed

+222
-61
lines changed

go/ssa/builder.go

Lines changed: 110 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,21 @@ package ssa
2828
// mutated during the CREATE phase, but during the BUILD phase they
2929
// remain constant. The sole exception is Prog.methodSets and its
3030
// related maps, which are protected by a dedicated mutex.
31+
//
32+
// Happens-before:
33+
//
34+
// The happens-before constraints (with X<Y denoting X happens-before Y) are:
35+
// - CREATE fn < fn.startBody() < fn.finishBody() < fn.built
36+
// for any function fn.
37+
// - anon.parent.startBody() < CREATE anon, and
38+
// anon.finishBody() < anon.parent().finishBody() < anon.built < fn.built
39+
// for an anonymous function anon (i.e. anon.parent() != nil).
40+
// - CREATE fn.Pkg < CREATE fn
41+
// for a declared function fn (i.e. fn.Pkg != nil)
42+
// - fn.built < BUILD pkg done
43+
// for any function fn created during the CREATE or BUILD phase of a package
44+
// pkg. This includes declared and synthetic functions.
45+
//
3146

3247
import (
3348
"fmt"
@@ -68,7 +83,12 @@ var (
6883

6984
// builder holds state associated with the package currently being built.
7085
// Its methods contain all the logic for AST-to-SSA conversion.
71-
type builder struct{}
86+
type builder struct {
87+
// Invariant: 0 <= rtypes <= finished <= created.Len()
88+
created *creator // functions created during building
89+
finished int // Invariant: create[i].built holds for i in [0,finished)
90+
rtypes int // Invariant: all of the runtime types for create[i] have been added for i in [0,rtypes)
91+
}
7292

7393
// cond emits to fn code to evaluate boolean condition e and jump
7494
// to t or f depending on its value, performing various simplifications.
@@ -558,7 +578,10 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
558578
info: fn.info,
559579
}
560580
fn.AnonFuncs = append(fn.AnonFuncs, fn2)
561-
b.buildFunction(fn2)
581+
b.created.Add(fn2)
582+
b.buildFunctionBody(fn2)
583+
// fn2 is not done BUILDing. fn2.referrers can still be updated.
584+
// fn2 is done BUILDing after fn.finishBody().
562585
if fn2.FreeVars == nil {
563586
return fn2
564587
}
@@ -713,7 +736,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
713736
case types.MethodExpr:
714737
// (*T).f or T.f, the method f from the method-set of type T.
715738
// The result is a "thunk".
716-
return emitConv(fn, makeThunk(fn.Prog, sel), tv.Type)
739+
return emitConv(fn, makeThunk(fn.Prog, sel, b.created), tv.Type)
717740

718741
case types.MethodVal:
719742
// e.f where e is an expression and f is a method.
@@ -730,7 +753,7 @@ func (b *builder) expr0(fn *Function, e ast.Expr, tv types.TypeAndValue) Value {
730753
emitTypeAssert(fn, v, rt, token.NoPos)
731754
}
732755
c := &MakeClosure{
733-
Fn: makeBound(fn.Prog, obj),
756+
Fn: makeBound(fn.Prog, obj, b.created),
734757
Bindings: []Value{v},
735758
}
736759
c.setPos(e.Sel.Pos())
@@ -2170,10 +2193,21 @@ start:
21702193

21712194
// buildFunction builds SSA code for the body of function fn. Idempotent.
21722195
func (b *builder) buildFunction(fn *Function) {
2196+
if !fn.built {
2197+
assert(fn.parent == nil, "anonymous functions should not be built by buildFunction()")
2198+
b.buildFunctionBody(fn)
2199+
fn.done()
2200+
}
2201+
}
2202+
2203+
// buildFunctionBody builds SSA code for the body of function fn.
2204+
//
2205+
// fn is not done building until fn.done() is called.
2206+
func (b *builder) buildFunctionBody(fn *Function) {
2207+
// TODO(taking): see if this check is reachable.
21732208
if fn.Blocks != nil {
21742209
return // building already started
21752210
}
2176-
21772211
var recvField *ast.FieldList
21782212
var body *ast.BlockStmt
21792213
var functype *ast.FuncType
@@ -2231,22 +2265,47 @@ func (b *builder) buildFunction(fn *Function) {
22312265
fn.finishBody()
22322266
}
22332267

2234-
// buildFuncDecl builds SSA code for the function or method declared
2235-
// by decl in package pkg.
2268+
// buildCreated does the BUILD phase for each function created by builder that is not yet BUILT.
2269+
// Functions are built using buildFunction.
22362270
//
2237-
func (b *builder) buildFuncDecl(pkg *Package, decl *ast.FuncDecl) {
2238-
id := decl.Name
2239-
if isBlankIdent(id) {
2240-
return // discard
2271+
// May add types that require runtime type information to builder.
2272+
//
2273+
func (b *builder) buildCreated() {
2274+
for ; b.finished < b.created.Len(); b.finished++ {
2275+
fn := b.created.At(b.finished)
2276+
b.buildFunction(fn)
22412277
}
2242-
fn := pkg.objects[pkg.info.Defs[id]].(*Function)
2243-
if decl.Recv == nil && id.Name == "init" {
2244-
var v Call
2245-
v.Call.Value = fn
2246-
v.setType(types.NewTuple())
2247-
pkg.init.emit(&v)
2278+
}
2279+
2280+
// Adds any needed runtime type information for the created functions.
2281+
//
2282+
// May add newly CREATEd functions that may need to be built or runtime type information.
2283+
//
2284+
// EXCLUSIVE_LOCKS_ACQUIRED(prog.methodsMu)
2285+
//
2286+
func (b *builder) needsRuntimeTypes() {
2287+
if b.created.Len() == 0 {
2288+
return
22482289
}
2249-
b.buildFunction(fn)
2290+
prog := b.created.At(0).Prog
2291+
2292+
var rtypes []types.Type
2293+
for ; b.rtypes < b.finished; b.rtypes++ {
2294+
fn := b.created.At(b.rtypes)
2295+
rtypes = append(rtypes, mayNeedRuntimeTypes(fn)...)
2296+
}
2297+
2298+
// Calling prog.needMethodsOf(T) on a basic type T is a no-op.
2299+
// Filter out the basic types to reduce acquiring prog.methodsMu.
2300+
rtypes = nonbasicTypes(rtypes)
2301+
2302+
for _, T := range rtypes {
2303+
prog.needMethodsOf(T, b.created)
2304+
}
2305+
}
2306+
2307+
func (b *builder) done() bool {
2308+
return b.rtypes >= b.created.Len()
22502309
}
22512310

22522311
// Build calls Package.Build for each package in prog.
@@ -2293,12 +2352,14 @@ func (p *Package) build() {
22932352
// that would require package creation in topological order.
22942353
for name, mem := range p.Members {
22952354
if ast.IsExported(name) {
2296-
p.Prog.needMethodsOf(mem.Type())
2355+
p.Prog.needMethodsOf(mem.Type(), &p.created)
22972356
}
22982357
}
22992358
if p.Prog.mode&LogSource != 0 {
23002359
defer logStack("build %s", p)()
23012360
}
2361+
2362+
b := builder{created: &p.created}
23022363
init := p.init
23032364
init.startBody()
23042365

@@ -2327,8 +2388,6 @@ func (p *Package) build() {
23272388
}
23282389
}
23292390

2330-
var b builder
2331-
23322391
// Initialize package-level vars in correct order.
23332392
for _, varinit := range p.info.InitOrder {
23342393
if init.Prog.mode&LogSource != 0 {
@@ -2356,13 +2415,18 @@ func (p *Package) build() {
23562415
}
23572416
}
23582417

2359-
// Build all package-level functions, init functions
2360-
// and methods, including unreachable/blank ones.
2361-
// We build them in source order, but it's not significant.
2418+
// Call all of the declared init() functions in source order.
23622419
for _, file := range p.files {
23632420
for _, decl := range file.Decls {
23642421
if decl, ok := decl.(*ast.FuncDecl); ok {
2365-
b.buildFuncDecl(p, decl)
2422+
id := decl.Name
2423+
if !isBlankIdent(id) && id.Name == "init" && decl.Recv == nil {
2424+
fn := p.objects[p.info.Defs[id]].(*Function)
2425+
var v Call
2426+
v.Call.Value = fn
2427+
v.setType(types.NewTuple())
2428+
p.init.emit(&v)
2429+
}
23662430
}
23672431
}
23682432
}
@@ -2374,8 +2438,28 @@ func (p *Package) build() {
23742438
}
23752439
init.emit(new(Return))
23762440
init.finishBody()
2441+
init.done()
2442+
2443+
// Build all CREATEd functions and add runtime types.
2444+
// These Functions include package-level functions, init functions, methods, and synthetic (including unreachable/blank ones).
2445+
// Builds any functions CREATEd while building this package.
2446+
//
2447+
// Initially the created functions for the package are:
2448+
// [init, decl0, ... , declN]
2449+
// Where decl0, ..., declN are declared functions in source order, but it's not significant.
2450+
//
2451+
// As these are built, more functions (function literals, wrappers, etc.) can be CREATEd.
2452+
// Iterate until we reach a fixed point.
2453+
//
2454+
// Wait for init() to be BUILT as that cannot be built by buildFunction().
2455+
//
2456+
for !b.done() {
2457+
b.buildCreated() // build any CREATEd and not BUILT function. May add runtime types.
2458+
b.needsRuntimeTypes() // Add all of the runtime type information. May CREATE Functions.
2459+
}
23772460

2378-
p.info = nil // We no longer need ASTs or go/types deductions.
2461+
p.info = nil // We no longer need ASTs or go/types deductions.
2462+
p.created = nil // We no longer need created functions.
23792463

23802464
if p.Prog.mode&SanityCheckFunctions != 0 {
23812465
sanityCheckPackage(p)

go/ssa/create.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,7 @@ func memberFromObject(pkg *Package, obj types.Object, syntax ast.Node) {
9595
Prog: pkg.Prog,
9696
info: pkg.info,
9797
}
98+
pkg.created.Add(fn)
9899
if syntax == nil {
99100
fn.Synthetic = "loaded from gc object file"
100101
}
@@ -152,6 +153,19 @@ func membersFromDecl(pkg *Package, decl ast.Decl) {
152153
}
153154
}
154155

156+
// creator tracks functions that have finished their CREATE phases.
157+
//
158+
// All Functions belong to the same Program. May have differing packages.
159+
//
160+
// creators are not thread-safe.
161+
type creator []*Function
162+
163+
func (c *creator) Add(fn *Function) {
164+
*c = append(*c, fn)
165+
}
166+
func (c *creator) At(i int) *Function { return (*c)[i] }
167+
func (c *creator) Len() int { return len(*c) }
168+
155169
// CreatePackage constructs and returns an SSA Package from the
156170
// specified type-checked, error-free file ASTs, and populates its
157171
// Members mapping.
@@ -182,6 +196,7 @@ func (prog *Program) CreatePackage(pkg *types.Package, files []*ast.File, info *
182196
info: p.info,
183197
}
184198
p.Members[p.init.name] = p.init
199+
p.created.Add(p.init)
185200

186201
// CREATE phase.
187202
// Allocate all package members: vars, funcs, consts and types.

go/ssa/emit.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,6 @@ func emitConv(f *Function, val Value, typ types.Type) Value {
214214
val = emitConv(f, val, types.Default(ut_src))
215215
}
216216

217-
f.Pkg.Prog.needMethodsOf(val.Type())
218217
mi := &MakeInterface{X: val}
219218
mi.setType(typ)
220219
return f.emit(mi)

go/ssa/func.go

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,22 @@ func buildReferrers(f *Function) {
207207
}
208208
}
209209

210-
// finishBody() finalizes the function after SSA code generation of its body.
210+
// mayNeedRuntimeTypes returns all of the types in the body of fn that might need runtime types.
211+
func mayNeedRuntimeTypes(fn *Function) []types.Type {
212+
var ts []types.Type
213+
for _, bb := range fn.Blocks {
214+
for _, instr := range bb.Instrs {
215+
if mi, ok := instr.(*MakeInterface); ok {
216+
ts = append(ts, mi.X.Type())
217+
}
218+
}
219+
}
220+
return ts
221+
}
222+
223+
// finishBody() finalizes the contents of the function after SSA code generation of its body.
224+
//
225+
// The function is not done being built until done() is called.
211226
func (f *Function) finishBody() {
212227
f.objects = nil
213228
f.currentBlock = nil
@@ -250,16 +265,31 @@ func (f *Function) finishBody() {
250265
f.info = nil
251266

252267
numberRegisters(f)
268+
}
253269

254-
if f.Prog.mode&PrintFunctions != 0 {
255-
printMu.Lock()
256-
f.WriteTo(os.Stdout)
257-
printMu.Unlock()
258-
}
270+
// After this, function is done with BUILD phase.
271+
func (f *Function) done() {
272+
assert(f.parent == nil, "done called on an anonymous function")
273+
274+
var visit func(*Function)
275+
visit = func(f *Function) {
276+
for _, anon := range f.AnonFuncs {
277+
visit(anon) // anon is done building before f.
278+
}
279+
280+
f.built = true // function is done with BUILD phase
259281

260-
if f.Prog.mode&SanityCheckFunctions != 0 {
261-
mustSanityCheck(f, nil)
282+
if f.Prog.mode&PrintFunctions != 0 {
283+
printMu.Lock()
284+
f.WriteTo(os.Stdout)
285+
printMu.Unlock()
286+
}
287+
288+
if f.Prog.mode&SanityCheckFunctions != 0 {
289+
mustSanityCheck(f, nil)
290+
}
262291
}
292+
visit(f)
263293
}
264294

265295
// removeNilBlocks eliminates nils from f.Blocks and updates each

0 commit comments

Comments
 (0)