From 8b8ab8f473b42933b9c1e292c4b1ab02adf1863a Mon Sep 17 00:00:00 2001 From: Richard Earnshaw Date: Fri, 18 Oct 2019 19:03:43 +0000 Subject: [PATCH] [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_out): New pattern. (cmpsi3_imm_carryin_out): Likewise. (cmpsi3_0_carryin_out): Likewise. From-SVN: r277179 --- gcc/ChangeLog | 13 +++ gcc/config/arm/arm-modes.def | 6 + gcc/config/arm/arm.c | 220 ++++++++++++++++++++++++++++++++++- gcc/config/arm/arm.md | 45 +++++++ gcc/config/arm/iterators.md | 4 + gcc/config/arm/predicates.md | 6 + 6 files changed, 291 insertions(+), 3 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 52d14e92e64..aa489bd9c98 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,16 @@ +2019-10-18 Richard Earnshaw + + * 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_out): New pattern. + (cmpsi3_imm_carryin_out): Likewise. + (cmpsi3_0_carryin_out): Likewise. + 2019-10-18 Richard Earnshaw * config/arm/arm.md (cbranchdi4): Accept reg_or_int_operand for diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def index 4fa7f1b43e5..65cddf68cdb 100644 --- a/gcc/config/arm/arm-modes.def +++ b/gcc/config/arm/arm-modes.def @@ -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); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index e7924df9b80..74c8eca7a64 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -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) { diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index 9d8b137651f..f0ff4dda396 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -1009,6 +1009,51 @@ (set_attr "type" "adc_reg,adc_imm,alu_shift_imm")] ) +(define_insn "cmpsi3_carryin_out" + [(set (reg: CC_REGNUM) + (compare: + (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_out" + [(set (reg: CC_REGNUM) + (compare: + (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_out" + [(set (reg: CC_REGNUM) + (compare: + (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 diff --git a/gcc/config/arm/iterators.md b/gcc/config/arm/iterators.md index 77e1645083f..5f1c833ad80 100644 --- a/gcc/config/arm/iterators.md +++ b/gcc/config/arm/iterators.md @@ -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")]) diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md index ed7495b69fc..d9470df8093 100644 --- a/gcc/config/arm/predicates.md +++ b/gcc/config/arm/predicates.md @@ -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"))) -- 2.30.2