Skip to content

Commit 072eea9

Browse files
randall77gopherbot
authored andcommitted
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 <[email protected]> Reviewed-by: Cuong Manh Le <[email protected]> Reviewed-by: Cherry Mui <[email protected]> Reviewed-by: David Chase <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
1 parent c8664ce commit 072eea9

File tree

7 files changed

+230
-8
lines changed

7 files changed

+230
-8
lines changed

src/cmd/compile/internal/reflectdata/reflect.go

+12-8
Original file line numberDiff line numberDiff line change
@@ -592,11 +592,21 @@ func TypePtrAt(pos src.XPos, t *types.Type) *ir.AddrExpr {
592592
// it may sometimes, but not always, be a type that can't implement the specified
593593
// interface.
594594
func ITabLsym(typ, iface *types.Type) *obj.LSym {
595+
return itabLsym(typ, iface, true)
596+
}
597+
598+
func itabLsym(typ, iface *types.Type, allowNonImplement bool) *obj.LSym {
595599
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
596600
lsym := s.Linksym()
601+
signatmu.Lock()
602+
if lsym.Extra == nil {
603+
ii := lsym.NewItabInfo()
604+
ii.Type = typ
605+
}
606+
signatmu.Unlock()
597607

598608
if !existed {
599-
writeITab(lsym, typ, iface, true)
609+
writeITab(lsym, typ, iface, allowNonImplement)
600610
}
601611
return lsym
602612
}
@@ -605,13 +615,7 @@ func ITabLsym(typ, iface *types.Type) *obj.LSym {
605615
// *runtime.itab value for concrete type typ implementing interface
606616
// iface.
607617
func ITabAddrAt(pos src.XPos, typ, iface *types.Type) *ir.AddrExpr {
608-
s, existed := ir.Pkgs.Itab.LookupOK(typ.LinkString() + "," + iface.LinkString())
609-
lsym := s.Linksym()
610-
611-
if !existed {
612-
writeITab(lsym, typ, iface, false)
613-
}
614-
618+
lsym := itabLsym(typ, iface, false)
615619
return typecheck.LinksymAddr(pos, lsym, types.Types[types.TUINT8])
616620
}
617621

src/cmd/compile/internal/ssa/_gen/generic.rules

+21
Original file line numberDiff line numberDiff line change
@@ -2072,6 +2072,11 @@
20722072
(NilCheck ptr:(Addr {_} (SB)) _) => ptr
20732073
(NilCheck ptr:(Convert (Addr {_} (SB)) _) _) => ptr
20742074

2075+
// Addresses of locals are always non-nil.
2076+
(NilCheck ptr:(LocalAddr _ _) _)
2077+
&& warnRule(fe.Debug_checknil(), v, "removed nil check")
2078+
=> ptr
2079+
20752080
// Nil checks of nil checks are redundant.
20762081
// See comment at the end of https://go-review.googlesource.com/c/go/+/537775.
20772082
(NilCheck ptr:(NilCheck _ _) _ ) => ptr
@@ -2774,3 +2779,19 @@
27742779
// If we don't use the result of cmpstring, might as well not call it.
27752780
// Note that this could pretty easily generalize to any pure function.
27762781
(SelectN [1] c:(StaticLECall {f} _ _ mem)) && c.Uses == 1 && isSameCall(f, "runtime.cmpstring") && clobber(c) => mem
2782+
2783+
// We can easily compute the result of efaceeq if
2784+
// we know the underlying type is pointer-ish.
2785+
(StaticLECall {f} typ_ x y mem)
2786+
&& isSameCall(f, "runtime.efaceeq")
2787+
&& isDirectType(typ_)
2788+
&& clobber(v)
2789+
=> (MakeResult (EqPtr x y) mem)
2790+
2791+
// We can easily compute the result of ifaceeq if
2792+
// we know the underlying type is pointer-ish.
2793+
(StaticLECall {f} itab x y mem)
2794+
&& isSameCall(f, "runtime.ifaceeq")
2795+
&& isDirectIface(itab)
2796+
&& clobber(v)
2797+
=> (MakeResult (EqPtr x y) mem)

src/cmd/compile/internal/ssa/rewrite.go

+83
Original file line numberDiff line numberDiff line change
@@ -2424,3 +2424,86 @@ func rewriteStructStore(v *Value) *Value {
24242424

24252425
return mem
24262426
}
2427+
2428+
// isDirectType reports whether v represents a type
2429+
// (a *runtime._type) whose value is stored directly in an
2430+
// interface (i.e., is pointer or pointer-like).
2431+
func isDirectType(v *Value) bool {
2432+
return isDirectType1(v)
2433+
}
2434+
2435+
// v is a type
2436+
func isDirectType1(v *Value) bool {
2437+
switch v.Op {
2438+
case OpITab:
2439+
return isDirectType2(v.Args[0])
2440+
case OpAddr:
2441+
lsym := v.Aux.(*obj.LSym)
2442+
if lsym.Extra == nil {
2443+
return false
2444+
}
2445+
if ti, ok := (*lsym.Extra).(*obj.TypeInfo); ok {
2446+
return types.IsDirectIface(ti.Type.(*types.Type))
2447+
}
2448+
}
2449+
return false
2450+
}
2451+
2452+
// v is an empty interface
2453+
func isDirectType2(v *Value) bool {
2454+
switch v.Op {
2455+
case OpIMake:
2456+
return isDirectType1(v.Args[0])
2457+
}
2458+
return false
2459+
}
2460+
2461+
// isDirectIface reports whether v represents an itab
2462+
// (a *runtime._itab) for a type whose value is stored directly
2463+
// in an interface (i.e., is pointer or pointer-like).
2464+
func isDirectIface(v *Value) bool {
2465+
return isDirectIface1(v, 9)
2466+
}
2467+
2468+
// v is an itab
2469+
func isDirectIface1(v *Value, depth int) bool {
2470+
if depth == 0 {
2471+
return false
2472+
}
2473+
switch v.Op {
2474+
case OpITab:
2475+
return isDirectIface2(v.Args[0], depth-1)
2476+
case OpAddr:
2477+
lsym := v.Aux.(*obj.LSym)
2478+
if lsym.Extra == nil {
2479+
return false
2480+
}
2481+
if ii, ok := (*lsym.Extra).(*obj.ItabInfo); ok {
2482+
return types.IsDirectIface(ii.Type.(*types.Type))
2483+
}
2484+
case OpConstNil:
2485+
// We can treat this as direct, because if the itab is
2486+
// nil, the data field must be nil also.
2487+
return true
2488+
}
2489+
return false
2490+
}
2491+
2492+
// v is an interface
2493+
func isDirectIface2(v *Value, depth int) bool {
2494+
if depth == 0 {
2495+
return false
2496+
}
2497+
switch v.Op {
2498+
case OpIMake:
2499+
return isDirectIface1(v.Args[0], depth-1)
2500+
case OpPhi:
2501+
for _, a := range v.Args {
2502+
if !isDirectIface2(a, depth-1) {
2503+
return false
2504+
}
2505+
}
2506+
return true
2507+
}
2508+
return false
2509+
}

src/cmd/compile/internal/ssa/rewritegeneric.go

+53
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/cmd/compile/internal/walk/compare.go

+9
Original file line numberDiff line numberDiff line change
@@ -317,8 +317,17 @@ func walkCompare(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
317317
}
318318

319319
func walkCompareInterface(n *ir.BinaryExpr, init *ir.Nodes) ir.Node {
320+
swap := n.X.Op() != ir.OCONVIFACE && n.Y.Op() == ir.OCONVIFACE
320321
n.Y = cheapExpr(n.Y, init)
321322
n.X = cheapExpr(n.X, init)
323+
if swap {
324+
// Put the concrete type first in the comparison.
325+
// This passes a constant type (itab) to efaceeq (ifaceeq)
326+
// which is easier to match against in rewrite rules.
327+
// See issue 70738.
328+
n.X, n.Y = n.Y, n.X
329+
}
330+
322331
eqtab, eqdata := compare.EqInterface(n.X, n.Y)
323332
var cmp ir.Node
324333
if n.Op() == ir.OEQ {

src/cmd/internal/obj/link.go

+16
Original file line numberDiff line numberDiff line change
@@ -603,6 +603,22 @@ func (s *LSym) NewTypeInfo() *TypeInfo {
603603
return t
604604
}
605605

606+
// An ItabInfo contains information for a symbol
607+
// that contains a runtime.itab.
608+
type ItabInfo struct {
609+
Type interface{} // a *cmd/compile/internal/types.Type
610+
}
611+
612+
func (s *LSym) NewItabInfo() *ItabInfo {
613+
if s.Extra != nil {
614+
panic(fmt.Sprintf("invalid use of LSym - NewItabInfo with Extra of type %T", *s.Extra))
615+
}
616+
t := new(ItabInfo)
617+
s.Extra = new(interface{})
618+
*s.Extra = t
619+
return t
620+
}
621+
606622
// WasmImport represents a WebAssembly (WASM) imported function with
607623
// parameters and results translated into WASM types based on the Go function
608624
// declaration.

test/codegen/ifaces.go

+36
Original file line numberDiff line numberDiff line change
@@ -25,3 +25,39 @@ func ConvToM(x any) I {
2525
// arm64:`CALL\truntime.typeAssert`,`LDAR`,`MOVWU`,`MOVD\t\(R.*\)\(R.*\)`
2626
return x.(I)
2727
}
28+
29+
func e1(x any, y *int) bool {
30+
// amd64:-`.*faceeq`,`SETEQ`
31+
// arm64:-`.*faceeq`,`CSET\tEQ`
32+
return x == y
33+
}
34+
35+
func e2(x any, y *int) bool {
36+
// amd64:-`.*faceeq`,`SETEQ`
37+
// arm64:-`.*faceeq`,`CSET\tEQ`
38+
return y == x
39+
}
40+
41+
type E *int
42+
43+
func e3(x any, y E) bool {
44+
// amd64:-`.*faceeq`,`SETEQ`
45+
// arm64:-`.*faceeq`,`CSET\tEQ`
46+
return x == y
47+
}
48+
49+
type T int
50+
51+
func (t *T) M() {}
52+
53+
func i1(x I, y *T) bool {
54+
// amd64:-`.*faceeq`,`SETEQ`
55+
// arm64:-`.*faceeq`,`CSET\tEQ`
56+
return x == y
57+
}
58+
59+
func i2(x I, y *T) bool {
60+
// amd64:-`.*faceeq`,`SETEQ`
61+
// arm64:-`.*faceeq`,`CSET\tEQ`
62+
return y == x
63+
}

0 commit comments

Comments
 (0)