Some type assertions of the form _, ok := i.(T) allow efficient inlining.
Such type assertions commonly show up in type switches.
For example, with this optimization, using 6g, the length of
encoding/binary's intDataSize function shrinks from 2224 to 1728 bytes (-22%).
benchmark old ns/op new ns/op delta
BenchmarkAssertI2E2Blank 4.67 0.82 -82.44%
BenchmarkAssertE2T2Blank 4.38 0.83 -81.05%
BenchmarkAssertE2E2Blank 3.88 0.83 -78.61%
BenchmarkAssertE2E2 14.2 14.4 +1.41%
BenchmarkAssertE2T2 10.3 10.4 +0.97%
BenchmarkAssertI2E2 13.4 13.3 -0.75%
Change-Id: Ie9798c3e85432bb8e0f2c723afc376e233639df7
Reviewed-on: https://go-review.googlesource.com/7697
Reviewed-by: Keith Randall <khr@golang.org>
oktype = ok.Type
}
+ fromKind := type2IET(from.Type)
+ toKind := type2IET(t)
+
+ // Avoid runtime calls in a few cases of the form _, ok := i.(T).
+ // This is faster and shorter and allows the corresponding assertX2X2
+ // routines to skip nil checks on their last argument.
+ if isblank(n.List.N) {
+ var fast *Node
+ switch {
+ case fromKind == "E" && toKind == "T":
+ tab := Nod(OITAB, from, nil) // type:eface::tab:iface
+ typ := Nod(OCONVNOP, typename(t), nil)
+ typ.Type = Ptrto(Types[TUINTPTR])
+ fast = Nod(OEQ, tab, typ)
+ case fromKind == "I" && toKind == "E",
+ fromKind == "E" && toKind == "E":
+ tab := Nod(OITAB, from, nil)
+ fast = Nod(ONE, tab, nodnil())
+ }
+ if fast != nil {
+ n = Nod(OAS, ok, fast)
+ typecheck(&n, Etop)
+ goto ret
+ }
+ }
+
var resptr *Node // &res
if isblank(n.List.N) {
resptr = nodnil()
}
resptr.Etype = 1 // addr does not escape
- buf := "assert" + type2IET(from.Type) + "2" + type2IET(t) + "2"
+ buf := "assert" + fromKind + "2" + toKind + "2"
fn := syslook(buf, 1)
substArgTypes(fn, from.Type, t)
call := mkcall1(fn, oktype, init, typename(t), from, resptr)
}
}
+// The compiler ensures that r is non-nil.
func assertE2T2(t *_type, e interface{}, r unsafe.Pointer) bool {
ep := (*eface)(unsafe.Pointer(&e))
if ep._type != t {
- if r != nil {
- memclr(r, uintptr(t.size))
- }
+ memclr(r, uintptr(t.size))
return false
}
- if r != nil {
- if isDirectIface(t) {
- writebarrierptr((*uintptr)(r), uintptr(ep.data))
- } else {
- typedmemmove(t, r, ep.data)
- }
+ if isDirectIface(t) {
+ writebarrierptr((*uintptr)(r), uintptr(ep.data))
+ } else {
+ typedmemmove(t, r, ep.data)
}
return true
}
return
}
+// The compiler ensures that r is non-nil.
func assertI2E2(inter *interfacetype, i fInterface, r *interface{}) bool {
ip := (*iface)(unsafe.Pointer(&i))
tab := ip.tab
if tab == nil {
return false
}
- if r != nil {
- rp := (*eface)(unsafe.Pointer(r))
- rp._type = tab._type
- rp.data = ip.data
- }
+ rp := (*eface)(unsafe.Pointer(r))
+ rp._type = tab._type
+ rp.data = ip.data
return true
}
*r = e
}
+// The compiler ensures that r is non-nil.
func assertE2E2(inter *interfacetype, e interface{}, r *interface{}) bool {
ep := (*eface)(unsafe.Pointer(&e))
if ep._type == nil {
- if r != nil {
- *r = nil
- }
+ *r = nil
return false
}
- if r != nil {
- *r = e
- }
+ *r = e
return true
}
ts TS
tm TM
tl TL
+ ok bool
)
// Issue 9370
e_ = e
}
}
+
+func BenchmarkAssertE2T2(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ tm, ok = e.(TM)
+ }
+}
+
+func BenchmarkAssertE2T2Blank(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ _, ok = e.(TM)
+ }
+}
+
+func BenchmarkAssertI2E2(b *testing.B) {
+ i1 = tm
+ for i := 0; i < b.N; i++ {
+ e, ok = i1.(interface{})
+ }
+}
+
+func BenchmarkAssertI2E2Blank(b *testing.B) {
+ i1 = tm
+ for i := 0; i < b.N; i++ {
+ _, ok = i1.(interface{})
+ }
+}
+
+func BenchmarkAssertE2E2(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ e_, ok = e.(interface{})
+ }
+}
+
+func BenchmarkAssertE2E2Blank(b *testing.B) {
+ e = tm
+ for i := 0; i < b.N; i++ {
+ _, ok = e.(interface{})
+ }
+}