mirror of
https://github.com/golang/go.git
synced 2025-05-05 15:43:04 +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
|
||||
// interface.
|
||||
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())
|
||||
lsym := s.Linksym()
|
||||
signatmu.Lock()
|
||||
if lsym.Extra == nil {
|
||||
ii := lsym.NewItabInfo()
|
||||
ii.Type = typ
|
||||
}
|
||||
signatmu.Unlock()
|
||||
|
||||
if !existed {
|
||||
writeITab(lsym, typ, iface, true)
|
||||
writeITab(lsym, typ, iface, allowNonImplement)
|
||||
}
|
||||
return lsym
|
||||
}
|
||||
@ -605,13 +615,7 @@ func ITabLsym(typ, iface *types.Type) *obj.LSym {
|
||||
// *runtime.itab value for concrete type typ implementing interface
|
||||
// iface.
|
||||
func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr {
|
||||
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
|
||||
lsym := s.Linksym()
|
||||
|
||||
if !existed {
|
||||
writeITab(lsym, typ, iface, false)
|
||||
}
|
||||
|
||||
lsym := itabLsym(typ, iface, false)
|
||||
return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
|
||||
}
|
||||
|
||||
|
@ -2072,6 +2072,11 @@
|
||||
(NilCheck ptr:(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.
|
||||
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
|
||||
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
|
||||
@ -2774,3 +2779,19 @@
|
||||
// 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.
|
||||
(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
|
||||
}
|
||||
|
||||
// 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)
|
||||
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 _ _) _ )
|
||||
// result: ptr
|
||||
for {
|
||||
@ -30297,6 +30308,48 @@ func rewriteValuegeneric_OpStaticLECall(v *Value) bool {
|
||||
v.AddArg2(v0, mem)
|
||||
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
|
||||
}
|
||||
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 {
|
||||
swap := n.X.Op() != ir.OCONVIFACE && n.Y.Op() == ir.OCONVIFACE
|
||||
n.Y = cheapExpr(n.Y, 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)
|
||||
var cmp ir.Node
|
||||
if n.Op() == ir.OEQ {
|
||||
|
@ -603,6 +603,22 @@ func (s *LSym) NewTypeInfo() *TypeInfo {
|
||||
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
|
||||
// parameters and results translated into WASM types based on the Go function
|
||||
// declaration.
|
||||
|
@ -25,3 +25,39 @@ func ConvToM(x any) I {
|
||||
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
|
||||
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