diff options
Diffstat (limited to 'gcc/config/rx/rx.c')
-rw-r--r-- | gcc/config/rx/rx.c | 561 |
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 |