]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
i386: Extend *cmp<mode>_minus_1 optimizations also to plus with CONST_INT [PR120360]
authorJakub Jelinek <jakub@redhat.com>
Thu, 22 May 2025 07:09:48 +0000 (09:09 +0200)
committerJakub Jelinek <jakub@gcc.gnu.org>
Thu, 22 May 2025 07:09:48 +0000 (09:09 +0200)
As mentioned by Linus, we can't optimize comparison of otherwise unused
result of plus with CONST_INT second operand, compared against zero.
This can be done using just cmp instruction with negated constant and say
js/jns/je/jne etc. conditional jumps (or setcc).
We already have *cmp<mode>_minus_1 instruction which handles it when
(as shown in foo in the testcase) the IL has MINUS rather than PLUS,
but for constants except for the minimum value the canonical form is
with PLUS.

The following patch adds a new pattern and predicate to handle this.

2025-05-22  Jakub Jelinek  <jakub@redhat.com>

PR target/120360
* config/i386/predicates.md (x86_64_neg_const_int_operand): New
predicate.
* config/i386/i386.md (*cmp<mode>_plus_1): New pattern.

* gcc.target/i386/pr120360.c: New test.

gcc/config/i386/i386.md
gcc/config/i386/predicates.md
gcc/testsuite/gcc.target/i386/pr120360.c [new file with mode: 0644]

index af4f12956251d846577d233ef9d28d9e1700b9b7..b7a18d583da3707a901ece3d1b0ca8b66cb8cfa8 100644 (file)
   [(set_attr "type" "icmp")
    (set_attr "mode" "<MODE>")])
 
+(define_insn "*cmp<mode>_plus_1"
+  [(set (reg FLAGS_REG)
+       (compare
+         (plus:SWI (match_operand:SWI 0 "nonimmediate_operand" "<r>m")
+                   (match_operand:SWI 1 "x86_64_neg_const_int_operand" "n"))
+         (const_int 0)))]
+  "ix86_match_ccmode (insn, CCGOCmode)"
+{
+  operands[1] = gen_int_mode (-INTVAL (operands[1]), <MODE>mode);
+  return "cmp{<imodesuffix>}\t{%1, %0|%0, %1}";
+}
+  [(set_attr "type" "icmp")
+   (set_attr "mode" "<MODE>")])
+
 (define_insn "*cmpqi_ext<mode>_1"
   [(set (reg FLAGS_REG)
        (compare
index 10ed6a5de56e308d8de398258cbf1fa860dc31ac..1bd63b2367e13abfb0c2da94982b97a646952c69 100644 (file)
   return false;
 })
 
+;; Return true if VALUE is a constant integer whose negation satisfies
+;; x86_64_immediate_operand.
+(define_predicate "x86_64_neg_const_int_operand"
+  (match_code "const_int")
+{
+  HOST_WIDE_INT val = -UINTVAL (op);
+  if (mode == DImode && trunc_int_for_mode (val, SImode) != val)
+    return false;
+  if (flag_cf_protection & CF_BRANCH)
+    {
+      unsigned HOST_WIDE_INT endbr = TARGET_64BIT ? 0xfa1e0ff3 : 0xfb1e0ff3;
+      if ((val & HOST_WIDE_INT_C (0xffffffff)) == endbr)
+       return false;
+    }
+  return true;
+})
+
 ;; Return true if VALUE is a constant integer whose low and high words satisfy
 ;; x86_64_immediate_operand.
 (define_predicate "x86_64_hilo_int_operand"
diff --git a/gcc/testsuite/gcc.target/i386/pr120360.c b/gcc/testsuite/gcc.target/i386/pr120360.c
new file mode 100644 (file)
index 0000000..69c510e
--- /dev/null
@@ -0,0 +1,36 @@
+/* PR target/120360 */
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-stack-protector -masm=att" } */
+/* { dg-additional-options "-fno-pic" { target { ! *-*-darwin* } } } */
+/* { dg-final { scan-assembler-times "\tjn*s\t" 3 } } */
+/* { dg-final { scan-assembler-times "\tcmp\[lq]\t%" 1 } } */
+/* { dg-final { scan-assembler-times "\tcmp\[lq]\t\\\$-1234," 1 } } */
+/* { dg-final { scan-assembler-times "\tcmp\[lq]\t\\\$2345," 1 } } */
+/* { dg-final { scan-assembler-not "\tadd\[lq]\t" { target { ! *-*-darwin* } } } } */
+/* { dg-final { scan-assembler-not "\tsub\[lq]\t" { target { ! *-*-darwin* } } } } */
+
+void qux (unsigned long);
+
+void
+foo (unsigned long x, unsigned long y)
+{
+  unsigned long z = x - y;
+  if ((long) z < 0)
+    qux (x);
+}
+
+void
+bar (unsigned long x)
+{
+  unsigned long z = x + 1234;
+  if ((long) z < 0)
+    qux (x);
+}
+
+void
+baz (unsigned long x)
+{
+  unsigned long z = x - 2345;
+  if ((long) z < 0)
+    qux (x);
+}