Newer
Older
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
as2 := ir.NewAssignListStmt(call.Pos(), ir.OAS2, r.inlvars, args)
as2.Def = true
var as2init ir.Nodes
for _, name := range r.inlvars {
if ir.IsBlank(name) {
continue
}
// TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
as2init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
name.Defn = as2
}
as2.SetInit(as2init)
init.Append(typecheck.Stmt(as2))
if !r.delayResults {
// If not delaying retvars, declare and zero initialize the
// result variables now.
for _, name := range r.retvars {
// TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
init.Append(ir.NewDecl(call.Pos(), ir.ODCL, name))
ras := ir.NewAssignStmt(call.Pos(), name, nil)
init.Append(typecheck.Stmt(ras))
}
}
// Add an inline mark just before the inlined body.
// This mark is inline in the code so that it's a reasonable spot
// to put a breakpoint. Not sure if that's really necessary or not
// (in which case it could go at the end of the function instead).
// Note issue 28603.
init.Append(ir.NewInlineMarkStmt(call.Pos().WithIsStmt(), int64(r.inlTreeIndex)))
nparams := len(r.curfn.Dcl)
ir.WithFunc(r.curfn, func() {
r.curfn.Body = r.stmts()
r.curfn.Endlineno = r.pos()
// TODO(mdempsky): This shouldn't be necessary. Inlining might
// read in new function/method declarations, which could
// potentially be recursively inlined themselves; but we shouldn't
// need to read in the non-inlined bodies for the declarations
// themselves. But currently it's an easy fix to #50552.
readBodies(typecheck.Target)
deadcode.Func(r.curfn)
// Replace any "return" statements within the function body.
var edit func(ir.Node) ir.Node
edit = func(n ir.Node) ir.Node {
if ret, ok := n.(*ir.ReturnStmt); ok {
n = typecheck.Stmt(r.inlReturn(ret))
}
ir.EditChildren(n, edit)
return n
}
edit(r.curfn)
})
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088
2089
2090
2091
2092
2093
2094
2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127
2128
2129
2130
2131
2132
body := ir.Nodes(r.curfn.Body)
// Quirkish: We need to eagerly prune variables added during
// inlining, but removed by deadcode.FuncBody above. Unused
// variables will get removed during stack frame layout anyway, but
// len(fn.Dcl) ends up influencing things like autotmp naming.
used := usedLocals(body)
for i, name := range r.curfn.Dcl {
if i < nparams || used.Has(name) {
name.Curfn = callerfn
callerfn.Dcl = append(callerfn.Dcl, name)
// Quirkish. TODO(mdempsky): Document why.
if name.AutoTemp() {
name.SetEsc(ir.EscUnknown)
if base.Flag.GenDwarfInl != 0 {
name.SetInlLocal(true)
} else {
name.SetPos(r.inlCall.Pos())
}
}
}
}
body.Append(ir.NewLabelStmt(call.Pos(), r.retlabel))
res := ir.NewInlinedCallExpr(call.Pos(), body, append([]ir.Node(nil), r.retvars...))
res.SetInit(init)
res.SetType(call.Type())
res.SetTypecheck(1)
// Inlining shouldn't add any functions to todoBodies.
assert(len(todoBodies) == 0)
return res
}
// inlReturn returns a statement that can substitute for the given
// return statement when inlining.
func (r *reader) inlReturn(ret *ir.ReturnStmt) *ir.BlockStmt {
pos := r.inlCall.Pos()
block := ir.TakeInit(ret)
if results := ret.Results; len(results) != 0 {
assert(len(r.retvars) == len(results))
as2 := ir.NewAssignListStmt(pos, ir.OAS2, append([]ir.Node(nil), r.retvars...), ret.Results)
if r.delayResults {
for _, name := range r.retvars {
// TODO(mdempsky): Use inlined position of name.Pos() instead?
name := name.(*ir.Name)
block.Append(ir.NewDecl(pos, ir.ODCL, name))
name.Defn = as2
}
}
block.Append(as2)
}
block.Append(ir.NewBranchStmt(pos, ir.OGOTO, r.retlabel))
return ir.NewBlockStmt(pos, block)
}
// expandInline reads in an extra copy of IR to populate
// fn.Inl.{Dcl,Body}.
func expandInline(fn *ir.Func, pri pkgReaderIndex) {
// TODO(mdempsky): Remove this function. It's currently needed by
// dwarfgen/dwarf.go:preInliningDcls, which requires fn.Inl.Dcl to
// create abstract function DIEs. But we should be able to provide it
// with the same information some other way.
fndcls := len(fn.Dcl)
topdcls := len(typecheck.Target.Decls)
tmpfn := ir.NewFunc(fn.Pos())
tmpfn.Nname = ir.NewNameAt(fn.Nname.Pos(), fn.Sym())
tmpfn.ClosureVars = fn.ClosureVars
{
r := pri.asReader(pkgbits.RelocBody, pkgbits.SyncFuncBody)
setType(tmpfn.Nname, fn.Type())
// Don't change parameter's Sym/Nname fields.
r.funarghack = true
r.funcBody(tmpfn)
ir.WithFunc(tmpfn, func() {
deadcode.Func(tmpfn)
})
}
2158
2159
2160
2161
2162
2163
2164
2165
2166
2167
2168
2169
2170
2171
2172
2173
2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
used := usedLocals(tmpfn.Body)
for _, name := range tmpfn.Dcl {
if name.Class != ir.PAUTO || used.Has(name) {
name.Curfn = fn
fn.Inl.Dcl = append(fn.Inl.Dcl, name)
}
}
fn.Inl.Body = tmpfn.Body
// Double check that we didn't change fn.Dcl by accident.
assert(fndcls == len(fn.Dcl))
// typecheck.Stmts may have added function literals to
// typecheck.Target.Decls. Remove them again so we don't risk trying
// to compile them multiple times.
typecheck.Target.Decls = typecheck.Target.Decls[:topdcls]
}
// usedLocals returns a set of local variables that are used within body.
func usedLocals(body []ir.Node) ir.NameSet {
var used ir.NameSet
ir.VisitList(body, func(n ir.Node) {
if n, ok := n.(*ir.Name); ok && n.Op() == ir.ONAME && n.Class == ir.PAUTO {
used.Add(n)
}
})
return used
}
// @@@ Method wrappers
// needWrapperTypes lists types for which we may need to generate
// method wrappers.
var needWrapperTypes []*types.Type
Matthew Dempsky
committed
// haveWrapperTypes lists types for which we know we already have
// method wrappers, because we found the type in an imported package.
var haveWrapperTypes []*types.Type
// needMethodValueWrappers lists methods for which we may need to
// generate method value wrappers.
var needMethodValueWrappers []methodValueWrapper
// haveMethodValueWrappers lists methods for which we know we already
// have method value wrappers, because we found it in an imported
// package.
var haveMethodValueWrappers []methodValueWrapper
type methodValueWrapper struct {
rcvr *types.Type
method *types.Field
}
func (r *reader) needWrapper(typ *types.Type) {
if typ.IsPtr() {
return
}
// If a type was found in an imported package, then we can assume
// that package (or one of its transitive dependencies) already
// generated method wrappers for it.
if r.importedDef() {
haveWrapperTypes = append(haveWrapperTypes, typ)
} else {
needWrapperTypes = append(needWrapperTypes, typ)
func (r *reader) importedDef() bool {
Matthew Dempsky
committed
// If a type was found in an imported package, then we can assume
// that package (or one of its transitive dependencies) already
// generated method wrappers for it.
//
// Exception: If we're instantiating an imported generic type or
// function, we might be instantiating it with type arguments not
// previously seen before.
//
// TODO(mdempsky): Distinguish when a generic function or type was
// instantiated in an imported package so that we can add types to
// haveWrapperTypes instead.
return r.p != localPkgReader && !r.hasTypeParams()
}
func MakeWrappers(target *ir.Package) {
// Only unified IR emits its own wrappers.
if base.Debug.Unified == 0 {
return
}
// always generate a wrapper for error.Error (#29304)
needWrapperTypes = append(needWrapperTypes, types.ErrorType)
seen := make(map[string]*types.Type)
Matthew Dempsky
committed
for _, typ := range haveWrapperTypes {
wrapType(typ, target, seen, false)
Matthew Dempsky
committed
}
haveWrapperTypes = nil
for _, typ := range needWrapperTypes {
wrapType(typ, target, seen, true)
Matthew Dempsky
committed
}
needWrapperTypes = nil
for _, wrapper := range haveMethodValueWrappers {
wrapMethodValue(wrapper.rcvr, wrapper.method, target, false)
}
haveMethodValueWrappers = nil
for _, wrapper := range needMethodValueWrappers {
wrapMethodValue(wrapper.rcvr, wrapper.method, target, true)
}
needMethodValueWrappers = nil
}
func wrapType(typ *types.Type, target *ir.Package, seen map[string]*types.Type, needed bool) {
key := typ.LinkString()
if prev := seen[key]; prev != nil {
if !types.Identical(typ, prev) {
base.Fatalf("collision: types %v and %v have link string %q", typ, prev, key)
}
return
}
seen[key] = typ
if !needed {
// Only called to add to 'seen'.
return
}
if !typ.IsInterface() {
typecheck.CalcMethods(typ)
}
for _, meth := range typ.AllMethods().Slice() {
if meth.Sym.IsBlank() || !meth.IsMethod() {
base.FatalfAt(meth.Pos, "invalid method: %v", meth)
}
methodWrapper(0, typ, meth, target)
// For non-interface types, we also want *T wrappers.
if !typ.IsInterface() {
methodWrapper(1, typ, meth, target)
// For not-in-heap types, *T is a scalar, not pointer shaped,
// so the interface wrappers use **T.
if typ.NotInHeap() {
methodWrapper(2, typ, meth, target)
}
}
}
}
func methodWrapper(derefs int, tbase *types.Type, method *types.Field, target *ir.Package) {
wrapper := tbase
for i := 0; i < derefs; i++ {
wrapper = types.NewPtr(wrapper)
}
sym := ir.MethodSym(wrapper, method.Sym)
base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym)
sym.SetSiggen(true)
wrappee := method.Type.Recv().Type
if types.Identical(wrapper, wrappee) ||
!types.IsMethodApplicable(wrapper, method) ||
!reflectdata.NeedEmit(tbase) {
return
}
// TODO(mdempsky): Use method.Pos instead?
pos := base.AutogeneratedPos
fn := newWrapperFunc(pos, sym, wrapper, method)
var recv ir.Node = fn.Nname.Type().Recv().Nname.(*ir.Name)
// For simple *T wrappers around T methods, panicwrap produces a
// nicer panic message.
if wrapper.IsPtr() && types.Identical(wrapper.Elem(), wrappee) {
cond := ir.NewBinaryExpr(pos, ir.OEQ, recv, types.BuiltinPkg.Lookup("nil").Def.(ir.Node))
then := []ir.Node{ir.NewCallExpr(pos, ir.OCALL, typecheck.LookupRuntime("panicwrap"), nil)}
fn.Body.Append(ir.NewIfStmt(pos, cond, then, nil))
}
// typecheck will add one implicit deref, if necessary,
// but not-in-heap types require more for their **T wrappers.
for i := 1; i < derefs; i++ {
recv = Implicit(ir.NewStarExpr(pos, recv))
}
addTailCall(pos, fn, recv, method)
Matthew Dempsky
committed
finishWrapperFunc(fn, target)
}
func wrapMethodValue(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) {
sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
if sym.Uniq() {
return
}
sym.SetUniq(true)
// TODO(mdempsky): Use method.Pos instead?
pos := base.AutogeneratedPos
fn := newWrapperFunc(pos, sym, nil, method)
Matthew Dempsky
committed
sym.Def = fn.Nname
// Declare and initialize variable holding receiver.
recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
if !needed {
Matthew Dempsky
committed
typecheck.Func(fn)
return
}
addTailCall(pos, fn, recv, method)
Matthew Dempsky
committed
finishWrapperFunc(fn, target)
}
func newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field) *ir.Func {
fn := ir.NewFunc(pos)
fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers?
name := ir.NewNameAt(pos, sym)
ir.MarkFunc(name)
name.Func = fn
name.Defn = fn
fn.Nname = name
sig := newWrapperType(wrapper, method)
setType(name, sig)
// TODO(mdempsky): De-duplicate with similar logic in funcargs.
defParams := func(class ir.Class, params *types.Type) {
for _, param := range params.FieldSlice() {
name := ir.NewNameAt(param.Pos, param.Sym)
name.Class = class
setType(name, param.Type)
name.Curfn = fn
fn.Dcl = append(fn.Dcl, name)
param.Nname = name
}
}
defParams(ir.PPARAM, sig.Recvs())
defParams(ir.PPARAM, sig.Params())
defParams(ir.PPARAMOUT, sig.Results())
return fn
}
func finishWrapperFunc(fn *ir.Func, target *ir.Package) {
Matthew Dempsky
committed
typecheck.Func(fn)
ir.WithFunc(fn, func() {
typecheck.Stmts(fn.Body)
})
// We generate wrappers after the global inlining pass,
// so we're responsible for applying inlining ourselves here.
inline.InlineCalls(fn)
Matthew Dempsky
committed
target.Decls = append(target.Decls, fn)
}
// newWrapperType returns a copy of the given signature type, but with
// the receiver parameter type substituted with recvType.
// If recvType is nil, newWrapperType returns a signature
// without a receiver parameter.
func newWrapperType(recvType *types.Type, method *types.Field) *types.Type {
clone := func(params []*types.Field) []*types.Field {
res := make([]*types.Field, len(params))
for i, param := range params {
sym := param.Sym
if sym == nil || sym.Name == "_" {
sym = typecheck.LookupNum(".anon", i)
}
res[i] = types.NewField(param.Pos, sym, param.Type)
res[i].SetIsDDD(param.IsDDD())
}
return res
}
sig := method.Type
var recv *types.Field
if recvType != nil {
recv = types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), recvType)
}
params := clone(sig.Params().FieldSlice())
results := clone(sig.Results().FieldSlice())
return types.NewSignature(types.NoPkg, recv, nil, params, results)
}
func addTailCall(pos src.XPos, fn *ir.Func, recv ir.Node, method *types.Field) {
sig := fn.Nname.Type()
args := make([]ir.Node, sig.NumParams())
for i, param := range sig.Params().FieldSlice() {
args[i] = param.Nname.(*ir.Name)
}
// TODO(mdempsky): Support creating OTAILCALL, when possible. See reflectdata.methodWrapper.
// Not urgent though, because tail calls are currently incompatible with regabi anyway.
fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls?
dot := ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym)
call := typecheck.Call(pos, dot, args, method.Type.IsVariadic()).(*ir.CallExpr)
if method.Type.NumResults() == 0 {
fn.Body.Append(call)
return
}
ret := ir.NewReturnStmt(pos, nil)
ret.Results = []ir.Node{call}
fn.Body.Append(ret)