[dev.regabi] cmd/compile: collect global compilation state

There are various global variables tracking the state of the
compilation. Collect them in a single global struct instead.
The struct definition is in package ir, but the struct itself is
still in package gc. It may eventually be threaded through the
code, but in the short term will end up in package typecheck.

Change-Id: I019db07aaedaed2c9b67dd45a4e138dc6028e54c
Reviewed-on: https://go-review.googlesource.com/c/go/+/279297
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
This commit is contained in:
Russ Cox 2020-12-21 01:29:02 -05:00
parent 2153a99914
commit 1a3b036b83
18 changed files with 116 additions and 96 deletions

View file

@ -394,7 +394,7 @@ func genhash(t *types.Type) *obj.LSym {
}
fn.SetNilCheckDisabled(true)
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
// Build closure. It doesn't close over any variables, so
// it contains just the function pointer.
@ -774,7 +774,7 @@ func geneq(t *types.Type) *obj.LSym {
// neither of which can be nil, and our comparisons
// are shallow.
fn.SetNilCheckDisabled(true)
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
// Generate a closure which points at the function we just generated.
dsymptr(closure, 0, sym.Linksym(), 0)

View file

@ -18,7 +18,7 @@ func (p *exporter) markObject(n ir.Node) {
if n.Op() == ir.ONAME {
n := n.(*ir.Name)
if n.Class() == ir.PFUNC {
inlFlood(n)
inlFlood(n, exportsym)
}
}

View file

@ -89,7 +89,7 @@ func typecheckclosure(clo ir.Node, top int) {
fn.SetClosureCalled(top&ctxCallee != 0)
// Do not typecheck fn twice, otherwise, we will end up pushing
// fn to xtop multiple times, causing initLSym called twice.
// fn to Target.Decls multiple times, causing initLSym called twice.
// See #30709
if fn.Typecheck() == 1 {
return
@ -118,7 +118,7 @@ func typecheckclosure(clo ir.Node, top int) {
// Type check the body now, but only if we're inside a function.
// At top level (in a variable initialization: curfn==nil) we're not
// ready to type check code yet; we'll check it later, because the
// underlying closure function we create is added to xtop.
// underlying closure function we create is added to Target.Decls.
if Curfn != nil && clo.Type() != nil {
oldfn := Curfn
Curfn = fn
@ -129,7 +129,7 @@ func typecheckclosure(clo ir.Node, top int) {
Curfn = oldfn
}
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
}
// globClosgen is like Func.Closgen, but for the global scope.
@ -499,7 +499,7 @@ func makepartialcall(dot *ir.SelectorExpr, t0 *types.Type, meth *types.Sym) *ir.
Curfn = fn
typecheckslice(fn.Body().Slice(), ctxStmt)
sym.Def = fn
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
Curfn = savecurfn
base.Pos = saveLineNo

View file

@ -17,8 +17,6 @@ import (
// Declaration stack & operations
var externdcl []ir.Node
func testdclstack() {
if !types.IsDclstackValid() {
base.Fatalf("mark left on the dclstack")
@ -75,7 +73,7 @@ func declare(n *ir.Name, ctxt ir.Class) {
if s.Name == "main" && s.Pkg.Name == "main" {
base.ErrorfAt(n.Pos(), "cannot declare main - must be func")
}
externdcl = append(externdcl, n)
Target.Externs = append(Target.Externs, n)
} else {
if Curfn == nil && ctxt == ir.PAUTO {
base.Pos = n.Pos()
@ -850,7 +848,7 @@ func newNowritebarrierrecChecker() *nowritebarrierrecChecker {
// important to handle it for this check, so we model it
// directly. This has to happen before transformclosure since
// it's a lot harder to work out the argument after.
for _, n := range xtop {
for _, n := range Target.Decls {
if n.Op() != ir.ODCLFUNC {
continue
}
@ -925,7 +923,7 @@ func (c *nowritebarrierrecChecker) check() {
// q is the queue of ODCLFUNC Nodes to visit in BFS order.
var q ir.NameQueue
for _, n := range xtop {
for _, n := range Target.Decls {
if n.Op() != ir.ODCLFUNC {
continue
}

View file

@ -17,8 +17,6 @@ import (
"strings"
)
var embedlist []ir.Node
const (
embedUnknown = iota
embedBytes
@ -117,12 +115,12 @@ func varEmbed(p *noder, names []ir.Node, typ ir.Ntype, exprs []ir.Node, embeds [
v.Sym().Def = v
v.Name().Ntype = typ
v.SetClass(ir.PEXTERN)
externdcl = append(externdcl, v)
Target.Externs = append(Target.Externs, v)
exprs = []ir.Node{v}
}
v.Name().SetEmbedFiles(list)
embedlist = append(embedlist, v)
Target.Embeds = append(Target.Embeds, v)
return exprs
}
@ -187,7 +185,7 @@ func embedFileLess(x, y string) bool {
}
func dumpembeds() {
for _, v := range embedlist {
for _, v := range Target.Embeds {
initEmbed(v)
}
}

View file

@ -21,8 +21,6 @@ func exportf(bout *bio.Writer, format string, args ...interface{}) {
}
}
var asmlist []ir.Node
// exportsym marks n for export (or reexport).
func exportsym(n *ir.Name) {
if n.Sym().OnExportList() {
@ -34,7 +32,7 @@ func exportsym(n *ir.Name) {
fmt.Printf("export symbol %v\n", n.Sym())
}
exportlist = append(exportlist, n)
Target.Exports = append(Target.Exports, n)
}
func initname(s string) bool {
@ -57,7 +55,7 @@ func autoexport(n *ir.Name, ctxt ir.Class) {
}
if base.Flag.AsmHdr != "" && !n.Sym().Asm() {
n.Sym().SetAsm(true)
asmlist = append(asmlist, n)
Target.Asms = append(Target.Asms, n)
}
}
@ -202,7 +200,7 @@ func dumpasmhdr() {
base.Fatalf("%v", err)
}
fmt.Fprintf(b, "// generated by compile -asmhdr from package %s\n\n", types.LocalPkg.Name)
for _, n := range asmlist {
for _, n := range Target.Asms {
if n.Sym().IsBlank() {
continue
}

View file

@ -128,10 +128,6 @@ var (
iscmp [ir.OEND]bool
)
var xtop []ir.Node
var exportlist []*ir.Name
var importlist []*ir.Func // imported functions and methods with inlinable bodies
var (

View file

@ -251,7 +251,7 @@ func iexport(out *bufio.Writer) {
{
// TODO(mdempsky): Separate from bexport logic.
p := &exporter{marked: make(map[*types.Type]bool)}
for _, n := range exportlist {
for _, n := range Target.Exports {
p.markObject(n)
}
}
@ -272,7 +272,7 @@ func iexport(out *bufio.Writer) {
}
// Initialize work queue with exported declarations.
for _, n := range exportlist {
for _, n := range Target.Exports {
p.pushDecl(n)
}

View file

@ -1111,7 +1111,7 @@ func (r *importReader) exprsOrNil() (a, b ir.Node) {
}
func builtinCall(pos src.XPos, op ir.Op) *ir.CallExpr {
return ir.NewCallExpr(pos, ir.OCALL, mkname(types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
return ir.NewCallExpr(pos, ir.OCALL, ir.NewIdent(base.Pos, types.BuiltinPkg.Lookup(ir.OpNames[op])), nil)
}
func npos(pos src.XPos, n ir.Node) ir.Node {

View file

@ -27,9 +27,6 @@ func renameinit() *types.Sym {
return s
}
// List of imported packages, in source code order. See #31636.
var sourceOrderImports []*types.Pkg
// fninit makes an initialization record for the package.
// See runtime/proc.go:initTask for its layout.
// The 3 tasks for initialization are:
@ -43,7 +40,7 @@ func fninit(n []ir.Node) {
var fns []*obj.LSym // functions to call for package initialization
// Find imported packages with init tasks.
for _, pkg := range sourceOrderImports {
for _, pkg := range Target.Imports {
n := resolve(oldname(pkg.Lookup(".inittask")))
if n.Op() == ir.ONONAME {
continue
@ -72,7 +69,7 @@ func fninit(n []ir.Node) {
Curfn = fn
typecheckslice(nf, ctxStmt)
Curfn = nil
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
fns = append(fns, initializers.Linksym())
}
if initTodo.Dcl != nil {
@ -84,16 +81,14 @@ func fninit(n []ir.Node) {
initTodo = nil
// Record user init functions.
for i := 0; i < renameinitgen; i++ {
s := lookupN("init.", i)
fn := ir.AsNode(s.Def).Name().Defn.(*ir.Func)
for _, fn := range Target.Inits {
// Skip init functions with empty bodies.
if fn.Body().Len() == 1 {
if stmt := fn.Body().First(); stmt.Op() == ir.OBLOCK && stmt.(*ir.BlockStmt).List().Len() == 0 {
continue
}
}
fns = append(fns, s.Linksym())
fns = append(fns, fn.Nname.Sym().Linksym())
}
if len(deps) == 0 && len(fns) == 0 && types.LocalPkg.Name != "main" && types.LocalPkg.Name != "runtime" {

View file

@ -230,7 +230,7 @@ func caninl(fn *ir.Func) {
// inlFlood marks n's inline body for export and recursively ensures
// all called functions are marked too.
func inlFlood(n *ir.Name) {
func inlFlood(n *ir.Name, exportsym func(*ir.Name)) {
if n == nil {
return
}
@ -258,13 +258,13 @@ func inlFlood(n *ir.Name) {
ir.VisitList(ir.AsNodes(fn.Inl.Body), func(n ir.Node) {
switch n.Op() {
case ir.OMETHEXPR, ir.ODOTMETH:
inlFlood(methodExprName(n))
inlFlood(methodExprName(n), exportsym)
case ir.ONAME:
n := n.(*ir.Name)
switch n.Class() {
case ir.PFUNC:
inlFlood(n)
inlFlood(n, exportsym)
exportsym(n)
case ir.PEXTERN:
exportsym(n)

View file

@ -51,6 +51,9 @@ func hidePanic() {
}
}
// Target is the package being compiled.
var Target *ir.Package
// timing data for compiler phases
var timings Timings
@ -207,6 +210,8 @@ func Main(archInit func(*Arch)) {
Widthptr = thearch.LinkArch.PtrSize
Widthreg = thearch.LinkArch.RegSize
Target = new(ir.Package)
// initialize types package
// (we need to do this to break dependencies that otherwise
// would lead to import cycles)
@ -240,33 +245,33 @@ func Main(archInit func(*Arch)) {
// to avoid cycles like #18640.
// TODO(gri) Remove this again once we have a fix for #25838.
// Don't use range--typecheck can add closures to xtop.
// Don't use range--typecheck can add closures to Target.Decls.
timings.Start("fe", "typecheck", "top1")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
for i := 0; i < len(Target.Decls); i++ {
n := Target.Decls[i]
if op := n.Op(); op != ir.ODCL && op != ir.OAS && op != ir.OAS2 && (op != ir.ODCLTYPE || !n.(*ir.Decl).Left().Name().Alias()) {
xtop[i] = typecheck(n, ctxStmt)
Target.Decls[i] = typecheck(n, ctxStmt)
}
}
// Phase 2: Variable assignments.
// To check interface assignments, depends on phase 1.
// Don't use range--typecheck can add closures to xtop.
// Don't use range--typecheck can add closures to Target.Decls.
timings.Start("fe", "typecheck", "top2")
for i := 0; i < len(xtop); i++ {
n := xtop[i]
for i := 0; i < len(Target.Decls); i++ {
n := Target.Decls[i]
if op := n.Op(); op == ir.ODCL || op == ir.OAS || op == ir.OAS2 || op == ir.ODCLTYPE && n.(*ir.Decl).Left().Name().Alias() {
xtop[i] = typecheck(n, ctxStmt)
Target.Decls[i] = typecheck(n, ctxStmt)
}
}
// Phase 3: Type check function bodies.
// Don't use range--typecheck can add closures to xtop.
// Don't use range--typecheck can add closures to Target.Decls.
timings.Start("fe", "typecheck", "func")
var fcount int64
for i := 0; i < len(xtop); i++ {
n := xtop[i]
for i := 0; i < len(Target.Decls); i++ {
n := Target.Decls[i]
if n.Op() == ir.ODCLFUNC {
Curfn = n.(*ir.Func)
decldepth = 1
@ -287,9 +292,9 @@ func Main(archInit func(*Arch)) {
// TODO(mdempsky): This should be handled when type checking their
// corresponding ODCL nodes.
timings.Start("fe", "typecheck", "externdcls")
for i, n := range externdcl {
for i, n := range Target.Externs {
if n.Op() == ir.ONAME {
externdcl[i] = typecheck(externdcl[i], ctxExpr)
Target.Externs[i] = typecheck(Target.Externs[i], ctxExpr)
}
}
@ -301,13 +306,13 @@ func Main(archInit func(*Arch)) {
timings.AddEvent(fcount, "funcs")
fninit(xtop)
fninit(Target.Decls)
// Phase 4: Decide how to capture closed variables.
// This needs to run before escape analysis,
// because variables captured by value do not escape.
timings.Start("fe", "capturevars")
for _, n := range xtop {
for _, n := range Target.Decls {
if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
Curfn = n.(*ir.Func)
capturevars(Curfn)
@ -332,7 +337,7 @@ func Main(archInit func(*Arch)) {
if base.Flag.LowerL != 0 {
// Find functions that can be inlined and clone them before walk expands them.
visitBottomUp(xtop, func(list []*ir.Func, recursive bool) {
visitBottomUp(Target.Decls, func(list []*ir.Func, recursive bool) {
numfns := numNonClosures(list)
for _, n := range list {
if !recursive || numfns > 1 {
@ -350,7 +355,7 @@ func Main(archInit func(*Arch)) {
})
}
for _, n := range xtop {
for _, n := range Target.Decls {
if n.Op() == ir.ODCLFUNC {
devirtualize(n.(*ir.Func))
}
@ -366,7 +371,7 @@ func Main(archInit func(*Arch)) {
// Large values are also moved off stack in escape analysis;
// because large values may contain pointers, it must happen early.
timings.Start("fe", "escapes")
escapes(xtop)
escapes(Target.Decls)
// Collect information for go:nowritebarrierrec
// checking. This must happen before transformclosure.
@ -380,7 +385,7 @@ func Main(archInit func(*Arch)) {
// This needs to happen before walk, because closures must be transformed
// before walk reaches a call of a closure.
timings.Start("fe", "xclosures")
for _, n := range xtop {
for _, n := range Target.Decls {
if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
Curfn = n.(*ir.Func)
transformclosure(Curfn)
@ -399,11 +404,11 @@ func Main(archInit func(*Arch)) {
peekitabs()
// Phase 8: Compile top level functions.
// Don't use range--walk can add functions to xtop.
// Don't use range--walk can add functions to Target.Decls.
timings.Start("be", "compilefuncs")
fcount = 0
for i := 0; i < len(xtop); i++ {
n := xtop[i]
for i := 0; i < len(Target.Decls); i++ {
n := Target.Decls[i]
if n.Op() == ir.ODCLFUNC {
funccompile(n.(*ir.Func))
fcount++

View file

@ -27,7 +27,7 @@ import (
// parseFiles concurrently parses files into *syntax.File structures.
// Each declaration in every *syntax.File is converted to a syntax tree
// and its root represented by *Node is appended to xtop.
// and its root represented by *Node is appended to Target.Decls.
// Returns the total count of parsed lines.
func parseFiles(filenames []string) uint {
noders := make([]*noder, 0, len(filenames))
@ -260,7 +260,7 @@ func (p *noder) node() {
p.checkUnused(pragma)
}
xtop = append(xtop, p.decls(p.file.DeclList)...)
Target.Decls = append(Target.Decls, p.decls(p.file.DeclList)...)
base.Pos = src.NoXPos
clearImports()
@ -297,7 +297,7 @@ func (p *noder) processPragmas() {
}
}
pragcgobuf = append(pragcgobuf, p.pragcgobuf...)
Target.CgoPragmas = append(Target.CgoPragmas, p.pragcgobuf...)
}
func (p *noder) decls(decls []syntax.Decl) (l []ir.Node) {
@ -354,7 +354,7 @@ func (p *noder) importDecl(imp *syntax.ImportDecl) {
}
if !ipkg.Direct {
sourceOrderImports = append(sourceOrderImports, ipkg)
Target.Imports = append(Target.Imports, ipkg)
}
ipkg.Direct = true
@ -530,6 +530,7 @@ func (p *noder) funcDecl(fun *syntax.FuncDecl) ir.Node {
if len(t.Params) > 0 || len(t.Results) > 0 {
base.ErrorfAt(f.Pos(), "func init must have no arguments and no return values")
}
Target.Inits = append(Target.Inits, f)
}
if types.LocalPkg.Name == "main" && name.Name == "main" {

View file

@ -117,13 +117,14 @@ func dumpCompilerObj(bout *bio.Writer) {
}
func dumpdata() {
externs := len(externdcl)
xtops := len(xtop)
numExterns := len(Target.Externs)
numDecls := len(Target.Decls)
dumpglobls()
dumpglobls(Target.Externs)
dumpfuncsyms()
addptabs()
exportlistLen := len(exportlist)
addsignats(externdcl)
numExports := len(Target.Exports)
addsignats(Target.Externs)
dumpsignats()
dumptabs()
ptabsLen := len(ptabs)
@ -140,28 +141,22 @@ func dumpdata() {
// In the typical case, we loop 0 or 1 times.
// It was not until issue 24761 that we found any code that required a loop at all.
for {
for i := xtops; i < len(xtop); i++ {
n := xtop[i]
for i := numDecls; i < len(Target.Decls); i++ {
n := Target.Decls[i]
if n.Op() == ir.ODCLFUNC {
funccompile(n.(*ir.Func))
}
}
xtops = len(xtop)
numDecls = len(Target.Decls)
compileFunctions()
dumpsignats()
if xtops == len(xtop) {
if numDecls == len(Target.Decls) {
break
}
}
// Dump extra globals.
tmp := externdcl
if externdcl != nil {
externdcl = externdcl[externs:]
}
dumpglobls()
externdcl = tmp
dumpglobls(Target.Externs[numExterns:])
if zerosize > 0 {
zero := mappkg.Lookup("zero")
@ -170,8 +165,8 @@ func dumpdata() {
addGCLocals()
if exportlistLen != len(exportlist) {
base.Fatalf("exportlist changed after compile functions loop")
if numExports != len(Target.Exports) {
base.Fatalf("Target.Exports changed after compile functions loop")
}
if ptabsLen != len(ptabs) {
base.Fatalf("ptabs changed after compile functions loop")
@ -184,11 +179,11 @@ func dumpdata() {
func dumpLinkerObj(bout *bio.Writer) {
printObjHeader(bout)
if len(pragcgobuf) != 0 {
if len(Target.CgoPragmas) != 0 {
// write empty export section; must be before cgo section
fmt.Fprintf(bout, "\n$$\n\n$$\n\n")
fmt.Fprintf(bout, "\n$$ // cgo\n")
if err := json.NewEncoder(bout).Encode(pragcgobuf); err != nil {
if err := json.NewEncoder(bout).Encode(Target.CgoPragmas); err != nil {
base.Fatalf("serializing pragcgobuf: %v", err)
}
fmt.Fprintf(bout, "\n$$\n\n")
@ -203,7 +198,7 @@ func addptabs() {
if !base.Ctxt.Flag_dynlink || types.LocalPkg.Name != "main" {
return
}
for _, exportn := range exportlist {
for _, exportn := range Target.Exports {
s := exportn.Sym()
nn := ir.AsNode(s.Def)
if nn == nil {
@ -267,9 +262,9 @@ func dumpGlobalConst(n ir.Node) {
base.Ctxt.DwarfIntConst(base.Ctxt.Pkgpath, n.Sym().Name, typesymname(t), ir.IntVal(t, v))
}
func dumpglobls() {
func dumpglobls(externs []ir.Node) {
// add globals
for _, n := range externdcl {
for _, n := range externs {
switch n.Op() {
case ir.ONAME:
dumpGlobal(n.(*ir.Name))
@ -277,7 +272,9 @@ func dumpglobls() {
dumpGlobalConst(n)
}
}
}
func dumpfuncsyms() {
sort.Slice(funcsyms, func(i, j int) bool {
return funcsyms[i].LinksymName() < funcsyms[j].LinksymName()
})
@ -286,9 +283,6 @@ func dumpglobls() {
dsymptr(sf, 0, s.Linksym(), 0)
ggloblsym(sf, int32(Widthptr), obj.DUPOK|obj.RODATA)
}
// Do not reprocess funcsyms on next dumpglobls call.
funcsyms = nil
}
// addGCLocals adds gcargs, gclocals, gcregs, and stack object symbols to Ctxt.Data.

View file

@ -287,7 +287,7 @@ func compilenow(fn *ir.Func) bool {
// 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 xtop list.
// compiling a function later on in the Target.Decls list.
if ir.IsMethod(fn) && isInlinableButNotInlined(fn) {
return false
}

View file

@ -1275,7 +1275,7 @@ func genwrapper(rcvr *types.Type, method *types.Field, newnam *types.Sym) {
escapeFuncs([]*ir.Func{fn}, false)
Curfn = nil
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
}
func paramNnames(ft *types.Type) []ir.Node {

View file

@ -3942,7 +3942,7 @@ func wrapCall(n *ir.CallExpr, init *ir.Nodes) ir.Node {
typecheckFunc(fn)
typecheckslice(fn.Body().Slice(), ctxStmt)
xtop = append(xtop, fn)
Target.Decls = append(Target.Decls, fn)
call = ir.NewCallExpr(base.Pos, ir.OCALL, fn.Nname, n.List().Slice())
return walkexpr(typecheck(call, ctxStmt), init)

View file

@ -0,0 +1,35 @@
// Copyright 2020 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 ir
import "cmd/compile/internal/types"
// A Package holds information about the package being compiled.
type Package struct {
// Imports, listed in source order.
// See golang.org/issue/31636.
Imports []*types.Pkg
// Init functions, listed in source order.
Inits []*Func
// Top-level declarations.
Decls []Node
// Extern (package global) declarations.
Externs []Node
// Assembly function declarations.
Asms []*Name
// Cgo directives.
CgoPragmas [][]string
// Variables with //go:embed lines.
Embeds []*Name
// Exported (or re-exported) symbols.
Exports []*Name
}