diff options
author | Matthew Dempsky <mdempsky@google.com> | 2021-01-22 15:35:11 -0800 |
---|---|---|
committer | Matthew Dempsky <mdempsky@google.com> | 2021-01-22 16:32:37 -0800 |
commit | 6e46c8fbb5ba440cc503193220476c204100fb40 (patch) | |
tree | 67472f55b9b1f5ab66cbcfd39cf154c1f8c2011c /src/cmd/compile/internal/inline | |
parent | e4ef30a66751c39bdd24764763531f1a4c325845 (diff) | |
parent | 7e0a81d2806b073c6455f73b10fbf2c811703f46 (diff) | |
download | go-6e46c8fbb5ba440cc503193220476c204100fb40.tar.gz go-6e46c8fbb5ba440cc503193220476c204100fb40.zip |
[dev.typeparams] all: merge dev.regabi (7e0a81d) into dev.typeparams
As with CL 285875, this required resolving some conflicts around
handling of //go:embed directives. Still further work is needed to
reject uses of //go:embed in files that don't import "embed", so this
is left as a TODO. (When this code was written for dev.typeparams, we
were still leaning towards not requiring the "embed" import.)
Also, the recent support for inlining closures (CL 283112) interacts
poorly with -G=3 mode. There are some known issues with this code
already (#43818), so for now this CL disables inlining of closures
when in -G=3 mode with a TODO to revisit this once closure inlining is
working fully.
Conflicts:
- src/cmd/compile/internal/noder/noder.go
- src/cmd/compile/internal/typecheck/dcl.go
- src/cmd/compile/internal/typecheck/func.go
- test/run.go
Merge List:
+ 2021-01-22 7e0a81d280 [dev.regabi] all: merge master (dab3e5a) into dev.regabi
+ 2021-01-22 dab3e5affe runtime: switch runtime to libc for openbsd/amd64
+ 2021-01-22 a1b53d85da cmd/go: add documentation for test and xtest fields output by go list
+ 2021-01-22 b268b60774 runtime: remove pthread_kill/pthread_self for openbsd
+ 2021-01-22 ec4051763d runtime: fix typo in mgcscavenge.go
+ 2021-01-22 7ece3a7b17 net/http: fix flaky TestDisableKeepAliveUpgrade
+ 2021-01-22 50cba0506f time: clarify Timer.Reset behavior on AfterFunc Timers
+ 2021-01-22 cf10e69f17 doc/go1.16: mention net/http.Transport.GetProxyConnectHeader
+ 2021-01-22 ec1b945265 doc/go1.16: mention path/filepath.WalkDir
+ 2021-01-22 11def3d40b doc/go1.16: mention syscall.AllThreadsSyscall
+ 2021-01-21 07b0235609 doc/go1.16: add notes about package-specific fs.FS changes
+ 2021-01-21 e2b4f1fea5 doc/go1.16: minor formatting fix
+ 2021-01-21 9f43a9e07b doc/go1.16: mention new debug/elf constants
+ 2021-01-21 3c2f11ba5b cmd/go: overwrite program name with full path
+ 2021-01-21 953d1feca9 all: introduce and use internal/execabs
+ 2021-01-21 b186e4d70d cmd/go: add test case for cgo CC setting
+ 2021-01-21 5a8a2265fb cmd/cgo: report exec errors a bit more clearly
+ 2021-01-21 46e2e2e9d9 cmd/go: pass resolved CC, GCCGO to cgo
+ 2021-01-21 3d40895e36 runtime: switch openbsd/arm64 to pthreads
+ 2021-01-21 d95ca91380 crypto/elliptic: fix P-224 field reduction
+ 2021-01-21 d7e71c01ad [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for dwarf
+ 2021-01-21 5248f59a22 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet for SSA
+ 2021-01-21 970d8b6cb2 [dev.regabi] cmd/compile: replace ir.Name map with ir.NameSet in inlining
+ 2021-01-21 68a4664475 [dev.regabi] cmd/compile: remove tempAssigns in walkCall1
+ 2021-01-21 fd9a391cdd [dev.regabi] cmd/compile: remove CallExpr.Rargs
+ 2021-01-21 19a6db6b63 [dev.regabi] cmd/compile: make sure mkcall* passed non-nil init
+ 2021-01-21 9f036844db [dev.regabi] cmd/compile: use ir.DoChildren directly in inlining
+ 2021-01-21 213c3905e9 [dev.regabi] cmd/compile: use node walked flag to prevent double walk for walkSelect
+ 2021-01-20 1760d736f6 [dev.regabi] cmd/compile: exporting, importing, and inlining functions with OCLOSURE
+ 2021-01-20 ecf4ebf100 cmd/internal/moddeps: check content of all modules in GOROOT
+ 2021-01-20 92cb157cf3 [dev.regabi] cmd/compile: late expansion of return values
+ 2021-01-20 d2d155d1ae runtime: don't adjust timer pp field in timerWaiting status
+ 2021-01-20 803d18fc6c cmd/go: set Incomplete field on go list output if no files match embed
+ 2021-01-20 6e243ce71d cmd/go: have go mod vendor copy embedded files in subdirs
+ 2021-01-20 be28e5abc5 cmd/go: fix mod_get_fallback test
+ 2021-01-20 928bda4f4a runtime: convert openbsd/amd64 locking to libc
+ 2021-01-19 824f2d635c cmd/go: allow go fmt to complete when embedded file is missing
+ 2021-01-19 0575e35e50 cmd/compile: require 'go 1.16' go.mod line for //go:embed
+ 2021-01-19 9423d50d53 [dev.regabi] cmd/compile: use '%q' for printing rune values less than 128
+ 2021-01-19 ccb2e90688 cmd/link: exit before Asmb2 if error
+ 2021-01-19 ca5774a5a5 embed: treat uninitialized FS as empty
+ 2021-01-19 d047c91a6c cmd/link,runtime: switch openbsd/amd64 to pthreads
+ 2021-01-19 61debffd97 runtime: factor out usesLibcall
+ 2021-01-19 9fed39d281 runtime: factor out mStackIsSystemAllocated
+ 2021-01-19 a2f825c542 [dev.regabi] cmd/compile: directly create go.map and go.track symbols
+ 2021-01-19 4a4212c0e5 [dev.regabi] cmd/compile: refactor Linksym creation
+ 2021-01-19 4f5c603c0f [dev.regabi] cmd/compile: cleanup callTargetLSym
+ 2021-01-18 dbab079835 runtime: free Windows event handles after last lock is dropped
+ 2021-01-18 5a8fbb0d2d os: do not close syscall.Stdin in TestReadStdin
+ 2021-01-18 422f38fb6c [dev.regabi] cmd/compile: move stack objects to liveness
+ 2021-01-18 6113db0bb4 [dev.regabi] cmd/compile: convert OPANIC argument to interface{} during typecheck
+ 2021-01-18 4c835f9169 [dev.regabi] cmd/compile: use LinksymOffsetExpr in TypePtr/ItabAddr
+ 2021-01-18 0ffa1ead6e [dev.regabi] cmd/compile: use *obj.LSym instead of *ir.Name for staticdata functions
+ 2021-01-17 7e0fa38aad [dev.regabi] cmd/compile: remove unneeded packages from ir.Pkgs
+ 2021-01-17 99a5db11ac [dev.regabi] cmd/compile: use LinksymOffsetExpr in walkConvInterface
+ 2021-01-17 87845d14f9 [dev.regabi] cmd/compile: add ir.TailCallStmt
+ 2021-01-17 e3027c6828 [dev.regabi] cmd/compile: fix linux-amd64-noopt builder
+ 2021-01-17 59ff93fe64 [dev.regabi] cmd/compile: rename NameOffsetExpr to LinksymOffsetExpr
+ 2021-01-17 82b9cae700 [dev.regabi] cmd/compile: change ir.NameOffsetExpr to use *obj.LSym instead of *Name
+ 2021-01-17 88956fc4b1 [dev.regabi] cmd/compile: stop analyze NameOffsetExpr.Name_ in escape analysis
+ 2021-01-17 7ce2a8383d [dev.regabi] cmd/compile: simplify stack temp initialization
+ 2021-01-17 ba0e8a92fa [dev.regabi] cmd/compile: refactor temp construction in walk
+ 2021-01-17 78e5aabcdb [dev.regabi] cmd/compile: replace Node.HasCall with walk.mayCall
+ 2021-01-16 6de9423445 [dev.regabi] cmd/compile: cleanup OAS2FUNC ordering
+ 2021-01-16 a956a0e909 [dev.regabi] cmd/compile, runtime: fix up comments/error messages from recent renames
+ 2021-01-16 ab3b67abfd [dev.regabi] cmd/compile: remove ONEWOBJ
+ 2021-01-16 c9b1445ac8 [dev.regabi] cmd/compile: remove TypeAssertExpr {Src,Dst}Type fields
+ 2021-01-15 682a1d2176 runtime: detect errors in DuplicateHandle
+ 2021-01-15 9f83418b83 cmd/link: remove GOROOT write in TestBuildForTvOS
+ 2021-01-15 ec9470162f cmd/compile: allow embed into any string or byte slice type
+ 2021-01-15 54198b04db cmd/compile: disallow embed of var inside func
+ 2021-01-15 b386c735e7 cmd/go: fix go generate docs
+ 2021-01-15 bb5075a525 syscall: remove RtlGenRandom and move it into internal/syscall
+ 2021-01-15 1deae0b597 os: invoke processKiller synchronously in testKillProcess
+ 2021-01-15 03a875137f [dev.regabi] cmd/compile: unexport reflectdata.WriteType
+ 2021-01-15 14537e6e54 [dev.regabi] cmd/compile: move stkobj symbol generation to SSA
+ 2021-01-15 ab523fc510 [dev.regabi] cmd/compile: don't promote Byval CaptureVars if Addrtaken
+ 2021-01-15 ff196c3e84 crypto/x509: update iOS bundled roots to version 55188.40.9
+ 2021-01-15 b7a698c73f [dev.regabi] test: disable test on windows because expected contains path separators.
+ 2021-01-15 4be7af23f9 [dev.regabi] cmd/compile: fix ICE during ir.Dump
+ 2021-01-14 e125ccd10e cmd/go: in 'go mod edit', validate versions given to -retract and -exclude
+ 2021-01-14 eb330020dc cmd/dist, cmd/go: pass -arch for C compilation on Darwin
+ 2021-01-14 84e8a06f62 cmd/cgo: remove unnecessary space in cgo export header
+ 2021-01-14 0c86b999c3 cmd/test2json: document passing -test.paniconexit0
+ 2021-01-14 9135795891 cmd/go/internal/load: report positions for embed errors
+ 2021-01-14 35b9c66601 [dev.regabi] cmd/compile,cmd/link: additional code review suggestions for CL 270863
+ 2021-01-14 d9b79e53bb cmd/compile: fix wrong complement for arm64 floating-point comparisons
+ 2021-01-14 c73232d08f cmd/go/internal/load: refactor setErrorPos to PackageError.setPos
+ 2021-01-14 6aa28d3e06 go/build: report positions for go:embed directives
+ 2021-01-14 9734fd482d [dev.regabi] cmd/compile: use node walked flag to prevent double walk for walkSwitch
+ 2021-01-14 f97983249a [dev.regabi] cmd/compile: move more PAUTOHEAP to SSA construction
+ 2021-01-14 4476300425 [dev.regabi] cmd/compile: use byte for CallExpr.Use
+ 2021-01-14 5a5ab24689 [dev.regabi] cmd/compile: do not rely on CallExpr.Rargs for detect already walked calls
+ 2021-01-14 983ac4b086 [dev.regabi] cmd/compile: fix ICE when initializing blank vars
+ 2021-01-13 7eb31d999c cmd/go: add hints to more missing sum error messages
+ 2021-01-13 d6d4673728 [dev.regabi] cmd/compile: fix GOEXPERIMENT=regabi builder
+ 2021-01-13 c41b999ad4 [dev.regabi] cmd/compile: refactor abiutils from "gc" into new "abi"
+ 2021-01-13 861707a8c8 [dev.regabi] cmd/compile: added limited //go:registerparams pragma for new ABI dev
+ 2021-01-13 c1370e918f [dev.regabi] cmd/compile: add code to support register ABI spills around morestack calls
+ 2021-01-13 2abd24f3b7 [dev.regabi] test: make run.go error messages slightly more informative
+ 2021-01-13 9a19481acb [dev.regabi] cmd/compile: make ordering for InvertFlags more stable
+ 2021-01-12 ba76567bc2 cmd/go/internal/modload: delete unused *mvsReqs.next method
+ 2021-01-12 665def2c11 encoding/asn1: document unmarshaling behavior for IMPLICIT string fields
+ 2021-01-11 81ea89adf3 cmd/go: fix non-script staleness checks interacting badly with GOFLAGS
+ 2021-01-11 759309029f doc: update editors.html for Go 1.16
+ 2021-01-11 c3b4c7093a cmd/internal/objfile: don't require runtime.symtab symbol for XCOFF
+ 2021-01-08 59bfc18e34 cmd/go: add hint to read 'go help vcs' to GOVCS errors
+ 2021-01-08 cd6f3a54e4 cmd/go: revise 'go help' documentation for modules
+ 2021-01-08 6192b98751 cmd/go: make hints in error messages more consistent
+ 2021-01-08 25886cf4bd cmd/go: preserve sums for indirect deps fetched by 'go mod download'
+ 2021-01-08 6250833911 runtime/metrics: mark histogram metrics as cumulative
+ 2021-01-08 8f6a9acbb3 runtime/metrics: remove unused StopTheWorld Description field
+ 2021-01-08 6598c65646 cmd/compile: fix exponential-time init-cycle reporting
+ 2021-01-08 fefad1dc85 test: fix timeout code for invoking compiler
+ 2021-01-08 6728118e0a cmd/go: pass signals forward during "go tool"
+ 2021-01-08 e65c543f3c go/build/constraint: add parser for build tag constraint expressions
+ 2021-01-08 0c5afc4fb7 testing/fstest,os: clarify racy behavior of TestFS
+ 2021-01-08 32afcc9436 runtime/metrics: change unit on *-by-size metrics to match bucket unit
+ 2021-01-08 c6513bca5a io/fs: minor corrections to Glob doc
+ 2021-01-08 304f769ffc cmd/compile: don't short-circuit copies whose source is volatile
+ 2021-01-08 ae97717133 runtime,runtime/metrics: use explicit histogram boundaries
+ 2021-01-08 a9ccd2d795 go/build: skip string literal while findEmbed
+ 2021-01-08 d92f8add32 archive/tar: fix typo in comment
+ 2021-01-08 cab1202183 cmd/link: accept extra blocks in TestFallocate
+ 2021-01-08 ee4d32249b io/fs: minor corrections to Glob release date
+ 2021-01-08 54bd1ccce2 cmd: update to latest golang.org/x/tools
+ 2021-01-07 9ec21a8f34 Revert "reflect: support multiple keys in struct tags"
+ 2021-01-07 091414b5b7 io/fs: correct WalkDirFunc documentation
+ 2021-01-07 9b55088d6b doc/go1.16: add release note for disallowing non-ASCII import paths
+ 2021-01-07 fa90aaca7d cmd/compile: fix late expand_calls leaf type for OpStructSelect/OpArraySelect
+ 2021-01-07 7cee66d4cb cmd/go: add documentation for Embed fields in go list output
+ 2021-01-07 e60cffa4ca html/template: attach functions to namespace
+ 2021-01-07 6da2d3b7d7 cmd/link: fix typo in asm.go
+ 2021-01-07 df81a15819 runtime: check mips64 VDSO clock_gettime return code
+ 2021-01-06 4787e906cf crypto/x509: rollback new CertificateRequest fields
+ 2021-01-06 c9658bee93 cmd/go: make module suggestion more friendly
+ 2021-01-06 4c668b25c6 runtime/metrics: fix panic message for Float64Histogram
+ 2021-01-06 d2131704a6 net/http/httputil: fix deadlock in DumpRequestOut
+ 2021-01-05 3e1e13ce6d cmd/go: set cfg.BuildMod to "readonly" by default with no module root
+ 2021-01-05 0b0d004983 cmd/go: pass embedcfg to gccgo if supported
+ 2021-01-05 1b85e7c057 cmd/go: don't scan gccgo standard library packages for imports
+ 2021-01-05 6b37b15d95 runtime: don't take allglock in tracebackothers
+ 2021-01-04 9eef49cfa6 math/rand: fix typo in comment
+ 2021-01-04 b01fb2af9e testing/fstest: fix typo in error message
+ 2021-01-01 3dd5867605 doc: 2021 is the Year of the Gopher
+ 2020-12-31 95ce805d14 io/fs: remove darwin/arm64 special condition
+ 2020-12-30 20d0991b86 lib/time, time/tzdata: update tzdata to 2020f
+ 2020-12-30 ed301733bb misc/cgo/testcarchive: remove special flags for Darwin/ARM
+ 2020-12-30 0ae2e032f2 misc/cgo/test: enable TestCrossPackageTests on darwin/arm64
+ 2020-12-29 780b4de16b misc/ios: fix wording for command line instructions
+ 2020-12-29 b4a71c95d2 doc/go1.16: reference misc/ios/README for how to build iOS programs
+ 2020-12-29 f83e0f6616 misc/ios: add to README how to build ios executables
+ 2020-12-28 4fd9455882 io/fs: fix typo in comment
Change-Id: If24bb93f1e1e7deb1d92ba223c85940ab93b2732
Diffstat (limited to 'src/cmd/compile/internal/inline')
-rw-r--r-- | src/cmd/compile/internal/inline/inl.go | 372 |
1 files changed, 291 insertions, 81 deletions
diff --git a/src/cmd/compile/internal/inline/inl.go b/src/cmd/compile/internal/inline/inl.go index 6f5f6499ce..f0be169f56 100644 --- a/src/cmd/compile/internal/inline/inl.go +++ b/src/cmd/compile/internal/inline/inl.go @@ -4,7 +4,7 @@ // // The inlining facility makes 2 passes: first caninl determines which // functions are suitable for inlining, and for those that are it -// saves a copy of the body. Then inlcalls walks each function body to +// saves a copy of the body. Then InlineCalls walks each function body to // expand calls to inlinable functions. // // The Debug.l flag controls the aggressiveness. Note that main() swaps level 0 and 1, @@ -27,7 +27,6 @@ package inline import ( - "errors" "fmt" "go/constant" "strings" @@ -74,12 +73,12 @@ func InlinePackage() { }) } -// Caninl determines whether fn is inlineable. +// CanInline determines whether fn is inlineable. // If so, CanInline saves fn->nbody in fn->inl and substitutes it with a copy. // fn and ->nbody will already have been typechecked. func CanInline(fn *ir.Func) { if fn.Nname == nil { - base.Fatalf("caninl no nname %+v", fn) + base.Fatalf("CanInline no nname %+v", fn) } var reason string // reason, if any, that the function was not inlined @@ -144,7 +143,7 @@ func CanInline(fn *ir.Func) { } if fn.Typecheck() == 0 { - base.Fatalf("caninl on non-typechecked function %v", fn) + base.Fatalf("CanInline on non-typechecked function %v", fn) } n := fn.Nname @@ -170,7 +169,6 @@ func CanInline(fn *ir.Func) { visitor := hairyVisitor{ budget: inlineMaxBudget, extraCallCost: cc, - usedLocals: make(map[*ir.Name]bool), } if visitor.tooHairy(fn) { reason = visitor.reason @@ -180,7 +178,7 @@ func CanInline(fn *ir.Func) { n.Func.Inl = &ir.Inline{ Cost: inlineMaxBudget - visitor.budget, Dcl: pruneUnusedAutos(n.Defn.(*ir.Func).Dcl, &visitor), - Body: ir.DeepCopyList(src.NoXPos, fn.Body), + Body: inlcopylist(fn.Body), } if base.Flag.LowerM > 1 { @@ -200,11 +198,11 @@ func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) { return } if n.Op() != ir.ONAME || n.Class != ir.PFUNC { - base.Fatalf("inlFlood: unexpected %v, %v, %v", n, n.Op(), n.Class) + base.Fatalf("Inline_Flood: unexpected %v, %v, %v", n, n.Op(), n.Class) } fn := n.Func if fn == nil { - base.Fatalf("inlFlood: missing Func on %v", n) + base.Fatalf("Inline_Flood: missing Func on %v", n) } if fn.Inl == nil { return @@ -217,10 +215,8 @@ func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) { typecheck.ImportedBody(fn) - // Recursively identify all referenced functions for - // reexport. We want to include even non-called functions, - // because after inlining they might be callable. - ir.VisitList(ir.Nodes(fn.Inl.Body), func(n ir.Node) { + var doFlood func(n ir.Node) + doFlood = func(n ir.Node) { switch n.Op() { case ir.OMETHEXPR, ir.ODOTMETH: Inline_Flood(ir.MethodExprName(n), exportsym) @@ -239,15 +235,16 @@ func Inline_Flood(n *ir.Name, exportsym func(*ir.Name)) { // Okay, because we don't yet inline indirect // calls to method values. case ir.OCLOSURE: - // If the closure is inlinable, we'll need to - // flood it too. But today we don't support - // inlining functions that contain closures. - // - // When we do, we'll probably want: - // inlFlood(n.Func.Closure.Func.Nname) - base.Fatalf("unexpected closure in inlinable function") + // VisitList doesn't visit closure bodies, so force a + // recursive call to VisitList on the body of the closure. + ir.VisitList(n.(*ir.ClosureExpr).Func.Body, doFlood) } - }) + } + + // Recursively identify all referenced functions for + // reexport. We want to include even non-called functions, + // because after inlining they might be callable. + ir.VisitList(ir.Nodes(fn.Inl.Body), doFlood) } // hairyVisitor visits a function body to determine its inlining @@ -256,18 +253,13 @@ type hairyVisitor struct { budget int32 reason string extraCallCost int32 - usedLocals map[*ir.Name]bool - do func(ir.Node) error + usedLocals ir.NameSet + do func(ir.Node) bool } -var errBudget = errors.New("too expensive") - func (v *hairyVisitor) tooHairy(fn *ir.Func) bool { v.do = v.doNode // cache closure - - err := errChildren(fn, v.do) - if err != nil { - v.reason = err.Error() + if ir.DoChildren(fn, v.do) { return true } if v.budget < 0 { @@ -277,11 +269,10 @@ func (v *hairyVisitor) tooHairy(fn *ir.Func) bool { return false } -func (v *hairyVisitor) doNode(n ir.Node) error { +func (v *hairyVisitor) doNode(n ir.Node) bool { if n == nil { - return nil + return false } - switch n.Op() { // Call is okay if inlinable and we have the budget for the body. case ir.OCALLFUNC: @@ -295,7 +286,8 @@ func (v *hairyVisitor) doNode(n ir.Node) error { if name.Class == ir.PFUNC && types.IsRuntimePkg(name.Sym().Pkg) { fn := name.Sym().Name if fn == "getcallerpc" || fn == "getcallersp" { - return errors.New("call to " + fn) + v.reason = "call to " + fn + return true } if fn == "throw" { v.budget -= inlineExtraThrowCost @@ -346,38 +338,61 @@ func (v *hairyVisitor) doNode(n ir.Node) error { v.budget -= v.extraCallCost case ir.OPANIC: + n := n.(*ir.UnaryExpr) + if n.X.Op() == ir.OCONVIFACE && n.X.(*ir.ConvExpr).Implicit() { + // Hack to keep reflect.flag.mustBe inlinable for TestIntendedInlining. + // Before CL 284412, these conversions were introduced later in the + // compiler, so they didn't count against inlining budget. + v.budget++ + } v.budget -= inlineExtraPanicCost case ir.ORECOVER: // recover matches the argument frame pointer to find // the right panic value, so it needs an argument frame. - return errors.New("call to recover") + v.reason = "call to recover" + return true + + case ir.OCLOSURE: + // TODO(danscales,mdempsky): Get working with -G. + // Probably after #43818 is fixed. + if base.Flag.G > 0 { + v.reason = "inlining closures not yet working with -G" + return true + } - case ir.OCLOSURE, - ir.ORANGE, + // TODO(danscales) - fix some bugs when budget is lowered below 30 + // Maybe make budget proportional to number of closure variables, e.g.: + //v.budget -= int32(len(n.(*ir.ClosureExpr).Func.ClosureVars) * 3) + v.budget -= 30 + + case ir.ORANGE, ir.OSELECT, ir.OGO, ir.ODEFER, ir.ODCLTYPE, // can't print yet - ir.ORETJMP: - return errors.New("unhandled op " + n.Op().String()) + ir.OTAILCALL: + v.reason = "unhandled op " + n.Op().String() + return true case ir.OAPPEND: v.budget -= inlineExtraAppendCost case ir.ODCLCONST, ir.OFALL: // These nodes don't produce code; omit from inlining budget. - return nil + return false case ir.OFOR, ir.OFORUNTIL: n := n.(*ir.ForStmt) if n.Label != nil { - return errors.New("labeled control") + v.reason = "labeled control" + return true } case ir.OSWITCH: n := n.(*ir.SwitchStmt) if n.Label != nil { - return errors.New("labeled control") + v.reason = "labeled control" + return true } // case ir.ORANGE, ir.OSELECT in "unhandled" above @@ -393,22 +408,15 @@ func (v *hairyVisitor) doNode(n ir.Node) error { if ir.IsConst(n.Cond, constant.Bool) { // This if and the condition cost nothing. // TODO(rsc): It seems strange that we visit the dead branch. - if err := errList(n.Init(), v.do); err != nil { - return err - } - if err := errList(n.Body, v.do); err != nil { - return err - } - if err := errList(n.Else, v.do); err != nil { - return err - } - return nil + return doList(n.Init(), v.do) || + doList(n.Body, v.do) || + doList(n.Else, v.do) } case ir.ONAME: n := n.(*ir.Name) if n.Class == ir.PAUTO { - v.usedLocals[n] = true + v.usedLocals.Add(n) } case ir.OBLOCK: @@ -428,10 +436,11 @@ func (v *hairyVisitor) doNode(n ir.Node) error { // When debugging, don't stop early, to get full cost of inlining this function if v.budget < 0 && base.Flag.LowerM < 2 && !logopt.Enabled() { - return errBudget + v.reason = "too expensive" + return true } - return errChildren(n, v.do) + return ir.DoChildren(n, v.do) } func isBigFunc(fn *ir.Func) bool { @@ -442,6 +451,52 @@ func isBigFunc(fn *ir.Func) bool { }) } +// inlcopylist (together with inlcopy) recursively copies a list of nodes, except +// that it keeps the same ONAME, OTYPE, and OLITERAL nodes. It is used for copying +// the body and dcls of an inlineable function. +func inlcopylist(ll []ir.Node) []ir.Node { + s := make([]ir.Node, len(ll)) + for i, n := range ll { + s[i] = inlcopy(n) + } + return s +} + +// inlcopy is like DeepCopy(), but does extra work to copy closures. +func inlcopy(n ir.Node) ir.Node { + var edit func(ir.Node) ir.Node + edit = func(x ir.Node) ir.Node { + switch x.Op() { + case ir.ONAME, ir.OTYPE, ir.OLITERAL, ir.ONIL: + return x + } + m := ir.Copy(x) + ir.EditChildren(m, edit) + if x.Op() == ir.OCLOSURE { + x := x.(*ir.ClosureExpr) + // Need to save/duplicate x.Func.Nname, + // x.Func.Nname.Ntype, x.Func.Dcl, x.Func.ClosureVars, and + // x.Func.Body for iexport and local inlining. + oldfn := x.Func + newfn := ir.NewFunc(oldfn.Pos()) + if oldfn.ClosureCalled() { + newfn.SetClosureCalled(true) + } + m.(*ir.ClosureExpr).Func = newfn + newfn.Nname = ir.NewNameAt(oldfn.Nname.Pos(), oldfn.Nname.Sym()) + // XXX OK to share fn.Type() ?? + newfn.Nname.SetType(oldfn.Nname.Type()) + newfn.Nname.Ntype = inlcopy(oldfn.Nname.Ntype).(ir.Ntype) + newfn.Body = inlcopylist(oldfn.Body) + // Make shallow copy of the Dcl and ClosureVar slices + newfn.Dcl = append([]*ir.Name(nil), oldfn.Dcl...) + newfn.ClosureVars = append([]*ir.Name(nil), oldfn.ClosureVars...) + } + return m + } + return edit(n) +} + // Inlcalls/nodelist/node walks fn's statements and expressions and substitutes any // calls made to inlineable functions. This is the external entry point. func InlineCalls(fn *ir.Func) { @@ -762,13 +817,6 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b if ln.Class == ir.PPARAMOUT { // return values handled below. continue } - if ir.IsParamStackCopy(ln) { // ignore the on-stack copy of a parameter that moved to the heap - // TODO(mdempsky): Remove once I'm confident - // this never actually happens. We currently - // perform inlining before escape analysis, so - // nothing should have moved to the heap yet. - base.Fatalf("impossible: %v", ln) - } inlf := typecheck.Expr(inlvar(ln)).(*ir.Name) inlvars[ln] = inlf if base.Flag.GenDwarfInl > 0 { @@ -925,6 +973,7 @@ func mkinlcall(n *ir.CallExpr, fn *ir.Func, maxCost int32, inlMap map[*ir.Func]b inlvars: inlvars, bases: make(map[*src.PosBase]*src.PosBase), newInlIndex: newIndex, + fn: fn, } subst.edit = subst.node @@ -1031,6 +1080,12 @@ type inlsubst struct { newInlIndex int edit func(ir.Node) ir.Node // cached copy of subst.node method value closure + + // If non-nil, we are inside a closure inside the inlined function, and + // newclofn is the Func of the new inlined closure. + newclofn *ir.Func + + fn *ir.Func // For debug -- the func that is being inlined } // list inlines a list of nodes. @@ -1042,6 +1097,157 @@ func (subst *inlsubst) list(ll ir.Nodes) []ir.Node { return s } +// fields returns a list of the fields of a struct type representing receiver, +// params, or results, after duplicating the field nodes and substituting the +// Nname nodes inside the field nodes. +func (subst *inlsubst) fields(oldt *types.Type) []*types.Field { + oldfields := oldt.FieldSlice() + newfields := make([]*types.Field, len(oldfields)) + for i := range oldfields { + newfields[i] = oldfields[i].Copy() + if oldfields[i].Nname != nil { + newfields[i].Nname = subst.node(oldfields[i].Nname.(*ir.Name)) + } + } + return newfields +} + +// clovar creates a new ONAME node for a local variable or param of a closure +// inside a function being inlined. +func (subst *inlsubst) clovar(n *ir.Name) *ir.Name { + // TODO(danscales): want to get rid of this shallow copy, with code like the + // following, but it is hard to copy all the necessary flags in a maintainable way. + // m := ir.NewNameAt(n.Pos(), n.Sym()) + // m.Class = n.Class + // m.SetType(n.Type()) + // m.SetTypecheck(1) + //if n.IsClosureVar() { + // m.SetIsClosureVar(true) + //} + m := &ir.Name{} + *m = *n + m.Curfn = subst.newclofn + if n.Defn != nil && n.Defn.Op() == ir.ONAME { + if !n.IsClosureVar() { + base.FatalfAt(n.Pos(), "want closure variable, got: %+v", n) + } + if n.Sym().Pkg != types.LocalPkg { + // If the closure came from inlining a function from + // another package, must change package of captured + // variable to localpkg, so that the fields of the closure + // struct are local package and can be accessed even if + // name is not exported. If you disable this code, you can + // reproduce the problem by running 'go test + // go/internal/srcimporter'. TODO(mdempsky) - maybe change + // how we create closure structs? + m.SetSym(types.LocalPkg.Lookup(n.Sym().Name)) + } + // Make sure any inlvar which is the Defn + // of an ONAME closure var is rewritten + // during inlining. Don't substitute + // if Defn node is outside inlined function. + if subst.inlvars[n.Defn.(*ir.Name)] != nil { + m.Defn = subst.node(n.Defn) + } + } + if n.Outer != nil { + // Either the outer variable is defined in function being inlined, + // and we will replace it with the substituted variable, or it is + // defined outside the function being inlined, and we should just + // skip the outer variable (the closure variable of the function + // being inlined). + s := subst.node(n.Outer).(*ir.Name) + if s == n.Outer { + s = n.Outer.Outer + } + m.Outer = s + } + return m +} + +// closure does the necessary substitions for a ClosureExpr n and returns the new +// closure node. +func (subst *inlsubst) closure(n *ir.ClosureExpr) ir.Node { + m := ir.Copy(n) + m.SetPos(subst.updatedPos(m.Pos())) + ir.EditChildren(m, subst.edit) + + //fmt.Printf("Inlining func %v with closure into %v\n", subst.fn, ir.FuncName(ir.CurFunc)) + + // The following is similar to funcLit + oldfn := n.Func + newfn := ir.NewFunc(oldfn.Pos()) + // These three lines are not strictly necessary, but just to be clear + // that new function needs to redo typechecking and inlinability. + newfn.SetTypecheck(0) + newfn.SetInlinabilityChecked(false) + newfn.Inl = nil + newfn.SetIsHiddenClosure(true) + newfn.Nname = ir.NewNameAt(n.Pos(), ir.BlankNode.Sym()) + newfn.Nname.Func = newfn + newfn.Nname.Ntype = subst.node(oldfn.Nname.Ntype).(ir.Ntype) + newfn.Nname.Defn = newfn + + m.(*ir.ClosureExpr).Func = newfn + newfn.OClosure = m.(*ir.ClosureExpr) + + if subst.newclofn != nil { + //fmt.Printf("Inlining a closure with a nested closure\n") + } + prevxfunc := subst.newclofn + + // Mark that we are now substituting within a closure (within the + // inlined function), and create new nodes for all the local + // vars/params inside this closure. + subst.newclofn = newfn + newfn.Dcl = nil + newfn.ClosureVars = nil + for _, oldv := range oldfn.Dcl { + newv := subst.clovar(oldv) + subst.inlvars[oldv] = newv + newfn.Dcl = append(newfn.Dcl, newv) + } + for _, oldv := range oldfn.ClosureVars { + newv := subst.clovar(oldv) + subst.inlvars[oldv] = newv + newfn.ClosureVars = append(newfn.ClosureVars, newv) + } + + // Need to replace ONAME nodes in + // newfn.Type().FuncType().Receiver/Params/Results.FieldSlice().Nname + oldt := oldfn.Type() + newrecvs := subst.fields(oldt.Recvs()) + var newrecv *types.Field + if len(newrecvs) > 0 { + newrecv = newrecvs[0] + } + newt := types.NewSignature(oldt.Pkg(), newrecv, + subst.fields(oldt.Params()), subst.fields(oldt.Results())) + + newfn.Nname.SetType(newt) + newfn.Body = subst.list(oldfn.Body) + + // Remove the nodes for the current closure from subst.inlvars + for _, oldv := range oldfn.Dcl { + delete(subst.inlvars, oldv) + } + for _, oldv := range oldfn.ClosureVars { + delete(subst.inlvars, oldv) + } + // Go back to previous closure func + subst.newclofn = prevxfunc + + // Actually create the named function for the closure, now that + // the closure is inlined in a specific function. + m.SetTypecheck(0) + if oldfn.ClosureCalled() { + typecheck.Callee(m) + } else { + typecheck.Expr(m) + } + return m +} + // node recursively copies a node from the saved pristine body of the // inlined function, substituting references to input/output // parameters with ones to the tmpnames, and substituting returns with @@ -1056,13 +1262,17 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { n := n.(*ir.Name) // Handle captured variables when inlining closures. - if n.IsClosureVar() { + if n.IsClosureVar() && subst.newclofn == nil { o := n.Outer + // Deal with case where sequence of closures are inlined. + // TODO(danscales) - write test case to see if we need to + // go up multiple levels. + if o.Curfn != ir.CurFunc { + o = o.Outer + } + // make sure the outer param matches the inlining location - // NB: if we enabled inlining of functions containing OCLOSURE or refined - // the reassigned check via some sort of copy propagation this would most - // likely need to be changed to a loop to walk up to the correct Param if o == nil || o.Curfn != ir.CurFunc { base.Fatalf("%v: unresolvable capture %v\n", ir.Line(n), n) } @@ -1098,6 +1308,10 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { } case ir.ORETURN: + if subst.newclofn != nil { + // Don't do special substitutions if inside a closure + break + } // Since we don't handle bodies with closures, // this return is guaranteed to belong to the current inlined function. n := n.(*ir.ReturnStmt) @@ -1136,6 +1350,10 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { return m case ir.OLABEL: + if subst.newclofn != nil { + // Don't do special substitutions if inside a closure + break + } n := n.(*ir.LabelStmt) m := ir.Copy(n).(*ir.LabelStmt) m.SetPos(subst.updatedPos(m.Pos())) @@ -1143,10 +1361,10 @@ func (subst *inlsubst) node(n ir.Node) ir.Node { p := fmt.Sprintf("%s·%d", n.Label.Name, inlgen) m.Label = typecheck.Lookup(p) return m - } - if n.Op() == ir.OCLOSURE { - base.Fatalf("cannot inline function containing closure: %+v", n) + case ir.OCLOSURE: + return subst.closure(n.(*ir.ClosureExpr)) + } m := ir.Copy(n) @@ -1171,7 +1389,7 @@ func pruneUnusedAutos(ll []*ir.Name, vis *hairyVisitor) []*ir.Name { s := make([]*ir.Name, 0, len(ll)) for _, n := range ll { if n.Class == ir.PAUTO { - if _, found := vis.usedLocals[n]; !found { + if !vis.usedLocals.Has(n) { continue } } @@ -1191,21 +1409,13 @@ func numNonClosures(list []*ir.Func) int { return count } -// TODO(mdempsky): Update inl.go to use ir.DoChildren directly. -func errChildren(n ir.Node, do func(ir.Node) error) (err error) { - ir.DoChildren(n, func(x ir.Node) bool { - err = do(x) - return err != nil - }) - return -} -func errList(list []ir.Node, do func(ir.Node) error) error { +func doList(list []ir.Node, do func(ir.Node) bool) bool { for _, x := range list { if x != nil { - if err := do(x); err != nil { - return err + if do(x) { + return true } } } - return nil + return false } |