]> Cypherpunks repositories - gostls13.git/commitdiff
cmd/compile: optimize non-empty-interface type conversions
authorKeith Randall <khr@golang.org>
Wed, 4 Jan 2017 00:15:38 +0000 (16:15 -0800)
committerKeith Randall <khr@golang.org>
Mon, 13 Feb 2017 18:16:31 +0000 (18:16 +0000)
When doing i.(T) for non-empty-interface i and concrete type T,
there's no need to read the type out of the itab. Just compare the
itab to the itab we expect for that interface/type pair.

Also optimize type switches by putting the type hash of the
concrete type in the itab. That way we don't need to load the
type pointer out of the itab.

Update #18492

Change-Id: I49e280a21e5687e771db5b8a56b685291ac168ce
Reviewed-on: https://go-review.googlesource.com/34810
Run-TryBot: Keith Randall <khr@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Josh Bleecher Snyder <josharian@gmail.com>
Reviewed-by: David Chase <drchase@google.com>
src/cmd/compile/internal/gc/builtin.go
src/cmd/compile/internal/gc/builtin/runtime.go
src/cmd/compile/internal/gc/go.go
src/cmd/compile/internal/gc/pgen.go
src/cmd/compile/internal/gc/reflect.go
src/cmd/compile/internal/gc/ssa.go
src/cmd/compile/internal/gc/swt.go
src/runtime/iface.go
src/runtime/plugin.go
src/runtime/runtime2.go

