diff --git a/src/cmd/compile/internal/gc/abiutils_test.go b/src/cmd/compile/internal/gc/abiutils_test.go index 14bd7ff097..6ed27d794f 100644 --- a/src/cmd/compile/internal/gc/abiutils_test.go +++ b/src/cmd/compile/internal/gc/abiutils_test.go @@ -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()) } diff --git a/src/cmd/compile/internal/gc/dcl.go b/src/cmd/compile/internal/gc/dcl.go index 09d2e7d8b7..bcd127b5f1 100644 --- a/src/cmd/compile/internal/gc/dcl.go +++ b/src/cmd/compile/internal/gc/dcl.go @@ -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. diff --git a/src/cmd/compile/internal/gc/go.go b/src/cmd/compile/internal/gc/go.go index 1707e6a11b..df91f6f530 100644 --- a/src/cmd/compile/internal/gc/go.go +++ b/src/cmd/compile/internal/gc/go.go @@ -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 diff --git a/src/cmd/compile/internal/gc/inl.go b/src/cmd/compile/internal/gc/inl.go index 5ada83b715..fde4d6910a 100644 --- a/src/cmd/compile/internal/gc/inl.go +++ b/src/cmd/compile/internal/gc/inl.go @@ -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 { diff --git a/src/cmd/compile/internal/gc/main.go b/src/cmd/compile/internal/gc/main.go index 2a5ff3f5fd..4aa2a2ca47 100644 --- a/src/cmd/compile/internal/gc/main.go +++ b/src/cmd/compile/internal/gc/main.go @@ -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("", ""), 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 + // Typecheck. + TypecheckPackage() - // 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 { - 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 { diff --git a/src/cmd/compile/internal/gc/pgen.go b/src/cmd/compile/internal/gc/pgen.go index 8f7aa8e4e7..e43471dbca 100644 --- a/src/cmd/compile/internal/gc/pgen.go +++ b/src/cmd/compile/internal/gc/pgen.go @@ -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 diff --git a/src/cmd/compile/internal/gc/ssa.go b/src/cmd/compile/internal/gc/ssa.go index b4cf8b6dc7..1fc1feae67 100644 --- a/src/cmd/compile/internal/gc/ssa.go +++ b/src/cmd/compile/internal/gc/ssa.go @@ -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 diff --git a/src/cmd/compile/internal/gc/typecheck.go b/src/cmd/compile/internal/gc/typecheck.go index 1aaa93fc3d..cc5df3ebae 100644 --- a/src/cmd/compile/internal/gc/typecheck.go +++ b/src/cmd/compile/internal/gc/typecheck.go @@ -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 diff --git a/src/cmd/compile/internal/gc/walk.go b/src/cmd/compile/internal/gc/walk.go index 410155b3ea..5545dcb345 100644 --- a/src/cmd/compile/internal/gc/walk.go +++ b/src/cmd/compile/internal/gc/walk.go @@ -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) {