From af74bfeee1faccef25dc0086d4249eb0f127c820 Mon Sep 17 00:00:00 2001 From: Richard Earnshaw Date: Fri, 18 Oct 2019 19:03:50 +0000 Subject: [PATCH] [arm] Handle some constant comparisons using rsbs+rscs In a small number of cases it is preferable to handle comparisons with constants using the sequence RSBS tmp, Xlo, constlo RSCS tmp, Xhi, consthi which allows us to handle a small number of LE/GT/LEU/GEU cases when changing the code to use LT/GE/LTU/GEU would make the constant more expensive. Sadly, we cannot do this on Thumb, since we need RSC, so we now always use the incremented constant in that case since normally that still works out cheaper than forcing the entire constant into a register. Further investigation has also shown that the canonicalization of a reverse subtract and compare is valid for signed as well as unsigned value, so we relax the restriction on selecting CC_RSBmode to allow all types of compare. * config/arm/arm.c (arm_const_double_prefer_rsbs_rsc): New function. (arm_canonicalize_comparison): For GT/LE/GTU/GEU, use the constant unchanged only if that will be cheaper. (arm_select_cc_mode): Recognize a swapped comparison that will be regenerated using RSBS or RSCS. Relax restriction on selecting CC_RSBmode. (arm_gen_dicompare_reg): Handle LE/GT/LEU/GEU comparisons against a constant. (arm_gen_compare_reg): Handle compare (CONST, X) when the mode is CC_RSBmode. (maybe_get_arm_condition_code): CC_RSBmode now returns the same codes as CCmode. * config/arm/arm.md (rsb_imm_compare_scratch): New pattern. (rscsi3_out_scratch): New pattern. From-SVN: r277180 --- gcc/ChangeLog | 17 +++++ gcc/config/arm/arm.c | 153 +++++++++++++++++++++++++++++------------- gcc/config/arm/arm.md | 27 ++++++++ 3 files changed, 151 insertions(+), 46 deletions(-) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index aa489bd9c98..b4b18113fbd 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2019-10-18 Richard Earnshaw + + * config/arm/arm.c (arm_const_double_prefer_rsbs_rsc): New function. + (arm_canonicalize_comparison): For GT/LE/GTU/GEU, use the constant + unchanged only if that will be cheaper. + (arm_select_cc_mode): Recognize a swapped comparison that will + be regenerated using RSBS or RSCS. Relax restriction on selecting + CC_RSBmode. + (arm_gen_dicompare_reg): Handle LE/GT/LEU/GEU comparisons against + a constant. + (arm_gen_compare_reg): Handle compare (CONST, X) when the mode + is CC_RSBmode. + (maybe_get_arm_condition_code): CC_RSBmode now returns the same codes + as CCmode. + * config/arm/arm.md (rsb_imm_compare_scratch): New pattern. + (rscsi3_out_scratch): New pattern. + 2019-10-18 Richard Earnshaw * config/arm/arm-modes.def (CC_NV, CC_B): New CC modes. diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index 74c8eca7a64..55fd16df678 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -5355,6 +5355,21 @@ arm_gen_constant (enum rtx_code code, machine_mode mode, rtx cond, return insns; } +/* Return TRUE if op is a constant where both the low and top words are + suitable for RSB/RSC instructions. This is never true for Thumb, since + we do not have RSC in that case. */ +static bool +arm_const_double_prefer_rsbs_rsc (rtx op) +{ + /* Thumb lacks RSC, so we never prefer that sequence. */ + if (TARGET_THUMB || !CONST_INT_P (op)) + return false; + HOST_WIDE_INT hi, lo; + lo = UINTVAL (op) & 0xffffffffULL; + hi = UINTVAL (op) >> 32; + return const_ok_for_arm (lo) && const_ok_for_arm (hi); +} + /* Canonicalize a comparison so that we are more likely to recognize it. This can be done for a few constant compares, where we can make the immediate value easier to load. */ @@ -5380,8 +5395,7 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1, { if (*code == GT || *code == LE - || ((!TARGET_ARM || CONST_INT_P (*op1)) - && (*code == GTU || *code == LEU))) + || *code == GTU || *code == LEU) { /* Missing comparison. First try to use an available comparison. */ @@ -5392,10 +5406,13 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1, { case GT: case LE: - if (i != maxval - && (!arm_const_double_by_immediates (*op1) - || arm_const_double_by_immediates (GEN_INT (i + 1)))) + if (i != maxval) { + /* Try to convert to GE/LT, unless that would be more + expensive. */ + if (!arm_const_double_by_immediates (GEN_INT (i + 1)) + && arm_const_double_prefer_rsbs_rsc (*op1)) + return; *op1 = GEN_INT (i + 1); *code = *code == GT ? GE : LT; return; @@ -5404,10 +5421,13 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1, case GTU: case LEU: - if (i != ~((unsigned HOST_WIDE_INT) 0) - && (!arm_const_double_by_immediates (*op1) - || arm_const_double_by_immediates (GEN_INT (i + 1)))) + if (i != ~((unsigned HOST_WIDE_INT) 0)) { + /* Try to convert to GEU/LTU, unless that would + be more expensive. */ + if (!arm_const_double_by_immediates (GEN_INT (i + 1)) + && arm_const_double_prefer_rsbs_rsc (*op1)) + return; *op1 = GEN_INT (i + 1); *code = *code == GTU ? GEU : LTU; return; @@ -5419,7 +5439,6 @@ arm_canonicalize_comparison (int *code, rtx *op0, rtx *op1, } } - /* If that did not work, reverse the condition. */ if (!op0_preserve_value) { std::swap (*op0, *op1); @@ -15251,6 +15270,28 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y) || GET_CODE (x) == ROTATERT)) return CC_SWPmode; + /* A widened compare of the sum of a value plus a carry against a + constant. This is a representation of RSC. We want to swap the + result of the comparison at output. Not valid if the Z bit is + needed. */ + if (GET_MODE (x) == DImode + && GET_CODE (x) == PLUS + && arm_borrow_operation (XEXP (x, 1), DImode) + && CONST_INT_P (y) + && ((GET_CODE (XEXP (x, 0)) == SIGN_EXTEND + && (op == LE || op == GT)) + || (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND + && (op == LEU || op == GTU)))) + return CC_SWPmode; + + /* If X is a constant we want to use CC_RSBmode. This is + non-canonical, but arm_gen_compare_reg uses this to generate the + correct canonical form. */ + if (GET_MODE (y) == SImode + && (REG_P (y) || GET_CODE (y) == SUBREG) + && CONST_INT_P (x)) + return CC_RSBmode; + /* This operation is performed swapped, but since we only rely on the Z flag we don't need an additional mode. */ if (GET_MODE (y) == SImode @@ -15329,15 +15370,13 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y) || (TARGET_32BIT && GET_CODE (x) == ZERO_EXTRACT))) return CC_NOOVmode; - /* An unsigned comparison of ~reg with a const is really a special + /* A comparison of ~reg with a const is really a special canoncialization of compare (~const, reg), which is a reverse subtract operation. We may not get here if CONST is 0, but that doesn't matter because ~0 isn't a valid immediate for RSB. */ if (GET_MODE (x) == SImode && GET_CODE (x) == NOT - && CONST_INT_P (y) - && (op == EQ || op == NE - || op == LTU || op == LEU || op == GEU || op == GTU)) + && CONST_INT_P (y)) return CC_RSBmode; if (GET_MODE (x) == QImode && (op == EQ || op == NE)) @@ -15431,6 +15470,7 @@ 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. */ gcc_assert (TARGET_32BIT); + gcc_assert (!CONST_INT_P (x)); rtx x_lo = simplify_gen_subreg (SImode, x, DImode, subreg_lowpart_offset (SImode, DImode)); @@ -15445,9 +15485,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) 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) @@ -15525,10 +15562,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) 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), @@ -15536,13 +15569,10 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) 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, @@ -15556,6 +15586,27 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) return SET_DEST (single_set (insn)); } + case LE: + case GT: + { + /* During expansion, we only expect to get here if y is a + constant that we want to handle, otherwise we should have + swapped the operands already. */ + gcc_assert (arm_const_double_prefer_rsbs_rsc (y)); + + if (!const_ok_for_arm (INTVAL (y_lo))) + y_lo = force_reg (SImode, y_lo); + + /* Perform a reverse subtract and compare. */ + rtx cmp1 + = gen_rtx_LTU (DImode, + arm_gen_compare_reg (LTU, y_lo, x_lo, scratch), + const0_rtx); + rtx_insn *insn = emit_insn (gen_rscsi3_CC_NVout_scratch (scratch, y_hi, + x_hi, cmp1)); + return SET_DEST (single_set (insn)); + } + case LTU: case GEU: { @@ -15572,10 +15623,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) 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), @@ -15586,10 +15633,6 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) 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, @@ -15607,6 +15650,28 @@ arm_gen_dicompare_reg (rtx_code code, rtx x, rtx y, rtx scratch) return SET_DEST (single_set (insn)); } + case LEU: + case GTU: + { + /* During expansion, we only expect to get here if y is a + constant that we want to handle, otherwise we should have + swapped the operands already. */ + gcc_assert (arm_const_double_prefer_rsbs_rsc (y)); + + if (!const_ok_for_arm (INTVAL (y_lo))) + y_lo = force_reg (SImode, y_lo); + + /* Perform a reverse subtract and compare. */ + rtx cmp1 + = gen_rtx_LTU (DImode, + arm_gen_compare_reg (LTU, y_lo, x_lo, scratch), + const0_rtx); + y_hi = GEN_INT (0xffffffff & UINTVAL (y_hi)); + rtx_insn *insn = emit_insn (gen_rscsi3_CC_Bout_scratch (scratch, y_hi, + x_hi, cmp1)); + return SET_DEST (single_set (insn)); + } + default: break; } @@ -15695,8 +15760,15 @@ arm_gen_compare_reg (rtx_code code, rtx x, rtx y, rtx scratch) machine_mode mode = SELECT_CC_MODE (code, x, y); rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM); - - emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y)); + if (mode == CC_RSBmode) + { + if (!scratch) + scratch = gen_rtx_SCRATCH (SImode); + emit_insn (gen_rsb_imm_compare_scratch (scratch, + GEN_INT (~UINTVAL (x)), y)); + } + else + emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y)); return cc_reg; } @@ -24025,19 +24097,8 @@ maybe_get_arm_condition_code (rtx comparison) default: return ARM_NV; } - case E_CC_RSBmode: - switch (comp_code) - { - case NE: return ARM_NE; - case EQ: return ARM_EQ; - case GEU: return ARM_CS; - case GTU: return ARM_HI; - case LEU: return ARM_LS; - case LTU: return ARM_CC; - default: return ARM_NV; - } - case E_CCmode: + case E_CC_RSBmode: switch (comp_code) { case NE: return ARM_NE; diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index f0ff4dda396..8607c6f95da 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -1363,6 +1363,33 @@ (set_attr "type" "alus_imm")] ) +;; Similarly, but the result is unused. +(define_insn "rsb_imm_compare_scratch" + [(set (reg:CC_RSB CC_REGNUM) + (compare:CC_RSB (not:SI (match_operand:SI 2 "s_register_operand" "r")) + (match_operand 1 "arm_not_immediate_operand" "K"))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_32BIT" + "rsbs\\t%0, %2, #%B1" + [(set_attr "conds" "set") + (set_attr "type" "alus_imm")] +) + +;; Compare the sum of a value plus a carry against a constant. Uses +;; RSC, so the result is swapped. Only available on Arm +(define_insn "rscsi3_out_scratch" + [(set (reg:CC_SWP CC_REGNUM) + (compare:CC_SWP + (plus:DI (SE:DI (match_operand:SI 2 "s_register_operand" "r")) + (match_operand:DI 3 "arm_borrow_operation" "")) + (match_operand 1 "arm_immediate_operand" "I"))) + (clobber (match_scratch:SI 0 "=r"))] + "TARGET_ARM" + "rscs\\t%0, %2, %1" + [(set_attr "conds" "set") + (set_attr "type" "alus_imm")] +) + (define_expand "subsf3" [(set (match_operand:SF 0 "s_register_operand") (minus:SF (match_operand:SF 1 "s_register_operand") -- 2.30.2