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>
// 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
}
// *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])
}
(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
// 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)
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
+}
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 {
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 {
}
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 {
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.
// 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
+}