import (
"cmd/compile/internal/base"
"cmd/compile/internal/ir"
+ "cmd/compile/internal/reflectdata"
"cmd/compile/internal/typecheck"
"cmd/compile/internal/types"
"fmt"
+ "go/constant"
"strings"
)
// instantiated function if it hasn't been created yet, and change
// to calling that function directly.
modified := false
- foundFuncInst := false
+ closureRequired := false
ir.Visit(decl, func(n ir.Node) {
if n.Op() == ir.OFUNCINST {
- // We found a function instantiation that is not
- // immediately called.
- foundFuncInst = true
+ // generic F, not immediately called
+ closureRequired = true
}
- if n.Op() != ir.OCALL || n.(*ir.CallExpr).X.Op() != ir.OFUNCINST {
- return
+ if n.Op() == ir.OMETHEXPR && len(n.(*ir.SelectorExpr).X.Type().RParams()) > 0 {
+ // T.M, T a type which is generic, not immediately called
+ closureRequired = true
}
- // We have found a function call using a generic function
- // instantiation.
- call := n.(*ir.CallExpr)
- inst := call.X.(*ir.InstExpr)
- // Replace the OFUNCINST with a direct reference to the
- // new stenciled function
- st := g.getInstantiationForNode(inst)
- call.X = st.Nname
- if inst.X.Op() == ir.OCALLPART {
- // When we create an instantiation of a method
- // call, we make it a function. So, move the
- // receiver to be the first arg of the function
- // call.
- withRecv := make([]ir.Node, len(call.Args)+1)
- dot := inst.X.(*ir.SelectorExpr)
- withRecv[0] = dot.X
- copy(withRecv[1:], call.Args)
- call.Args = withRecv
+ if n.Op() == ir.OCALL && n.(*ir.CallExpr).X.Op() == ir.OFUNCINST {
+ // We have found a function call using a generic function
+ // instantiation.
+ call := n.(*ir.CallExpr)
+ inst := call.X.(*ir.InstExpr)
+ st := g.getInstantiationForNode(inst)
+ // Replace the OFUNCINST with a direct reference to the
+ // new stenciled function
+ call.X = st.Nname
+ if inst.X.Op() == ir.OCALLPART {
+ // When we create an instantiation of a method
+ // call, we make it a function. So, move the
+ // receiver to be the first arg of the function
+ // call.
+ call.Args.Prepend(inst.X.(*ir.SelectorExpr).X)
+ }
+ // Add dictionary to argument list.
+ dict := reflectdata.GetDictionaryForInstantiation(inst)
+ call.Args.Prepend(dict)
+ // Transform the Call now, which changes OCALL
+ // to OCALLFUNC and does typecheckaste/assignconvfn.
+ transformCall(call)
+ modified = true
+ }
+ if n.Op() == ir.OCALLMETH && n.(*ir.CallExpr).X.Op() == ir.ODOTMETH && len(deref(n.(*ir.CallExpr).X.Type().Recv().Type).RParams()) > 0 {
+ // Method call on a generic type, which was instantiated by stenciling.
+ // Method calls on explicitly instantiated types will have an OFUNCINST
+ // and are handled above.
+ call := n.(*ir.CallExpr)
+ meth := call.X.(*ir.SelectorExpr)
+ targs := deref(meth.Type().Recv().Type).RParams()
+
+ t := meth.X.Type()
+ baseSym := deref(t).OrigSym
+ baseType := baseSym.Def.(*ir.Name).Type()
+ var gf *ir.Name
+ for _, m := range baseType.Methods().Slice() {
+ if meth.Sel == m.Sym {
+ gf = m.Nname.(*ir.Name)
+ break
+ }
+ }
+
+ st := g.getInstantiation(gf, targs, true)
+ call.SetOp(ir.OCALL)
+ call.X = st.Nname
+ dict := reflectdata.GetDictionaryForMethod(gf, targs)
+ call.Args.Prepend(dict, meth.X)
+ // Transform the Call now, which changes OCALL
+ // to OCALLFUNC and does typecheckaste/assignconvfn.
+ transformCall(call)
+ modified = true
}
- // Transform the Call now, which changes OCALL
- // to OCALLFUNC and does typecheckaste/assignconvfn.
- transformCall(call)
- modified = true
})
- // If we found an OFUNCINST without a corresponding call in the
- // above decl, then traverse the nodes of decl again (with
+ // If we found a reference to a generic instantiation that wasn't an
+ // immediate call, then traverse the nodes of decl again (with
// EditChildren rather than Visit), where we actually change the
- // OFUNCINST node to an ONAME for the instantiated function.
+ // reference to the instantiation to a closure that captures the
+ // dictionary, then does a direct call.
// EditChildren is more expensive than Visit, so we only do this
// in the infrequent case of an OFUNCINST without a corresponding
// call.
- if foundFuncInst {
+ if closureRequired {
var edit func(ir.Node) ir.Node
edit = func(x ir.Node) ir.Node {
- if x.Op() == ir.OFUNCINST {
- // inst.X is either a function name node
- // or a selector expression for a method.
- inst := x.(*ir.InstExpr)
- st := g.getInstantiationForNode(inst)
- modified = true
- if inst.X.Op() == ir.ONAME {
- return st.Nname
- }
- assert(inst.X.Op() == ir.OCALLPART)
-
- // Return a new selector expression referring
- // to the newly stenciled function.
- oldse := inst.X.(*ir.SelectorExpr)
- newse := ir.NewSelectorExpr(oldse.Pos(), ir.OCALLPART, oldse.X, oldse.Sel)
- newse.Selection = types.NewField(oldse.Pos(), st.Sym(), st.Type())
- newse.Selection.Nname = st
- typed(inst.Type(), newse)
- return newse
- }
ir.EditChildren(x, edit)
+ switch {
+ case x.Op() == ir.OFUNCINST:
+ return g.buildClosure(decl.(*ir.Func), x)
+ case x.Op() == ir.OMETHEXPR && len(deref(x.(*ir.SelectorExpr).X.Type()).RParams()) > 0: // TODO: test for ptr-to-method case
+ return g.buildClosure(decl.(*ir.Func), x)
+ }
return x
}
edit(decl)
}
+// buildClosure makes a closure to implement x, a OFUNCINST or OMETHEXPR
+// of generic type. outer is the containing function.
+func (g *irgen) buildClosure(outer *ir.Func, x ir.Node) ir.Node {
+ pos := x.Pos()
+ var target *ir.Func // target instantiated function/method
+ var dictValue ir.Node // dictionary to use
+ var rcvrValue ir.Node // receiver, if a method value
+ typ := x.Type() // type of the closure
+ if x.Op() == ir.OFUNCINST {
+ inst := x.(*ir.InstExpr)
+
+ // Type arguments we're instantiating with.
+ targs := typecheck.TypesOf(inst.Targs)
+
+ // Find the generic function/method.
+ var gf *ir.Name
+ if inst.X.Op() == ir.ONAME {
+ // Instantiating a generic function call.
+ gf = inst.X.(*ir.Name)
+ } else if inst.X.Op() == ir.OCALLPART {
+ // Instantiating a method value x.M.
+ se := inst.X.(*ir.SelectorExpr)
+ rcvrValue = se.X
+ gf = se.Selection.Nname.(*ir.Name)
+ } else {
+ panic("unhandled")
+ }
+
+ // target is the instantiated function we're trying to call.
+ // For functions, the target expects a dictionary as its first argument.
+ // For method values, the target expects a dictionary and the receiver
+ // as its first two arguments.
+ target = g.getInstantiation(gf, targs, rcvrValue != nil)
+
+ // The value to use for the dictionary argument.
+ if rcvrValue == nil {
+ dictValue = reflectdata.GetDictionaryForFunc(gf, targs)
+ } else {
+ dictValue = reflectdata.GetDictionaryForMethod(gf, targs)
+ }
+ } else { // ir.OMETHEXPR
+ // Method expression T.M where T is a generic type.
+ // TODO: Is (*T).M right?
+ se := x.(*ir.SelectorExpr)
+ targs := se.X.Type().RParams()
+ if len(targs) == 0 {
+ if se.X.Type().IsPtr() {
+ targs = se.X.Type().Elem().RParams()
+ if len(targs) == 0 {
+ panic("bad")
+ }
+ }
+ }
+ t := se.X.Type()
+ baseSym := t.OrigSym
+ baseType := baseSym.Def.(*ir.Name).Type()
+ var gf *ir.Name
+ for _, m := range baseType.Methods().Slice() {
+ if se.Sel == m.Sym {
+ gf = m.Nname.(*ir.Name)
+ break
+ }
+ }
+ target = g.getInstantiation(gf, targs, true)
+ dictValue = reflectdata.GetDictionaryForMethod(gf, targs)
+ }
+
+ // Build a closure to implement a function instantiation.
+ //
+ // func f[T any] (int, int) (int, int) { ...whatever... }
+ //
+ // Then any reference to f[int] not directly called gets rewritten to
+ //
+ // .dictN := ... dictionary to use ...
+ // func(a0, a1 int) (r0, r1 int) {
+ // return .inst.f[int](.dictN, a0, a1)
+ // }
+ //
+ // Similarly for method expressions,
+ //
+ // type g[T any] ....
+ // func (rcvr g[T]) f(a0, a1 int) (r0, r1 int) { ... }
+ //
+ // Any reference to g[int].f not directly called gets rewritten to
+ //
+ // .dictN := ... dictionary to use ...
+ // func(rcvr g[int], a0, a1 int) (r0, r1 int) {
+ // return .inst.g[int].f(.dictN, rcvr, a0, a1)
+ // }
+ //
+ // Also method values
+ //
+ // var x g[int]
+ //
+ // Any reference to x.f not directly called gets rewritten to
+ //
+ // .dictN := ... dictionary to use ...
+ // x2 := x
+ // func(a0, a1 int) (r0, r1 int) {
+ // return .inst.g[int].f(.dictN, x2, a0, a1)
+ // }
+
+ // Make a new internal function.
+ fn := ir.NewFunc(pos)
+ fn.SetIsHiddenClosure(true)
+
+ // This is the dictionary we want to use.
+ // Note: for now this is a compile-time constant, so we don't really need a closure
+ // to capture it (a wrapper function would work just as well). But eventually it
+ // will be a read of a subdictionary from the parent dictionary.
+ dictVar := ir.NewNameAt(pos, typecheck.LookupNum(".dict", g.dnum))
+ g.dnum++
+ dictVar.Class = ir.PAUTO
+ typed(types.Types[types.TUINTPTR], dictVar)
+ dictVar.Curfn = outer
+ dictAssign := ir.NewAssignStmt(pos, dictVar, dictValue)
+ dictAssign.SetTypecheck(1)
+ dictVar.Defn = dictAssign
+ outer.Dcl = append(outer.Dcl, dictVar)
+
+ // assign the receiver to a temporary.
+ var rcvrVar *ir.Name
+ var rcvrAssign ir.Node
+ if rcvrValue != nil {
+ rcvrVar = ir.NewNameAt(pos, typecheck.LookupNum(".rcvr", g.dnum))
+ g.dnum++
+ rcvrVar.Class = ir.PAUTO
+ typed(rcvrValue.Type(), rcvrVar)
+ rcvrVar.Curfn = outer
+ rcvrAssign = ir.NewAssignStmt(pos, rcvrVar, rcvrValue)
+ rcvrAssign.SetTypecheck(1)
+ rcvrVar.Defn = rcvrAssign
+ outer.Dcl = append(outer.Dcl, rcvrVar)
+ }
+
+ // Build formal argument and return lists.
+ var formalParams []*types.Field // arguments of closure
+ var formalResults []*types.Field // returns of closure
+ for i := 0; i < typ.NumParams(); i++ {
+ t := typ.Params().Field(i).Type
+ arg := ir.NewNameAt(pos, typecheck.LookupNum("a", i))
+ arg.Class = ir.PPARAM
+ typed(t, arg)
+ arg.Curfn = fn
+ fn.Dcl = append(fn.Dcl, arg)
+ f := types.NewField(pos, arg.Sym(), t)
+ f.Nname = arg
+ formalParams = append(formalParams, f)
+ }
+ for i := 0; i < typ.NumResults(); i++ {
+ t := typ.Results().Field(i).Type
+ result := ir.NewNameAt(pos, typecheck.LookupNum("r", i)) // TODO: names not needed?
+ result.Class = ir.PPARAMOUT
+ typed(t, result)
+ result.Curfn = fn
+ fn.Dcl = append(fn.Dcl, result)
+ f := types.NewField(pos, result.Sym(), t)
+ f.Nname = result
+ formalResults = append(formalResults, f)
+ }
+
+ // Build an internal function with the right signature.
+ closureType := types.NewSignature(x.Type().Pkg(), nil, nil, formalParams, formalResults)
+ sym := typecheck.ClosureName(outer)
+ sym.SetFunc(true)
+ fn.Nname = ir.NewNameAt(pos, sym)
+ fn.Nname.Func = fn
+ fn.Nname.Defn = fn
+ typed(closureType, fn.Nname)
+ fn.SetTypecheck(1)
+
+ // Build body of closure. This involves just calling the wrapped function directly
+ // with the additional dictionary argument.
+
+ // First, capture the dictionary variable for use in the closure.
+ dict2Var := ir.CaptureName(pos, fn, dictVar)
+ // Also capture the receiver variable.
+ var rcvr2Var *ir.Name
+ if rcvrValue != nil {
+ rcvr2Var = ir.CaptureName(pos, fn, rcvrVar)
+ }
+
+ // Build arguments to call inside the closure.
+ var args []ir.Node
+
+ // First the dictionary argument.
+ args = append(args, dict2Var)
+ // Then the receiver.
+ if rcvrValue != nil {
+ args = append(args, rcvr2Var)
+ }
+ // Then all the other arguments (including receiver for method expressions).
+ for i := 0; i < typ.NumParams(); i++ {
+ args = append(args, formalParams[i].Nname.(*ir.Name))
+ }
+
+ // Build call itself.
+ var innerCall ir.Node = ir.NewCallExpr(pos, ir.OCALL, target.Nname, args)
+ if len(formalResults) > 0 {
+ innerCall = ir.NewReturnStmt(pos, []ir.Node{innerCall})
+ }
+ // Finish building body of closure.
+ ir.CurFunc = fn
+ // TODO: set types directly here instead of using typecheck.Stmt
+ typecheck.Stmt(innerCall)
+ ir.CurFunc = nil
+ fn.Body = []ir.Node{innerCall}
+
+ // We're all done with the captured dictionary (and receiver, for method values).
+ ir.FinishCaptureNames(pos, outer, fn)
+
+ // Make a closure referencing our new internal function.
+ c := ir.NewClosureExpr(pos, fn)
+ init := []ir.Node{dictAssign}
+ if rcvrValue != nil {
+ init = append(init, rcvrAssign)
+ }
+ c.SetInit(init)
+ typed(x.Type(), c)
+ return c
+}
+
// instantiateMethods instantiates all the methods of all fully-instantiated
// generic types that have been added to g.instTypeList.
func (g *irgen) instantiateMethods() {
// not be set on imported instantiated types.
baseSym := typ.OrigSym
baseType := baseSym.Def.(*ir.Name).Type()
- for j, m := range typ.Methods().Slice() {
- name := m.Nname.(*ir.Name)
+ for j, _ := range typ.Methods().Slice() {
baseNname := baseType.Methods().Slice()[j].Nname.(*ir.Name)
- // Note: we are breaking an invariant here:
- // m.Nname is now not equal m.Nname.Func.Nname.
- // m.Nname has the type of a method, whereas m.Nname.Func.Nname has
- // the type of a function, since it is an function instantiation.
- name.Func = g.getInstantiation(baseNname, typ.RParams(), true)
+ // Eagerly generate the instantiations that implement these methods.
+ // We don't use the instantiations here, just generate them (and any
+ // further instantiations those generate, etc.).
+ // Note that we don't set the Func for any methods on instantiated
+ // types. Their signatures don't match so that would be confusing.
+ // Direct method calls go directly to the instantiations, implemented above.
+ // Indirect method calls use wrappers generated in reflectcall. Those wrappers
+ // will use these instantiations if they are needed (for interface tables or reflection).
+ _ = g.getInstantiation(baseNname, typ.RParams(), true)
}
}
g.instTypeList = nil
vars: make(map[*ir.Name]*ir.Name),
}
- newf.Dcl = make([]*ir.Name, len(gf.Dcl))
- for i, n := range gf.Dcl {
- newf.Dcl[i] = subst.localvar(n)
- }
+ newf.Dcl = make([]*ir.Name, 0, len(gf.Dcl)+1)
// Replace the types in the function signature.
// Ugly: also, we have to insert the Name nodes of the parameters/results into
// because it came via conversion from the types2 type.
oldt := nameNode.Type()
// We also transform a generic method type to the corresponding
- // instantiated function type where the receiver is the first parameter.
+ // instantiated function type where the dictionary is the first parameter.
+ dictionarySym := types.LocalPkg.Lookup(".dict")
+ dictionaryType := types.Types[types.TUINTPTR]
+ dictionaryName := ir.NewNameAt(gf.Pos(), dictionarySym)
+ typed(dictionaryType, dictionaryName)
+ dictionaryName.Class = ir.PPARAM
+ dictionaryName.Curfn = newf
+ newf.Dcl = append(newf.Dcl, dictionaryName)
+ for _, n := range gf.Dcl {
+ if n.Sym().Name == ".dict" {
+ panic("already has dictionary")
+ }
+ newf.Dcl = append(newf.Dcl, subst.localvar(n))
+ }
+ dictionaryArg := types.NewField(gf.Pos(), dictionarySym, dictionaryType)
+ dictionaryArg.Nname = dictionaryName
+ var args []*types.Field
+ args = append(args, dictionaryArg)
+ args = append(args, oldt.Recvs().FieldSlice()...)
+ args = append(args, oldt.Params().FieldSlice()...)
newt := types.NewSignature(oldt.Pkg(), nil, nil,
- subst.fields(ir.PPARAM, append(oldt.Recvs().FieldSlice(), oldt.Params().FieldSlice()...), newf.Dcl),
+ subst.fields(ir.PPARAM, args, newf.Dcl),
subst.fields(ir.PPARAMOUT, oldt.Results().FieldSlice(), newf.Dcl))
- newf.Nname.SetType(newt)
+ typed(newt, newf.Nname)
ir.MarkFunc(newf.Nname)
newf.SetTypecheck(1)
- newf.Nname.SetTypecheck(1)
// Make sure name/type of newf is set before substituting the body.
newf.Body = subst.list(gf.Body)
+
+ // Add code to check that the dictionary is correct.
+ newf.Body.Prepend(g.checkDictionary(dictionaryName, targs)...)
+
ir.CurFunc = savef
return newf
return m
}
+// checkDictionary returns code that does runtime consistency checks
+// between the dictionary and the types it should contain.
+func (g *irgen) checkDictionary(name *ir.Name, targs []*types.Type) (code []ir.Node) {
+ if false {
+ return // checking turned off
+ }
+ // TODO: when moving to GCshape, this test will become harder. Call into
+ // runtime to check the expected shape is correct?
+ pos := name.Pos()
+ // Convert dictionary to *[N]uintptr
+ d := ir.NewConvExpr(pos, ir.OCONVNOP, types.Types[types.TUNSAFEPTR], name)
+ d.SetTypecheck(1)
+ d = ir.NewConvExpr(pos, ir.OCONVNOP, types.NewArray(types.Types[types.TUINTPTR], int64(len(targs))).PtrTo(), d)
+ d.SetTypecheck(1)
+
+ // Check that each type entry in the dictionary is correct.
+ for i, t := range targs {
+ want := reflectdata.TypePtr(t)
+ typed(types.Types[types.TUINTPTR], want)
+ deref := ir.NewStarExpr(pos, d)
+ typed(d.Type().Elem(), deref)
+ idx := ir.NewConstExpr(constant.MakeUint64(uint64(i)), name) // TODO: what to set orig to?
+ typed(types.Types[types.TUINTPTR], idx)
+ got := ir.NewIndexExpr(pos, deref, idx)
+ typed(types.Types[types.TUINTPTR], got)
+ cond := ir.NewBinaryExpr(pos, ir.ONE, want, got)
+ typed(types.Types[types.TBOOL], cond)
+ panicArg := ir.NewNilExpr(pos)
+ typed(types.NewInterface(types.LocalPkg, nil), panicArg)
+ then := ir.NewUnaryExpr(pos, ir.OPANIC, panicArg)
+ then.SetTypecheck(1)
+ x := ir.NewIfStmt(pos, cond, []ir.Node{then}, nil)
+ x.SetTypecheck(1)
+ code = append(code, x)
+ }
+ return
+}
+
// node is like DeepCopy(), but substitutes ONAME nodes based on subst.vars, and
// also descends into closures. It substitutes type arguments for type parameters
// in all the new nodes.
t2 := subst.typ(f.Type)
oldsym := f.Nname.Sym()
newsym := typecheck.MakeInstName(oldsym, subst.targs, true)
+ // TODO: use newsym?
var nname *ir.Name
if newsym.Def != nil {
nname = newsym.Def.(*ir.Name)
} else {
- nname = ir.NewNameAt(f.Pos, newsym)
+ nname = ir.NewNameAt(f.Pos, oldsym)
nname.SetType(t2)
- newsym.Def = nname
+ oldsym.Def = nname
}
newfields[i] = types.NewField(f.Pos, f.Sym, t2)
newfields[i].Nname = nname
}
typecheck.CalcMethods(mt)
- // type stored in interface word
- it := t
-
- if !types.IsDirectIface(it) {
- it = types.NewPtr(t)
- }
-
// make list of methods for t,
// generating code if necessary.
var ms []*typeSig
sig := &typeSig{
name: f.Sym,
- isym: methodWrapper(it, f),
- tsym: methodWrapper(t, f),
+ isym: methodWrapper(t, f, true),
+ tsym: methodWrapper(t, f, false),
type_: typecheck.NewMethodType(f.Type, t),
mtype: typecheck.NewMethodType(f.Type, nil),
}
// IfaceType.Method is not in the reflect data.
// Generate the method body, so that compiled
// code can refer to it.
- methodWrapper(t, f)
+ methodWrapper(t, f, false)
}
return methods
//
// rcvr - U
// method - M func (t T)(), a TFIELD type struct
-func methodWrapper(rcvr *types.Type, method *types.Field) *obj.LSym {
+//
+// Also wraps methods on instantiated generic types for use in itab entries.
+// For an instantiated generic type G[int], we generate wrappers like:
+// G[int] pointer shaped:
+// func (x G[int]) f(arg) {
+// .inst.G[int].f(dictionary, x, arg)
+// }
+// G[int] not pointer shaped:
+// func (x *G[int]) f(arg) {
+// .inst.G[int].f(dictionary, *x, arg)
+// }
+// These wrappers are always fully stenciled.
+func methodWrapper(rcvr *types.Type, method *types.Field, forItab bool) *obj.LSym {
+ orig := rcvr
+ if forItab && !types.IsDirectIface(rcvr) {
+ rcvr = rcvr.PtrTo()
+ }
+ generic := false
+ if !rcvr.IsInterface() && len(rcvr.RParams()) > 0 || rcvr.IsPtr() && len(rcvr.Elem().RParams()) > 0 { // TODO: right detection?
+ // TODO: check that we do the right thing when rcvr.IsInterface().
+ generic = true
+ }
newnam := ir.MethodSym(rcvr, method.Sym)
lsym := newnam.Linksym()
if newnam.Siggen() {
}
newnam.SetSiggen(true)
- if types.Identical(rcvr, method.Type.Recv().Type) {
+ if !generic && types.Identical(rcvr, method.Type.Recv().Type) {
return lsym
}
nthis := ir.AsNode(tfn.Type().Recv().Nname)
methodrcvr := method.Type.Recv().Type
+ indirect := rcvr.IsPtr() && rcvr.Elem() == methodrcvr
// generate nil pointer check for better error
- if rcvr.IsPtr() && rcvr.Elem() == methodrcvr {
+ if indirect {
// generating wrapper from *T to T.
n := ir.NewIfStmt(base.Pos, nil, nil, nil)
n.Cond = ir.NewBinaryExpr(base.Pos, ir.OEQ, nthis, typecheck.NodNil())
// Disable tailcall for RegabiArgs for now. The IR does not connect the
// arguments with the OTAILCALL node, and the arguments are not marshaled
// correctly.
- if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs {
+ if !base.Flag.Cfg.Instrumenting && rcvr.IsPtr() && methodrcvr.IsPtr() && method.Embedded != 0 && !types.IsInterfaceMethod(method.Type) && !(base.Ctxt.Arch.Name == "ppc64le" && base.Ctxt.Flag_dynlink) && !buildcfg.Experiment.RegabiArgs && !generic {
// generate tail call: adjust pointer receiver and jump to embedded method.
left := dot.X // skip final .M
if !left.Type().IsPtr() {
fn.Body.Append(ir.NewTailCallStmt(base.Pos, method.Nname.(*ir.Name)))
} else {
fn.SetWrapper(true) // ignore frame for panic+recover matching
- call := ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
- call.Args = ir.ParamNames(tfn.Type())
+ var call *ir.CallExpr
+ if generic {
+ var args []ir.Node
+ var targs []*types.Type
+ if rcvr.IsPtr() { // TODO: correct condition?
+ targs = rcvr.Elem().RParams()
+ } else {
+ targs = rcvr.RParams()
+ }
+ if strings.HasPrefix(ir.MethodSym(orig, method.Sym).Name, ".inst.") {
+ fmt.Printf("%s\n", ir.MethodSym(orig, method.Sym).Name)
+ panic("multiple .inst.")
+ }
+ args = append(args, getDictionary(".inst."+ir.MethodSym(orig, method.Sym).Name, targs)) // TODO: remove .inst.
+ if indirect {
+ args = append(args, ir.NewStarExpr(base.Pos, nthis))
+ } else {
+ args = append(args, nthis)
+ }
+ args = append(args, ir.ParamNames(tfn.Type())...)
+
+ // TODO: Once we enter the gcshape world, we'll need a way to look up
+ // the stenciled implementation to use for this concrete type. Essentially,
+ // erase the concrete types and replace them with gc shape representatives.
+ sym := typecheck.MakeInstName(ir.MethodSym(methodrcvr, method.Sym), targs, true)
+ if sym.Def == nil {
+ // Currently we make sure that we have all the instantiations
+ // we need by generating them all in ../noder/stencil.go:instantiateMethods
+ // TODO: maybe there's a better, more incremental way to generate
+ // only the instantiations we need?
+ base.Fatalf("instantiation %s not found", sym.Name)
+ }
+ target := ir.AsNode(sym.Def)
+ call = ir.NewCallExpr(base.Pos, ir.OCALL, target, args)
+ } else {
+ call = ir.NewCallExpr(base.Pos, ir.OCALL, dot, nil)
+ call.Args = ir.ParamNames(tfn.Type())
+ }
call.IsDDD = tfn.Type().IsVariadic()
if method.Type.NumResults() > 0 {
ret := ir.NewReturnStmt(base.Pos, nil)
r.Add = InterfaceMethodOffset(ityp, midx)
r.Type = objabi.R_USEIFACEMETHOD
}
+
+// getDictionaryForInstantiation returns the dictionary that should be used for invoking
+// the concrete instantiation described by inst.
+func GetDictionaryForInstantiation(inst *ir.InstExpr) ir.Node {
+ targs := typecheck.TypesOf(inst.Targs)
+ if meth, ok := inst.X.(*ir.SelectorExpr); ok {
+ return GetDictionaryForMethod(meth.Selection.Nname.(*ir.Name), targs)
+ }
+ return GetDictionaryForFunc(inst.X.(*ir.Name), targs)
+}
+
+func GetDictionaryForFunc(fn *ir.Name, targs []*types.Type) ir.Node {
+ return getDictionary(typecheck.MakeInstName(fn.Sym(), targs, false).Name, targs)
+}
+func GetDictionaryForMethod(meth *ir.Name, targs []*types.Type) ir.Node {
+ return getDictionary(typecheck.MakeInstName(meth.Sym(), targs, true).Name, targs)
+}
+
+// getDictionary returns the dictionary for the given named generic function
+// or method, with the given type arguments.
+// TODO: pass a reference to the generic function instead? We might need
+// that to look up protodictionaries.
+func getDictionary(name string, targs []*types.Type) ir.Node {
+ if len(targs) == 0 {
+ base.Fatalf("%s should have type arguments", name)
+ }
+
+ // The dictionary for this instantiation is named after the function
+ // and concrete types it is instantiated with.
+ // TODO: decouple this naming from the instantiation naming. The instantiation
+ // naming will be based on GC shapes, this naming must be fully stenciled.
+ if !strings.HasPrefix(name, ".inst.") {
+ base.Fatalf("%s should start in .inst.", name)
+ }
+ name = ".dict." + name[6:]
+
+ // Get a symbol representing the dictionary.
+ sym := typecheck.Lookup(name)
+
+ // Initialize the dictionary, if we haven't yet already.
+ if lsym := sym.Linksym(); len(lsym.P) == 0 {
+ off := 0
+ // Emit an entry for each concrete type.
+ for _, t := range targs {
+ s := TypeLinksym(t)
+ off = objw.SymPtr(lsym, off, s, 0)
+ }
+ // TODO: subdictionaries
+ objw.Global(lsym, int32(off), obj.DUPOK|obj.RODATA)
+ }
+
+ // Make a node referencing the dictionary symbol.
+ n := typecheck.NewName(sym)
+ n.SetType(types.Types[types.TUINTPTR]) // should probably be [...]uintptr, but doesn't really matter
+ n.SetTypecheck(1)
+ n.Class = ir.PEXTERN
+ sym.Def = n
+
+ // Return the address of the dictionary.
+ np := typecheck.NodAddr(n)
+ // Note: treat dictionary pointers as uintptrs, so they aren't pointers
+ // with respect to GC. That saves on stack scanning work, write barriers, etc.
+ // We can get away with it because dictionaries are global variables.
+ // TODO: use a cast, or is typing directly ok?
+ np.SetType(types.Types[types.TUINTPTR])
+ np.SetTypecheck(1)
+ return np
+}