aboutsummaryrefslogtreecommitdiff
path: root/src/cmd/compile/internal/gc/closure.go
diff options
context:
space:
mode:
Diffstat (limited to 'src/cmd/compile/internal/gc/closure.go')
-rw-r--r--src/cmd/compile/internal/gc/closure.go310
1 files changed, 12 insertions, 298 deletions
diff --git a/src/cmd/compile/internal/gc/closure.go b/src/cmd/compile/internal/gc/closure.go
index 454d97e17f..29455bffd8 100644
--- a/src/cmd/compile/internal/gc/closure.go
+++ b/src/cmd/compile/internal/gc/closure.go
@@ -8,9 +8,9 @@ import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
"cmd/compile/internal/syntax"
+ "cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"cmd/internal/src"
- "fmt"
)
func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
@@ -72,156 +72,6 @@ func (p *noder) funcLit(expr *syntax.FuncLit) ir.Node {
return clo
}
-// typecheckclosure typechecks an OCLOSURE node. It also creates the named
-// function associated with the closure.
-// TODO: This creation of the named function should probably really be done in a
-// separate pass from type-checking.
-func typecheckclosure(clo *ir.ClosureExpr, top int) {
- fn := clo.Func
- // Set current associated iota value, so iota can be used inside
- // function in ConstSpec, see issue #22344
- if x := getIotaValue(); x >= 0 {
- fn.Iota = x
- }
-
- fn.ClosureType = typecheck(fn.ClosureType, ctxType)
- clo.SetType(fn.ClosureType.Type())
- fn.SetClosureCalled(top&ctxCallee != 0)
-
- // Do not typecheck fn twice, otherwise, we will end up pushing
- // fn to Target.Decls multiple times, causing initLSym called twice.
- // See #30709
- if fn.Typecheck() == 1 {
- return
- }
-
- for _, ln := range fn.ClosureVars {
- n := ln.Defn
- if !n.Name().Captured() {
- n.Name().SetCaptured(true)
- if n.Name().Decldepth == 0 {
- base.Fatalf("typecheckclosure: var %v does not have decldepth assigned", n)
- }
-
- // Ignore assignments to the variable in straightline code
- // preceding the first capturing by a closure.
- if n.Name().Decldepth == decldepth {
- n.Name().SetAssigned(false)
- }
- }
- }
-
- fn.Nname.SetSym(closurename(ir.CurFunc))
- ir.MarkFunc(fn.Nname)
- typecheckFunc(fn)
-
- // Type check the body now, but only if we're inside a function.
- // At top level (in a variable initialization: curfn==nil) we're not
- // ready to type check code yet; we'll check it later, because the
- // underlying closure function we create is added to Target.Decls.
- if ir.CurFunc != nil && clo.Type() != nil {
- oldfn := ir.CurFunc
- ir.CurFunc = fn
- olddd := decldepth
- decldepth = 1
- typecheckslice(fn.Body, ctxStmt)
- decldepth = olddd
- ir.CurFunc = oldfn
- }
-
- Target.Decls = append(Target.Decls, fn)
-}
-
-// globClosgen is like Func.Closgen, but for the global scope.
-var globClosgen int32
-
-// closurename generates a new unique name for a closure within
-// outerfunc.
-func closurename(outerfunc *ir.Func) *types.Sym {
- outer := "glob."
- prefix := "func"
- gen := &globClosgen
-
- if outerfunc != nil {
- if outerfunc.OClosure != nil {
- prefix = ""
- }
-
- outer = ir.FuncName(outerfunc)
-
- // There may be multiple functions named "_". In those
- // cases, we can't use their individual Closgens as it
- // would lead to name clashes.
- if !ir.IsBlank(outerfunc.Nname) {
- gen = &outerfunc.Closgen
- }
- }
-
- *gen++
- return lookup(fmt.Sprintf("%s.%s%d", outer, prefix, *gen))
-}
-
-// capturevarscomplete is set to true when the capturevars phase is done.
-var capturevarscomplete bool
-
-// capturevars is called in a separate phase after all typechecking is done.
-// It decides whether each variable captured by a closure should be captured
-// by value or by reference.
-// We use value capturing for values <= 128 bytes that are never reassigned
-// after capturing (effectively constant).
-func capturevars(fn *ir.Func) {
- lno := base.Pos
- base.Pos = fn.Pos()
- cvars := fn.ClosureVars
- out := cvars[:0]
- for _, v := range cvars {
- if v.Type() == nil {
- // If v.Type is nil, it means v looked like it
- // was going to be used in the closure, but
- // isn't. This happens in struct literals like
- // s{f: x} where we can't distinguish whether
- // f is a field identifier or expression until
- // resolving s.
- continue
- }
- out = append(out, v)
-
- // type check the & of closed variables outside the closure,
- // so that the outer frame also grabs them and knows they escape.
- types.CalcSize(v.Type())
-
- var outer ir.Node
- outer = v.Outer
- outermost := v.Defn.(*ir.Name)
-
- // out parameters will be assigned to implicitly upon return.
- if outermost.Class_ != ir.PPARAMOUT && !outermost.Name().Addrtaken() && !outermost.Name().Assigned() && v.Type().Width <= 128 {
- v.SetByval(true)
- } else {
- outermost.Name().SetAddrtaken(true)
- outer = nodAddr(outer)
- }
-
- if base.Flag.LowerM > 1 {
- var name *types.Sym
- if v.Curfn != nil && v.Curfn.Nname != nil {
- name = v.Curfn.Sym()
- }
- how := "ref"
- if v.Byval() {
- how = "value"
- }
- base.WarnfAt(v.Pos(), "%v capturing by %s: %v (addr=%v assign=%v width=%d)", name, how, v.Sym(), outermost.Name().Addrtaken(), outermost.Name().Assigned(), int32(v.Type().Width))
- }
-
- outer = typecheck(outer, ctxExpr)
- fn.ClosureEnter.Append(outer)
- }
-
- fn.ClosureVars = out
- base.Pos = lno
-}
-
// transformclosure is called in a separate phase after escape analysis.
// It transform closure bodies to properly reference captured variables.
func transformclosure(fn *ir.Func) {
@@ -256,7 +106,7 @@ func transformclosure(fn *ir.Func) {
// we introduce function param &v *T
// and v remains PAUTOHEAP with &v heapaddr
// (accesses will implicitly deref &v).
- addr := NewName(lookup("&" + v.Sym().Name))
+ addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
addr.SetType(types.NewPtr(v.Type()))
v.Heapaddr = addr
v = addr
@@ -300,7 +150,7 @@ func transformclosure(fn *ir.Func) {
} else {
// Declare variable holding addresses taken from closure
// and initialize in entry prologue.
- addr := NewName(lookup("&" + v.Sym().Name))
+ addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
addr.SetType(types.NewPtr(v.Type()))
addr.Class_ = ir.PAUTO
addr.SetUsed(true)
@@ -309,14 +159,14 @@ func transformclosure(fn *ir.Func) {
v.Heapaddr = addr
var src ir.Node = cr
if v.Byval() {
- src = nodAddr(cr)
+ src = typecheck.NodAddr(cr)
}
body = append(body, ir.NewAssignStmt(base.Pos, addr, src))
}
}
if len(body) > 0 {
- typecheckslice(body, ctxStmt)
+ typecheck.Stmts(body)
fn.Enter.Set(body)
fn.SetNeedctxt(true)
}
@@ -346,38 +196,6 @@ func closuredebugruntimecheck(clo *ir.ClosureExpr) {
}
}
-// closureType returns the struct type used to hold all the information
-// needed in the closure for clo (clo must be a OCLOSURE node).
-// The address of a variable of the returned type can be cast to a func.
-func closureType(clo *ir.ClosureExpr) *types.Type {
- // Create closure in the form of a composite literal.
- // supposing the closure captures an int i and a string s
- // and has one float64 argument and no results,
- // the generated code looks like:
- //
- // clos = &struct{.F uintptr; i *int; s *string}{func.1, &i, &s}
- //
- // The use of the struct provides type information to the garbage
- // collector so that it can walk the closure. We could use (in this case)
- // [3]unsafe.Pointer instead, but that would leave the gc in the dark.
- // The information appears in the binary in the form of type descriptors;
- // the struct is unnamed so that closures in multiple packages with the
- // same struct type can share the descriptor.
- fields := []*ir.Field{
- ir.NewField(base.Pos, lookup(".F"), nil, types.Types[types.TUINTPTR]),
- }
- for _, v := range clo.Func.ClosureVars {
- typ := v.Type()
- if !v.Byval() {
- typ = types.NewPtr(typ)
- }
- fields = append(fields, ir.NewField(base.Pos, v.Sym(), nil, typ))
- }
- typ := tostruct(fields)
- typ.SetNoalg(true)
- return typ
-}
-
func walkclosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
fn := clo.Func
@@ -390,17 +208,17 @@ func walkclosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
}
closuredebugruntimecheck(clo)
- typ := closureType(clo)
+ typ := typecheck.ClosureType(clo)
clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ).(ir.Ntype), nil)
clos.SetEsc(clo.Esc())
clos.List.Set(append([]ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, fn.Nname)}, fn.ClosureEnter...))
- addr := nodAddr(clos)
+ addr := typecheck.NodAddr(clos)
addr.SetEsc(clo.Esc())
// Force type conversion from *struct to the func type.
- cfn := convnop(addr, clo.Type())
+ cfn := typecheck.ConvNop(addr, clo.Type())
// non-escaping temp to use, if any.
if x := clo.Prealloc; x != nil {
@@ -414,110 +232,6 @@ func walkclosure(clo *ir.ClosureExpr, init *ir.Nodes) ir.Node {
return walkexpr(cfn, init)
}
-func typecheckpartialcall(n ir.Node, sym *types.Sym) *ir.CallPartExpr {
- switch n.Op() {
- case ir.ODOTINTER, ir.ODOTMETH:
- break
-
- default:
- base.Fatalf("invalid typecheckpartialcall")
- }
- dot := n.(*ir.SelectorExpr)
-
- // Create top-level function.
- fn := makepartialcall(dot, dot.Type(), sym)
- fn.SetWrapper(true)
-
- return ir.NewCallPartExpr(dot.Pos(), dot.X, dot.Selection, fn)
-}
-
-// makepartialcall returns a DCLFUNC node representing the wrapper function (*-fm) needed
-// for partial calls.
-func makepartialcall(dot *ir.SelectorExpr, t0 *types.Type, meth *types.Sym) *ir.Func {
- rcvrtype := dot.X.Type()
- sym := ir.MethodSymSuffix(rcvrtype, meth, "-fm")
-
- if sym.Uniq() {
- return sym.Def.(*ir.Func)
- }
- sym.SetUniq(true)
-
- savecurfn := ir.CurFunc
- saveLineNo := base.Pos
- ir.CurFunc = nil
-
- // Set line number equal to the line number where the method is declared.
- var m *types.Field
- if lookdot0(meth, rcvrtype, &m, false) == 1 && m.Pos.IsKnown() {
- base.Pos = m.Pos
- }
- // Note: !m.Pos.IsKnown() happens for method expressions where
- // the method is implicitly declared. The Error method of the
- // built-in error type is one such method. We leave the line
- // number at the use of the method expression in this
- // case. See issue 29389.
-
- tfn := ir.NewFuncType(base.Pos, nil,
- structargs(t0.Params(), true),
- structargs(t0.Results(), false))
-
- fn := dclfunc(sym, tfn)
- fn.SetDupok(true)
- fn.SetNeedctxt(true)
-
- // Declare and initialize variable holding receiver.
- cr := ir.NewClosureRead(rcvrtype, types.Rnd(int64(types.PtrSize), int64(rcvrtype.Align)))
- ptr := NewName(lookup(".this"))
- declare(ptr, ir.PAUTO)
- ptr.SetUsed(true)
- var body []ir.Node
- if rcvrtype.IsPtr() || rcvrtype.IsInterface() {
- ptr.SetType(rcvrtype)
- body = append(body, ir.NewAssignStmt(base.Pos, ptr, cr))
- } else {
- ptr.SetType(types.NewPtr(rcvrtype))
- body = append(body, ir.NewAssignStmt(base.Pos, ptr, nodAddr(cr)))
- }
-
- call := ir.NewCallExpr(base.Pos, ir.OCALL, ir.NewSelectorExpr(base.Pos, ir.OXDOT, ptr, meth), nil)
- call.Args.Set(ir.ParamNames(tfn.Type()))
- call.IsDDD = tfn.Type().IsVariadic()
- if t0.NumResults() != 0 {
- ret := ir.NewReturnStmt(base.Pos, nil)
- ret.Results = []ir.Node{call}
- body = append(body, ret)
- } else {
- body = append(body, call)
- }
-
- fn.Body.Set(body)
- funcbody()
-
- typecheckFunc(fn)
- // Need to typecheck the body of the just-generated wrapper.
- // typecheckslice() requires that Curfn is set when processing an ORETURN.
- ir.CurFunc = fn
- typecheckslice(fn.Body, ctxStmt)
- sym.Def = fn
- Target.Decls = append(Target.Decls, fn)
- ir.CurFunc = savecurfn
- base.Pos = saveLineNo
-
- return fn
-}
-
-// partialCallType returns the struct type used to hold all the information
-// needed in the closure for n (n must be a OCALLPART node).
-// The address of a variable of the returned type can be cast to a func.
-func partialCallType(n *ir.CallPartExpr) *types.Type {
- t := tostruct([]*ir.Field{
- ir.NewField(base.Pos, lookup("F"), nil, types.Types[types.TUINTPTR]),
- ir.NewField(base.Pos, lookup("R"), nil, n.X.Type()),
- })
- t.SetNoalg(true)
- return t
-}
-
func walkpartialcall(n *ir.CallPartExpr, init *ir.Nodes) ir.Node {
// Create closure in the form of a composite literal.
// For x.M with receiver (x) type T, the generated code looks like:
@@ -532,24 +246,24 @@ func walkpartialcall(n *ir.CallPartExpr, init *ir.Nodes) ir.Node {
n.X = cheapexpr(n.X, init)
n.X = walkexpr(n.X, nil)
- tab := typecheck(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X), ctxExpr)
+ tab := typecheck.Expr(ir.NewUnaryExpr(base.Pos, ir.OITAB, n.X))
c := ir.NewUnaryExpr(base.Pos, ir.OCHECKNIL, tab)
c.SetTypecheck(1)
init.Append(c)
}
- typ := partialCallType(n)
+ typ := typecheck.PartialCallType(n)
clos := ir.NewCompLitExpr(base.Pos, ir.OCOMPLIT, ir.TypeNode(typ).(ir.Ntype), nil)
clos.SetEsc(n.Esc())
clos.List = []ir.Node{ir.NewUnaryExpr(base.Pos, ir.OCFUNC, n.Func.Nname), n.X}
- addr := nodAddr(clos)
+ addr := typecheck.NodAddr(clos)
addr.SetEsc(n.Esc())
// Force type conversion from *struct to the func type.
- cfn := convnop(addr, n.Type())
+ cfn := typecheck.ConvNop(addr, n.Type())
// non-escaping temp to use, if any.
if x := n.Prealloc; x != nil {