diff --git a/src/cmd/compile/internal/ssa/gen/ARM64.rules b/src/cmd/compile/internal/ssa/gen/ARM64.rules index a70600918b..3392644e7d 100644 --- a/src/cmd/compile/internal/ssa/gen/ARM64.rules +++ b/src/cmd/compile/internal/ssa/gen/ARM64.rules @@ -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 y))) +(NotEqual (CMPconst [0] z:(MADD a x y))) && z.Uses==1 => (NotEqual (CMN a (MUL x y))) + +(Equal (CMPconst [0] z:(MSUB a x y))) && z.Uses==1 => (Equal (CMP a (MUL x y))) +(NotEqual (CMPconst [0] z:(MSUB a x y))) && z.Uses==1 => (NotEqual (CMP a (MUL x y))) + +(Equal (CMPWconst [0] z:(MADDW a x y))) && z.Uses==1 => (Equal (CMNW a (MULW x y))) +(NotEqual (CMPWconst [0] z:(MADDW a x y))) && z.Uses==1 => (NotEqual (CMNW a (MULW x y))) + +(Equal (CMPWconst [0] z:(MSUBW a x y))) && z.Uses==1 => (Equal (CMPW a (MULW x y))) +(NotEqual (CMPWconst [0] z:(MSUBW a x y))) && z.Uses==1 => (NotEqual (CMPW a (MULW 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) diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go index d39f69c22f..b026532df3 100644 --- a/src/cmd/compile/internal/ssa/rewriteARM64.go +++ b/src/cmd/compile/internal/ssa/rewriteARM64.go @@ -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 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 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 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 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 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 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 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 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 { diff --git a/src/cmd/internal/obj/arm64/obj7.go b/src/cmd/internal/obj/arm64/obj7.go index 6e4335562d..7d20beb5d6 100644 --- a/src/cmd/internal/obj/arm64/obj7.go +++ b/src/cmd/internal/obj/arm64/obj7.go @@ -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] diff --git a/test/codegen/comparisons.go b/test/codegen/comparisons.go index b1dba2482f..4b66044804 100644 --- a/test/codegen/comparisons.go +++ b/test/codegen/comparisons.go @@ -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 +}