cmd/compile: Add some CMP and CMN optimization rules on arm64

This CL adds some optimizaion rules:
1, Converts CMP to CMN, or vice versa, when comparing with a negative
number.
2, For equal and not equal comparisons, CMP can be converted to CMN in
some cases. In theory we could do the same optimization for LT, LE, GT
and GE, but need to account for overflow, this CL doesn't handle them.

There are no noticeable performance changes.

Change-Id: Ia49266c019ab7908ebc9510c2f02e121b1607869
Reviewed-on: https://go-review.googlesource.com/c/go/+/429795
Reviewed-by: Keith Randall <khr@golang.org>
Reviewed-by: Keith Randall <khr@google.com>
Reviewed-by: Cherry Mui <cherryyz@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Run-TryBot: Eric Fang <eric.fang@arm.com>
This commit is contained in:
eric fang 2022-09-07 08:34:52 +00:00 committed by Eric Fang
parent 7db923fe56
commit cf53990b18
4 changed files with 616 additions and 6 deletions

View File

@ -742,10 +742,47 @@
(EQ (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (EQ (CMN x y) yes no)
(NE (CMP x z:(NEG y)) yes no) && z.Uses == 1 => (NE (CMN x y) yes no)
(Equal (CMP x z:(NEG y))) && z.Uses == 1 => (Equal (CMN x y))
(NotEqual (CMP x z:(NEG y))) && z.Uses == 1 => (NotEqual (CMN x y))
// CMPW(x,-y) -> CMNW(x,y) is only valid for unordered comparison, if y can be -1<<31
(EQ (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (EQ (CMNW x y) yes no)
(NE (CMPW x z:(NEG y)) yes no) && z.Uses == 1 => (NE (CMNW x y) yes no)
(Equal (CMPW x z:(NEG y))) && z.Uses == 1 => (Equal (CMNW x y))
(NotEqual (CMPW x z:(NEG y))) && z.Uses == 1 => (NotEqual (CMNW x y))
// For conditional instructions such as CSET, CSEL.
// TODO: add support for LT, LE, GT, GE, overflow needs to be considered.
(Equal (CMPconst [0] x:(ADDconst [c] y))) && x.Uses == 1 => (Equal (CMNconst [c] y))
(NotEqual (CMPconst [0] x:(ADDconst [c] y))) && x.Uses == 1 => (NotEqual (CMNconst [c] y))
(Equal (CMPWconst [0] x:(ADDconst [c] y))) && x.Uses == 1 => (Equal (CMNWconst [int32(c)] y))
(NotEqual (CMPWconst [0] x:(ADDconst [c] y))) && x.Uses == 1 => (NotEqual (CMNWconst [int32(c)] y))
(Equal (CMPconst [0] z:(ADD x y))) && z.Uses == 1 => (Equal (CMN x y))
(NotEqual (CMPconst [0] z:(ADD x y))) && z.Uses == 1 => (NotEqual (CMN x y))
(Equal (CMPWconst [0] z:(ADD x y))) && z.Uses == 1 => (Equal (CMNW x y))
(NotEqual (CMPWconst [0] z:(ADD x y))) && z.Uses == 1 => (NotEqual (CMNW x y))
(Equal (CMPconst [0] z:(MADD a x y))) && z.Uses==1 => (Equal (CMN a (MUL <x.Type> x y)))
(NotEqual (CMPconst [0] z:(MADD a x y))) && z.Uses==1 => (NotEqual (CMN a (MUL <x.Type> x y)))
(Equal (CMPconst [0] z:(MSUB a x y))) && z.Uses==1 => (Equal (CMP a (MUL <x.Type> x y)))
(NotEqual (CMPconst [0] z:(MSUB a x y))) && z.Uses==1 => (NotEqual (CMP a (MUL <x.Type> x y)))
(Equal (CMPWconst [0] z:(MADDW a x y))) && z.Uses==1 => (Equal (CMNW a (MULW <x.Type> x y)))
(NotEqual (CMPWconst [0] z:(MADDW a x y))) && z.Uses==1 => (NotEqual (CMNW a (MULW <x.Type> x y)))
(Equal (CMPWconst [0] z:(MSUBW a x y))) && z.Uses==1 => (Equal (CMPW a (MULW <x.Type> x y)))
(NotEqual (CMPWconst [0] z:(MSUBW a x y))) && z.Uses==1 => (NotEqual (CMPW a (MULW <x.Type> x y)))
(CMPconst [c] y) && c < 0 && c != -1<<63 => (CMNconst [-c] y)
(CMPWconst [c] y) && c < 0 && c != -1<<31 => (CMNWconst [-c] y)
(CMNconst [c] y) && c < 0 && c != -1<<63 => (CMPconst [-c] y)
(CMNWconst [c] y) && c < 0 && c != -1<<31 => (CMPWconst [-c] y)
(EQ (CMPconst [0] x) yes no) => (Z x yes no)
(NE (CMPconst [0] x) yes no) => (NZ x yes no)
(EQ (CMPWconst [0] x) yes no) => (ZW x yes no)

View File

@ -2505,6 +2505,20 @@ func rewriteValueARM64_OpARM64CMNW(v *Value) bool {
}
func rewriteValueARM64_OpARM64CMNWconst(v *Value) bool {
v_0 := v.Args[0]
// match: (CMNWconst [c] y)
// cond: c < 0 && c != -1<<31
// result: (CMPWconst [-c] y)
for {
c := auxIntToInt32(v.AuxInt)
y := v_0
if !(c < 0 && c != -1<<31) {
break
}
v.reset(OpARM64CMPWconst)
v.AuxInt = int32ToAuxInt(-c)
v.AddArg(y)
return true
}
// match: (CMNWconst (MOVDconst [x]) [y])
// result: (FlagConstant [addFlags32(int32(x),y)])
for {
@ -2521,6 +2535,20 @@ func rewriteValueARM64_OpARM64CMNWconst(v *Value) bool {
}
func rewriteValueARM64_OpARM64CMNconst(v *Value) bool {
v_0 := v.Args[0]
// match: (CMNconst [c] y)
// cond: c < 0 && c != -1<<63
// result: (CMPconst [-c] y)
for {
c := auxIntToInt64(v.AuxInt)
y := v_0
if !(c < 0 && c != -1<<63) {
break
}
v.reset(OpARM64CMPconst)
v.AuxInt = int64ToAuxInt(-c)
v.AddArg(y)
return true
}
// match: (CMNconst (MOVDconst [x]) [y])
// result: (FlagConstant [addFlags64(x,y)])
for {
@ -2866,6 +2894,20 @@ func rewriteValueARM64_OpARM64CMPW(v *Value) bool {
}
func rewriteValueARM64_OpARM64CMPWconst(v *Value) bool {
v_0 := v.Args[0]
// match: (CMPWconst [c] y)
// cond: c < 0 && c != -1<<31
// result: (CMNWconst [-c] y)
for {
c := auxIntToInt32(v.AuxInt)
y := v_0
if !(c < 0 && c != -1<<31) {
break
}
v.reset(OpARM64CMNWconst)
v.AuxInt = int32ToAuxInt(-c)
v.AddArg(y)
return true
}
// match: (CMPWconst (MOVDconst [x]) [y])
// result: (FlagConstant [subFlags32(int32(x),y)])
for {
@ -2906,6 +2948,20 @@ func rewriteValueARM64_OpARM64CMPWconst(v *Value) bool {
}
func rewriteValueARM64_OpARM64CMPconst(v *Value) bool {
v_0 := v.Args[0]
// match: (CMPconst [c] y)
// cond: c < 0 && c != -1<<63
// result: (CMNconst [-c] y)
for {
c := auxIntToInt64(v.AuxInt)
y := v_0
if !(c < 0 && c != -1<<63) {
break
}
v.reset(OpARM64CMNconst)
v.AuxInt = int64ToAuxInt(-c)
v.AddArg(y)
return true
}
// match: (CMPconst (MOVDconst [x]) [y])
// result: (FlagConstant [subFlags64(x,y)])
for {
@ -3985,6 +4041,242 @@ func rewriteValueARM64_OpARM64Equal(v *Value) bool {
v.AddArg(v0)
return true
}
// match: (Equal (CMP x z:(NEG y)))
// cond: z.Uses == 1
// result: (Equal (CMN x y))
for {
if v_0.Op != OpARM64CMP {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (Equal (CMPW x z:(NEG y)))
// cond: z.Uses == 1
// result: (Equal (CMNW x y))
for {
if v_0.Op != OpARM64CMPW {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (Equal (CMPconst [0] x:(ADDconst [c] y)))
// cond: x.Uses == 1
// result: (Equal (CMNconst [c] y))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
x := v_0.Args[0]
if x.Op != OpARM64ADDconst {
break
}
c := auxIntToInt64(x.AuxInt)
y := x.Args[0]
if !(x.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMNconst, types.TypeFlags)
v0.AuxInt = int64ToAuxInt(c)
v0.AddArg(y)
v.AddArg(v0)
return true
}
// match: (Equal (CMPWconst [0] x:(ADDconst [c] y)))
// cond: x.Uses == 1
// result: (Equal (CMNWconst [int32(c)] y))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
x := v_0.Args[0]
if x.Op != OpARM64ADDconst {
break
}
c := auxIntToInt64(x.AuxInt)
y := x.Args[0]
if !(x.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMNWconst, types.TypeFlags)
v0.AuxInt = int32ToAuxInt(int32(c))
v0.AddArg(y)
v.AddArg(v0)
return true
}
// match: (Equal (CMPconst [0] z:(ADD x y)))
// cond: z.Uses == 1
// result: (Equal (CMN x y))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64ADD {
break
}
y := z.Args[1]
x := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (Equal (CMPWconst [0] z:(ADD x y)))
// cond: z.Uses == 1
// result: (Equal (CMNW x y))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64ADD {
break
}
y := z.Args[1]
x := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (Equal (CMPconst [0] z:(MADD a x y)))
// cond: z.Uses==1
// result: (Equal (CMN a (MUL <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MADD {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMN, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MUL, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (Equal (CMPconst [0] z:(MSUB a x y)))
// cond: z.Uses==1
// result: (Equal (CMP a (MUL <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MSUB {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMP, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MUL, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (Equal (CMPWconst [0] z:(MADDW a x y)))
// cond: z.Uses==1
// result: (Equal (CMNW a (MULW <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MADDW {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMNW, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MULW, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (Equal (CMPWconst [0] z:(MSUBW a x y)))
// cond: z.Uses==1
// result: (Equal (CMPW a (MULW <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MSUBW {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64Equal)
v0 := b.NewValue0(v.Pos, OpARM64CMPW, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MULW, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (Equal (FlagConstant [fc]))
// result: (MOVDconst [b2i(fc.eq())])
for {
@ -16435,6 +16727,242 @@ func rewriteValueARM64_OpARM64NotEqual(v *Value) bool {
v.AddArg(v0)
return true
}
// match: (NotEqual (CMP x z:(NEG y)))
// cond: z.Uses == 1
// result: (NotEqual (CMN x y))
for {
if v_0.Op != OpARM64CMP {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPW x z:(NEG y)))
// cond: z.Uses == 1
// result: (NotEqual (CMNW x y))
for {
if v_0.Op != OpARM64CMPW {
break
}
_ = v_0.Args[1]
x := v_0.Args[0]
z := v_0.Args[1]
if z.Op != OpARM64NEG {
break
}
y := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPconst [0] x:(ADDconst [c] y)))
// cond: x.Uses == 1
// result: (NotEqual (CMNconst [c] y))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
x := v_0.Args[0]
if x.Op != OpARM64ADDconst {
break
}
c := auxIntToInt64(x.AuxInt)
y := x.Args[0]
if !(x.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMNconst, types.TypeFlags)
v0.AuxInt = int64ToAuxInt(c)
v0.AddArg(y)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPWconst [0] x:(ADDconst [c] y)))
// cond: x.Uses == 1
// result: (NotEqual (CMNWconst [int32(c)] y))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
x := v_0.Args[0]
if x.Op != OpARM64ADDconst {
break
}
c := auxIntToInt64(x.AuxInt)
y := x.Args[0]
if !(x.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMNWconst, types.TypeFlags)
v0.AuxInt = int32ToAuxInt(int32(c))
v0.AddArg(y)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPconst [0] z:(ADD x y)))
// cond: z.Uses == 1
// result: (NotEqual (CMN x y))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64ADD {
break
}
y := z.Args[1]
x := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMN, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPWconst [0] z:(ADD x y)))
// cond: z.Uses == 1
// result: (NotEqual (CMNW x y))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64ADD {
break
}
y := z.Args[1]
x := z.Args[0]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMNW, types.TypeFlags)
v0.AddArg2(x, y)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPconst [0] z:(MADD a x y)))
// cond: z.Uses==1
// result: (NotEqual (CMN a (MUL <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MADD {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMN, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MUL, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPconst [0] z:(MSUB a x y)))
// cond: z.Uses==1
// result: (NotEqual (CMP a (MUL <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPconst || auxIntToInt64(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MSUB {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMP, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MUL, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPWconst [0] z:(MADDW a x y)))
// cond: z.Uses==1
// result: (NotEqual (CMNW a (MULW <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MADDW {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMNW, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MULW, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (NotEqual (CMPWconst [0] z:(MSUBW a x y)))
// cond: z.Uses==1
// result: (NotEqual (CMPW a (MULW <x.Type> x y)))
for {
if v_0.Op != OpARM64CMPWconst || auxIntToInt32(v_0.AuxInt) != 0 {
break
}
z := v_0.Args[0]
if z.Op != OpARM64MSUBW {
break
}
y := z.Args[2]
a := z.Args[0]
x := z.Args[1]
if !(z.Uses == 1) {
break
}
v.reset(OpARM64NotEqual)
v0 := b.NewValue0(v.Pos, OpARM64CMPW, types.TypeFlags)
v1 := b.NewValue0(v.Pos, OpARM64MULW, x.Type)
v1.AddArg2(x, y)
v0.AddArg2(a, v1)
v.AddArg(v0)
return true
}
// match: (NotEqual (FlagConstant [fc]))
// result: (MOVDconst [b2i(fc.ne())])
for {

View File

@ -45,10 +45,6 @@ var complements = []obj.As{
AADDW: ASUBW,
ASUB: AADD,
ASUBW: AADDW,
ACMP: ACMN,
ACMPW: ACMNW,
ACMN: ACMP,
ACMNW: ACMPW,
}
// zrReplace is the set of instructions for which $0 in the From operand
@ -382,12 +378,12 @@ func progedit(ctxt *obj.Link, p *obj.Prog, newprog obj.ProgAlloc) {
// Rewrite negative immediates as positive immediates with
// complementary instruction.
switch p.As {
case AADD, ASUB, ACMP, ACMN:
case AADD, ASUB:
if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && p.From.Offset != -1<<63 {
p.From.Offset = -p.From.Offset
p.As = complements[p.As]
}
case AADDW, ASUBW, ACMPW, ACMNW:
case AADDW, ASUBW:
if p.From.Type == obj.TYPE_CONST && p.From.Offset < 0 && int32(p.From.Offset) != -1<<31 {
p.From.Offset = -p.From.Offset
p.As = complements[p.As]

View File

@ -665,3 +665,52 @@ func equalVarString8(a string) bool {
// ppc64le:-".*memequal"
return a[:8] == b
}
func cmpToCmn(a, b, c, d int) int {
var c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11 int
// arm64:`CMN`,-`CMP`
if a < -8 {
c1 = 1
}
// arm64:`CMN`,-`CMP`
if a+1 == 0 {
c2 = 1
}
// arm64:`CMN`,-`CMP`
if a+3 != 0 {
c3 = 1
}
// arm64:`CMN`,-`CMP`
if a+b == 0 {
c4 = 1
}
// arm64:`CMN`,-`CMP`
if b+c != 0 {
c5 = 1
}
// arm64:`CMN`,-`CMP`
if a == -c {
c6 = 1
}
// arm64:`CMN`,-`CMP`
if b != -d {
c7 = 1
}
// arm64:`CMN`,-`CMP`
if a*b+c == 0 {
c8 = 1
}
// arm64:`CMN`,-`CMP`
if a*c+b != 0 {
c9 = 1
}
// arm64:`CMP`,-`CMN`
if b*c-a == 0 {
c10 = 1
}
// arm64:`CMP`,-`CMN`
if a*d-b != 0 {
c11 = 1
}
return c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c10 + c11
}