"cmd/internal/src"
)
-type itabEntry struct {
- t, itype *types.Type
- lsym *obj.LSym // symbol of the itab itself
-
- // symbols of each method in
- // the itab, sorted by byte offset;
- // filled in by CompileITabs
- entries []*obj.LSym
-}
-
type ptabEntry struct {
s *types.Sym
t *types.Type
}
-func CountTabs() (numPTabs, numITabs int) {
- return len(ptabs), len(itabs)
+func CountPTabs() int {
+ return len(ptabs)
}
// runtime interface and reflection data structures
gcsymmu sync.Mutex // protects gcsymset and gcsymslice
gcsymset = make(map[*types.Type]struct{})
- itabs []itabEntry
ptabs []*ir.Name
)
}
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
// tflag is documented in reflect/type.go.
//
// tflag values must be kept in sync with copies in:
- // cmd/compile/internal/gc/reflect.go
+ // cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// reflect/type.go
// runtime/type.go
}
exported := false
- p := t.LongString()
+ p := t.NameString()
// If we're writing out type T,
// we are very likely to write out type *T as well.
// Use the string "*T"[1:] for "T", so that the two
// TrackSym returns the symbol for tracking use of field/method f, assumed
// to be a member of struct/interface type t.
func TrackSym(t *types.Type, f *types.Field) *obj.LSym {
- return base.PkgLinksym("go.track", t.ShortString()+"."+f.Sym.Name, obj.ABI0)
+ return base.PkgLinksym("go.track", t.LinkString()+"."+f.Sym.Name, obj.ABI0)
}
func TypeSymPrefix(prefix string, t *types.Type) *types.Sym {
- p := prefix + "." + t.ShortString()
+ p := prefix + "." + t.LinkString()
s := types.TypeSymLookup(p)
// This function is for looking up type-related generated functions
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
}
-func ITabAddr(t, itype *types.Type) *ir.AddrExpr {
- if t == nil || (t.IsPtr() && t.Elem() == nil) || t.IsUntyped() || !itype.IsInterface() || itype.IsEmptyInterface() {
- base.Fatalf("ITabAddr(%v, %v)", t, itype)
- }
- s, existed := ir.Pkgs.Itab.LookupOK(t.ShortString() + "," + itype.ShortString())
+// ITabAddr returns an expression representing a pointer to the itab
+// for concrete type typ implementing interface iface.
+func ITabAddr(typ, iface *types.Type) *ir.AddrExpr {
+ s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
+ lsym := s.Linksym()
+
if !existed {
- itabs = append(itabs, itabEntry{t: t, itype: itype, lsym: s.Linksym()})
+ writeITab(lsym, typ, iface)
}
- lsym := s.Linksym()
n := ir.NewLinksymExpr(base.Pos, lsym, types.Types[types.TUINT8])
return typecheck.Expr(typecheck.NodAddr(n)).(*ir.AddrExpr)
}
if t.IsPtr() && t.Sym() == nil && t.Elem().Sym() != nil {
tbase = t.Elem()
}
+ if tbase.Kind() == types.TFORW {
+ base.Fatalf("unresolved defined type: %v", tbase)
+ }
+
dupok := 0
- if tbase.Sym() == nil {
+ if tbase.Sym() == nil { // TODO(mdempsky): Probably need DUPOK for instantiated types too.
dupok = obj.DUPOK
}
- if base.Ctxt.Pkgpath != "runtime" || (tbase != types.Types[tbase.Kind()] && tbase != types.ByteType && tbase != types.RuneType && tbase != types.ErrorType) { // int, float, etc
- // named types from other files are defined only by those files
- if tbase.Sym() != nil && tbase.Sym().Pkg != types.LocalPkg {
- if i := typecheck.BaseTypeIndex(t); i >= 0 {
- lsym.Pkg = tbase.Sym().Pkg.Prefix
- lsym.SymIdx = int32(i)
- lsym.Set(obj.AttrIndexed, true)
- }
- return lsym
- }
- // TODO(mdempsky): Investigate whether this can happen.
- if tbase.Kind() == types.TFORW {
- return lsym
+ if !NeedEmit(tbase) {
+ if i := typecheck.BaseTypeIndex(t); i >= 0 {
+ lsym.Pkg = tbase.Sym().Pkg.Prefix
+ lsym.SymIdx = int32(i)
+ lsym.Set(obj.AttrIndexed, true)
}
+
+ // TODO(mdempsky): Investigate whether this still happens.
+ // If we know we don't need to emit code for a type,
+ // we should have a link-symbol index for it.
+ // See also TODO in NeedEmit.
+ return lsym
}
ot := 0
return int64(commonSize()+4*types.PtrSize+uncommonSize(ityp)) + i*8
}
-// for each itabEntry, gather the methods on
-// the concrete type that implement the interface
-func CompileITabs() {
- for i := range itabs {
- tab := &itabs[i]
- methods := genfun(tab.t, tab.itype)
- if len(methods) == 0 {
- continue
- }
- tab.entries = methods
- }
-}
-
-// for the given concrete type and interface
-// type, return the (sorted) set of methods
-// on the concrete type that implement the interface
-func genfun(t, it *types.Type) []*obj.LSym {
- if t == nil || it == nil {
- return nil
- }
- sigs := imethods(it)
- methods := methods(t)
- out := make([]*obj.LSym, 0, len(sigs))
- // TODO(mdempsky): Short circuit before calling methods(t)?
- // See discussion on CL 105039.
- if len(sigs) == 0 {
- return nil
- }
-
- // both sigs and methods are sorted by name,
- // so we can find the intersect in a single pass
- for _, m := range methods {
- if m.name == sigs[0].name {
- out = append(out, m.isym)
- sigs = sigs[1:]
- if len(sigs) == 0 {
- break
- }
- }
- }
-
- if len(sigs) != 0 {
- base.Fatalf("incomplete itab")
- }
-
- return out
-}
-
-// ITabSym uses the information gathered in
-// CompileITabs to de-virtualize interface methods.
-// Since this is called by the SSA backend, it shouldn't
-// generate additional Nodes, Syms, etc.
-func ITabSym(it *obj.LSym, offset int64) *obj.LSym {
- var syms []*obj.LSym
- if it == nil {
- return nil
- }
-
- for i := range itabs {
- e := &itabs[i]
- if e.lsym == it {
- syms = e.entries
- break
- }
- }
- if syms == nil {
- return nil
- }
-
- // keep this arithmetic in sync with *itab layout
- methodnum := int((offset - 2*int64(types.PtrSize) - 8) / int64(types.PtrSize))
- if methodnum >= len(syms) {
- return nil
- }
- return syms[methodnum]
-}
-
// NeedRuntimeType ensures that a runtime type descriptor is emitted for t.
func NeedRuntimeType(t *types.Type) {
if t.HasTParam() {
}
}
-func WriteTabs() {
- // process itabs
- for _, i := range itabs {
- // dump empty itab symbol into i.sym
- // type itab struct {
- // inter *interfacetype
- // _type *_type
- // hash uint32
- // _ [4]byte
- // fun [1]uintptr // variable sized
- // }
- o := objw.SymPtr(i.lsym, 0, writeType(i.itype), 0)
- o = objw.SymPtr(i.lsym, o, writeType(i.t), 0)
- o = objw.Uint32(i.lsym, o, types.TypeHash(i.t)) // copy of type hash
- o += 4 // skip unused field
- for _, fn := range genfun(i.t, i.itype) {
- o = objw.SymPtrWeak(i.lsym, o, fn, 0) // method pointer for each method
+// writeITab writes the itab for concrete type typ implementing
+// interface iface.
+func writeITab(lsym *obj.LSym, typ, iface *types.Type) {
+ // TODO(mdempsky): Fix methodWrapper, geneq, and genhash (and maybe
+ // others) to stop clobbering these.
+ oldpos, oldfn := base.Pos, ir.CurFunc
+ defer func() { base.Pos, ir.CurFunc = oldpos, oldfn }()
+
+ if typ == nil || (typ.IsPtr() && typ.Elem() == nil) || typ.IsUntyped() || iface == nil || !iface.IsInterface() || iface.IsEmptyInterface() {
+ base.Fatalf("writeITab(%v, %v)", typ, iface)
+ }
+
+ sigs := iface.AllMethods().Slice()
+ entries := make([]*obj.LSym, 0, len(sigs))
+
+ // both sigs and methods are sorted by name,
+ // so we can find the intersection in a single pass
+ for _, m := range methods(typ) {
+ if m.name == sigs[0].Sym {
+ entries = append(entries, m.isym)
+ sigs = sigs[1:]
+ if len(sigs) == 0 {
+ break
+ }
}
- // Nothing writes static itabs, so they are read only.
- objw.Global(i.lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
- i.lsym.Set(obj.AttrContentAddressable, true)
+ }
+ if len(sigs) != 0 {
+ base.Fatalf("incomplete itab")
}
+ // dump empty itab symbol into i.sym
+ // type itab struct {
+ // inter *interfacetype
+ // _type *_type
+ // hash uint32
+ // _ [4]byte
+ // fun [1]uintptr // variable sized
+ // }
+ o := objw.SymPtr(lsym, 0, writeType(iface), 0)
+ o = objw.SymPtr(lsym, o, writeType(typ), 0)
+ o = objw.Uint32(lsym, o, types.TypeHash(typ)) // copy of type hash
+ o += 4 // skip unused field
+ for _, fn := range entries {
+ o = objw.SymPtrWeak(lsym, o, fn, 0) // method pointer for each method
+ }
+ // Nothing writes static itabs, so they are read only.
+ objw.Global(lsym, int32(o), int16(obj.DUPOK|obj.RODATA))
+ lsym.Set(obj.AttrContentAddressable, true)
+}
+
+func WriteTabs() {
// process ptabs
if types.LocalPkg.Name == "main" && len(ptabs) > 0 {
ot := 0
type typeAndStr struct {
t *types.Type
- short string
+ short string // "short" here means NameString
regular string
}
}
// When the only difference between the types is whether
// they refer to byte or uint8, such as **byte vs **uint8,
- // the types' ShortStrings can be identical.
+ // the types' NameStrings can be identical.
// To preserve deterministic sort ordering, sort these by String().
+ //
+ // TODO(mdempsky): This all seems suspect. Using LinkString would
+ // avoid naming collisions, and there shouldn't be a reason to care
+ // about "byte" vs "uint8": they share the same runtime type
+ // descriptor anyway.
if a[i].regular != a[j].regular {
return a[i].regular < a[j].regular
}
}
}
+// NeedEmit reports whether typ is a type that we need to emit code
+// for (e.g., runtime type descriptors, method wrappers).
+func NeedEmit(typ *types.Type) bool {
+ // TODO(mdempsky): Export data should keep track of which anonymous
+ // and instantiated types were emitted, so at least downstream
+ // packages can skip re-emitting them.
+ //
+ // Perhaps we can just generalize the linker-symbol indexing to
+ // track the index of arbitrary types, not just defined types, and
+ // use its presence to detect this. The same idea would work for
+ // instantiated generic functions too.
+
+ switch sym := typ.Sym(); {
+ case sym == nil:
+ // Anonymous type; possibly never seen before or ever again.
+ // Need to emit to be safe (however, see TODO above).
+ return true
+
+ case sym.Pkg == types.LocalPkg:
+ // Local defined type; our responsibility.
+ return true
+
+ case base.Ctxt.Pkgpath == "runtime" && (sym.Pkg == types.BuiltinPkg || sym.Pkg == ir.Pkgs.Unsafe):
+ // Package runtime is responsible for including code for builtin
+ // types (predeclared and package unsafe).
+ return true
+
+ case typ.IsFullyInstantiated():
+ // Instantiated type; possibly instantiated with unique type arguments.
+ // Need to emit to be safe (however, see TODO above).
+ return true
+
+ default:
+ // Should have been emitted by an imported package.
+ return false
+ }
+}
+
// Generate a wrapper function to convert from
// a receiver of type T to a receiver of type U.
// That is,
//
// 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 !types.IsInterfaceMethod(method.Type) &&
+ (len(rcvr.RParams()) > 0 ||
+ rcvr.IsPtr() && len(rcvr.Elem().RParams()) > 0) { // TODO: right detection?
+ // Don't need dictionary if we are reaching a method (possibly via
+ // an embedded field) which is an interface method.
+ // TODO: check that we do the right thing when method is an interface method.
+ 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) {
- return lsym
+ // Except in quirks mode, unified IR creates its own wrappers.
+ // Complain loudly if it missed any.
+ if base.Debug.Unified != 0 && base.Debug.UnifiedQuirks == 0 {
+ base.FatalfAt(method.Pos, "missing wrapper for %+v (%+v, %v) / %+v / %+v", rcvr, orig, types.IsDirectIface(orig), method.Sym, newnam)
}
- // Only generate (*T).M wrappers for T.M in T's own package.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type &&
- rcvr.Elem().Sym() != nil && rcvr.Elem().Sym().Pkg != types.LocalPkg {
+ if !generic && types.Identical(rcvr, method.Type.Recv().Type) {
return lsym
}
- // Only generate I.M wrappers for I in I's own package
- // but keep doing it for error.Error (was issue #29304).
- if rcvr.IsInterface() && rcvr.Sym() != nil && rcvr.Sym().Pkg != types.LocalPkg && rcvr != types.ErrorType {
+ if !NeedEmit(rcvr) || rcvr.IsPtr() && !NeedEmit(rcvr.Elem()) {
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())
}
dot := typecheck.AddImplicitDots(ir.NewSelectorExpr(base.Pos, ir.OXDOT, nthis, method.Sym))
-
// generate call
// It's not possible to use a tail call when dynamic linking on ppc64le. The
// bad scenario is when a local call is made to the wrapper: the wrapper will
// 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 && dot.X != nthis {
+ // TODO: for now, we don't try to generate dictionary wrappers for
+ // any methods involving embedded fields, because we're not
+ // generating the needed dictionaries in instantiateMethods.
+ generic = false
+ }
+
+ 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.")
+ }
+ // Temporary fix: the wrapper for an auto-generated
+ // pointer/non-pointer receiver method should share the
+ // same dictionary as the corresponding original
+ // (user-written) method.
+ baseOrig := orig
+ if baseOrig.IsPtr() && !method.Type.Recv().Type.IsPtr() {
+ baseOrig = baseOrig.Elem()
+ } else if !baseOrig.IsPtr() && method.Type.Recv().Type.IsPtr() {
+ baseOrig = types.NewPtr(baseOrig)
+ }
+ args = append(args, getDictionary(ir.MethodSym(baseOrig, method.Sym), targs))
+ if indirect {
+ args = append(args, ir.NewStarExpr(base.Pos, dot.X))
+ } else if methodrcvr.IsPtr() && methodrcvr.Elem() == dot.X.Type() {
+ // Case where method call is via a non-pointer
+ // embedded field with a pointer method.
+ args = append(args, typecheck.NodAddrAt(base.Pos, dot.X))
+ } else {
+ args = append(args, dot.X)
+ }
+ 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)
+ // Fill-in the generic method node that was not filled in
+ // in instantiateMethod.
+ method.Nname = fn.Nname
+ } 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)
ir.CurFunc = fn
typecheck.Stmts(fn.Body)
- // Inline calls within (*T).M wrappers. This is safe because we only
- // generate those wrappers within the same compilation unit as (T).M.
- // TODO(mdempsky): Investigate why we can't enable this more generally.
- if rcvr.IsPtr() && rcvr.Elem() == method.Type.Recv().Type && rcvr.Elem().Sym() != nil {
+ if AfterGlobalEscapeAnalysis {
inline.InlineCalls(fn)
+ escape.Batch([]*ir.Func{fn}, false)
}
- escape.Batch([]*ir.Func{fn}, false)
ir.CurFunc = nil
typecheck.Target.Decls = append(typecheck.Target.Decls, fn)
return lsym
}
+// AfterGlobalEscapeAnalysis tracks whether package gc has already
+// performed the main, global escape analysis pass. If so,
+// methodWrapper takes responsibility for escape analyzing any
+// generated wrappers.
+var AfterGlobalEscapeAnalysis bool
+
var ZeroSize int64
// MarkTypeUsedInInterface marks that type t is converted to an interface.
r.Add = InterfaceMethodOffset(ityp, midx)
r.Type = objabi.R_USEIFACEMETHOD
}
+
+// getDictionary returns the dictionary for the given named generic function
+// or method, with the given type arguments.
+func getDictionary(gf *types.Sym, targs []*types.Type) ir.Node {
+ if len(targs) == 0 {
+ base.Fatalf("%s should have type arguments", gf.Name)
+ }
+
+ sym := typecheck.MakeDictName(gf, targs, true)
+
+ // Initialize the dictionary, if we haven't yet already.
+ if lsym := sym.Linksym(); len(lsym.P) == 0 {
+ base.Fatalf("Dictionary should have already been generated: %s.%s", sym.Pkg.Path, sym.Name)
+ }
+
+ // 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
+}
return "<S>"
}
- if s.Name == "_" {
- return "_"
+ q := pkgqual(s.Pkg, verb, mode)
+ if q == "" {
+ return s.Name
}
+
buf := fmtBufferPool.Get().(*bytes.Buffer)
buf.Reset()
defer fmtBufferPool.Put(buf)
- symfmt(buf, s, verb, mode)
+ buf.WriteString(q)
+ buf.WriteByte('.')
+ buf.WriteString(s.Name)
return InternString(buf.Bytes())
}
b.WriteString("<S>")
return
}
- if s.Name == "_" {
- b.WriteString("_")
- return
- }
symfmt(b, s, verb, mode)
}
func symfmt(b *bytes.Buffer, s *Sym, verb rune, mode fmtMode) {
+ if q := pkgqual(s.Pkg, verb, mode); q != "" {
+ b.WriteString(q)
+ b.WriteByte('.')
+ }
+ b.WriteString(s.Name)
+ }
+
+ // pkgqual returns the qualifier that should be used for printing
+ // symbols from the given package in the given mode.
+ // If it returns the empty string, no qualification is needed.
+ func pkgqual(pkg *Pkg, verb rune, mode fmtMode) string {
if verb != 'S' {
switch mode {
case fmtGo: // This is for the user
- if s.Pkg == BuiltinPkg || s.Pkg == LocalPkg {
- b.WriteString(s.Name)
- return
+ if pkg == BuiltinPkg || pkg == LocalPkg {
+ return ""
}
// If the name was used by multiple packages, display the full path,
- if s.Pkg.Name != "" && NumImport[s.Pkg.Name] > 1 {
- fmt.Fprintf(b, "%q.%s", s.Pkg.Path, s.Name)
- return
+ if pkg.Name != "" && NumImport[pkg.Name] > 1 {
+ return strconv.Quote(pkg.Path)
}
- b.WriteString(s.Pkg.Name)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
+ return pkg.Name
case fmtDebug:
- b.WriteString(s.Pkg.Name)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
+ return pkg.Name
case fmtTypeIDName:
// dcommontype, typehash
- b.WriteString(s.Pkg.Name)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
+ return pkg.Name
case fmtTypeID:
// (methodsym), typesym, weaksym
- b.WriteString(s.Pkg.Prefix)
- b.WriteByte('.')
- b.WriteString(s.Name)
- return
+ return pkg.Prefix
}
}
- b.WriteString(s.Name)
+ return ""
}
// Type
return tconv(t, 0, fmtGo)
}
-// ShortString generates a short description of t.
-// It is used in autogenerated method names, reflection,
-// and itab names.
-func (t *Type) ShortString() string {
+// LinkString returns an unexpanded string description of t, suitable
+// for use in link symbols. "Unexpanded" here means that the
+// description uses `"".` to qualify identifiers from the current
+// package, and "expansion" refers to the renaming step performed by
+// the linker to replace these qualifiers with proper `path/to/pkg.`
+// qualifiers.
+//
+// After expansion, the description corresponds to type identity. That
+// is, for any pair of types t1 and t2, Identical(t1, t2) and
+// expand(t1.LinkString()) == expand(t2.LinkString()) report the same
+// value.
+//
+// Within a single compilation unit, LinkString always returns the
+// same unexpanded description for identical types. Thus it's safe to
+// use as a map key to implement a type-identity-keyed map. However,
+// make sure all LinkString calls used for this purpose happen within
+// the same compile process; the string keys are not stable across
+// multiple processes.
+func (t *Type) LinkString() string {
return tconv(t, 0, fmtTypeID)
}
-// LongString generates a complete description of t.
-// It is useful for reflection,
-// or when a unique fingerprint or hash of a type is required.
-func (t *Type) LongString() string {
+// NameString generates a user-readable, mostly unique string
+// description of t. NameString always returns the same description
+// for identical types, even across compilation units.
+//
+// NameString qualifies identifiers by package name, so it has
+// collisions when different packages share the same names and
+// identifiers. It also does not distinguish function-scope defined
+// types from package-scoped defined types or from each other.
+func (t *Type) NameString() string {
return tconv(t, 0, fmtTypeIDName)
}
// Unless the 'L' flag was specified, if the type has a name, just print that name.
if verb != 'L' && t.Sym() != nil && t != Types[t.Kind()] {
- switch mode {
- case fmtTypeID, fmtTypeIDName:
- if verb == 'S' {
- if t.Vargen != 0 {
- sconv2(b, t.Sym(), 'S', mode)
- fmt.Fprintf(b, "·%d", t.Vargen)
- return
- }
- sconv2(b, t.Sym(), 'S', mode)
- return
- }
+ // Default to 'v' if verb is invalid.
+ if verb != 'S' {
+ verb = 'v'
+ }
- if mode == fmtTypeIDName {
- sconv2(b, t.Sym(), 'v', fmtTypeIDName)
- return
+ // In unified IR, function-scope defined types will have a ·N
+ // suffix embedded directly in their Name. Trim this off for
+ // non-fmtTypeID modes.
+ sym := t.Sym()
+ if mode != fmtTypeID {
+ i := len(sym.Name)
+ for i > 0 && sym.Name[i-1] >= '0' && sym.Name[i-1] <= '9' {
+ i--
}
-
- if t.Sym().Pkg == LocalPkg && t.Vargen != 0 {
- sconv2(b, t.Sym(), 'v', mode)
- fmt.Fprintf(b, "·%d", t.Vargen)
- return
+ const dot = "·"
+ if i >= len(dot) && sym.Name[i-len(dot):i] == dot {
+ sym = &Sym{Pkg: sym.Pkg, Name: sym.Name[:i-len(dot)]}
}
}
-
- sconv2(b, t.Sym(), 'v', mode)
+ sconv2(b, sym, verb, mode)
+
+ // TODO(mdempsky): Investigate including Vargen in fmtTypeIDName
+ // output too. It seems like it should, but that mode is currently
+ // used in string representation used by reflection, which is
+ // user-visible and doesn't expect this.
+ if mode == fmtTypeID && t.Vargen != 0 {
+ fmt.Fprintf(b, "·%d", t.Vargen)
+ }
return
}
// TypeHash computes a hash value for type t to use in type switch statements.
func TypeHash(t *Type) uint32 {
- p := t.LongString()
+ p := t.NameString()
// Using MD5 is overkill, but reduces accidental collisions.
h := md5.Sum([]byte(p))
package reflect
import (
+ "internal/goarch"
"internal/unsafeheader"
"strconv"
"sync"
// See https://golang.org/issue/4876 for more details.
/*
- * These data structures are known to the compiler (../../cmd/internal/gc/reflect.go).
+ * These data structures are known to the compiler (../../cmd/internal/reflectdata/reflect.go).
* A few are known to ../runtime/type.go to convey to debuggers.
* They are also known to ../runtime/type.go.
*/
// available in the memory directly following the rtype value.
//
// tflag values must be kept in sync with copies in:
- // cmd/compile/internal/gc/reflect.go
+ // cmd/compile/internal/reflectdata/reflect.go
// cmd/link/internal/ld/decodesym.go
// runtime/type.go
type tflag uint8
// Make a map type.
// Note: flag values must match those used in the TMAP case
- // in ../cmd/compile/internal/gc/reflect.go:writeType.
+ // in ../cmd/compile/internal/reflectdata/reflect.go:writeType.
var imap interface{} = (map[unsafe.Pointer]unsafe.Pointer)(nil)
mt := **(**mapType)(unsafe.Pointer(&imap))
mt.str = resolveReflectName(newName(s, "", false))
}
mt.flags = 0
if ktyp.size > maxKeySize {
- mt.keysize = uint8(ptrSize)
+ mt.keysize = uint8(goarch.PtrSize)
mt.flags |= 1 // indirect key
} else {
mt.keysize = uint8(ktyp.size)
}
if etyp.size > maxValSize {
- mt.valuesize = uint8(ptrSize)
+ mt.valuesize = uint8(goarch.PtrSize)
mt.flags |= 2 // indirect value
} else {
mt.valuesize = uint8(etyp.size)
var ptrdata uintptr
var overflowPad uintptr
- size := bucketSize*(1+ktyp.size+etyp.size) + overflowPad + ptrSize
+ size := bucketSize*(1+ktyp.size+etyp.size) + overflowPad + goarch.PtrSize
if size&uintptr(ktyp.align-1) != 0 || size&uintptr(etyp.align-1) != 0 {
panic("reflect: bad size computation in MapOf")
}
if ktyp.ptrdata != 0 || etyp.ptrdata != 0 {
- nptr := (bucketSize*(1+ktyp.size+etyp.size) + ptrSize) / ptrSize
+ nptr := (bucketSize*(1+ktyp.size+etyp.size) + goarch.PtrSize) / goarch.PtrSize
mask := make([]byte, (nptr+7)/8)
- base := bucketSize / ptrSize
+ base := bucketSize / goarch.PtrSize
if ktyp.ptrdata != 0 {
emitGCMask(mask, base, ktyp, bucketSize)
}
- base += bucketSize * ktyp.size / ptrSize
+ base += bucketSize * ktyp.size / goarch.PtrSize
if etyp.ptrdata != 0 {
emitGCMask(mask, base, etyp, bucketSize)
}
- base += bucketSize * etyp.size / ptrSize
- base += overflowPad / ptrSize
+ base += bucketSize * etyp.size / goarch.PtrSize
+ base += overflowPad / goarch.PtrSize
word := base
mask[word/8] |= 1 << (word % 8)
gcdata = &mask[0]
- ptrdata = (word + 1) * ptrSize
+ ptrdata = (word + 1) * goarch.PtrSize
// overflow word must be last
if ptrdata != size {
}
b := &rtype{
- align: ptrSize,
+ align: goarch.PtrSize,
size: size,
kind: uint8(Struct),
ptrdata: ptrdata,
if typ.kind&kindGCProg != 0 {
panic("reflect: unexpected GC program")
}
- ptrs := typ.ptrdata / ptrSize
- words := typ.size / ptrSize
+ ptrs := typ.ptrdata / goarch.PtrSize
+ words := typ.size / goarch.PtrSize
mask := typ.gcSlice(0, (ptrs+7)/8)
for j := uintptr(0); j < ptrs; j++ {
if (mask[j/8]>>(j%8))&1 != 0 {
}
// Element is small with pointer mask; use as literal bits.
- ptrs := typ.ptrdata / ptrSize
+ ptrs := typ.ptrdata / goarch.PtrSize
mask := typ.gcSlice(0, (ptrs+7)/8)
// Emit 120-bit chunks of full bytes (max is 127 but we avoid using partial bytes).
}
// Pad to start of this field with zeros.
if ft.offset() > off {
- n := (ft.offset() - off) / ptrSize
+ n := (ft.offset() - off) / goarch.PtrSize
prog = append(prog, 0x01, 0x00) // emit a 0 bit
if n > 1 {
prog = append(prog, 0x81) // repeat previous bit
// typeptrdata returns the length in bytes of the prefix of t
// containing pointer data. Anything after this offset is scalar data.
- // keep in sync with ../cmd/compile/internal/gc/reflect.go
+ // keep in sync with ../cmd/compile/internal/reflectdata/reflect.go
func typeptrdata(t *rtype) uintptr {
switch t.Kind() {
case Struct:
}
}
- // See cmd/compile/internal/gc/reflect.go for derivation of constant.
+ // See cmd/compile/internal/reflectdata/reflect.go for derivation of constant.
const maxPtrmaskBytes = 2048
// ArrayOf returns the array type with the given length and element type.
array.gcdata = typ.gcdata
array.ptrdata = typ.ptrdata
- case typ.kind&kindGCProg == 0 && array.size <= maxPtrmaskBytes*8*ptrSize:
+ case typ.kind&kindGCProg == 0 && array.size <= maxPtrmaskBytes*8*goarch.PtrSize:
// Element is small with pointer mask; array is still small.
// Create direct pointer mask by turning each 1 bit in elem
// into length 1 bits in larger mask.
- mask := make([]byte, (array.ptrdata/ptrSize+7)/8)
+ mask := make([]byte, (array.ptrdata/goarch.PtrSize+7)/8)
emitGCMask(mask, 0, typ, array.len)
array.gcdata = &mask[0]
prog := []byte{0, 0, 0, 0} // will be length of prog
prog = appendGCProg(prog, typ)
// Pad from ptrdata to size.
- elemPtrs := typ.ptrdata / ptrSize
- elemWords := typ.size / ptrSize
+ elemPtrs := typ.ptrdata / goarch.PtrSize
+ elemWords := typ.size / goarch.PtrSize
if elemPtrs < elemWords {
// Emit literal 0 bit, then repeat as needed.
prog = append(prog, 0x01, 0x00)
// build dummy rtype holding gc program
x := &rtype{
- align: ptrSize,
+ align: goarch.PtrSize,
// Don't add spill space here; it's only necessary in
// reflectcall's frame, not in the allocated frame.
// TODO(mknyszek): Remove this comment when register
// spill space in the frame is no longer required.
- size: align(abi.retOffset+abi.ret.stackBytes, ptrSize),
- ptrdata: uintptr(abi.stackPtrs.n) * ptrSize,
+ size: align(abi.retOffset+abi.ret.stackBytes, goarch.PtrSize),
+ ptrdata: uintptr(abi.stackPtrs.n) * goarch.PtrSize,
}
if abi.stackPtrs.n > 0 {
x.gcdata = &abi.stackPtrs.data[0]
switch Kind(t.kind & kindMask) {
case Chan, Func, Map, Ptr, Slice, String, UnsafePointer:
// 1 pointer at start of representation
- for bv.n < uint32(offset/uintptr(ptrSize)) {
+ for bv.n < uint32(offset/uintptr(goarch.PtrSize)) {
bv.append(0)
}
bv.append(1)
case Interface:
// 2 pointers
- for bv.n < uint32(offset/uintptr(ptrSize)) {
+ for bv.n < uint32(offset/uintptr(goarch.PtrSize)) {
bv.append(0)
}
bv.append(1)