[dev.regabi] cmd/compile: bind closure vars during SSA constructions

For function literals that aren't inlined or directly called, we need
to pass their arguments via a closure struct. This also means we need
to rewrite uses of closure variables to access from this closure
struct.

Currently we do this rewrite in a pass before walking begins. This CL
moves the code to SSA construction instead, alongside binding other
input parameters.

Change-Id: I13538ef3394e2d6f75d5b7b2d0adbb00db812dc2
Reviewed-on: https://go-review.googlesource.com/c/go/+/281352
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Trust: Matthew Dempsky <mdempsky@google.com>
Reviewed-by: Keith Randall <khr@golang.org>
This commit is contained in:
Matthew Dempsky 2021-01-03 21:34:03 -08:00
parent 8b2efa990b
commit 950cf4d46c
2 changed files with 89 additions and 87 deletions

View file

@ -470,6 +470,47 @@ func buildssa(fn *ir.Func, worker int) *ssa.Func {
}
}
// Populate closure variables.
if !fn.ClosureCalled() {
clo := s.entryNewValue0(ssa.OpGetClosurePtr, s.f.Config.Types.BytePtr)
offset := int64(types.PtrSize) // PtrSize to skip past function entry PC field
for _, n := range fn.ClosureVars {
typ := n.Type()
if !n.Byval() {
typ = types.NewPtr(typ)
}
offset = types.Rnd(offset, typ.Alignment())
r := s.newValue1I(ssa.OpOffPtr, types.NewPtr(typ), offset, clo)
offset += typ.Size()
if n.Byval() && TypeOK(n.Type()) {
// If it is a small variable captured by value, downgrade it to PAUTO.
r = s.load(n.Type(), r)
n.Class = ir.PAUTO
} else {
if !n.Byval() {
r = s.load(typ, r)
}
// Declare variable holding address taken from closure.
addr := ir.NewNameAt(fn.Pos(), &types.Sym{Name: "&" + n.Sym().Name, Pkg: types.LocalPkg})
addr.SetType(types.NewPtr(n.Type()))
addr.Class = ir.PAUTO
addr.SetUsed(true)
addr.Curfn = fn
types.CalcSize(addr.Type())
n.Heapaddr = addr
n = addr
}
fn.Dcl = append(fn.Dcl, n)
s.assign(n, r, false, 0)
}
}
// Convert the AST-based IR to the SSA-based IR
s.stmtList(fn.Enter)
s.stmtList(fn.Body)

View file

@ -15,103 +15,64 @@ import (
// Closure is called in a separate phase after escape analysis.
// It transform closure bodies to properly reference captured variables.
func Closure(fn *ir.Func) {
if len(fn.ClosureVars) == 0 {
return
}
if !fn.ClosureCalled() {
// The closure is not directly called, so it is going to stay as closure.
fn.SetNeedctxt(true)
return
}
lno := base.Pos
base.Pos = fn.Pos()
if fn.ClosureCalled() {
// If the closure is directly called, we transform it to a plain function call
// with variables passed as args. This avoids allocation of a closure object.
// Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
// will complete the transformation later.
// For illustration, the following closure:
// func(a int) {
// println(byval)
// byref++
// }(42)
// becomes:
// func(byval int, &byref *int, a int) {
// println(byval)
// (*&byref)++
// }(byval, &byref, 42)
// If the closure is directly called, we transform it to a plain function call
// with variables passed as args. This avoids allocation of a closure object.
// Here we do only a part of the transformation. Walk of OCALLFUNC(OCLOSURE)
// will complete the transformation later.
// For illustration, the following closure:
// func(a int) {
// println(byval)
// byref++
// }(42)
// becomes:
// func(byval int, &byref *int, a int) {
// println(byval)
// (*&byref)++
// }(byval, &byref, 42)
// f is ONAME of the actual function.
f := fn.Nname
// f is ONAME of the actual function.
f := fn.Nname
// We are going to insert captured variables before input args.
var params []*types.Field
var decls []*ir.Name
for _, v := range fn.ClosureVars {
if !v.Byval() {
// If v of type T is captured by reference,
// we introduce function param &v *T
// and v remains PAUTOHEAP with &v heapaddr
// (accesses will implicitly deref &v).
addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
addr.SetType(types.NewPtr(v.Type()))
v.Heapaddr = addr
v = addr
}
v.Class = ir.PPARAM
decls = append(decls, v)
fld := types.NewField(src.NoXPos, v.Sym(), v.Type())
fld.Nname = v
params = append(params, fld)
// We are going to insert captured variables before input args.
var params []*types.Field
var decls []*ir.Name
for _, v := range fn.ClosureVars {
if !v.Byval() {
// If v of type T is captured by reference,
// we introduce function param &v *T
// and v remains PAUTOHEAP with &v heapaddr
// (accesses will implicitly deref &v).
addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
addr.SetType(types.NewPtr(v.Type()))
v.Heapaddr = addr
v = addr
}
if len(params) > 0 {
// Prepend params and decls.
f.Type().Params().SetFields(append(params, f.Type().Params().FieldSlice()...))
fn.Dcl = append(decls, fn.Dcl...)
}
v.Class = ir.PPARAM
decls = append(decls, v)
types.CalcSize(f.Type())
fn.Nname.SetType(f.Type()) // update type of ODCLFUNC
} else {
// The closure is not called, so it is going to stay as closure.
var body []ir.Node
offset := int64(types.PtrSize)
for _, v := range fn.ClosureVars {
// cv refers to the field inside of closure OSTRUCTLIT.
typ := v.Type()
if !v.Byval() {
typ = types.NewPtr(typ)
}
offset = types.Rnd(offset, int64(typ.Align))
cr := ir.NewClosureRead(typ, offset)
offset += typ.Width
if v.Byval() && v.Type().Width <= int64(2*types.PtrSize) {
// If it is a small variable captured by value, downgrade it to PAUTO.
v.Class = ir.PAUTO
fn.Dcl = append(fn.Dcl, v)
body = append(body, ir.NewAssignStmt(base.Pos, v, cr))
} else {
// Declare variable holding addresses taken from closure
// and initialize in entry prologue.
addr := typecheck.NewName(typecheck.Lookup("&" + v.Sym().Name))
addr.SetType(types.NewPtr(v.Type()))
addr.Class = ir.PAUTO
addr.SetUsed(true)
addr.Curfn = fn
fn.Dcl = append(fn.Dcl, addr)
v.Heapaddr = addr
var src ir.Node = cr
if v.Byval() {
src = typecheck.NodAddr(cr)
}
body = append(body, ir.NewAssignStmt(base.Pos, addr, src))
}
}
if len(body) > 0 {
typecheck.Stmts(body)
fn.Enter = body
fn.SetNeedctxt(true)
}
fld := types.NewField(src.NoXPos, v.Sym(), v.Type())
fld.Nname = v
params = append(params, fld)
}
// Prepend params and decls.
f.Type().Params().SetFields(append(params, f.Type().Params().FieldSlice()...))
fn.Dcl = append(decls, fn.Dcl...)
base.Pos = lno
}