@@ -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 @@ (define_insn "subsi3_carryin"
(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 @@ (define_mode_attr vsi2qi [(V2SI "v8qi") (V4SI "v16qi")])
;; 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 @@ (define_predicate "arm_not_operand"
(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")))