diff --git a/src/cmd/compile/internal/ssa/_gen/ARM64.rules b/src/cmd/compile/internal/ssa/_gen/ARM64.rules
index f5393b1..56ab68b 100644
--- a/src/cmd/compile/internal/ssa/_gen/ARM64.rules
+++ b/src/cmd/compile/internal/ssa/_gen/ARM64.rules
@@ -1207,6 +1207,31 @@
(CSEL [cc] x (NEG a) flag) => (CSNEG [cc] x a flag)
(CSEL [cc] (NEG a) x flag) => (CSNEG [arm64Negate(cc)] x a flag)
+// Optimize conditional selects that implement integer min/max when CSSC is available.
+(CSEL <t> [cc] x y (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual) => (SMAX x y)
+(CSEL <t> [cc] y x (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual) => (SMAX x y)
+(CSEL <t> [cc] x y (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual) => (SMIN x y)
+(CSEL <t> [cc] y x (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual) => (SMIN x y)
+
+(CSEL <t> [cc] x y (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual) => (SMAXW x y)
+(CSEL <t> [cc] y x (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual) => (SMAXW x y)
+(CSEL <t> [cc] x y (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual) => (SMINW x y)
+(CSEL <t> [cc] y x (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual) => (SMINW x y)
+
+(CSEL <t> [cc] x y (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU) => (UMAX x y)
+(CSEL <t> [cc] y x (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU) => (UMAX x y)
+(CSEL <t> [cc] x y (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU) => (UMIN x y)
+(CSEL <t> [cc] y x (CMP x y)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU) => (UMIN x y)
+
+(CSEL <t> [cc] x y (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU) => (UMAXW x y)
+(CSEL <t> [cc] y x (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU) => (UMAXW x y)
+(CSEL <t> [cc] x y (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU) => (UMINW x y)
+(CSEL <t> [cc] y x (CMPW x y)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU) => (UMINW x y)
+
+// Optimize conditional negation that implements integer abs when CSSC is available.
+(CSNEG <t> [cc] x x (CMPconst [0] x)) && buildcfg.GOARM64.CSSC && is64BitInt(t) && cc == OpARM64GreaterEqual => (ABS x)
+(CSNEG <t> [cc] x x (CMPWconst [0] x)) && buildcfg.GOARM64.CSSC && is32BitInt(t) && cc == OpARM64GreaterEqual => (ABSW x)
+
(SUB x (SUB y z)) => (SUB (ADD <v.Type> x z) y)
(SUB (SUB x y) z) => (SUB x (ADD <y.Type> y z))
diff --git a/src/cmd/compile/internal/ssa/rewriteARM64.go b/src/cmd/compile/internal/ssa/rewriteARM64.go
index 1c57fcf..8fada2b1 100644
--- a/src/cmd/compile/internal/ssa/rewriteARM64.go
+++ b/src/cmd/compile/internal/ssa/rewriteARM64.go
@@ -4896,6 +4896,310 @@
v.AddArg3(x, a, flag)
return true
}
+ // match: (CSEL <t> [cc] x y (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)
+ // result: (SMAX x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)) {
+ break
+ }
+ v.reset(OpARM64SMAX)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)
+ // result: (SMAX x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)) {
+ break
+ }
+ v.reset(OpARM64SMAX)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)
+ // result: (SMIN x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)) {
+ break
+ }
+ v.reset(OpARM64SMIN)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)
+ // result: (SMIN x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)) {
+ break
+ }
+ v.reset(OpARM64SMIN)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)
+ // result: (SMAXW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)) {
+ break
+ }
+ v.reset(OpARM64SMAXW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)
+ // result: (SMAXW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)) {
+ break
+ }
+ v.reset(OpARM64SMAXW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)
+ // result: (SMINW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThan || cc == OpARM64LessEqual)) {
+ break
+ }
+ v.reset(OpARM64SMINW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)
+ // result: (SMINW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThan || cc == OpARM64GreaterEqual)) {
+ break
+ }
+ v.reset(OpARM64SMINW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)
+ // result: (UMAX x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMAX)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)
+ // result: (UMAX x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMAX)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)
+ // result: (UMIN x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMIN)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMP x y))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)
+ // result: (UMIN x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMP {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMIN)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)
+ // result: (UMAXW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMAXW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)
+ // result: (UMAXW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMAXW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] x y (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)
+ // result: (UMINW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ y := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64LessThanU || cc == OpARM64LessEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMINW)
+ v.AddArg2(x, y)
+ return true
+ }
+ // match: (CSEL <t> [cc] y x (CMPW x y))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)
+ // result: (UMINW x y)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ y := v_0
+ x := v_1
+ if v_2.Op != OpARM64CMPW {
+ break
+ }
+ _ = v_2.Args[1]
+ if x != v_2.Args[0] || y != v_2.Args[1] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && (cc == OpARM64GreaterThanU || cc == OpARM64GreaterEqualU)) {
+ break
+ }
+ v.reset(OpARM64UMINW)
+ v.AddArg2(x, y)
+ return true
+ }
// match: (CSEL [cc] x y (InvertFlags cmp))
// result: (CSEL [arm64Invert(cc)] x y cmp)
for {
@@ -5202,6 +5506,34 @@
v_2 := v.Args[2]
v_1 := v.Args[1]
v_0 := v.Args[0]
+ // match: (CSNEG <t> [cc] x x (CMPconst [0] x))
+ // cond: buildcfg.GOARM64.CSSC && is64BitInt(t) && cc == OpARM64GreaterEqual
+ // result: (ABS x)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ if x != v_1 || v_2.Op != OpARM64CMPconst || auxIntToInt64(v_2.AuxInt) != 0 || x != v_2.Args[0] || !(buildcfg.GOARM64.CSSC && is64BitInt(t) && cc == OpARM64GreaterEqual) {
+ break
+ }
+ v.reset(OpARM64ABS)
+ v.AddArg(x)
+ return true
+ }
+ // match: (CSNEG <t> [cc] x x (CMPWconst [0] x))
+ // cond: buildcfg.GOARM64.CSSC && is32BitInt(t) && cc == OpARM64GreaterEqual
+ // result: (ABSW x)
+ for {
+ t := v.Type
+ cc := auxIntToOp(v.AuxInt)
+ x := v_0
+ if x != v_1 || v_2.Op != OpARM64CMPWconst || auxIntToInt32(v_2.AuxInt) != 0 || x != v_2.Args[0] || !(buildcfg.GOARM64.CSSC && is32BitInt(t) && cc == OpARM64GreaterEqual) {
+ break
+ }
+ v.reset(OpARM64ABSW)
+ v.AddArg(x)
+ return true
+ }
// match: (CSNEG [cc] x y (InvertFlags cmp))
// result: (CSNEG [arm64Invert(cc)] x y cmp)
for {
diff --git a/test/codegen/arithmetic.go b/test/codegen/arithmetic.go
index 0bb611a..1c5fbfd 100644
--- a/test/codegen/arithmetic.go
+++ b/test/codegen/arithmetic.go
@@ -794,6 +794,122 @@
return max(a, b)
}
+func Int64MinIf(a, b int64) int64 {
+ var c int64
+ if a < b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "SMIN" -"CMP" -"CSEL"
+ return c
+}
+
+func Int64MaxIf(a, b int64) int64 {
+ var c int64
+ if a > b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "SMAX" -"CMP" -"CSEL"
+ return c
+}
+
+func Uint64MinIf(a, b uint64) uint64 {
+ var c uint64
+ if a < b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "UMIN" -"CMP" -"CSEL"
+ return c
+}
+
+func Uint64MaxIf(a, b uint64) uint64 {
+ var c uint64
+ if a > b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "UMAX" -"CMP" -"CSEL"
+ return c
+}
+
+func Int32MinIf(a, b int32) int32 {
+ var c int32
+ if a <= b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "SMINW" -"CMPW" -"CSEL"
+ return c
+}
+
+func Int32MaxIf(a, b int32) int32 {
+ var c int32
+ if a >= b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "SMAXW" -"CMPW" -"CSEL"
+ return c
+}
+
+func Uint32MinIf(a, b uint32) uint32 {
+ var c uint32
+ if a <= b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "UMINW" -"CMPW" -"CSEL"
+ return c
+}
+
+func Uint32MaxIf(a, b uint32) uint32 {
+ var c uint32
+ if a >= b {
+ c = a
+ } else {
+ c = b
+ }
+ // arm64: "CSEL"
+ // arm64/v8.9: "UMAXW" -"CMPW" -"CSEL"
+ return c
+}
+
+func Int64AbsIf(a int64) int64 {
+ c := a
+ if a < 0 {
+ c = -a
+ }
+ // arm64: "CSNEG"
+ // arm64/v8.9: "ABS" -"CMP" -"CSNEG"
+ return c
+}
+
+func Int32AbsIf(a int32) int32 {
+ c := a
+ if a < 0 {
+ c = -a
+ }
+ // arm64: "CSNEG"
+ // arm64/v8.9: "ABSW" -"CMPW" -"CSNEG"
+ return c
+}
+
// PPC64x: Canonicalization of uint8/uint16 logical immediates
func U16And(v uint16) uint16 {
// ppc64x:"ANDCC [$]32768" -"MOVD"