diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 1c96b17631f..0f389a1f3f6 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,20 @@ +2019-10-18 Richard Earnshaw + + * config/arm/arm-modes.def (CC_RSB): New CC mode. + * config/arm/predicates.md (arm_borrow_operation): Handle CC_RSBmode. + * config/arm/arm.c (arm_select_cc_mode): Detect when we should + return CC_RSBmode. + (maybe_get_arm_condition_code): Handle CC_RSBmode. + * config/arm/arm.md (subsi3_carryin): Make this pattern available to + expand. + (subdi3): Rewrite to early-expand the sub-operations. + (rsb_im_compare): New pattern. + (negdi2): Delete. + (negdi2_insn): Delete. + (arm_negsi2): Correct type attribute to alu_imm. + (negsi2_0compare): New insn pattern. + (negsi2_carryin): New insn pattern. + 2019-10-18 Richard Earnshaw * config/arm/arm.md (addsi3_carryin_alt2): Use arm_not_operand for diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def index 8f131c369b5..4fa7f1b43e5 100644 --- a/gcc/config/arm/arm-modes.def +++ b/gcc/config/arm/arm-modes.def @@ -36,6 +36,9 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE) CC_Nmode should be used if only the N (sign) flag is set correctly 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 (used for DImode signed comparisons). CCmode should be used otherwise. */ @@ -45,6 +48,7 @@ CC_MODE (CC_Z); CC_MODE (CC_CZ); CC_MODE (CC_NCV); CC_MODE (CC_SWP); +CC_MODE (CC_RSB); CC_MODE (CCFP); CC_MODE (CCFPE); CC_MODE (CC_DNE); diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index f26945dbcf0..41e4832f2c9 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -15214,6 +15214,17 @@ 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 + 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)) + return CC_RSBmode; + if (GET_MODE (x) == QImode && (op == EQ || op == NE)) return CC_Zmode; @@ -23629,6 +23640,18 @@ 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: switch (comp_code) { diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index fbe154a9873..99d931525f8 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -989,7 +989,7 @@ (set_attr "type" "alus_sreg")] ) -(define_insn "*subsi3_carryin" +(define_insn "subsi3_carryin" [(set (match_operand:SI 0 "s_register_operand" "=r,r,r") (minus:SI (minus:SI (match_operand:SI 1 "reg_or_int_operand" "r,I,Pz") (match_operand:SI 2 "s_register_operand" "r,r,r")) @@ -1094,12 +1094,72 @@ (define_expand "subdi3" [(parallel [(set (match_operand:DI 0 "s_register_operand") - (minus:DI (match_operand:DI 1 "s_register_operand") + (minus:DI (match_operand:DI 1 "reg_or_int_operand") (match_operand:DI 2 "s_register_operand"))) (clobber (reg:CC CC_REGNUM))])] "TARGET_EITHER" " -") + if (TARGET_THUMB1) + { + if (!REG_P (operands[1])) + operands[1] = force_reg (DImode, operands[1]); + } + else + { + rtx lo_result, hi_result, lo_dest, hi_dest; + rtx lo_op1, hi_op1, lo_op2, hi_op2; + rtx condition; + + /* Since operands[1] may be an integer, pass it second, so that + any necessary simplifications will be done on the decomposed + constant. */ + arm_decompose_di_binop (operands[2], operands[1], &lo_op2, &hi_op2, + &lo_op1, &hi_op1); + lo_result = lo_dest = gen_lowpart (SImode, operands[0]); + hi_result = hi_dest = gen_highpart (SImode, operands[0]); + + if (!arm_rhs_operand (lo_op1, SImode)) + lo_op1 = force_reg (SImode, lo_op1); + + if ((TARGET_THUMB2 && ! s_register_operand (hi_op1, SImode)) + || !arm_rhs_operand (hi_op1, SImode)) + hi_op1 = force_reg (SImode, hi_op1); + + rtx cc_reg; + if (lo_op1 == const0_rtx) + { + cc_reg = gen_rtx_REG (CC_RSBmode, CC_REGNUM); + emit_insn (gen_negsi2_0compare (lo_dest, lo_op2)); + } + else if (CONST_INT_P (lo_op1)) + { + cc_reg = gen_rtx_REG (CC_RSBmode, CC_REGNUM); + emit_insn (gen_rsb_imm_compare (lo_dest, lo_op1, lo_op2, + GEN_INT (~UINTVAL (lo_op1)))); + } + else + { + cc_reg = gen_rtx_REG (CCmode, CC_REGNUM); + emit_insn (gen_subsi3_compare (lo_dest, lo_op1, lo_op2)); + } + + condition = gen_rtx_LTU (SImode, cc_reg, const0_rtx); + + if (hi_op1 == const0_rtx) + emit_insn (gen_negsi2_carryin (hi_dest, hi_op2, condition)); + else + emit_insn (gen_subsi3_carryin (hi_dest, hi_op1, hi_op2, condition)); + + if (lo_result != lo_dest) + emit_move_insn (lo_result, lo_dest); + + if (hi_result != hi_dest) + emit_move_insn (hi_result, hi_dest); + + DONE; + } + " +) (define_insn "*arm_subdi3" [(set (match_operand:DI 0 "arm_general_register_operand" "=&r,&r,&r") @@ -1213,7 +1273,23 @@ subs%?\\t%0, %1, %2 rsbs%?\\t%0, %2, %1" [(set_attr "conds" "set") - (set_attr "type" "alus_imm,alus_sreg,alus_sreg")] + (set_attr "type" "alus_imm,alus_sreg,alus_imm")] +) + +;; To keep the comparison in canonical form we express it as (~reg cmp ~0) +;; rather than (0 cmp reg). This gives the same results for unsigned +;; and equality compares which is what we mostly need here. +(define_insn "rsb_imm_compare" + [(set (reg:CC_RSB CC_REGNUM) + (compare:CC_RSB (not:SI (match_operand:SI 2 "s_register_operand" "r")) + (match_operand 3 "const_int_operand" ""))) + (set (match_operand:SI 0 "s_register_operand" "=r") + (minus:SI (match_operand 1 "arm_immediate_operand" "I") + (match_dup 2)))] + "TARGET_32BIT && ~UINTVAL (operands[1]) == UINTVAL (operands[3])" + "rsbs\\t%0, %2, %1" + [(set_attr "conds" "set") + (set_attr "type" "alus_imm")] ) (define_expand "subsf3" @@ -3726,29 +3802,6 @@ (set_attr "type" "multiple")] ) -(define_expand "negdi2" - [(parallel - [(set (match_operand:DI 0 "s_register_operand") - (neg:DI (match_operand:DI 1 "s_register_operand"))) - (clobber (reg:CC CC_REGNUM))])] - "TARGET_EITHER" -) - -;; The constraints here are to prevent a *partial* overlap (where %Q0 == %R1). -(define_insn "*negdi2_insn" - [(set (match_operand:DI 0 "s_register_operand" "=&r,&r") - (neg:DI (match_operand:DI 1 "s_register_operand" "r,r"))) - (clobber (reg:CC CC_REGNUM))] - "TARGET_32BIT" - "@ - rsbs\\t%Q0, %Q1, #0; rsc\\t%R0, %R1, #0 - negs\\t%Q0, %Q1; sbc\\t%R0, %R1, %R1, lsl #1" - [(set_attr "conds" "clob") - (set_attr "arch" "a,t2") - (set_attr "length" "8") - (set_attr "type" "multiple")] -) - (define_expand "negsi2" [(set (match_operand:SI 0 "s_register_operand") (neg:SI (match_operand:SI 1 "s_register_operand")))] @@ -3765,7 +3818,39 @@ (set_attr "predicable_short_it" "yes,no") (set_attr "arch" "t2,*") (set_attr "length" "4") - (set_attr "type" "alu_sreg")] + (set_attr "type" "alu_imm")] +) + +;; To keep the comparison in canonical form we express it as (~reg cmp ~0) +;; rather than (0 cmp reg). This gives the same results for unsigned +;; and equality compares which is what we mostly need here. +(define_insn "negsi2_0compare" + [(set (reg:CC_RSB CC_REGNUM) + (compare:CC_RSB (not:SI (match_operand:SI 1 "s_register_operand" "l,r")) + (const_int -1))) + (set (match_operand:SI 0 "s_register_operand" "=l,r") + (neg:SI (match_dup 1)))] + "TARGET_32BIT" + "@ + negs\\t%0, %1 + rsbs\\t%0, %1, #0" + [(set_attr "conds" "set") + (set_attr "arch" "t2,*") + (set_attr "length" "2,*") + (set_attr "type" "alus_imm")] +) + +(define_insn "negsi2_carryin" + [(set (match_operand:SI 0 "s_register_operand" "=r,r") + (minus:SI (neg:SI (match_operand:SI 1 "s_register_operand" "r,r")) + (match_operand:SI 2 "arm_borrow_operation" "")))] + "TARGET_32BIT" + "@ + rsc\\t%0, %1, #0 + sbc\\t%0, %1, %1, lsl #1" + [(set_attr "conds" "use") + (set_attr "arch" "a,t2") + (set_attr "type" "adc_imm,adc_reg")] ) (define_expand "negsf2" diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md index 8b36e7ee462..e6766a97fc4 100644 --- a/gcc/config/arm/predicates.md +++ b/gcc/config/arm/predicates.md @@ -371,7 +371,7 @@ machine_mode ccmode = GET_MODE (op0); if (ccmode == CC_Cmode) return GET_CODE (op) == GEU; - else if (ccmode == CCmode) + else if (ccmode == CCmode || ccmode == CC_RSBmode) return GET_CODE (op) == LTU; return false; }