From d44ed5d14486728e2f9dfb9f682f37d6fb4024cb Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Sat, 26 Jun 2021 12:44:53 -0700 Subject: [PATCH] [dev.typeparams] cmd/compile: add method value wrappers to unified IR Method value wrappers will need dictionary support too, so bring them under the unified IR umbrella as well. Change-Id: Iec36bb04efdf59843d1b00f55d2c44bc841fa2ef Reviewed-on: https://go-review.googlesource.com/c/go/+/331190 Run-TryBot: Matthew Dempsky TryBot-Result: Go Bot Reviewed-by: Cuong Manh Le Trust: Matthew Dempsky --- src/cmd/compile/internal/noder/reader.go | 141 ++++++++++++++++------- 1 file changed, 99 insertions(+), 42 deletions(-) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index e5ad3f4b8ef..66c0e99d114 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -2065,6 +2065,8 @@ func (r *reader) wrapType(typ *types.Type, target *ir.Package) { base.FatalfAt(meth.Pos, "invalid method: %v", meth) } + r.methodValueWrapper(typ, meth, target) + r.methodWrapper(0, typ, meth, target) // For non-interface types, we also want *T wrappers. @@ -2100,21 +2102,81 @@ func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Fiel // TODO(mdempsky): Use method.Pos instead? pos := base.AutogeneratedPos + fn := r.newWrapperFunc(pos, sym, wrapper, method, target) + + 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) +} + +func (r *reader) methodValueWrapper(tbase *types.Type, method *types.Field, target *ir.Package) { + 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") + assert(!sym.Uniq()) + sym.SetUniq(true) + + // TODO(mdempsky): Fix typecheck to not depend on creation of + // imported method value wrappers. + if false && !reflectdata.NeedEmit(tbase) { + return + } + + // TODO(mdempsky): Use method.Pos instead? + pos := base.AutogeneratedPos + + fn := r.newWrapperFunc(pos, sym, nil, method, target) + fn.SetNeedctxt(true) + sym.Def = fn + + // Declare and initialize variable holding receiver. + recv := ir.NewNameAt(pos, typecheck.Lookup(".this")) + recv.Class = ir.PAUTOHEAP + recv.SetType(recvType) + recv.Curfn = fn + recv.SetIsClosureVar(true) + recv.SetByval(true) + fn.ClosureVars = append(fn.ClosureVars, recv) + + addTailCall(pos, fn, recv, method) +} + +func (r *reader) newWrapperFunc(pos src.XPos, sym *types.Sym, wrapper *types.Type, method *types.Field, target *ir.Package) *ir.Func { fn := ir.NewFunc(pos) - fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? - fn.SetWrapper(true) // TODO(mdempsky): Leave unset for tail calls? + fn.SetDupok(true) // TODO(mdempsky): Leave unset for local, non-generic wrappers? - fn.Nname = ir.NewNameAt(pos, sym) - ir.MarkFunc(fn.Nname) - fn.Nname.Func = fn - fn.Nname.Defn = fn + name := ir.NewNameAt(pos, sym) + ir.MarkFunc(name) + name.Func = fn + name.Defn = fn + fn.Nname = name - sig := newWrapperType(wrapper, method.Type) - r.setType(fn.Nname, sig) + sig := newWrapperType(wrapper, method) + r.setType(name, sig) // TODO(mdempsky): De-duplicate with similar logic in funcargs. - defParams := func(class ir.Class, params ...*types.Field) { - for _, param := range params { + defParams := func(class ir.Class, params *types.Type) { + for _, param := range params.FieldSlice() { name := ir.NewNameAt(param.Pos, param.Sym) name.Class = class r.setType(name, param.Type) @@ -2126,39 +2188,20 @@ func (r *reader) methodWrapper(derefs int, tbase *types.Type, method *types.Fiel } } - defParams(ir.PPARAM, sig.Recv()) - defParams(ir.PPARAM, sig.Params().FieldSlice()...) - defParams(ir.PPARAMOUT, sig.Results().FieldSlice()...) - - var recv ir.Node = sig.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)) - } - - // Add implicit derefs, as necessary. typecheck will add one deref, - // but not-in-heap types will need another for their **T wrappers. - for i := 0; i < derefs; i++ { - recv = Implicit(ir.NewStarExpr(pos, recv)) - } - - args := make([]ir.Node, sig.NumParams()) - for i, param := range sig.Params().FieldSlice() { - args[i] = param.Nname.(*ir.Name) - } - - fn.Body.Append(newTailCall(pos, method, recv, args)) + defParams(ir.PPARAM, sig.Recvs()) + defParams(ir.PPARAM, sig.Params()) + defParams(ir.PPARAMOUT, sig.Results()) target.Decls = append(target.Decls, fn) + + return fn } // newWrapperType returns a copy of the given signature type, but with -// the receiver parameter type substituted with wrapper. -func newWrapperType(wrapper, sig *types.Type) *types.Type { +// 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 { @@ -2172,25 +2215,39 @@ func newWrapperType(wrapper, sig *types.Type) *types.Type { return res } - recv := types.NewField(sig.Recv().Pos, typecheck.Lookup(".this"), wrapper) + 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 newTailCall(pos src.XPos, method *types.Field, recv ir.Node, args []ir.Node) ir.Node { +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? + call := ir.NewCallExpr(pos, ir.OCALL, ir.NewSelectorExpr(pos, ir.OXDOT, recv, method.Sym), args) call.IsDDD = method.Type.IsVariadic() if method.Type.NumResults() == 0 { - return call + fn.Body.Append(call) + return } ret := ir.NewReturnStmt(pos, nil) ret.Results = []ir.Node{call} - return ret + fn.Body.Append(ret) }