aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorUros Bizjak <ubizjak@gmail.com>2024-12-05 17:02:46 +0100
committerUros Bizjak <ubizjak@gmail.com>2024-12-05 17:13:55 +0100
commitb3cb0c3302a7c16e661a08c15c897c8f7bbb5d23 (patch)
tree64d702d9e830d4f297c7938fe07daced3441e81a /gcc
parenta92b2be97f369ae4c6e1cdcbb7a45525994afaad (diff)
downloadgcc-b3cb0c3302a7c16e661a08c15c897c8f7bbb5d23.zip
gcc-b3cb0c3302a7c16e661a08c15c897c8f7bbb5d23.tar.gz
gcc-b3cb0c3302a7c16e661a08c15c897c8f7bbb5d23.tar.bz2
i386: Fix addcarry/subborrow issues [PR117860]
Fix several things to enable combine to handle addcarry/subborrow patterns: - Fix wrong canonical form of addcarry<mode> insn and friends. For commutative operand (PLUS RTX) binary operand (LTU) takes precedence before unary operand (ZERO_EXTEND). - Swap operands of GTU comparison to canonicalize addcarry/subborrow comparison. Again, the canonical form of the compare is PLUS RTX before ZERO_EXTEND RTX. GTU comparison is not a carry flag comparison, so we have to swap operands in x86_canonicalize_comparison to a non-canonical form to use LTU comparison. - Return correct compare mode (CCCmode) for addcarry/subborrow pattern from ix86_cc_mode, so combine is able to emit required compare mode for combined insn. - Add *subborrow<mode>_1 pattern having const_scalar_int_operand predicate. Here, canonicalization of SUB (op1, const) RTX to PLUS (op1, -const) requires negation of constant operand when ckecking operands. With the above changes, combine is able to create *addcarry_1/*subborrow_1 pattern with immediate operand for the testcase in the PR: SomeAddFunc: addq %rcx, %rsi # 10 [c=4 l=3] adddi3_cc_overflow_1/0 movq %rdi, %rax # 33 [c=4 l=3] *movdi_internal/3 adcq $5, %rdx # 19 [c=4 l=4] *addcarrydi_1/0 movq %rsi, (%rdi) # 23 [c=4 l=3] *movdi_internal/5 movq %rdx, 8(%rdi) # 24 [c=4 l=4] *movdi_internal/5 setc %dl # 39 [c=4 l=3] *setcc_qi movzbl %dl, %edx # 40 [c=4 l=3] zero_extendqidi2/0 movq %rdx, 16(%rdi) # 26 [c=4 l=4] *movdi_internal/5 ret # 43 [c=0 l=1] simple_return_internal SomeSubFunc: subq %rcx, %rsi # 10 [c=4 l=3] *subdi_3/0 movq %rdi, %rax # 42 [c=4 l=3] *movdi_internal/3 sbbq $17, %rdx # 19 [c=4 l=4] *subborrowdi_1/0 movq %rsi, (%rdi) # 33 [c=4 l=3] *movdi_internal/5 sbbq %rcx, %rcx # 29 [c=8 l=3] *x86_movdicc_0_m1_neg movq %rdx, 8(%rdi) # 34 [c=4 l=4] *movdi_internal/5 movq %rcx, 16(%rdi) # 35 [c=4 l=4] *movdi_internal/5 ret # 51 [c=0 l=1] simple_return_internal PR target/117860 gcc/ChangeLog: * config/i386/i386.cc (ix86_canonicalize_comparison): Swap operands of GTU comparison to canonicalize addcarry/subborrow comparison. (ix86_cc_mode): Return CCCmode for the comparison of addcarry/subborrow pattern. * config/i386/i386.md (addcarry<mode>): Swap operands of PLUS RTX to make it canonical. (*addcarry<mode>_1): Ditto. (addcarry peephole2s): Update RTXes for addcarry<mode>_1 change. (*add<dwi>3_doubleword_cc_overflow_1): Ditto. (*subborrow<mode>_1): New insn pattern. gcc/testsuite/ChangeLog: * gcc.target/i386/pr117860.c: New test.
Diffstat (limited to 'gcc')
-rw-r--r--gcc/config/i386/i386.cc23
-rw-r--r--gcc/config/i386/i386.md85
-rw-r--r--gcc/testsuite/gcc.target/i386/pr117860.c52
3 files changed, 140 insertions, 20 deletions
diff --git a/gcc/config/i386/i386.cc b/gcc/config/i386/i386.cc
index 0beeb51..23ff16b 100644
--- a/gcc/config/i386/i386.cc
+++ b/gcc/config/i386/i386.cc
@@ -578,11 +578,25 @@ ix86_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
{
std::swap (*op0, *op1);
*code = (int) scode;
+ return;
}
}
+
+ /* Swap operands of GTU comparison to canonicalize
+ addcarry/subborrow comparison. */
+ if (!op0_preserve_value
+ && *code == GTU
+ && GET_CODE (*op0) == PLUS
+ && ix86_carry_flag_operator (XEXP (*op0, 0), VOIDmode)
+ && GET_CODE (XEXP (*op0, 1)) == ZERO_EXTEND
+ && GET_CODE (*op1) == ZERO_EXTEND)
+ {
+ std::swap (*op0, *op1);
+ *code = (int) swap_condition ((enum rtx_code) *code);
+ return;
+ }
}
-
/* Hook to determine if one function can safely inline another. */
static bool
@@ -16479,6 +16493,13 @@ ix86_cc_mode (enum rtx_code code, rtx op0, rtx op1)
&& GET_CODE (op1) == GEU
&& GET_MODE (XEXP (op1, 0)) == CCCmode)
return CCCmode;
+ /* Similarly for the comparison of addcarry/subborrow pattern. */
+ else if (code == LTU
+ && GET_CODE (op0) == ZERO_EXTEND
+ && GET_CODE (op1) == PLUS
+ && ix86_carry_flag_operator (XEXP (op1, 0), VOIDmode)
+ && GET_CODE (XEXP (op1, 1)) == ZERO_EXTEND)
+ return CCCmode;
else
return CCmode;
case GTU: /* CF=0 & ZF=0 */
diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md
index ffbb107..ec816be 100644
--- a/gcc/config/i386/i386.md
+++ b/gcc/config/i386/i386.md
@@ -9036,12 +9036,12 @@
(match_operand:SWI48 1 "nonimmediate_operand" "%0,0,rm,r"))
(match_operand:SWI48 2 "nonimmediate_operand" "r,rm,r,m")))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 2))
(match_operator:<DWI> 4 "ix86_carry_flag_operator"
- [(match_dup 3) (const_int 0)]))))
+ [(match_dup 3) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 2)))))
(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm,r,r,r")
(plus:SWI48 (plus:SWI48 (match_op_dup 5
- [(match_dup 3) (const_int 0)])
+ [(match_dup 3) (const_int 0)])
(match_dup 1))
(match_dup 2)))]
"ix86_binary_operator_ok (PLUS, <MODE>mode, operands, TARGET_APX_NDD)"
@@ -9068,9 +9068,9 @@
(match_operand:SWI48 0 "general_reg_operand"))
(match_operand:SWI48 1 "memory_operand")))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 1))
(match_operator:<DWI> 3 "ix86_carry_flag_operator"
- [(match_dup 2) (const_int 0)]))))
+ [(match_dup 2) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 1)))))
(set (match_dup 0)
(plus:SWI48 (plus:SWI48 (match_op_dup 4
[(match_dup 2) (const_int 0)])
@@ -9090,9 +9090,9 @@
(match_dup 1))
(match_dup 0)))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 0))
(match_op_dup 3
- [(match_dup 2) (const_int 0)]))))
+ [(match_dup 2) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 0)))))
(set (match_dup 1)
(plus:SWI48 (plus:SWI48 (match_op_dup 4
[(match_dup 2) (const_int 0)])
@@ -9113,9 +9113,9 @@
(match_dup 0))
(match_operand:SWI48 2 "memory_operand")))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 2))
(match_operator:<DWI> 4 "ix86_carry_flag_operator"
- [(match_dup 3) (const_int 0)]))))
+ [(match_dup 3) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 2)))))
(set (match_dup 0)
(plus:SWI48 (plus:SWI48 (match_op_dup 5
[(match_dup 3) (const_int 0)])
@@ -9137,9 +9137,9 @@
(match_dup 1))
(match_dup 0)))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 0))
(match_op_dup 4
- [(match_dup 3) (const_int 0)]))))
+ [(match_dup 3) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 0)))))
(set (match_dup 1)
(plus:SWI48 (plus:SWI48 (match_op_dup 5
[(match_dup 3) (const_int 0)])
@@ -9158,9 +9158,9 @@
(match_operand:SWI48 0 "general_reg_operand"))
(match_operand:SWI48 1 "memory_operand")))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 1))
(match_operator:<DWI> 3 "ix86_carry_flag_operator"
- [(match_dup 2) (const_int 0)]))))
+ [(match_dup 2) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 1)))))
(set (match_dup 0)
(plus:SWI48 (plus:SWI48 (match_op_dup 4
[(match_dup 2) (const_int 0)])
@@ -9188,9 +9188,9 @@
(match_dup 1))
(match_dup 0)))
(plus:<DWI>
- (zero_extend:<DWI> (match_dup 0))
(match_op_dup 3
- [(match_dup 2) (const_int 0)]))))
+ [(match_dup 2) (const_int 0)])
+ (zero_extend:<DWI> (match_dup 0)))))
(set (match_dup 1)
(plus:SWI48 (plus:SWI48 (match_op_dup 4
[(match_dup 2) (const_int 0)])
@@ -9222,9 +9222,9 @@
(match_operand:SWI48 1 "nonimmediate_operand" "%0,rm"))
(match_operand:SWI48 2 "x86_64_immediate_operand" "e,e")))
(plus:<DWI>
- (match_operand:<DWI> 6 "const_scalar_int_operand")
(match_operator:<DWI> 4 "ix86_carry_flag_operator"
- [(match_dup 3) (const_int 0)]))))
+ [(match_dup 3) (const_int 0)])
+ (match_operand:<DWI> 6 "const_scalar_int_operand"))))
(set (match_operand:SWI48 0 "nonimmediate_operand" "=rm,r")
(plus:SWI48 (plus:SWI48 (match_op_dup 5
[(match_dup 3) (const_int 0)])
@@ -9748,6 +9748,53 @@
(minus:SWI48 (match_dup 1) (match_dup 2)))])]
"ix86_binary_operator_ok (MINUS, <MODE>mode, operands, TARGET_APX_NDD)")
+(define_insn "*subborrow<mode>_1"
+ [(set (reg:CCC FLAGS_REG)
+ (compare:CCC
+ (zero_extend:<DWI>
+ (match_operand:SWI48 1 "nonimmediate_operand" "0,rm"))
+ (plus:<DWI>
+ (match_operator:<DWI> 4 "ix86_carry_flag_operator"
+ [(match_operand 3 "flags_reg_operand") (const_int 0)])
+ (match_operand:<DWI> 6 "const_scalar_int_operand"))))
+ (set (match_operand:SWI48 0 "nonimmediate_operand" "=rm,r")
+ (plus:SWI48 (minus:SWI48
+ (match_dup 1)
+ (match_operator:SWI48 5 "ix86_carry_flag_operator"
+ [(match_dup 3) (const_int 0)]))
+ (match_operand:SWI48 2 "x86_64_immediate_operand" "e,e")))]
+ "ix86_binary_operator_ok (MINUS, <MODE>mode, operands, TARGET_APX_NDD)
+ && CONST_INT_P (operands[2])
+ /* Check that operands[6] is -operands[2] zero extended from
+ <MODE>mode to <DWI>mode. */
+ && ((<MODE>mode == SImode || -INTVAL (operands[2]) >= 0)
+ ? (CONST_INT_P (operands[6])
+ && (UINTVAL (operands[6])
+ == ((unsigned HOST_WIDE_INT) -INTVAL (operands[2])
+ & GET_MODE_MASK (<MODE>mode))))
+ : (CONST_WIDE_INT_P (operands[6])
+ && CONST_WIDE_INT_NUNITS (operands[6]) == 2
+ && ((unsigned HOST_WIDE_INT) CONST_WIDE_INT_ELT (operands[6], 0)
+ == (unsigned HOST_WIDE_INT) -INTVAL (operands[2]))
+ && CONST_WIDE_INT_ELT (operands[6], 1) == 0))"
+{
+ bool use_ndd = get_attr_isa (insn) == ISA_APX_NDD;
+
+ operands[2] = GEN_INT (-INTVAL (operands[2]));
+
+ return use_ndd ? "sbb{<imodesuffix>}\t{%2, %1, %0|%0, %1, %2}"
+ : "sbb{<imodesuffix>}\t{%2, %0|%0, %2}";
+}
+ [(set_attr "isa" "*,apx_ndd")
+ (set_attr "type" "alu")
+ (set_attr "use_carry" "1")
+ (set_attr "pent_pair" "pu")
+ (set_attr "mode" "<MODE>")
+ (set (attr "length_immediate")
+ (if_then_else (match_test "IN_RANGE (-INTVAL (operands[2]), -128, 127)")
+ (const_string "1")
+ (const_string "4")))])
+
(define_expand "uaddc<mode>5"
[(match_operand:SWI48 0 "register_operand")
(match_operand:SWI48 1 "register_operand")
@@ -10040,8 +10087,8 @@
(match_dup 4))
(match_dup 5)))
(plus:<DWI>
- (match_dup 6)
- (ltu:<DWI> (reg:CC FLAGS_REG) (const_int 0)))))
+ (ltu:<DWI> (reg:CC FLAGS_REG) (const_int 0))
+ (match_dup 6))))
(set (match_dup 3)
(plus:DWIH
(plus:DWIH (ltu:DWIH (reg:CC FLAGS_REG) (const_int 0))
diff --git a/gcc/testsuite/gcc.target/i386/pr117860.c b/gcc/testsuite/gcc.target/i386/pr117860.c
new file mode 100644
index 0000000..22ed0af
--- /dev/null
+++ b/gcc/testsuite/gcc.target/i386/pr117860.c
@@ -0,0 +1,52 @@
+/* PR target/117116 */
+/* { dg-do compile { target { ! ia32 } } } */
+/* { dg-options "-O2 -masm=att" } */
+
+#include <stdint.h>
+
+#if (defined(__GNUC__) || defined(__clang__))
+#include <immintrin.h>
+#elif defined(_MSC_VER)
+#include <intrin.h>
+#endif
+
+typedef struct {
+ uint64_t lo64;
+ uint64_t mid64;
+ uint64_t hi64;
+} UInt192;
+
+UInt192 SomeAddFunc(uint64_t a_lo, uint64_t a_hi, uint64_t b) {
+ UInt192 result;
+ unsigned char cf;
+ unsigned long long sum;
+
+ cf = _addcarry_u64(0, a_lo, b, &sum);
+ result.lo64 = sum;
+
+ cf = _addcarry_u64(cf, a_hi, 5, &sum);
+ result.mid64 = sum;
+ result.hi64 = cf;
+
+ return result;
+}
+
+/* { dg-final { scan-assembler "adcq\[ \\t\]+\\\$5," } } */
+
+UInt192 SomeSubFunc(uint64_t a_lo, uint64_t a_hi, uint64_t b) {
+ UInt192 result;
+ unsigned char cf;
+ unsigned long long diff;
+
+ cf = _subborrow_u64(0, a_lo, b, &diff);
+ result.lo64 = diff;
+
+ cf = _subborrow_u64(cf, a_hi, 17, &diff);
+ result.mid64 = diff;
+ (void)_subborrow_u64(cf, 0, 0, &diff);
+ result.hi64 = diff;
+
+ return result;
+}
+
+/* { dg-final { scan-assembler "sbbq\[ \\t\]+\\\$17," } } */