static void mips_extra_live_on_entry (bitmap);
static int mips_comp_type_attributes (const_tree, const_tree);
static void mips_set_mips16_mode (int);
+static void mips_insert_attributes (tree, tree *);
+static tree mips_merge_decl_attributes (tree, tree);
static void mips_set_current_function (tree);
static int mips_mode_rep_extended (enum machine_mode, enum machine_mode);
static bool mips_offset_within_alignment_p (rtx, HOST_WIDE_INT);
{ "long_call", 0, 0, false, true, true, NULL },
{ "far", 0, 0, false, true, true, NULL },
{ "near", 0, 0, false, true, true, NULL },
- /* Switch MIPS16 ASE on and off per-function. */
- { "mips16", 0, 0, false, true, true, NULL },
- { "nomips16", 0, 0, false, true, true, NULL },
+ /* Switch MIPS16 ASE on and off per-function. We would really like
+ to make these type attributes, but GCC doesn't provide the hooks
+ we need to support the right conversion rules. As declaration
+ attributes, they affect code generation but don't carry other
+ semantics. */
+ { "mips16", 0, 0, true, false, false, NULL },
+ { "nomips16", 0, 0, true, false, false, NULL },
{ NULL, 0, 0, false, false, false, NULL }
};
\f
options correctly. */
const struct mips_cpu_info mips_cpu_info_table[] = {
/* Entries for generic ISAs */
- { "mips1", PROCESSOR_R3000, 1 },
- { "mips2", PROCESSOR_R6000, 2 },
- { "mips3", PROCESSOR_R4000, 3 },
- { "mips4", PROCESSOR_R8000, 4 },
- { "mips32", PROCESSOR_4KC, 32 },
- { "mips32r2", PROCESSOR_M4K, 33 },
- { "mips64", PROCESSOR_5KC, 64 },
+ { "mips1", PROCESSOR_R3000, 1, 0 },
+ { "mips2", PROCESSOR_R6000, 2, 0 },
+ { "mips3", PROCESSOR_R4000, 3, 0 },
+ { "mips4", PROCESSOR_R8000, 4, 0 },
+ /* Prefer not to use branch-likely instructions for generic MIPS32rX
+ and MIPS64rX code. The instructions were officially deprecated
+ in revisions 2 and earlier, but revision 3 is likely to downgrade
+ that to a recommendation to avoid the instructions in code that
+ isn't tuned to a specific processor. */
+ { "mips32", PROCESSOR_4KC, 32, PTF_AVOID_BRANCHLIKELY },
+ { "mips32r2", PROCESSOR_M4K, 33, PTF_AVOID_BRANCHLIKELY },
+ { "mips64", PROCESSOR_5KC, 64, PTF_AVOID_BRANCHLIKELY },
/* MIPS I */
- { "r3000", PROCESSOR_R3000, 1 },
- { "r2000", PROCESSOR_R3000, 1 }, /* = r3000 */
- { "r3900", PROCESSOR_R3900, 1 },
+ { "r3000", PROCESSOR_R3000, 1, 0 },
+ { "r2000", PROCESSOR_R3000, 1, 0 }, /* = r3000 */
+ { "r3900", PROCESSOR_R3900, 1, 0 },
/* MIPS II */
- { "r6000", PROCESSOR_R6000, 2 },
+ { "r6000", PROCESSOR_R6000, 2, 0 },
/* MIPS III */
- { "r4000", PROCESSOR_R4000, 3 },
- { "vr4100", PROCESSOR_R4100, 3 },
- { "vr4111", PROCESSOR_R4111, 3 },
- { "vr4120", PROCESSOR_R4120, 3 },
- { "vr4130", PROCESSOR_R4130, 3 },
- { "vr4300", PROCESSOR_R4300, 3 },
- { "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */
- { "r4600", PROCESSOR_R4600, 3 },
- { "orion", PROCESSOR_R4600, 3 }, /* = r4600 */
- { "r4650", PROCESSOR_R4650, 3 },
+ { "r4000", PROCESSOR_R4000, 3, 0 },
+ { "vr4100", PROCESSOR_R4100, 3, 0 },
+ { "vr4111", PROCESSOR_R4111, 3, 0 },
+ { "vr4120", PROCESSOR_R4120, 3, 0 },
+ { "vr4130", PROCESSOR_R4130, 3, 0 },
+ { "vr4300", PROCESSOR_R4300, 3, 0 },
+ { "r4400", PROCESSOR_R4000, 3, 0 }, /* = r4000 */
+ { "r4600", PROCESSOR_R4600, 3, 0 },
+ { "orion", PROCESSOR_R4600, 3, 0 }, /* = r4600 */
+ { "r4650", PROCESSOR_R4650, 3, 0 },
/* MIPS IV */
- { "r8000", PROCESSOR_R8000, 4 },
- { "vr5000", PROCESSOR_R5000, 4 },
- { "vr5400", PROCESSOR_R5400, 4 },
- { "vr5500", PROCESSOR_R5500, 4 },
- { "rm7000", PROCESSOR_R7000, 4 },
- { "rm9000", PROCESSOR_R9000, 4 },
+ { "r8000", PROCESSOR_R8000, 4, 0 },
+ { "vr5000", PROCESSOR_R5000, 4, 0 },
+ { "vr5400", PROCESSOR_R5400, 4, 0 },
+ { "vr5500", PROCESSOR_R5500, 4, PTF_AVOID_BRANCHLIKELY },
+ { "rm7000", PROCESSOR_R7000, 4, 0 },
+ { "rm9000", PROCESSOR_R9000, 4, 0 },
/* MIPS32 */
- { "4kc", PROCESSOR_4KC, 32 },
- { "4km", PROCESSOR_4KC, 32 }, /* = 4kc */
- { "4kp", PROCESSOR_4KP, 32 },
- { "4ksc", PROCESSOR_4KC, 32 },
+ { "4kc", PROCESSOR_4KC, 32, 0 },
+ { "4km", PROCESSOR_4KC, 32, 0 }, /* = 4kc */
+ { "4kp", PROCESSOR_4KP, 32, 0 },
+ { "4ksc", PROCESSOR_4KC, 32, 0 },
/* MIPS32 Release 2 */
- { "m4k", PROCESSOR_M4K, 33 },
- { "4kec", PROCESSOR_4KC, 33 },
- { "4kem", PROCESSOR_4KC, 33 },
- { "4kep", PROCESSOR_4KP, 33 },
- { "4ksd", PROCESSOR_4KC, 33 },
-
- { "24kc", PROCESSOR_24KC, 33 },
- { "24kf2_1", PROCESSOR_24KF2_1, 33 },
- { "24kf", PROCESSOR_24KF2_1, 33 },
- { "24kf1_1", PROCESSOR_24KF1_1, 33 },
- { "24kfx", PROCESSOR_24KF1_1, 33 },
- { "24kx", PROCESSOR_24KF1_1, 33 },
-
- { "24kec", PROCESSOR_24KC, 33 }, /* 24K with DSP */
- { "24kef2_1", PROCESSOR_24KF2_1, 33 },
- { "24kef", PROCESSOR_24KF2_1, 33 },
- { "24kef1_1", PROCESSOR_24KF1_1, 33 },
- { "24kefx", PROCESSOR_24KF1_1, 33 },
- { "24kex", PROCESSOR_24KF1_1, 33 },
-
- { "34kc", PROCESSOR_24KC, 33 }, /* 34K with MT/DSP */
- { "34kf2_1", PROCESSOR_24KF2_1, 33 },
- { "34kf", PROCESSOR_24KF2_1, 33 },
- { "34kf1_1", PROCESSOR_24KF1_1, 33 },
- { "34kfx", PROCESSOR_24KF1_1, 33 },
- { "34kx", PROCESSOR_24KF1_1, 33 },
-
- { "74kc", PROCESSOR_74KC, 33 }, /* 74K with DSPr2 */
- { "74kf2_1", PROCESSOR_74KF2_1, 33 },
- { "74kf", PROCESSOR_74KF2_1, 33 },
- { "74kf1_1", PROCESSOR_74KF1_1, 33 },
- { "74kfx", PROCESSOR_74KF1_1, 33 },
- { "74kx", PROCESSOR_74KF1_1, 33 },
- { "74kf3_2", PROCESSOR_74KF3_2, 33 },
+ { "m4k", PROCESSOR_M4K, 33, 0 },
+ { "4kec", PROCESSOR_4KC, 33, 0 },
+ { "4kem", PROCESSOR_4KC, 33, 0 },
+ { "4kep", PROCESSOR_4KP, 33, 0 },
+ { "4ksd", PROCESSOR_4KC, 33, 0 },
+
+ { "24kc", PROCESSOR_24KC, 33, 0 },
+ { "24kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kfx", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kx", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "24kec", PROCESSOR_24KC, 33, 0 }, /* 24K with DSP */
+ { "24kef2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kef", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kef1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kefx", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kex", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "34kc", PROCESSOR_24KC, 33, 0 }, /* 34K with MT/DSP */
+ { "34kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "34kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "34kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "34kfx", PROCESSOR_24KF1_1, 33, 0 },
+ { "34kx", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "74kc", PROCESSOR_74KC, 33, 0 }, /* 74K with DSPr2 */
+ { "74kf2_1", PROCESSOR_74KF2_1, 33, 0 },
+ { "74kf", PROCESSOR_74KF2_1, 33, 0 },
+ { "74kf1_1", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kfx", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kx", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kf3_2", PROCESSOR_74KF3_2, 33, 0 },
/* MIPS64 */
- { "5kc", PROCESSOR_5KC, 64 },
- { "5kf", PROCESSOR_5KF, 64 },
- { "20kc", PROCESSOR_20KC, 64 },
- { "sb1", PROCESSOR_SB1, 64 },
- { "sb1a", PROCESSOR_SB1A, 64 },
- { "sr71000", PROCESSOR_SR71000, 64 },
-
- /* End marker */
- { 0, 0, 0 }
+ { "5kc", PROCESSOR_5KC, 64, 0 },
+ { "5kf", PROCESSOR_5KF, 64, 0 },
+ { "20kc", PROCESSOR_20KC, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sb1", PROCESSOR_SB1, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sb1a", PROCESSOR_SB1A, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sr71000", PROCESSOR_SR71000, 64, PTF_AVOID_BRANCHLIKELY },
};
/* Default costs. If these are used for a processor we should look
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES mips_insert_attributes
+#undef TARGET_MERGE_DECL_ATTRIBUTES
+#define TARGET_MERGE_DECL_ATTRIBUTES mips_merge_decl_attributes
#undef TARGET_SET_CURRENT_FUNCTION
#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
#undef TARGET_ATTRIBUTE_TABLE
#define TARGET_ATTRIBUTE_TABLE mips_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+ be compiled or called. They don't in themselves prevent inlining. */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
#undef TARGET_EXTRA_LIVE_ON_ENTRY
#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
/* Similar predicates for "mips16"/"nomips16" attributes. */
static bool
-mips_mips16_type_p (const_tree type)
+mips_mips16_decl_p (const_tree decl)
{
- return lookup_attribute ("mips16", TYPE_ATTRIBUTES (type)) != NULL;
+ return lookup_attribute ("mips16", DECL_ATTRIBUTES (decl)) != NULL;
}
static bool
-mips_nomips16_type_p (const_tree type)
+mips_nomips16_decl_p (const_tree decl)
{
- return lookup_attribute ("nomips16", TYPE_ATTRIBUTES (type)) != NULL;
+ return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
}
/* Return 0 if the attributes for two types are incompatible, 1 if they
if (mips_near_type_p (type1) && mips_far_type_p (type2))
return 0;
- /* Mips16/nomips16 attributes must match exactly. */
- if (mips_nomips16_type_p (type1) != mips_nomips16_type_p (type2)
- || mips_mips16_type_p (type1) != mips_mips16_type_p (type2))
- return 0;
-
return 1;
}
\f
rtx
mips_subword (rtx op, int high_p)
{
- unsigned int byte;
+ unsigned int byte, offset;
enum machine_mode mode;
mode = GET_MODE (op);
byte = 0;
if (FP_REG_RTX_P (op))
- return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op));
+ {
+ /* Paired FPRs are always ordered little-endian. */
+ offset = (UNITS_PER_WORD < UNITS_PER_HWFPVALUE ? high_p : byte != 0);
+ return gen_rtx_REG (word_mode, REGNO (op) + offset);
+ }
if (MEM_P (op))
return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
}
-/* Split a 64-bit move from SRC to DEST assuming that
- mips_split_64bit_move_p holds.
-
- Moves into and out of FPRs cause some difficulty here. Such moves
- will always be DFmode, since paired FPRs are not allowed to store
- DImode values. The most natural representation would be two separate
- 32-bit moves, such as:
-
- (set (reg:SI $f0) (mem:SI ...))
- (set (reg:SI $f1) (mem:SI ...))
-
- However, the second insn is invalid because odd-numbered FPRs are
- not allowed to store independent values. Use the patterns load_df_low,
- load_df_high and store_df_high instead. */
+/* Split a doubleword move from SRC to DEST. On 32-bit targets,
+ this function handles 64-bit moves for which mips_split_64bit_move_p
+ holds. For 64-bit targets, this function handles 128-bit moves. */
void
-mips_split_64bit_move (rtx dest, rtx src)
+mips_split_doubleword_move (rtx dest, rtx src)
{
- if (FP_REG_RTX_P (dest))
- {
- /* Loading an FPR from memory or from GPRs. */
- if (ISA_HAS_MXHC1)
- {
- dest = gen_lowpart (DFmode, dest);
- emit_insn (gen_load_df_low (dest, mips_subword (src, 0)));
- emit_insn (gen_mthc1 (dest, mips_subword (src, 1),
- copy_rtx (dest)));
- }
- else
- {
- emit_insn (gen_load_df_low (copy_rtx (dest),
- mips_subword (src, 0)));
- emit_insn (gen_load_df_high (dest, mips_subword (src, 1),
- copy_rtx (dest)));
- }
- }
- else if (FP_REG_RTX_P (src))
+ if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
{
- /* Storing an FPR into memory or GPRs. */
- if (ISA_HAS_MXHC1)
- {
- src = gen_lowpart (DFmode, src);
- mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
- emit_insn (gen_mfhc1 (mips_subword (dest, 1), src));
- }
+ if (!TARGET_64BIT && GET_MODE (dest) == DImode)
+ emit_insn (gen_move_doubleword_fprdi (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == DFmode)
+ emit_insn (gen_move_doubleword_fprdf (dest, src));
+ else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+ emit_insn (gen_move_doubleword_fprtf (dest, src));
else
- {
- mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
- emit_insn (gen_store_df_high (mips_subword (dest, 1), src));
- }
+ gcc_unreachable ();
}
else
{
operands[1]));
}
\f
+/* Return true if function DECL is a MIPS16 function. Return the ambient
+ setting if DECL is null. */
+
+static bool
+mips_use_mips16_mode_p (tree decl)
+{
+ if (decl)
+ {
+ /* Nested functions must use the same frame pointer as their
+ parent and must therefore use the same ISA mode. */
+ tree parent = decl_function_context (decl);
+ if (parent)
+ decl = parent;
+ if (mips_mips16_decl_p (decl))
+ return true;
+ if (mips_nomips16_decl_p (decl))
+ return false;
+ }
+ return mips_base_mips16;
+}
+
/* Return true if calls to X can use R_MIPS_CALL* relocations. */
static bool
/* We can't do a sibcall if the called function is a MIPS16 function
because there is no direct "jx" instruction equivalent to "jalx" to
switch the ISA mode. */
- if (decl && SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (decl), 0)))
+ if (mips_use_mips16_mode_p (decl))
+ return false;
+
+ /* ...and when -minterlink-mips16 is in effect, assume that external
+ functions could be MIPS16 ones unless an attribute explicitly
+ tells us otherwise. We only care about cases where the sibling
+ and normal calls would both be direct. */
+ if (TARGET_INTERLINK_MIPS16
+ && decl
+ && DECL_EXTERNAL (decl)
+ && !mips_nomips16_decl_p (decl)
+ && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
return false;
/* Otherwise OK. */
we use a %gprel() operator. */
target_flags &= ~MASK_EXPLICIT_RELOCS;
- /* Silently disable DSP extensions. */
- target_flags &= ~MASK_DSP;
- target_flags &= ~MASK_DSPR2;
+ /* Experiments suggest we get the best overall results from using
+ the range of an unextended lw or sw. Code that makes heavy use
+ of byte or short accesses can do better with ranges of 0...31
+ and 0...63 respectively, but most code is sensitive to the range
+ of lw and sw instead. */
+ targetm.min_anchor_offset = 0;
+ targetm.max_anchor_offset = 127;
+
+ if (flag_pic || TARGET_ABICALLS)
+ sorry ("MIPS16 PIC");
}
else
{
if (align_functions == 0)
align_functions = 8;
}
+
+ targetm.min_anchor_offset = TARGET_MIN_ANCHOR_OFFSET;
+ targetm.max_anchor_offset = TARGET_MAX_ANCHOR_OFFSET;
}
/* (Re)initialize mips target internals for new ISA. */
was_mips16_p = TARGET_MIPS16;
}
+/* Use a hash table to keep track of implicit mips16/nomips16 attributes
+ for -mflip_mips16. It maps decl names onto a boolean mode setting. */
+
+struct mflip_mips16_entry GTY (()) {
+ const char *name;
+ bool mips16_p;
+};
+static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
+
+/* Hash table callbacks for mflip_mips16_htab. */
+
+static hashval_t
+mflip_mips16_htab_hash (const void *entry)
+{
+ return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
+}
+
+static int
+mflip_mips16_htab_eq (const void *entry, const void *name)
+{
+ return strcmp (((const struct mflip_mips16_entry *) entry)->name,
+ (const char *) name) == 0;
+}
+
+/* DECL is a function that needs a default "mips16" or "nomips16" attribute
+ for -mflip-mips16. Return true if it should use "mips16" and false if
+ it should use "nomips16". */
+
+static bool
+mflip_mips16_use_mips16_p (tree decl)
+{
+ struct mflip_mips16_entry *entry;
+ const char *name;
+ hashval_t hash;
+ void **slot;
+
+ /* Use the opposite of the command-line setting for anonymous decls. */
+ if (!DECL_NAME (decl))
+ return !mips_base_mips16;
+
+ if (!mflip_mips16_htab)
+ mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
+ mflip_mips16_htab_eq, NULL);
+
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ hash = htab_hash_string (name);
+ slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
+ entry = (struct mflip_mips16_entry *) *slot;
+ if (!entry)
+ {
+ mips16_flipper = !mips16_flipper;
+ entry = GGC_NEW (struct mflip_mips16_entry);
+ entry->name = name;
+ entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
+ *slot = entry;
+ }
+ return entry->mips16_p;
+}
+
+/* Implement TARGET_INSERT_ATTRIBUTES. */
+
+static void
+mips_insert_attributes (tree decl, tree *attributes)
+{
+ const char *name;
+ bool mips16_p, nomips16_p;
+
+ /* Check for "mips16" and "nomips16" attributes. */
+ mips16_p = lookup_attribute ("mips16", *attributes) != NULL;
+ nomips16_p = lookup_attribute ("nomips16", *attributes) != NULL;
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ if (mips16_p)
+ error ("%qs attribute only applies to functions", "mips16");
+ if (nomips16_p)
+ error ("%qs attribute only applies to functions", "nomips16");
+ }
+ else
+ {
+ mips16_p |= mips_mips16_decl_p (decl);
+ nomips16_p |= mips_nomips16_decl_p (decl);
+ if (mips16_p || nomips16_p)
+ {
+ /* DECL cannot be simultaneously mips16 and nomips16. */
+ if (mips16_p && nomips16_p)
+ error ("%qs cannot have both %<mips16%> and "
+ "%<nomips16%> attributes",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ }
+ else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
+ {
+ /* Implement -mflip-mips16. If DECL has neither a "nomips16" nor a
+ "mips16" attribute, arbitrarily pick one. We must pick the same
+ setting for duplicate declarations of a function. */
+ name = mflip_mips16_use_mips16_p (decl) ? "mips16" : "nomips16";
+ *attributes = tree_cons (get_identifier (name), NULL, *attributes);
+ }
+ }
+}
+
+/* Implement TARGET_MERGE_DECL_ATTRIBUTES. */
+
+static tree
+mips_merge_decl_attributes (tree olddecl, tree newdecl)
+{
+ /* The decls' "mips16" and "nomips16" attributes must match exactly. */
+ if (mips_mips16_decl_p (olddecl) != mips_mips16_decl_p (newdecl))
+ error ("%qs redeclared with conflicting %qs attributes",
+ IDENTIFIER_POINTER (DECL_NAME (newdecl)), "mips16");
+ if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
+ error ("%qs redeclared with conflicting %qs attributes",
+ IDENTIFIER_POINTER (DECL_NAME (newdecl)), "nomips16");
+
+ return merge_attributes (DECL_ATTRIBUTES (olddecl),
+ DECL_ATTRIBUTES (newdecl));
+}
+
/* Implement TARGET_SET_CURRENT_FUNCTION. Decide whether the current
function should use the MIPS16 ISA and switch modes accordingly. */
static void
-mips_set_current_function (tree fndecl ATTRIBUTE_UNUSED)
+mips_set_current_function (tree fndecl)
{
+ mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
}
/* Implement TARGET_HANDLE_OPTION. */
{
/* If neither -mbranch-likely nor -mno-branch-likely was given
on the command line, set MASK_BRANCHLIKELY based on the target
- architecture.
-
- By default, we enable use of Branch Likely instructions on
- all architectures which support them with the following
- exceptions: when creating MIPS32 or MIPS64 code, and when
- tuning for architectures where their use tends to hurt
- performance.
-
- The MIPS32 and MIPS64 architecture specifications say "Software
- is strongly encouraged to avoid use of Branch Likely
- instructions, as they will be removed from a future revision
- of the [MIPS32 and MIPS64] architecture." Therefore, we do not
- issue those instructions unless instructed to do so by
- -mbranch-likely. */
+ architecture and tuning flags. Annulled delay slots are a
+ size win, so we only consider the processor-specific tuning
+ for !optimize_size. */
if (ISA_HAS_BRANCHLIKELY
- && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64)
- && !(TUNE_MIPS5500 || TUNE_SB1))
+ && (optimize_size
+ || (mips_tune_info->tune_flags & PTF_AVOID_BRANCHLIKELY) == 0))
target_flags |= MASK_BRANCHLIKELY;
else
target_flags &= ~MASK_BRANCHLIKELY;
}
- if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
- warning (0, "generation of Branch Likely instructions enabled, but not supported by architecture");
+ else if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
+ warning (0, "the %qs architecture does not support branch-likely"
+ " instructions", mips_arch_info->name);
/* The effect of -mabicalls isn't defined for the EABI. */
if (mips_abi == ABI_EABI && TARGET_ABICALLS)
target_flags |= MASK_PAIRED_SINGLE_FLOAT;
/* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64
- and TARGET_HARD_FLOAT are both true. */
- if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT))
+ and TARGET_HARD_FLOAT_ABI are both true. */
+ if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float");
/* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
mips_print_operand_punct['$'] = 1;
mips_print_operand_punct['+'] = 1;
mips_print_operand_punct['~'] = 1;
+ mips_print_operand_punct['|'] = 1;
+ mips_print_operand_punct['-'] = 1;
/* Set up array to map GCC register number to debug register number.
Ignore the special purpose register numbers. */
void
mips_conditional_register_usage (void)
{
- if (!TARGET_DSP)
+ if (!ISA_HAS_DSP)
{
int regno;
'^' Print the name of the pic call-through register (t9 or $25).
'$' Print the name of the stack pointer register (sp or $29).
'+' Print the name of the gp register (usually gp or $28).
- '~' Output a branch alignment to LABEL_ALIGN(NULL). */
+ '~' Output a branch alignment to LABEL_ALIGN(NULL).
+ '|' Print .set push; .set mips2 if !ISA_HAS_LL_SC.
+ '-' Print .set pop under the same conditions for '|'. */
void
print_operand (FILE *file, rtx op, int letter)
}
break;
+ case '|':
+ if (!ISA_HAS_LL_SC)
+ fputs (".set\tpush\n\t.set\tmips2\n\t", file);
+ break;
+
+ case '-':
+ if (!ISA_HAS_LL_SC)
+ fputs ("\n\t.set\tpop", file);
+ break;
+
default:
error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
break;
because in this way we can avoid creating an allocated section. We
do not want this section to take up any space in the running
executable. */
- fprintf (asm_out_file, "\t.section .mdebug.%s\n", abi_string);
+ fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
+ abi_string);
/* There is no ELF header flag to distinguish long32 forms of the
EABI from long64 forms. Emit a special section to help tools
such as GDB. Do the same for o64, which is sometimes used with
-mlong64. */
if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
- fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n",
- TARGET_LONG64 ? 64 : 32);
-
- /* Restore the default section. */
- fprintf (asm_out_file, "\t.previous\n");
+ fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
+ "\t.previous\n", TARGET_LONG64 ? 64 : 32);
#ifdef HAVE_AS_GNU_ATTRIBUTE
fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
return true;
+ /* Check for registers that must be saved for FUNCTION_PROFILER. */
+ if (current_function_profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
+ return true;
+
/* We need to save the incoming return address if it is ever clobbered
within the function, if __builtin_eh_return is being used to set a
different return address, or if a stub is being used to return a
rtx x1, x2;
if (mips_split_64bit_move_p (mem, reg))
- mips_split_64bit_move (mem, reg);
+ mips_split_doubleword_move (mem, reg);
else
mips_emit_move (mem, reg);
tree function)
{
rtx this, temp1, temp2, insn, fnaddr;
+ bool use_sibcall_p;
/* Pretend to be a post-reload pass while generating rtl. */
reload_completed = 1;
/* Mark the end of the (empty) prologue. */
emit_note (NOTE_INSN_PROLOGUE_END);
- /* Pick a global pointer. Use a call-clobbered register if
- TARGET_CALL_SAVED_GP, so that we can use a sibcall. */
- if (TARGET_USE_GOT)
- {
- cfun->machine->global_pointer =
- TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+ /* Determine if we can use a sibcall to call FUNCTION directly. */
+ fnaddr = XEXP (DECL_RTL (function), 0);
+ use_sibcall_p = (mips_function_ok_for_sibcall (function, NULL)
+ && const_call_insn_operand (fnaddr, Pmode));
- SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
- }
+ /* Determine if we need to load FNADDR from the GOT. */
+ if (!use_sibcall_p)
+ switch (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))
+ {
+ case SYMBOL_GOT_PAGE_OFST:
+ case SYMBOL_GOT_DISP:
+ /* Pick a global pointer. Use a call-clobbered register if
+ TARGET_CALL_SAVED_GP. */
+ cfun->machine->global_pointer =
+ TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+ SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+ /* Set up the global pointer for n32 or n64 abicalls. */
+ mips_emit_loadgp ();
+ break;
- /* Set up the global pointer for n32 or n64 abicalls. If
- LOADGP_ABSOLUTE then the thunk does not use the gp and there is
- no need to load it.*/
- if (mips_current_loadgp_style () != LOADGP_ABSOLUTE
- || !targetm.binds_local_p (function))
- mips_emit_loadgp ();
+ default:
+ break;
+ }
/* We need two temporary registers in some cases. */
temp1 = gen_rtx_REG (Pmode, 2);
/* Jump to the target function. Use a sibcall if direct jumps are
allowed, otherwise load the address into a register first. */
- fnaddr = XEXP (DECL_RTL (function), 0);
- if (TARGET_MIPS16 || TARGET_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr)
- || SYMBOL_REF_MIPS16_FUNC_P (fnaddr))
+ if (use_sibcall_p)
+ {
+ insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+ SIBLING_CALL_P (insn) = 1;
+ }
+ else
{
/* This is messy. gas treats "la $25,foo" as part of a call
sequence and may allow a global "foo" to be lazily bound.
mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
emit_jump_insn (gen_indirect_jump (temp1));
}
- else
- {
- insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
- SIBLING_CALL_P (insn) = 1;
- }
/* Run just enough of rest_of_compilation. This sequence was
"borrowed" from alpha.c. */
return false;
default:
- return true;
+ return default_use_anchors_for_symbol_p (symbol);
}
}
\f
to mode TO. */
bool
-mips_cannot_change_mode_class (enum machine_mode from,
- enum machine_mode to, enum reg_class class)
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+ enum machine_mode to ATTRIBUTE_UNUSED,
+ enum reg_class class)
{
- if (MIN (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) <= UNITS_PER_WORD
- && MAX (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) > UNITS_PER_WORD)
- {
- if (TARGET_BIG_ENDIAN)
- {
- /* When a multi-word value is stored in paired floating-point
- registers, the first register always holds the low word.
- We therefore can't allow FPRs to change between single-word
- and multi-word modes. */
- if (MAX_FPRS_PER_FMT > 1 && reg_classes_intersect_p (FP_REGS, class))
- return true;
- }
- }
+ /* There are several problems with changing the modes of values
+ in floating-point registers:
- /* gcc assumes that each word of a multiword register can be accessed
- individually using SUBREGs. This is not true for floating-point
- registers if they are bigger than a word. */
- if (UNITS_PER_FPREG > UNITS_PER_WORD
- && GET_MODE_SIZE (from) > UNITS_PER_WORD
- && GET_MODE_SIZE (to) < UNITS_PER_FPREG
- && reg_classes_intersect_p (FP_REGS, class))
- return true;
+ - When a multi-word value is stored in paired floating-point
+ registers, the first register always holds the low word.
+ We therefore can't allow FPRs to change between single-word
+ and multi-word modes on big-endian targets.
- /* Loading a 32-bit value into a 64-bit floating-point register
- will not sign-extend the value, despite what LOAD_EXTEND_OP says.
- We can't allow 64-bit float registers to change from SImode to
- to a wider mode. */
- if (TARGET_64BIT
- && TARGET_FLOAT64
- && from == SImode
- && GET_MODE_SIZE (to) >= UNITS_PER_WORD
- && reg_classes_intersect_p (FP_REGS, class))
- return true;
+ - GCC assumes that each word of a multiword register can be accessed
+ individually using SUBREGs. This is not true for floating-point
+ registers if they are bigger than a word.
- return false;
+ - Loading a 32-bit value into a 64-bit floating-point register
+ will not sign-extend the value, despite what LOAD_EXTEND_OP says.
+ We can't allow FPRs to change from SImode to to a wider mode on
+ 64-bit targets.
+
+ - If the FPU has already interpreted a value in one format, we must
+ not ask it to treat the value as having a different format.
+
+ We therefore only allow changes between 4-byte and smaller integer
+ values, all of which have the "W" format as far as the FPU is
+ concerned. */
+ return (reg_classes_intersect_p (FP_REGS, class)
+ && (GET_MODE_CLASS (from) != MODE_INT
+ || GET_MODE_CLASS (to) != MODE_INT
+ || GET_MODE_SIZE (from) > 4
+ || GET_MODE_SIZE (to) > 4));
}
/* Return true if X should not be moved directly into register $25.
&& mips_global_symbol_p (x));
}
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction. */
+
+static bool
+mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case SFmode:
+ return TARGET_HARD_FLOAT;
+
+ case DFmode:
+ return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
+
+ case V2SFmode:
+ return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
+
+ default:
+ return false;
+ }
+}
+
/* Implement PREFERRED_RELOAD_CLASS. */
enum reg_class
if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
return LEA_REGS;
- if (TARGET_HARD_FLOAT
- && FLOAT_MODE_P (GET_MODE (x))
- && reg_class_subset_p (FP_REGS, class))
+ if (reg_class_subset_p (FP_REGS, class)
+ && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
return FP_REGS;
if (reg_class_subset_p (GR_REGS, class))
mips_secondary_reload_class (enum reg_class class,
enum machine_mode mode, rtx x, int in_p)
{
- enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
- int regno = -1;
- int gp_reg_p;
-
- if (REG_P (x)|| GET_CODE (x) == SUBREG)
- regno = true_regnum (x);
-
- gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+ int regno;
+ /* If X is a constant that cannot be loaded into $25, it must be loaded
+ into some other GPR. No other register class allows a direct move. */
if (mips_dangerous_for_la25_p (x))
+ return reg_class_subset_p (class, LEA_REGS) ? NO_REGS : LEA_REGS;
+
+ regno = true_regnum (x);
+ if (TARGET_MIPS16)
{
- gr_regs = LEA_REGS;
- if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25))
- return gr_regs;
+ /* In MIPS16 mode, every move must involve a member of M16_REGS. */
+ if (!reg_class_subset_p (class, M16_REGS) && !M16_REG_P (regno))
+ return M16_REGS;
+
+ /* We can't really copy to HI or LO at all in MIPS16 mode. */
+ if (in_p ? reg_classes_intersect_p (class, ACC_REGS) : ACC_REG_P (regno))
+ return M16_REGS;
+
+ return NO_REGS;
}
- /* Copying from HI or LO to anywhere other than a general register
- requires a general register.
- This rule applies to both the original HI/LO pair and the new
- DSP accumulators. */
+ /* Copying from accumulator registers to anywhere other than a general
+ register requires a temporary general register. */
if (reg_class_subset_p (class, ACC_REGS))
- {
- if (TARGET_MIPS16 && in_p)
- {
- /* We can't really copy to HI or LO at all in mips16 mode. */
- return M16_REGS;
- }
- return gp_reg_p ? NO_REGS : gr_regs;
- }
+ return GP_REG_P (regno) ? NO_REGS : GR_REGS;
if (ACC_REG_P (regno))
- {
- if (TARGET_MIPS16 && ! in_p)
- {
- /* We can't really copy to HI or LO at all in mips16 mode. */
- return M16_REGS;
- }
- return class == gr_regs ? NO_REGS : gr_regs;
- }
+ return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
/* We can only copy a value to a condition code register from a
floating point register, and even then we require a scratch
floating point register. We can only copy a value out of a
condition code register into a general register. */
- if (class == ST_REGS)
+ if (reg_class_subset_p (class, ST_REGS))
{
if (in_p)
return FP_REGS;
- return gp_reg_p ? NO_REGS : gr_regs;
+ return GP_REG_P (regno) ? NO_REGS : GR_REGS;
}
if (ST_REG_P (regno))
{
- if (! in_p)
+ if (!in_p)
return FP_REGS;
- return class == gr_regs ? NO_REGS : gr_regs;
+ return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
}
- if (class == FP_REGS)
+ if (reg_class_subset_p (class, FP_REGS))
{
- if (MEM_P (x))
- {
- /* In this case we can use lwc1, swc1, ldc1 or sdc1. */
- return NO_REGS;
- }
- else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT)
- {
- /* We can use the l.s and l.d macros to load floating-point
- constants. ??? For l.s, we could probably get better
- code by returning GR_REGS here. */
- return NO_REGS;
- }
- else if (gp_reg_p || x == CONST0_RTX (mode))
- {
- /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */
- return NO_REGS;
- }
- else if (FP_REG_P (regno))
- {
- /* In this case we can use mov.s or mov.d. */
- return NO_REGS;
- }
- else
- {
- /* Otherwise, we need to reload through an integer register. */
- return gr_regs;
- }
- }
+ if (MEM_P (x)
+ && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
+ /* In this case we can use lwc1, swc1, ldc1 or sdc1. We'll use
+ pairs of lwc1s and swc1s if ldc1 and sdc1 are not supported. */
+ return NO_REGS;
- /* In mips16 mode, going between memory and anything but M16_REGS
- requires an M16_REG. */
- if (TARGET_MIPS16)
- {
- if (class != M16_REGS && class != M16_NA_REGS)
- {
- if (gp_reg_p)
- return NO_REGS;
- return M16_REGS;
- }
- if (! gp_reg_p)
- {
- if (class == M16_REGS || class == M16_NA_REGS)
- return NO_REGS;
- return M16_REGS;
- }
+ if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+ /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */
+ return NO_REGS;
+
+ if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (x))
+ /* We can force the constant to memory and use lwc1
+ and ldc1. As above, we will use pairs of lwc1s if
+ ldc1 is not supported. */
+ return NO_REGS;
+
+ if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
+ /* In this case we can use mov.fmt. */
+ return NO_REGS;
+
+ /* Otherwise, we need to reload through an integer register. */
+ return GR_REGS;
}
+ if (FP_REG_P (regno))
+ return reg_class_subset_p (class, GR_REGS) ? NO_REGS : GR_REGS;
return NO_REGS;
}
unsigned int f;
fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ fnname = targetm.strip_name_encoding (fnname);
secname = (char *) alloca (strlen (fnname) + 20);
sprintf (secname, ".mips16.fn.%s", fnname);
stubname = (char *) alloca (strlen (fnname) + 20);
/* We know the function we are going to call. If we have already
built a stub, we don't need to do anything further. */
- fnname = XSTR (fn, 0);
+ fnname = targetm.strip_name_encoding (XSTR (fn, 0));
for (l = mips16_stubs; l != NULL; l = l->next)
if (strcmp (l->name, fnname) == 0)
break;
fprintf (asm_out_file, "\tjal\t%s\n", fnname);
/* As above, we can't fill the delay slot. */
fprintf (asm_out_file, "\tnop\n");
- if (GET_MODE (retval) == SFmode)
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
- else if (GET_MODE (retval) == SCmode)
- {
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 2],
- reg_names[FP_REG_FIRST + 0]);
+ switch (GET_MODE (retval))
+ {
+ case SCmode:
fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 3],
reg_names[FP_REG_FIRST + MAX_FPRS_PER_FMT]);
- }
- else if (GET_MODE (retval) == DFmode
- || GET_MODE (retval) == V2SFmode)
- {
- mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
- }
- else if (GET_MODE (retval) == DCmode)
- {
- mips16_fpret_double (GP_REG_FIRST + 2,
- FP_REG_FIRST + 0);
- mips16_fpret_double (GP_REG_FIRST + 4,
- FP_REG_FIRST + MAX_FPRS_PER_FMT);
- }
- else
- {
- if (TARGET_BIG_ENDIAN)
+ /* Fall though. */
+ case SFmode:
+ fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+ reg_names[GP_REG_FIRST + 2],
+ reg_names[FP_REG_FIRST + 0]);
+ if (GET_MODE (retval) == SCmode && TARGET_64BIT)
{
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
+ /* On 64-bit targets, complex floats are returned in
+ a single GPR, such that "sd" on a suitably-aligned
+ target would store the value correctly. */
+ fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+ reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN],
+ reg_names[GP_REG_FIRST + 2 + TARGET_LITTLE_ENDIAN]);
+ fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
reg_names[GP_REG_FIRST + 2],
- reg_names[FP_REG_FIRST + 1]);
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 3],
- reg_names[FP_REG_FIRST + 0]);
- }
- else
- {
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
reg_names[GP_REG_FIRST + 2],
- reg_names[FP_REG_FIRST + 0]);
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 3],
- reg_names[FP_REG_FIRST + 1]);
+ reg_names[GP_REG_FIRST + 3]);
}
+ break;
+
+ case DCmode:
+ mips16_fpret_double (GP_REG_FIRST + 2 + (8 / UNITS_PER_WORD),
+ FP_REG_FIRST + MAX_FPRS_PER_FMT);
+ /* Fall though. */
+ case DFmode:
+ case V2SFmode:
+ mips16_fpret_double (GP_REG_FIRST + 2, FP_REG_FIRST + 0);
+ break;
+
+ default:
+ gcc_unreachable ();
}
fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
/* As above, we can't fill the delay slot. */
static rtx
dump_constants_1 (enum machine_mode mode, rtx value, rtx insn)
{
- switch (GET_MODE_CLASS (mode))
+ if (SCALAR_INT_MODE_P (mode)
+ || ALL_SCALAR_FRACT_MODE_P (mode)
+ || ALL_SCALAR_ACCUM_MODE_P (mode))
{
- case MODE_INT:
- {
- rtx size = GEN_INT (GET_MODE_SIZE (mode));
- return emit_insn_after (gen_consttable_int (value, size), insn);
- }
+ rtx size = GEN_INT (GET_MODE_SIZE (mode));
+ return emit_insn_after (gen_consttable_int (value, size), insn);
+ }
- case MODE_FLOAT:
- return emit_insn_after (gen_consttable_float (value), insn);
+ if (SCALAR_FLOAT_MODE_P (mode))
+ return emit_insn_after (gen_consttable_float (value), insn);
- case MODE_VECTOR_FLOAT:
- case MODE_VECTOR_INT:
- {
- int i;
- for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
- insn = dump_constants_1 (GET_MODE_INNER (mode),
- CONST_VECTOR_ELT (value, i), insn);
- return insn;
- }
+ if (VECTOR_MODE_P (mode))
+ {
+ int i;
- default:
- gcc_unreachable ();
+ for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+ insn = dump_constants_1 (GET_MODE_INNER (mode),
+ CONST_VECTOR_ELT (value, i), insn);
+ return insn;
}
+
+ gcc_unreachable ();
}
we need to use. This gets pretty messy, but it is feasible. */
int
-mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+mips_register_move_cost (enum machine_mode mode,
enum reg_class to, enum reg_class from)
{
- if (from == M16_REGS && reg_class_subset_p (to, GENERAL_REGS))
- return 2;
- else if (from == M16_NA_REGS && reg_class_subset_p (to, GENERAL_REGS))
- return 2;
- else if (reg_class_subset_p (from, GENERAL_REGS))
+ if (TARGET_MIPS16)
{
- if (to == M16_REGS)
- return 2;
- else if (to == M16_NA_REGS)
- return 2;
- else if (reg_class_subset_p (to, GENERAL_REGS))
+ if (reg_class_subset_p (from, GENERAL_REGS)
+ && reg_class_subset_p (to, GENERAL_REGS))
{
- if (TARGET_MIPS16)
- return 4;
- else
+ if (reg_class_subset_p (from, M16_REGS)
+ || reg_class_subset_p (to, M16_REGS))
return 2;
- }
- else if (to == FP_REGS)
- return 4;
- else if (reg_class_subset_p (to, ACC_REGS))
- {
- if (TARGET_MIPS16)
- return 12;
- else
- return 6;
- }
- else if (reg_class_subset_p (to, ALL_COP_REGS))
- {
- return 5;
+ /* Two MOVEs. */
+ return 4;
}
}
- else if (from == FP_REGS)
+ else if (reg_class_subset_p (from, GENERAL_REGS))
{
if (reg_class_subset_p (to, GENERAL_REGS))
- return 4;
- else if (to == FP_REGS)
return 2;
- else if (to == ST_REGS)
- return 8;
+ if (reg_class_subset_p (to, FP_REGS))
+ return 4;
+ if (reg_class_subset_p (to, ALL_COP_AND_GR_REGS))
+ return 5;
+ if (reg_class_subset_p (to, ACC_REGS))
+ return 6;
}
- else if (reg_class_subset_p (from, ACC_REGS))
+ else if (reg_class_subset_p (to, GENERAL_REGS))
{
- if (reg_class_subset_p (to, GENERAL_REGS))
- {
- if (TARGET_MIPS16)
- return 12;
- else
- return 6;
- }
+ if (reg_class_subset_p (from, FP_REGS))
+ return 4;
+ if (reg_class_subset_p (from, ST_REGS))
+ /* LUI followed by MOVF. */
+ return 4;
+ if (reg_class_subset_p (from, ALL_COP_AND_GR_REGS))
+ return 5;
+ if (reg_class_subset_p (from, ACC_REGS))
+ return 6;
}
- else if (from == ST_REGS && reg_class_subset_p (to, GENERAL_REGS))
- return 4;
- else if (reg_class_subset_p (from, ALL_COP_REGS))
+ else if (reg_class_subset_p (from, FP_REGS))
{
- return 5;
+ if (reg_class_subset_p (to, FP_REGS)
+ && mips_mode_ok_for_mov_fmt_p (mode))
+ return 4;
+ if (reg_class_subset_p (to, ST_REGS))
+ /* An expensive sequence. */
+ return 8;
}
- /* Fall through.
- ??? What cases are these? Shouldn't we return 2 here? */
-
return 12;
}
static const struct mips_cpu_info *
mips_parse_cpu (const char *cpu_string)
{
- const struct mips_cpu_info *p;
+ unsigned int i;
const char *s;
/* In the past, we allowed upper-case CPU names, but it doesn't
if (strcasecmp (cpu_string, "default") == 0)
return 0;
- for (p = mips_cpu_info_table; p->name != 0; p++)
- if (mips_matching_cpu_name_p (p->name, cpu_string))
- return p;
+ for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+ if (mips_matching_cpu_name_p (mips_cpu_info_table[i].name, cpu_string))
+ return mips_cpu_info_table + i;
return 0;
}
static const struct mips_cpu_info *
mips_cpu_info_from_isa (int isa)
{
- const struct mips_cpu_info *p;
+ unsigned int i;
- for (p = mips_cpu_info_table; p->name != 0; p++)
- if (p->isa == isa)
- return p;
+ for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+ if (mips_cpu_info_table[i].isa == isa)
+ return mips_cpu_info_table + i;
return 0;
}
const1_rtx, const0_rtx);
}
\f
-/* Return true if we should force MIPS16 mode for the function named by
- the SYMBOL_REF SYMBOL, which belongs to DECL and has type TYPE.
- FIRST is true if this is the first time handling this decl. */
-
-static bool
-mips_use_mips16_mode_p (rtx symbol, tree decl, int first, tree type)
-{
- tree parent;
-
- /* Explicit function attributes take precedence. */
- if (mips_mips16_type_p (type))
- return true;
- if (mips_nomips16_type_p (type))
- return false;
-
- /* A nested function should inherit the MIPS16 setting from its parent. */
- parent = decl_function_context (decl);
- if (parent)
- return SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (parent), 0));
-
- /* Handle -mflip-mips16. */
- if (TARGET_FLIP_MIPS16
- && !DECL_BUILT_IN (decl)
- && !DECL_ARTIFICIAL (decl))
- {
- if (!first)
- /* Use the setting we picked first time around. */
- return SYMBOL_REF_MIPS16_FUNC_P (symbol);
-
- mips16_flipper = !mips16_flipper;
- if (mips16_flipper)
- return !mips_base_mips16;
- }
-
- return mips_base_mips16;
-}
-
/* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL.
FIRST is true if this is the first time handling this decl. */
if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
|| mips_far_type_p (type))
SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
-
- if (mips_use_mips16_mode_p (symbol, decl, first, type))
- {
- if (flag_pic || TARGET_ABICALLS)
- sorry ("MIPS16 PIC");
- else
- SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_MIPS16_FUNC;
- }
}
}