aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/inline
diff options
context:
space:
mode:
authorMatthew Dempsky <mdempsky@google.com>2021-01-22 15:35:11 -0800
committerMatthew Dempsky <mdempsky@google.com>2021-01-22 16:32:37 -0800
commit6e46c8fbb5ba440cc503193220476c204100fb40 (patch)
tree67472f55b9b1f5ab66cbcfd39cf154c1f8c2011c /src/cmd/compile/internal/inline
parente4ef30a66751c39bdd24764763531f1a4c325845 (diff)
parent7e0a81d2806b073c6455f73b10fbf2c811703f46 (diff)
downloadgo-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.go372
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
}