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.
This commit is contained in:
parent
4cf3b10f27
commit
c111f60660
4 changed files with 131 additions and 7 deletions
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"))
|
||||
|
|
47
gcc/testsuite/gcc.target/i386/pr98060.c
Normal file
47
gcc/testsuite/gcc.target/i386/pr98060.c
Normal file
|
@ -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);
|
||||
}
|
Loading…
Add table
Reference in a new issue