]> Cypherpunks repositories - gostls13.git/commitdiff
[dev.regabi] cmd/compile: separate various from Main
authorRuss Cox <rsc@golang.org>
Mon, 21 Dec 2020 07:08:34 +0000 (02:08 -0500)
committerRuss Cox <rsc@golang.org>
Tue, 22 Dec 2020 19:32:10 +0000 (19:32 +0000)
Move various code out of Main itself and into helper functions
that can be moved into other packages as package gc splits up.

Similarly, move order and instrument inside walk to reduce the amount
of API surface needed from the eventual package walk.

Change-Id: I7849258038c6e39625a0385af9c0edd6a3b654a1
Reviewed-on: https://go-review.googlesource.com/c/go/+/279304
Trust: Russ Cox <rsc@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
src/cmd/compile/internal/gc/abiutils_test.go
src/cmd/compile/internal/gc/dcl.go
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/inl.go
src/cmd/compile/internal/gc/main.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/typecheck.go
src/cmd/compile/internal/gc/walk.go

index 14bd7ff0970b965d38cee09d71c1eebe16de8005..6ed27d794f7437832c14d23d673d131962cbba21 100644 (file)
@@ -36,7 +36,13 @@ func TestMain(m *testing.M) {
        base.Ctxt.Bso = bufio.NewWriter(os.Stdout)
        Widthptr = thearch.LinkArch.PtrSize
        Widthreg = thearch.LinkArch.RegSize
-       initializeTypesPackage()
+       types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+               return typenamesym(t).Linksym()
+       }
+       types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+               return typenamesym(t).Linksym()
+       }
+       TypecheckInit()
        os.Exit(m.Run())
 }
 
index 09d2e7d8b78c7ec09b2eff4409efc92e8e236008..bcd127b5f17c98691951dd1ce082919995501f5b 100644 (file)
@@ -442,6 +442,12 @@ type funcStackEnt struct {
        dclcontext ir.Class
 }
 
+func CheckFuncStack() {
+       if len(funcStack) != 0 {
+               base.Fatalf("funcStack is non-empty: %v", len(funcStack))
+       }
+}
+
 // finish the body.
 // called in auto-declaration context.
 // returns in extern-declaration context.
index 1707e6a11b86221e30b82988bfe253bf7ee44ffa..df91f6f530aa43114e1b63c8c733d579805af7df 100644 (file)
@@ -129,8 +129,6 @@ var (
        iscmp [ir.OEND]bool
 )
 
