mirror of
https://github.com/golang/go.git
synced 2025-05-05 23:53:05 +00:00
cmd/compile: avoid ifaceeq call if we know the interface is direct
We can just use == if the interface is direct. Fixes #70738 Change-Id: Ia9a644791a370fec969c04c42d28a9b58f16911f Reviewed-on: https://go-review.googlesource.com/c/go/+/635435 Auto-Submit: Keith Randall <khr@golang.org> Reviewed-by: Cuong Manh Le <cuong.manhle.vn@gmail.com> Reviewed-by: Cherry Mui <cherryyz@google.com> Reviewed-by: David Chase <drchase@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
c8664ced4e
commit
072eea9b3b
@ -592,11 +592,21 @@ func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr {
|
|||||||
// it may sometimes, but not always, be a type that can't implement the specified
|
// it may sometimes, but not always, be a type that can't implement the specified
|
||||||
// interface.
|
// interface.
|
||||||
func ITabLsym(typ, iface *types.Type) *obj.LSym {
|
func ITabLsym(typ, iface *types.Type) *obj.LSym {
|
||||||
|
return itabLsym(typ, iface, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func itabLsym(typ, iface *types.Type, allowNonImplement bool) *obj.LSym {
|
||||||
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
|
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
|
||||||
lsym := s.Linksym()
|
lsym := s.Linksym()
|
||||||
|
signatmu.Lock()
|
||||||
|
if lsym.Extra == nil {
|
||||||
|
ii := lsym.NewItabInfo()
|
||||||
|
ii.Type = typ
|
||||||
|
}
|
||||||
|
signatmu.Unlock()
|
||||||
|
|
||||||
if !existed {
|
if !existed {
|
||||||
writeITab(lsym, typ, iface, true)
|
writeITab(lsym, typ, iface, allowNonImplement)
|
||||||
}
|
}
|
||||||
return lsym
|
return lsym
|
||||||
}
|
}
|
||||||
@ -605,13 +615,7 @@ func ITabLsym(typ, iface *types.Type) *obj.LSym {
|
|||||||
// *runtime.itab value for concrete type typ implementing interface
|
// *runtime.itab value for concrete type typ implementing interface
|
||||||
// iface.
|
// iface.
|
||||||
func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr {
|
func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr {
|
||||||
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
|
lsym := itabLsym(typ, iface, false)
|
||||||
lsym := s.Linksym()
|
|
||||||
|
|
||||||
if !existed {
|
|
||||||
writeITab(lsym, typ, iface, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
|
return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2072,6 +2072,11 @@
|
|||||||
(NilCheck ptr:(Addr {_} (SB)) _) => ptr
|
(NilCheck ptr:(Addr {_} (SB)) _) => ptr
|
||||||
(NilCheck ptr:(Convert (Addr {_} (SB)) _) _) => ptr
|
(NilCheck ptr:(Convert (Addr {_} (SB)) _) _) => ptr
|
||||||
|
|
||||||
|
// Addresses of locals are always non-nil.
|
||||||
|
(NilCheck ptr:(LocalAddr _ _) _)
|
||||||
|
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
|
||||||
|
=> ptr
|
||||||
|
|
||||||
// Nil checks of nil checks are redundant.
|
// Nil checks of nil checks are redundant.
|
||||||
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
|
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
|
||||||
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
|
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
|
||||||
@ -2774,3 +2779,19 @@
|
|||||||
// If we don't use the result of cmpstring, might as well not call it.
|
// If we don't use the result of cmpstring, might as well not call it.
|
||||||
// Note that this could pretty easily generalize to any pure function.
|
// Note that this could pretty easily generalize to any pure function.
|
||||||
(SelectN [1] c:(StaticLECall {f} _ _ mem)) && c.Uses == 1 && isSameCall(f, "runtime.cmpstring") && clobber(c) => mem
|
(SelectN [1] c:(StaticLECall {f} _ _ mem)) && c.Uses == 1 && isSameCall(f, "runtime.cmpstring") && clobber(c) => mem
|
||||||
|
|
||||||
|
// We can easily compute the result of efaceeq if
|
||||||
|
// we know the underlying type is pointer-ish.
|
||||||
|
(StaticLECall {f} typ_ x y mem)
|
||||||
|
&& isSameCall(f, "runtime.efaceeq")
|
||||||
|
&& isDirectType(typ_)
|
||||||
|
&& clobber(v)
|
||||||
|
=> (MakeResult (EqPtr x y) mem)
|
||||||
|
|
||||||
|
// We can easily compute the result of ifaceeq if
|
||||||
|
// we know the underlying type is pointer-ish.
|
||||||
|
(StaticLECall {f} itab x y mem)
|
||||||
|
&& isSameCall(f, "runtime.ifaceeq")
|
||||||
|
&& isDirectIface(itab)
|
||||||
|
&& clobber(v)
|
||||||
|
=> (MakeResult (EqPtr x y) mem)
|
||||||
|
@ -2424,3 +2424,86 @@ func rewriteStructStore(v *Value) *Value {
|
|||||||
|
|
||||||
return mem
|
return mem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// isDirectType reports whether v represents a type
|
||||||
|
// (a *runtime._type) whose value is stored directly in an
|
||||||
|
// interface (i.e., is pointer or pointer-like).
|
||||||
|
func isDirectType(v *Value) bool {
|
||||||
|
return isDirectType1(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// v is a type
|
||||||
|
func isDirectType1(v *Value) bool {
|
||||||
|
switch v.Op {
|
||||||
|
case OpITab:
|
||||||
|
return isDirectType2(v.Args[0])
|
||||||
|
case OpAddr:
|
||||||
|
lsym := v.Aux.(*obj.LSym)
|
||||||
|
if lsym.Extra == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
|
||||||
|
return types.IsDirectIface(ti.Type.(*types.Type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// v is an empty interface
|
||||||
|
func isDirectType2(v *Value) bool {
|
||||||
|
switch v.Op {
|
||||||
|
case OpIMake:
|
||||||
|
return isDirectType1(v.Args[0])
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// isDirectIface reports whether v represents an itab
|
||||||
|
// (a *runtime._itab) for a type whose value is stored directly
|
||||||
|
// in an interface (i.e., is pointer or pointer-like).
|
||||||
|
func isDirectIface(v *Value) bool {
|
||||||
|
return isDirectIface1(v, 9)
|
||||||
|
}
|
||||||
|
|
||||||
|
// v is an itab
|
||||||
|
func isDirectIface1(v *Value, depth int) bool {
|
||||||
|
if depth == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch v.Op {
|
||||||
|
case OpITab:
|
||||||
|
return isDirectIface2(v.Args[0], depth-1)
|
||||||
|
case OpAddr:
|
||||||
|
lsym := v.Aux.(*obj.LSym)
|
||||||
|
if lsym.Extra == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if ii, ok := (*lsym.Extra).(*obj.ItabInfo); ok {
|
||||||
|
return types.IsDirectIface(ii.Type.(*types.Type))
|
||||||
|
}
|
||||||
|
case OpConstNil:
|
||||||
|
// We can treat this as direct, because if the itab is
|
||||||
|
// nil, the data field must be nil also.
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// v is an interface
|
||||||
|
func isDirectIface2(v *Value, depth int) bool {
|
||||||
|
if depth == 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch v.Op {
|
||||||
|
case OpIMake:
|
||||||
|
return isDirectIface1(v.Args[0], depth-1)
|
||||||
|
case OpPhi:
|
||||||
|
for _, a := range v.Args {
|
||||||
|
if !isDirectIface2(a, depth-1) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@ -20678,6 +20678,17 @@ func rewriteValuegeneric_OpNilCheck(v *Value) bool {
|
|||||||
v.copyOf(ptr)
|
v.copyOf(ptr)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// match: (NilCheck ptr:(LocalAddr _ _) _)
|
||||||
|
// cond: warnRule(fe.Debug_checknil(), v, "removed nil check")
|
||||||
|
// result: ptr
|
||||||
|
for {
|
||||||
|
ptr := v_0
|
||||||
|
if ptr.Op != OpLocalAddr || !(warnRule(fe.Debug_checknil(), v, "removed nil check")) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.copyOf(ptr)
|
||||||
|
return true
|
||||||
|
}
|
||||||
// match: (NilCheck ptr:(NilCheck _ _) _ )
|
// match: (NilCheck ptr:(NilCheck _ _) _ )
|
||||||
// result: ptr
|
// result: ptr
|
||||||
for {
|
for {
|
||||||
@ -30297,6 +30308,48 @@ func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
|
|||||||
v.AddArg2(v0, mem)
|
v.AddArg2(v0, mem)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
// match: (StaticLECall {f} typ_ x y mem)
|
||||||
|
// cond: isSameCall(f, "runtime.efaceeq") && isDirectType(typ_) && clobber(v)
|
||||||
|
// result: (MakeResult (EqPtr x y) mem)
|
||||||
|
for {
|
||||||
|
if len(v.Args) != 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f := auxToCall(v.Aux)
|
||||||
|
mem := v.Args[3]
|
||||||
|
typ_ := v.Args[0]
|
||||||
|
x := v.Args[1]
|
||||||
|
y := v.Args[2]
|
||||||
|
if !(isSameCall(f, "runtime.efaceeq") && isDirectType(typ_) && clobber(v)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.reset(OpMakeResult)
|
||||||
|
v0 := b.NewValue0(v.Pos, OpEqPtr, typ.Bool)
|
||||||
|
v0.AddArg2(x, y)
|
||||||
|
v.AddArg2(v0, mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
// match: (StaticLECall {f} itab x y mem)
|
||||||
|
// cond: isSameCall(f, "runtime.ifaceeq") && isDirectIface(itab) && clobber(v)
|
||||||
|
// result: (MakeResult (EqPtr x y) mem)
|
||||||
|
for {
|
||||||
|
if len(v.Args) != 4 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
f := auxToCall(v.Aux)
|
||||||
|
mem := v.Args[3]
|
||||||
|
itab := v.Args[0]
|
||||||
|
x := v.Args[1]
|
||||||
|
y := v.Args[2]
|
||||||
|
if !(isSameCall(f, "runtime.ifaceeq") && isDirectIface(itab) && clobber(v)) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
v.reset(OpMakeResult)
|
||||||
|
v0 := b.NewValue0(v.Pos, OpEqPtr, typ.Bool)
|
||||||
|
v0.AddArg2(x, y)
|
||||||
|
v.AddArg2(v0, mem)
|
||||||
|
return true
|
||||||
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
func rewriteValuegeneric_OpStore(v *Value) bool {
|
func rewriteValuegeneric_OpStore(v *Value) bool {
|
||||||
|
@ -317,8 +317,17 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
|
func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
|
||||||
|
swap := n.X.Op() != ir.OCONVIFACE && n.Y.Op() == ir.OCONVIFACE
|
||||||
n.Y = cheapExpr(n.Y, init)
|
n.Y = cheapExpr(n.Y, init)
|
||||||
n.X = cheapExpr(n.X, init)
|
n.X = cheapExpr(n.X, init)
|
||||||
|
if swap {
|
||||||
|
// Put the concrete type first in the comparison.
|
||||||
|
// This passes a constant type (itab) to efaceeq (ifaceeq)
|
||||||
|
// which is easier to match against in rewrite rules.
|
||||||
|
// See issue 70738.
|
||||||
|
n.X, n.Y = n.Y, n.X
|
||||||
|
}
|
||||||
|
|
||||||
eqtab, eqdata := compare.EqInterface(n.X, n.Y)
|
eqtab, eqdata := compare.EqInterface(n.X, n.Y)
|
||||||
var cmp ir.Node
|
var cmp ir.Node
|
||||||
if n.Op() == ir.OEQ {
|
if n.Op() == ir.OEQ {
|
||||||
|
@ -603,6 +603,22 @@ func (s *LSym) NewTypeInfo() *TypeInfo {
|
|||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// An ItabInfo contains information for a symbol
|
||||||
|
// that contains a runtime.itab.
|
||||||
|
type ItabInfo struct {
|
||||||
|
Type interface{} // a *cmd/compile/internal/types.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *LSym) NewItabInfo() *ItabInfo {
|
||||||
|
if s.Extra != nil {
|
||||||
|
panic(fmt.Sprintf("invalid use of LSym - NewItabInfo with Extra of type %T", *s.Extra))
|
||||||
|
}
|
||||||
|
t := new(ItabInfo)
|
||||||
|
s.Extra = new(interface{})
|
||||||
|
*s.Extra = t
|
||||||
|
return t
|
||||||
|
}
|
||||||
|
|
||||||
// WasmImport represents a WebAssembly (WASM) imported function with
|
// WasmImport represents a WebAssembly (WASM) imported function with
|
||||||
// parameters and results translated into WASM types based on the Go function
|
// parameters and results translated into WASM types based on the Go function
|
||||||
// declaration.
|
// declaration.
|
||||||
|
@ -25,3 +25,39 @@ func ConvToM(x any) I {
|
|||||||
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
|
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
|
||||||
return x.(I)
|
return x.(I)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func e1(x any, y *int) bool {
|
||||||
|
// amd64:-`.*faceeq`,`SETEQ`
|
||||||
|
// arm64:-`.*faceeq`,`CSET\tEQ`
|
||||||
|
return x == y
|
||||||
|
}
|
||||||
|
|
||||||
|
func e2(x any, y *int) bool {
|
||||||
|
// amd64:-`.*faceeq`,`SETEQ`
|
||||||
|
// arm64:-`.*faceeq`,`CSET\tEQ`
|
||||||
|
return y == x
|
||||||
|
}
|
||||||
|
|
||||||
|
type E *int
|
||||||
|
|
||||||
|
func e3(x any, y E) bool {
|
||||||
|
// amd64:-`.*faceeq`,`SETEQ`
|
||||||
|
// arm64:-`.*faceeq`,`CSET\tEQ`
|
||||||
|
return x == y
|
||||||
|
}
|
||||||
|
|
||||||
|
type T int
|
||||||
|
|
||||||
|
func (t *T) M() {}
|
||||||
|
|
||||||
|
func i1(x I, y *T) bool {
|
||||||
|
// amd64:-`.*faceeq`,`SETEQ`
|
||||||
|
// arm64:-`.*faceeq`,`CSET\tEQ`
|
||||||
|
return x == y
|
||||||
|
}
|
||||||
|
|
||||||
|
func i2(x I, y *T) bool {
|
||||||
|
// amd64:-`.*faceeq`,`SETEQ`
|
||||||
|
// arm64:-`.*faceeq`,`CSET\tEQ`
|
||||||
|
return y == x
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user