@@ -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);
@@ -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)
{
@@ -989,7 +989,7 @@ (define_insn "subsi3_compare1"
(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 "adddf3"
(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 @@ (define_insn "subsi3_compare"
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 @@ (define_insn "negdi2_compare"
(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 @@ (define_insn "*arm_negsi2"
(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"
@@ -371,7 +371,7 @@ (define_special_predicate "arm_borrow_operation"
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;
}