[arm] early split most DImode comparison operations.
This patch does most of the work for early splitting the DImode comparisons. We now handle EQ, NE, LT, GE, LTU and GEU during early expansion, in addition to EQ and NE, for which the expansion has now been reworked to use a standard conditional-compare pattern already in the back-end. To handle this we introduce two new condition flag modes that are used when comparing the upper words of decomposed DImode values: one for signed, and one for unsigned comparisons. CC_Bmode (B for Borrow) is essentially the inverse of CC_Cmode and is used when the carry flag is set by a subtraction of unsigned values. * config/arm/arm-modes.def (CC_NV, CC_B): New CC modes. * config/arm/arm.c (arm_select_cc_mode): Recognize constructs that need these modes. (arm_gen_dicompare_reg): New code to early expand the sub-operations of EQ, NE, LT, GE, LTU and GEU. * config/arm/iterators.md (CC_EXTEND): New code attribute. * config/arm/predicates.md (arm_adcimm_operand): New predicate.. * config/arm/arm.md (cmpsi3_carryin_<CC_EXTEND>out): New pattern. (cmpsi3_imm_carryin_<CC_EXTEND>out): Likewise. (cmpsi3_0_carryin_<CC_EXTEND>out): Likewise. From-SVN: r277179
This commit is contained in:
parent
22060d0e57
commit
8b8ab8f473
@ -1,3 +1,16 @@
|
||||
2019-10-18 Richard Earnshaw <rearnsha@arm.com>
|
||||
|
||||
* config/arm/arm-modes.def (CC_NV, CC_B): New CC modes.
|
||||
* config/arm/arm.c (arm_select_cc_mode): Recognize constructs that
|
||||
need these modes.
|
||||
(arm_gen_dicompare_reg): New code to early expand the sub-operations
|
||||
of EQ, NE, LT, GE, LTU and GEU.
|
||||
* config/arm/iterators.md (CC_EXTEND): New code attribute.
|
||||
* config/arm/predicates.md (arm_adcimm_operand): New predicate..
|
||||
* config/arm/arm.md (cmpsi3_carryin_<CC_EXTEND>out): New pattern.
|
||||
(cmpsi3_imm_carryin_<CC_EXTEND>out): Likewise.
|
||||
(cmpsi3_0_carryin_<CC_EXTEND>out): Likewise.
|
||||
|
||||
2019-10-18 Richard Earnshaw <rearnsha@arm.com>
|
||||
|
||||
* config/arm/arm.md (cbranchdi4): Accept reg_or_int_operand for
|
||||
|
@ -34,12 +34,16 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE)
|
||||
CC_Cmode should be used if only the C flag is set correctly, after an
|
||||
addition.
|
||||
CC_Nmode should be used if only the N (sign) flag is set correctly
|
||||
CC_NVmode should be used if only the N and V bits are set correctly,
|
||||
(used for signed comparisons when the carry is propagated in).
|
||||
CC_CZmode should be used if only the C and Z flags are correct
|
||||
(used for DImode unsigned comparisons).
|
||||
CC_RSBmode should be used where the comparison is set by an RSB immediate,
|
||||
or NEG instruction. The form of the comparison for (const - reg) will
|
||||
be (COMPARE (not (reg)) (~const)).
|
||||
CC_NCVmode should be used if only the N, C, and V flags are correct
|
||||
CC_Bmode should be used if only the C flag is correct after a subtract
|
||||
(eg after an unsigned borrow with carry-in propagation).
|
||||
(used for DImode signed comparisons).
|
||||
CCmode should be used otherwise. */
|
||||
|
||||
@ -47,6 +51,7 @@ CC_MODE (CC_NOOV);
|
||||
CC_MODE (CC_Z);
|
||||
CC_MODE (CC_CZ);
|
||||
CC_MODE (CC_NCV);
|
||||
CC_MODE (CC_NV);
|
||||
CC_MODE (CC_SWP);
|
||||
CC_MODE (CC_RSB);
|
||||
CC_MODE (CCFP);
|
||||
@ -62,6 +67,7 @@ CC_MODE (CC_DLTU);
|
||||
CC_MODE (CC_DGEU);
|
||||
CC_MODE (CC_DGTU);
|
||||
CC_MODE (CC_C);
|
||||
CC_MODE (CC_B);
|
||||
CC_MODE (CC_N);
|
||||
CC_MODE (CC_V);
|
||||
|
||||
|
@ -15348,6 +15348,22 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
|
||||
&& (rtx_equal_p (XEXP (x, 0), y) || rtx_equal_p (XEXP (x, 1), y)))
|
||||
return CC_Cmode;
|
||||
|
||||
if (GET_MODE (x) == DImode
|
||||
&& (op == GE || op == LT)
|
||||
&& GET_CODE (x) == SIGN_EXTEND
|
||||
&& ((GET_CODE (y) == PLUS
|
||||
&& arm_borrow_operation (XEXP (y, 0), DImode))
|
||||
|| arm_borrow_operation (y, DImode)))
|
||||
return CC_NVmode;
|
||||
|
||||
if (GET_MODE (x) == DImode
|
||||
&& (op == GEU || op == LTU)
|
||||
&& GET_CODE (x) == ZERO_EXTEND
|
||||
&& ((GET_CODE (y) == PLUS
|
||||
&& arm_borrow_operation (XEXP (y, 0), DImode))
|
||||
|| arm_borrow_operation (y, DImode)))
|
||||
return CC_Bmode;
|
||||
|
||||
if (GET_MODE (x) == DImode || GET_MODE (y) == DImode)
|
||||
{
|
||||
switch (op)
|
||||
@ -15410,16 +15426,198 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
|
||||
static rtx
|
||||
arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch)
|
||||
{
|
||||
/* We don't currently handle DImode in thumb1, but rely on libgcc. */
|
||||
machine_mode mode;
|
||||
rtx cc_reg;
|
||||
|
||||
/* We don't currently handle DImode in thumb1, but rely on libgcc. */
|
||||
gcc_assert (TARGET_32BIT);
|
||||
|
||||
rtx x_lo = simplify_gen_subreg (SImode, x, DImode,
|
||||
subreg_lowpart_offset (SImode, DImode));
|
||||
rtx x_hi = simplify_gen_subreg (SImode, x, DImode,
|
||||
subreg_highpart_offset (SImode, DImode));
|
||||
rtx y_lo = simplify_gen_subreg (SImode, y, DImode,
|
||||
subreg_lowpart_offset (SImode, DImode));
|
||||
rtx y_hi = simplify_gen_subreg (SImode, y, DImode,
|
||||
subreg_highpart_offset (SImode, DImode));
|
||||
switch (code)
|
||||
{
|
||||
case EQ:
|
||||
case NE:
|
||||
{
|
||||
/* We should never have X as a const_int in this case. */
|
||||
gcc_assert (!CONST_INT_P (x));
|
||||
|
||||
if (y_lo == const0_rtx || y_hi == const0_rtx)
|
||||
{
|
||||
if (y_lo != const0_rtx)
|
||||
{
|
||||
rtx scratch2 = scratch ? scratch : gen_reg_rtx (SImode);
|
||||
|
||||
gcc_assert (y_hi == const0_rtx);
|
||||
y_lo = gen_int_mode (-INTVAL (y_lo), SImode);
|
||||
if (!arm_add_operand (y_lo, SImode))
|
||||
y_lo = force_reg (SImode, y_lo);
|
||||
emit_insn (gen_addsi3 (scratch2, x_lo, y_lo));
|
||||
x_lo = scratch2;
|
||||
}
|
||||
else if (y_hi != const0_rtx)
|
||||
{
|
||||
rtx scratch2 = scratch ? scratch : gen_reg_rtx (SImode);
|
||||
|
||||
y_hi = gen_int_mode (-INTVAL (y_hi), SImode);
|
||||
if (!arm_add_operand (y_hi, SImode))
|
||||
y_hi = force_reg (SImode, y_hi);
|
||||
emit_insn (gen_addsi3 (scratch2, x_hi, y_hi));
|
||||
x_hi = scratch2;
|
||||
}
|
||||
|
||||
if (!scratch)
|
||||
{
|
||||
gcc_assert (!reload_completed);
|
||||
scratch = gen_rtx_SCRATCH (SImode);
|
||||
}
|
||||
|
||||
rtx clobber = gen_rtx_CLOBBER (VOIDmode, scratch);
|
||||
cc_reg = gen_rtx_REG (CC_NOOVmode, CC_REGNUM);
|
||||
|
||||
rtx set
|
||||
= gen_rtx_SET (cc_reg,
|
||||
gen_rtx_COMPARE (CC_NOOVmode,
|
||||
gen_rtx_IOR (SImode, x_lo, x_hi),
|
||||
const0_rtx));
|
||||
emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set,
|
||||
clobber)));
|
||||
return cc_reg;
|
||||
}
|
||||
|
||||
if (!arm_add_operand (y_lo, SImode))
|
||||
y_lo = force_reg (SImode, y_lo);
|
||||
|
||||
if (!arm_add_operand (y_hi, SImode))
|
||||
y_hi = force_reg (SImode, y_hi);
|
||||
|
||||
rtx cmp1 = gen_rtx_NE (SImode, x_lo, y_lo);
|
||||
rtx cmp2 = gen_rtx_NE (SImode, x_hi, y_hi);
|
||||
rtx conjunction = gen_rtx_IOR (SImode, cmp1, cmp2);
|
||||
mode = SELECT_CC_MODE (code, conjunction, const0_rtx);
|
||||
cc_reg = gen_rtx_REG (mode, CC_REGNUM);
|
||||
|
||||
emit_insn (gen_rtx_SET (cc_reg,
|
||||
gen_rtx_COMPARE (VOIDmode, conjunction,
|
||||
const0_rtx)));
|
||||
return cc_reg;
|
||||
}
|
||||
|
||||
case LT:
|
||||
case GE:
|
||||
{
|
||||
if (y_lo == const0_rtx)
|
||||
{
|
||||
/* If the low word of y is 0, then this is simply a normal
|
||||
compare of the upper words. */
|
||||
if (!arm_add_operand (y_hi, SImode))
|
||||
y_hi = force_reg (SImode, y_hi);
|
||||
|
||||
return arm_gen_compare_reg (code, x_hi, y_hi, NULL_RTX);
|
||||
}
|
||||
|
||||
if (!arm_add_operand (y_lo, SImode))
|
||||
y_lo = force_reg (SImode, y_lo);
|
||||
|
||||
/* Just for now. */
|
||||
if (!register_operand (x_lo, SImode))
|
||||
x_lo = force_reg (SImode, x_lo);
|
||||
|
||||
rtx cmp1
|
||||
= gen_rtx_LTU (DImode,
|
||||
arm_gen_compare_reg (LTU, x_lo, y_lo, NULL_RTX),
|
||||
const0_rtx);
|
||||
|
||||
if (!scratch)
|
||||
scratch = gen_rtx_SCRATCH (SImode);
|
||||
if (!arm_not_operand (y_hi, SImode))
|
||||
y_hi = force_reg (SImode, y_hi);
|
||||
|
||||
/* Just for now. */
|
||||
if (!register_operand (x_hi, SImode))
|
||||
x_hi = force_reg (SImode, x_hi);
|
||||
|
||||
rtx_insn *insn;
|
||||
if (y_hi == const0_rtx)
|
||||
insn = emit_insn (gen_cmpsi3_0_carryin_CC_NVout (scratch, x_hi,
|
||||
cmp1));
|
||||
else if (CONST_INT_P (y_hi))
|
||||
insn = emit_insn (gen_cmpsi3_imm_carryin_CC_NVout (scratch, x_hi,
|
||||
y_hi, cmp1));
|
||||
else
|
||||
insn = emit_insn (gen_cmpsi3_carryin_CC_NVout (scratch, x_hi, y_hi,
|
||||
cmp1));
|
||||
return SET_DEST (single_set (insn));
|
||||
}
|
||||
|
||||
case LTU:
|
||||
case GEU:
|
||||
{
|
||||
if (y_lo == const0_rtx)
|
||||
{
|
||||
/* If the low word of y is 0, then this is simply a normal
|
||||
compare of the upper words. */
|
||||
if (!arm_add_operand (y_hi, SImode))
|
||||
y_hi = force_reg (SImode, y_hi);
|
||||
|
||||
return arm_gen_compare_reg (code, x_hi, y_hi, NULL_RTX);
|
||||
}
|
||||
|
||||
if (!arm_add_operand (y_lo, SImode))
|
||||
y_lo = force_reg (SImode, y_lo);
|
||||
|
||||
/* Just for now. */
|
||||
if (!register_operand (x_lo, SImode))
|
||||
x_lo = force_reg (SImode, x_lo);
|
||||
|
||||
rtx cmp1
|
||||
= gen_rtx_LTU (DImode,
|
||||
arm_gen_compare_reg (LTU, x_lo, y_lo, NULL_RTX),
|
||||
const0_rtx);
|
||||
|
||||
if (!scratch)
|
||||
scratch = gen_rtx_SCRATCH (SImode);
|
||||
if (!arm_not_operand (y_hi, SImode))
|
||||
y_hi = force_reg (SImode, y_hi);
|
||||
|
||||
/* Just for now. */
|
||||
if (!register_operand (x_hi, SImode))
|
||||
x_hi = force_reg (SImode, x_hi);
|
||||
|
||||
rtx_insn *insn;
|
||||
if (y_hi == const0_rtx)
|
||||
insn = emit_insn (gen_cmpsi3_0_carryin_CC_Bout (scratch, x_hi,
|
||||
cmp1));
|
||||
else if (CONST_INT_P (y_hi))
|
||||
{
|
||||
/* Constant is viewed as unsigned when zero-extended. */
|
||||
y_hi = GEN_INT (UINTVAL (y_hi) & 0xffffffffULL);
|
||||
insn = emit_insn (gen_cmpsi3_imm_carryin_CC_Bout (scratch, x_hi,
|
||||
y_hi, cmp1));
|
||||
}
|
||||
else
|
||||
insn = emit_insn (gen_cmpsi3_carryin_CC_Bout (scratch, x_hi, y_hi,
|
||||
cmp1));
|
||||
return SET_DEST (single_set (insn));
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* We might have X as a constant, Y as a register because of the predicates
|
||||
used for cmpdi. If so, force X to a register here. */
|
||||
if (!REG_P (x))
|
||||
x = force_reg (DImode, x);
|
||||
|
||||
machine_mode mode = SELECT_CC_MODE (code, x, y);
|
||||
rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);
|
||||
mode = SELECT_CC_MODE (code, x, y);
|
||||
cc_reg = gen_rtx_REG (mode, CC_REGNUM);
|
||||
|
||||
if (mode != CC_CZmode)
|
||||
{
|
||||
@ -23803,6 +24001,22 @@ maybe_get_arm_condition_code (rtx comparison)
|
||||
default: return ARM_NV;
|
||||
}
|
||||
|
||||
case E_CC_NVmode:
|
||||
switch (comp_code)
|
||||
{
|
||||
case GE: return ARM_GE;
|
||||
case LT: return ARM_LT;
|
||||
default: return ARM_NV;
|
||||
}
|
||||
|
||||
case E_CC_Bmode:
|
||||
switch (comp_code)
|
||||
{
|
||||
case GEU: return ARM_CS;
|
||||
case LTU: return ARM_CC;
|
||||
default: return ARM_NV;
|
||||
}
|
||||
|
||||
case E_CC_Vmode:
|
||||
switch (comp_code)
|
||||
{
|
||||
|
@ -1009,6 +1009,51 @@
|
||||
(set_attr "type" "adc_reg,adc_imm,alu_shift_imm")]
|
||||
)
|
||||
|
||||
(define_insn "cmpsi3_carryin_<CC_EXTEND>out"
|
||||
[(set (reg:<CC_EXTEND> CC_REGNUM)
|
||||
(compare:<CC_EXTEND>
|
||||
(SE:DI (match_operand:SI 1 "s_register_operand" "0,r"))
|
||||
(plus:DI (match_operand:DI 3 "arm_borrow_operation" "")
|
||||
(SE:DI (match_operand:SI 2 "s_register_operand" "l,r")))))
|
||||
(clobber (match_scratch:SI 0 "=l,r"))]
|
||||
"TARGET_32BIT"
|
||||
"sbcs\\t%0, %1, %2"
|
||||
[(set_attr "conds" "set")
|
||||
(set_attr "arch" "t2,*")
|
||||
(set_attr "length" "2,4")
|
||||
(set_attr "type" "adc_reg")]
|
||||
)
|
||||
|
||||
;; Similar to the above, but handling a constant which has a different
|
||||
;; canonicalization.
|
||||
(define_insn "cmpsi3_imm_carryin_<CC_EXTEND>out"
|
||||
[(set (reg:<CC_EXTEND> CC_REGNUM)
|
||||
(compare:<CC_EXTEND>
|
||||
(SE:DI (match_operand:SI 1 "s_register_operand" "r,r"))
|
||||
(plus:DI (match_operand:DI 3 "arm_borrow_operation" "")
|
||||
(match_operand:DI 2 "arm_adcimm_operand" "I,K"))))
|
||||
(clobber (match_scratch:SI 0 "=l,r"))]
|
||||
"TARGET_32BIT"
|
||||
"@
|
||||
sbcs\\t%0, %1, %2
|
||||
adcs\\t%0, %1, #%B2"
|
||||
[(set_attr "conds" "set")
|
||||
(set_attr "type" "adc_imm")]
|
||||
)
|
||||
|
||||
;; Further canonicalization when the constant is zero.
|
||||
(define_insn "cmpsi3_0_carryin_<CC_EXTEND>out"
|
||||
[(set (reg:<CC_EXTEND> CC_REGNUM)
|
||||
(compare:<CC_EXTEND>
|
||||
(SE:DI (match_operand:SI 1 "s_register_operand" "r,r"))
|
||||
(match_operand:DI 2 "arm_borrow_operation" "")))
|
||||
(clobber (match_scratch:SI 0 "=l,r"))]
|
||||
"TARGET_32BIT"
|
||||
"sbcs\\t%0, %1, #0"
|
||||
[(set_attr "conds" "set")
|
||||
(set_attr "type" "adc_imm")]
|
||||
)
|
||||
|
||||
(define_insn "*subsi3_carryin_const"
|
||||
[(set (match_operand:SI 0 "s_register_operand" "=r")
|
||||
(minus:SI (plus:SI
|
||||
|
@ -792,6 +792,10 @@
|
||||
;; Code attributes
|
||||
;;----------------------------------------------------------------------------
|
||||
|
||||
;; Determine the mode of a 'wide compare', ie where the carry flag is
|
||||
;; propagated into the comparison.
|
||||
(define_code_attr CC_EXTEND [(sign_extend "CC_NV") (zero_extend "CC_B")])
|
||||
|
||||
;; Assembler mnemonics for vqh_ops and vqhs_ops iterators.
|
||||
(define_code_attr VQH_mnem [(plus "vadd") (smin "vmin") (smax "vmax")
|
||||
(umin "vmin") (umax "vmax")])
|
||||
|
@ -229,6 +229,12 @@
|
||||
(ior (match_operand 0 "arm_rhs_operand")
|
||||
(match_operand 0 "arm_not_immediate_operand")))
|
||||
|
||||
;; A constant that can be used with ADC(SBC) or SBC(ADC) when bit-wise
|
||||
;; inverted. Similar to arm_not_operand, but excludes registers.
|
||||
(define_predicate "arm_adcimm_operand"
|
||||
(ior (match_operand 0 "arm_immediate_operand")
|
||||
(match_operand 0 "arm_not_immediate_operand")))
|
||||
|
||||
(define_predicate "arm_di_operand"
|
||||
(ior (match_operand 0 "s_register_operand")
|
||||
(match_operand 0 "arm_immediate_di_operand")))
|
||||
|
Loading…
Reference in New Issue
Block a user