From: Keith Randall Date: Sat, 5 Jun 2021 00:19:09 +0000 (-0700) Subject: [dev.typeparams] cmd/compile: convert generic values to interface type using dictionary X-Git-Tag: go1.18beta1~1818^2^2~389 X-Git-Url: http://www.git.cypherpunks.su/?a=commitdiff_plain;h=ccfb0ce8df980599750db4fa56a8ab16202f1ba6;p=gostls13.git [dev.typeparams] cmd/compile: convert generic values to interface type using dictionary When converting a variable of generic type to an interface, use the entry in the dictionary for the type field instead of using the compile-time type (which we only have when fully stenciling). Note: this isn't all the conversions. Conversions often get processed in the ir.OCALL case. Those aren't handled yet. Change-Id: I9a6a4c572e3c54a8e8efad98365184dbb94c4487 Reviewed-on: https://go-review.googlesource.com/c/go/+/325330 Trust: Keith Randall Trust: Dan Scales Reviewed-by: Dan Scales --- diff --git a/src/cmd/compile/internal/escape/escape.go b/src/cmd/compile/internal/escape/escape.go index 3ac7ff1ebe..842b0f4a7e 100644 --- a/src/cmd/compile/internal/escape/escape.go +++ b/src/cmd/compile/internal/escape/escape.go @@ -669,6 +669,13 @@ func (e *escape) exprSkipInit(k hole, n ir.Node) { k = e.spill(k, n) } e.expr(k.note(n, "interface-converted"), n.X) + case ir.OEFACE: + n := n.(*ir.BinaryExpr) + // Note: n.X is not needed because it can never point to memory that might escape. + e.expr(k, n.Y) + case ir.OIDATA: + n := n.(*ir.UnaryExpr) + e.expr(k, n.X) case ir.OSLICE2ARRPTR: // the slice pointer flows directly to the result n := n.(*ir.ConvExpr) diff --git a/src/cmd/compile/internal/noder/stencil.go b/src/cmd/compile/internal/noder/stencil.go index 8b5a91f6d1..3e3de1908e 100644 --- a/src/cmd/compile/internal/noder/stencil.go +++ b/src/cmd/compile/internal/noder/stencil.go @@ -13,6 +13,7 @@ import ( "cmd/compile/internal/reflectdata" "cmd/compile/internal/typecheck" "cmd/compile/internal/types" + "cmd/internal/src" "fmt" "go/constant" ) @@ -496,10 +497,11 @@ func (g *irgen) getInstantiation(nameNode *ir.Name, targs []*types.Type, isMeth // Struct containing info needed for doing the substitution as we create the // instantiation of a generic function with specified type arguments. type subster struct { - g *irgen - isMethod bool // If a method is being instantiated - newf *ir.Func // Func node for the new stenciled function - ts typecheck.Tsubster + g *irgen + isMethod bool // If a method is being instantiated + newf *ir.Func // Func node for the new stenciled function + ts typecheck.Tsubster + dictionary *ir.Name // Name of dictionary variable } // genericSubst returns a new function with name newsym. The function is an @@ -573,6 +575,7 @@ func (g *irgen) genericSubst(newsym *types.Sym, nameNode *ir.Name, targs []*type } dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType) dictionaryArg.Nname = dictionaryName + subst.dictionary = dictionaryName var args []*types.Field args = append(args, dictionaryArg) args = append(args, oldt.Recvs().FieldSlice()...) @@ -656,6 +659,38 @@ func (g *irgen) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.N return } +// getDictionaryType returns a *runtime._type from the dictionary corresponding to the input type. +// The input type must be a type parameter (TODO: or a local derived type). +func (subst *subster) getDictionaryType(pos src.XPos, t *types.Type) ir.Node { + tparams := subst.ts.Tparams + var i = 0 + for i = range tparams { + if t == tparams[i] { + break + } + } + if i == len(tparams) { + base.Fatalf(fmt.Sprintf("couldn't find type param %+v", t)) + } + + // Convert dictionary to *[N]uintptr + // All entries in the dictionary are pointers. They all point to static data, though, so we + // treat them as uintptrs so the GC doesn't need to keep track of them. + d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], subst.dictionary) + d.SetTypecheck(1) + d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(tparams))).PtrTo(), d) + d.SetTypecheck(1) + + // Load entry i out of the dictionary. + deref := ir.NewStarExpr(pos, d) + typed(d.Type().Elem(), deref) + idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), subst.dictionary) // TODO: what to set orig to? + typed(types.Types[types.TUINTPTR], idx) + r := ir.NewIndexExpr(pos, deref, idx) + typed(types.Types[types.TUINT8].PtrTo(), r) // standard typing of a *runtime._type in the compiler is *byte + return r +} + // node is like DeepCopy(), but substitutes ONAME nodes based on subst.ts.vars, and // also descends into closures. It substitutes type arguments for type parameters // in all the new nodes. @@ -859,6 +894,34 @@ func (subst *subster) node(n ir.Node) ir.Node { ir.CurFunc = saveNewf subst.g.target.Decls = append(subst.g.target.Decls, newfn) + + case ir.OCONVIFACE: + x := x.(*ir.ConvExpr) + // TODO: handle converting from derived types. For now, just from naked + // type parameters. + if x.X.Type().IsTypeParam() { + // Load the actual runtime._type of the type parameter from the dictionary. + rt := subst.getDictionaryType(m.Pos(), x.X.Type()) + + // At this point, m is an interface type with a data word we want. + // But the type word represents a gcshape type, which we don't want. + // Replace with the instantiated type loaded from the dictionary. + m = ir.NewUnaryExpr(m.Pos(), ir.OIDATA, m) + typed(types.Types[types.TUNSAFEPTR], m) + m = ir.NewBinaryExpr(m.Pos(), ir.OEFACE, rt, m) + if !x.Type().IsEmptyInterface() { + // We just built an empty interface{}. Type it as such, + // then assert it to the required non-empty interface. + typed(types.NewInterface(types.LocalPkg, nil), m) + m = ir.NewTypeAssertExpr(m.Pos(), m, nil) + } + typed(x.Type(), m) + // TODO: we're throwing away the type word of the original version + // of m here (it would be OITAB(m)), which probably took some + // work to generate. Can we avoid generating it at all? + // (The linker will throw them away if not needed, so it would just + // save toolchain work, not binary size.) + } } return m }