Fold the following patterns:
- umax (a, add (a, b)) -> [sum, ovf] = add (a, b); ovf ? a : sum
- umin (a, add (a, b)) -> [sum, ovf] = add (a, b); ovf ? sum : a
- umax (a, sub (a, b)) -> [diff, udf] = sub (a, b); udf ? diff : a
- umin (a, sub (a, b)) -> [diff, udf] = sub (a, b); udf ? a : diff
Where ovf/udf is the carry flag that represents overflow in case of
add and underflow in case of sub.
Co-developed-by: Dhruv Chawla <[email protected]>
PR target/116815
gcc/ChangeLog:
* config/i386/i386.md (ovf_add_cmp): New code attribute.
(udf_sub_cmp): Ditto.
(ovf_comm): New int iterator.
(*plus_within_<code><mode>3_<ovf_comm>): New insn and split pattern.
(*minus_within_<code><mode>3): Ditto.
gcc/testsuite/ChangeLog:
* gcc.dg/pr116815.c: New test.
* gcc.target/i386/pr116815.c: New test.
Bootstrapped and regression tested on x86_64-linux-gnu {,-m32}.
Uros.
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index 218377a1770..b812d8b3823 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -27353,6 +27353,72 @@ (define_peephole2
(match_dup 0))]
"peep2_reg_dead_p (2, operands[0])"
[(set (match_dup 2) (match_dup 1))])
+
+;; umax (a, add (a, b)) => [sum, ovf] = add (a, b); ovf ? a : sum
+;; umin (a, add (a, b)) => [sum, ovf] = add (a, b); ovf ? sum : a
+
+(define_code_attr ovf_add_cmp [(umax "geu") (umin "ltu")])
+
+(define_int_iterator ovf_comm [1 2])
+
+(define_insn_and_split "*plus_within_<code><mode>3_<ovf_comm>"
+ [(set (match_operand:SWI248 0 "register_operand")
+ (umaxmin:SWI248
+ (plus:SWI248 (match_operand:SWI248 1 "nonimmediate_operand")
+ (match_operand:SWI248 2 "<general_operand>"))
+ (match_dup ovf_comm)))
+ (clobber (reg:CC FLAGS_REG))]
+ "TARGET_CMOVE
+ && ix86_pre_reload_split ()"
+ "#"
+ "&& 1"
+ [(parallel
+ [(set (reg:CCC FLAGS_REG)
+ (compare:CCC
+ (plus:SWI248 (match_dup 1) (match_dup 2))
+ (match_dup ovf_comm)))
+ (set (match_dup 3)
+ (plus:SWI248 (match_dup 1) (match_dup 2)))])
+ (set (match_dup 0)
+ (if_then_else:SWI248
+ (<ovf_add_cmp> (reg:CCC FLAGS_REG) (const_int 0))
+ (match_dup 3)
+ (match_dup ovf_comm)))]
+{
+ operands[<ovf_comm>] = force_reg (<MODE>mode, operands[<ovf_comm>]);
+ operands[3] = gen_reg_rtx (<MODE>mode);
+})
+
+;; umax (a, sub (a, b)) => [diff, udf] = sub (a, b); udf ? diff : a
+;; umin (a, sub (a, b)) => [diff, udf] = sub (a, b); udf ? a : diff
+
+(define_code_attr udf_sub_cmp [(umax "ltu") (umin "geu")])
+
+(define_insn_and_split "*minus_within_<code><mode>3"
+ [(set (match_operand:SWI248 0 "register_operand")
+ (umaxmin:SWI248
+ (minus:SWI248 (match_operand:SWI248 1 "nonimmediate_operand")
+ (match_operand:SWI248 2 "<general_operand>"))
+ (match_dup 1)))
+ (clobber (reg:CC FLAGS_REG))]
+ "TARGET_CMOVE
+ && ix86_pre_reload_split ()"
+ "#"
+ "&& 1"
+ [(parallel
+ [(set (reg:CC FLAGS_REG)
+ (compare:CC (match_dup 1) (match_dup 2)))
+ (set (match_dup 3)
+ (minus:SWI248 (match_dup 1) (match_dup 2)))])
+ (set (match_dup 0)
+ (if_then_else:SWI248
+ (<udf_sub_cmp> (reg:CC FLAGS_REG) (const_int 0))
+ (match_dup 3)
+ (match_dup 1)))]
+{
+ operands[1] = force_reg (<MODE>mode, operands[1]);
+ operands[3] = gen_reg_rtx (<MODE>mode);
+})
;; Misc patterns (?)
diff --git a/gcc/testsuite/gcc.dg/pr116815.c b/gcc/testsuite/gcc.dg/pr116815.c
new file mode 100644
index 00000000000..b5f1330b335
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr116815.c
@@ -0,0 +1,57 @@
+/* PR target/116815 */
+/* { dg-do run } */
+/* { dg-options "-O2" } */
+
+[[gnu::always_inline]]
+inline unsigned min (unsigned a, unsigned b)
+{
+ return (a < b) ? a : b;
+}
+
+[[gnu::always_inline]]
+inline unsigned max (unsigned a, unsigned b)
+{
+ return (a > b) ? a : b;
+}
+
+[[gnu::noipa]] unsigned
+umaxadd (unsigned a, unsigned b)
+{
+ return max (a + b, a);
+}
+
+[[gnu::noipa]] unsigned
+umaxsub (unsigned a, unsigned b)
+{
+ return max (a - b, a);
+}
+
+[[gnu::noipa]] unsigned
+uminadd (unsigned a, unsigned b)
+{
+ return min (a + b, a);
+}
+
+[[gnu::noipa]] unsigned
+uminsub (unsigned a, unsigned b)
+{
+ return min (a - b, a);
+}
+
+int
+main ()
+{
+ /* Overflows to 0x30000000. */
+ if (umaxadd (0x90000000, 0xa0000000) != 0x90000000)
+ __builtin_abort ();
+
+ if (uminadd (0x90000000, 0xa0000000) != 0x30000000)
+ __builtin_abort ();
+
+ /* Underflows to 0x60000000. */
+ if (umaxsub (0x00000000, 0xa0000000) != 0x60000000)
+ __builtin_abort ();
+
+ if (uminsub (0x00000000, 0xa0000000) != 0x00000000)
+ __builtin_abort ();
+}
diff --git a/gcc/testsuite/gcc.target/i386/pr116815.c
b/gcc/testsuite/gcc.target/i386/pr116815.c
new file mode 100644
index 00000000000..1cd2f72c3b4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr116815.c
@@ -0,0 +1,31 @@
+/* PR target/116815 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+/* { dg-additional-options "-march=pentiumpro" { target ia32 } } */
+
+static inline __attribute__ ((always_inline))
+unsigned max (unsigned a, unsigned b) { return a > b ? a : b; }
+
+static inline __attribute__ ((always_inline))
+unsigned min (unsigned a, unsigned b) { return a < b ? a : b; }
+
+#define OPERATION(op, type, N, exp1, exp2) \
+ unsigned u##op##type##N (unsigned a, unsigned b) { return op (exp1, exp2); }
+
+OPERATION (max, add, 1, a, a + b)
+OPERATION (max, add, 2, a, b + a)
+OPERATION (max, add, 3, a + b, a)
+OPERATION (max, add, 4, b + a, a)
+
+OPERATION (min, add, 1, a, a + b)
+OPERATION (min, add, 2, a, b + a)
+OPERATION (min, add, 3, a + b, a)
+OPERATION (min, add, 4, b + a, a)
+
+OPERATION (max, sub, 1, a, a - b)
+OPERATION (max, sub, 2, a - b, a)
+
+OPERATION (min, sub, 1, a, a - b)
+OPERATION (min, sub, 2, a - b, a)
+
+/* { dg-final { scan-assembler-not "cmp" } } */