index 70200c624b4bafe81e88eb70e813ea00d5ba035a..47dcf0bb4bcdcc3fc8c49f9f21545a9520e2d166 100644 (file)
@@ -54,7 +54,8 @@ var runtimeDecls = [...]struct {
        {"assertE2I2", funcTag, 54},
        {"assertI2I", funcTag, 52},
        {"assertI2I2", funcTag, 54},
-       {"panicdottype", funcTag, 55},
+       {"panicdottypeE", funcTag, 55},
+       {"panicdottypeI", funcTag, 55},
        {"panicnildottype", funcTag, 56},
        {"ifaceeq", funcTag, 57},
        {"efaceeq", funcTag, 57},
index e9d41a3095f02a98362e736e113c3fd74bc22ef6..618f1c421e24ae19b67a4b93602068d8e3b39a68 100644 (file)
@@ -67,7 +67,8 @@ func assertE2I(typ *byte, iface any) (ret any)
 func assertE2I2(typ *byte, iface any) (ret any, b bool)
 func assertI2I(typ *byte, iface any) (ret any)
 func assertI2I2(typ *byte, iface any) (ret any, b bool)
-func panicdottype(have, want, iface *byte)
+func panicdottypeE(have, want, iface *byte)
+func panicdottypeI(have, want, iface *byte)
 func panicnildottype(want *byte)
 
 func ifaceeq(i1 any, i2 any) (ret bool)
index c6fcfd73470b189d63a30cf089f45911ef848203..9e5d1843d0fd68bd8c16398cd104745a499474ff 100644 (file)
@@ -376,7 +376,8 @@ var (
        panicslice,
        panicdivide,
        growslice,
-       panicdottype,
+       panicdottypeE,
+       panicdottypeI,
        panicnildottype,
        assertE2I,
        assertE2I2,
index 1acbbf3b1e41367c0740523d059edc161351cb31..e612cf6a334ecbc1dc8565f9f0baeb80151d5d91 100644 (file)
@@ -306,7 +306,8 @@ func compile(fn *Node) {
                panicslice = Sysfunc("panicslice")
                panicdivide = Sysfunc("panicdivide")
                growslice = Sysfunc("growslice")
-               panicdottype = Sysfunc("panicdottype")
+               panicdottypeE = Sysfunc("panicdottypeE")
+               panicdottypeI = Sysfunc("panicdottypeI")
                panicnildottype = Sysfunc("panicnildottype")
                assertE2I = Sysfunc("assertE2I")
                assertE2I2 = Sysfunc("assertE2I2")
index 9d744c6a969f1ab0378cfcaf5e022a536ea237c8..b6bda3c86b091cb4e90f6e61298b1316f8f47be7 100644 (file)
@@ -1411,13 +1411,17 @@ func dumptypestructs() {
                //   inter  *interfacetype
                //   _type  *_type
                //   link   *itab
-               //   bad    int32
-               //   unused int32
+               //   hash   uint32
+               //   bad    bool
+               //   inhash bool
+               //   unused [2]byte
                //   fun    [1]uintptr // variable sized
                // }
                o := dsymptr(i.sym, 0, dtypesym(i.itype), 0)
                o = dsymptr(i.sym, o, dtypesym(i.t), 0)
-               o += Widthptr + 8                      // skip link/bad/inhash fields
+               o += Widthptr                          // skip link field
+               o = duint32(i.sym, o, typehash(i.t))   // copy of type hash
+               o += 4                                 // skip bad/inhash/unused fields
                o += len(imethods(i.itype)) * Widthptr // skip fun method pointers
                // at runtime the itab will contain pointers to types, other itabs and
                // method functions. None are allocated on heap, so we can use obj.NOPTR.
index ca198575d13c4a8a27bc1fb22f64941422025299..6871a9eed85b5dd592471b4aa69d7cff805c6edc 100644 (file)
@@ -4013,48 +4013,6 @@ func (s *state) floatToUint(cvttab *f2uCvtTab, n *Node, x *ssa.Value, ft, tt *Ty
        return s.variable(n, n.Type)
 }
 
-// ifaceType returns the value for the word containing the type.
-// t is the type of the interface expression.
-// v is the corresponding value.
-func (s *state) ifaceType(t *Type, v *ssa.Value) *ssa.Value {
-       byteptr := ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
-
-       if t.IsEmptyInterface() {
-               // Have eface. The type is the first word in the struct.
-               return s.newValue1(ssa.OpITab, byteptr, v)
-       }
-
-       // Have iface.
-       // The first word in the struct is the itab.
-       // If the itab is nil, return 0.
-       // Otherwise, the second word in the itab is the type.
-
-       tab := s.newValue1(ssa.OpITab, byteptr, v)
-       s.vars[&typVar] = tab
-       isnonnil := s.newValue2(ssa.OpNeqPtr, Types[TBOOL], tab, s.constNil(byteptr))
-       b := s.endBlock()
-       b.Kind = ssa.BlockIf
-       b.SetControl(isnonnil)
-       b.Likely = ssa.BranchLikely
-
-       bLoad := s.f.NewBlock(ssa.BlockPlain)
-       bEnd := s.f.NewBlock(ssa.BlockPlain)
-
-       b.AddEdgeTo(bLoad)
-       b.AddEdgeTo(bEnd)
-       bLoad.AddEdgeTo(bEnd)
-
-       s.startBlock(bLoad)
-       off := s.newValue1I(ssa.OpOffPtr, byteptr, int64(Widthptr), tab)
-       s.vars[&typVar] = s.newValue2(ssa.OpLoad, byteptr, off, s.mem())
-       s.endBlock()
-
-       s.startBlock(bEnd)
-       typ := s.variable(&typVar, byteptr)
-       delete(s.vars, &typVar)
-       return typ
-}
-
 // dottype generates SSA for a type assertion node.
 // commaok indicates whether to panic or return a bool.
 // If commaok is false, resok will be nil.
@@ -4157,11 +4115,18 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
 
        // Converting to a concrete type.
        direct := isdirectiface(n.Type)
-       typ := s.ifaceType(n.Left.Type, iface) // actual concrete type of input interface
-
+       itab := s.newValue1(ssa.OpITab, byteptr, iface) // type word of interface
        if Debug_typeassert > 0 {
                Warnl(n.Pos, "type assertion inlined")
        }
+       var targetITab *ssa.Value
+       if n.Left.Type.IsEmptyInterface() {
+               // Looking for pointer to target type.
+               targetITab = target
+       } else {
+               // Looking for pointer to itab for target type and source interface.
+               targetITab = s.expr(itabname(n.Type, n.Left.Type))
+       }
 
        var tmp *Node       // temporary for use with large types
        var addr *ssa.Value // address of tmp
@@ -4173,9 +4138,7 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
                s.vars[&memVar] = s.newValue1A(ssa.OpVarDef, ssa.TypeMem, tmp, s.mem())
        }
 
-       // TODO:  If we have a nonempty interface and its itab field is nil,
-       // then this test is redundant and ifaceType should just branch directly to bFail.
-       cond := s.newValue2(ssa.OpEqPtr, Types[TBOOL], typ, target)
+       cond := s.newValue2(ssa.OpEqPtr, Types[TBOOL], itab, targetITab)
        b := s.endBlock()
        b.Kind = ssa.BlockIf
        b.SetControl(cond)
@@ -4190,7 +4153,11 @@ func (s *state) dottype(n *Node, commaok bool) (res, resok *ssa.Value) {
                // on failure, panic by calling panicdottype
                s.startBlock(bFail)
                taddr := s.newValue1A(ssa.OpAddr, byteptr, &ssa.ExternSymbol{Typ: byteptr, Sym: Linksym(typenamesym(n.Left.Type))}, s.sb)
-               s.rtcall(panicdottype, false, nil, typ, target, taddr)
+               if n.Left.Type.IsEmptyInterface() {
+                       s.rtcall(panicdottypeE, false, nil, itab, target, taddr)
+               } else {
+                       s.rtcall(panicdottypeI, false, nil, itab, target, taddr)
+               }
 
                // on success, return data from interface
                s.startBlock(bOk)
index 8f6ffa269053778b1e1ee31fce40ceacdbe3d9aa..f48894d77bbc7c5a8a785769830345acbd8220b0 100644 (file)
@@ -729,11 +729,13 @@ func (s *typeSwitch) walk(sw *Node) {
        // Use a similar strategy for non-empty interfaces.
 
        // Get interface descriptor word.
-       typ := nod(OITAB, s.facename, nil)
+       // For empty interfaces this will be the type.
+       // For non-empty interfaces this will be the itab.
+       itab := nod(OITAB, s.facename, nil)
 
        // Check for nil first.
        i := nod(OIF, nil, nil)
-       i.Left = nod(OEQ, typ, nodnil())
+       i.Left = nod(OEQ, itab, nodnil())
        if clauses.niljmp != nil {
                // Do explicit nil case right here.
                i.Nbody.Set1(clauses.niljmp)
@@ -749,16 +751,16 @@ func (s *typeSwitch) walk(sw *Node) {
        i.Left = typecheck(i.Left, Erv)
        cas = append(cas, i)
 
-       if !cond.Right.Type.IsEmptyInterface() {
-               // Load type from itab.
-               typ = itabType(typ)
-       }
-       // Load hash from type.
-       h := nodSym(ODOTPTR, typ, nil)
+       // Load hash from type or itab.
+       h := nodSym(ODOTPTR, itab, nil)
        h.Type = Types[TUINT32]
        h.Typecheck = 1
-       h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
-       h.Bounded = true                // guaranteed not to fault
+       if cond.Right.Type.IsEmptyInterface() {
+               h.Xoffset = int64(2 * Widthptr) // offset of hash in runtime._type
+       } else {
+               h.Xoffset = int64(3 * Widthptr) // offset of hash in runtime.itab
+       }
+       h.Bounded = true // guaranteed not to fault
        a = nod(OAS, s.hashname, h)
        a = typecheck(a, Etop)
        cas = append(cas, a)
index b5c31a301d6f93424ade2b8020fb13bfc13efab4..f043724a56e94eeb777cf4771e10129f3f95df6d 100644 (file)
@@ -53,7 +53,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
                }
                for m = (*itab)(atomic.Loadp(unsafe.Pointer(&hash[h]))); m != nil; m = m.link {
                        if m.inter == inter && m._type == typ {
-                               if m.bad != 0 {
+                               if m.bad {
                                        if !canfail {
                                                // this can only happen if the conversion
                                                // was already done once using the , ok form
@@ -78,7 +78,7 @@ func getitab(inter *interfacetype, typ *_type, canfail bool) *itab {
        m._type = typ
        additab(m, true, canfail)
        unlock(&ifaceLock)
-       if m.bad != 0 {
+       if m.bad {
                return nil
        }
        return m
@@ -130,7 +130,7 @@ func additab(m *itab, locked, canfail bool) {
                        }
                        panic(&TypeAssertionError{"", typ.string(), inter.typ.string(), iname})
                }
-               m.bad = 1
+               m.bad = true
                break
        nextimethod:
        }
@@ -139,7 +139,7 @@ func additab(m *itab, locked, canfail bool) {
        }
        h := itabhash(inter, typ)
        m.link = hash[h]
-       m.inhash = 1
+       m.inhash = true
        atomicstorep(unsafe.Pointer(&hash[h]), unsafe.Pointer(m))
 }
 
@@ -152,7 +152,7 @@ func itabsinit() {
                        // and thanks to the way global symbol resolution works, the
                        // pointed-to itab may already have been inserted into the
                        // global 'hash'.
-                       if i.inhash == 0 {
+                       if !i.inhash {
                                additab(i, true, false)
                        }
                }
@@ -160,11 +160,11 @@ func itabsinit() {
        unlock(&ifaceLock)
 }
 
-// panicdottype is called when doing an i.(T) conversion and the conversion fails.
+// panicdottypeE is called when doing an e.(T) conversion and the conversion fails.
 // have = the dynamic type we have.
 // want = the static type we're trying to convert to.
 // iface = the static type we're converting from.
-func panicdottype(have, want, iface *_type) {
+func panicdottypeE(have, want, iface *_type) {
        haveString := ""
        if have != nil {
                haveString = have.string()
@@ -172,6 +172,16 @@ func panicdottype(have, want, iface *_type) {
        panic(&TypeAssertionError{iface.string(), haveString, want.string(), ""})
 }
 
+// panicdottypeI is called when doing an i.(T) conversion and the conversion fails.
+// Same args as panicdottypeE, but "have" is the dynamic itab we have.
+func panicdottypeI(have *itab, want, iface *_type) {
+       var t *_type
+       if have != nil {
+               t = have._type
+       }
+       panicdottypeE(t, want, iface)
+}
+
 // panicnildottype is called when doing a i.(T) conversion and the interface i is nil.
 // want = the static type we're trying to convert to.
 func panicnildottype(want *_type) {
index 8edb29c9fea5604febd823da1667b50f4531057b..ea246509ccbe7d949286f485909b26ce73a16bb3 100644 (file)
@@ -56,7 +56,7 @@ func plugin_lastmoduleinit() (path string, syms map[string]interface{}, mismatch
 
        lock(&ifaceLock)
        for _, i := range md.itablinks {
-               if i.inhash == 0 {
+               if !i.inhash {
                        additab(i, true, false)
                }
        }
index 9cb2b85f33d5d5a7e0706f8562099a2a79f197ab..8cf13e96d8d5772a09cc19db3203e6e9ee2997d0 100644 (file)
@@ -644,8 +644,10 @@ type itab struct {
        inter  *interfacetype
        _type  *_type
        link   *itab
-       bad    int32
-       inhash int32      // has this itab been added to hash?
+       hash   uint32 // copy of _type.hash. Used for type switches.
+       bad    bool   // type does not implement interface
+       inhash bool   // has this itab been added to hash?
+       unused [2]byte
        fun    [1]uintptr // variable sized
 }