1
0
mirror of https://github.com/golang/go synced 2024-11-22 20:50:05 -07:00

cmd/compile: make unified IR more selective about method wrappers

This CL makes two changes to how unified IR emits method wrappers:

1. It no longer emits wrappers for defined types' underlying
types. Previously, a declaration like `type T struct { U }` would emit
wrappers for both `T` and `struct { U }`. Now they're only emitted for
`T`.

2. It emits method value wrappers only when OMETHVALUE nodes are
actually created, like how -G=0 works. Method values are relatively
rare, aren't needed for runtime type descriptors (unlike method
expression wrappers), and large projects end up spending a non-trivial
amount of time compiling these unneeded wrappers.

Change-Id: I21da97df3132ec12cc67debf62b5b2d282f481cf
Reviewed-on: https://go-review.googlesource.com/c/go/+/346230
Trust: Matthew Dempsky <mdempsky@google.com>
Run-TryBot: Matthew Dempsky <mdempsky@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com>
This commit is contained in:
Matthew Dempsky 2021-08-30 01:01:20 -07:00
parent d384ebde60
commit f118d145a5
2 changed files with 98 additions and 55 deletions

View File

@ -295,7 +295,13 @@ func (r *reader) doPkg() *types.Pkg {
// @@@ Types // @@@ Types
func (r *reader) typ() *types.Type { func (r *reader) typ() *types.Type {
return r.p.typIdx(r.typInfo(), r.dict) return r.typWrapped(true)
}
// typWrapped is like typ, but allows suppressing generation of
// unnecessary wrappers as a compile-time optimization.
func (r *reader) typWrapped(wrapped bool) *types.Type {
return r.p.typIdx(r.typInfo(), r.dict, wrapped)
} }
func (r *reader) typInfo() typeInfo { func (r *reader) typInfo() typeInfo {
@ -306,7 +312,7 @@ func (r *reader) typInfo() typeInfo {
return typeInfo{idx: r.reloc(relocType), derived: false} return typeInfo{idx: r.reloc(relocType), derived: false}
} }
func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) *types.Type { func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict, wrapped bool) *types.Type {
idx := info.idx idx := info.idx
var where **types.Type var where **types.Type
if info.derived { if info.derived {
@ -370,7 +376,13 @@ func (pr *pkgReader) typIdx(info typeInfo, dict *readerDict) *types.Type {
return prev return prev
} }
*where = typ if wrapped {
// Only cache if we're adding wrappers, so that other callers that
// find a cached type know it was wrapped.
*where = typ
r.needWrapper(typ)
}
if !typ.IsUntyped() { if !typ.IsUntyped() {
types.CheckSize(typ) types.CheckSize(typ)
@ -438,7 +450,7 @@ func (r *reader) interfaceType() *types.Type {
if len(fields) == 0 { if len(fields) == 0 {
return types.Types[types.TINTER] // empty interface return types.Types[types.TINTER] // empty interface
} }
return r.needWrapper(types.NewInterface(tpkg, fields)) return types.NewInterface(tpkg, fields)
} }
func (r *reader) structType() *types.Type { func (r *reader) structType() *types.Type {
@ -459,7 +471,7 @@ func (r *reader) structType() *types.Type {
} }
fields[i] = f fields[i] = f
} }
return r.needWrapper(types.NewStruct(tpkg, fields)) return types.NewStruct(tpkg, fields)
} }
func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type { func (r *reader) signature(tpkg *types.Pkg, recv *types.Field) *types.Type {
@ -507,7 +519,7 @@ func (r *reader) obj() ir.Node {
fn := r.dict.funcs[idx] fn := r.dict.funcs[idx]
targs := make([]*types.Type, len(fn.explicits)) targs := make([]*types.Type, len(fn.explicits))
for i, targ := range fn.explicits { for i, targ := range fn.explicits {
targs[i] = r.p.typIdx(targ, r.dict) targs[i] = r.p.typIdx(targ, r.dict, true)
} }
obj = r.p.objIdx(fn.idx, nil, targs) obj = r.p.objIdx(fn.idx, nil, targs)
@ -625,7 +637,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node
// We need to defer CheckSize until we've called SetUnderlying to // We need to defer CheckSize until we've called SetUnderlying to
// handle recursive types. // handle recursive types.
types.DeferCheckSize() types.DeferCheckSize()
typ.SetUnderlying(r.typ()) typ.SetUnderlying(r.typWrapped(false))
types.ResumeCheckSize() types.ResumeCheckSize()
methods := make([]*types.Field, r.len()) methods := make([]*types.Field, r.len())
@ -636,9 +648,7 @@ func (pr *pkgReader) objIdx(idx int, implicits, explicits []*types.Type) ir.Node
typ.Methods().Set(methods) typ.Methods().Set(methods)
} }
if !typ.IsPtr() { r.needWrapper(typ)
r.needWrapper(typ)
}
return name return name
@ -1537,7 +1547,19 @@ func (r *reader) expr() (res ir.Node) {
x := r.expr() x := r.expr()
pos := r.pos() pos := r.pos()
_, sym := r.selector() _, sym := r.selector()
return typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)) n := typecheck.Expr(ir.NewSelectorExpr(pos, ir.OXDOT, x, sym)).(*ir.SelectorExpr)
if n.Op() == ir.OMETHVALUE {
wrapper := methodValueWrapper{
rcvr: n.X.Type(),
method: n.Selection,
}
if r.importedDef() {
haveMethodValueWrappers = append(haveMethodValueWrappers, wrapper)
} else {
needMethodValueWrappers = append(needMethodValueWrappers, wrapper)
}
}
return n
case exprIndex: case exprIndex:
x := r.expr() x := r.expr()
@ -2128,11 +2150,36 @@ var needWrapperTypes []*types.Type
// method wrappers, because we found the type in an imported package. // method wrappers, because we found the type in an imported package.
var haveWrapperTypes []*types.Type var haveWrapperTypes []*types.Type
func (r *reader) needWrapper(typ *types.Type) *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() { if typ.IsPtr() {
base.Fatalf("bad pointer type: %v", typ) 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 {
// If a type was found in an imported package, then we can assume // If a type was found in an imported package, then we can assume
// that package (or one of its transitive dependencies) already // that package (or one of its transitive dependencies) already
// generated method wrappers for it. // generated method wrappers for it.
@ -2144,13 +2191,7 @@ func (r *reader) needWrapper(typ *types.Type) *types.Type {
// TODO(mdempsky): Distinguish when a generic function or type was // TODO(mdempsky): Distinguish when a generic function or type was
// instantiated in an imported package so that we can add types to // instantiated in an imported package so that we can add types to
// haveWrapperTypes instead. // haveWrapperTypes instead.
if r.p != localPkgReader && !r.hasTypeParams() { return r.p != localPkgReader && !r.hasTypeParams()
haveWrapperTypes = append(haveWrapperTypes, typ)
} else {
needWrapperTypes = append(needWrapperTypes, typ)
}
return typ
} }
func (r *reader) wrapTypes(target *ir.Package) { func (r *reader) wrapTypes(target *ir.Package) {
@ -2158,37 +2199,43 @@ func (r *reader) wrapTypes(target *ir.Package) {
r.needWrapper(types.ErrorType) r.needWrapper(types.ErrorType)
seen := make(map[string]*types.Type) seen := make(map[string]*types.Type)
addType := func(typ *types.Type) bool {
if typ.Sym() != nil {
return true
}
key := typ.LinkString()
if prev := seen[key]; prev != nil {
if !types.Identical(typ, prev) {
base.Fatalf("collision: types %v and %v have short string %q", typ, prev, key)
}
return false
}
seen[key] = typ
return true
}
for _, typ := range haveWrapperTypes { for _, typ := range haveWrapperTypes {
addType(typ) r.wrapType(typ, target, seen, false)
} }
haveWrapperTypes = nil haveWrapperTypes = nil
for _, typ := range needWrapperTypes { for _, typ := range needWrapperTypes {
if addType(typ) { r.wrapType(typ, target, seen, true)
r.wrapType(typ, target)
}
} }
needWrapperTypes = nil needWrapperTypes = nil
for _, wrapper := range haveMethodValueWrappers {
r.methodValueWrapper(wrapper.rcvr, wrapper.method, target, false)
}
haveMethodValueWrappers = nil
for _, wrapper := range needMethodValueWrappers {
r.methodValueWrapper(wrapper.rcvr, wrapper.method, target, true)
}
needMethodValueWrappers = nil
} }
func (r *reader) wrapType(typ *types.Type, target *ir.Package) { func (r *reader) 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() { if !typ.IsInterface() {
typecheck.CalcMethods(typ) typecheck.CalcMethods(typ)
} }
@ -2197,8 +2244,6 @@ func (r *reader) wrapType(typ *types.Type, target *ir.Package) {
base.FatalfAt(meth.Pos, "invalid method: %v", meth) base.FatalfAt(meth.Pos, "invalid method: %v", meth)
} }
r.methodValueWrapper(typ, meth, target)
r.methodWrapper(0, typ, meth, target) r.methodWrapper(0, typ, meth, target)
// For non-interface types, we also want *T wrappers. // For non-interface types, we also want *T wrappers.
@ -2221,7 +2266,7 @@ func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Fiel
} }
sym := ir.MethodSym(wrapper, method.Sym) sym := ir.MethodSym(wrapper, method.Sym)
assert(!sym.Siggen()) base.Assertf(!sym.Siggen(), "already generated wrapper %v", sym)
sym.SetSiggen(true) sym.SetSiggen(true)
wrappee := method.Type.Recv().Type wrappee := method.Type.Recv().Type
@ -2257,17 +2302,11 @@ func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Fiel
r.finishWrapperFunc(fn, target) r.finishWrapperFunc(fn, target)
} }
func (r *reader) methodValueWrapper(tbase *types.Type, method *types.Field, target *ir.Package) { func (r *reader) methodValueWrapper(recvType *types.Type, method *types.Field, target *ir.Package, needed bool) {
recvType := tbase
if !tbase.IsInterface() {
recvType = method.Type.Recv().Type
if !types.Identical(tbase, types.ReceiverBaseType(recvType)) {
return
}
}
sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm") sym := ir.MethodSymSuffix(recvType, method.Sym, "-fm")
assert(!sym.Uniq()) if sym.Uniq() {
return
}
sym.SetUniq(true) sym.SetUniq(true)
// TODO(mdempsky): Use method.Pos instead? // TODO(mdempsky): Use method.Pos instead?
@ -2279,7 +2318,7 @@ func (r *reader) methodValueWrapper(tbase *types.Type, method *types.Field, targ
// Declare and initialize variable holding receiver. // Declare and initialize variable holding receiver.
recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType) recv := ir.NewHiddenParam(pos, fn, typecheck.Lookup(".this"), recvType)
if !reflectdata.NeedEmit(tbase) { if !needed {
typecheck.Func(fn) typecheck.Func(fn)
return return
} }

View File

@ -218,6 +218,10 @@ func methodValueWrapper(dot *ir.SelectorExpr) *ir.Name {
} }
sym.SetUniq(true) sym.SetUniq(true)
if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
base.FatalfAt(dot.Pos(), "missing wrapper for %v", meth)
}
savecurfn := ir.CurFunc savecurfn := ir.CurFunc
saveLineNo := base.Pos saveLineNo := base.Pos
ir.CurFunc = nil ir.CurFunc = nil