Skip to content

Commit 88cb17e

Browse files
mdempskygopherbot
authored andcommitted
cmd/compile: create "init" function during noding
This CL arranges for package-scope initialization statements to be constructed directly into their eventual "init" function, so we can eliminate the roundabout solution of using InitTodoFunc. While here, somewhat simplify and generalize the logic for outlining map initialization statements. Change-Id: I8aff042e6b266f7024de436424ec6711b8b69129 Reviewed-on: https://go-review.googlesource.com/c/go/+/522318 Run-TryBot: Matthew Dempsky <[email protected]> Reviewed-by: Than McIntosh <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Auto-Submit: Matthew Dempsky <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent cf68384 commit 88cb17e

File tree

7 files changed

+86
-193
lines changed

7 files changed

+86
-193
lines changed

src/cmd/compile/internal/gc/main.go

+1-6
Original file line numberDiff line numberDiff line change
@@ -205,14 +205,9 @@ func Main(archInit func(*ssagen.ArchInfo)) {
205205

206206
dwarfgen.RecordPackageName()
207207

208-
// Prepare for backend processing. This must happen before pkginit,
209-
// because it generates itabs for initializing global variables.
208+
// Prepare for backend processing.
210209
ssagen.InitConfig()
211210

212-
// Create "init" function for package-scope variable initialization
213-
// statements, if any.
214-
pkginit.MakeInit()
215-
216211
// Apply coverage fixups, if applicable.
217212
coverage.Fixup()
218213

src/cmd/compile/internal/ir/package.go

-4
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,6 @@ type Package struct {
1212
// See golang.org/issue/31636.
1313
Imports []*types.Pkg
1414

15-
// InitOrder is the list of package-level initializers in the order
16-
// in which they must be executed.
17-
InitOrder []Node
18-
1915
// Init functions, listed in source order.
2016
Inits []*Func
2117

src/cmd/compile/internal/noder/reader.go

+34-43
Original file line numberDiff line numberDiff line change
@@ -2988,52 +2988,12 @@ func (r *reader) multiExpr() []ir.Node {
29882988

29892989
// temp returns a new autotemp of the specified type.
29902990
func (r *reader) temp(pos src.XPos, typ *types.Type) *ir.Name {
2991-
// See typecheck.typecheckargs.
2992-
curfn := r.curfn
2993-
if curfn == nil {
2994-
curfn = typecheck.InitTodoFunc
2995-
}
2996-
2997-
return typecheck.TempAt(pos, curfn, typ)
2991+
return typecheck.TempAt(pos, r.curfn, typ)
29982992
}
29992993

30002994
// tempCopy declares and returns a new autotemp initialized to the
30012995
// value of expr.
30022996
func (r *reader) tempCopy(pos src.XPos, expr ir.Node, init *ir.Nodes) *ir.Name {
3003-
if r.curfn == nil {
3004-
// Escape analysis doesn't know how to handle package-scope
3005-
// function literals with free variables (i.e., that capture
3006-
// temporary variables added to typecheck.InitTodoFunc).
3007-
//
3008-
// stencil.go works around this limitation by spilling values to
3009-
// global variables instead, but that causes the value to stay
3010-
// alive indefinitely; see go.dev/issue/54343.
3011-
//
3012-
// This code path (which implements the same workaround) isn't
3013-
// actually needed by unified IR, because it creates uses normal
3014-
// OMETHEXPR/OMETHVALUE nodes when statically-known instantiated
3015-
// types are used. But it's kept around for now because it's handy
3016-
// for testing that the generic fallback paths work correctly.
3017-
base.Fatalf("tempCopy called at package scope")
3018-
3019-
tmp := staticinit.StaticName(expr.Type())
3020-
3021-
assign := ir.NewAssignStmt(pos, tmp, expr)
3022-
assign.Def = true
3023-
tmp.Defn = assign
3024-
3025-
// TODO(mdempsky): This code doesn't work anymore, because we now
3026-
// rely on types2 to compute InitOrder. If it's going to be used
3027-
// for testing again, the assignment here probably needs to be
3028-
// added to typecheck.Target.InitOrder somewhere.
3029-
//
3030-
// Probably just easier to address the escape analysis limitation.
3031-
//
3032-
// typecheck.Target.Decls = append(typecheck.Target.Decls, typecheck.Stmt(assign))
3033-
3034-
return tmp
3035-
}
3036-
30372997
tmp := r.temp(pos, expr.Type())
30382998

30392999
init.Append(typecheck.Stmt(ir.NewDecl(pos, ir.ODCL, tmp)))
@@ -3328,9 +3288,32 @@ func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
33283288
}
33293289
target.CgoPragmas = cgoPragmas
33303290

3291+
r.pkgInitOrder(target)
3292+
33313293
r.pkgDecls(target)
33323294

3295+
r.Sync(pkgbits.SyncEOF)
3296+
}
3297+
3298+
// pkgInitOrder creates a synthetic init function to handle any
3299+
// package-scope initialization statements.
3300+
func (r *reader) pkgInitOrder(target *ir.Package) {
33333301
initOrder := make([]ir.Node, r.Len())
3302+
if len(initOrder) == 0 {
3303+
return
3304+
}
3305+
3306+
// Make a function that contains all the initialization statements.
3307+
pos := base.AutogeneratedPos
3308+
base.Pos = pos
3309+
3310+
fn := ir.NewFunc(pos, pos, typecheck.Lookup("init"), types.NewSignature(nil, nil, nil))
3311+
fn.SetIsPackageInit(true)
3312+
fn.SetInlinabilityChecked(true) // suppress useless "can inline" diagnostics
3313+
3314+
typecheck.DeclFunc(fn)
3315+
r.curfn = fn
3316+
33343317
for i := range initOrder {
33353318
lhs := make([]ir.Node, r.Len())
33363319
for j := range lhs {
@@ -3352,9 +3335,17 @@ func (r *reader) pkgInit(self *types.Pkg, target *ir.Package) {
33523335

33533336
initOrder[i] = as
33543337
}
3355-
target.InitOrder = initOrder
33563338

3357-
r.Sync(pkgbits.SyncEOF)
3339+
fn.Body = initOrder
3340+
3341+
typecheck.FinishFuncBody()
3342+
r.curfn = nil
3343+
r.locals = nil
3344+
3345+
// Outline (if legal/profitable) global map inits.
3346+
staticinit.OutlineMapInits(fn)
3347+
3348+
target.Inits = append(target.Inits, fn)
33583349
}
33593350

33603351
func (r *reader) pkgDecls(target *ir.Package) {

src/cmd/compile/internal/noder/writer.go

+7-2
Original file line numberDiff line numberDiff line change
@@ -2597,6 +2597,8 @@ func (w *writer) pkgInit(noders []*noder) {
25972597
w.Strings(cgoPragma)
25982598
}
25992599

2600+
w.pkgInitOrder()
2601+
26002602
w.Sync(pkgbits.SyncDecls)
26012603
for _, p := range noders {
26022604
for _, decl := range p.file.DeclList {
@@ -2605,6 +2607,11 @@ func (w *writer) pkgInit(noders []*noder) {
26052607
}
26062608
w.Code(declEnd)
26072609

2610+
w.Sync(pkgbits.SyncEOF)
2611+
}
2612+
2613+
func (w *writer) pkgInitOrder() {
2614+
// TODO(mdempsky): Write as a function body instead?
26082615
w.Len(len(w.p.info.InitOrder))
26092616
for _, init := range w.p.info.InitOrder {
26102617
w.Len(len(init.Lhs))
@@ -2613,8 +2620,6 @@ func (w *writer) pkgInit(noders []*noder) {
26132620
}
26142621
w.expr(init.Rhs)
26152622
}
2616-
2617-
w.Sync(pkgbits.SyncEOF)
26182623
}
26192624

26202625
func (w *writer) pkgDecl(decl syntax.Decl) {

src/cmd/compile/internal/pkginit/init.go

-59
Original file line numberDiff line numberDiff line change
@@ -15,67 +15,8 @@ import (
1515
"cmd/internal/obj"
1616
"cmd/internal/objabi"
1717
"cmd/internal/src"
18-
"fmt"
19-
"os"
2018
)
2119

22-
// MakeInit creates a synthetic init function to handle any
23-
// package-scope initialization statements.
24-
func MakeInit() {
25-
nf := typecheck.Target.InitOrder
26-
if len(nf) == 0 {
27-
return
28-
}
29-
30-
// Make a function that contains all the initialization statements.
31-
pos := nf[0].Pos() // prolog/epilog gets line number of first init stmt
32-
base.Pos = pos
33-
34-
sym := typecheck.Lookup("init")
35-
fn := ir.NewFunc(pos, pos, sym, types.NewSignature(nil, nil, nil))
36-
typecheck.DeclFunc(fn)
37-
38-
for _, dcl := range typecheck.InitTodoFunc.Dcl {
39-
dcl.Curfn = fn
40-
}
41-
fn.Dcl = append(fn.Dcl, typecheck.InitTodoFunc.Dcl...)
42-
typecheck.InitTodoFunc.Dcl = nil
43-
fn.SetIsPackageInit(true)
44-
45-
// Outline (if legal/profitable) global map inits.
46-
nf, newfuncs := staticinit.OutlineMapInits(nf)
47-
48-
// Suppress useless "can inline" diagnostics.
49-
// Init functions are only called dynamically.
50-
fn.SetInlinabilityChecked(true)
51-
for _, nfn := range newfuncs {
52-
nfn.SetInlinabilityChecked(true)
53-
}
54-
55-
fn.Body = nf
56-
typecheck.FinishFuncBody()
57-
58-
ir.WithFunc(fn, func() {
59-
typecheck.Stmts(nf)
60-
})
61-
if base.Debug.WrapGlobalMapDbg > 1 {
62-
fmt.Fprintf(os.Stderr, "=-= len(newfuncs) is %d for %v\n",
63-
len(newfuncs), fn)
64-
}
65-
66-
// Prepend to Inits, so it runs first, before any user-declared init
67-
// functions.
68-
typecheck.Target.Inits = append([]*ir.Func{fn}, typecheck.Target.Inits...)
69-
70-
if typecheck.InitTodoFunc.Dcl != nil {
71-
// We only generate temps using InitTodoFunc if there
72-
// are package-scope initialization statements, so
73-
// something's weird if we get here.
74-
base.Fatalf("InitTodoFunc still has declarations")
75-
}
76-
typecheck.InitTodoFunc = nil
77-
}
78-
7920
// MakeTask makes an initialization record for the package, if necessary.
8021
// See runtime/proc.go:initTask for its layout.
8122
// The 3 tasks for initialization are:

src/cmd/compile/internal/staticinit/sched.go

+44-52
Original file line numberDiff line numberDiff line change
@@ -980,26 +980,26 @@ func addStr(n *ir.AddStringExpr) ir.Node {
980980

981981
const wrapGlobalMapInitSizeThreshold = 20
982982

983-
// tryWrapGlobalMapInit examines the node 'n' to see if it is a map
984-
// variable initialization, and if so, possibly returns the mapvar
985-
// being assigned, a new function containing the init code, and a call
986-
// to the function passing the mapvar. Returns will be nil if the
987-
// assignment is not to a map, or the map init is not big enough,
988-
// or if the expression being assigned to the map has side effects.
989-
func tryWrapGlobalMapInit(n ir.Node) (mapvar *ir.Name, genfn *ir.Func, call ir.Node) {
983+
// tryWrapGlobalInit returns a new outlined function to contain global
984+
// initializer statement n, if possible and worthwhile. Otherwise, it
985+
// returns nil.
986+
//
987+
// Currently, it outlines map assignment statements with large,
988+
// side-effect-free RHS expressions.
989+
func tryWrapGlobalInit(n ir.Node) *ir.Func {
990990
// Look for "X = ..." where X has map type.
991991
// FIXME: might also be worth trying to look for cases where
992992
// the LHS is of interface type but RHS is map type.
993993
if n.Op() != ir.OAS {
994-
return nil, nil, nil
994+
return nil
995995
}
996996
as := n.(*ir.AssignStmt)
997997
if ir.IsBlank(as.X) || as.X.Op() != ir.ONAME {
998-
return nil, nil, nil
998+
return nil
999999
}
10001000
nm := as.X.(*ir.Name)
10011001
if !nm.Type().IsMap() {
1002-
return nil, nil, nil
1002+
return nil
10031003
}
10041004

10051005
// Determine size of RHS.
@@ -1019,15 +1019,15 @@ func tryWrapGlobalMapInit(n ir.Node) (mapvar *ir.Name, genfn *ir.Func, call ir.N
10191019
fmt.Fprintf(os.Stderr, "=-= skipping %v size too small at %d\n",
10201020
nm, rsiz)
10211021
}
1022-
return nil, nil, nil
1022+
return nil
10231023
}
10241024

10251025
// Reject right hand sides with side effects.
10261026
if AnySideEffects(as.Y) {
10271027
if base.Debug.WrapGlobalMapDbg > 0 {
10281028
fmt.Fprintf(os.Stderr, "=-= rejected %v due to side effects\n", nm)
10291029
}
1030-
return nil, nil, nil
1030+
return nil
10311031
}
10321032

10331033
if base.Debug.WrapGlobalMapDbg > 1 {
@@ -1036,42 +1036,37 @@ func tryWrapGlobalMapInit(n ir.Node) (mapvar *ir.Name, genfn *ir.Func, call ir.N
10361036

10371037
// Create a new function that will (eventually) have this form:
10381038
//
1039-
// func map.init.%d() {
1040-
// globmapvar = <map initialization>
1041-
// }
1039+
// func map.init.%d() {
1040+
// globmapvar = <map initialization>
1041+
// }
10421042
//
1043+
// Note: cmd/link expects the function name to contain "map.init".
10431044
minitsym := typecheck.LookupNum("map.init.", mapinitgen)
10441045
mapinitgen++
10451046

1046-
newfn := ir.NewFunc(base.Pos, base.Pos, minitsym, types.NewSignature(nil, nil, nil))
1047-
typecheck.DeclFunc(newfn)
1047+
fn := ir.NewFunc(n.Pos(), n.Pos(), minitsym, types.NewSignature(nil, nil, nil))
1048+
fn.SetInlinabilityChecked(true) // suppress inlining (which would defeat the point)
1049+
typecheck.DeclFunc(fn)
10481050
if base.Debug.WrapGlobalMapDbg > 0 {
1049-
fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", newfn)
1051+
fmt.Fprintf(os.Stderr, "=-= generated func is %v\n", fn)
10501052
}
10511053

10521054
// NB: we're relying on this phase being run before inlining;
10531055
// if for some reason we need to move it after inlining, we'll
10541056
// need code here that relocates or duplicates inline temps.
10551057

10561058
// Insert assignment into function body; mark body finished.
1057-
newfn.Body = append(newfn.Body, as)
1059+
fn.Body = []ir.Node{as}
10581060
typecheck.FinishFuncBody()
10591061

1060-
const no = `
1061-
// Register new function with decls.
1062-
typecheck.Target.Decls = append(typecheck.Target.Decls, newfn)
1063-
`
1064-
1065-
// Create call to function, passing mapvar.
1066-
fncall := ir.NewCallExpr(n.Pos(), ir.OCALL, newfn.Nname, nil)
1067-
10681062
if base.Debug.WrapGlobalMapDbg > 1 {
10691063
fmt.Fprintf(os.Stderr, "=-= mapvar is %v\n", nm)
1070-
fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", newfn)
1071-
fmt.Fprintf(os.Stderr, "=-= call is %+v\n", fncall)
1064+
fmt.Fprintf(os.Stderr, "=-= newfunc is %+v\n", fn)
10721065
}
10731066

1074-
return nm, newfn, typecheck.Stmt(fncall)
1067+
recordFuncForVar(nm, fn)
1068+
1069+
return fn
10751070
}
10761071

10771072
// mapinitgen is a counter used to uniquify compiler-generated
@@ -1108,31 +1103,28 @@ func AddKeepRelocations() {
11081103
varToMapInit = nil
11091104
}
11101105

1111-
// OutlineMapInits walks through a list of init statements (candidates
1112-
// for inclusion in the package "init" function) and returns an
1113-
// updated list in which items corresponding to map variable
1114-
// initializations have been replaced with calls to outline "map init"
1115-
// functions (if legal/profitable). Return value is an updated list
1116-
// and a list of any newly generated "map init" functions.
1117-
func OutlineMapInits(stmts []ir.Node) ([]ir.Node, []*ir.Func) {
1106+
// OutlineMapInits replaces global map initializers with outlined
1107+
// calls to separate "map init" functions (where possible and
1108+
// profitable), to facilitate better dead-code elimination by the
1109+
// linker.
1110+
func OutlineMapInits(fn *ir.Func) {
11181111
if base.Debug.WrapGlobalMapCtl == 1 {
1119-
return stmts, nil
1112+
return
11201113
}
1121-
newfuncs := []*ir.Func{}
1122-
for i := range stmts {
1123-
s := stmts[i]
1124-
// Call the helper tryWrapGlobalMapInit to see if the LHS of
1125-
// this assignment is to a map var, and if so whether the RHS
1126-
// should be outlined into a separate init function. If the
1127-
// outline goes through, then replace the original init
1128-
// statement with the call to the outlined func, and append
1129-
// the new outlined func to our return list.
1130-
if mapvar, genfn, call := tryWrapGlobalMapInit(s); call != nil {
1131-
stmts[i] = call
1132-
newfuncs = append(newfuncs, genfn)
1133-
recordFuncForVar(mapvar, genfn)
1114+
1115+
outlined := 0
1116+
for i, stmt := range fn.Body {
1117+
// Attempt to outline stmt. If successful, replace it with a call
1118+
// to the returned wrapper function.
1119+
if wrapperFn := tryWrapGlobalInit(stmt); wrapperFn != nil {
1120+
ir.WithFunc(fn, func() {
1121+
fn.Body[i] = typecheck.Call(stmt.Pos(), wrapperFn.Nname, nil, false)
1122+
})
1123+
outlined++
11341124
}
11351125
}
11361126

1137-
return stmts, newfuncs
1127+
if base.Debug.WrapGlobalMapDbg > 1 {
1128+
fmt.Fprintf(os.Stderr, "=-= outlined %v map initializations\n", outlined)
1129+
}
11381130
}

0 commit comments

Comments
 (0)