summaryrefslogtreecommitdiff
path: root/gcc/config/rx/rx.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/rx/rx.c')
-rw-r--r--gcc/config/rx/rx.c561
1 files changed, 271 insertions, 290 deletions
diff --git a/gcc/config/rx/rx.c b/gcc/config/rx/rx.c
index 8f6f384c4c2..0179b1ff520 100644
--- a/gcc/config/rx/rx.c
+++ b/gcc/config/rx/rx.c
@@ -53,6 +53,15 @@
static void rx_print_operand (FILE *, rtx, int);
+#define CC_FLAG_S (1 << 0)
+#define CC_FLAG_Z (1 << 1)
+#define CC_FLAG_O (1 << 2)
+#define CC_FLAG_C (1 << 3)
+#define CC_FLAG_FP (1 << 4) /* fake, to differentiate CC_Fmode */
+
+static unsigned int flags_from_mode (enum machine_mode mode);
+static unsigned int flags_from_code (enum rtx_code code);
+
enum rx_cpu_types rx_cpu_type = RX600;
/* Return true if OP is a reference to an object in a small data area. */
@@ -354,8 +363,6 @@ rx_assemble_integer (rtx x, unsigned int size, int is_aligned)
}
-int rx_float_compare_mode;
-
/* Handles the insertion of a single operand into the assembler output.
The %<letter> directives supported are:
@@ -395,21 +402,82 @@ rx_print_operand (FILE * file, rtx op, int letter)
break;
case 'B':
- switch (GET_CODE (op))
- {
- case LT: fprintf (file, "lt"); break;
- case GE: fprintf (file, "ge"); break;
- case GT: fprintf (file, "gt"); break;
- case LE: fprintf (file, "le"); break;
- case GEU: fprintf (file, "geu"); break;
- case LTU: fprintf (file, "ltu"); break;
- case GTU: fprintf (file, "gtu"); break;
- case LEU: fprintf (file, "leu"); break;
- case EQ: fprintf (file, "eq"); break;
- case NE: fprintf (file, "ne"); break;
- default: debug_rtx (op); gcc_unreachable ();
- }
- break;
+ {
+ enum rtx_code code = GET_CODE (op);
+ enum machine_mode mode = GET_MODE (XEXP (op, 0));
+ const char *ret;
+
+ if (mode == CC_Fmode)
+ {
+ /* C flag is undefined, and O flag carries unordered. None of the
+ branch combinations that include O use it helpfully. */
+ switch (code)
+ {
+ case ORDERED:
+ ret = "no";
+ break;
+ case UNORDERED:
+ ret = "o";
+ break;
+ case LT:
+ ret = "n";
+ break;
+ case GE:
+ ret = "pz";
+ break;
+ case EQ:
+ ret = "eq";
+ break;
+ case NE:
+ ret = "ne";
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ }
+ else
+ {
+ switch (code)
+ {
+ case LT:
+ ret = "n";
+ break;
+ case GE:
+ ret = "pz";
+ break;
+ case GT:
+ ret = "gt";
+ break;
+ case LE:
+ ret = "le";
+ break;
+ case GEU:
+ ret = "geu";
+ break;
+ case LTU:
+ ret = "ltu";
+ break;
+ case GTU:
+ ret = "gtu";
+ break;
+ case LEU:
+ ret = "leu";
+ break;
+ case EQ:
+ ret = "eq";
+ break;
+ case NE:
+ ret = "ne";
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ gcc_checking_assert ((flags_from_code (code)
+ & ~flags_from_mode (mode)) == 0);
+ }
+ fputs (ret, file);
+ break;
+ }
case 'C':
gcc_assert (CONST_INT_P (op));
@@ -700,51 +768,6 @@ rx_gen_move_template (rtx * operands, bool is_movu)
extension, src_template, dst_template);
return out_template;
}
-
-/* Returns an assembler template for a conditional branch instruction. */
-
-const char *
-rx_gen_cond_branch_template (rtx condition, bool reversed)
-{
- enum rtx_code code = GET_CODE (condition);
-
- if (reversed)
- {
- if (rx_float_compare_mode)
- code = reverse_condition_maybe_unordered (code);
- else
- code = reverse_condition (code);
- }
-
- /* We do not worry about encoding the branch length here as GAS knows
- how to choose the smallest version, and how to expand a branch that
- is to a destination that is out of range. */
-
- switch (code)
- {
- case UNEQ: return "bo\t1f\n\tbeq\t%0\n1:";
- case LTGT: return "bo\t1f\n\tbne\t%0\n1:";
- case UNLT: return "bo\t1f\n\tbn\t%0\n1:";
- case UNGE: return "bo\t1f\n\tbpz\t%0\n1:";
- case UNLE: return "bo\t1f\n\tbgt\t1f\n\tbra\t%0\n1:";
- case UNGT: return "bo\t1f\n\tble\t1f\n\tbra\t%0\n1:";
- case UNORDERED: return "bo\t%0";
- case ORDERED: return "bno\t%0";
-
- case LT: return rx_float_compare_mode ? "bn\t%0" : "blt\t%0";
- case GE: return rx_float_compare_mode ? "bpz\t%0" : "bge\t%0";
- case GT: return "bgt\t%0";
- case LE: return "ble\t%0";
- case GEU: return "bgeu\t%0";
- case LTU: return "bltu\t%0";
- case GTU: return "bgtu\t%0";
- case LEU: return "bleu\t%0";
- case EQ: return "beq\t%0";
- case NE: return "bne\t%0";
- default:
- gcc_unreachable ();
- }
-}
/* Return VALUE rounded up to the next ALIGNMENT boundary. */
@@ -1226,13 +1249,13 @@ gen_rx_store_vector (unsigned int low, unsigned int high)
vector = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
XVECEXP (vector, 0, 0) =
- gen_rtx_SET (SImode, stack_pointer_rtx,
+ gen_rtx_SET (VOIDmode, stack_pointer_rtx,
gen_rtx_MINUS (SImode, stack_pointer_rtx,
GEN_INT ((count - 1) * UNITS_PER_WORD)));
for (i = 0; i < count - 1; i++)
XVECEXP (vector, 0, i + 1) =
- gen_rtx_SET (SImode,
+ gen_rtx_SET (VOIDmode,
gen_rtx_MEM (SImode,
gen_rtx_MINUS (SImode, stack_pointer_rtx,
GEN_INT ((i + 1) * UNITS_PER_WORD))),
@@ -1456,12 +1479,12 @@ gen_rx_rtsd_vector (unsigned int adjust, unsigned int low, unsigned int high)
vector = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
XVECEXP (vector, 0, 0) =
- gen_rtx_SET (SImode, stack_pointer_rtx,
+ gen_rtx_SET (VOIDmode, stack_pointer_rtx,
plus_constant (stack_pointer_rtx, adjust));
for (i = 0; i < count - 2; i++)
XVECEXP (vector, 0, i + 1) =
- gen_rtx_SET (SImode,
+ gen_rtx_SET (VOIDmode,
gen_rtx_REG (SImode, low + i),
gen_rtx_MEM (SImode,
i == 0 ? stack_pointer_rtx
@@ -1485,13 +1508,13 @@ gen_rx_popm_vector (unsigned int low, unsigned int high)
vector = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
XVECEXP (vector, 0, 0) =
- gen_rtx_SET (SImode, stack_pointer_rtx,
+ gen_rtx_SET (VOIDmode, stack_pointer_rtx,
plus_constant (stack_pointer_rtx,
(count - 1) * UNITS_PER_WORD));
for (i = 0; i < count - 1; i++)
XVECEXP (vector, 0, i + 1) =
- gen_rtx_SET (SImode,
+ gen_rtx_SET (VOIDmode,
gen_rtx_REG (SImode, low + i),
gen_rtx_MEM (SImode,
i == 0 ? stack_pointer_rtx
@@ -1824,7 +1847,6 @@ enum rx_builtin
RX_BUILTIN_REVW,
RX_BUILTIN_RMPA,
RX_BUILTIN_ROUND,
- RX_BUILTIN_SAT,
RX_BUILTIN_SETPSW,
RX_BUILTIN_WAIT,
RX_BUILTIN_max
@@ -1879,7 +1901,6 @@ rx_init_builtins (void)
ADD_RX_BUILTIN1 (RACW, "racw", void, integer);
ADD_RX_BUILTIN1 (ROUND, "round", intSI, float);
ADD_RX_BUILTIN1 (REVW, "revw", intSI, intSI);
- ADD_RX_BUILTIN1 (SAT, "sat", intSI, intSI);
ADD_RX_BUILTIN1 (WAIT, "wait", void, void);
}
@@ -2077,8 +2098,6 @@ rx_expand_builtin (tree exp,
case RX_BUILTIN_ROUND: return rx_expand_builtin_round (op, target);
case RX_BUILTIN_REVW: return rx_expand_int_builtin_1_arg
(op, target, gen_revw, false);
- case RX_BUILTIN_SAT: return rx_expand_int_builtin_1_arg
- (op, target, gen_sat, false);
case RX_BUILTIN_WAIT: emit_insn (gen_wait ()); return NULL_RTX;
default:
@@ -2316,48 +2335,6 @@ rx_is_ms_bitfield_layout (const_tree record_type ATTRIBUTE_UNUSED)
/* The packed attribute overrides the MS behaviour. */
return ! TYPE_PACKED (record_type);
}
-
-/* Try to generate code for the "isnv" pattern which inserts bits
- into a word.
- operands[0] => Location to be altered.
- operands[1] => Number of bits to change.
- operands[2] => Starting bit.
- operands[3] => Value to insert.
- Returns TRUE if successful, FALSE otherwise. */
-
-bool
-rx_expand_insv (rtx * operands)
-{
- if (INTVAL (operands[1]) != 1
- || ! CONST_INT_P (operands[3]))
- return false;
-
- if (MEM_P (operands[0])
- && INTVAL (operands[2]) > 7)
- return false;
-
- switch (INTVAL (operands[3]))
- {
- case 0:
- if (MEM_P (operands[0]))
- emit_insn (gen_bitclr_in_memory (operands[0], operands[0],
- operands[2]));
- else
- emit_insn (gen_bitclr (operands[0], operands[0], operands[2]));
- break;
- case 1:
- case -1:
- if (MEM_P (operands[0]))
- emit_insn (gen_bitset_in_memory (operands[0], operands[0],
- operands[2]));
- else
- emit_insn (gen_bitset (operands[0], operands[0], operands[2]));
- break;
- default:
- return false;
- }
- return true;
-}
/* Returns true if X a legitimate constant for an immediate
operand on the RX. X is already known to satisfy CONSTANT_P. */
@@ -2543,219 +2520,223 @@ rx_trampoline_init (rtx tramp, tree fndecl, rtx chain)
}
}
+static int
+rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
+{
+ return 2 + memory_move_secondary_cost (mode, regclass, in);
+}
+
+/* Convert a CC_MODE to the set of flags that it represents. */
+
+static unsigned int
+flags_from_mode (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case CC_ZSmode:
+ return CC_FLAG_S | CC_FLAG_Z;
+ case CC_ZSOmode:
+ return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
+ case CC_ZSCmode:
+ return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_C;
+ case CCmode:
+ return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O | CC_FLAG_C;
+ case CC_Fmode:
+ return CC_FLAG_FP;
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Convert a set of flags to a CC_MODE that can implement it. */
static enum machine_mode
-rx_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
+mode_from_flags (unsigned int f)
{
- if (m1 == CCmode)
- return m2;
- if (m2 == CCmode)
- return m1;
- if (m1 == m2)
- return m1;
- if (m1 == CC_ZSmode)
- return m1;
- if (m2 == CC_ZSmode)
- return m2;
- return VOIDmode;
+ if (f & CC_FLAG_FP)
+ return CC_Fmode;
+ if (f & CC_FLAG_O)
+ {
+ if (f & CC_FLAG_C)
+ return CCmode;
+ else
+ return CC_ZSOmode;
+ }
+ else if (f & CC_FLAG_C)
+ return CC_ZSCmode;
+ else
+ return CC_ZSmode;
}
-#define CC_FLAG_S (1 << 0)
-#define CC_FLAG_Z (1 << 1)
-#define CC_FLAG_O (1 << 2)
-#define CC_FLAG_C (1 << 3)
+/* Convert an RTX_CODE to the set of flags needed to implement it.
+ This assumes an integer comparison. */
static unsigned int
-flags_needed_for_conditional (rtx conditional)
+flags_from_code (enum rtx_code code)
{
- switch (GET_CODE (conditional))
+ switch (code)
{
+ case LT:
+ case GE:
+ return CC_FLAG_S;
+ case GT:
case LE:
- case GT: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
-
+ return CC_FLAG_S | CC_FLAG_O | CC_FLAG_Z;
+ case GEU:
+ case LTU:
+ return CC_FLAG_C;
+ case GTU:
case LEU:
- case GTU: return CC_FLAG_Z | CC_FLAG_C;
+ return CC_FLAG_C | CC_FLAG_Z;
+ case EQ:
+ case NE:
+ return CC_FLAG_Z;
+ default:
+ gcc_unreachable ();
+ }
+}
- case LT:
- case GE: return CC_FLAG_S | CC_FLAG_O;
+/* Return a CC_MODE of which both M1 and M2 are subsets. */
- case LTU:
- case GEU: return CC_FLAG_C;
+static enum machine_mode
+rx_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
+{
+ unsigned f;
- case EQ:
- case NE: return CC_FLAG_Z;
+ /* Early out for identical modes. */
+ if (m1 == m2)
+ return m1;
- default: gcc_unreachable ();
- }
+ /* There's no valid combination for FP vs non-FP. */
+ f = flags_from_mode (m1) | flags_from_mode (m2);
+ if (f & CC_FLAG_FP)
+ return VOIDmode;
+
+ /* Otherwise, see what mode can implement all the flags. */
+ return mode_from_flags (f);
}
-static unsigned int
-flags_from_mode (enum machine_mode mode)
+/* Return the minimal CC mode needed to implement (CMP_CODE X Y). */
+
+enum machine_mode
+rx_select_cc_mode (enum rtx_code cmp_code, rtx x, rtx y ATTRIBUTE_UNUSED)
{
- switch (mode)
- {
- case CCmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O | CC_FLAG_C;
- case CC_ZSmode: return CC_FLAG_S | CC_FLAG_Z;
- case CC_ZSOmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_O;
- case CC_ZSCmode: return CC_FLAG_S | CC_FLAG_Z | CC_FLAG_C;
- default: gcc_unreachable ();
- }
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ return CC_Fmode;
+
+ return mode_from_flags (flags_from_code (cmp_code));
}
-/* Returns true if a compare insn is redundant because it
- would only set flags that are already set correctly. */
+/* Split the floating-point comparison IN into individual comparisons
+ O1 and O2. O2 may be UNKNOWN if there is no second comparison.
+ Return true iff the comparison operands must be swapped. */
bool
-rx_compare_redundant (rtx cmp)
-{
- unsigned int flags_needed;
- unsigned int flags_set;
- rtx next;
- rtx prev;
- rtx source;
- rtx dest;
- static rtx cc_reg = NULL_RTX;
-
- if (cc_reg == NULL_RTX)
- cc_reg = gen_rtx_REG (CCmode, CC_REGNUM);
-
- /* We can only eliminate compares against 0. */
- if (GET_CODE (XEXP (SET_SRC (PATTERN (cmp)), 1)) != CONST_INT
- || INTVAL (XEXP (SET_SRC (PATTERN (cmp)), 1)) != 0)
- return false;
+rx_split_fp_compare (enum rtx_code in, enum rtx_code *o1, enum rtx_code *o2)
+{
+ enum rtx_code cmp1 = in, cmp2 = UNKNOWN;
+ bool swap = false;
- /* Locate the branch insn that follows the
- compare and which tests the bits in the PSW. */
- next = cmp;
- do
+ switch (in)
{
- /* If we have found an insn that sets or clobbers the CC
- register and it was not the IF_THEN_ELSE insn that we
- are looking for, then the comparison is redundant. */
- if (next != cmp && reg_mentioned_p (cc_reg, PATTERN (next)))
- return true;
-
- next = next_nonnote_insn (next);
-
- /* If we run out of insns without finding the
- user then the comparison is unnecessary. */
- if (next == NULL_RTX)
- return true;
-
- /* If we have found another comparison
- insn then the first one is redundant. */
- if (INSN_P (next)
- && GET_CODE (PATTERN (next)) == SET
- && REG_P (SET_DEST (PATTERN (next)))
- && REGNO (SET_DEST (PATTERN (next))) == CC_REGNUM)
- return true;
+ case ORDERED:
+ case UNORDERED:
+ case LT:
+ case GE:
+ case EQ:
+ case NE:
+ break;
- /* If we have found another arithmetic/logic insn that
- sets the PSW flags then the comparison is redundant. */
- if (INSN_P (next)
- && GET_CODE (PATTERN (next)) == PARALLEL
- && GET_CODE (XVECEXP (PATTERN (next), 0, 1)) == SET
- && REG_P (SET_DEST (XVECEXP (PATTERN (next), 0, 1)))
- && REGNO (SET_DEST (XVECEXP (PATTERN (next), 0, 1))) == CC_REGNUM)
- return true;
+ case GT:
+ case LE:
+ cmp1 = swap_condition (cmp1);
+ swap = true;
+ break;
- /* If we have found an unconditional branch then the
- PSW flags might be carried along with the jump, so
- the comparison is necessary. */
- if (INSN_P (next) && JUMP_P (next))
- {
- if (GET_CODE (PATTERN (next)) != SET)
- /* If the jump does not involve setting the PC
- then it is a return of some kind, and we know
- that the comparison is not used. */
- return true;
+ case UNEQ:
+ cmp1 = UNORDERED;
+ cmp2 = EQ;
+ break;
+ case UNLT:
+ cmp1 = UNORDERED;
+ cmp2 = LT;
+ break;
+ case UNGE:
+ cmp1 = UNORDERED;
+ cmp2 = GE;
+ break;
+ case UNLE:
+ cmp1 = UNORDERED;
+ cmp2 = GT;
+ swap = true;
+ break;
+ case UNGT:
+ cmp1 = UNORDERED;
+ cmp2 = LE;
+ swap = true;
+ break;
+ case LTGT:
+ cmp1 = ORDERED;
+ cmp2 = NE;
+ break;
- if (GET_CODE (SET_SRC (PATTERN (next))) != IF_THEN_ELSE)
- return false;
- }
+ default:
+ gcc_unreachable ();
}
- while (! INSN_P (next)
- || DEBUG_INSN_P (next)
- || GET_CODE (PATTERN (next)) != SET
- || GET_CODE (SET_SRC (PATTERN (next))) != IF_THEN_ELSE);
-
- flags_needed = flags_needed_for_conditional (XEXP (SET_SRC (PATTERN (next)), 0));
-
- /* Now look to see if there was a previous
- instruction which set the PSW bits. */
- source = XEXP (SET_SRC (PATTERN (cmp)), 0);
- prev = cmp;
- do
- {
- /* If this insn uses/sets/clobbers the CC register
- and it is not the insn that we are looking for
- below, then we must need the comparison. */
- if (prev != cmp && reg_mentioned_p (cc_reg, PATTERN (prev)))
- return false;
- prev = prev_nonnote_insn (prev);
+ *o1 = cmp1;
+ *o2 = cmp2;
+ return swap;
+}
- if (prev == NULL_RTX)
- return false;
+/* Split the conditional branch. Emit (COMPARE C1 C2) into CC_REG with
+ CC_MODE, and use that in branches based on that compare. */
- /* If we encounter an insn which changes the contents of
- the register which is the source of the comparison then
- we will definitely need the comparison. */
- if (INSN_P (prev)
- && GET_CODE (PATTERN (prev)) == SET
- && rtx_equal_p (SET_DEST (PATTERN (prev)), source))
- {
- /* Unless this instruction is a simple register move
- instruction. In which case we can continue our
- scan backwards, but now using the *source* of this
- set instruction. */
- if (REG_P (SET_SRC (PATTERN (prev))))
- source = SET_SRC (PATTERN (prev));
- /* We can also survive a sign-extension if the test is
- for EQ/NE. Note the same does not apply to zero-
- extension as this can turn a non-zero bit-pattern
- into zero. */
- else if (flags_needed == CC_FLAG_Z
- && GET_CODE (SET_SRC (PATTERN (prev))) == SIGN_EXTEND)
- source = XEXP (SET_SRC (PATTERN (prev)), 0);
- else
- return false;
- }
+void
+rx_split_cbranch (enum machine_mode cc_mode, enum rtx_code cmp1,
+ rtx c1, rtx c2, rtx label)
+{
+ rtx flags, x;
- /* A label means a possible branch into the
- code here, so we have to stop scanning. */
- if (LABEL_P (prev))
- return false;
- }
- while (! INSN_P (prev)
- || DEBUG_INSN_P (prev)
- || GET_CODE (PATTERN (prev)) != PARALLEL
- || GET_CODE (XVECEXP (PATTERN (prev), 0, 1)) != SET
- || ! REG_P (SET_DEST (XVECEXP (PATTERN (prev), 0, 1)))
- || REGNO (SET_DEST (XVECEXP (PATTERN (prev), 0, 1))) != CC_REGNUM);
-
- flags_set = flags_from_mode (GET_MODE (SET_DEST (XVECEXP (PATTERN (prev), 0, 1))));
-
- dest = SET_DEST (XVECEXP (PATTERN (prev), 0, 0));
- /* The destination of the previous arithmetic/logic instruction
- must match the source in the comparison operation. For registers
- we ignore the mode as there may have been a sign-extension involved. */
- if (! rtx_equal_p (source, dest))
- {
- if (REG_P (source) && REG_P (dest) && REGNO (dest) == REGNO (source))
- ;
- else
- return false;
- }
+ flags = gen_rtx_REG (cc_mode, CC_REG);
+ x = gen_rtx_COMPARE (cc_mode, c1, c2);
+ x = gen_rtx_SET (VOIDmode, flags, x);
+ emit_insn (x);
- return ((flags_set & flags_needed) == flags_needed);
+ x = gen_rtx_fmt_ee (cmp1, VOIDmode, flags, const0_rtx);
+ x = gen_rtx_IF_THEN_ELSE (VOIDmode, x, label, pc_rtx);
+ x = gen_rtx_SET (VOIDmode, pc_rtx, x);
+ emit_jump_insn (x);
}
-static int
-rx_memory_move_cost (enum machine_mode mode, reg_class_t regclass, bool in)
+/* A helper function for matching parallels that set the flags. */
+
+bool
+rx_match_ccmode (rtx insn, enum machine_mode cc_mode)
{
- return 2 + memory_move_secondary_cost (mode, regclass, in);
+ rtx op1, flags;
+ enum machine_mode flags_mode;
+
+ gcc_checking_assert (XVECLEN (PATTERN (insn), 0) == 2);
+
+ op1 = XVECEXP (PATTERN (insn), 0, 1);
+ gcc_checking_assert (GET_CODE (SET_SRC (op1)) == COMPARE);
+
+ flags = SET_DEST (op1);
+ flags_mode = GET_MODE (flags);
+
+ if (GET_MODE (SET_SRC (op1)) != flags_mode)
+ return false;
+ if (GET_MODE_CLASS (flags_mode) != MODE_CC)
+ return false;
+
+ /* Ensure that the mode of FLAGS is compatible with CC_MODE. */
+ if (flags_from_mode (flags_mode) & ~flags_from_mode (cc_mode))
+ return false;
+
+ return true;
}
+
#undef TARGET_FUNCTION_VALUE
#define TARGET_FUNCTION_VALUE rx_function_value