-var importlist []*ir.Func // imported functions and methods with inlinable bodies
-
 var (
        funcsymsmu sync.Mutex // protects funcsyms and associated package lookups (see func funcsym)
        funcsyms   []*types.Sym
index 5ada83b715b8800b3d4a2616cdc18ed444901430..fde4d6910adabd5ada234c622f7717fa3cc3cea9 100644 (file)
@@ -55,6 +55,26 @@ const (
        inlineBigFunctionMaxCost = 20   // Max cost of inlinee when inlining into a "big" function.
 )
 
+func InlinePackage() {
+       // Find functions that can be inlined and clone them before walk expands them.
+       visitBottomUp(Target.Decls, func(list []*ir.Func, recursive bool) {
+               numfns := numNonClosures(list)
+               for _, n := range list {
+                       if !recursive || numfns > 1 {
+                               // We allow inlining if there is no
+                               // recursion, or the recursion cycle is
+                               // across more than one function.
+                               caninl(n)
+                       } else {
+                               if base.Flag.LowerM > 1 {
+                                       fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
+                               }
+                       }
+                       inlcalls(n)
+               }
+       })
+}
+
 // Get the function's package. For ordinary functions it's on the ->sym, but for imported methods
 // the ->sym can be re-used in the local package, so peel it off the receiver's type.
 func fnpkg(fn *ir.Name) *types.Pkg {
index 2a5ff3f5fda2d4a2eae6e1f9c526b1f26ce72903..4aa2a2ca47f4c8195c1015e11953511237dc4f77 100644 (file)
@@ -191,24 +191,15 @@ func Main(archInit func(*Arch)) {
 
        IsIntrinsicCall = isIntrinsicCall
        SSADumpInline = ssaDumpInline
-
-       ssaDump = os.Getenv("GOSSAFUNC")
-       ssaDir = os.Getenv("GOSSADIR")
-       if ssaDump != "" {
-               if strings.HasSuffix(ssaDump, "+") {
-                       ssaDump = ssaDump[:len(ssaDump)-1]
-                       ssaDumpStdout = true
-               }
-               spl := strings.Split(ssaDump, ":")
-               if len(spl) > 1 {
-                       ssaDump = spl[0]
-                       ssaDumpCFG = spl[1]
-               }
-       }
+       initSSAEnv()
+       initSSATables()
 
        Widthptr = thearch.LinkArch.PtrSize
        Widthreg = thearch.LinkArch.RegSize
        MaxWidth = thearch.MAXWIDTH
+       types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+               return typenamesym(t).Linksym()
+       }
 
        Target = new(ir.Package)
 
@@ -216,152 +207,40 @@ func Main(archInit func(*Arch)) {
        NeedITab = func(t, iface *types.Type) { itabname(t, iface) }
        NeedRuntimeType = addsignat // TODO(rsc): typenamesym for lock?
 
-       // initialize types package
-       // (we need to do this to break dependencies that otherwise
-       // would lead to import cycles)
-       initializeTypesPackage()
-
-       dclcontext = ir.PEXTERN
-
        autogeneratedPos = makePos(src.NewFileBase("<autogenerated>", "<autogenerated>"), 1, 0)
 
-       timings.Start("fe", "loadsys")
-       loadsys()
+       types.TypeLinkSym = func(t *types.Type) *obj.LSym {
+               return typenamesym(t).Linksym()
+       }
+       TypecheckInit()
 
+       // Parse input.
        timings.Start("fe", "parse")
        lines := parseFiles(flag.Args())
        cgoSymABIs()
        timings.Stop()
        timings.AddEvent(int64(lines), "lines")
-
-       finishUniverse()
-
        recordPackageName()
 
-       typecheckok = true
-
-       // Process top-level declarations in phases.
-
-       // Phase 1: const, type, and names and types of funcs.
-       //   This will gather all the information about types
-       //   and methods but doesn't depend on any of it.
-       //
-       //   We also defer type alias declarations until phase 2
-       //   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 Target.Decls.
-       timings.Start("fe", "typecheck", "top1")
-       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()) {
-                       Target.Decls[i] = typecheck(n, ctxStmt)
-               }
-       }
+       // Typecheck.
+       TypecheckPackage()
 
-       // Phase 2: Variable assignments.
-       //   To check interface assignments, depends on phase 1.
-
-       // Don't use range--typecheck can add closures to Target.Decls.
-       timings.Start("fe", "typecheck", "top2")
-       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() {
-                       Target.Decls[i] = typecheck(n, ctxStmt)
-               }
-       }
-
-       // Phase 3: Type check function bodies.
-       // Don't use range--typecheck can add closures to Target.Decls.
-       timings.Start("fe", "typecheck", "func")
-       var fcount int64
-       for i := 0; i < len(Target.Decls); i++ {
-               n := Target.Decls[i]
-               if n.Op() == ir.ODCLFUNC {
-                       Curfn = n.(*ir.Func)
-                       decldepth = 1
-                       errorsBefore := base.Errors()
-                       typecheckslice(Curfn.Body().Slice(), ctxStmt)
-                       checkreturn(Curfn)
-                       if base.Errors() > errorsBefore {
-                               Curfn.PtrBody().Set(nil) // type errors; do not compile
-                       }
-                       // Now that we've checked whether n terminates,
-                       // we can eliminate some obviously dead code.
-                       deadcode(Curfn)
-                       fcount++
-               }
-       }
-
-       // Phase 3.11: Check external declarations.
-       // TODO(mdempsky): This should be handled when type checking their
-       // corresponding ODCL nodes.
-       timings.Start("fe", "typecheck", "externdcls")
-       for i, n := range Target.Externs {
-               if n.Op() == ir.ONAME {
-                       Target.Externs[i] = typecheck(Target.Externs[i], ctxExpr)
-               }
-       }
-
-       // Phase 3.14: With all user code type-checked, it's now safe to verify map keys
-       // and unused dot imports.
-       checkMapKeys()
+       // With all user code typechecked, it's now safe to verify unused dot imports.
        checkDotImports()
        base.ExitIfErrors()
 
