i386: Introduce reversed ADC and SBB patterns [PR98060]
authorUros Bizjak <ubizjak@gmail.com>
Fri, 30 Apr 2021 08:15:26 +0000 (10:15 +0200)
committerUros Bizjak <ubizjak@gmail.com>
Fri, 30 Apr 2021 08:16:30 +0000 (10:16 +0200)
The compiler is able to merge LTU comparisons with PLUS or MINUS pattern to
form addition with carry (ADC) and subtraction with borrow (SBB) instructions:

op = op + carry [ADC $0, op]
op = op - carry [SBB $0, op]

The patch introduces reversed ADC and SBB insn patterns:

op = op + !carry [SBB $-1, op]
op = op - !carry [ADC $-1, op]

allowing the compiler to also merge GEU comparisons.

2021-04-30  Uroš Bizjak  <ubizjak@gmail.com>

gcc/
PR target/98060
* config/i386/i386.md (*add<mode>3_carry_0r): New insn pattern.
(*addsi3_carry_zext_0r): Ditto.
(*sub<mode>3_carry_0): Ditto.
(*subsi3_carry_zext_0r): Ditto.
* config/i386/predicates.md (ix86_carry_flag_unset_operator):
New predicate.
* config/i386/i386.c (ix86_rtx_costs) <case PLUS, case MINUS>:
Also consider ix86_carry_flag_unset_operator to calculate
the cost of adc/sbb insn.

gcc/testsuite/

PR target/98060
* gcc.target/i386/pr98060.c: New test.

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

index 48079c8..780da10 100644 (file)
@@ -20057,13 +20057,16 @@ ix86_rtx_costs (rtx x, machine_mode mode, int outer_code_i, int opno,
            }
          else if (GET_CODE (XEXP (x, 0)) == PLUS)
            {
+             rtx op = XEXP (XEXP (x, 0), 0);
+
              /* Add with carry, ignore the cost of adding a carry flag.  */
-             if (ix86_carry_flag_operator (XEXP (XEXP (x, 0), 0), mode))
+             if (ix86_carry_flag_operator (op, mode)
+                 || ix86_carry_flag_unset_operator (op, mode))
                *total = cost->add;
              else
                {
                  *total = cost->lea;
-                 *total += rtx_cost (XEXP (XEXP (x, 0), 0), mode,
+                 *total += rtx_cost (op, mode,
                                      outer_code, opno, speed);
                }
 
@@ -20081,7 +20084,8 @@ ix86_rtx_costs (rtx x, machine_mode mode, int outer_code_i, int opno,
       if (GET_MODE_CLASS (mode) == MODE_INT
          && GET_MODE_SIZE (mode) <= UNITS_PER_WORD
          && GET_CODE (XEXP (x, 0)) == MINUS
-         && ix86_carry_flag_operator (XEXP (XEXP (x, 0), 1), mode))
+         && (ix86_carry_flag_operator (XEXP (XEXP (x, 0), 1), mode)
+             || ix86_carry_flag_unset_operator (XEXP (XEXP (x, 0), 1), mode)))
        {
          *total = cost->add;
          *total += rtx_cost (XEXP (XEXP (x, 0), 0), mode,
index 70ff29b..f79fd12 100644 (file)
 (define_insn "*add<mode>3_carry_0"
   [(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m")
        (plus:SWI
-         (match_operator:SWI 3 "ix86_carry_flag_operator"
-           [(match_operand 2 "flags_reg_operand") (const_int 0)])
+         (match_operator:SWI 2 "ix86_carry_flag_operator"
+           [(reg FLAGS_REG) (const_int 0)])
          (match_operand:SWI 1 "nonimmediate_operand" "0")))
    (clobber (reg:CC FLAGS_REG))]
   "!MEM_P (operands[0]) || rtx_equal_p (operands[0], operands[1])"
    (set_attr "pent_pair" "pu")
    (set_attr "mode" "<MODE>")])
 
+(define_insn "*add<mode>3_carry_0r"
+  [(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m")
+       (plus:SWI
+         (match_operator:SWI 2 "ix86_carry_flag_unset_operator"
+           [(reg FLAGS_REG) (const_int 0)])
+         (match_operand:SWI 1 "nonimmediate_operand" "0")))
+   (clobber (reg:CC FLAGS_REG))]
+  "!MEM_P (operands[0]) || rtx_equal_p (operands[0], operands[1])"
+  "sbb{<imodesuffix>}\t{$-1, %0|%0, -1}"
+  [(set_attr "type" "alu")
+   (set_attr "use_carry" "1")
+   (set_attr "pent_pair" "pu")
+   (set_attr "mode" "<MODE>")])
+
 (define_insn "*addsi3_carry_zext"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (zero_extend:DI
    (set_attr "pent_pair" "pu")
    (set_attr "mode" "SI")])
 
+(define_insn "*addsi3_carry_zext_0r"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (zero_extend:DI
+         (plus:SI (match_operator:SI 2 "ix86_carry_flag_unset_operator"
+                   [(reg FLAGS_REG) (const_int 0)])
+                  (match_operand:SI 1 "register_operand" "0"))))
+   (clobber (reg:CC FLAGS_REG))]
+  "TARGET_64BIT"
+  "sbb{l}\t{$-1, %k0|%k0, -1}"
+  [(set_attr "type" "alu")
+   (set_attr "use_carry" "1")
+   (set_attr "pent_pair" "pu")
+   (set_attr "mode" "SI")])
+
 ;; There is no point to generate ADCX instruction. ADC is shorter and faster.
 
 (define_insn "addcarry<mode>"
   [(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m")
        (minus:SWI
          (match_operand:SWI 1 "nonimmediate_operand" "0")
-         (match_operator:SWI 3 "ix86_carry_flag_operator"
-           [(match_operand 2 "flags_reg_operand") (const_int 0)])))
+         (match_operator:SWI 2 "ix86_carry_flag_operator"
+           [(reg FLAGS_REG) (const_int 0)])))
    (clobber (reg:CC FLAGS_REG))]
   "!MEM_P (operands[0]) || rtx_equal_p (operands[0], operands[1])"
   "sbb{<imodesuffix>}\t{$0, %0|%0, 0}"
    (set_attr "pent_pair" "pu")
    (set_attr "mode" "<MODE>")])
 
