diff options
author | Uros Bizjak <ubizjak@gmail.com> | 2021-04-30 10:15:26 +0200 |
---|---|---|
committer | Uros Bizjak <ubizjak@gmail.com> | 2021-04-30 10:16:30 +0200 |
commit | c111f6066043d3b7bc4141ca0411eae9294aa6c5 (patch) | |
tree | 939d67a66c9ca4f2fe200f34ee1168a88171c97a /gcc/config | |
parent | 4cf3b10f27b1994cf4a9eb12079d85412ebc7cad (diff) | |
download | gcc-c111f6066043d3b7bc4141ca0411eae9294aa6c5.zip gcc-c111f6066043d3b7bc4141ca0411eae9294aa6c5.tar.gz gcc-c111f6066043d3b7bc4141ca0411eae9294aa6c5.tar.bz2 |
i386: Introduce reversed ADC and SBB patterns [PR98060]
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.
Diffstat (limited to 'gcc/config')
-rw-r--r-- | gcc/config/i386/i386.c | 10 | ||||
-rw-r--r-- | gcc/config/i386/i386.md | 65 | ||||
-rw-r--r-- | gcc/config/i386/predicates.md | 16 |
3 files changed, 84 insertions, 7 deletions
diff --git a/gcc/config/i386/i386.c b/gcc/config/i386/i386.c index 48079c8..780da10 100644 --- a/gcc/config/i386/i386.c +++ b/gcc/config/i386/i386.c @@ -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, diff --git a/gcc/config/i386/i386.md b/gcc/config/i386/i386.md index 70ff29b..f79fd12 100644 --- a/gcc/config/i386/i386.md +++ b/gcc/config/i386/i386.md @@ -6773,8 +6773,8 @@ (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])" @@ -6784,6 +6784,20 @@ (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 @@ -6814,6 +6828,20 @@ (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>" @@ -6916,8 +6944,8 @@ [(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}" @@ -6926,6 +6954,20 @@ (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 @@ -6958,6 +7000,21 @@ (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 diff --git a/gcc/config/i386/predicates.md b/gcc/config/i386/predicates.md index 04a03a7..6dfbb08 100644 --- a/gcc/config/i386/predicates.md +++ b/gcc/config/i386/predicates.md @@ -1455,6 +1455,22 @@ 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")) |