-       timings.AddEvent(fcount, "funcs")
-
+       // Build init task.
        if initTask := fninit(); initTask != nil {
                exportsym(initTask)
        }
 
-       // 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 Target.Decls {
-               if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
-                       Curfn = n.(*ir.Func)
-                       capturevars(Curfn)
-               }
-       }
-       capturevarscomplete = true
-       Curfn = nil
-       base.ExitIfErrors()
-
-       // Phase 5: Inlining
+       // Inlining
        timings.Start("fe", "inlining")
-       if base.Debug.TypecheckInl != 0 {
-               // Typecheck imported function bodies if Debug.l > 1,
-               // otherwise lazily when used or re-exported.
-               for _, n := range importlist {
-                       if n.Inl != nil {
-                               typecheckinl(n)
-                       }
-               }
-               base.ExitIfErrors()
-       }
-
        if base.Flag.LowerL != 0 {
-               // Find functions that can be inlined and clone them before walk expands them.
-               visitBottomUp(Target.Decls, func(list []*ir.Func, recursive bool) {
-                       numfns := numNonClosures(list)
-                       for _, n := range list {
-                               if !recursive || numfns > 1 {
-                                       // We allow inlining if there is no
-                                       // recursion, or the recursion cycle is
-                                       // across more than one function.
-                                       caninl(n)
-                               } else {
-                                       if base.Flag.LowerM > 1 {
-                                               fmt.Printf("%v: cannot inline %v: recursive\n", ir.Line(n), n.Nname)
-                                       }
-                               }
-                               inlcalls(n)
-                       }
-               })
+               InlinePackage()
        }
 
