[dev.regabi] cmd/compile: exporting, importing, and inlining functions with OCLOSURE

I have exporting, importing, and inlining of functions with closures
working in all cases (issue #28727). all.bash runs successfully without
errors.

Approach:
  - Write out the Func type, Dcls, ClosureVars, and Body when exporting
    an OCLOSURE.

  - When importing an OCLOSURE, read in the type, dcls, closure vars,
    and body, and then do roughly equivalent code to (*noder).funcLit

  - During inlining of a closure within inlined function, create new
    nodes for all params and local variables (including closure
    variables), so they can have a new Curfn and some other field
    values. Must substitute not only on the Nbody of the closure, but
    also the Type, Cvars, and Dcl fields.

Fixes #28727

Change-Id: I4da1e2567c3fa31a5121afbe82dc4e5ee32b3170
Reviewed-on: https://go-review.googlesource.com/c/go/+/283112
Run-TryBot: Dan Scales <danscales@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Matthew Dempsky <mdempsky@google.com>
Trust: Dan Scales <danscales@google.com>
This commit is contained in:
Dan Scales 2020-12-01 14:48:03 -08:00
parent 92cb157cf3
commit 1760d736f6
13 changed files with 472 additions and 87 deletions

View File

@ -218,6 +218,10 @@ func Batch(fns []*ir.Func, recursive bool) {
// Construct data-flow graph from syntax trees.
for _, fn := range fns {
if base.Flag.W > 1 {
s := fmt.Sprintf("\nbefore escape %v", fn)
ir.Dump(s, fn)
}
b.initFunc(fn)
}
for _, fn := range fns {

View File

@ -180,7 +180,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 {
@ -217,10 +217,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 +237,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
@ -360,8 +359,13 @@ func (v *hairyVisitor) doNode(n ir.Node) error {
// the right panic value, so it needs an argument frame.
return errors.New("call to recover")
case ir.OCLOSURE,
ir.ORANGE,
case ir.OCLOSURE:
// 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,
@ -449,6 +453,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) {
@ -925,6 +975,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 +1082,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 +1099,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 +1264,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 +1310,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 +1352,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 +1363,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)

View File

@ -1020,6 +1020,15 @@ func dumpNodeHeader(w io.Writer, n Node) {
fmt.Fprintf(w, " defn(%p)", n.Name().Defn)
}
if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Curfn != nil {
// Useful to see where Defn is set and what node it points to
fmt.Fprintf(w, " curfn(%p)", n.Name().Curfn)
}
if base.Debug.DumpPtrs != 0 && n.Name() != nil && n.Name().Outer != nil {
// Useful to see where Defn is set and what node it points to
fmt.Fprintf(w, " outer(%p)", n.Name().Outer)
}
if EscFmt != nil {
if esc := EscFmt(n); esc != "" {
fmt.Fprintf(w, " %s", esc)
@ -1187,6 +1196,18 @@ func dumpNode(w io.Writer, n Node, depth int) {
dumpNode(w, dcl, depth+1)
}
}
if len(fn.ClosureVars) > 0 {
indent(w, depth)
fmt.Fprintf(w, "%+v-ClosureVars", n.Op())
for _, cv := range fn.ClosureVars {
dumpNode(w, cv, depth+1)
}
}
if len(fn.Enter) > 0 {
indent(w, depth)
fmt.Fprintf(w, "%+v-Enter", n.Op())
dumpNodes(w, fn.Enter, depth+1)
}
if len(fn.Body) > 0 {
indent(w, depth)
fmt.Fprintf(w, "%+v-body", n.Op())

View File

@ -291,6 +291,10 @@ const (
OTSLICE // []int
// misc
// intermediate representation of an inlined call. Uses Init (assignments
// for the captured variables, parameters, retvars, & INLMARK op),
// Body (body of the inlined function), and ReturnVars (list of
// return values)
OINLCALL // intermediary representation of an inlined call.
OEFACE // itable and data words of an empty-interface value.
OITAB // itable word of an interface value.

View File

@ -142,7 +142,15 @@ func Package() {
for i := 0; i < len(typecheck.Target.Decls); i++ {
n := typecheck.Target.Decls[i]
if n.Op() == ir.ODCLFUNC {
if base.Flag.W > 1 {
s := fmt.Sprintf("\nbefore typecheck %v", n)
ir.Dump(s, n)
}
typecheck.FuncBody(n.(*ir.Func))
if base.Flag.W > 1 {
s := fmt.Sprintf("\nafter typecheck %v", n)
ir.Dump(s, n)
}
fcount++
}
}

View File

@ -145,7 +145,7 @@ func ImportedBody(fn *ir.Func) {
// declarations are added to fn.Func.Dcl by funcBody(). Move them
// to fn.Func.Inl.Dcl for consistency with how local functions
// behave. (Append because ImportedBody may be called multiple
// times.)
// times on same fn.)
fn.Inl.Dcl = append(fn.Inl.Dcl, fn.Dcl...)
fn.Dcl = nil
@ -303,8 +303,15 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
return
}
fn.Nname.SetSym(closurename(ir.CurFunc))
ir.MarkFunc(fn.Nname)
// Don't give a name and add to xtop if we are typechecking an inlined
// body in ImportedBody(), since we only want to create the named function
// when the closure is actually inlined (and then we force a typecheck
// explicitly in (*inlsubst).node()).
inTypeCheckInl := ir.CurFunc != nil && ir.CurFunc.Body == nil
if !inTypeCheckInl {
fn.Nname.SetSym(closurename(ir.CurFunc))
ir.MarkFunc(fn.Nname)
}
Func(fn)
clo.SetType(fn.Type())
@ -338,7 +345,14 @@ func tcClosure(clo *ir.ClosureExpr, top int) {
}
fn.ClosureVars = fn.ClosureVars[:out]
Target.Decls = append(Target.Decls, fn)
if base.Flag.W > 1 {
s := fmt.Sprintf("New closure func: %s", ir.FuncName(fn))
ir.Dump(s, fn)
}
if !inTypeCheckInl {
// Add function to xtop once only when we give it a name
Target.Decls = append(Target.Decls, fn)
}
}
// type check function definition

View File

@ -423,9 +423,13 @@ type exportWriter struct {
prevLine int64
prevColumn int64
// dclIndex maps function-scoped declarations to their index
// within their respective Func's Dcl list.
dclIndex map[*ir.Name]int
// dclIndex maps function-scoped declarations to an int used to refer to
// them later in the function. For local variables/params, the int is
// non-negative and in order of the appearance in the Func's Dcl list. For
// closure variables, the index is negative starting at -2.
dclIndex map[*ir.Name]int
maxDclIndex int
maxClosureVarIndex int
}
func (p *iexporter) doDecl(n *ir.Name) {
@ -1038,14 +1042,19 @@ func (w *exportWriter) typeExt(t *types.Type) {
// Inline bodies.
func (w *exportWriter) funcBody(fn *ir.Func) {
w.int64(int64(len(fn.Inl.Dcl)))
for i, n := range fn.Inl.Dcl {
func (w *exportWriter) writeNames(dcl []*ir.Name) {
w.int64(int64(len(dcl)))
for i, n := range dcl {
w.pos(n.Pos())
w.localIdent(n.Sym())
w.typ(n.Type())
w.dclIndex[n] = i
w.dclIndex[n] = w.maxDclIndex + i
}
w.maxDclIndex += len(dcl)
}
func (w *exportWriter) funcBody(fn *ir.Func) {
w.writeNames(fn.Inl.Dcl)
w.stmtList(fn.Inl.Body)
}
@ -1315,8 +1324,30 @@ func (w *exportWriter) expr(n ir.Node) {
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// should have been resolved by typechecking - handled by default case
// case OCLOSURE:
// unimplemented - handled by default case
case ir.OCLOSURE:
n := n.(*ir.ClosureExpr)
w.op(ir.OCLOSURE)
w.pos(n.Pos())
w.signature(n.Type())
// Write out id for the Outer of each conditional variable. The
// conditional variable itself for this closure will be re-created
// during import.
w.int64(int64(len(n.Func.ClosureVars)))
for i, cv := range n.Func.ClosureVars {
w.pos(cv.Pos())
w.localName(cv.Outer)
// Closure variable (which will be re-created during
// import) is given via a negative id, starting at -2,
// which is used to refer to it later in the function
// during export. -1 represents blanks.
w.dclIndex[cv] = -(i + 2) - w.maxClosureVarIndex
}
w.maxClosureVarIndex += len(n.Func.ClosureVars)
// like w.funcBody(n.Func), but not for .Inl
w.writeNames(n.Func.Dcl)
w.stmtList(n.Func.Body)
// case OCOMPLIT:
// should have been resolved by typechecking - handled by default case

View File

@ -265,6 +265,9 @@ type importReader struct {
// curfn is the current function we're importing into.
curfn *ir.Func
// Slice of all dcls for function, including any interior closures
allDcls []*ir.Name
allClosureVars []*ir.Name
}
func (p *iimporter) newReader(off uint64, pkg *types.Pkg) *importReader {
@ -721,6 +724,7 @@ func (r *importReader) doInline(fn *ir.Func) {
base.Fatalf("%v already has inline body", fn)
}
//fmt.Printf("Importing %v\n", n)
r.funcBody(fn)
importlist = append(importlist, fn)
@ -754,6 +758,24 @@ func (r *importReader) funcBody(fn *ir.Func) {
r.curfn = fn
// Import local declarations.
fn.Inl.Dcl = r.readFuncDcls(fn)
// Import function body.
body := r.stmtList()
if body == nil {
// Make sure empty body is not interpreted as
// no inlineable body (see also parser.fnbody)
// (not doing so can cause significant performance
// degradation due to unnecessary calls to empty
// functions).
body = []ir.Node{}
}
fn.Inl.Body = body
r.curfn = outerfn
}
func (r *importReader) readNames(fn *ir.Func) []*ir.Name {
dcls := make([]*ir.Name, r.int64())
for i := range dcls {
n := ir.NewDeclNameAt(r.pos(), ir.ONAME, r.localIdent())
@ -762,7 +784,12 @@ func (r *importReader) funcBody(fn *ir.Func) {
n.SetType(r.typ())
dcls[i] = n
}
fn.Inl.Dcl = dcls
r.allDcls = append(r.allDcls, dcls...)
return dcls
}
func (r *importReader) readFuncDcls(fn *ir.Func) []*ir.Name {
dcls := r.readNames(fn)
// Fixup parameter classes and associate with their
// signature's type fields.
@ -787,28 +814,18 @@ func (r *importReader) funcBody(fn *ir.Func) {
for _, f := range typ.Results().FieldSlice() {
fix(f, ir.PPARAMOUT)
}
// Import function body.
body := r.stmtList()
if body == nil {
// Make sure empty body is not interpreted as
// no inlineable body (see also parser.fnbody)
// (not doing so can cause significant performance
// degradation due to unnecessary calls to empty
// functions).
body = []ir.Node{}
}
fn.Inl.Body = body
r.curfn = outerfn
return dcls
}
func (r *importReader) localName() *ir.Name {
i := r.int64()
if i < 0 {
if i == -1 {
return ir.BlankNode.(*ir.Name)
}
return r.curfn.Inl.Dcl[i]
if i < 0 {
return r.allClosureVars[-i-2]
}
return r.allDcls[i]
}
func (r *importReader) stmtList() []ir.Node {
@ -924,8 +941,38 @@ func (r *importReader) node() ir.Node {
// case OTARRAY, OTMAP, OTCHAN, OTSTRUCT, OTINTER, OTFUNC:
// unreachable - should have been resolved by typechecking
// case OCLOSURE:
// unimplemented
case ir.OCLOSURE:
//println("Importing CLOSURE")
pos := r.pos()
typ := r.signature(nil)
// All the remaining code below is similar to (*noder).funcLit(), but
// with Dcls and ClosureVars lists already set up
fn := ir.NewFunc(pos)
fn.SetIsHiddenClosure(true)
fn.Nname = ir.NewNameAt(pos, ir.BlankNode.Sym())
fn.Nname.Func = fn
fn.Nname.Ntype = ir.TypeNode(typ)
fn.Nname.Defn = fn
fn.Nname.SetType(typ)
cvars := make([]*ir.Name, r.int64())
for i := range cvars {
cvars[i] = ir.CaptureName(r.pos(), fn, r.localName().Canonical())
}
fn.ClosureVars = cvars
r.allClosureVars = append(r.allClosureVars, cvars...)
fn.Dcl = r.readFuncDcls(fn)
body := r.stmtList()
ir.FinishCaptureNames(pos, r.curfn, fn)
clo := ir.NewClosureExpr(pos, fn)
fn.OClosure = clo
fn.Body = body
return clo
// case OPTRLIT:
// unreachable - mapped to case OADDR below by exporter

View File

@ -93,11 +93,11 @@ func main() {
y := func(x int) int { // ERROR "can inline main.func11" "func literal does not escape"
return x + 2
}
y, sink = func() (func(int) int, int) { // ERROR "func literal does not escape"
return func(x int) int { // ERROR "can inline main.func12" "func literal escapes"
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func12"
return func(x int) int { // ERROR "can inline main.func12"
return x + 1
}, 42
}()
}() // ERROR "func literal does not escape" "inlining call to main.func12"
if y(40) != 41 {
ppanic("y(40) != 41")
}
@ -105,14 +105,14 @@ func main() {
{
func() { // ERROR "func literal does not escape"
y := func(x int) int { // ERROR "can inline main.func13.1" "func literal does not escape"
y := func(x int) int { // ERROR "func literal does not escape" "can inline main.func13.1"
return x + 2
}
y, sink = func() (func(int) int, int) { // ERROR "func literal does not escape"
return func(x int) int { // ERROR "can inline main.func13.2" "func literal escapes"
y, sink = func() (func(int) int, int) { // ERROR "can inline main.func13.2"
return func(x int) int { // ERROR "can inline main.func13.2"
return x + 1
}, 42
}()
}() // ERROR "inlining call to main.func13.2" "func literal does not escape"
if y(40) != 41 {
ppanic("y(40) != 41")
}
@ -187,29 +187,29 @@ func main() {
{
x := 42
if z := func(y int) int { // ERROR "func literal does not escape"
return func() int { // ERROR "can inline main.func22.1"
if z := func(y int) int { // ERROR "can inline main.func22"
return func() int { // ERROR "can inline main.func22.1" "can inline main.func30"
return x + y
}() // ERROR "inlining call to main.func22.1"
}(1); z != 43 {
}(1); z != 43 { // ERROR "inlining call to main.func22" "inlining call to main.func30"
ppanic("z != 43")
}
if z := func(y int) int { // ERROR "func literal does not escape"
return func() int { // ERROR "can inline main.func23.1"
if z := func(y int) int { // ERROR "func literal does not escape" "can inline main.func23"
return func() int { // ERROR "can inline main.func23.1" "can inline main.func31"
return x + y
}() // ERROR "inlining call to main.func23.1"
}; z(1) != 43 {
}; z(1) != 43 { // ERROR "inlining call to main.func23" "inlining call to main.func31"
ppanic("z(1) != 43")
}
}
{
a := 1
func() { // ERROR "func literal does not escape"
func() { // ERROR "can inline main.func24"
func() { // ERROR "can inline main.func24"
func() { // ERROR "can inline main.func24" "can inline main.func32"
a = 2
}() // ERROR "inlining call to main.func24"
}()
}() // ERROR "inlining call to main.func24" "inlining call to main.func32"
if a != 2 {
ppanic("a != 2")
}
@ -250,12 +250,12 @@ func main() {
a := 2
if r := func(x int) int { // ERROR "func literal does not escape"
b := 3
return func(y int) int { // ERROR "func literal does not escape"
return func(y int) int { // ERROR "can inline main.func27.1"
c := 5
return func(z int) int { // ERROR "can inline main.func27.1.1"
return func(z int) int { // ERROR "can inline main.func27.1.1" "can inline main.func27.2"
return a*x + b*y + c*z
}(10) // ERROR "inlining call to main.func27.1.1"
}(100)
}(100) // ERROR "inlining call to main.func27.1" "inlining call to main.func27.2"
}(1000); r != 2350 {
ppanic("r != 2350")
}
@ -265,15 +265,15 @@ func main() {
a := 2
if r := func(x int) int { // ERROR "func literal does not escape"
b := 3
return func(y int) int { // ERROR "func literal does not escape"
return func(y int) int { // ERROR "can inline main.func28.1"
c := 5
func(z int) { // ERROR "can inline main.func28.1.1"
func(z int) { // ERROR "can inline main.func28.1.1" "can inline main.func28.2"
a = a * x
b = b * y
c = c * z
}(10) // ERROR "inlining call to main.func28.1.1"
return a + c
}(100) + b
}(100) + b // ERROR "inlining call to main.func28.1" "inlining call to main.func28.2"
}(1000); r != 2350 {
ppanic("r != 2350")
}

11
test/closure5.dir/a.go Normal file
View File

@ -0,0 +1,11 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Check correctness of various closure corner cases
// that are expected to be inlined
package a
func f() bool { return true }
func G() func() func() bool { return func() func() bool { return f } }

15
test/closure5.dir/main.go Normal file
View File

@ -0,0 +1,15 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Check correctness of various closure corner cases
// that are expected to be inlined
package main
import "a"
func main() {
if !a.G()()() {
panic("FAIL")
}
}

10
test/closure5.go Normal file
View File

@ -0,0 +1,10 @@
// compiledir
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Check correctness of various closure corner cases
// that are expected to be inlined
package ignored

View File

@ -58,7 +58,7 @@ func _() int { // ERROR "can inline _"
var somethingWrong error
// local closures can be inlined
func l(x, y int) (int, int, error) {
func l(x, y int) (int, int, error) { // ERROR "can inline l"
e := func(err error) (int, int, error) { // ERROR "can inline l.func1" "func literal does not escape" "leaking param: err to result"
return 0, 0, err
}
@ -90,19 +90,19 @@ func n() int {
// make sure assignment inside closure is detected
func o() int {
foo := func() int { return 1 } // ERROR "can inline o.func1" "func literal does not escape"
func(x int) { // ERROR "func literal does not escape"
func(x int) { // ERROR "can inline o.func2"
if x > 10 {
foo = func() int { return 2 } // ERROR "can inline o.func2" "func literal escapes"
foo = func() int { return 2 } // ERROR "can inline o.func2"
}
}(11)
}(11) // ERROR "func literal does not escape" "inlining call to o.func2"
return foo()
}
func p() int {
func p() int { // ERROR "can inline p"
return func() int { return 42 }() // ERROR "can inline p.func1" "inlining call to p.func1"
}
func q(x int) int {
func q(x int) int { // ERROR "can inline q"
foo := func() int { return x * 2 } // ERROR "can inline q.func1" "func literal does not escape"
return foo() // ERROR "inlining call to q.func1"
}
@ -111,15 +111,15 @@ func r(z int) int {
foo := func(x int) int { // ERROR "can inline r.func1" "func literal does not escape"
return x + z
}
bar := func(x int) int { // ERROR "func literal does not escape"
return x + func(y int) int { // ERROR "can inline r.func2.1"
bar := func(x int) int { // ERROR "func literal does not escape" "can inline r.func2"
return x + func(y int) int { // ERROR "can inline r.func2.1" "can inline r.func3"
return 2*y + x*z
}(x) // ERROR "inlining call to r.func2.1"
}
return foo(42) + bar(42) // ERROR "inlining call to r.func1"
return foo(42) + bar(42) // ERROR "inlining call to r.func1" "inlining call to r.func2" "inlining call to r.func3"
}
func s0(x int) int {
func s0(x int) int { // ERROR "can inline s0"
foo := func() { // ERROR "can inline s0.func1" "func literal does not escape"
x = x + 1
}
@ -127,7 +127,7 @@ func s0(x int) int {
return x
}
func s1(x int) int {
func s1(x int) int { // ERROR "can inline s1"
foo := func() int { // ERROR "can inline s1.func1" "func literal does not escape"
return x
}