#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) \
struct mips_integer_op;
struct mips_sim;
-static enum mips_symbol_type mips_classify_symbol (rtx);
static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
-static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode);
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 int mips_symbol_insns (enum mips_symbol_type);
+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 rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type);
static void mips_set_tune (const struct mips_cpu_info *);
static bool mips_handle_option (size_t, const char *, int);
static struct machine_function *mips_init_machine_status (void);
-static void print_operand_reloc (FILE *, rtx, const char **);
+static void print_operand_reloc (FILE *, rtx, enum mips_symbol_context,
+ const char **);
static void mips_file_start (void);
-static bool mips_rewrite_small_data_p (rtx);
static int mips_small_data_pattern_1 (rtx *, void *);
static int mips_rewrite_small_data_1 (rtx *, void *);
static bool mips_function_has_gp_insn (void);
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 bool vr4130_swap_insns_p (rtx, rtx);
static void vr4130_reorder (rtx *, int);
static void mips_promote_ready (rtx *, int, int);
+static void mips_sched_init (FILE *, int, int);
static int mips_sched_reorder (FILE *, int, rtx *, int *, int);
static int mips_variable_issue (FILE *, int, rtx, int);
static int mips_adjust_cost (rtx, rtx, 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;
-/* Whether we are generating mips16 hard float code. In mips16 mode
- we always set TARGET_SOFT_FLOAT; this variable is nonzero if
- -msoft-float was not specified by the user, which means that we
- should arrange to call mips32 hard floating point code. */
-int mips16_hard_float;
+/* 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;
/* The architecture selected by -mipsN. */
static const struct mips_cpu_info *mips_isa_info;
{ "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
31, 30, 23, 22, 21, 20, 19, 18, 17, 16, 7, 6, 5, 4
};
\f
-/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT. */
+/* Nonzero if -march should decide the default value of
+ MASK_SOFT_FLOAT_ABI. */
#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0
#endif
#undef TARGET_ASM_FUNCTION_RODATA_SECTION
#define TARGET_ASM_FUNCTION_RODATA_SECTION mips_function_rodata_section
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT mips_sched_init
#undef TARGET_SCHED_REORDER
#define TARGET_SCHED_REORDER mips_sched_reorder
+#undef TARGET_SCHED_REORDER2
+#define TARGET_SCHED_REORDER2 mips_sched_reorder
#undef TARGET_SCHED_VARIABLE_ISSUE
#define TARGET_SCHED_VARIABLE_ISSUE mips_variable_issue
#undef TARGET_SCHED_ADJUST_COST
#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));
}
-/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */
+/* 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)
+mips_classify_symbol (const_rtx x, enum mips_symbol_context context)
{
if (TARGET_RTP_PIC)
return SYMBOL_GOT_DISP;
if (GET_CODE (x) == LABEL_REF)
{
- if (TARGET_MIPS16)
+ /* LABEL_REFs are used for jump tables as well as text labels.
+ Only return SYMBOL_PC_RELATIVE if we know the label is in
+ the text section. */
+ if (TARGET_MIPS16_SHORT_JUMP_TABLES)
return SYMBOL_PC_RELATIVE;
if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
return SYMBOL_GOT_PAGE_OFST;
if (CONSTANT_POOL_ADDRESS_P (x))
{
- if (TARGET_MIPS16)
+ if (TARGET_MIPS16_TEXT_LOADS)
return SYMBOL_PC_RELATIVE;
- if (!TARGET_EMBEDDED_DATA
- && GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
+ if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
+ return SYMBOL_PC_RELATIVE;
+
+ 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;
- if (TARGET_ABICALLS)
+ /* Don't use GOT accesses for locally-binding symbols when -mno-shared
+ is in effect. */
+ if (TARGET_ABICALLS
+ && !(TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x)))
{
- /* Don't use GOT accesses for locally-binding symbols; we can use
- %hi and %lo instead. */
- if (TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x))
- return SYMBOL_ABSOLUTE;
-
/* There are three cases to consider:
- o32 PIC (either with or without explicit relocs)
return SYMBOL_GOT_PAGE_OFST;
}
+ if (TARGET_MIPS16_PCREL_LOADS && context != SYMBOL_CONTEXT_CALL)
+ return SYMBOL_FORCE_TO_MEM;
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. */
return offset >= 0 && offset < align;
}
-/* Return true if X is a symbolic constant that can be calculated in
- the same way as a bare symbol. If it is, store the type of the
- symbol in *SYMBOL_TYPE. */
+/* Return true if X is a symbolic constant that can be used in context
+ CONTEXT. If it is, store the type of the symbol in *SYMBOL_TYPE. */
bool
-mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
+mips_symbolic_constant_p (rtx x, enum mips_symbol_context context,
+ enum mips_symbol_type *symbol_type)
{
rtx offset;
}
else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
{
- *symbol_type = mips_classify_symbol (x);
+ *symbol_type = mips_classify_symbol (x, context);
if (*symbol_type == SYMBOL_TLS)
return false;
}
switch (*symbol_type)
{
case SYMBOL_ABSOLUTE:
+ case SYMBOL_FORCE_TO_MEM:
+ case SYMBOL_32_HIGH:
case SYMBOL_64_HIGH:
case SYMBOL_64_MID:
case SYMBOL_64_LOW:
}
-/* Return true if symbols of type SYMBOL_TYPE can directly address a value
- with mode MODE. This is used for both symbolic and LO_SUM addresses. */
-
-static bool
-mips_symbolic_address_p (enum mips_symbol_type symbol_type,
- enum machine_mode mode)
-{
- switch (symbol_type)
- {
- case SYMBOL_ABSOLUTE:
- return !TARGET_MIPS16;
-
- case SYMBOL_GP_RELATIVE:
- return true;
-
- case SYMBOL_PC_RELATIVE:
- /* PC-relative addressing is only available for lw and ld. */
- return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
-
- case SYMBOL_GOT_PAGE_OFST:
- return true;
-
- case SYMBOL_GOT_DISP:
- /* The address will have to be loaded from the GOT first. */
- return false;
-
- case SYMBOL_GOTOFF_PAGE:
- case SYMBOL_GOTOFF_DISP:
- case SYMBOL_GOTOFF_CALL:
- case SYMBOL_GOTOFF_LOADGP:
- case SYMBOL_TLS:
- case SYMBOL_TLSGD:
- case SYMBOL_TLSLDM:
- case SYMBOL_DTPREL:
- case SYMBOL_GOTTPREL:
- case SYMBOL_TPREL:
- case SYMBOL_64_HIGH:
- case SYMBOL_64_MID:
- case SYMBOL_64_LOW:
- case SYMBOL_HALF:
- return true;
- }
- gcc_unreachable ();
-}
-
-
/* Return true if X is a valid address for machine mode MODE. If it is,
fill in INFO appropriately. STRICT is true if we should only accept
hard base registers. */
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, &info->symbol_type)
- && mips_symbolic_address_p (info->symbol_type, mode)
+ && mips_symbol_insns (info->symbol_type, mode) > 0
&& mips_lo_relocs[info->symbol_type] != 0);
case CONST_INT:
case LABEL_REF:
case SYMBOL_REF:
info->type = ADDRESS_SYMBOLIC;
- return (mips_symbolic_constant_p (x, &info->symbol_type)
- && mips_symbolic_address_p (info->symbol_type, mode)
+ return (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_MEM,
+ &info->symbol_type)
+ && mips_symbol_insns (info->symbol_type, mode) > 0
&& !mips_split_p[info->symbol_type]);
default:
return false;
}
-/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. MIPS16 uses per-function
- constant pools, but normal-mode code doesn't need to. */
+/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. We can't use blocks for
+ constants when we're using a per-function constant pool. */
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;
+ return !TARGET_MIPS16_PCREL_LOADS;
}
\f
-/* Return the number of instructions needed to load a symbol of the
- given type into a register. If valid in an address, the same number
- of instructions are needed for loads and stores. Treat extended
- mips16 instructions as two instructions. */
+/* Like mips_symbol_insns, but treat extended MIPS16 instructions as a
+ single instruction. We rely on the fact that, in the worst case,
+ all instructions involved in a MIPS16 address calculation are usually
+ extended ones. */
static int
-mips_symbol_insns (enum mips_symbol_type type)
+mips_symbol_insns_1 (enum mips_symbol_type type, enum machine_mode mode)
{
switch (type)
{
case SYMBOL_ABSOLUTE:
- /* In mips16 code, general symbols must be fetched from the
- constant pool. */
- if (TARGET_MIPS16)
- return 0;
-
/* When using 64-bit symbols, we need 5 preparatory instructions,
such as:
dsll $at,$at,16
The final address is then $at + %lo(symbol). With 32-bit
- symbols we just need a preparatory lui. */
- return (ABI_HAS_64BIT_SYMBOLS ? 6 : 2);
+ symbols we just need a preparatory lui for normal mode and
+ a preparatory "li; sll" for MIPS16. */
+ return ABI_HAS_64BIT_SYMBOLS ? 6 : TARGET_MIPS16 ? 3 : 2;
case SYMBOL_GP_RELATIVE:
- case SYMBOL_HALF:
+ /* Treat GP-relative accesses as taking a single instruction on
+ MIPS16 too; the copy of $gp can often be shared. */
return 1;
case SYMBOL_PC_RELATIVE:
- /* This case is for mips16 only. Assume we'll need an
- extended instruction. */
- return 2;
+ /* PC-relative constants can be only be used with addiupc,
+ lwpc and ldpc. */
+ if (mode == MAX_MACHINE_MODE
+ || GET_MODE_SIZE (mode) == 4
+ || GET_MODE_SIZE (mode) == 8)
+ return 1;
+
+ /* The constant must be loaded using addiupc first. */
+ return 0;
+
+ case SYMBOL_FORCE_TO_MEM:
+ /* The constant must be loaded from the constant pool. */
+ return 0;
- case SYMBOL_GOT_PAGE_OFST:
case SYMBOL_GOT_DISP:
+ /* The constant will have to be loaded from the GOT before it
+ is used in an address. */
+ if (mode != MAX_MACHINE_MODE)
+ return 0;
+
+ /* Fall through. */
+
+ case SYMBOL_GOT_PAGE_OFST:
/* Unless -funit-at-a-time is in effect, we can't be sure whether
the local/global classification is accurate. See override_options
for details.
case SYMBOL_GOTOFF_DISP:
case SYMBOL_GOTOFF_CALL:
case SYMBOL_GOTOFF_LOADGP:
+ case SYMBOL_32_HIGH:
case SYMBOL_64_HIGH:
case SYMBOL_64_MID:
case SYMBOL_64_LOW:
case SYMBOL_DTPREL:
case SYMBOL_GOTTPREL:
case SYMBOL_TPREL:
- /* Check whether the offset is a 16- or 32-bit value. */
- return mips_split_p[type] ? 2 : 1;
+ case SYMBOL_HALF:
+ /* A 16-bit constant formed by a single relocation, or a 32-bit
+ constant formed from a high 16-bit relocation and a low 16-bit
+ relocation. Use mips_split_p to determine which. */
+ return !mips_split_p[type] ? 1 : TARGET_MIPS16 ? 3 : 2;
case SYMBOL_TLS:
/* We don't treat a bare TLS symbol as a constant. */
gcc_unreachable ();
}
+/* If MODE is MAX_MACHINE_MODE, return the number of instructions needed
+ to load symbols of type TYPE into a register. Return 0 if the given
+ type of symbol cannot be used as an immediate operand.
+
+ Otherwise, return the number of instructions needed to load or store
+ values of mode MODE to or from addresses of type TYPE. Return 0 if
+ the given type of symbol is not valid in addresses.
+
+ In both cases, treat extended MIPS16 instructions as two instructions. */
+
+static int
+mips_symbol_insns (enum mips_symbol_type type, enum machine_mode mode)
+{
+ return mips_symbol_insns_1 (type, mode) * (TARGET_MIPS16 ? 2 : 1);
+}
+
/* Return true if X is a legitimate $sp-based address for mode MDOE. */
bool
/* Return the number of instructions needed to load or store a value
- of mode MODE at X. Return 0 if X isn't valid for MODE.
+ of mode MODE at X. Return 0 if X isn't valid for MODE. Assume that
+ multiword moves may need to be split into word moves if MIGHT_SPLIT_P,
+ otherwise assume that a single load or store is enough.
For mips16 code, count extended instructions as two instructions. */
int
-mips_address_insns (rtx x, enum machine_mode mode)
+mips_address_insns (rtx x, enum machine_mode mode, bool might_split_p)
{
struct mips_address_info addr;
int factor;
- if (mode == BLKmode)
- /* BLKmode is used for single unaligned loads and stores. */
- factor = 1;
- else
- /* Each word of a multi-word value will be accessed individually. */
+ /* BLKmode is used for single unaligned loads and stores and should
+ not count as a multiword mode. (GET_MODE_SIZE (BLKmode) is pretty
+ meaningless, so we have to single it out as a special case one way
+ or the other.) */
+ if (mode != BLKmode && might_split_p)
factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ else
+ factor = 1;
if (mips_classify_address (&addr, x, mode, false))
switch (addr.type)
return factor;
case ADDRESS_SYMBOLIC:
- return factor * mips_symbol_insns (addr.symbol_type);
+ return factor * mips_symbol_insns (addr.symbol_type, mode);
}
return 0;
}
switch (GET_CODE (x))
{
case HIGH:
- if (TARGET_MIPS16
- || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type)
+ if (!mips_symbolic_constant_p (XEXP (x, 0), SYMBOL_CONTEXT_LEA,
+ &symbol_type)
|| !mips_split_p[symbol_type])
return 0;
- return 1;
+ /* This is simply an lui for normal mode. It is an extended
+ "li" followed by an extended "sll" for MIPS16. */
+ return TARGET_MIPS16 ? 4 : 1;
case CONST_INT:
if (TARGET_MIPS16)
return 1;
/* See if we can refer to X directly. */
- if (mips_symbolic_constant_p (x, &symbol_type))
- return mips_symbol_insns (symbol_type);
+ if (mips_symbolic_constant_p (x, SYMBOL_CONTEXT_LEA, &symbol_type))
+ return mips_symbol_insns (symbol_type, MAX_MACHINE_MODE);
/* Otherwise try splitting the constant into a base and offset.
16-bit offsets can be added using an extra addiu. Larger offsets
case SYMBOL_REF:
case LABEL_REF:
- return mips_symbol_insns (mips_classify_symbol (x));
+ return mips_symbol_insns (mips_classify_symbol (x, SYMBOL_CONTEXT_LEA),
+ MAX_MACHINE_MODE);
default:
return 0;
}
-/* Return the number of instructions needed for memory reference X.
- Count extended mips16 instructions as two instructions. */
+/* Return the number of instructions needed to implement INSN,
+ given that it loads from or stores to MEM. Count extended
+ mips16 instructions as two instructions. */
int
-mips_fetch_insns (rtx x)
+mips_load_store_insns (rtx mem, rtx insn)
{
- gcc_assert (MEM_P (x));
- return mips_address_insns (XEXP (x, 0), GET_MODE (x));
+ enum machine_mode mode;
+ bool might_split_p;
+ rtx set;
+
+ gcc_assert (MEM_P (mem));
+ mode = GET_MODE (mem);
+
+ /* Try to prove that INSN does not need to be split. */
+ might_split_p = true;
+ if (GET_MODE_BITSIZE (mode) == 64)
+ {
+ set = single_set (insn);
+ if (set && !mips_split_64bit_move_p (SET_DEST (set), SET_SRC (set)))
+ might_split_p = false;
+ }
+
+ return mips_address_insns (XEXP (mem, 0), mode, might_split_p);
}
return mips_classify_address (&addr, x, mode, strict);
}
+/* Emit a move from SRC to DEST. Assume that the move expanders can
+ handle all moves if !can_create_pseudo_p (). The distinction is
+ important because, unlike emit_move_insn, the move expanders know
+ how to force Pmode objects into the constant pool even when the
+ constant pool address is not itself legitimate. */
+
+rtx
+mips_emit_move (rtx dest, rtx src)
+{
+ return (can_create_pseudo_p ()
+ ? emit_move_insn (dest, src)
+ : emit_move_insn_1 (dest, src));
+}
/* Copy VALUE to a register and return that register. If new psuedos
are allowed, copy it into a new register, otherwise use DEST. */
return force_reg (Pmode, value);
else
{
- emit_move_insn (copy_rtx (dest), value);
+ mips_emit_move (copy_rtx (dest), value);
return dest;
}
}
-/* Return a LO_SUM expression for ADDR. TEMP is as for mips_force_temporary
- and is used to load the high part into a register. */
+/* If MODE is MAX_MACHINE_MODE, ADDR appears as a move operand, otherwise
+ it appears in a MEM of that mode. Return true if ADDR is a legitimate
+ constant in that context and can be split into a high part and a LO_SUM.
+ If so, and if LO_SUM_OUT is nonnull, emit the high part and return
+ the LO_SUM in *LO_SUM_OUT. Leave *LO_SUM_OUT unchanged otherwise.
-rtx
-mips_split_symbol (rtx temp, rtx addr)
+ TEMP is as for mips_force_temporary and is used to load the high
+ part into a register. */
+
+bool
+mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *lo_sum_out)
{
+ enum mips_symbol_context context;
+ enum mips_symbol_type symbol_type;
rtx high;
- if (!TARGET_MIPS16)
- high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr)));
- else if (!can_create_pseudo_p ())
+ context = (mode == MAX_MACHINE_MODE
+ ? SYMBOL_CONTEXT_LEA
+ : SYMBOL_CONTEXT_MEM);
+ if (!mips_symbolic_constant_p (addr, context, &symbol_type)
+ || mips_symbol_insns (symbol_type, mode) == 0
+ || !mips_split_p[symbol_type])
+ return false;
+
+ if (lo_sum_out)
{
- emit_insn (gen_load_const_gp (copy_rtx (temp)));
- high = temp;
+ if (symbol_type == SYMBOL_GP_RELATIVE)
+ {
+ if (!can_create_pseudo_p ())
+ {
+ emit_insn (gen_load_const_gp (copy_rtx (temp)));
+ high = temp;
+ }
+ else
+ high = mips16_gp_pseudo_reg ();
+ }
+ else
+ {
+ high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+ high = mips_force_temporary (temp, high);
+ }
+ *lo_sum_out = gen_rtx_LO_SUM (Pmode, high, addr);
}
- else
- high = mips16_gp_pseudo_reg ();
- return gen_rtx_LO_SUM (Pmode, high, addr);
+ return true;
}
+/* Wrap symbol or label BASE in an unspec address of type SYMBOL_TYPE
+ and add CONST_INT OFFSET to the result. */
+
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+ enum mips_symbol_type symbol_type)
+{
+ base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
+ UNSPEC_ADDRESS_FIRST + symbol_type);
+ if (offset != const0_rtx)
+ base = gen_rtx_PLUS (Pmode, base, offset);
+ return gen_rtx_CONST (Pmode, base);
+}
+
/* Return an UNSPEC address with underlying address ADDRESS and symbol
type SYMBOL_TYPE. */
rtx base, offset;
split_const (address, &base, &offset);
- base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
- UNSPEC_ADDRESS_FIRST + symbol_type);
- if (offset != const0_rtx)
- base = gen_rtx_PLUS (Pmode, base, offset);
- return gen_rtx_CONST (Pmode, base);
+ return mips_unspec_address_offset (base, offset, symbol_type);
}
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);
bool
mips_legitimize_address (rtx *xloc, enum machine_mode mode)
{
- enum mips_symbol_type symbol_type;
-
if (mips_tls_operand_p (*xloc))
{
*xloc = mips_legitimize_tls_address (*xloc);
}
/* See if the address can split into a high part and a LO_SUM. */
- if (mips_symbolic_constant_p (*xloc, &symbol_type)
- && mips_symbolic_address_p (symbol_type, mode)
- && mips_split_p[symbol_type])
- {
- *xloc = mips_split_symbol (0, *xloc);
- return true;
- }
+ if (mips_split_symbol (NULL, *xloc, mode, xloc))
+ return true;
if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
{
}
/* Split moves of symbolic constants into high/low pairs. */
- if (splittable_symbolic_operand (src, mode))
+ if (mips_split_symbol (dest, src, MAX_MACHINE_MODE, &src))
{
- emit_insn (gen_rtx_SET (VOIDmode, dest, mips_split_symbol (dest, src)));
+ emit_insn (gen_rtx_SET (VOIDmode, dest, src));
return;
}
if (mips_tls_operand_p (src))
{
- emit_move_insn (dest, mips_legitimize_tls_address (src));
+ mips_emit_move (dest, mips_legitimize_tls_address (src));
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);
- emit_move_insn (dest, mips_add_offset (0, base, INTVAL (offset)));
+ mips_emit_move (dest, mips_add_offset (0, base, INTVAL (offset)));
return;
}
/* When using explicit relocs, constant pool references are sometimes
not legitimate addresses. */
- if (!memory_operand (src, VOIDmode))
- src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0)));
- emit_move_insn (dest, src);
+ mips_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0));
+ mips_emit_move (dest, src);
}
{
if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
{
- emit_move_insn (dest, force_reg (mode, src));
+ mips_emit_move (dest, force_reg (mode, src));
return true;
}
/* If the address is legitimate, return the number of
instructions it needs. */
rtx addr = XEXP (x, 0);
- int n = mips_address_insns (addr, GET_MODE (x));
+ int n = mips_address_insns (addr, GET_MODE (x), true);
if (n > 0)
{
*total = COSTS_N_INSNS (n + 1);
static int
mips_address_cost (rtx addr)
{
- return mips_address_insns (addr, SImode);
+ return mips_address_insns (addr, SImode, false);
}
\f
/* Return one word of double-word value OP, taking into account the fixed
if (ISA_HAS_MXHC1)
{
src = gen_lowpart (DFmode, src);
- emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0));
+ mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
emit_insn (gen_mfhc1 (mips_subword (dest, 1), src));
}
else
{
- emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0));
+ mips_emit_move (mips_subword (dest, 0), mips_subword (src, 0));
emit_insn (gen_store_df_high (mips_subword (dest, 1), src));
}
}
if (REG_P (low_dest)
&& reg_overlap_mentioned_p (low_dest, src))
{
- emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1));
- emit_move_insn (low_dest, mips_subword (src, 0));
+ mips_emit_move (mips_subword (dest, 1), mips_subword (src, 1));
+ mips_emit_move (low_dest, mips_subword (src, 0));
}
else
{
- emit_move_insn (low_dest, mips_subword (src, 0));
- emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1));
+ mips_emit_move (low_dest, mips_subword (src, 0));
+ mips_emit_move (mips_subword (dest, 1), mips_subword (src, 1));
}
}
}
mips_output_move (rtx dest, rtx src)
{
enum rtx_code dest_code, src_code;
+ enum mips_symbol_type symbol_type;
bool dbl_p;
dest_code = GET_CODE (dest);
}
if (src_code == HIGH)
- return "lui\t%0,%h1";
+ return TARGET_MIPS16 ? "#" : "lui\t%0,%h1";
if (CONST_GP_P (src))
return "move\t%0,%1";
+ if (mips_symbolic_constant_p (src, SYMBOL_CONTEXT_LEA, &symbol_type)
+ && mips_lo_relocs[symbol_type] != 0)
+ {
+ /* A signed 16-bit constant formed by applying a relocation
+ operator to a symbolic address. */
+ gcc_assert (!mips_split_p[symbol_type]);
+ return "li\t%0,%R1";
+ }
+
if (symbolic_operand (src, VOIDmode))
- return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1");
+ {
+ gcc_assert (TARGET_MIPS16
+ ? TARGET_MIPS16_TEXT_LOADS
+ : !TARGET_EXPLICIT_RELOCS);
+ return (dbl_p ? "dla\t%0,%1" : "la\t%0,%1");
+ }
}
if (src_code == REG && FP_REG_P (REGNO (src)))
{
current_function_outgoing_args_size);
slot = gen_rtx_MEM (Pmode, address);
- emit_move_insn (pic_offset_table_rtx, slot);
+ mips_emit_move (pic_offset_table_rtx, slot);
if (!TARGET_EXPLICIT_RELOCS)
emit_insn (gen_blockage ());
}
emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
}
else
- emit_move_insn (dest, addr);
+ mips_emit_move (dest, addr);
}
mips_load_call_address (addr, orig_addr, sibcall_p);
}
- if (mips16_hard_float
+ if (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
&& build_mips16_call_stub (result, addr, args_size,
aux == 0 ? 0 : (int) GET_MODE (aux)))
return;
}
-/* 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
fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
- emit_move_insn (copy_rtx (fp1), src);
- emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
+ mips_emit_move (copy_rtx (fp1), src);
+ mips_emit_move (copy_rtx (fp2), CONST0_RTX (SFmode));
emit_insn (gen_slt_sf (dest, fp2, fp1));
}
\f
slot_address = mips_add_offset (scratch, stack_pointer_rtx,
cfun->machine->frame.gp_sp_offset);
- emit_move_insn (gen_rtx_MEM (GET_MODE (address), slot_address), address);
+ mips_emit_move (gen_rtx_MEM (GET_MODE (address), slot_address), address);
}
\f
/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
{
regs[i] = gen_reg_rtx (mode);
if (MEM_ALIGN (src) >= bits)
- emit_move_insn (regs[i], adjust_address (src, mode, offset));
+ mips_emit_move (regs[i], adjust_address (src, mode, offset));
else
{
rtx part = adjust_address (src, BLKmode, offset);
/* Copy the chunks to the destination. */
for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
if (MEM_ALIGN (dest) >= bits)
- emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
+ mips_emit_move (adjust_address (dest, mode, offset), regs[i]);
else
{
rtx part = adjust_address (dest, BLKmode, offset);
mips_block_move_straight (dest, src, MAX_MOVE_BYTES);
/* Move on to the next block. */
- emit_move_insn (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES));
- emit_move_insn (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES));
+ mips_emit_move (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES));
+ mips_emit_move (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES));
/* Emit the loop condition. */
if (Pmode == DImode)
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. */
ptr = plus_constant (virtual_incoming_args_rtx, off);
mem = gen_rtx_MEM (mode, ptr);
set_mem_alias_set (mem, get_varargs_alias_set ());
- emit_move_insn (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
+ mips_emit_move (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
off += UNITS_PER_HWFPVALUE;
}
}
}
}
+/* 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
mips_cache_flush_func = NULL;
return true;
+ case OPT_mcode_readable_:
+ if (strcmp (arg, "yes") == 0)
+ mips_code_readable = CODE_READABLE_YES;
+ else if (strcmp (arg, "pcrel") == 0)
+ mips_code_readable = CODE_READABLE_PCREL;
+ else if (strcmp (arg, "no") == 0)
+ mips_code_readable = CODE_READABLE_NO;
+ else
+ return false;
+ return true;
+
default:
return true;
}
}
if (MIPS_MARCH_CONTROLS_SOFT_FLOAT
- && (target_flags_explicit & MASK_SOFT_FLOAT) == 0)
+ && (target_flags_explicit & MASK_SOFT_FLOAT_ABI) == 0)
{
/* For some configurations, it is useful to have -march control
- the default setting of MASK_SOFT_FLOAT. */
+ the default setting of MASK_SOFT_FLOAT_ABI. */
switch ((int) mips_arch)
{
case PROCESSOR_R4100:
case PROCESSOR_R4111:
case PROCESSOR_R4120:
case PROCESSOR_R4130:
- target_flags |= MASK_SOFT_FLOAT;
+ target_flags |= MASK_SOFT_FLOAT_ABI;
break;
default:
- target_flags &= ~MASK_SOFT_FLOAT;
+ target_flags &= ~MASK_SOFT_FLOAT_ABI;
break;
}
}
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.
-
- Like explicit relocation support for REL targets, it relies
- on GNU extensions in the assembler and the linker.
+ 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.)
- 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;
- /* When compiling for the mips16, we cannot use floating point. We
- record the original hard float value in mips16_hard_float. */
- if (TARGET_MIPS16)
- {
- if (TARGET_SOFT_FLOAT)
- mips16_hard_float = 0;
- else
- mips16_hard_float = 1;
- target_flags |= MASK_SOFT_FLOAT;
-
- /* 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;
- }
+ /* 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;
- /* When using explicit relocs, we call dbr_schedule from within
- mips_reorg. */
- if (TARGET_EXPLICIT_RELOCS)
+ /* 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)
{
- mips_flag_delayed_branch = flag_delayed_branch;
- flag_delayed_branch = 0;
+ if (!TARGET_GPOPT)
+ {
+ if (!TARGET_MIPS16 && !TARGET_EXPLICIT_RELOCS)
+ error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
+
+ TARGET_LOCAL_SDATA = false;
+ TARGET_EXTERN_SDATA = false;
+ }
+ else
+ {
+ if (TARGET_VXWORKS_RTP)
+ warning (0, "cannot use small-data accesses for %qs", "-mrtp");
+
+ 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)
- {
- mips_split_p[SYMBOL_ABSOLUTE] = true;
- mips_hi_relocs[SYMBOL_ABSOLUTE] = "%hi(";
- mips_lo_relocs[SYMBOL_ABSOLUTE] = "%lo(";
- }
- }
-
- 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
return offset;
}
\f
+/* If OP is an UNSPEC address, return the address to which it refers,
+ otherwise return OP itself. */
+
+static rtx
+mips_strip_unspec_address (rtx op)
+{
+ rtx base, offset;
+
+ split_const (op, &base, &offset);
+ if (UNSPEC_ADDRESS_P (base))
+ op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
+ return op;
+}
+
/* Implement the PRINT_OPERAND macro. The MIPS-specific operand codes are:
'X' OP is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
if (GET_CODE (op) == HIGH)
op = XEXP (op, 0);
- print_operand_reloc (file, op, mips_hi_relocs);
+ print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_hi_relocs);
}
else if (letter == 'R')
- print_operand_reloc (file, op, mips_lo_relocs);
+ print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_lo_relocs);
else if (letter == 'Y')
{
fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
else
- output_addr_const (file, op);
+ output_addr_const (file, mips_strip_unspec_address (op));
}
-/* Print symbolic operand OP, which is part of a HIGH or LO_SUM.
- RELOCS is the array of relocations to use. */
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+ in context CONTEXT. RELOCS is the array of relocations to use. */
static void
-print_operand_reloc (FILE *file, rtx op, const char **relocs)
+print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
+ const char **relocs)
{
enum mips_symbol_type symbol_type;
const char *p;
- rtx base, offset;
- if (!mips_symbolic_constant_p (op, &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);
- /* If OP uses an UNSPEC address, we want to print the inner symbol. */
- split_const (op, &base, &offset);
- if (UNSPEC_ADDRESS_P (base))
- op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
-
fputs (relocs[symbol_type], file);
- output_addr_const (file, op);
+ output_addr_const (file, mips_strip_unspec_address (op));
for (p = relocs[symbol_type]; *p != 0; p++)
if (*p == '(')
fputc (')', file);
return;
case ADDRESS_LO_SUM:
- print_operand (file, addr.offset, 'R');
+ print_operand_reloc (file, addr.offset, SYMBOL_CONTEXT_MEM,
+ mips_lo_relocs);
fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
return;
return;
case ADDRESS_SYMBOLIC:
- output_addr_const (file, x);
+ output_addr_const (file, mips_strip_unspec_address (x));
return;
}
gcc_unreachable ();
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,
}
#endif
\f
-/* Return true if X is a small data address that can be rewritten
- as a LO_SUM. */
+/* Return true if X in context CONTEXT is a small data address that can
+ be rewritten as a LO_SUM. */
static bool
-mips_rewrite_small_data_p (rtx x)
+mips_rewrite_small_data_p (rtx x, enum mips_symbol_context context)
{
enum mips_symbol_type symbol_type;
return (TARGET_EXPLICIT_RELOCS
- && mips_symbolic_constant_p (x, &symbol_type)
+ && mips_symbolic_constant_p (x, context, &symbol_type)
&& symbol_type == SYMBOL_GP_RELATIVE);
}
-/* A for_each_rtx callback for mips_small_data_pattern_p. */
+/* A for_each_rtx callback for mips_small_data_pattern_p. DATA is the
+ containing MEM, or null if none. */
static int
-mips_small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+mips_small_data_pattern_1 (rtx *loc, void *data)
{
+ enum mips_symbol_context context;
+
if (GET_CODE (*loc) == LO_SUM)
return -1;
- return mips_rewrite_small_data_p (*loc);
+ if (MEM_P (*loc))
+ {
+ if (for_each_rtx (&XEXP (*loc, 0), mips_small_data_pattern_1, *loc))
+ return 1;
+ return -1;
+ }
+
+ context = data ? SYMBOL_CONTEXT_MEM : SYMBOL_CONTEXT_LEA;
+ return mips_rewrite_small_data_p (*loc, context);
}
/* Return true if OP refers to small data symbols directly, not through
return for_each_rtx (&op, mips_small_data_pattern_1, 0);
}
\f
-/* A for_each_rtx callback, used by mips_rewrite_small_data. */
+/* A for_each_rtx callback, used by mips_rewrite_small_data.
+ DATA is the containing MEM, or null if none. */
static int
-mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+mips_rewrite_small_data_1 (rtx *loc, void *data)
{
- if (mips_rewrite_small_data_p (*loc))
+ enum mips_symbol_context context;
+
+ if (MEM_P (*loc))
+ {
+ for_each_rtx (&XEXP (*loc, 0), mips_rewrite_small_data_1, *loc);
+ return -1;
+ }
+
+ context = data ? SYMBOL_CONTEXT_MEM : SYMBOL_CONTEXT_LEA;
+ if (mips_rewrite_small_data_p (*loc, context))
*loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc);
if (GET_CODE (*loc) == LO_SUM)
mips16_cfun_returns_in_fpr_p (void)
{
tree return_type = DECL_RESULT (current_function_decl);
- return (mips16_hard_float
+ return (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
&& !aggregate_value_p (return_type, current_function_decl)
&& mips_return_mode_in_fpr_p (DECL_MODE (return_type)));
}
floating point arguments. The linker will arrange for any 32-bit
functions to call this stub, which will then jump to the 16-bit
function proper. */
- if (mips16_hard_float
+ if (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
&& 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
if (mips_split_64bit_move_p (mem, reg))
mips_split_64bit_move (mem, reg);
else
- emit_move_insn (mem, reg);
+ mips_emit_move (mem, reg);
x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0));
x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1));
/* Save a non-mips16 register by moving it through a temporary.
We don't need to do this for $31 since there's a special
instruction for it. */
- emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
- emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
+ mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
+ mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
}
else
- emit_move_insn (mem, reg);
+ mips_emit_move (mem, reg);
mips_set_frame_expr (mips_frame_set (mem, reg));
}
GEN_INT (-size)))) = 1;
else
{
- emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
if (TARGET_MIPS16)
{
/* There are no instructions to add or subtract registers
temporary. We should always be using a frame pointer
in this case anyway. */
gcc_assert (frame_pointer_needed);
- emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+ mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
MIPS_PROLOGUE_TEMP (Pmode)));
- emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
+ mips_emit_move (stack_pointer_rtx, hard_frame_pointer_rtx);
}
else
emit_insn (gen_sub3_insn (stack_pointer_rtx,
offset))) = 1;
else
{
- emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), offset);
- emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), offset);
+ mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
hard_frame_pointer_rtx,
MIPS_PROLOGUE_TEMP (Pmode)));
}
}
else
- RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx,
+ RTX_FRAME_RELATED_P (mips_emit_move (hard_frame_pointer_rtx,
stack_pointer_rtx)) = 1;
}
if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
{
/* Can't restore directly; move through a temporary. */
- emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
- emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
+ mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
+ mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
}
else
- emit_move_insn (reg, mem);
+ mips_emit_move (reg, mem);
}
adjust = GEN_INT (step1);
if (!SMALL_OPERAND (step1))
{
- emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+ mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), adjust);
adjust = MIPS_EPILOGUE_TEMP (Pmode);
}
/* Copy TARGET into the stack pointer. */
if (target != stack_pointer_rtx)
- emit_move_insn (stack_pointer_rtx, target);
+ mips_emit_move (stack_pointer_rtx, target);
/* If we're using addressing macros, $gp is implicitly used by all
SYMBOL_REFs. We must emit a blockage insn before restoring $gp
{
if (TARGET_MIPS16)
{
- emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
+ mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
MIPS_EPILOGUE_TEMP (Pmode),
EH_RETURN_STACKADJ_RTX));
- emit_move_insn (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
+ mips_emit_move (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
}
else
emit_insn (gen_add3_insn (stack_pointer_rtx,
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
rtx offset = GEN_INT (delta);
if (!SMALL_OPERAND (delta))
{
- emit_move_insn (temp1, offset);
+ mips_emit_move (temp1, offset);
offset = temp1;
}
emit_insn (gen_add3_insn (this, this, offset));
rtx addr;
/* Set TEMP1 to *THIS. */
- emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, this));
/* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */
addr = mips_add_offset (temp2, temp1, vcall_offset);
/* Load the offset and add it to THIS. */
- emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
emit_insn (gen_add3_insn (this, this, temp1));
}
/* 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.
if (TARGET_USE_PIC_FN_ADDR_REG
&& REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
- emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+ mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
emit_jump_insn (gen_indirect_jump (temp1));
}
else
insn = get_insns ();
insn_locators_alloc ();
split_all_insns_noflow ();
- if (TARGET_MIPS16)
- mips16_lay_out_constants ();
+ mips16_lay_out_constants ();
shorten_branches (insn);
final_start_function (insn, file, 1);
final (insn, file, 1);
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_MIPS16)
- {
- /* In mips16 mode, the constant table always goes in the same section
- as the function, so that constants can be loaded using PC relative
- addressing. */
- return function_section (current_function_decl);
- }
- else 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))
+ switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
{
case SYMBOL_PC_RELATIVE:
case SYMBOL_GP_RELATIVE:
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;
}
/* Don't initialize the pseudo register if we are being called from
the tree optimizers' cost-calculation routines. */
if (!cfun->machine->initialized_mips16_gp_pseudo_p
- && current_ir_type () != IR_GIMPLE)
+ && (current_ir_type () != IR_GIMPLE || currently_expanding_to_rtl))
{
rtx insn, scan;
fputs ("\n", file);
}
- fprintf (file, "\t.set\tmips16\n");
-
switch_to_section (function_section (current_function_decl));
}
/* We don't need to do anything if we aren't in mips16 mode, or if
we were invoked with the -msoft-float option. */
- if (!mips16_hard_float)
+ if (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
return 0;
/* Figure out whether the value might come back in a floating point
id = get_identifier (buf);
stub_fn = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (id));
- emit_move_insn (gen_rtx_REG (Pmode, 2), fn);
+ mips_emit_move (gen_rtx_REG (Pmode, 2), fn);
if (retval == NULL_RTX)
insn = gen_call_internal (stub_fn, arg_size);
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);
mips16_rewrite_pool_refs (rtx *x, void *data)
{
struct mips16_constant_pool *pool = data;
- if (GET_CODE (*x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (*x))
- *x = gen_rtx_LABEL_REF (Pmode, add_constant (pool,
- get_pool_constant (*x),
- get_pool_mode (*x)));
- return 0;
+ rtx base, offset, label;
+
+ if (MEM_P (*x))
+ x = &XEXP (*x, 0);
+ else if (!TARGET_MIPS16_TEXT_LOADS)
+ return 0;
+
+ split_const (*x, &base, &offset);
+ if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
+ {
+ label = add_constant (pool, get_pool_constant (base),
+ get_pool_mode (base));
+ base = gen_rtx_LABEL_REF (Pmode, label);
+ *x = mips_unspec_address_offset (base, offset, SYMBOL_PC_RELATIVE);
+ return -1;
+ }
+ return GET_CODE (*x) == CONST ? -1 : 0;
}
/* Build MIPS16 constant pools. */
struct mips16_constant_pool pool;
rtx insn, barrier;
+ if (!TARGET_MIPS16_PCREL_LOADS)
+ return;
+
barrier = 0;
memset (&pool, 0, sizeof (pool));
for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
static void
mips_reorg (void)
{
- if (TARGET_MIPS16)
- mips16_lay_out_constants ();
- else if (TARGET_EXPLICIT_RELOCS)
+ mips16_lay_out_constants ();
+ if (TARGET_EXPLICIT_RELOCS)
{
if (mips_flag_delayed_branch)
dbr_schedule (get_insns ());
set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
}
- if (mips16_hard_float)
+ if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
{
set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
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
ready[i] = new_head;
}
-/* Implement TARGET_SCHED_REORDER. */
+/* If the priority of the instruction at POS2 in the ready queue READY
+ is within LIMIT units of that of the instruction at POS1, swap the
+ instructions if POS2 is not already less than POS1. */
-static int
-mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
- rtx *ready, int *nreadyp, int cycle)
+static void
+mips_maybe_swap_ready (rtx *ready, int pos1, int pos2, int limit)
+{
+ if (pos1 < pos2
+ && INSN_PRIORITY (ready[pos1]) + limit >= INSN_PRIORITY (ready[pos2]))
+ {
+ rtx temp;
+ temp = ready[pos1];
+ ready[pos1] = ready[pos2];
+ ready[pos2] = temp;
+ }
+}
+
+/* Record whether last 74k AGEN instruction was a load or store. */
+
+static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
+
+/* Initialize mips_last_74k_agen_insn from INSN. A null argument
+ resets to TYPE_UNKNOWN state. */
+
+static void
+mips_74k_agen_init (rtx insn)
{
- if (!reload_completed && TUNE_MACC_CHAINS)
+ if (!insn || !NONJUMP_INSN_P (insn))
+ mips_last_74k_agen_insn = TYPE_UNKNOWN;
+ else if (USEFUL_INSN_P (insn))
+ {
+ enum attr_type type = get_attr_type (insn);
+ if (type == TYPE_LOAD || type == TYPE_STORE)
+ mips_last_74k_agen_insn = type;
+ }
+}
+
+/* A TUNE_74K helper function. The 74K AGEN pipeline likes multiple
+ loads to be grouped together, and multiple stores to be grouped
+ together. Swap things around in the ready queue to make this happen. */
+
+static void
+mips_74k_agen_reorder (rtx *ready, int nready)
+{
+ int i;
+ int store_pos, load_pos;
+
+ store_pos = -1;
+ load_pos = -1;
+
+ for (i = nready - 1; i >= 0; i--)
{
- if (cycle == 0)
- mips_macc_chains_last_hilo = 0;
- if (*nreadyp > 0)
- mips_macc_chains_reorder (ready, *nreadyp);
+ rtx insn = ready[i];
+ if (USEFUL_INSN_P (insn))
+ switch (get_attr_type (insn))
+ {
+ case TYPE_STORE:
+ if (store_pos == -1)
+ store_pos = i;
+ break;
+
+ case TYPE_LOAD:
+ if (load_pos == -1)
+ load_pos = i;
+ break;
+
+ default:
+ break;
+ }
}
- if (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN)
+
+ if (load_pos == -1 || store_pos == -1)
+ return;
+
+ switch (mips_last_74k_agen_insn)
{
- if (cycle == 0)
- vr4130_last_insn = 0;
- if (*nreadyp > 1)
- vr4130_reorder (ready, *nreadyp);
+ case TYPE_UNKNOWN:
+ /* Prefer to schedule loads since they have a higher latency. */
+ case TYPE_LOAD:
+ /* Swap loads to the front of the queue. */
+ mips_maybe_swap_ready (ready, load_pos, store_pos, 4);
+ break;
+ case TYPE_STORE:
+ /* Swap stores to the front of the queue. */
+ mips_maybe_swap_ready (ready, store_pos, load_pos, 4);
+ break;
+ default:
+ break;
}
+}
+
+/* Implement TARGET_SCHED_INIT. */
+
+static void
+mips_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ int max_ready ATTRIBUTE_UNUSED)
+{
+ mips_macc_chains_last_hilo = 0;
+ vr4130_last_insn = 0;
+ mips_74k_agen_init (NULL_RTX);
+}
+
+/* Implement TARGET_SCHED_REORDER and TARG_SCHED_REORDER2. */
+
+static int
+mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx *ready, int *nreadyp, int cycle ATTRIBUTE_UNUSED)
+{
+ if (!reload_completed
+ && TUNE_MACC_CHAINS
+ && *nreadyp > 0)
+ mips_macc_chains_reorder (ready, *nreadyp);
+ if (reload_completed
+ && TUNE_MIPS4130
+ && !TARGET_VR4130_ALIGN
+ && *nreadyp > 1)
+ vr4130_reorder (ready, *nreadyp);
+ if (TUNE_74K)
+ mips_74k_agen_reorder (ready, *nreadyp);
return mips_issue_rate ();
}
mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
rtx insn, int more)
{
+ if (TUNE_74K)
+ mips_74k_agen_init (insn);
switch (GET_CODE (PATTERN (insn)))
{
case USE:
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++)
{
done_label = gen_label_rtx ();
/* First assume that CONDITION is false. */
- emit_move_insn (target, value_if_false);
+ mips_emit_move (target, value_if_false);
/* Branch to TRUE_LABEL if CONDITION is true and DONE_LABEL otherwise. */
emit_jump_insn (gen_condjump (condition, true_label));
/* Fix TARGET if CONDITION is true. */
emit_label (true_label);
- emit_move_insn (target, value_if_true);
+ mips_emit_move (target, value_if_true);
emit_label (done_label);
return target;
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;
+ }
}
}