+(define_insn "*sub<mode>3_carry_0r"
+  [(set (match_operand:SWI 0 "nonimmediate_operand" "=<r>m")
+       (minus:SWI
+         (match_operand:SWI 1 "nonimmediate_operand" "0")
+         (match_operator:SWI 2 "ix86_carry_flag_unset_operator"
+           [(reg FLAGS_REG) (const_int 0)])))
+   (clobber (reg:CC FLAGS_REG))]
+  "!MEM_P (operands[0]) || rtx_equal_p (operands[0], operands[1])"
+  "adc{<imodesuffix>}\t{$-1, %0|%0, -1}"
+  [(set_attr "type" "alu")
+   (set_attr "use_carry" "1")
+   (set_attr "pent_pair" "pu")
+   (set_attr "mode" "<MODE>")])
+
 (define_insn "*subsi3_carry_zext"
   [(set (match_operand:DI 0 "register_operand" "=r")
        (zero_extend:DI
    (set_attr "pent_pair" "pu")
    (set_attr "mode" "SI")])
 
+(define_insn "*subsi3_carry_zext_0r"
+  [(set (match_operand:DI 0 "register_operand" "=r")
+       (zero_extend:DI
+         (minus:SI
+           (match_operand:SI 1 "register_operand" "0")
+           (match_operator:SI 2 "ix86_carry_flag_unset_operator"
+             [(reg FLAGS_REG) (const_int 0)]))))
+   (clobber (reg:CC FLAGS_REG))]
+  "TARGET_64BIT"
+  "adc{l}\t{$-1, %k0|%k0, -1}"
+  [(set_attr "type" "alu")
+   (set_attr "use_carry" "1")
+   (set_attr "pent_pair" "pu")
+   (set_attr "mode" "SI")])
+
 (define_insn "@sub<mode>3_carry_ccc"
   [(set (reg:CCC FLAGS_REG)
        (compare:CCC
index 04a03a7..6dfbb08 100644 (file)
   return code == LTU;
 })
 
+;; Return true if OP is a valid comparison operator
+;; testing carry flag to be unset.
+(define_predicate "ix86_carry_flag_unset_operator"
+  (match_code "geu,ge")
+{
+  machine_mode inmode = GET_MODE (XEXP (op, 0));
+  enum rtx_code code = GET_CODE (op);
+
+  if (inmode == CCFPmode)
+    code = ix86_fp_compare_code_to_integer (code);
+  else if (inmode != CCmode && inmode != CCCmode && inmode != CCGZmode)
+    return false;
+
+  return code == GEU;
+})
+
 ;; Return true if this comparison only requires testing one flag bit.
 (define_predicate "ix86_trivial_fp_comparison_operator"
   (match_code "gt,ge,unlt,unle,uneq,ltgt,ordered,unordered"))
diff --git a/gcc/testsuite/gcc.target/i386/pr98060.c b/gcc/testsuite/gcc.target/i386/pr98060.c
new file mode 100644 (file)
index 0000000..f82620c
--- /dev/null
@@ -0,0 +1,47 @@
+/* PR target/98060 */
+/* { dg-do compile } */
+/* { dg-options "-O2" } */
+
+/* { dg-final { scan-assembler-not "set" } } */
+/* { dg-final { scan-assembler-times "adc"  4 } } */
+/* { dg-final { scan-assembler-times "sbb"  4 } } */
+
+int r1 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 + (v0 >= v1);
+}
+
+int r2 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 + (v0 <= v1);
+}
+
+int r3 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 + (v0 > v1);
+}
+
+int r4 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 + (v0 < v1);
+}
+
+int r5 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 - (v0 >= v1);
+}
+
+int r6 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 - (v0 <= v1);
+}
+
+int r7 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 - (v0 > v1);
+}
+
+int r8 (unsigned v0, unsigned v1, int v2)
+{
+  return v2 - (v0 < v1);
+}