aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorDaniel Jacobowitz <dan@codesourcery.com>2010-07-03 01:00:37 +0000
committerSandra Loosemore <sandra@gcc.gnu.org>2010-07-02 21:00:37 -0400
commit73160ba9dc7be367d91f286eb5b07cb8af4a79bc (patch)
treea0af68d8ac04351704260ecd0693e4526a5d9616 /gcc
parent400cfcf50e5aeae792e8ef39a743b1cb6fafedd5 (diff)
downloadgcc-73160ba9dc7be367d91f286eb5b07cb8af4a79bc.zip
gcc-73160ba9dc7be367d91f286eb5b07cb8af4a79bc.tar.gz
gcc-73160ba9dc7be367d91f286eb5b07cb8af4a79bc.tar.bz2
arm.c (arm_canonicalize_comparison): Canonicalize DImode comparisons.
2010-07-02 Daniel Jacobowitz <dan@codesourcery.com> Julian Brown <julian@codesourcery.com> Sandra Loosemore <sandra@codesourcery.com> gcc/ * config/arm/arm.c (arm_canonicalize_comparison): Canonicalize DImode comparisons. Adjust to take both operands. (arm_select_cc_mode): Handle DImode comparisons. (arm_gen_compare_reg): Generate a scratch register for DImode comparisons which require one. Use xor for Thumb equality checks. (arm_const_double_by_immediates): New. (arm_print_operand): Allow 'Q' and 'R' for constants. (get_arm_condition_code): Handle new CC_CZmode and CC_NCVmode. * config/arm/arm.h (CANONICALIZE_COMPARISON): Always use arm_canonicalize_comparison. * config/arm/arm-modes.def: Add CC_CZmode and CC_NCVmode. * config/arm/arm-protos.h (arm_canonicalize_comparison): Update prototype. (arm_const_double_by_immediates): Declare. * config/arm/constraints.md (Di): New constraint. * config/arm/predicates.md (arm_immediate_di_operand) (arm_di_operand, cmpdi_operand): New. * config/arm/arm.md (cbranchdi4): Handle non-Cirrus also. (*arm_cmpdi_insn, *arm_cmpdi_unsigned) (*arm_cmpdi_zero, *thumb_cmpdi_zero): New insns. (cstoredi4): Handle non-Cirrus also. gcc/testsuite/ * gcc.c-torture/execute/20100416-1.c: New test case. Co-Authored-By: Julian Brown <julian@codesourcery.com> Co-Authored-By: Sandra Loosemore <sandra@codesourcery.com> From-SVN: r161764
Diffstat (limited to 'gcc')
-rw-r--r--gcc/ChangeLog26
-rw-r--r--gcc/config/arm/arm-modes.def6
-rw-r--r--gcc/config/arm/arm-protos.h4
-rw-r--r--gcc/config/arm/arm.c235
-rw-r--r--gcc/config/arm/arm.h14
-rw-r--r--gcc/config/arm/arm.md127
-rw-r--r--gcc/config/arm/constraints.md9
-rw-r--r--gcc/config/arm/predicates.md18
-rw-r--r--gcc/testsuite/ChangeLog6
-rw-r--r--gcc/testsuite/gcc.c-torture/execute/20100416-1.c40
10 files changed, 448 insertions, 37 deletions
diff --git a/gcc/ChangeLog b/gcc/ChangeLog
index 075139a..7aa6618 100644
--- a/gcc/ChangeLog
+++ b/gcc/ChangeLog
@@ -1,3 +1,29 @@
+2010-07-02 Daniel Jacobowitz <dan@codesourcery.com>
+ Julian Brown <julian@codesourcery.com>
+ Sandra Loosemore <sandra@codesourcery.com>
+
+ * config/arm/arm.c (arm_canonicalize_comparison): Canonicalize DImode
+ comparisons. Adjust to take both operands.
+ (arm_select_cc_mode): Handle DImode comparisons.
+ (arm_gen_compare_reg): Generate a scratch register for DImode
+ comparisons which require one. Use xor for Thumb equality checks.
+ (arm_const_double_by_immediates): New.
+ (arm_print_operand): Allow 'Q' and 'R' for constants.
+ (get_arm_condition_code): Handle new CC_CZmode and CC_NCVmode.
+ * config/arm/arm.h (CANONICALIZE_COMPARISON): Always use
+ arm_canonicalize_comparison.
+ * config/arm/arm-modes.def: Add CC_CZmode and CC_NCVmode.
+ * config/arm/arm-protos.h (arm_canonicalize_comparison): Update
+ prototype.
+ (arm_const_double_by_immediates): Declare.
+ * config/arm/constraints.md (Di): New constraint.
+ * config/arm/predicates.md (arm_immediate_di_operand)
+ (arm_di_operand, cmpdi_operand): New.
+ * config/arm/arm.md (cbranchdi4): Handle non-Cirrus also.
+ (*arm_cmpdi_insn, *arm_cmpdi_unsigned)
+ (*arm_cmpdi_zero, *thumb_cmpdi_zero): New insns.
+ (cstoredi4): Handle non-Cirrus also.
+
2010-07-02 Julian Brown <julian@codesourcery.com>
Sandra Loosemore <sandra@codesourcery.com>
diff --git a/gcc/config/arm/arm-modes.def b/gcc/config/arm/arm-modes.def
index 55b98bc..e9825ed 100644
--- a/gcc/config/arm/arm-modes.def
+++ b/gcc/config/arm/arm-modes.def
@@ -39,10 +39,16 @@ ADJUST_FLOAT_FORMAT (HF, ((arm_fp16_format == ARM_FP16_FORMAT_ALTERNATIVE)
CC_NOTBmode should be used if only the C flag is set as a not-borrow
flag, after a subtraction.
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_NCVmode should be used if only the N, C, and V flags are correct
+ (used for DImode signed comparisons).
CCmode should be used otherwise. */
CC_MODE (CC_NOOV);
CC_MODE (CC_Z);
+CC_MODE (CC_CZ);
+CC_MODE (CC_NCV);
CC_MODE (CC_SWP);
CC_MODE (CCFP);
CC_MODE (CCFPE);
diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h
index 61dcf07..40d0f93 100644
--- a/gcc/config/arm/arm-protos.h
+++ b/gcc/config/arm/arm-protos.h
@@ -50,8 +50,7 @@ extern int arm_hard_regno_mode_ok (unsigned int, enum machine_mode);
extern int const_ok_for_arm (HOST_WIDE_INT);
extern int arm_split_constant (RTX_CODE, enum machine_mode, rtx,
HOST_WIDE_INT, rtx, rtx, int);
-extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, enum machine_mode,
- rtx *);
+extern RTX_CODE arm_canonicalize_comparison (RTX_CODE, rtx *, rtx *);
extern int legitimate_pic_operand_p (rtx);
extern rtx legitimize_pic_address (rtx, enum machine_mode, rtx);
extern rtx legitimize_tls_address (rtx, rtx);
@@ -117,6 +116,7 @@ extern void arm_reload_in_hi (rtx *);
extern void arm_reload_out_hi (rtx *);
extern int arm_const_double_inline_cost (rtx);
extern bool arm_const_double_by_parts (rtx);
+extern bool arm_const_double_by_immediates (rtx);
extern const char *fp_immediate_constant (rtx);
extern void arm_emit_call_insn (rtx, rtx);
extern const char *output_call (rtx *);
diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c
index 2cc8d69..f59c1fc 100644
--- a/gcc/config/arm/arm.c
+++ b/gcc/config/arm/arm.c
@@ -3183,13 +3183,82 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
immediate value easier to load. */
enum rtx_code
-arm_canonicalize_comparison (enum rtx_code code, enum machine_mode mode,
- rtx * op1)
+arm_canonicalize_comparison (enum rtx_code code, rtx *op0, rtx *op1)
{
- unsigned HOST_WIDE_INT i = INTVAL (*op1);
- unsigned HOST_WIDE_INT maxval;
+ enum machine_mode mode;
+ unsigned HOST_WIDE_INT i, maxval;
+
+ mode = GET_MODE (*op0);
+ if (mode == VOIDmode)
+ mode = GET_MODE (*op1);
+
maxval = (((unsigned HOST_WIDE_INT) 1) << (GET_MODE_BITSIZE(mode) - 1)) - 1;
+ /* For DImode, we have GE/LT/GEU/LTU comparisons. In ARM mode
+ we can also use cmp/cmpeq for GTU/LEU. GT/LE must be either
+ reversed or (for constant OP1) adjusted to GE/LT. Similarly
+ for GTU/LEU in Thumb mode. */
+ if (mode == DImode)
+ {
+ rtx tem;
+
+ /* To keep things simple, always use the Cirrus cfcmp64 if it is
+ available. */
+ if (TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK)
+ return code;
+
+ if (code == GT || code == LE
+ || (!TARGET_ARM && (code == GTU || code == LEU)))
+ {
+ /* Missing comparison. First try to use an available
+ comparison. */
+ if (GET_CODE (*op1) == CONST_INT)
+ {
+ i = INTVAL (*op1);
+ switch (code)
+ {
+ case GT:
+ case LE:
+ if (i != maxval
+ && arm_const_double_by_immediates (GEN_INT (i + 1)))
+ {
+ *op1 = GEN_INT (i + 1);
+ return code == GT ? GE : LT;
+ }
+ break;
+ case GTU:
+ case LEU:
+ if (i != ~((unsigned HOST_WIDE_INT) 0)
+ && arm_const_double_by_immediates (GEN_INT (i + 1)))
+ {
+ *op1 = GEN_INT (i + 1);
+ return code == GTU ? GEU : LTU;
+ }
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+
+ /* If that did not work, reverse the condition. */
+ tem = *op0;
+ *op0 = *op1;
+ *op1 = tem;
+ return swap_condition (code);
+ }
+
+ return code;
+ }
+
+ /* Comparisons smaller than DImode. Only adjust comparisons against
+ an out-of-range constant. */
+ if (GET_CODE (*op1) != CONST_INT
+ || const_ok_for_arm (INTVAL (*op1))
+ || const_ok_for_arm (- INTVAL (*op1)))
+ return code;
+
+ i = INTVAL (*op1);
+
switch (code)
{
case EQ:
@@ -10014,6 +10083,55 @@ 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 || GET_MODE (y) == DImode)
+ {
+ /* To keep things simple, always use the Cirrus cfcmp64 if it is
+ available. */
+ if (TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK)
+ return CCmode;
+
+ switch (op)
+ {
+ case EQ:
+ case NE:
+ /* A DImode comparison against zero can be implemented by
+ or'ing the two halves together. */
+ if (y == const0_rtx)
+ return CC_Zmode;
+
+ /* We can do an equality test in three Thumb instructions. */
+ if (!TARGET_ARM)
+ return CC_Zmode;
+
+ /* FALLTHROUGH */
+
+ case LTU:
+ case LEU:
+ case GTU:
+ case GEU:
+ /* DImode unsigned comparisons can be implemented by cmp +
+ cmpeq without a scratch register. Not worth doing in
+ Thumb-2. */
+ if (TARGET_ARM)
+ return CC_CZmode;
+
+ /* FALLTHROUGH */
+
+ case LT:
+ case LE:
+ case GT:
+ case GE:
+ /* DImode signed and unsigned comparisons can be implemented
+ by cmp + sbcs with a scratch register, but that does not
+ set the Z flag - we must reverse GT/LE/GTU/LEU. */
+ gcc_assert (op != EQ && op != NE);
+ return CC_NCVmode;
+
+ default:
+ gcc_unreachable ();
+ }
+ }
+
return CCmode;
}
@@ -10023,10 +10141,39 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
rtx
arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y)
{
- enum machine_mode mode = SELECT_CC_MODE (code, x, y);
- rtx cc_reg = gen_rtx_REG (mode, CC_REGNUM);
+ enum machine_mode mode;
+ rtx cc_reg;
+ int dimode_comparison = GET_MODE (x) == DImode || GET_MODE (y) == DImode;
+
+ /* 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 (dimode_comparison && !REG_P (x))
+ x = force_reg (DImode, x);
+
+ mode = SELECT_CC_MODE (code, x, y);
+ cc_reg = gen_rtx_REG (mode, CC_REGNUM);
+
+ if (dimode_comparison
+ && !(TARGET_HARD_FLOAT && TARGET_MAVERICK)
+ && mode != CC_CZmode)
+ {
+ rtx clobber, set;
- emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));
+ /* To compare two non-zero values for equality, XOR them and
+ then compare against zero. Not used for ARM mode; there
+ CC_CZmode is cheaper. */
+ if (mode == CC_Zmode && y != const0_rtx)
+ {
+ x = expand_binop (DImode, xor_optab, x, y, NULL_RTX, 0, OPTAB_WIDEN);
+ y = const0_rtx;
+ }
+ /* A scratch register is required. */
+ clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode));
+ set = gen_rtx_SET (VOIDmode, cc_reg, gen_rtx_COMPARE (mode, x, y));
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
+ }
+ else
+ emit_set_insn (cc_reg, gen_rtx_COMPARE (mode, x, y));
return cc_reg;
}
@@ -11355,6 +11502,34 @@ arm_const_double_by_parts (rtx val)
return false;
}
+/* Return true if it is possible to inline both the high and low parts
+ of a 64-bit constant into 32-bit data processing instructions. */
+bool
+arm_const_double_by_immediates (rtx val)
+{
+ enum machine_mode mode = GET_MODE (val);
+ rtx part;
+
+ if (mode == VOIDmode)
+ mode = DImode;
+
+ part = gen_highpart_mode (SImode, mode, val);
+
+ gcc_assert (GET_CODE (part) == CONST_INT);
+
+ if (!const_ok_for_arm (INTVAL (part)))
+ return false;
+
+ part = gen_lowpart (SImode, val);
+
+ gcc_assert (GET_CODE (part) == CONST_INT);
+
+ if (!const_ok_for_arm (INTVAL (part)))
+ return false;
+
+ return true;
+}
+
/* Scan INSN and note any of its operands that need fixing.
If DO_PUSHES is false we do not actually push any of the fixups
needed. The function returns TRUE if any fixups were needed/pushed.
@@ -15256,8 +15431,18 @@ arm_print_operand (FILE *stream, rtx x, int code)
the value being loaded is big-wordian or little-wordian. The
order of the two register loads can matter however, if the address
of the memory location is actually held in one of the registers
- being overwritten by the load. */
+ being overwritten by the load.
+
+ The 'Q' and 'R' constraints are also available for 64-bit
+ constants. */
case 'Q':
+ if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+ {
+ rtx part = gen_lowpart (SImode, x);
+ fprintf (stream, "#" HOST_WIDE_INT_PRINT_DEC, INTVAL (part));
+ return;
+ }
+
if (GET_CODE (x) != REG || REGNO (x) > LAST_ARM_REGNUM)
{
output_operand_lossage ("invalid operand for code '%c'", code);
@@ -15268,6 +15453,18 @@ arm_print_operand (FILE *stream, rtx x, int code)
return;
case 'R':
+ if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
+ {
+ enum machine_mode mode = GET_MODE (x);
+ rtx part;
+
+ if (mode == VOIDmode)
+ mode = DImode;
+ part = gen_highpart_mode (SImode, mode, x);
+ fprintf (stream, "#" HOST_WIDE_INT_PRINT_DEC, INTVAL (part));
+ return;
+ }
+
if (GET_CODE (x) != REG || REGNO (x) > LAST_ARM_REGNUM)
{
output_operand_lossage ("invalid operand for code '%c'", code);
@@ -16102,6 +16299,28 @@ get_arm_condition_code (rtx comparison)
default: gcc_unreachable ();
}
+ case CC_CZmode:
+ 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: gcc_unreachable ();
+ }
+
+ case CC_NCVmode:
+ switch (comp_code)
+ {
+ case GE: return ARM_GE;
+ case LT: return ARM_LT;
+ case GEU: return ARM_CS;
+ case LTU: return ARM_CC;
+ default: gcc_unreachable ();
+ }
+
case CCmode:
switch (comp_code)
{
diff --git a/gcc/config/arm/arm.h b/gcc/config/arm/arm.h
index fcd1ef3..cad50a8 100644
--- a/gcc/config/arm/arm.h
+++ b/gcc/config/arm/arm.h
@@ -2244,19 +2244,7 @@ extern int making_const_table;
: reverse_condition (code))
#define CANONICALIZE_COMPARISON(CODE, OP0, OP1) \
- do \
- { \
- if (GET_CODE (OP1) == CONST_INT \
- && ! (const_ok_for_arm (INTVAL (OP1)) \
- || (const_ok_for_arm (- INTVAL (OP1))))) \
- { \
- rtx const_op = OP1; \
- CODE = arm_canonicalize_comparison ((CODE), GET_MODE (OP0), \
- &const_op); \
- OP1 = const_op; \
- } \
- } \
- while (0)
+ (CODE) = arm_canonicalize_comparison (CODE, &(OP0), &(OP1))
/* The arm5 clz instruction returns 32. */
#define CLZ_DEFINED_VALUE_AT_ZERO(MODE, VALUE) ((VALUE) = 32, 1)
diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md
index fbbe7f4..bf91918 100644
--- a/gcc/config/arm/arm.md
+++ b/gcc/config/arm/arm.md
@@ -6593,17 +6593,45 @@
operands[3])); DONE;"
)
-;; this uses the Cirrus DI compare instruction
(define_expand "cbranchdi4"
[(set (pc) (if_then_else
(match_operator 0 "arm_comparison_operator"
- [(match_operand:DI 1 "cirrus_fp_register" "")
- (match_operand:DI 2 "cirrus_fp_register" "")])
+ [(match_operand:DI 1 "cmpdi_operand" "")
+ (match_operand:DI 2 "cmpdi_operand" "")])
(label_ref (match_operand 3 "" ""))
(pc)))]
- "TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK"
- "emit_jump_insn (gen_cbranch_cc (operands[0], operands[1], operands[2],
- operands[3])); DONE;"
+ "TARGET_32BIT"
+ "{
+ rtx swap = NULL_RTX;
+ enum rtx_code code = GET_CODE (operands[0]);
+
+ /* We should not have two constants. */
+ gcc_assert (GET_MODE (operands[1]) == DImode
+ || GET_MODE (operands[2]) == DImode);
+
+ /* Flip unimplemented DImode comparisons to a form that
+ arm_gen_compare_reg can handle. */
+ switch (code)
+ {
+ case GT:
+ swap = gen_rtx_LT (VOIDmode, operands[2], operands[1]); break;
+ case LE:
+ swap = gen_rtx_GE (VOIDmode, operands[2], operands[1]); break;
+ case GTU:
+ swap = gen_rtx_LTU (VOIDmode, operands[2], operands[1]); break;
+ case LEU:
+ swap = gen_rtx_GEU (VOIDmode, operands[2], operands[1]); break;
+ default:
+ break;
+ }
+ if (swap)
+ emit_jump_insn (gen_cbranch_cc (swap, operands[2], operands[1],
+ operands[3]));
+ else
+ emit_jump_insn (gen_cbranch_cc (operands[0], operands[1], operands[2],
+ operands[3]));
+ DONE;
+ }"
)
(define_insn "cbranchsi4_insn"
@@ -7767,6 +7795,52 @@
(const_string "alu_shift_reg")))]
)
+;; DImode comparisons. The generic code generates branches that
+;; if-conversion can not reduce to a conditional compare, so we do
+;; that directly.
+
+(define_insn "*arm_cmpdi_insn"
+ [(set (reg:CC_NCV CC_REGNUM)
+ (compare:CC_NCV (match_operand:DI 0 "s_register_operand" "r")
+ (match_operand:DI 1 "arm_di_operand" "rDi")))
+ (clobber (match_scratch:SI 2 "=r"))]
+ "TARGET_32BIT && !(TARGET_HARD_FLOAT && TARGET_MAVERICK)"
+ "cmp\\t%Q0, %Q1\;sbcs\\t%2, %R0, %R1"
+ [(set_attr "conds" "set")
+ (set_attr "length" "8")]
+)
+
+(define_insn "*arm_cmpdi_unsigned"
+ [(set (reg:CC_CZ CC_REGNUM)
+ (compare:CC_CZ (match_operand:DI 0 "s_register_operand" "r")
+ (match_operand:DI 1 "arm_di_operand" "rDi")))]
+ "TARGET_ARM"
+ "cmp%?\\t%R0, %R1\;cmpeq\\t%Q0, %Q1"
+ [(set_attr "conds" "set")
+ (set_attr "length" "8")]
+)
+
+(define_insn "*arm_cmpdi_zero"
+ [(set (reg:CC_Z CC_REGNUM)
+ (compare:CC_Z (match_operand:DI 0 "s_register_operand" "r")
+ (const_int 0)))
+ (clobber (match_scratch:SI 1 "=r"))]
+ "TARGET_32BIT"
+ "orr%.\\t%1, %Q0, %R0"
+ [(set_attr "conds" "set")]
+)
+
+(define_insn "*thumb_cmpdi_zero"
+ [(set (reg:CC_Z CC_REGNUM)
+ (compare:CC_Z (match_operand:DI 0 "s_register_operand" "l")
+ (const_int 0)))
+ (clobber (match_scratch:SI 1 "=l"))]
+ "TARGET_THUMB1"
+ "orr\\t%1, %Q0, %R0"
+ [(set_attr "conds" "set")
+ (set_attr "length" "2")]
+)
+
;; Cirrus SF compare instruction
(define_insn "*cirrus_cmpsf"
[(set (reg:CCFP CC_REGNUM)
@@ -8070,18 +8144,45 @@
operands[2], operands[3])); DONE;"
)
-;; this uses the Cirrus DI compare instruction
(define_expand "cstoredi4"
[(set (match_operand:SI 0 "s_register_operand" "")
(match_operator:SI 1 "arm_comparison_operator"
- [(match_operand:DI 2 "cirrus_fp_register" "")
- (match_operand:DI 3 "cirrus_fp_register" "")]))]
- "TARGET_ARM && TARGET_HARD_FLOAT && TARGET_MAVERICK"
- "emit_insn (gen_cstore_cc (operands[0], operands[1],
- operands[2], operands[3])); DONE;"
+ [(match_operand:DI 2 "cmpdi_operand" "")
+ (match_operand:DI 3 "cmpdi_operand" "")]))]
+ "TARGET_32BIT"
+ "{
+ rtx swap = NULL_RTX;
+ enum rtx_code code = GET_CODE (operands[1]);
+
+ /* We should not have two constants. */
+ gcc_assert (GET_MODE (operands[2]) == DImode
+ || GET_MODE (operands[3]) == DImode);
+
+ /* Flip unimplemented DImode comparisons to a form that
+ arm_gen_compare_reg can handle. */
+ switch (code)
+ {
+ case GT:
+ swap = gen_rtx_LT (VOIDmode, operands[3], operands[2]); break;
+ case LE:
+ swap = gen_rtx_GE (VOIDmode, operands[3], operands[2]); break;
+ case GTU:
+ swap = gen_rtx_LTU (VOIDmode, operands[3], operands[2]); break;
+ case LEU:
+ swap = gen_rtx_GEU (VOIDmode, operands[3], operands[2]); break;
+ default:
+ break;
+ }
+ if (swap)
+ emit_insn (gen_cstore_cc (operands[0], swap, operands[3],
+ operands[2]));
+ else
+ emit_insn (gen_cstore_cc (operands[0], operands[1], operands[2],
+ operands[3]));
+ DONE;
+ }"
)
-
(define_expand "cstoresi_eq0_thumb1"
[(parallel
[(set (match_operand:SI 0 "s_register_operand" "")
diff --git a/gcc/config/arm/constraints.md b/gcc/config/arm/constraints.md
index 6d6c77d..8d0d184 100644
--- a/gcc/config/arm/constraints.md
+++ b/gcc/config/arm/constraints.md
@@ -29,7 +29,7 @@
;; in Thumb-1 state: I, J, K, L, M, N, O
;; The following multi-letter normal constraints have been used:
-;; in ARM/Thumb-2 state: Da, Db, Dc, Dn, Dl, DL, Dv, Dy
+;; in ARM/Thumb-2 state: Da, Db, Dc, Dn, Dl, DL, Dv, Dy, Di
;; in Thumb-1 state: Pa, Pb
;; in Thumb-2 state: Ps, Pt, Pu, Pv, Pw, Px
@@ -211,6 +211,13 @@
(match_test "TARGET_32BIT && arm_const_double_inline_cost (op) == 4
&& !(optimize_size || arm_ld_sched)")))
+(define_constraint "Di"
+ "@internal
+ In ARM/Thumb-2 state a const_int or const_double where both the high
+ and low SImode words can be generated as immediates in 32-bit instructions."
+ (and (match_code "const_double,const_int")
+ (match_test "TARGET_32BIT && arm_const_double_by_immediates (op)")))
+
(define_constraint "Dn"
"@internal
In ARM/Thumb-2 state a const_vector which can be loaded with a Neon vmov
diff --git a/gcc/config/arm/predicates.md b/gcc/config/arm/predicates.md
index 211ccd3..0ff7aef 100644
--- a/gcc/config/arm/predicates.md
+++ b/gcc/config/arm/predicates.md
@@ -86,6 +86,12 @@
(and (match_code "const_int")
(match_test "const_ok_for_arm (INTVAL (op))")))
+;; A constant value which fits into two instructions, each taking
+;; an arithmetic constant operand for one of the words.
+(define_predicate "arm_immediate_di_operand"
+ (and (match_code "const_int,const_double")
+ (match_test "arm_const_double_by_immediates (op)")))
+
(define_predicate "arm_neg_immediate_operand"
(and (match_code "const_int")
(match_test "const_ok_for_arm (-INTVAL (op))")))
@@ -119,6 +125,10 @@
(ior (match_operand 0 "arm_rhs_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")))
+
;; True if the operand is a memory reference which contains an
;; offsettable address.
(define_predicate "offsettable_memory_operand"
@@ -529,4 +539,12 @@
(define_predicate "neon_lane_number"
(and (match_code "const_int")
(match_test "INTVAL (op) >= 0 && INTVAL (op) <= 7")))
+;; Predicates for named expanders that overlap multiple ISAs.
+
+(define_predicate "cmpdi_operand"
+ (if_then_else (match_test "TARGET_HARD_FLOAT && TARGET_MAVERICK")
+ (and (match_test "TARGET_ARM")
+ (match_operand 0 "cirrus_fp_register"))
+ (and (match_test "TARGET_32BIT")
+ (match_operand 0 "arm_di_operand"))))
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 1ab9e0a..d4ee6d8 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,9 @@
+2010-07-02 Daniel Jacobowitz <dan@codesourcery.com>
+ Julian Brown <julian@codesourcery.com>
+ Sandra Loosemore <sandra@codesourcery.com>
+
+ * gcc.c-torture/execute/20100416-1.c: New test case.
+
2010-07-02 Julian Brown <julian@codesourcery.com>
Sandra Loosemore <sandra@codesourcery.com>
diff --git a/gcc/testsuite/gcc.c-torture/execute/20100416-1.c b/gcc/testsuite/gcc.c-torture/execute/20100416-1.c
new file mode 100644
index 0000000..8b5a6f4
--- /dev/null
+++ b/gcc/testsuite/gcc.c-torture/execute/20100416-1.c
@@ -0,0 +1,40 @@
+void abort(void);
+
+int
+movegt(int x, int y, long long a)
+{
+ int i;
+ int ret = 0;
+ for (i = 0; i < y; i++)
+ {
+ if (a >= (long long) 0xf000000000000000LL)
+ ret = x;
+ else
+ ret = y;
+ }
+ return ret;
+}
+
+struct test
+{
+ long long val;
+ int ret;
+} tests[] = {
+ { 0xf000000000000000LL, -1 },
+ { 0xefffffffffffffffLL, 1 },
+ { 0xf000000000000001LL, -1 },
+ { 0x0000000000000000LL, -1 },
+ { 0x8000000000000000LL, 1 },
+};
+
+int
+main()
+{
+ int i;
+ for (i = 0; i < sizeof (tests) / sizeof (tests[0]); i++)
+ {
+ if (movegt (-1, 1, tests[i].val) != tests[i].ret)
+ abort ();
+ }
+ return 0;
+}