From 9fc09d2ffb4384579998b5046a1d4aee32c5ca17 Mon Sep 17 00:00:00 2001 From: Matthew Dempsky Date: Thu, 1 Sep 2022 16:58:15 -0700 Subject: [PATCH] cmd/compile/internal/noder: optimize itabs section of runtime dicts Currently, the itabs section for runtime dictionaries includes its own redundant *runtime._type pointers for typ and iface, which were sometimes necessary. This simplified the initial implementation, but is a little wasteful of space when the same type or interface appeared across multiple (typ, iface) pairs. This CL instead reuses the pointers from the rtypes section. Change-Id: I48448515c319c0403c1a8e7706794d443176f0a4 Reviewed-on: https://go-review.googlesource.com/c/go/+/427754 Reviewed-by: Cuong Manh Le Run-TryBot: Matthew Dempsky Reviewed-by: Keith Randall TryBot-Result: Gopher Robot Reviewed-by: Keith Randall --- src/cmd/compile/internal/noder/reader.go | 66 +++++++++++------------- src/cmd/compile/internal/noder/writer.go | 12 +++-- 2 files changed, 39 insertions(+), 39 deletions(-) diff --git a/src/cmd/compile/internal/noder/reader.go b/src/cmd/compile/internal/noder/reader.go index c37f49c1ea..fb9df3284f 100644 --- a/src/cmd/compile/internal/noder/reader.go +++ b/src/cmd/compile/internal/noder/reader.go @@ -1386,27 +1386,18 @@ func (pr *pkgReader) dictNameOf(dict *readerDict) *ir.Name { reflectdata.MarkTypeUsedInInterface(typ, lsym) } - // For each (typ, iface) pair, we write *runtime._type pointers - // for typ and iface, as well as the *runtime.itab pointer for the - // pair. This is wasteful, but it simplifies worrying about tricky - // cases like instantiating type parameters with interface types. - // - // TODO(mdempsky): Add the needed *runtime._type pointers into the - // rtypes section above instead, and omit itabs entries when we - // statically know it won't be needed. + // For each (typ, iface) pair, we write the *runtime.itab pointer + // for the pair. For pairs that don't actually require an itab + // (i.e., typ is an interface, or iface is an empty interface), we + // write a nil pointer instead. This is wasteful, but rare in + // practice (e.g., instantiating a type parameter with an interface + // type). assertOffset("itabs", dict.itabsOffset()) for _, info := range dict.itabs { typ := pr.typIdx(info.typ, dict, true) iface := pr.typIdx(info.iface, dict, true) - if !iface.IsInterface() { - ot += 3 * types.PtrSize - continue - } - - ot = objw.SymPtr(lsym, ot, reflectdata.TypeLinksym(typ), 0) - ot = objw.SymPtr(lsym, ot, reflectdata.TypeLinksym(iface), 0) - if !typ.IsInterface() && !iface.IsEmptyInterface() { + if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() { ot = objw.SymPtr(lsym, ot, reflectdata.ITabLsym(typ, iface), 0) } else { ot += types.PtrSize @@ -1452,7 +1443,7 @@ func (dict *readerDict) itabsOffset() int { // numWords returns the total number of words that comprise dict's // runtime dictionary variable. func (dict *readerDict) numWords() int64 { - return int64(dict.itabsOffset() + 3*len(dict.itabs)) + return int64(dict.itabsOffset() + len(dict.itabs)) } // varType returns the type of dict's runtime dictionary variable. @@ -3152,28 +3143,35 @@ func (r *reader) varDictIndex(name *ir.Name) { } } +// itab returns a (typ, iface) pair of types. +// +// typRType and ifaceRType are expressions that evaluate to the +// *runtime._type for typ and iface, respectively. +// +// If typ is a concrete type and iface is a non-empty interface type, +// then itab is an expression that evaluates to the *runtime.itab for +// the pair. Otherwise, itab is nil. func (r *reader) itab(pos src.XPos) (typ *types.Type, typRType ir.Node, iface *types.Type, ifaceRType ir.Node, itab ir.Node) { - if r.Bool() { // derived types - idx := r.Len() - info := r.dict.itabs[idx] - typ = r.p.typIdx(info.typ, r.dict, true) - typRType = r.rttiWord(pos, r.dict.itabsOffset()+3*idx) - iface = r.p.typIdx(info.iface, r.dict, true) - ifaceRType = r.rttiWord(pos, r.dict.itabsOffset()+3*idx+1) - itab = r.rttiWord(pos, r.dict.itabsOffset()+3*idx+2) - return + typ, typRType = r.rtype0(pos) + iface, ifaceRType = r.rtype0(pos) + + idx := -1 + if r.Bool() { + idx = r.Len() } - typ = r.typ() - iface = r.typ() - if iface.IsInterface() { - typRType = reflectdata.TypePtrAt(pos, typ) - ifaceRType = reflectdata.TypePtrAt(pos, iface) - if !typ.IsInterface() && !iface.IsEmptyInterface() { + if !typ.IsInterface() && iface.IsInterface() && !iface.IsEmptyInterface() { + if idx >= 0 { + itab = r.rttiWord(pos, r.dict.itabsOffset()+idx) + } else { + base.AssertfAt(!typ.HasShape(), pos, "%v is a shape type", typ) + base.AssertfAt(!iface.HasShape(), pos, "%v is a shape type", iface) + lsym := reflectdata.ITabLsym(typ, iface) itab = typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8]) } } + return } @@ -3215,9 +3213,7 @@ func (r *reader) exprType() ir.Node { if r.Bool() { typ, rtype, _, _, itab = r.itab(pos) - if typ.IsInterface() { - itab = nil - } else { + if !typ.IsInterface() { rtype = nil // TODO(mdempsky): Leave set? } } else { diff --git a/src/cmd/compile/internal/noder/writer.go b/src/cmd/compile/internal/noder/writer.go index a03593e743..0b1d41d750 100644 --- a/src/cmd/compile/internal/noder/writer.go +++ b/src/cmd/compile/internal/noder/writer.go @@ -2183,9 +2183,13 @@ func (w *writer) exprs(exprs []syntax.Expr) { func (w *writer) rtype(typ types2.Type) { typ = types2.Default(typ) + info := w.p.typIdx(typ, w.dict) + w.rtypeInfo(info) +} + +func (w *writer) rtypeInfo(info typeInfo) { w.Sync(pkgbits.SyncRType) - info := w.p.typIdx(typ, w.dict) if w.Bool(info.derived) { w.Len(w.dict.rtypeIdx(info)) } else { @@ -2218,11 +2222,11 @@ func (w *writer) itab(typ, iface types2.Type) { typInfo := w.p.typIdx(typ, w.dict) ifaceInfo := w.p.typIdx(iface, w.dict) + + w.rtypeInfo(typInfo) + w.rtypeInfo(ifaceInfo) if w.Bool(typInfo.derived || ifaceInfo.derived) { w.Len(w.dict.itabIdx(typInfo, ifaceInfo)) - } else { - w.typInfo(typInfo) - w.typInfo(ifaceInfo) } } -- 2.48.1