#include "sched-int.h"
#include "tree-gimple.h"
#include "bitmap.h"
+#include "diagnostic.h"
/* True if X is an unspec wrapper around a SYMBOL_REF or LABEL_REF. */
#define UNSPEC_ADDRESS_P(X) \
static bool mips_classify_address (struct mips_address_info *, rtx,
enum machine_mode, int);
static bool mips_cannot_force_const_mem (rtx);
-static bool mips_use_blocks_for_constant_p (enum machine_mode, rtx);
+static bool mips_use_blocks_for_constant_p (enum machine_mode, const_rtx);
static int mips_symbol_insns (enum mips_symbol_type, enum machine_mode);
static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
static rtx mips_force_temporary (rtx, rtx);
static void mips_restore_reg (rtx, rtx);
static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
HOST_WIDE_INT, tree);
-static int symbolic_expression_p (rtx);
static section *mips_select_rtx_section (enum machine_mode, rtx,
unsigned HOST_WIDE_INT);
static section *mips_function_rodata_section (tree);
-static bool mips_in_small_data_p (tree);
-static bool mips_use_anchors_for_symbol_p (rtx);
-static int mips_fpr_return_fields (tree, tree *);
-static bool mips_return_in_msb (tree);
+static bool mips_in_small_data_p (const_tree);
+static bool mips_use_anchors_for_symbol_p (const_rtx);
+static int mips_fpr_return_fields (const_tree, tree *);
+static bool mips_return_in_msb (const_tree);
static rtx mips_return_fpr_pair (enum machine_mode mode,
enum machine_mode mode1, HOST_WIDE_INT,
enum machine_mode mode2, HOST_WIDE_INT);
static bool mips_matching_cpu_name_p (const char *, const char *);
static const struct mips_cpu_info *mips_parse_cpu (const char *);
static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
-static bool mips_return_in_memory (tree, tree);
+static bool mips_return_in_memory (const_tree, const_tree);
static bool mips_strict_argument_naming (CUMULATIVE_ARGS *);
static void mips_macc_chains_record (rtx);
static void mips_macc_chains_reorder (rtx *, int);
static tree mips_build_builtin_va_list (void);
static tree mips_gimplify_va_arg_expr (tree, tree, tree *, tree *);
static bool mips_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode,
- tree, bool);
+ const_tree, bool);
static bool mips_callee_copies (CUMULATIVE_ARGS *, enum machine_mode mode,
- tree, bool);
+ const_tree, bool);
static int mips_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode mode,
tree, bool);
static bool mips_valid_pointer_mode (enum machine_mode);
static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx);
static void mips_encode_section_info (tree, rtx, int);
static void mips_extra_live_on_entry (bitmap);
-static int mips_comp_type_attributes (tree, tree);
+static int mips_comp_type_attributes (const_tree, const_tree);
+static void mips_set_mips16_mode (int);
+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);
static void mips_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
/* Cost information to use. */
const struct mips_rtx_cost_data *mips_cost;
+/* Remember the ambient target flags, excluding mips16. */
+static int mips_base_target_flags;
+/* The mips16 command-line target flags only. */
+static bool mips_base_mips16;
+/* Similar copies of option settings. */
+static int mips_base_schedule_insns; /* flag_schedule_insns */
+static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
+static int mips_base_align_loops; /* align_loops */
+static int mips_base_align_jumps; /* align_jumps */
+static int mips_base_align_functions; /* align_functions */
+static GTY(()) int mips16_flipper;
+
/* The -mtext-loads setting. */
enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
{ "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 },
{ NULL, 0, 0, false, false, false, NULL }
};
\f
#undef TARGET_FUNCTION_OK_FOR_SIBCALL
#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
+
#undef TARGET_VALID_POINTER_MODE
#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
#undef TARGET_RTX_COSTS
#define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
#undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_tree_true
#undef TARGET_RETURN_IN_MEMORY
#define TARGET_RETURN_IN_MEMORY mips_return_in_memory
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK mips_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
-#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
#undef TARGET_SETUP_INCOMING_VARARGS
#define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
attributes on the given TYPE. */
static bool
-mips_near_type_p (tree type)
+mips_near_type_p (const_tree type)
{
return lookup_attribute ("near", TYPE_ATTRIBUTES (type)) != NULL;
}
static bool
-mips_far_type_p (tree type)
+mips_far_type_p (const_tree type)
{
return (lookup_attribute ("long_call", TYPE_ATTRIBUTES (type)) != NULL
|| lookup_attribute ("far", TYPE_ATTRIBUTES (type)) != NULL);
}
+/* Similar predicates for "mips16"/"nomips16" attributes. */
+
+static bool
+mips_mips16_type_p (const_tree type)
+{
+ return lookup_attribute ("mips16", TYPE_ATTRIBUTES (type)) != NULL;
+}
+
+static bool
+mips_nomips16_type_p (const_tree type)
+{
+ return lookup_attribute ("nomips16", TYPE_ATTRIBUTES (type)) != NULL;
+}
/* Return 0 if the attributes for two types are incompatible, 1 if they
are compatible, and 2 if they are nearly compatible (which causes a
warning to be generated). */
static int
-mips_comp_type_attributes (tree type1, tree type2)
+mips_comp_type_attributes (const_tree type1, const_tree type2)
{
/* Check for mismatch of non-default calling convention. */
if (TREE_CODE (type1) != FUNCTION_TYPE)
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
(in the STB_GLOBAL sense). */
static bool
-mips_global_symbol_p (rtx x)
+mips_global_symbol_p (const_rtx x)
{
- tree decl;
+ const_tree const decl = SYMBOL_REF_DECL (x);
- decl = SYMBOL_REF_DECL (x);
if (!decl)
return !SYMBOL_REF_LOCAL_P (x);
/* Return true if SYMBOL_REF X binds locally. */
static bool
-mips_symbol_binds_local_p (rtx x)
+mips_symbol_binds_local_p (const_rtx x)
{
return (SYMBOL_REF_DECL (x)
? targetm.binds_local_p (SYMBOL_REF_DECL (x))
: SYMBOL_REF_LOCAL_P (x));
}
+/* Return true if rtx constants of mode MODE should be put into a small
+ data section. */
+
+static bool
+mips_rtx_constant_in_small_data_p (enum machine_mode mode)
+{
+ return (!TARGET_EMBEDDED_DATA
+ && TARGET_LOCAL_SDATA
+ && GET_MODE_SIZE (mode) <= mips_section_threshold);
+}
+
/* Return the method that should be used to access SYMBOL_REF or
LABEL_REF X in context CONTEXT. */
static enum mips_symbol_type
-mips_classify_symbol (rtx x, enum mips_symbol_context context)
+mips_classify_symbol (const_rtx x, enum mips_symbol_context context)
{
if (TARGET_RTP_PIC)
return SYMBOL_GOT_DISP;
if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
return SYMBOL_PC_RELATIVE;
- if (!TARGET_EMBEDDED_DATA
- && GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
+ if (mips_rtx_constant_in_small_data_p (get_pool_mode (x)))
return SYMBOL_GP_RELATIVE;
}
/* Do not use small-data accesses for weak symbols; they may end up
being zero. */
- if (SYMBOL_REF_SMALL_P (x)
+ if (TARGET_GPOPT
+ && SYMBOL_REF_SMALL_P (x)
&& !SYMBOL_REF_WEAK (x))
return SYMBOL_GP_RELATIVE;
return SYMBOL_ABSOLUTE;
}
+/* Classify symbolic expression X, given that it appears in context
+ CONTEXT. */
+
+static enum mips_symbol_type
+mips_classify_symbolic_expression (rtx x, enum mips_symbol_context context)
+{
+ rtx offset;
+
+ split_const (x, &x, &offset);
+ if (UNSPEC_ADDRESS_P (x))
+ return UNSPEC_ADDRESS_TYPE (x);
+
+ return mips_classify_symbol (x, context);
+}
+
/* Return true if OFFSET is within the range [0, ALIGN), where ALIGN
is the alignment (in bytes) of SYMBOL_REF X. */
info->type = ADDRESS_LO_SUM;
info->reg = XEXP (x, 0);
info->offset = XEXP (x, 1);
+ /* We have to trust the creator of the LO_SUM to do something vaguely
+ sane. Target-independent code that creates a LO_SUM should also
+ create and verify the matching HIGH. Target-independent code that
+ adds an offset to a LO_SUM must prove that the offset will not
+ induce a carry. Failure to do either of these things would be
+ a bug, and we are not required to check for it here. The MIPS
+ backend itself should only create LO_SUMs for valid symbolic
+ constants, with the high part being either a HIGH or a copy
+ of _gp. */
+ info->symbol_type
+ = mips_classify_symbolic_expression (info->offset, SYMBOL_CONTEXT_MEM);
return (mips_valid_base_register_p (info->reg, mode, strict)
- && mips_symbolic_constant_p (info->offset, SYMBOL_CONTEXT_MEM,
- &info->symbol_type)
&& mips_symbol_insns (info->symbol_type, mode) > 0
&& mips_lo_relocs[info->symbol_type] != 0);
static bool
mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED,
- rtx x ATTRIBUTE_UNUSED)
+ const_rtx x ATTRIBUTE_UNUSED)
{
return !TARGET_MIPS16_PCREL_LOADS;
}
rtx dest, insn, v0, v1, tmp1, tmp2, eqv;
enum tls_model model;
+ if (TARGET_MIPS16)
+ {
+ sorry ("MIPS16 TLS");
+ return gen_reg_rtx (Pmode);
+ }
+
v0 = gen_rtx_REG (Pmode, GP_RETURN);
v1 = gen_rtx_REG (Pmode, GP_RETURN + 1);
return;
}
- /* If we have (const (plus symbol offset)), load the symbol first
- and then add in the offset. This is usually better than forcing
- the constant into memory, at least in non-mips16 code. */
+ /* If we have (const (plus symbol offset)), and that expression cannot
+ be forced into memory, load the symbol first and add in the offset.
+ In non-MIPS16 mode, prefer to do this even if the constant _can_ be
+ forced into memory, as it usually produces better code. */
split_const (src, &base, &offset);
- if (!TARGET_MIPS16
- && offset != const0_rtx
- && (can_create_pseudo_p () || SMALL_INT (offset)))
+ if (offset != const0_rtx
+ && (targetm.cannot_force_const_mem (src)
+ || (!TARGET_MIPS16 && can_create_pseudo_p ())))
{
base = mips_force_temporary (dest, base);
mips_emit_move (dest, mips_add_offset (0, base, INTVAL (offset)));
}
-/* We can handle any sibcall when TARGET_SIBCALLS is true. */
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
static bool
-mips_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
- tree exp ATTRIBUTE_UNUSED)
+mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
{
- return TARGET_SIBCALLS;
+ if (!TARGET_SIBCALLS)
+ return false;
+
+ /* 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)))
+ return false;
+
+ /* Otherwise OK. */
+ return true;
}
\f
/* Emit code to move general operand SRC into condition-code
byte does. */
bool
-mips_pad_arg_upward (enum machine_mode mode, tree type)
+mips_pad_arg_upward (enum machine_mode mode, const_tree type)
{
/* On little-endian targets, the first byte of every stack argument
is passed in the first byte of the stack slot. */
}
}
+/* Initialize mips_split_addresses from the associated command-line
+ settings.
+
+ mips_split_addresses is a half-way house between explicit
+ relocations and the traditional assembler macros. It can
+ split absolute 32-bit symbolic constants into a high/lo_sum
+ pair but uses macros for other sorts of access.
+
+ Like explicit relocation support for REL targets, it relies
+ on GNU extensions in the assembler and the linker.
+
+ Although this code should work for -O0, it has traditionally
+ been treated as an optimization. */
+
+static void
+mips_init_split_addresses (void)
+{
+ if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
+ && optimize && !flag_pic
+ && !ABI_HAS_64BIT_SYMBOLS)
+ mips_split_addresses = 1;
+ else
+ mips_split_addresses = 0;
+}
+
+/* (Re-)Initialize information about relocs. */
+
+static void
+mips_init_relocs (void)
+{
+ memset (mips_split_p, '\0', sizeof (mips_split_p));
+ memset (mips_hi_relocs, '\0', sizeof (mips_hi_relocs));
+ memset (mips_lo_relocs, '\0', sizeof (mips_lo_relocs));
+
+ if (ABI_HAS_64BIT_SYMBOLS)
+ {
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ mips_split_p[SYMBOL_64_HIGH] = true;
+ mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
+ mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
+
+ mips_split_p[SYMBOL_64_MID] = true;
+ mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
+ mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
+
+ mips_split_p[SYMBOL_64_LOW] = true;
+ mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
+ mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
+
+ mips_split_p[SYMBOL_ABSOLUTE] = true;
+ mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+ }
+ }
+ else
+ {
+ if (TARGET_EXPLICIT_RELOCS || mips_split_addresses || TARGET_MIPS16)
+ {
+ mips_split_p[SYMBOL_ABSOLUTE] = true;
+ mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
+ mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
+
+ mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
+ }
+ }
+
+ if (TARGET_MIPS16)
+ {
+ /* The high part is provided by a pseudo copy of $gp. */
+ mips_split_p[SYMBOL_GP_RELATIVE] = true;
+ mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
+ }
+
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ /* Small data constants are kept whole until after reload,
+ then lowered by mips_rewrite_small_data. */
+ mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
+
+ mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
+ if (TARGET_NEWABI)
+ {
+ mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
+ mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
+ }
+ else
+ {
+ mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
+ mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
+ }
+
+ if (TARGET_XGOT)
+ {
+ /* The HIGH and LO_SUM are matched by special .md patterns. */
+ mips_split_p[SYMBOL_GOT_DISP] = true;
+
+ mips_split_p[SYMBOL_GOTOFF_DISP] = true;
+ mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
+ mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
+
+ mips_split_p[SYMBOL_GOTOFF_CALL] = true;
+ mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
+ mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
+ }
+ else
+ {
+ if (TARGET_NEWABI)
+ mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
+ else
+ mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
+ mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
+ }
+ }
+
+ if (TARGET_NEWABI)
+ {
+ mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
+ mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
+ mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
+ }
+
+ /* Thread-local relocation operators. */
+ mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
+ mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
+ mips_split_p[SYMBOL_DTPREL] = 1;
+ mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
+ mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
+ mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
+ mips_split_p[SYMBOL_TPREL] = 1;
+ mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
+ mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
+
+ mips_lo_relocs[SYMBOL_HALF] = "%half(";
+}
+
+static GTY(()) int was_mips16_p = -1;
+
+/* Set up the target-dependent global state so that it matches the
+ current function's ISA mode. */
+
+static void
+mips_set_mips16_mode (int mips16_p)
+{
+ if (mips16_p == was_mips16_p)
+ return;
+
+ /* Restore base settings of various flags. */
+ target_flags = mips_base_target_flags;
+ align_loops = mips_base_align_loops;
+ align_jumps = mips_base_align_jumps;
+ align_functions = mips_base_align_functions;
+ flag_schedule_insns = mips_base_schedule_insns;
+ flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
+ flag_delayed_branch = mips_flag_delayed_branch;
+
+ if (mips16_p)
+ {
+ /* Select mips16 instruction set. */
+ target_flags |= MASK_MIPS16;
+
+ /* Don't run the scheduler before reload, since it tends to
+ increase register pressure. */
+ flag_schedule_insns = 0;
+
+ /* Don't do hot/cold partitioning. The constant layout code expects
+ the whole function to be in a single section. */
+ flag_reorder_blocks_and_partition = 0;
+
+ /* Silently disable -mexplicit-relocs since it doesn't apply
+ to mips16 code. Even so, it would overly pedantic to warn
+ about "-mips16 -mexplicit-relocs", especially given that
+ we use a %gprel() operator. */
+ target_flags &= ~MASK_EXPLICIT_RELOCS;
+
+ /* Silently disable DSP extensions. */
+ target_flags &= ~MASK_DSP;
+ target_flags &= ~MASK_DSPR2;
+ }
+ else
+ {
+ /* Reset to select base non-mips16 ISA. */
+ target_flags &= ~MASK_MIPS16;
+
+ /* When using explicit relocs, we call dbr_schedule from within
+ mips_reorg. */
+ if (TARGET_EXPLICIT_RELOCS)
+ flag_delayed_branch = 0;
+
+ /* Provide default values for align_* for 64-bit targets. */
+ if (TARGET_64BIT)
+ {
+ if (align_loops == 0)
+ align_loops = 8;
+ if (align_jumps == 0)
+ align_jumps = 8;
+ if (align_functions == 0)
+ align_functions = 8;
+ }
+ }
+
+ /* (Re)initialize mips target internals for new ISA. */
+ mips_init_split_addresses ();
+ mips_init_relocs ();
+
+ if (was_mips16_p >= 0)
+ /* Reinitialize target-dependent state. */
+ target_reinit ();
+
+ was_mips16_p = TARGET_MIPS16;
+}
+
+/* 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)
+{
+ int mips16p;
+ if (errorcount || sorrycount)
+ /* Avoid generating RTL when fndecl is possibly invalid. Best to fall
+ back on non-MIPS16 mode to avoid any strange secondary errors about
+ use of unsupported features in MIPS16 mode. */
+ mips16p = false;
+ else if (fndecl)
+ mips16p = SYMBOL_REF_MIPS16_FUNC_P (XEXP (DECL_RTL (fndecl), 0));
+ else
+ mips16p = mips_base_mips16;
+ mips_set_mips16_mode (mips16p);
+}
+
/* Implement TARGET_HANDLE_OPTION. */
static bool
target_flags &= ~MASK_ABICALLS;
}
- if (TARGET_ABICALLS)
+ /* MIPS16 cannot generate PIC yet. */
+ if (TARGET_MIPS16 && (flag_pic || TARGET_ABICALLS))
{
- /* We need to set flag_pic for executables as well as DSOs
- because we may reference symbols that are not defined in
- the final executable. (MIPS does not use things like
- copy relocs, for example.)
-
- Also, there is a body of code that uses __PIC__ to distinguish
- between -mabicalls and -mno-abicalls code. */
- flag_pic = 1;
- if (mips_section_threshold > 0)
- warning (0, "%<-G%> is incompatible with %<-mabicalls%>");
+ sorry ("MIPS16 PIC");
+ target_flags &= ~MASK_ABICALLS;
+ flag_pic = flag_pie = flag_shlib = 0;
}
- if (TARGET_VXWORKS_RTP && mips_section_threshold > 0)
- warning (0, "-G and -mrtp are incompatible");
-
- /* mips_split_addresses is a half-way house between explicit
- relocations and the traditional assembler macros. It can
- split absolute 32-bit symbolic constants into a high/lo_sum
- pair but uses macros for other sorts of access.
+ if (TARGET_ABICALLS)
+ /* We need to set flag_pic for executables as well as DSOs
+ because we may reference symbols that are not defined in
+ the final executable. (MIPS does not use things like
+ copy relocs, for example.)
- Like explicit relocation support for REL targets, it relies
- on GNU extensions in the assembler and the linker.
-
- Although this code should work for -O0, it has traditionally
- been treated as an optimization. */
- if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
- && optimize && !flag_pic
- && !ABI_HAS_64BIT_SYMBOLS)
- mips_split_addresses = 1;
- else
- mips_split_addresses = 0;
+ Also, there is a body of code that uses __PIC__ to distinguish
+ between -mabicalls and -mno-abicalls code. */
+ flag_pic = 1;
/* -mvr4130-align is a "speed over size" optimization: it usually produces
faster code, but at the expense of more nops. Enable it at -O3 and
if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
target_flags |= MASK_VR4130_ALIGN;
- if (TARGET_MIPS16)
- {
- /* Don't run the scheduler before reload, since it tends to
- increase register pressure. */
- flag_schedule_insns = 0;
+ /* Prefer a call to memcpy over inline code when optimizing for size,
+ though see MOVE_RATIO in mips.h. */
+ if (optimize_size && (target_flags_explicit & MASK_MEMCPY) == 0)
+ target_flags |= MASK_MEMCPY;
- /* Don't do hot/cold partitioning. The constant layout code expects
- the whole function to be in a single section. */
- flag_reorder_blocks_and_partition = 0;
+ /* If we have a nonzero small-data limit, check that the -mgpopt
+ setting is consistent with the other target flags. */
+ if (mips_section_threshold > 0)
+ {
+ if (!TARGET_GPOPT)
+ {
+ if (!TARGET_MIPS16 && !TARGET_EXPLICIT_RELOCS)
+ error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
- /* Silently disable -mexplicit-relocs since it doesn't apply
- to mips16 code. Even so, it would overly pedantic to warn
- about "-mips16 -mexplicit-relocs", especially given that
- we use a %gprel() operator. */
- target_flags &= ~MASK_EXPLICIT_RELOCS;
- }
+ TARGET_LOCAL_SDATA = false;
+ TARGET_EXTERN_SDATA = false;
+ }
+ else
+ {
+ if (TARGET_VXWORKS_RTP)
+ warning (0, "cannot use small-data accesses for %qs", "-mrtp");
- /* When using explicit relocs, we call dbr_schedule from within
- mips_reorg. */
- if (TARGET_EXPLICIT_RELOCS)
- {
- mips_flag_delayed_branch = flag_delayed_branch;
- flag_delayed_branch = 0;
+ if (TARGET_ABICALLS)
+ warning (0, "cannot use small-data accesses for %qs",
+ "-mabicalls");
+ }
}
#ifdef MIPS_TFMODE_FORMAT
if (TARGET_DSPR2)
target_flags |= MASK_DSP;
- if (TARGET_MIPS16 && TARGET_DSP)
- error ("-mips16 and -mdsp cannot be used together");
-
mips_print_operand_punct['?'] = 1;
mips_print_operand_punct['#'] = 1;
mips_print_operand_punct['/'] = 1;
initialized yet, so we can't use that here. */
gpr_mode = TARGET_64BIT ? DImode : SImode;
- /* Provide default values for align_* for 64-bit targets. */
- if (TARGET_64BIT && !TARGET_MIPS16)
- {
- if (align_loops == 0)
- align_loops = 8;
- if (align_jumps == 0)
- align_jumps = 8;
- if (align_functions == 0)
- align_functions = 8;
- }
-
/* Function to allocate machine-dependent function status. */
init_machine_status = &mips_init_machine_status;
- if (ABI_HAS_64BIT_SYMBOLS)
- {
- if (TARGET_EXPLICIT_RELOCS)
- {
- mips_split_p[SYMBOL_64_HIGH] = true;
- mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
- mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
-
- mips_split_p[SYMBOL_64_MID] = true;
- mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
- mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
-
- mips_split_p[SYMBOL_64_LOW] = true;
- mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
- mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
-
- mips_split_p[SYMBOL_ABSOLUTE] = true;
- mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
- }
- }
- else
- {
- if (TARGET_EXPLICIT_RELOCS || mips_split_addresses || TARGET_MIPS16)
- {
- mips_split_p[SYMBOL_ABSOLUTE] = true;
- mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
- mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
-
- mips_lo_relocs[SYMBOL_32_HIGH] = "%hi(";
- }
- }
-
- if (TARGET_MIPS16)
- {
- /* The high part is provided by a pseudo copy of $gp. */
- mips_split_p[SYMBOL_GP_RELATIVE] = true;
- mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gprel(";
- }
-
- if (TARGET_EXPLICIT_RELOCS)
- {
- /* Small data constants are kept whole until after reload,
- then lowered by mips_rewrite_small_data. */
- mips_lo_relocs[SYMBOL_GP_RELATIVE] = "%gp_rel(";
-
- mips_split_p[SYMBOL_GOT_PAGE_OFST] = true;
- if (TARGET_NEWABI)
- {
- mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
- mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%got_ofst(";
- }
- else
- {
- mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
- mips_lo_relocs[SYMBOL_GOT_PAGE_OFST] = "%lo(";
- }
-
- if (TARGET_XGOT)
- {
- /* The HIGH and LO_SUM are matched by special .md patterns. */
- mips_split_p[SYMBOL_GOT_DISP] = true;
-
- mips_split_p[SYMBOL_GOTOFF_DISP] = true;
- mips_hi_relocs[SYMBOL_GOTOFF_DISP] = "%got_hi(";
- mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_lo(";
-
- mips_split_p[SYMBOL_GOTOFF_CALL] = true;
- mips_hi_relocs[SYMBOL_GOTOFF_CALL] = "%call_hi(";
- mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call_lo(";
- }
- else
- {
- if (TARGET_NEWABI)
- mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got_disp(";
- else
- mips_lo_relocs[SYMBOL_GOTOFF_DISP] = "%got(";
- mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
- }
- }
-
- if (TARGET_NEWABI)
- {
- mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
- mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
- mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
- }
-
- /* Thread-local relocation operators. */
- mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
- mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
- mips_split_p[SYMBOL_DTPREL] = 1;
- mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
- mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
- mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
- mips_split_p[SYMBOL_TPREL] = 1;
- mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
- mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
-
- mips_lo_relocs[SYMBOL_HALF] = "%half(";
-
- /* We don't have a thread pointer access instruction on MIPS16, or
- appropriate TLS relocations. */
- if (TARGET_MIPS16)
- targetm.have_tls = false;
-
/* Default to working around R4000 errata only if the processor
was selected explicitly. */
if ((target_flags_explicit & MASK_FIX_R4000) == 0
if ((target_flags_explicit & MASK_FIX_R4400) == 0
&& mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
target_flags |= MASK_FIX_R4400;
+
+ /* Save base state of options. */
+ mips_base_mips16 = TARGET_MIPS16;
+ mips_base_target_flags = target_flags;
+ mips_base_schedule_insns = flag_schedule_insns;
+ mips_base_reorder_blocks_and_partition = flag_reorder_blocks_and_partition;
+ mips_base_align_loops = align_loops;
+ mips_base_align_jumps = align_jumps;
+ mips_base_align_functions = align_functions;
+ mips_flag_delayed_branch = flag_delayed_branch;
+
+ /* Now select the mips16 or 32-bit instruction set, as requested. */
+ mips_set_mips16_mode (mips_base_mips16);
}
/* Swap the register information for registers I and I + 1, which
enum mips_symbol_type symbol_type;
const char *p;
- if (!mips_symbolic_constant_p (op, context, &symbol_type)
- || relocs[symbol_type] == 0)
+ symbol_type = mips_classify_symbolic_expression (op, context);
+ if (relocs[symbol_type] == 0)
fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
fputs (relocs[symbol_type], file);
if (TARGET_ABICALLS)
fprintf (asm_out_file, "\t.abicalls\n");
- if (TARGET_MIPS16)
- fprintf (asm_out_file, "\t.set\tmips16\n");
-
if (flag_verbose_asm)
fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
ASM_COMMENT_START,
&& current_function_args_info.fp_code != 0)
build_mips16_function_stub (file);
+ /* Select the mips16 mode for this function. */
+ if (TARGET_MIPS16)
+ fprintf (file, "\t.set\tmips16\n");
+ else
+ fprintf (file, "\t.set\tnomips16\n");
+
if (!FUNCTION_NAME_ALREADY_DECLARED)
{
/* Get the function name the same way that toplev.c does before calling
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. If
/* 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))
+ if (TARGET_MIPS16 || TARGET_USE_GOT || SYMBOL_REF_LONG_CALL_P (fnaddr)
+ || SYMBOL_REF_MIPS16_FUNC_P (fnaddr))
{
/* This is messy. gas treats "la $25,foo" as part of a call
sequence and may allow a global "foo" to be lazily bound.
reload_completed = 0;
}
\f
-/* Returns nonzero if X contains a SYMBOL_REF. */
-
-static int
-symbolic_expression_p (rtx x)
-{
- if (GET_CODE (x) == SYMBOL_REF)
- return 1;
-
- if (GET_CODE (x) == CONST)
- return symbolic_expression_p (XEXP (x, 0));
-
- if (UNARY_P (x))
- return symbolic_expression_p (XEXP (x, 0));
-
- if (ARITHMETIC_P (x))
- return (symbolic_expression_p (XEXP (x, 0))
- || symbolic_expression_p (XEXP (x, 1)));
-
- return 0;
-}
-
-/* Choose the section to use for the constant rtx expression X that has
- mode MODE. */
+/* Implement TARGET_SELECT_RTX_SECTION. */
static section *
mips_select_rtx_section (enum machine_mode mode, rtx x,
unsigned HOST_WIDE_INT align)
{
- if (TARGET_EMBEDDED_DATA)
- {
- /* For embedded applications, always put constants in read-only data,
- in order to reduce RAM usage. */
- return mergeable_constant_section (mode, align, 0);
- }
- else
- {
- /* For hosted applications, always put constants in small data if
- possible, as this gives the best performance. */
- /* ??? Consider using mergeable small data sections. */
+ /* ??? Consider using mergeable small data sections. */
+ if (mips_rtx_constant_in_small_data_p (mode))
+ return get_named_section (NULL, ".sdata", 0);
- if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold
- && mips_section_threshold > 0)
- return get_named_section (NULL, ".sdata", 0);
- else if (flag_pic && symbolic_expression_p (x))
- return get_named_section (NULL, ".data.rel.ro", 3);
- else
- return mergeable_constant_section (mode, align, 0);
- }
+ return default_elf_select_rtx_section (mode, x, align);
}
/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses. */
static bool
-mips_in_small_data_p (tree decl)
+mips_in_small_data_p (const_tree decl)
{
HOST_WIDE_INT size;
/* If a symbol is defined externally, the assembler will use the
usual -G rules when deciding how to implement macros. */
- if (TARGET_EXPLICIT_RELOCS || !DECL_EXTERNAL (decl))
+ if (mips_lo_relocs[SYMBOL_GP_RELATIVE] || !DECL_EXTERNAL (decl))
return true;
}
else if (TARGET_EMBEDDED_DATA)
return false;
}
+ /* Enforce -mlocal-sdata. */
+ if (!TARGET_LOCAL_SDATA && !TREE_PUBLIC (decl))
+ return false;
+
+ /* Enforce -mextern-sdata. */
+ if (!TARGET_EXTERN_SDATA && DECL_P (decl))
+ {
+ if (DECL_EXTERNAL (decl))
+ return false;
+ if (DECL_COMMON (decl) && DECL_INITIAL (decl) == NULL)
+ return false;
+ }
+
size = int_size_in_bytes (TREE_TYPE (decl));
return (size > 0 && size <= mips_section_threshold);
}
where the PC acts as an anchor. */
static bool
-mips_use_anchors_for_symbol_p (rtx symbol)
+mips_use_anchors_for_symbol_p (const_rtx symbol)
{
switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
{
type. */
static int
-mips_fpr_return_fields (tree valtype, tree *fields)
+mips_fpr_return_fields (const_tree valtype, tree *fields)
{
tree field;
int i;
- the structure is not returned in floating-point registers. */
static bool
-mips_return_in_msb (tree valtype)
+mips_return_in_msb (const_tree valtype)
{
tree fields[2];
VALTYPE is null and MODE is the mode of the return value. */
rtx
-mips_function_value (tree valtype, tree func ATTRIBUTE_UNUSED,
+mips_function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED,
enum machine_mode mode)
{
if (valtype)
static bool
mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
- enum machine_mode mode, tree type,
+ enum machine_mode mode, const_tree type,
bool named ATTRIBUTE_UNUSED)
{
if (mips_abi == ABI_EABI)
static bool
mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED, bool named)
+ const_tree type ATTRIBUTE_UNUSED, bool named)
{
return mips_abi == ABI_EABI && named;
}
fputs ("\n", file);
}
- fprintf (file, "\t.set\tmips16\n");
-
switch_to_section (function_section (current_function_decl));
}
fputs ("\n", asm_out_file);
}
- fprintf (asm_out_file, "\t.set\tmips16\n");
-
/* Record this stub. */
l = (struct mips16_stub *) xmalloc (sizeof *l);
l->name = xstrdup (fnname);
course. */
static bool
-mips_return_in_memory (tree type, tree fndecl ATTRIBUTE_UNUSED)
+mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
{
if (TARGET_OLDABI)
return (TYPE_MODE (type) == BLKmode);
static bool
vr4130_swap_insns_p (rtx insn1, rtx insn2)
{
- dep_link_t dep;
+ sd_iterator_def sd_it;
+ dep_t dep;
/* Check for the following case:
If INSN1 is the last instruction blocking X, it would better to
choose (INSN1, X) over (INSN2, INSN1). */
- FOR_EACH_DEP_LINK (dep, INSN_FORW_DEPS (insn1))
- if (DEP_LINK_KIND (dep) == REG_DEP_ANTI
- && INSN_PRIORITY (DEP_LINK_CON (dep)) > INSN_PRIORITY (insn2)
- && recog_memoized (DEP_LINK_CON (dep)) >= 0
- && get_attr_vr4130_class (DEP_LINK_CON (dep)) == VR4130_CLASS_ALU)
+ FOR_EACH_DEP (insn1, SD_LIST_FORW, sd_it, dep)
+ if (DEP_TYPE (dep) == REG_DEP_ANTI
+ && INSN_PRIORITY (DEP_CON (dep)) > INSN_PRIORITY (insn2)
+ && recog_memoized (DEP_CON (dep)) >= 0
+ && get_attr_vr4130_class (DEP_CON (dep)) == VR4130_CLASS_ALU)
return false;
if (vr4130_last_insn != 0
fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
fcode = DECL_FUNCTION_CODE (fndecl);
+ if (TARGET_MIPS16)
+ {
+ error ("built-in function %qs not supported for MIPS16",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ return const0_rtx;
+ }
+
bdesc = NULL;
for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
{
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 (TREE_CODE (decl) == FUNCTION_DECL)
{
rtx symbol = XEXP (rtl, 0);
+ tree type = TREE_TYPE (decl);
- if ((TARGET_LONG_CALLS && !mips_near_type_p (TREE_TYPE (decl)))
- || mips_far_type_p (TREE_TYPE (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;
+ }
}
}