These can be implemented with just a compare and a move instruction.
Do so, avoiding the overhead of a call into the runtime.
These assertions are a significant cost in Go code that uses interface{}
as a safe alternative to C's void* (or unsafe.Pointer), such as the
current version of the Go compiler.
*T here includes pointer to T but also any Go type represented as
a single pointer (chan, func, map). It does not include [1]*T or struct{*int}.
That requires more work in other parts of the compiler; there is a TODO.
Change-Id: I7ff681c20d2c3eb6ad11dd7b3a37b1f3dda23965
Reviewed-on: https://go-review.googlesource.com/7862
Reviewed-by: Rob Pike <r@golang.org>
"func @\"\".assertI2I2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" +
"func @\"\".assertI2T (@\"\".typ·1 *byte, @\"\".iface·2 any, @\"\".ret·3 *any)\n" +
"func @\"\".assertI2T2 (@\"\".typ·2 *byte, @\"\".iface·3 any, @\"\".ret·4 *any) (? bool)\n" +
+ "func @\"\".panicdottype (@\"\".have·1 *byte, @\"\".want·2 *byte, @\"\".iface·3 *byte)\n" +
"func @\"\".ifaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" +
"func @\"\".efaceeq (@\"\".i1·2 any, @\"\".i2·3 any) (@\"\".ret·1 bool)\n" +
"func @\"\".ifacethash (@\"\".i1·2 any) (@\"\".ret·1 uint32)\n" +
func assertI2I2(typ *byte, iface any, ret *any) bool
func assertI2T(typ *byte, iface any, ret *any)
func assertI2T2(typ *byte, iface any, ret *any) bool
+func panicdottype(have, want, iface *byte)
func ifaceeq(i1 any, i2 any) (ret bool)
func efaceeq(i1 any, i2 any) (ret bool)
Cgen_eface(n, res)
}
return
+
+ case ODOTTYPE:
+ cgen_dottype(n, res, nil)
+ return
}
if n.Ullman >= UINF {
Agenr(nl, &n3, res)
} else {
if nl.Addable == 0 {
+ if res != nil && res.Op == OREGISTER { // give up res, which we don't need yet.
+ Regfree(res)
+ }
+
// igen will need an addressable node.
var tmp2 Node
Tempname(&tmp2, nl.Type)
-
Cgen(nl, &tmp2)
nl = &tmp2
+
+ if res != nil && res.Op == OREGISTER { // reacquire res
+ Regrealloc(res)
+ }
}
Igen(nl, &nlen, res)
cgen_call(n, 0)
cgen_aret(n, res)
- case OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
+ case OEFACE, ODOTTYPE, OSLICE, OSLICEARR, OSLICESTR, OSLICE3, OSLICE3ARR:
var n1 Node
Tempname(&n1, n.Type)
- Cgen_slice(n, &n1)
- Agen(&n1, res)
-
- case OEFACE:
- var n1 Node
- Tempname(&n1, n.Type)
- Cgen_eface(n, &n1)
+ Cgen(n, &n1)
Agen(&n1, res)
case OINDEX:
Regfree(&n2)
}
-/*
- * generate:
- * newreg = &n;
- * res = newreg
- *
- * on exit, a has been changed to be *newreg.
- * caller must Regfree(a).
- * The generated code checks that the result is not *nil.
- */
+// Igen computes the address &n, stores it in a register r,
+// and rewrites a to refer to *r. The chosen r may be the
+// stack pointer, it may be borrowed from res, or it may
+// be a newly allocated register. The caller must call Regfree(a)
+// to free r when the address is no longer needed.
+// The generated code ensures that &n is not nil.
func Igen(n *Node, a *Node, res *Node) {
if Debug['g'] != 0 {
Dump("\nigen-n", n)
Cgen(n.Left, &dst)
}
+/*
+ * generate one of:
+ * res, resok = x.(T)
+ * res = x.(T) (when resok == nil)
+ * n.Left is x
+ * n.Type is T
+ */
+func cgen_dottype(n *Node, res, resok *Node) {
+ if Debug_typeassert > 0 {
+ Warn("type assertion inlined")
+ }
+ // iface := n.Left
+ // r1 := iword(iface)
+ // if n.Left is non-empty interface {
+ // r1 = *r1
+ // }
+ // if r1 == T {
+ // res = idata(iface)
+ // resok = true
+ // } else {
+ // assert[EI]2T(x, T, nil) // (when resok == nil; does not return)
+ // resok = false // (when resok != nil)
+ // }
+ //
+ var iface Node
+ Igen(n.Left, &iface, res)
+ var r1, r2 Node
+ byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
+ Regalloc(&r1, byteptr, nil)
+ iface.Type = byteptr
+ Cgen(&iface, &r1)
+ if !isnilinter(n.Left.Type) {
+ // Holding itab, want concrete type in second word.
+ Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, Nodintconst(0))
+ p := Gbranch(Thearch.Optoas(OEQ, byteptr), nil, -1)
+ r2 = r1
+ r2.Op = OINDREG
+ r2.Xoffset = int64(Widthptr)
+ Cgen(&r2, &r1)
+ Patch(p, Pc)
+ }
+ Regalloc(&r2, byteptr, nil)
+ Cgen(typename(n.Type), &r2)
+ Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, &r2)
+ p := Gbranch(Thearch.Optoas(ONE, byteptr), nil, -1)
+ iface.Xoffset += int64(Widthptr)
+ Cgen(&iface, &r1)
+ Regfree(&iface)
+
+ if resok == nil {
+ r1.Type = res.Type
+ Cgen(&r1, res)
+ q := Gbranch(obj.AJMP, nil, 0)
+ Patch(p, Pc)
+
+ fn := syslook("panicdottype", 0)
+ dowidth(fn.Type)
+ call := Nod(OCALLFUNC, fn, nil)
+ r1.Type = byteptr
+ r2.Type = byteptr
+ call.List = list(list(list1(&r1), &r2), typename(n.Left.Type))
+ call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil)
+ gen(call)
+ Regfree(&r1)
+ Regfree(&r2)
+ Thearch.Gins(obj.AUNDEF, nil, nil)
+ Patch(q, Pc)
+ } else {
+ // This half is handling the res, resok = x.(T) case,
+ // which is called from gen, not cgen, and is consequently fussier
+ // about blank assignments. We have to avoid calling cgen for those.
+ Regfree(&r2)
+ r1.Type = res.Type
+ if !isblank(res) {
+ Cgen(&r1, res)
+ }
+ Regfree(&r1)
+ if !isblank(resok) {
+ Cgen(Nodbool(true), resok)
+ }
+ q := Gbranch(obj.AJMP, nil, 0)
+ Patch(p, Pc)
+ if !isblank(res) {
+ n := nodnil()
+ n.Type = res.Type
+ Cgen(n, res)
+ }
+ if !isblank(resok) {
+ Cgen(Nodbool(false), resok)
+ }
+ Patch(q, Pc)
+ }
+}
+
+/*
+ * generate:
+ * res, resok = x.(T)
+ * n.Left is x
+ * n.Type is T
+ */
+func Cgen_As2dottype(n, res, resok *Node) {
+ if Debug_typeassert > 0 {
+ Warn("type assertion inlined")
+ }
+ // iface := n.Left
+ // r1 := iword(iface)
+ // if n.Left is non-empty interface {
+ // r1 = *r1
+ // }
+ // if r1 == T {
+ // res = idata(iface)
+ // resok = true
+ // } else {
+ // res = nil
+ // resok = false
+ // }
+ //
+ var iface Node
+ Igen(n.Left, &iface, nil)
+ var r1, r2 Node
+ byteptr := Ptrto(Types[TUINT8]) // type used in runtime prototypes for runtime type (*byte)
+ Regalloc(&r1, byteptr, res)
+ iface.Type = byteptr
+ Cgen(&iface, &r1)
+ if !isnilinter(n.Left.Type) {
+ // Holding itab, want concrete type in second word.
+ Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, Nodintconst(0))
+ p := Gbranch(Thearch.Optoas(OEQ, byteptr), nil, -1)
+ r2 = r1
+ r2.Op = OINDREG
+ r2.Xoffset = int64(Widthptr)
+ Cgen(&r2, &r1)
+ Patch(p, Pc)
+ }
+ Regalloc(&r2, byteptr, nil)
+ Cgen(typename(n.Type), &r2)
+ Thearch.Gins(Thearch.Optoas(OCMP, byteptr), &r1, &r2)
+ p := Gbranch(Thearch.Optoas(ONE, byteptr), nil, -1)
+ iface.Type = n.Type
+ iface.Xoffset += int64(Widthptr)
+ Cgen(&iface, &r1)
+ if iface.Op != 0 {
+ Regfree(&iface)
+ }
+ Cgen(&r1, res)
+ q := Gbranch(obj.AJMP, nil, 0)
+ Patch(p, Pc)
+
+ fn := syslook("panicdottype", 0)
+ dowidth(fn.Type)
+ call := Nod(OCALLFUNC, fn, nil)
+ call.List = list(list(list1(&r1), &r2), typename(n.Left.Type))
+ call.List = ascompatte(OCALLFUNC, call, false, getinarg(fn.Type), call.List, 0, nil)
+ gen(call)
+ Regfree(&r1)
+ Regfree(&r2)
+ Thearch.Gins(obj.AUNDEF, nil, nil)
+ Patch(q, Pc)
+}
+
/*
* generate:
* res = s[lo, hi];
}
Cgen_as(n.Left, n.Right)
+ case OAS2DOTTYPE:
+ cgen_dottype(n.Rlist.N, n.List.N, n.List.Next.N)
+
case OCALLMETH:
cgen_callmeth(n, 0)
var debugstr string
var Debug_checknil int
+var Debug_typeassert int
var importmyname *Sym // my name for package
name string
val *int
}{
- {"nil", &Debug_checknil},
- {"disablenil", &Disable_checknil},
+ {"nil", &Debug_checknil}, // print information about nil checks
+ {"typeassert", &Debug_typeassert}, // print information about type assertion inlining
+ {"disablenil", &Disable_checknil}, // disable nil checks
}
// Our own isdigit, isspace, isalpha, isalnum that take care
n.Alloc = ordertemp(n.Type.Type, order, false)
}
- case ORECV,
- ODOTTYPE:
+ case ODOTTYPE, ODOTTYPE2:
+ orderexpr(&n.Left, order)
+ // TODO(rsc): The Isfat is for consistency with componentgen and walkexpr.
+ // It needs to be removed in all three places.
+ // That would allow inlining x.(struct{*int}) the same as x.(*int).
+ if !isdirectiface(n.Type) || Isfat(n.Type) || flag_race != 0 {
+ n = ordercopyexpr(n, n.Type, order, 1)
+ }
+
+ case ORECV:
orderexpr(&n.Left, order)
n = ordercopyexpr(n, n.Type, order, 1)
goto out
}
switch r.Op {
- case OINDEXMAP,
- ORECV,
- ODOTTYPE:
+ case OINDEXMAP, ORECV, ODOTTYPE:
switch r.Op {
case OINDEXMAP:
n.Op = OAS2MAPR
default:
walkexpr(&n.Right, init)
- // x = i.(T); n->left is x, n->right->left is i.
- // orderstmt made sure x is addressable.
case ODOTTYPE:
+ // TODO(rsc): The Isfat is for consistency with componentgen and orderexpr.
+ // It needs to be removed in all three places.
+ // That would allow inlining x.(struct{*int}) the same as x.(*int).
+ if isdirectiface(n.Right.Type) && !Isfat(n.Right.Type) && flag_race == 0 {
+ // handled directly during cgen
+ walkexpr(&n.Right, init)
+ break
+ }
+
+ // x = i.(T); n->left is x, n->right->left is i.
+ // orderstmt made sure x is addressable.
walkexpr(&n.Right.Left, init)
n1 := Nod(OADDR, n.Left, nil)
r := n.Right // i.(T)
+ if Debug_typeassert > 0 {
+ Warn("type assertion not inlined")
+ }
+
buf := "assert" + type2IET(r.Left.Type) + "2" + type2IET(r.Type)
fn := syslook(buf, 1)
substArgTypes(fn, r.Left.Type, r.Type)
walkexpr(&n, init)
goto ret
- // x = <-c; n->left is x, n->right->left is c.
- // orderstmt made sure x is addressable.
case ORECV:
+ // x = <-c; n->left is x, n->right->left is c.
+ // orderstmt made sure x is addressable.
walkexpr(&n.Right.Left, init)
n1 := Nod(OADDR, n.Left, nil)
n = mkcall1(mapfndel("mapdelete", t), nil, init, typename(t), map_, key)
goto ret
- // res, ok = i.(T)
- // orderstmt made sure a is addressable.
case OAS2DOTTYPE:
+ e := n.Rlist.N // i.(T)
+ // TODO(rsc): The Isfat is for consistency with componentgen and orderexpr.
+ // It needs to be removed in all three places.
+ // That would allow inlining x.(struct{*int}) the same as x.(*int).
+ if isdirectiface(e.Type) && !Isfat(e.Type) && flag_race == 0 {
+ // handled directly during gen.
+ walkexprlistsafe(n.List, init)
+ walkexpr(&e.Left, init)
+ goto ret
+ }
+
+ // res, ok = i.(T)
+ // orderstmt made sure a is addressable.
*init = concat(*init, n.Ninit)
n.Ninit = nil
- e := n.Rlist.N // i.(T)
walkexprlistsafe(n.List, init)
walkexpr(&e.Left, init)
t := e.Type // T
fast = Nod(ONE, nodnil(), tab)
}
if fast != nil {
+ if Debug_typeassert > 0 {
+ Warn("type assertion (ok only) inlined")
+ }
n = Nod(OAS, ok, fast)
typecheck(&n, Etop)
goto ret
}
resptr.Etype = 1 // addr does not escape
+ if Debug_typeassert > 0 {
+ Warn("type assertion not inlined")
+ }
buf := "assert" + fromKind + "2" + toKind + "2"
fn := syslook(buf, 1)
substArgTypes(fn, from.Type, t)
typecheck(&n, Etop)
goto ret
- case ODOTTYPE,
- ODOTTYPE2:
- Fatal("walkexpr ODOTTYPE") // should see inside OAS or OAS2 only
+ case ODOTTYPE, ODOTTYPE2:
+ if !isdirectiface(n.Type) || Isfat(n.Type) {
+ Fatal("walkexpr ODOTTYPE") // should see inside OAS only
+ }
+ walkexpr(&n.Left, init)
+ goto ret
case OCONVIFACE:
walkexpr(&n.Left, init)
": missing method " + e.missingMethod
}
-// For calling from C.
-func newTypeAssertionError(ps1, ps2, ps3 *string, pmeth *string, ret *interface{}) {
- var s1, s2, s3, meth string
-
- if ps1 != nil {
- s1 = *ps1
- }
- if ps2 != nil {
- s2 = *ps2
- }
- if ps3 != nil {
- s3 = *ps3
- }
- if pmeth != nil {
- meth = *pmeth
- }
- *ret = &TypeAssertionError{s1, s2, s3, meth}
-}
-
// An errorString represents a runtime error described by a single string.
type errorString string
return
}
+func panicdottype(have, want, iface *_type) {
+ haveString := ""
+ if have != nil {
+ haveString = *have._string
+ }
+ panic(&TypeAssertionError{*iface._string, haveString, *want._string, ""})
+}
+
func assertI2T(t *_type, i fInterface, r unsafe.Pointer) {
ip := (*iface)(unsafe.Pointer(&i))
tab := ip.tab
--- /dev/null
+// errorcheck -0 -d=typeassert
+
+// Copyright 2015 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package p
+
+func assertptr(x interface{}) *int {
+ return x.(*int) // ERROR "type assertion inlined"
+}
+
+func assertptr2(x interface{}) (*int, bool) {
+ z, ok := x.(*int) // ERROR "type assertion inlined"
+ return z, ok
+}
+
+func assertfunc(x interface{}) func() {
+ return x.(func()) // ERROR "type assertion inlined"
+}
+
+func assertfunc2(x interface{}) (func(), bool) {
+ z, ok := x.(func()) // ERROR "type assertion inlined"
+ return z, ok
+}
+
+// TODO(rsc): struct{*int} is stored directly in the interface
+// and should be possible to fetch back out of the interface,
+// but more of the general data movement code needs to
+// realize that before we can inline the assertion.
+
+func assertstruct(x interface{}) struct{ *int } {
+ return x.(struct{ *int }) // ERROR "type assertion not inlined"
+}
+
+func assertstruct2(x interface{}) (struct{ *int }, bool) {
+ z, ok := x.(struct{ *int }) // ERROR "type assertion not inlined"
+ return z, ok
+}
+
+func assertbig(x interface{}) complex128 {
+ return x.(complex128) // ERROR "type assertion not inlined"
+}
+
+func assertbig2(x interface{}) (complex128, bool) {
+ z, ok := x.(complex128) // ERROR "type assertion not inlined"
+ return z, ok
+}
+
+func assertbig2ok(x interface{}) (complex128, bool) {
+ _, ok := x.(complex128) // ERROR "type assertion [(]ok only[)] inlined"
+ return 0, ok
+}