+       // Devirtualize.
        for _, n := range Target.Decls {
                if n.Op() == ir.ODCLFUNC {
                        devirtualize(n.(*ir.Func))
@@ -369,7 +248,7 @@ func Main(archInit func(*Arch)) {
        }
        Curfn = nil
 
-       // Phase 6: Escape analysis.
+       // Escape analysis.
        // Required for moving heap allocations onto stack,
        // which in turn is required by the closure implementation,
        // which stores the addresses of stack variables into the closure.
@@ -388,7 +267,7 @@ func Main(archInit func(*Arch)) {
                EnableNoWriteBarrierRecCheck()
        }
 
-       // Phase 7: Transform closure bodies to properly reference captured variables.
+       // Transform closure bodies to properly reference captured variables.
        // This needs to happen before walk, because closures must be transformed
        // before walk reaches a call of a closure.
        timings.Start("fe", "xclosures")
@@ -410,10 +289,10 @@ func Main(archInit func(*Arch)) {
        Curfn = nil
        peekitabs()
 
-       // Phase 8: Compile top level functions.
+       // Compile top level functions.
        // Don't use range--walk can add functions to Target.Decls.
        timings.Start("be", "compilefuncs")
-       fcount = 0
+       fcount := int64(0)
        for i := 0; i < len(Target.Decls); i++ {
                n := Target.Decls[i]
                if n.Op() == ir.ODCLFUNC {
@@ -448,21 +327,9 @@ func Main(archInit func(*Arch)) {
                dumpasmhdr()
        }
 
-       // Check whether any of the functions we have compiled have gigantic stack frames.
-       sort.Slice(largeStackFrames, func(i, j int) bool {
-               return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
-       })
-       for _, large := range largeStackFrames {
-               if large.callee != 0 {
-                       base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
-               } else {
-                       base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
-               }
-       }
+       CheckLargeStacks()
+       CheckFuncStack()
 
-       if len(funcStack) != 0 {
-               base.Fatalf("funcStack is non-empty: %v", len(funcStack))
-       }
        if len(compilequeue) != 0 {
                base.Fatalf("%d uncompiled functions", len(compilequeue))
        }
@@ -480,6 +347,20 @@ func Main(archInit func(*Arch)) {
        }
 }
 
+func CheckLargeStacks() {
+       // Check whether any of the functions we have compiled have gigantic stack frames.
+       sort.Slice(largeStackFrames, func(i, j int) bool {
+               return largeStackFrames[i].pos.Before(largeStackFrames[j].pos)
+       })
+       for _, large := range largeStackFrames {
+               if large.callee != 0 {
+                       base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args + %d MB callee", large.locals>>20, large.args>>20, large.callee>>20)
+               } else {
+                       base.ErrorfAt(large.pos, "stack frame too large (>1GB): %d MB locals + %d MB args", large.locals>>20, large.args>>20)
+               }
+       }
+}
+
 func cgoSymABIs() {
        // The linker expects an ABI0 wrapper for all cgo-exported
        // functions.
@@ -1140,16 +1021,6 @@ func parseLang(s string) (lang, error) {
        return lang{major: major, minor: minor}, nil
 }
 
-func initializeTypesPackage() {
-       types.Widthptr = Widthptr
-       types.Dowidth = dowidth
-       types.TypeLinkSym = func(t *types.Type) *obj.LSym {
-               return typenamesym(t).Linksym()
-       }
-
-       initUniverse()
-}
-
 // useNewABIWrapGen returns TRUE if the compiler should generate an
 // ABI wrapper for the function 'f'.
 func useABIWrapGen(f *ir.Func) bool {
index 8f7aa8e4e74d6f8645997299f6dc2792e3117e7e..e43471dbcaafbddb5c0bf6198858d3e2b627132f 100644 (file)
@@ -222,24 +222,16 @@ func funccompile(fn *ir.Func) {
 }
 
 func compile(fn *ir.Func) {
-       errorsBefore := base.Errors()
-       order(fn)
-       if base.Errors() > errorsBefore {
-               return
-       }
-
        // 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).
        initLSym(fn, true)
 
+       errorsBefore := base.Errors()
        walk(fn)
        if base.Errors() > errorsBefore {
                return
        }
-       if instrumenting {
-               instrument(fn)
-       }
 
        // From this point, there should be no uses of Curfn. Enforce that.
        Curfn = nil
index b4cf8b6dc799454c8eb7450584c3932121566393..1fc1feae67509389fa061e72686fe12f372036d6 100644 (file)
@@ -12,6 +12,7 @@ import (
        "os"
        "path/filepath"
        "sort"
+       "strings"
 
        "bufio"
        "bytes"
@@ -48,6 +49,22 @@ func ssaDumpInline(fn *ir.Func) {
        }
 }
 
+func initSSAEnv() {
+       ssaDump = os.Getenv("GOSSAFUNC")
+       ssaDir = os.Getenv("GOSSADIR")
+       if ssaDump != "" {
+               if strings.HasSuffix(ssaDump, "+") {
+                       ssaDump = ssaDump[:len(ssaDump)-1]
+                       ssaDumpStdout = true
+               }
+               spl := strings.Split(ssaDump, ":")
+               if len(spl) > 1 {
+                       ssaDump = spl[0]
+                       ssaDumpCFG = spl[1]
+               }
+       }
+}
+
 func initssaconfig() {
        types_ := ssa.NewTypes()
 
@@ -3357,7 +3374,7 @@ type intrinsicKey struct {
        fn   string
 }
 
-func init() {
+func initSSATables() {
        intrinsics = map[intrinsicKey]intrinsicBuilder{}
 
        var all []*sys.Arch
index 1aaa93fc3d4544ba9208a987f4ad13b95e1bb487..cc5df3ebaef3e8bab4f661afbbeddef8abf732d1 100644 (file)
@@ -20,6 +20,96 @@ var (
        NeedRuntimeType = func(*types.Type) {}
 )
 
+func TypecheckInit() {
+       types.Widthptr = Widthptr
+       types.Dowidth = dowidth
+       initUniverse()
+       dclcontext = ir.PEXTERN
+       timings.Start("fe", "loadsys")
+       loadsys()
+}
+
+func TypecheckPackage() {
+       finishUniverse()
+
+       typecheckok = true
+
+       // Process top-level declarations in phases.
+
+       // Phase 1: const, type, and names and types of funcs.
+       //   This will gather all the information about types
+       //   and methods but doesn't depend on any of it.
+       //
+       //   We also defer type alias declarations until phase 2
+       //   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 Target.Decls.
+       timings.Start("fe", "typecheck", "top1")
+       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()) {
+                       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 Target.Decls.
+       timings.Start("fe", "typecheck", "top2")
+       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() {
+                       Target.Decls[i] = typecheck(n, ctxStmt)
+               }
+       }
+
+       // Phase 3: Type check function bodies.
+       // Don't use range--typecheck can add closures to Target.Decls.
+       timings.Start("fe", "typecheck", "func")
+       var fcount int64
+       for i := 0; i < len(Target.Decls); i++ {
+               n := Target.Decls[i]
+               if n.Op() == ir.ODCLFUNC {
+                       TypecheckFuncBody(n.(*ir.Func))
+                       fcount++
+               }
+       }
+
+       // Phase 4: Check external declarations.
+       // TODO(mdempsky): This should be handled when type checking their
+       // corresponding ODCL nodes.
+       timings.Start("fe", "typecheck", "externdcls")
+       for i, n := range Target.Externs {
+               if n.Op() == ir.ONAME {
+                       Target.Externs[i] = typecheck(Target.Externs[i], ctxExpr)
+               }
+       }
+
+       // Phase 5: With all user code type-checked, it's now safe to verify map keys.
+       checkMapKeys()
+
+       // Phase 6: 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 Target.Decls {
+               if n.Op() == ir.ODCLFUNC && n.Func().OClosure != nil {
+                       Curfn = n.(*ir.Func)
+                       capturevars(Curfn)
+               }
+       }
+       capturevarscomplete = true
+       Curfn = nil
+
+       if base.Debug.TypecheckInl != 0 {
+               // Typecheck imported function bodies if Debug.l > 1,
+               // otherwise lazily when used or re-exported.
+               TypecheckImports()
+       }
+}
+
 func TypecheckAssignExpr(n ir.Node) ir.Node { return typecheck(n, ctxExpr|ctxAssign) }
 func TypecheckExpr(n ir.Node) ir.Node       { return typecheck(n, ctxExpr) }
 func TypecheckStmt(n ir.Node) ir.Node       { return typecheck(n, ctxStmt) }
@@ -45,6 +135,30 @@ func TypecheckCallee(n ir.Node) ir.Node {
        return typecheck(n, ctxExpr|ctxCallee)
 }
 
+func TypecheckFuncBody(n *ir.Func) {
+       Curfn = n
+       decldepth = 1
+       errorsBefore := base.Errors()
+       typecheckslice(n.Body(), ctxStmt)
+       checkreturn(n)
+       if base.Errors() > errorsBefore {
+               n.PtrBody().Set(nil) // type errors; do not compile
+       }
+       // Now that we've checked whether n terminates,
+       // we can eliminate some obviously dead code.
+       deadcode(n)
+}
+
+var importlist []*ir.Func
+
+func TypecheckImports() {
+       for _, n := range importlist {
+               if n.Inl != nil {
+                       typecheckinl(n)
+               }
+       }
+}
+
 // To enable tracing support (-t flag), set enableTrace to true.
 const enableTrace = false
 
index 410155b3ea9d3f2b3c6ad427d722082eae464baf..5545dcb3453df1628eb10df67ae837c4553fe8b7 100644 (file)
@@ -26,6 +26,10 @@ const zeroValSize = 1024 // must match value of runtime/map.go:maxZero
 func walk(fn *ir.Func) {
        Curfn = fn
        errorsBefore := base.Errors()
+       order(fn)
+       if base.Errors() > errorsBefore {
+               return
+       }
 
        if base.Flag.W != 0 {
                s := fmt.Sprintf("\nbefore walk %v", Curfn.Sym())
@@ -80,6 +84,10 @@ func walk(fn *ir.Func) {
                s := fmt.Sprintf("enter %v", Curfn.Sym())
                ir.DumpList(s, Curfn.Enter)
        }
+
+       if instrumenting {
+               instrument(fn)
+       }
 }
 
 func walkstmtlist(s []ir.Node) {