-/* Subroutines for insn-output.c for MIPS
+/* Subroutines used for MIPS code generation.
Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+ Free Software Foundation, Inc.
Contributed by A. Lichnewsky, lich@inria.inria.fr.
Changes by Michael Meissner, meissner@osf.org.
- 64 bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
+ 64-bit r4000 support by Ian Lance Taylor, ian@cygnus.com, and
Brendan Eich, brendan@microunity.com.
-This file is part of GNU CC.
+This file is part of GCC.
-GNU CC is free software; you can redistribute it and/or modify
+GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
-GNU CC is distributed in the hope that it will be useful,
+GCC is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
-
-/* ??? The TARGET_FP_CALL_32 macros are intended to simulate a 32 bit
- calling convention in 64 bit mode. It doesn't work though, and should
- be replaced with something better designed. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
#include <signal.h>
#include "rtl.h"
#include "regs.h"
#include "tree.h"
#include "function.h"
#include "expr.h"
+#include "optabs.h"
+#include "libfuncs.h"
#include "flags.h"
#include "reload.h"
-#include "output.h"
#include "tm_p.h"
#include "ggc.h"
#include "gstab.h"
#include "debug.h"
#include "target.h"
#include "target-def.h"
+#include "integrate.h"
+#include "langhooks.h"
+#include "cfglayout.h"
+#include "sched-int.h"
+#include "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) \
+ (GET_CODE (X) == UNSPEC \
+ && XINT (X, 1) >= UNSPEC_ADDRESS_FIRST \
+ && XINT (X, 1) < UNSPEC_ADDRESS_FIRST + NUM_SYMBOL_TYPES)
+
+/* Extract the symbol or label from UNSPEC wrapper X. */
+#define UNSPEC_ADDRESS(X) \
+ XVECEXP (X, 0, 0)
+
+/* Extract the symbol type from UNSPEC wrapper X. */
+#define UNSPEC_ADDRESS_TYPE(X) \
+ ((enum mips_symbol_type) (XINT (X, 1) - UNSPEC_ADDRESS_FIRST))
+
+/* The maximum distance between the top of the stack frame and the
+ value $sp has when we save and restore registers.
+
+ The value for normal-mode code must be a SMALL_OPERAND and must
+ preserve the maximum stack alignment. We therefore use a value
+ of 0x7ff0 in this case.
+
+ MIPS16e SAVE and RESTORE instructions can adjust the stack pointer by
+ up to 0x7f8 bytes and can usually save or restore all the registers
+ that we need to save or restore. (Note that we can only use these
+ instructions for o32, for which the stack alignment is 8 bytes.)
+
+ We use a maximum gap of 0x100 or 0x400 for MIPS16 code when SAVE and
+ RESTORE are not available. We can then use unextended instructions
+ to save and restore registers, and to allocate and deallocate the top
+ part of the frame. */
+#define MIPS_MAX_FIRST_STACK_STEP \
+ (!TARGET_MIPS16 ? 0x7ff0 \
+ : GENERATE_MIPS16E_SAVE_RESTORE ? 0x7f8 \
+ : TARGET_64BIT ? 0x100 : 0x400)
+
+/* True if INSN is a mips.md pattern or asm statement. */
+#define USEFUL_INSN_P(INSN) \
+ (INSN_P (INSN) \
+ && GET_CODE (PATTERN (INSN)) != USE \
+ && GET_CODE (PATTERN (INSN)) != CLOBBER \
+ && GET_CODE (PATTERN (INSN)) != ADDR_VEC \
+ && GET_CODE (PATTERN (INSN)) != ADDR_DIFF_VEC)
+
+/* If INSN is a delayed branch sequence, return the first instruction
+ in the sequence, otherwise return INSN itself. */
+#define SEQ_BEGIN(INSN) \
+ (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE \
+ ? XVECEXP (PATTERN (INSN), 0, 0) \
+ : (INSN))
+
+/* Likewise for the last instruction in a delayed branch sequence. */
+#define SEQ_END(INSN) \
+ (INSN_P (INSN) && GET_CODE (PATTERN (INSN)) == SEQUENCE \
+ ? XVECEXP (PATTERN (INSN), 0, XVECLEN (PATTERN (INSN), 0) - 1) \
+ : (INSN))
+
+/* Execute the following loop body with SUBINSN set to each instruction
+ between SEQ_BEGIN (INSN) and SEQ_END (INSN) inclusive. */
+#define FOR_EACH_SUBINSN(SUBINSN, INSN) \
+ for ((SUBINSN) = SEQ_BEGIN (INSN); \
+ (SUBINSN) != NEXT_INSN (SEQ_END (INSN)); \
+ (SUBINSN) = NEXT_INSN (SUBINSN))
+
+/* True if bit BIT is set in VALUE. */
+#define BITSET_P(VALUE, BIT) (((VALUE) & (1 << (BIT))) != 0)
+
+/* Classifies an address.
+
+ ADDRESS_REG
+ A natural register + offset address. The register satisfies
+ mips_valid_base_register_p and the offset is a const_arith_operand.
+
+ ADDRESS_LO_SUM
+ A LO_SUM rtx. The first operand is a valid base register and
+ the second operand is a symbolic address.
+
+ ADDRESS_CONST_INT
+ A signed 16-bit constant address.
+
+ ADDRESS_SYMBOLIC:
+ A constant symbolic address. */
+enum mips_address_type {
+ ADDRESS_REG,
+ ADDRESS_LO_SUM,
+ ADDRESS_CONST_INT,
+ ADDRESS_SYMBOLIC
+};
-#ifdef HALF_PIC_DEBUG
-#include "halfpic.h"
-#endif
+/* Enumerates the setting of the -mr10k-cache-barrier option. */
+enum mips_r10k_cache_barrier_setting {
+ R10K_CACHE_BARRIER_NONE,
+ R10K_CACHE_BARRIER_STORE,
+ R10K_CACHE_BARRIER_LOAD_STORE
+};
-#ifdef __GNU_STAB__
-#define STAB_CODE_TYPE enum __stab_debug_code
-#else
-#define STAB_CODE_TYPE int
-#endif
+/* Macros to create an enumeration identifier for a function prototype. */
+#define MIPS_FTYPE_NAME1(A, B) MIPS_##A##_FTYPE_##B
+#define MIPS_FTYPE_NAME2(A, B, C) MIPS_##A##_FTYPE_##B##_##C
+#define MIPS_FTYPE_NAME3(A, B, C, D) MIPS_##A##_FTYPE_##B##_##C##_##D
+#define MIPS_FTYPE_NAME4(A, B, C, D, E) MIPS_##A##_FTYPE_##B##_##C##_##D##_##E
+
+/* Classifies the prototype of a built-in function. */
+enum mips_function_type {
+#define DEF_MIPS_FTYPE(NARGS, LIST) MIPS_FTYPE_NAME##NARGS LIST,
+#include "config/mips/mips-ftypes.def"
+#undef DEF_MIPS_FTYPE
+ MIPS_MAX_FTYPE_MAX
+};
-extern tree lookup_name PARAMS ((tree));
-
-/* Enumeration for all of the relational tests, so that we can build
- arrays indexed by the test type, and not worry about the order
- of EQ, NE, etc. */
-
-enum internal_test {
- ITEST_EQ,
- ITEST_NE,
- ITEST_GT,
- ITEST_GE,
- ITEST_LT,
- ITEST_LE,
- ITEST_GTU,
- ITEST_GEU,
- ITEST_LTU,
- ITEST_LEU,
- ITEST_MAX
- };
+/* Specifies how a built-in function should be converted into rtl. */
+enum mips_builtin_type {
+ /* The function corresponds directly to an .md pattern. The return
+ value is mapped to operand 0 and the arguments are mapped to
+ operands 1 and above. */
+ MIPS_BUILTIN_DIRECT,
+
+ /* The function corresponds directly to an .md pattern. There is no return
+ value and the arguments are mapped to operands 0 and above. */
+ MIPS_BUILTIN_DIRECT_NO_TARGET,
+
+ /* The function corresponds to a comparison instruction followed by
+ a mips_cond_move_tf_ps pattern. The first two arguments are the
+ values to compare and the second two arguments are the vector
+ operands for the movt.ps or movf.ps instruction (in assembly order). */
+ MIPS_BUILTIN_MOVF,
+ MIPS_BUILTIN_MOVT,
+
+ /* The function corresponds to a V2SF comparison instruction. Operand 0
+ of this instruction is the result of the comparison, which has mode
+ CCV2 or CCV4. The function arguments are mapped to operands 1 and
+ above. The function's return value is an SImode boolean that is
+ true under the following conditions:
+
+ MIPS_BUILTIN_CMP_ANY: one of the registers is true
+ MIPS_BUILTIN_CMP_ALL: all of the registers are true
+ MIPS_BUILTIN_CMP_LOWER: the first register is true
+ MIPS_BUILTIN_CMP_UPPER: the second register is true. */
+ MIPS_BUILTIN_CMP_ANY,
+ MIPS_BUILTIN_CMP_ALL,
+ MIPS_BUILTIN_CMP_UPPER,
+ MIPS_BUILTIN_CMP_LOWER,
+
+ /* As above, but the instruction only sets a single $fcc register. */
+ MIPS_BUILTIN_CMP_SINGLE,
+
+ /* For generating bposge32 branch instructions in MIPS32 DSP ASE. */
+ MIPS_BUILTIN_BPOSGE32
+};
+/* Invoke MACRO (COND) for each C.cond.fmt condition. */
+#define MIPS_FP_CONDITIONS(MACRO) \
+ MACRO (f), \
+ MACRO (un), \
+ MACRO (eq), \
+ MACRO (ueq), \
+ MACRO (olt), \
+ MACRO (ult), \
+ MACRO (ole), \
+ MACRO (ule), \
+ MACRO (sf), \
+ MACRO (ngle), \
+ MACRO (seq), \
+ MACRO (ngl), \
+ MACRO (lt), \
+ MACRO (nge), \
+ MACRO (le), \
+ MACRO (ngt)
+
+/* Enumerates the codes above as MIPS_FP_COND_<X>. */
+#define DECLARE_MIPS_COND(X) MIPS_FP_COND_ ## X
+enum mips_fp_condition {
+ MIPS_FP_CONDITIONS (DECLARE_MIPS_COND)
+};
-struct constant;
-static enum internal_test map_test_to_internal_test PARAMS ((enum rtx_code));
-static int mips16_simple_memory_operand PARAMS ((rtx, rtx,
- enum machine_mode));
-static int m16_check_op PARAMS ((rtx, int, int, int));
-static void block_move_loop PARAMS ((rtx, rtx,
- unsigned int,
- int,
- rtx, rtx));
-static void block_move_call PARAMS ((rtx, rtx, rtx));
-static rtx mips_add_large_offset_to_sp PARAMS ((HOST_WIDE_INT,
- FILE *));
-static void mips_annotate_frame_insn PARAMS ((rtx, rtx));
-static rtx mips_frame_set PARAMS ((enum machine_mode,
- int, int));
-static void mips_emit_frame_related_store PARAMS ((rtx, rtx,
- HOST_WIDE_INT));
-static void save_restore_insns PARAMS ((int, rtx,
- long, FILE *));
-static void mips16_output_gp_offset PARAMS ((FILE *, rtx));
-static void mips16_fp_args PARAMS ((FILE *, int, int));
-static void build_mips16_function_stub PARAMS ((FILE *));
-static void mips16_optimize_gp PARAMS ((rtx));
-static rtx add_constant PARAMS ((struct constant **,
- rtx,
- enum machine_mode));
-static void dump_constants PARAMS ((struct constant *,
- rtx));
-static rtx mips_find_symbol PARAMS ((rtx));
-static void abort_with_insn PARAMS ((rtx, const char *))
- ATTRIBUTE_NORETURN;
-static int symbolic_expression_p PARAMS ((rtx));
-static void mips_add_gc_roots PARAMS ((void));
-static bool mips_assemble_integer PARAMS ((rtx, unsigned int, int));
-static void mips_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
-static void mips_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
-static enum processor_type mips_parse_cpu PARAMS ((const char *));
-static void copy_file_data PARAMS ((FILE *, FILE *));
-#ifdef TARGET_IRIX6
-static void iris6_asm_named_section_1 PARAMS ((const char *,
- unsigned int,
- unsigned int));
-static void iris6_asm_named_section PARAMS ((const char *,
- unsigned int));
-static int iris_section_align_entry_eq PARAMS ((const PTR, const PTR));
-static hashval_t iris_section_align_entry_hash PARAMS ((const PTR));
-static int iris6_section_align_1 PARAMS ((void **, void *));
-#endif
-static int mips_adjust_cost PARAMS ((rtx, rtx, rtx, int));
+/* Index X provides the string representation of MIPS_FP_COND_<X>. */
+#define STRINGIFY(X) #X
+static const char *const mips_fp_conditions[] = {
+ MIPS_FP_CONDITIONS (STRINGIFY)
+};
-/* Global variables for machine-dependent things. */
+/* Information about a function's frame layout. */
+struct mips_frame_info GTY(()) {
+ /* The size of the frame in bytes. */
+ HOST_WIDE_INT total_size;
-/* Threshold for data being put into the small data/bss area, instead
- of the normal data area (references to the small data/bss area take
- 1 instruction, and use the global pointer, references to the normal
- data area takes 2 instructions). */
-int mips_section_threshold = -1;
+ /* The number of bytes allocated to variables. */
+ HOST_WIDE_INT var_size;
-/* Count the number of .file directives, so that .loc is up to date. */
-int num_source_filenames = 0;
+ /* The number of bytes allocated to outgoing function arguments. */
+ HOST_WIDE_INT args_size;
-/* Count the number of sdb related labels are generated (to find block
- start and end boundaries). */
-int sdb_label_count = 0;
+ /* The number of bytes allocated to the .cprestore slot, or 0 if there
+ is no such slot. */
+ HOST_WIDE_INT cprestore_size;
-/* Next label # for each statement for Silicon Graphics IRIS systems. */
-int sym_lineno = 0;
+ /* Bit X is set if the function saves or restores GPR X. */
+ unsigned int mask;
-/* Non-zero if inside of a function, because the stupid MIPS asm can't
- handle .files inside of functions. */
-int inside_function = 0;
+ /* Likewise FPR X. */
+ unsigned int fmask;
-/* Files to separate the text and the data output, so that all of the data
- can be emitted before the text, which will mean that the assembler will
- generate smaller code, based on the global pointer. */
-FILE *asm_out_data_file;
-FILE *asm_out_text_file;
+ /* The number of GPRs and FPRs saved. */
+ unsigned int num_gp;
+ unsigned int num_fp;
-/* Linked list of all externals that are to be emitted when optimizing
- for the global pointer if they haven't been declared by the end of
- the program with an appropriate .comm or initialization. */
+ /* The offset of the topmost GPR and FPR save slots from the top of
+ the frame, or zero if no such slots are needed. */
+ HOST_WIDE_INT gp_save_offset;
+ HOST_WIDE_INT fp_save_offset;
-struct extern_list
-{
- struct extern_list *next; /* next external */
- const char *name; /* name of the external */
- int size; /* size in bytes */
-} *extern_head = 0;
+ /* Likewise, but giving offsets from the bottom of the frame. */
+ HOST_WIDE_INT gp_sp_offset;
+ HOST_WIDE_INT fp_sp_offset;
-/* Name of the file containing the current function. */
-const char *current_function_file = "";
+ /* The offset of arg_pointer_rtx from frame_pointer_rtx. */
+ HOST_WIDE_INT arg_pointer_offset;
-/* Warning given that Mips ECOFF can't support changing files
- within a function. */
-int file_in_function_warning = FALSE;
+ /* The offset of hard_frame_pointer_rtx from frame_pointer_rtx. */
+ HOST_WIDE_INT hard_frame_pointer_offset;
+};
-/* Whether to suppress issuing .loc's because the user attempted
- to change the filename within a function. */
-int ignore_line_number = FALSE;
+struct machine_function GTY(()) {
+ /* The register returned by mips16_gp_pseudo_reg; see there for details. */
+ rtx mips16_gp_pseudo_rtx;
-/* Number of nested .set noreorder, noat, nomacro, and volatile requests. */
-int set_noreorder;
-int set_noat;
-int set_nomacro;
-int set_volatile;
+ /* The number of extra stack bytes taken up by register varargs.
+ This area is allocated by the callee at the very top of the frame. */
+ int varargs_size;
+
+ /* The current frame information, calculated by mips_compute_frame_info. */
+ struct mips_frame_info frame;
-/* The next branch instruction is a branch likely, not branch normal. */
-int mips_branch_likely;
+ /* The register to use as the function's global pointer, or INVALID_REGNUM
+ if the function doesn't need one. */
+ unsigned int global_pointer;
-/* Count of delay slots and how many are filled. */
-int dslots_load_total;
-int dslots_load_filled;
-int dslots_jump_total;
-int dslots_jump_filled;
+ /* True if mips_adjust_insn_length should ignore an instruction's
+ hazard attribute. */
+ bool ignore_hazard_length_p;
-/* # of nops needed by previous insn */
-int dslots_number_nops;
+ /* True if the whole function is suitable for .set noreorder and
+ .set nomacro. */
+ bool all_noreorder_p;
-/* Number of 1/2/3 word references to data items (ie, not jal's). */
-int num_refs[3];
+ /* True if the function is known to have an instruction that needs $gp. */
+ bool has_gp_insn_p;
-/* registers to check for load delay */
-rtx mips_load_reg, mips_load_reg2, mips_load_reg3, mips_load_reg4;
+ /* True if we have emitted an instruction to initialize
+ mips16_gp_pseudo_rtx. */
+ bool initialized_mips16_gp_pseudo_p;
+};
-/* Cached operands, and operator to compare for use in set/branch/trap
- on condition codes. */
-rtx branch_cmp[2];
+/* Information about a single argument. */
+struct mips_arg_info {
+ /* True if the argument is passed in a floating-point register, or
+ would have been if we hadn't run out of registers. */
+ bool fpr_p;
-/* what type of branch to use */
-enum cmp_type branch_type;
+ /* The number of words passed in registers, rounded up. */
+ unsigned int reg_words;
-/* Number of previously seen half-pic pointers and references. */
-static int prev_half_pic_ptrs = 0;
-static int prev_half_pic_refs = 0;
+ /* For EABI, the offset of the first register from GP_ARG_FIRST or
+ FP_ARG_FIRST. For other ABIs, the offset of the first register from
+ the start of the ABI's argument structure (see the CUMULATIVE_ARGS
+ comment for details).
-/* The target cpu for code generation. */
-enum processor_type mips_arch;
+ The value is MAX_ARGS_IN_REGISTERS if the argument is passed entirely
+ on the stack. */
+ unsigned int reg_offset;
-/* The target cpu for optimization and scheduling. */
-enum processor_type mips_tune;
+ /* The number of words that must be passed on the stack, rounded up. */
+ unsigned int stack_words;
-/* which instruction set architecture to use. */
-int mips_isa;
+ /* The offset from the start of the stack overflow area of the argument's
+ first stack word. Only meaningful when STACK_WORDS is nonzero. */
+ unsigned int stack_offset;
+};
+
+/* Information about an address described by mips_address_type.
+
+ ADDRESS_CONST_INT
+ No fields are used.
+
+ ADDRESS_REG
+ REG is the base register and OFFSET is the constant offset.
+
+ ADDRESS_LO_SUM
+ REG and OFFSET are the operands to the LO_SUM and SYMBOL_TYPE
+ is the type of symbol it references.
+
+ ADDRESS_SYMBOLIC
+ SYMBOL_TYPE is the type of symbol that the address references. */
+struct mips_address_info {
+ enum mips_address_type type;
+ rtx reg;
+ rtx offset;
+ enum mips_symbol_type symbol_type;
+};
-/* which abi to use. */
-int mips_abi;
+/* One stage in a constant building sequence. These sequences have
+ the form:
-/* Strings to hold which cpu and instruction set architecture to use. */
-const char *mips_cpu_string; /* for -mcpu=<xxx> */
-const char *mips_arch_string; /* for -march=<xxx> */
-const char *mips_tune_string; /* for -mtune=<xxx> */
-const char *mips_isa_string; /* for -mips{1,2,3,4} */
-const char *mips_abi_string; /* for -mabi={32,n32,64,eabi} */
+ A = VALUE[0]
+ A = A CODE[1] VALUE[1]
+ A = A CODE[2] VALUE[2]
+ ...
-/* Whether we are generating mips16 code. This is a synonym for
- TARGET_MIPS16, and exists for use as an attribute. */
-int mips16;
+ where A is an accumulator, each CODE[i] is a binary rtl operation
+ and each VALUE[i] is a constant integer. CODE[0] is undefined. */
+struct mips_integer_op {
+ enum rtx_code code;
+ unsigned HOST_WIDE_INT value;
+};
-/* This variable is set by -mno-mips16. We only care whether
- -mno-mips16 appears or not, and using a string in this fashion is
- just a way to avoid using up another bit in target_flags. */
-const char *mips_no_mips16_string;
+/* The largest number of operations needed to load an integer constant.
+ The worst accepted case for 64-bit constants is LUI,ORI,SLL,ORI,SLL,ORI.
+ When the lowest bit is clear, we can try, but reject a sequence with
+ an extra SLL at the end. */
+#define MIPS_MAX_INTEGER_OPS 7
-/* This is only used to determine if an type size setting option was
- explicitly specified (-mlong64, -mint64, -mlong32). The specs
- set this option if such an option is used. */
-const char *mips_explicit_type_size_string;
+/* Information about a MIPS16e SAVE or RESTORE instruction. */
+struct mips16e_save_restore_info {
+ /* The number of argument registers saved by a SAVE instruction.
+ 0 for RESTORE instructions. */
+ unsigned int nargs;
-/* 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;
+ /* Bit X is set if the instruction saves or restores GPR X. */
+ unsigned int mask;
-/* This variable is set by -mentry. We only care whether -mentry
- appears or not, and using a string in this fashion is just a way to
- avoid using up another bit in target_flags. */
-const char *mips_entry_string;
+ /* The total number of bytes to allocate. */
+ HOST_WIDE_INT size;
+};
-const char *mips_cache_flush_func = CACHE_FLUSH_FUNC;
+/* Global variables for machine-dependent things. */
-/* Whether we should entry and exit pseudo-ops in mips16 mode. */
-int mips_entry;
+/* The -G setting, or the configuration's default small-data limit if
+ no -G option is given. */
+static unsigned int mips_small_data_threshold;
-/* If TRUE, we split addresses into their high and low parts in the RTL. */
-int mips_split_addresses;
+/* The number of file directives written by mips_output_filename. */
+int num_source_filenames;
-/* Generating calls to position independent functions? */
-enum mips_abicalls_type mips_abicalls;
+/* The name that appeared in the last .file directive written by
+ mips_output_filename, or "" if mips_output_filename hasn't
+ written anything yet. */
+const char *current_function_file = "";
-/* High and low marks for floating point values which we will accept
- as legitimate constants for LEGITIMATE_CONSTANT_P. These are
- initialized in override_options. */
-REAL_VALUE_TYPE dfhigh, dflow, sfhigh, sflow;
+/* A label counter used by PUT_SDB_BLOCK_START and PUT_SDB_BLOCK_END. */
+int sdb_label_count;
-/* Mode used for saving/restoring general purpose registers. */
-static enum machine_mode gpr_mode;
+/* Arrays that map GCC register numbers to debugger register numbers. */
+int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+int mips_dwarf_regno[FIRST_PSEUDO_REGISTER];
-/* Array giving truth value on whether or not a given hard register
- can support a given mode. */
-char mips_hard_regno_mode_ok[(int)MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+/* The nesting depth of the PRINT_OPERAND '%(', '%<' and '%[' constructs. */
+int set_noreorder;
+int set_nomacro;
+static int set_noat;
-/* Current frame information calculated by compute_frame_size. */
-struct mips_frame_info current_frame_info;
+/* True if we're writing out a branch-likely instruction rather than a
+ normal branch. */
+static bool mips_branch_likely;
-/* Zero structure to initialize current_frame_info. */
-struct mips_frame_info zero_frame_info;
+/* The operands passed to the last cmpMM expander. */
+rtx cmp_operands[2];
-/* Pseudo-reg holding the address of the current function when
- generating embedded PIC code. Created by LEGITIMIZE_ADDRESS, used
- by mips_finalize_pic if it was created. */
-rtx embedded_pic_fnaddr_rtx;
+/* The current instruction-set architecture. */
+enum processor_type mips_arch;
+const struct mips_cpu_info *mips_arch_info;
-/* The length of all strings seen when compiling for the mips16. This
- is used to tell how many strings are in the constant pool, so that
- we can see if we may have an overflow. This is reset each time the
- constant pool is output. */
-int mips_string_length;
+/* The processor that we should tune the code for. */
+enum processor_type mips_tune;
+const struct mips_cpu_info *mips_tune_info;
-/* Pseudo-reg holding the value of $28 in a mips16 function which
- refers to GP relative global variables. */
-rtx mips16_gp_pseudo_rtx;
+/* The ISA level associated with mips_arch. */
+int mips_isa;
-/* In mips16 mode, we build a list of all the string constants we see
- in a particular function. */
+/* The architecture selected by -mipsN, or null if -mipsN wasn't used. */
+static const struct mips_cpu_info *mips_isa_option_info;
-struct string_constant
-{
- struct string_constant *next;
- const char *label;
-};
+/* Which ABI to use. */
+int mips_abi = MIPS_ABI_DEFAULT;
-static struct string_constant *string_constants;
+/* Which cost information to use. */
+const struct mips_rtx_cost_data *mips_cost;
-/* List of all MIPS punctuation characters used by print_operand. */
-char mips_print_operand_punct[256];
+/* The ambient target flags, excluding MASK_MIPS16. */
+static int mips_base_target_flags;
-/* Map GCC register number to debugger register number. */
-int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
+/* True if MIPS16 is the default mode. */
+bool mips_base_mips16;
-/* Buffer to use to enclose a load/store operation with %{ %} to
- turn on .set volatile. */
-static char volatile_buffer[60];
-
-/* Hardware names for the registers. If -mrnames is used, this
- will be overwritten with mips_sw_reg_names. */
-
-char mips_reg_names[][8] =
-{
- "$0", "$1", "$2", "$3", "$4", "$5", "$6", "$7",
- "$8", "$9", "$10", "$11", "$12", "$13", "$14", "$15",
- "$16", "$17", "$18", "$19", "$20", "$21", "$22", "$23",
- "$24", "$25", "$26", "$27", "$28", "$sp", "$fp", "$31",
- "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
- "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
- "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
- "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
- "hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
- "$fcc5","$fcc6","$fcc7","$rap"
-};
+/* The ambient values of other global variables. */
+static int mips_base_schedule_insns; /* flag_schedule_insns */
+static int mips_base_reorder_blocks_and_partition; /* flag_reorder... */
+static int mips_base_move_loop_invariants; /* flag_move_loop_invariants */
+static int mips_base_align_loops; /* align_loops */
+static int mips_base_align_jumps; /* align_jumps */
+static int mips_base_align_functions; /* align_functions */
-/* Mips software names for the registers, used to overwrite the
- mips_reg_names array. */
-
-static const char mips_sw_reg_names[][8] =
-{
- "$zero","$at", "$v0", "$v1", "$a0", "$a1", "$a2", "$a3",
- "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
- "$s0", "$s1", "$s2", "$s3", "$s4", "$s5", "$s6", "$s7",
- "$t8", "$t9", "$k0", "$k1", "$gp", "$sp", "$fp", "$ra",
- "$f0", "$f1", "$f2", "$f3", "$f4", "$f5", "$f6", "$f7",
- "$f8", "$f9", "$f10", "$f11", "$f12", "$f13", "$f14", "$f15",
- "$f16", "$f17", "$f18", "$f19", "$f20", "$f21", "$f22", "$f23",
- "$f24", "$f25", "$f26", "$f27", "$f28", "$f29", "$f30", "$f31",
- "hi", "lo", "accum","$fcc0","$fcc1","$fcc2","$fcc3","$fcc4",
- "$fcc5","$fcc6","$fcc7","$rap"
-};
+/* The -mcode-readable setting. */
+enum mips_code_readable_setting mips_code_readable = CODE_READABLE_YES;
-/* Map hard register number to register class */
-const enum reg_class mips_regno_to_class[] =
-{
- GR_REGS, GR_REGS, M16_NA_REGS, M16_NA_REGS,
+/* The -mr10k-cache-barrier setting. */
+static enum mips_r10k_cache_barrier_setting mips_r10k_cache_barrier;
+
+/* Index [M][R] is true if register R is allowed to hold a value of mode M. */
+bool mips_hard_regno_mode_ok[(int) MAX_MACHINE_MODE][FIRST_PSEUDO_REGISTER];
+
+/* Index C is true if character C is a valid PRINT_OPERAND punctation
+ character. */
+bool mips_print_operand_punct[256];
+
+static GTY (()) int mips_output_filename_first_time = 1;
+
+/* mips_split_p[X] is true if symbols of type X can be split by
+ mips_split_symbol. */
+bool mips_split_p[NUM_SYMBOL_TYPES];
+
+/* mips_split_hi_p[X] is true if the high parts of symbols of type X
+ can be split by mips_split_symbol. */
+bool mips_split_hi_p[NUM_SYMBOL_TYPES];
+
+/* mips_lo_relocs[X] is the relocation to use when a symbol of type X
+ appears in a LO_SUM. It can be null if such LO_SUMs aren't valid or
+ if they are matched by a special .md file pattern. */
+static const char *mips_lo_relocs[NUM_SYMBOL_TYPES];
+
+/* Likewise for HIGHs. */
+static const char *mips_hi_relocs[NUM_SYMBOL_TYPES];
+
+/* Index R is the smallest register class that contains register R. */
+const enum reg_class mips_regno_to_class[FIRST_PSEUDO_REGISTER] = {
+ LEA_REGS, LEA_REGS, M16_REGS, V1_REG,
M16_REGS, M16_REGS, M16_REGS, M16_REGS,
- GR_REGS, GR_REGS, GR_REGS, GR_REGS,
- GR_REGS, GR_REGS, GR_REGS, GR_REGS,
- M16_NA_REGS, M16_NA_REGS, GR_REGS, GR_REGS,
- GR_REGS, GR_REGS, GR_REGS, GR_REGS,
- T_REG, GR_REGS, GR_REGS, GR_REGS,
- GR_REGS, GR_REGS, GR_REGS, GR_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ M16_REGS, M16_REGS, LEA_REGS, LEA_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
+ T_REG, PIC_FN_ADDR_REG, LEA_REGS, LEA_REGS,
+ LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- HI_REG, LO_REG, HILO_REG, ST_REGS,
+ MD0_REG, MD1_REG, NO_REGS, ST_REGS,
ST_REGS, ST_REGS, ST_REGS, ST_REGS,
- ST_REGS, ST_REGS, ST_REGS, GR_REGS
+ ST_REGS, ST_REGS, ST_REGS, NO_REGS,
+ NO_REGS, FRAME_REGS, FRAME_REGS, NO_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP0_REGS, COP0_REGS, COP0_REGS, COP0_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP2_REGS, COP2_REGS, COP2_REGS, COP2_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ COP3_REGS, COP3_REGS, COP3_REGS, COP3_REGS,
+ DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS, DSP_ACC_REGS,
+ DSP_ACC_REGS, DSP_ACC_REGS, ALL_REGS, ALL_REGS,
+ ALL_REGS, ALL_REGS, ALL_REGS, ALL_REGS
};
-/* Map register constraint character to register class. */
-enum reg_class mips_char_to_class[256] =
-{
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
- NO_REGS, NO_REGS, NO_REGS, NO_REGS,
+/* The value of TARGET_ATTRIBUTE_TABLE. */
+const struct attribute_spec mips_attribute_table[] = {
+ /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+ { "long_call", 0, 0, false, true, true, NULL },
+ { "far", 0, 0, false, true, true, NULL },
+ { "near", 0, 0, false, true, true, NULL },
+ /* We would really like to treat "mips16" and "nomips16" as type
+ attributes, but GCC doesn't provide the hooks we need to support
+ the right conversion rules. As declaration attributes, they affect
+ code generation but don't carry other semantics. */
+ { "mips16", 0, 0, true, false, false, NULL },
+ { "nomips16", 0, 0, true, false, false, NULL },
+ { NULL, 0, 0, false, false, false, NULL }
};
\f
-/* Initialize the GCC target structure. */
-#undef TARGET_ASM_ALIGNED_HI_OP
-#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
-#undef TARGET_ASM_ALIGNED_SI_OP
-#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
-#undef TARGET_ASM_INTEGER
-#define TARGET_ASM_INTEGER mips_assemble_integer
-
-#if TARGET_IRIX5 && !TARGET_IRIX6
-#undef TARGET_ASM_UNALIGNED_HI_OP
-#define TARGET_ASM_UNALIGNED_HI_OP "\t.align 0\n\t.half\t"
-#undef TARGET_ASM_UNALIGNED_SI_OP
-#define TARGET_ASM_UNALIGNED_SI_OP "\t.align 0\n\t.word\t"
-#endif
-
-#undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
-#undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE mips_output_function_epilogue
+/* A table describing all the processors GCC knows about. Names are
+ matched in the order listed. The first mention of an ISA level is
+ taken as the canonical name for that ISA.
+
+ To ease comparison, please keep this table in the same order
+ as GAS's mips_cpu_info_table. Please also make sure that
+ MIPS_ISA_LEVEL_SPEC and MIPS_ARCH_FLOAT_SPEC handle all -march
+ options correctly. */
+static const struct mips_cpu_info mips_cpu_info_table[] = {
+ /* Entries for generic ISAs. */
+ { "mips1", PROCESSOR_R3000, 1, 0 },
+ { "mips2", PROCESSOR_R6000, 2, 0 },
+ { "mips3", PROCESSOR_R4000, 3, 0 },
+ { "mips4", PROCESSOR_R8000, 4, 0 },
+ /* Prefer not to use branch-likely instructions for generic MIPS32rX
+ and MIPS64rX code. The instructions were officially deprecated
+ in revisions 2 and earlier, but revision 3 is likely to downgrade
+ that to a recommendation to avoid the instructions in code that
+ isn't tuned to a specific processor. */
+ { "mips32", PROCESSOR_4KC, 32, PTF_AVOID_BRANCHLIKELY },
+ { "mips32r2", PROCESSOR_M4K, 33, PTF_AVOID_BRANCHLIKELY },
+ { "mips64", PROCESSOR_5KC, 64, PTF_AVOID_BRANCHLIKELY },
+ /* ??? For now just tune the generic MIPS64r2 for 5KC as well. */
+ { "mips64r2", PROCESSOR_5KC, 65, PTF_AVOID_BRANCHLIKELY },
+
+ /* MIPS I processors. */
+ { "r3000", PROCESSOR_R3000, 1, 0 },
+ { "r2000", PROCESSOR_R3000, 1, 0 },
+ { "r3900", PROCESSOR_R3900, 1, 0 },
+
+ /* MIPS II processors. */
+ { "r6000", PROCESSOR_R6000, 2, 0 },
+
+ /* MIPS III processors. */
+ { "r4000", PROCESSOR_R4000, 3, 0 },
+ { "vr4100", PROCESSOR_R4100, 3, 0 },
+ { "vr4111", PROCESSOR_R4111, 3, 0 },
+ { "vr4120", PROCESSOR_R4120, 3, 0 },
+ { "vr4130", PROCESSOR_R4130, 3, 0 },
+ { "vr4300", PROCESSOR_R4300, 3, 0 },
+ { "r4400", PROCESSOR_R4000, 3, 0 },
+ { "r4600", PROCESSOR_R4600, 3, 0 },
+ { "orion", PROCESSOR_R4600, 3, 0 },
+ { "r4650", PROCESSOR_R4650, 3, 0 },
+ /* ST Loongson 2E/2F processors. */
+ { "loongson2e", PROCESSOR_LOONGSON_2E, 3, PTF_AVOID_BRANCHLIKELY },
+ { "loongson2f", PROCESSOR_LOONGSON_2F, 3, PTF_AVOID_BRANCHLIKELY },
+
+ /* MIPS IV processors. */
+ { "r8000", PROCESSOR_R8000, 4, 0 },
+ { "r10000", PROCESSOR_R10000, 4, 0 },
+ { "r12000", PROCESSOR_R10000, 4, 0 },
+ { "r14000", PROCESSOR_R10000, 4, 0 },
+ { "r16000", PROCESSOR_R10000, 4, 0 },
+ { "vr5000", PROCESSOR_R5000, 4, 0 },
+ { "vr5400", PROCESSOR_R5400, 4, 0 },
+ { "vr5500", PROCESSOR_R5500, 4, PTF_AVOID_BRANCHLIKELY },
+ { "rm7000", PROCESSOR_R7000, 4, 0 },
+ { "rm9000", PROCESSOR_R9000, 4, 0 },
+
+ /* MIPS32 processors. */
+ { "4kc", PROCESSOR_4KC, 32, 0 },
+ { "4km", PROCESSOR_4KC, 32, 0 },
+ { "4kp", PROCESSOR_4KP, 32, 0 },
+ { "4ksc", PROCESSOR_4KC, 32, 0 },
+
+ /* MIPS32 Release 2 processors. */
+ { "m4k", PROCESSOR_M4K, 33, 0 },
+ { "4kec", PROCESSOR_4KC, 33, 0 },
+ { "4kem", PROCESSOR_4KC, 33, 0 },
+ { "4kep", PROCESSOR_4KP, 33, 0 },
+ { "4ksd", PROCESSOR_4KC, 33, 0 },
+
+ { "24kc", PROCESSOR_24KC, 33, 0 },
+ { "24kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kfx", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kx", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "24kec", PROCESSOR_24KC, 33, 0 }, /* 24K with DSP. */
+ { "24kef2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kef", PROCESSOR_24KF2_1, 33, 0 },
+ { "24kef1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kefx", PROCESSOR_24KF1_1, 33, 0 },
+ { "24kex", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "34kc", PROCESSOR_24KC, 33, 0 }, /* 34K with MT/DSP. */
+ { "34kf2_1", PROCESSOR_24KF2_1, 33, 0 },
+ { "34kf", PROCESSOR_24KF2_1, 33, 0 },
+ { "34kf1_1", PROCESSOR_24KF1_1, 33, 0 },
+ { "34kfx", PROCESSOR_24KF1_1, 33, 0 },
+ { "34kx", PROCESSOR_24KF1_1, 33, 0 },
+
+ { "74kc", PROCESSOR_74KC, 33, 0 }, /* 74K with DSPr2. */
+ { "74kf2_1", PROCESSOR_74KF2_1, 33, 0 },
+ { "74kf", PROCESSOR_74KF2_1, 33, 0 },
+ { "74kf1_1", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kfx", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kx", PROCESSOR_74KF1_1, 33, 0 },
+ { "74kf3_2", PROCESSOR_74KF3_2, 33, 0 },
+
+ /* MIPS64 processors. */
+ { "5kc", PROCESSOR_5KC, 64, 0 },
+ { "5kf", PROCESSOR_5KF, 64, 0 },
+ { "20kc", PROCESSOR_20KC, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sb1", PROCESSOR_SB1, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sb1a", PROCESSOR_SB1A, 64, PTF_AVOID_BRANCHLIKELY },
+ { "sr71000", PROCESSOR_SR71000, 64, PTF_AVOID_BRANCHLIKELY },
+ { "xlr", PROCESSOR_XLR, 64, 0 },
+
+ /* MIPS64 Release 2 processors. */
+ { "octeon", PROCESSOR_OCTEON, 65, PTF_AVOID_BRANCHLIKELY }
+};
-#undef TARGET_SCHED_ADJUST_COST
-#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
+/* Default costs. If these are used for a processor we should look
+ up the actual costs. */
+#define DEFAULT_COSTS COSTS_N_INSNS (6), /* fp_add */ \
+ COSTS_N_INSNS (7), /* fp_mult_sf */ \
+ COSTS_N_INSNS (8), /* fp_mult_df */ \
+ COSTS_N_INSNS (23), /* fp_div_sf */ \
+ COSTS_N_INSNS (36), /* fp_div_df */ \
+ COSTS_N_INSNS (10), /* int_mult_si */ \
+ COSTS_N_INSNS (10), /* int_mult_di */ \
+ COSTS_N_INSNS (69), /* int_div_si */ \
+ COSTS_N_INSNS (69), /* int_div_di */ \
+ 2, /* branch_cost */ \
+ 4 /* memory_latency */
+
+/* Floating-point costs for processors without an FPU. Just assume that
+ all floating-point libcalls are very expensive. */
+#define SOFT_FP_COSTS COSTS_N_INSNS (256), /* fp_add */ \
+ COSTS_N_INSNS (256), /* fp_mult_sf */ \
+ COSTS_N_INSNS (256), /* fp_mult_df */ \
+ COSTS_N_INSNS (256), /* fp_div_sf */ \
+ COSTS_N_INSNS (256) /* fp_div_df */
+
+/* Costs to use when optimizing for size. */
+static const struct mips_rtx_cost_data mips_rtx_cost_optimize_size = {
+ COSTS_N_INSNS (1), /* fp_add */
+ COSTS_N_INSNS (1), /* fp_mult_sf */
+ COSTS_N_INSNS (1), /* fp_mult_df */
+ COSTS_N_INSNS (1), /* fp_div_sf */
+ COSTS_N_INSNS (1), /* fp_div_df */
+ COSTS_N_INSNS (1), /* int_mult_si */
+ COSTS_N_INSNS (1), /* int_mult_di */
+ COSTS_N_INSNS (1), /* int_div_si */
+ COSTS_N_INSNS (1), /* int_div_di */
+ 2, /* branch_cost */
+ 4 /* memory_latency */
+};
-struct gcc_target targetm = TARGET_INITIALIZER;
+/* Costs to use when optimizing for speed, indexed by processor. */
+static const struct mips_rtx_cost_data mips_rtx_cost_data[PROCESSOR_MAX] = {
+ { /* R3000 */
+ COSTS_N_INSNS (2), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (19), /* fp_div_df */
+ COSTS_N_INSNS (12), /* int_mult_si */
+ COSTS_N_INSNS (12), /* int_mult_di */
+ COSTS_N_INSNS (35), /* int_div_si */
+ COSTS_N_INSNS (35), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 4KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (6), /* int_mult_si */
+ COSTS_N_INSNS (6), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (36), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 4KP */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (36), /* int_mult_si */
+ COSTS_N_INSNS (36), /* int_mult_di */
+ COSTS_N_INSNS (37), /* int_div_si */
+ COSTS_N_INSNS (37), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 5KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (11), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 5KF */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (11), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 20KC */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (7), /* int_mult_di */
+ COSTS_N_INSNS (42), /* int_div_si */
+ COSTS_N_INSNS (72), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 24KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 24KF2_1 */
+ COSTS_N_INSNS (8), /* fp_add */
+ COSTS_N_INSNS (8), /* fp_mult_sf */
+ COSTS_N_INSNS (10), /* fp_mult_df */
+ COSTS_N_INSNS (34), /* fp_div_sf */
+ COSTS_N_INSNS (64), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 24KF1_1 */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KC */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KF2_1 */
+ COSTS_N_INSNS (8), /* fp_add */
+ COSTS_N_INSNS (8), /* fp_mult_sf */
+ COSTS_N_INSNS (10), /* fp_mult_df */
+ COSTS_N_INSNS (34), /* fp_div_sf */
+ COSTS_N_INSNS (64), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KF1_1 */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (17), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* 74KF3_2 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (6), /* fp_mult_sf */
+ COSTS_N_INSNS (7), /* fp_mult_df */
+ COSTS_N_INSNS (25), /* fp_div_sf */
+ COSTS_N_INSNS (48), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (41), /* int_div_si */
+ COSTS_N_INSNS (41), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* Loongson-2E */
+ DEFAULT_COSTS
+ },
+ { /* Loongson-2F */
+ DEFAULT_COSTS
+ },
+ { /* M4k */
+ DEFAULT_COSTS
+ },
+ /* Octeon */
+ {
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (72), /* int_div_si */
+ COSTS_N_INSNS (72), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R3900 */
+ COSTS_N_INSNS (2), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (19), /* fp_div_df */
+ COSTS_N_INSNS (2), /* int_mult_si */
+ COSTS_N_INSNS (2), /* int_mult_di */
+ COSTS_N_INSNS (35), /* int_div_si */
+ COSTS_N_INSNS (35), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R6000 */
+ COSTS_N_INSNS (3), /* fp_add */
+ COSTS_N_INSNS (5), /* fp_mult_sf */
+ COSTS_N_INSNS (6), /* fp_mult_df */
+ COSTS_N_INSNS (15), /* fp_div_sf */
+ COSTS_N_INSNS (16), /* fp_div_df */
+ COSTS_N_INSNS (17), /* int_mult_si */
+ COSTS_N_INSNS (17), /* int_mult_di */
+ COSTS_N_INSNS (38), /* int_div_si */
+ COSTS_N_INSNS (38), /* int_div_di */
+ 2, /* branch_cost */
+ 6 /* memory_latency */
+ },
+ { /* R4000 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (7), /* fp_mult_sf */
+ COSTS_N_INSNS (8), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (10), /* int_mult_si */
+ COSTS_N_INSNS (10), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 2, /* branch_cost */
+ 6 /* memory_latency */
+ },
+ { /* R4100 */
+ DEFAULT_COSTS
+ },
+ { /* R4111 */
+ DEFAULT_COSTS
+ },
+ { /* R4120 */
+ DEFAULT_COSTS
+ },
+ { /* R4130 */
+ /* The only costs that appear to be updated here are
+ integer multiplication. */
+ SOFT_FP_COSTS,
+ COSTS_N_INSNS (4), /* int_mult_si */
+ COSTS_N_INSNS (6), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R4300 */
+ DEFAULT_COSTS
+ },
+ { /* R4600 */
+ DEFAULT_COSTS
+ },
+ { /* R4650 */
+ DEFAULT_COSTS
+ },
+ { /* R5000 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (5), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (5), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (36), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R5400 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (5), /* fp_mult_sf */
+ COSTS_N_INSNS (6), /* fp_mult_df */
+ COSTS_N_INSNS (30), /* fp_div_sf */
+ COSTS_N_INSNS (59), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (4), /* int_mult_di */
+ COSTS_N_INSNS (42), /* int_div_si */
+ COSTS_N_INSNS (74), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R5500 */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (5), /* fp_mult_sf */
+ COSTS_N_INSNS (6), /* fp_mult_df */
+ COSTS_N_INSNS (30), /* fp_div_sf */
+ COSTS_N_INSNS (59), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (9), /* int_mult_di */
+ COSTS_N_INSNS (42), /* int_div_si */
+ COSTS_N_INSNS (74), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R7000 */
+ /* The only costs that are changed here are
+ integer multiplication. */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (7), /* fp_mult_sf */
+ COSTS_N_INSNS (8), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (9), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R8000 */
+ DEFAULT_COSTS
+ },
+ { /* R9000 */
+ /* The only costs that are changed here are
+ integer multiplication. */
+ COSTS_N_INSNS (6), /* fp_add */
+ COSTS_N_INSNS (7), /* fp_mult_sf */
+ COSTS_N_INSNS (8), /* fp_mult_df */
+ COSTS_N_INSNS (23), /* fp_div_sf */
+ COSTS_N_INSNS (36), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (8), /* int_mult_di */
+ COSTS_N_INSNS (69), /* int_div_si */
+ COSTS_N_INSNS (69), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* R1x000 */
+ COSTS_N_INSNS (2), /* fp_add */
+ COSTS_N_INSNS (2), /* fp_mult_sf */
+ COSTS_N_INSNS (2), /* fp_mult_df */
+ COSTS_N_INSNS (12), /* fp_div_sf */
+ COSTS_N_INSNS (19), /* fp_div_df */
+ COSTS_N_INSNS (5), /* int_mult_si */
+ COSTS_N_INSNS (9), /* int_mult_di */
+ COSTS_N_INSNS (34), /* int_div_si */
+ COSTS_N_INSNS (66), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* SB1 */
+ /* These costs are the same as the SB-1A below. */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (4), /* fp_mult_df */
+ COSTS_N_INSNS (24), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (4), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* SB1-A */
+ /* These costs are the same as the SB-1 above. */
+ COSTS_N_INSNS (4), /* fp_add */
+ COSTS_N_INSNS (4), /* fp_mult_sf */
+ COSTS_N_INSNS (4), /* fp_mult_df */
+ COSTS_N_INSNS (24), /* fp_div_sf */
+ COSTS_N_INSNS (32), /* fp_div_df */
+ COSTS_N_INSNS (3), /* int_mult_si */
+ COSTS_N_INSNS (4), /* int_mult_di */
+ COSTS_N_INSNS (36), /* int_div_si */
+ COSTS_N_INSNS (68), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ },
+ { /* SR71000 */
+ DEFAULT_COSTS
+ },
+ { /* XLR */
+ /* Need to replace first five with the costs of calling the appropriate
+ libgcc routine. */
+ COSTS_N_INSNS (256), /* fp_add */
+ COSTS_N_INSNS (256), /* fp_mult_sf */
+ COSTS_N_INSNS (256), /* fp_mult_df */
+ COSTS_N_INSNS (256), /* fp_div_sf */
+ COSTS_N_INSNS (256), /* fp_div_df */
+ COSTS_N_INSNS (8), /* int_mult_si */
+ COSTS_N_INSNS (8), /* int_mult_di */
+ COSTS_N_INSNS (72), /* int_div_si */
+ COSTS_N_INSNS (72), /* int_div_di */
+ 1, /* branch_cost */
+ 4 /* memory_latency */
+ }
+};
\f
-/* Return truth value of whether OP can be used as an operands
- where a register or 16 bit unsigned integer is needed. */
+/* This hash table keeps track of implicit "mips16" and "nomips16" attributes
+ for -mflip_mips16. It maps decl names onto a boolean mode setting. */
+struct mflip_mips16_entry GTY (()) {
+ const char *name;
+ bool mips16_p;
+};
+static GTY ((param_is (struct mflip_mips16_entry))) htab_t mflip_mips16_htab;
-int
-uns_arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+/* Hash table callbacks for mflip_mips16_htab. */
+
+static hashval_t
+mflip_mips16_htab_hash (const void *entry)
{
- if (GET_CODE (op) == CONST_INT && SMALL_INT_UNSIGNED (op))
- return 1;
+ return htab_hash_string (((const struct mflip_mips16_entry *) entry)->name);
+}
- return register_operand (op, mode);
+static int
+mflip_mips16_htab_eq (const void *entry, const void *name)
+{
+ return strcmp (((const struct mflip_mips16_entry *) entry)->name,
+ (const char *) name) == 0;
}
-/* Return truth value of whether OP can be used as an operands
- where a 16 bit integer is needed */
+/* True if -mflip-mips16 should next add an attribute for the default MIPS16
+ mode, false if it should next add an attribute for the opposite mode. */
+static GTY(()) bool mips16_flipper;
-int
-arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+/* DECL is a function that needs a default "mips16" or "nomips16" attribute
+ for -mflip-mips16. Return true if it should use "mips16" and false if
+ it should use "nomips16". */
+
+static bool
+mflip_mips16_use_mips16_p (tree decl)
{
- if (GET_CODE (op) == CONST_INT && SMALL_INT (op))
- return 1;
+ struct mflip_mips16_entry *entry;
+ const char *name;
+ hashval_t hash;
+ void **slot;
+
+ /* Use the opposite of the command-line setting for anonymous decls. */
+ if (!DECL_NAME (decl))
+ return !mips_base_mips16;
+
+ if (!mflip_mips16_htab)
+ mflip_mips16_htab = htab_create_ggc (37, mflip_mips16_htab_hash,
+ mflip_mips16_htab_eq, NULL);
+
+ name = IDENTIFIER_POINTER (DECL_NAME (decl));
+ hash = htab_hash_string (name);
+ slot = htab_find_slot_with_hash (mflip_mips16_htab, name, hash, INSERT);
+ entry = (struct mflip_mips16_entry *) *slot;
+ if (!entry)
+ {
+ mips16_flipper = !mips16_flipper;
+ entry = GGC_NEW (struct mflip_mips16_entry);
+ entry->name = name;
+ entry->mips16_p = mips16_flipper ? !mips_base_mips16 : mips_base_mips16;
+ *slot = entry;
+ }
+ return entry->mips16_p;
+}
+\f
+/* Predicates to test for presence of "near" and "far"/"long_call"
+ attributes on the given TYPE. */
- /* On the mips16, a GP relative value is a signed 16 bit offset. */
- if (TARGET_MIPS16 && GET_CODE (op) == CONST && mips16_gp_offset_p (op))
- return 1;
+static bool
+mips_near_type_p (const_tree type)
+{
+ return lookup_attribute ("near", TYPE_ATTRIBUTES (type)) != NULL;
+}
- return register_operand (op, mode);
+static bool
+mips_far_type_p (const_tree type)
+{
+ return (lookup_attribute ("long_call", TYPE_ATTRIBUTES (type)) != NULL
+ || lookup_attribute ("far", TYPE_ATTRIBUTES (type)) != NULL);
}
-/* Return truth value of whether OP can be used as an operand in a two
- address arithmetic insn (such as set 123456,%o4) of mode MODE. */
+/* Similar predicates for "mips16"/"nomips16" function attributes. */
-int
-arith32_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_mips16_decl_p (const_tree decl)
{
- if (GET_CODE (op) == CONST_INT)
- return 1;
+ return lookup_attribute ("mips16", DECL_ATTRIBUTES (decl)) != NULL;
+}
- return register_operand (op, mode);
+static bool
+mips_nomips16_decl_p (const_tree decl)
+{
+ return lookup_attribute ("nomips16", DECL_ATTRIBUTES (decl)) != NULL;
}
-/* Return truth value of whether OP is an integer which fits in 16 bits. */
+/* Return true if function DECL is a MIPS16 function. Return the ambient
+ setting if DECL is null. */
-int
-small_int (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+mips_use_mips16_mode_p (tree decl)
{
- return (GET_CODE (op) == CONST_INT && SMALL_INT (op));
+ if (decl)
+ {
+ /* Nested functions must use the same frame pointer as their
+ parent and must therefore use the same ISA mode. */
+ tree parent = decl_function_context (decl);
+ if (parent)
+ decl = parent;
+ if (mips_mips16_decl_p (decl))
+ return true;
+ if (mips_nomips16_decl_p (decl))
+ return false;
+ }
+ return mips_base_mips16;
}
-/* Return truth value of whether OP is a 32 bit integer which is too big to
- be loaded with one instruction. */
+/* Implement TARGET_COMP_TYPE_ATTRIBUTES. */
-int
-large_int (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static int
+mips_comp_type_attributes (const_tree type1, const_tree type2)
{
- HOST_WIDE_INT value;
-
- if (GET_CODE (op) != CONST_INT)
+ /* Disallow mixed near/far attributes. */
+ if (mips_far_type_p (type1) && mips_near_type_p (type2))
return 0;
-
- value = INTVAL (op);
-
- /* ior reg,$r0,value */
- if ((value & ~ ((HOST_WIDE_INT) 0x0000ffff)) == 0)
+ if (mips_near_type_p (type1) && mips_far_type_p (type2))
return 0;
+ return 1;
+}
- /* subu reg,$r0,value */
- if (((unsigned HOST_WIDE_INT) (value + 32768)) <= 32767)
- return 0;
+/* Implement TARGET_INSERT_ATTRIBUTES. */
- /* lui reg,value>>16 */
- if ((value & 0x0000ffff) == 0)
- return 0;
+static void
+mips_insert_attributes (tree decl, tree *attributes)
+{
+ const char *name;
+ bool mips16_p, nomips16_p;
- return 1;
+ /* Check for "mips16" and "nomips16" attributes. */
+ mips16_p = lookup_attribute ("mips16", *attributes) != NULL;
+ nomips16_p = lookup_attribute ("nomips16", *attributes) != NULL;
+ if (TREE_CODE (decl) != FUNCTION_DECL)
+ {
+ if (mips16_p)
+ error ("%qs attribute only applies to functions", "mips16");
+ if (nomips16_p)
+ error ("%qs attribute only applies to functions", "nomips16");
+ }
+ else
+ {
+ mips16_p |= mips_mips16_decl_p (decl);
+ nomips16_p |= mips_nomips16_decl_p (decl);
+ if (mips16_p || nomips16_p)
+ {
+ /* DECL cannot be simultaneously "mips16" and "nomips16". */
+ if (mips16_p && nomips16_p)
+ error ("%qs cannot have both %<mips16%> and "
+ "%<nomips16%> attributes",
+ IDENTIFIER_POINTER (DECL_NAME (decl)));
+ }
+ else if (TARGET_FLIP_MIPS16 && !DECL_ARTIFICIAL (decl))
+ {
+ /* Implement -mflip-mips16. If DECL has neither a "nomips16" nor a
+ "mips16" attribute, arbitrarily pick one. We must pick the same
+ setting for duplicate declarations of a function. */
+ name = mflip_mips16_use_mips16_p (decl) ? "mips16" : "nomips16";
+ *attributes = tree_cons (get_identifier (name), NULL, *attributes);
+ }
+ }
}
-/* Return truth value of whether OP is a register or the constant 0.
- In mips16 mode, we only accept a register, since the mips16 does
- not have $0. */
+/* Implement TARGET_MERGE_DECL_ATTRIBUTES. */
-int
-reg_or_0_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static tree
+mips_merge_decl_attributes (tree olddecl, tree newdecl)
+{
+ /* The decls' "mips16" and "nomips16" attributes must match exactly. */
+ if (mips_mips16_decl_p (olddecl) != mips_mips16_decl_p (newdecl))
+ error ("%qs redeclared with conflicting %qs attributes",
+ IDENTIFIER_POINTER (DECL_NAME (newdecl)), "mips16");
+ if (mips_nomips16_decl_p (olddecl) != mips_nomips16_decl_p (newdecl))
+ error ("%qs redeclared with conflicting %qs attributes",
+ IDENTIFIER_POINTER (DECL_NAME (newdecl)), "nomips16");
+
+ return merge_attributes (DECL_ATTRIBUTES (olddecl),
+ DECL_ATTRIBUTES (newdecl));
+}
+\f
+/* If X is a PLUS of a CONST_INT, return the two terms in *BASE_PTR
+ and *OFFSET_PTR. Return X in *BASE_PTR and 0 in *OFFSET_PTR otherwise. */
+
+static void
+mips_split_plus (rtx x, rtx *base_ptr, HOST_WIDE_INT *offset_ptr)
{
- switch (GET_CODE (op))
+ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
{
- case CONST_INT:
- if (TARGET_MIPS16)
- return 0;
- return INTVAL (op) == 0;
+ *base_ptr = XEXP (x, 0);
+ *offset_ptr = INTVAL (XEXP (x, 1));
+ }
+ else
+ {
+ *base_ptr = x;
+ *offset_ptr = 0;
+ }
+}
+\f
+static unsigned int mips_build_integer (struct mips_integer_op *,
+ unsigned HOST_WIDE_INT);
- case CONST_DOUBLE:
- if (TARGET_MIPS16)
- return 0;
- return op == CONST0_RTX (mode);
+/* A subroutine of mips_build_integer, with the same interface.
+ Assume that the final action in the sequence should be a left shift. */
- case REG:
- case SUBREG:
- return register_operand (op, mode);
+static unsigned int
+mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value)
+{
+ unsigned int i, shift;
- default:
- break;
- }
+ /* Shift VALUE right until its lowest bit is set. Shift arithmetically
+ since signed numbers are easier to load than unsigned ones. */
+ shift = 0;
+ while ((value & 1) == 0)
+ value /= 2, shift++;
- return 0;
+ i = mips_build_integer (codes, value);
+ codes[i].code = ASHIFT;
+ codes[i].value = shift;
+ return i + 1;
}
-/* Return truth value of whether OP is a register or the constant 0,
- even in mips16 mode. */
+/* As for mips_build_shift, but assume that the final action will be
+ an IOR or PLUS operation. */
-int
-true_reg_or_0_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static unsigned int
+mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value)
{
- switch (GET_CODE (op))
+ unsigned HOST_WIDE_INT high;
+ unsigned int i;
+
+ high = value & ~(unsigned HOST_WIDE_INT) 0xffff;
+ if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000)
{
- case CONST_INT:
- return INTVAL (op) == 0;
+ /* The constant is too complex to load with a simple LUI/ORI pair,
+ so we want to give the recursive call as many trailing zeros as
+ possible. In this case, we know bit 16 is set and that the
+ low 16 bits form a negative number. If we subtract that number
+ from VALUE, we will clear at least the lowest 17 bits, maybe more. */
+ i = mips_build_integer (codes, CONST_HIGH_PART (value));
+ codes[i].code = PLUS;
+ codes[i].value = CONST_LOW_PART (value);
+ }
+ else
+ {
+ /* Either this is a simple LUI/ORI pair, or clearing the lowest 16
+ bits gives a value with at least 17 trailing zeros. */
+ i = mips_build_integer (codes, high);
+ codes[i].code = IOR;
+ codes[i].value = value & 0xffff;
+ }
+ return i + 1;
+}
- case CONST_DOUBLE:
- return op == CONST0_RTX (mode);
+/* Fill CODES with a sequence of rtl operations to load VALUE.
+ Return the number of operations needed. */
- case REG:
- case SUBREG:
- return register_operand (op, mode);
+static unsigned int
+mips_build_integer (struct mips_integer_op *codes,
+ unsigned HOST_WIDE_INT value)
+{
+ if (SMALL_OPERAND (value)
+ || SMALL_OPERAND_UNSIGNED (value)
+ || LUI_OPERAND (value))
+ {
+ /* The value can be loaded with a single instruction. */
+ codes[0].code = UNKNOWN;
+ codes[0].value = value;
+ return 1;
+ }
+ else if ((value & 1) != 0 || LUI_OPERAND (CONST_HIGH_PART (value)))
+ {
+ /* Either the constant is a simple LUI/ORI combination or its
+ lowest bit is set. We don't want to shift in this case. */
+ return mips_build_lower (codes, value);
+ }
+ else if ((value & 0xffff) == 0)
+ {
+ /* The constant will need at least three actions. The lowest
+ 16 bits are clear, so the final action will be a shift. */
+ return mips_build_shift (codes, value);
+ }
+ else
+ {
+ /* The final action could be a shift, add or inclusive OR.
+ Rather than use a complex condition to select the best
+ approach, try both mips_build_shift and mips_build_lower
+ and pick the one that gives the shortest sequence.
+ Note that this case is only used once per constant. */
+ struct mips_integer_op alt_codes[MIPS_MAX_INTEGER_OPS];
+ unsigned int cost, alt_cost;
+
+ cost = mips_build_shift (codes, value);
+ alt_cost = mips_build_lower (alt_codes, value);
+ if (alt_cost < cost)
+ {
+ memcpy (codes, alt_codes, alt_cost * sizeof (codes[0]));
+ cost = alt_cost;
+ }
+ return cost;
+ }
+}
+\f
+/* Return true if symbols of type TYPE require a GOT access. */
+
+static bool
+mips_got_symbol_type_p (enum mips_symbol_type type)
+{
+ switch (type)
+ {
+ case SYMBOL_GOT_PAGE_OFST:
+ case SYMBOL_GOT_DISP:
+ return true;
default:
- break;
+ return false;
}
+}
- return 0;
+/* Return true if X is a thread-local symbol. */
+
+static bool
+mips_tls_symbol_p (rtx x)
+{
+ return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
}
-/* Return truth value if a CONST_DOUBLE is ok to be a legitimate constant. */
+/* Return true if SYMBOL_REF X is associated with a global symbol
+ (in the STB_GLOBAL sense). */
-int
-mips_const_double_ok (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_global_symbol_p (const_rtx x)
{
- REAL_VALUE_TYPE d;
+ const_tree decl = SYMBOL_REF_DECL (x);
- if (GET_CODE (op) != CONST_DOUBLE)
- return 0;
+ if (!decl)
+ return !SYMBOL_REF_LOCAL_P (x) || SYMBOL_REF_EXTERNAL_P (x);
- if (mode == VOIDmode)
- return 1;
+ /* Weakref symbols are not TREE_PUBLIC, but their targets are global
+ or weak symbols. Relocations in the object file will be against
+ the target symbol, so it's that symbol's binding that matters here. */
+ return DECL_P (decl) && (TREE_PUBLIC (decl) || DECL_WEAK (decl));
+}
- if (mode != SFmode && mode != DFmode)
- return 0;
+/* Return true if function X is a libgcc MIPS16 stub function. */
- if (op == CONST0_RTX (mode))
- return 1;
+static bool
+mips16_stub_function_p (const_rtx x)
+{
+ return (GET_CODE (x) == SYMBOL_REF
+ && strncmp (XSTR (x, 0), "__mips16_", 9) == 0);
+}
- /* ??? li.s does not work right with SGI's Irix 6 assembler. */
- if (mips_abi != ABI_32 && mips_abi != ABI_O64 && mips_abi != ABI_EABI)
- return 0;
+/* Return true if function X is a locally-defined and locally-binding
+ MIPS16 function. */
- REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+static bool
+mips16_local_function_p (const_rtx x)
+{
+ return (GET_CODE (x) == SYMBOL_REF
+ && SYMBOL_REF_LOCAL_P (x)
+ && !SYMBOL_REF_EXTERNAL_P (x)
+ && mips_use_mips16_mode_p (SYMBOL_REF_DECL (x)));
+}
- if (REAL_VALUE_ISNAN (d))
- return FALSE;
+/* Return true if SYMBOL_REF X binds locally. */
- if (REAL_VALUE_NEGATIVE (d))
- d = REAL_VALUE_NEGATE (d);
+static bool
+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));
+}
- if (mode == DFmode)
- {
- if (REAL_VALUES_LESS (d, dfhigh)
- && REAL_VALUES_LESS (dflow, d))
- return 1;
- }
- else
- {
- if (REAL_VALUES_LESS (d, sfhigh)
- && REAL_VALUES_LESS (sflow, d))
- return 1;
- }
+/* Return true if rtx constants of mode MODE should be put into a small
+ data section. */
- return 0;
+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_small_data_threshold);
}
-/* Accept the floating point constant 1 in the appropriate mode. */
+/* Return true if X should not be moved directly into register $25.
+ We need this because many versions of GAS will treat "la $25,foo" as
+ part of a call sequence and so allow a global "foo" to be lazily bound. */
-int
-const_float_1_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- REAL_VALUE_TYPE d;
- static REAL_VALUE_TYPE onedf;
- static REAL_VALUE_TYPE onesf;
- static int one_initialized;
-
- if (GET_CODE (op) != CONST_DOUBLE
- || mode != GET_MODE (op)
- || (mode != DFmode && mode != SFmode))
- return 0;
+bool
+mips_dangerous_for_la25_p (rtx x)
+{
+ return (!TARGET_EXPLICIT_RELOCS
+ && TARGET_USE_GOT
+ && GET_CODE (x) == SYMBOL_REF
+ && mips_global_symbol_p (x));
+}
+
+/* Return true if calls to X might need $25 to be valid on entry. */
+
+bool
+mips_use_pic_fn_addr_reg_p (const_rtx x)
+{
+ if (!TARGET_USE_PIC_FN_ADDR_REG)
+ return false;
- REAL_VALUE_FROM_CONST_DOUBLE (d, op);
+ /* MIPS16 stub functions are guaranteed not to use $25. */
+ if (mips16_stub_function_p (x))
+ return false;
- /* We only initialize these values if we need them, since we will
- never get called unless mips_isa >= 4. */
- if (! one_initialized)
+ if (GET_CODE (x) == SYMBOL_REF)
{
- onedf = REAL_VALUE_ATOF ("1.0", DFmode);
- onesf = REAL_VALUE_ATOF ("1.0", SFmode);
- one_initialized = 1;
+ /* If PLTs and copy relocations are available, the static linker
+ will make sure that $25 is valid on entry to the target function. */
+ if (TARGET_ABICALLS_PIC0)
+ return false;
+
+ /* Locally-defined functions use absolute accesses to set up
+ the global pointer. */
+ if (TARGET_ABSOLUTE_ABICALLS
+ && mips_symbol_binds_local_p (x)
+ && !SYMBOL_REF_EXTERNAL_P (x))
+ return false;
}
- if (mode == DFmode)
- return REAL_VALUES_EQUAL (d, onedf);
- else
- return REAL_VALUES_EQUAL (d, onesf);
+ return true;
}
-/* Return true if a memory load or store of REG plus OFFSET in MODE
- can be represented in a single word on the mips16. */
+/* Return the method that should be used to access SYMBOL_REF or
+ LABEL_REF X in context CONTEXT. */
-static int
-mips16_simple_memory_operand (reg, offset, mode)
- rtx reg;
- rtx offset;
- enum machine_mode mode;
+static enum mips_symbol_type
+mips_classify_symbol (const_rtx x, enum mips_symbol_context context)
{
- unsigned int size;
- int off;
+ if (TARGET_RTP_PIC)
+ return SYMBOL_GOT_DISP;
- if (mode == BLKmode)
+ if (GET_CODE (x) == LABEL_REF)
{
- /* We can't tell, because we don't know how the value will
- eventually be accessed. Returning 0 here does no great
- harm; it just prevents some possible instruction scheduling. */
- return 0;
+ /* 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;
+
+ return SYMBOL_ABSOLUTE;
}
- size = GET_MODE_SIZE (mode);
+ gcc_assert (GET_CODE (x) == SYMBOL_REF);
- if (INTVAL (offset) % size != 0)
- return 0;
- if (REGNO (reg) == STACK_POINTER_REGNUM && GET_MODE_SIZE (mode) == 4)
- off = 0x100;
- else
- off = 0x20;
- if (INTVAL (offset) >= 0 && INTVAL (offset) < (HOST_WIDE_INT)(off * size))
- return 1;
- return 0;
-}
+ if (SYMBOL_REF_TLS_MODEL (x))
+ return SYMBOL_TLS;
-/* Return truth value if a memory operand fits in a single instruction
- (ie, register + small offset). */
+ if (CONSTANT_POOL_ADDRESS_P (x))
+ {
+ if (TARGET_MIPS16_TEXT_LOADS)
+ return SYMBOL_PC_RELATIVE;
-int
-simple_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx addr, plus0, plus1;
+ if (TARGET_MIPS16_PCREL_LOADS && context == SYMBOL_CONTEXT_MEM)
+ return SYMBOL_PC_RELATIVE;
- /* Eliminate non-memory operations */
- if (GET_CODE (op) != MEM)
- return 0;
+ if (mips_rtx_constant_in_small_data_p (get_pool_mode (x)))
+ return SYMBOL_GP_RELATIVE;
+ }
- /* dword operations really put out 2 instructions, so eliminate them. */
- /* ??? This isn't strictly correct. It is OK to accept multiword modes
- here, since the length attributes are being set correctly, but only
- if the address is offsettable. LO_SUM is not offsettable. */
- if (GET_MODE_SIZE (GET_MODE (op)) > (unsigned) UNITS_PER_WORD)
- return 0;
+ /* Do not use small-data accesses for weak symbols; they may end up
+ being zero. */
+ if (TARGET_GPOPT && SYMBOL_REF_SMALL_P (x) && !SYMBOL_REF_WEAK (x))
+ return SYMBOL_GP_RELATIVE;
- /* Decode the address now. */
- addr = XEXP (op, 0);
- switch (GET_CODE (addr))
+ /* Don't use GOT accesses for locally-binding symbols when -mno-shared
+ is in effect. */
+ if (TARGET_ABICALLS_PIC2
+ && !(TARGET_ABSOLUTE_ABICALLS && mips_symbol_binds_local_p (x)))
{
- case REG:
- case LO_SUM:
- return 1;
+ /* There are three cases to consider:
- case CONST_INT:
- if (TARGET_MIPS16)
- return 0;
- return SMALL_INT (addr);
+ - o32 PIC (either with or without explicit relocs)
+ - n32/n64 PIC without explicit relocs
+ - n32/n64 PIC with explicit relocs
- case PLUS:
- plus0 = XEXP (addr, 0);
- plus1 = XEXP (addr, 1);
- if (GET_CODE (plus0) == REG
- && GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1)
- && (! TARGET_MIPS16
- || mips16_simple_memory_operand (plus0, plus1, mode)))
- return 1;
+ In the first case, both local and global accesses will use an
+ R_MIPS_GOT16 relocation. We must correctly predict which of
+ the two semantics (local or global) the assembler and linker
+ will apply. The choice depends on the symbol's binding rather
+ than its visibility.
- else if (GET_CODE (plus1) == REG
- && GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0)
- && (! TARGET_MIPS16
- || mips16_simple_memory_operand (plus1, plus0, mode)))
- return 1;
+ In the second case, the assembler will not use R_MIPS_GOT16
+ relocations, but it chooses between local and global accesses
+ in the same way as for o32 PIC.
- else
- return 0;
+ In the third case we have more freedom since both forms of
+ access will work for any kind of symbol. However, there seems
+ little point in doing things differently. */
+ if (mips_global_symbol_p (x))
+ return SYMBOL_GOT_DISP;
-#if 0
- /* We used to allow small symbol refs here (ie, stuff in .sdata
- or .sbss), but this causes some bugs in G++. Also, it won't
- interfere if the MIPS linker rewrites the store instruction
- because the function is PIC. */
+ return SYMBOL_GOT_PAGE_OFST;
+ }
- case LABEL_REF: /* never gp relative */
- break;
+ if (TARGET_MIPS16_PCREL_LOADS && context != SYMBOL_CONTEXT_CALL)
+ return SYMBOL_FORCE_TO_MEM;
- case CONST:
- /* If -G 0, we can never have a GP relative memory operation.
- Also, save some time if not optimizing. */
- if (!TARGET_GP_OPT)
- return 0;
+ return SYMBOL_ABSOLUTE;
+}
- {
- rtx offset = const0_rtx;
- addr = eliminate_constant_term (XEXP (addr, 0), &offset);
- if (GET_CODE (op) != SYMBOL_REF)
- return 0;
-
- /* let's be paranoid.... */
- if (! SMALL_INT (offset))
- return 0;
- }
+/* Classify the base of symbolic expression X, given that X appears in
+ context CONTEXT. */
- /* fall through */
+static enum mips_symbol_type
+mips_classify_symbolic_expression (rtx x, enum mips_symbol_context context)
+{
+ rtx offset;
- case SYMBOL_REF:
- return SYMBOL_REF_FLAG (addr);
-#endif
+ split_const (x, &x, &offset);
+ if (UNSPEC_ADDRESS_P (x))
+ return UNSPEC_ADDRESS_TYPE (x);
- /* This SYMBOL_REF case is for the mips16. If the above case is
- reenabled, this one should be merged in. */
- case SYMBOL_REF:
- /* References to the constant pool on the mips16 use a small
- offset if the function is small. The only time we care about
- getting this right is during delayed branch scheduling, so
- don't need to check until then. The machine_dependent_reorg
- function will set the total length of the instructions used
- in the function in current_frame_info. If that is small
- enough, we know for sure that this is a small offset. It
- would be better if we could take into account the location of
- the instruction within the function, but we can't, because we
- don't know where we are. */
- if (TARGET_MIPS16
- && CONSTANT_POOL_ADDRESS_P (addr)
- && current_frame_info.insns_len > 0)
- {
- long size;
+ return mips_classify_symbol (x, context);
+}
- size = current_frame_info.insns_len + get_pool_size ();
- if (GET_MODE_SIZE (mode) == 4)
- return size < 4 * 0x100;
- else if (GET_MODE_SIZE (mode) == 8)
- return size < 8 * 0x20;
- else
- return 0;
- }
+/* Return true if OFFSET is within the range [0, ALIGN), where ALIGN
+ is the alignment in bytes of SYMBOL_REF X. */
- return 0;
+static bool
+mips_offset_within_alignment_p (rtx x, HOST_WIDE_INT offset)
+{
+ HOST_WIDE_INT align;
- default:
- break;
+ align = SYMBOL_REF_DECL (x) ? DECL_ALIGN_UNIT (SYMBOL_REF_DECL (x)) : 1;
+ return IN_RANGE (offset, 0, align - 1);
+}
+
+/* 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_context context,
+ enum mips_symbol_type *symbol_type)
+{
+ rtx offset;
+
+ split_const (x, &x, &offset);
+ if (UNSPEC_ADDRESS_P (x))
+ {
+ *symbol_type = UNSPEC_ADDRESS_TYPE (x);
+ x = UNSPEC_ADDRESS (x);
+ }
+ else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+ {
+ *symbol_type = mips_classify_symbol (x, context);
+ if (*symbol_type == SYMBOL_TLS)
+ return false;
}
+ else
+ return false;
+
+ if (offset == const0_rtx)
+ return true;
+
+ /* Check whether a nonzero offset is valid for the underlying
+ relocations. */
+ 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:
+ /* If the target has 64-bit pointers and the object file only
+ supports 32-bit symbols, the values of those symbols will be
+ sign-extended. In this case we can't allow an arbitrary offset
+ in case the 32-bit value X + OFFSET has a different sign from X. */
+ if (Pmode == DImode && !ABI_HAS_64BIT_SYMBOLS)
+ return offset_within_block_p (x, INTVAL (offset));
+
+ /* In other cases the relocations can handle any offset. */
+ return true;
- return 0;
+ case SYMBOL_PC_RELATIVE:
+ /* Allow constant pool references to be converted to LABEL+CONSTANT.
+ In this case, we no longer have access to the underlying constant,
+ but the original symbol-based access was known to be valid. */
+ if (GET_CODE (x) == LABEL_REF)
+ return true;
+
+ /* Fall through. */
+
+ case SYMBOL_GP_RELATIVE:
+ /* Make sure that the offset refers to something within the
+ same object block. This should guarantee that the final
+ PC- or GP-relative offset is within the 16-bit limit. */
+ return offset_within_block_p (x, INTVAL (offset));
+
+ case SYMBOL_GOT_PAGE_OFST:
+ case SYMBOL_GOTOFF_PAGE:
+ /* If the symbol is global, the GOT entry will contain the symbol's
+ address, and we will apply a 16-bit offset after loading it.
+ If the symbol is local, the linker should provide enough local
+ GOT entries for a 16-bit offset, but larger offsets may lead
+ to GOT overflow. */
+ return SMALL_INT (offset);
+
+ case SYMBOL_TPREL:
+ case SYMBOL_DTPREL:
+ /* There is no carry between the HI and LO REL relocations, so the
+ offset is only valid if we know it won't lead to such a carry. */
+ return mips_offset_within_alignment_p (x, INTVAL (offset));
+
+ case SYMBOL_GOT_DISP:
+ case SYMBOL_GOTOFF_DISP:
+ case SYMBOL_GOTOFF_CALL:
+ case SYMBOL_GOTOFF_LOADGP:
+ case SYMBOL_TLSGD:
+ case SYMBOL_TLSLDM:
+ case SYMBOL_GOTTPREL:
+ case SYMBOL_TLS:
+ case SYMBOL_HALF:
+ return false;
+ }
+ gcc_unreachable ();
}
+\f
+/* 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. */
-/* Return nonzero for a memory address that can be used to load or store
- a doubleword. */
+static int
+mips_symbol_insns_1 (enum mips_symbol_type type, enum machine_mode mode)
+{
+ switch (type)
+ {
+ case SYMBOL_ABSOLUTE:
+ /* When using 64-bit symbols, we need 5 preparatory instructions,
+ such as:
-int
-double_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) != MEM
- || ! memory_operand (op, mode))
- {
- /* During reload, we accept a pseudo register if it has an
- appropriate memory address. If we don't do this, we will
- wind up reloading into a register, and then reloading that
- register from memory, when we could just reload directly from
- memory. */
- if (reload_in_progress
- && GET_CODE (op) == REG
- && REGNO (op) >= FIRST_PSEUDO_REGISTER
- && reg_renumber[REGNO (op)] < 0
- && reg_equiv_mem[REGNO (op)] != 0
- && double_memory_operand (reg_equiv_mem[REGNO (op)], mode))
+ lui $at,%highest(symbol)
+ daddiu $at,$at,%higher(symbol)
+ dsll $at,$at,16
+ daddiu $at,$at,%hi(symbol)
+ dsll $at,$at,16
+
+ The final address is then $at + %lo(symbol). With 32-bit
+ symbols we just need a preparatory LUI for normal mode and
+ a preparatory LI and SLL for MIPS16. */
+ return ABI_HAS_64BIT_SYMBOLS ? 6 : TARGET_MIPS16 ? 3 : 2;
+
+ case SYMBOL_GP_RELATIVE:
+ /* 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:
+ /* PC-relative constants can be only be used with ADDIUPC,
+ DADDIUPC, LWPC and LDPC. */
+ if (mode == MAX_MACHINE_MODE
+ || GET_MODE_SIZE (mode) == 4
+ || GET_MODE_SIZE (mode) == 8)
return 1;
- /* All reloaded addresses are valid in TARGET_64BIT mode. This is
- the same test performed for 'm' in find_reloads. */
+ /* The constant must be loaded using ADDIUPC or DADDIUPC first. */
+ return 0;
- if (reload_in_progress
- && TARGET_64BIT
- && (GET_CODE (op) == MEM
- || (GET_CODE (op) == REG
- && REGNO (op) >= FIRST_PSEUDO_REGISTER
- && reg_renumber[REGNO (op)] < 0)))
+ case SYMBOL_FORCE_TO_MEM:
+ /* LEAs will be converted into constant-pool references by
+ mips_reorg. */
+ if (mode == MAX_MACHINE_MODE)
return 1;
- if (reload_in_progress
- && TARGET_MIPS16
- && GET_CODE (op) == MEM)
- {
- rtx addr;
-
- addr = XEXP (op, 0);
-
- /* During reload on the mips16, we accept a large offset
- from the frame pointer or the stack pointer. This large
- address will get reloaded anyhow. */
- if (GET_CODE (addr) == PLUS
- && GET_CODE (XEXP (addr, 0)) == REG
- && (REGNO (XEXP (addr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
- || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
- && ((GET_CODE (XEXP (addr, 1)) == CONST_INT
- && ! SMALL_INT (XEXP (addr, 1)))
- || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
- && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
- return 1;
-
- /* Similarly, we accept a case where the memory address is
- itself on the stack, and will be reloaded. */
- if (GET_CODE (addr) == MEM)
- {
- rtx maddr;
-
- maddr = XEXP (addr, 0);
- if (GET_CODE (maddr) == PLUS
- && GET_CODE (XEXP (maddr, 0)) == REG
- && (REGNO (XEXP (maddr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
- || REGNO (XEXP (maddr, 0)) == STACK_POINTER_REGNUM)
- && ((GET_CODE (XEXP (maddr, 1)) == CONST_INT
- && ! SMALL_INT (XEXP (maddr, 1)))
- || (GET_CODE (XEXP (maddr, 1)) == SYMBOL_REF
- && CONSTANT_POOL_ADDRESS_P (XEXP (maddr, 1)))))
- return 1;
- }
+ /* The constant must be loaded and then dereferenced. */
+ return 0;
- /* We also accept the same case when we have a 16 bit signed
- offset mixed in as well. The large address will get
- reloaded, and the 16 bit offset will be OK. */
- if (GET_CODE (addr) == PLUS
- && GET_CODE (XEXP (addr, 0)) == MEM
- && GET_CODE (XEXP (addr, 1)) == CONST_INT
- && SMALL_INT (XEXP (addr, 1)))
- {
- addr = XEXP (XEXP (addr, 0), 0);
- if (GET_CODE (addr) == PLUS
- && GET_CODE (XEXP (addr, 0)) == REG
- && (REGNO (XEXP (addr, 0)) == (unsigned) HARD_FRAME_POINTER_REGNUM
- || REGNO (XEXP (addr, 0)) == STACK_POINTER_REGNUM)
- && ((GET_CODE (XEXP (addr, 1)) == CONST_INT
- && ! SMALL_INT (XEXP (addr, 1)))
- || (GET_CODE (XEXP (addr, 1)) == SYMBOL_REF
- && CONSTANT_POOL_ADDRESS_P (XEXP (addr, 1)))))
- return 1;
- }
- }
+ 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. The worst cases are:
+
+ (1) For local symbols when generating o32 or o64 code. The assembler
+ will use:
+
+ lw $at,%got(symbol)
+ nop
+
+ ...and the final address will be $at + %lo(symbol).
+
+ (2) For global symbols when -mxgot. The assembler will use:
+
+ lui $at,%got_hi(symbol)
+ (d)addu $at,$at,$gp
+
+ ...and the final address will be $at + %got_lo(symbol). */
+ return 3;
+
+ case SYMBOL_GOTOFF_PAGE:
+ 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_TLSGD:
+ case SYMBOL_TLSLDM:
+ case SYMBOL_DTPREL:
+ case SYMBOL_GOTTPREL:
+ case SYMBOL_TPREL:
+ 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. 32-bit
+ constants need an "lui; addiu" sequence for normal mode and
+ an "li; sll; addiu" sequence for MIPS16 mode. */
+ return !mips_split_p[type] ? 1 : TARGET_MIPS16 ? 3 : 2;
+
+ case SYMBOL_TLS:
+ /* We don't treat a bare TLS symbol as a constant. */
return 0;
}
+ gcc_unreachable ();
+}
- if (TARGET_64BIT)
- {
- /* In this case we can use an instruction like sd. */
- return 1;
- }
+/* 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.
- /* Make sure that 4 added to the address is a valid memory address.
- This essentially just checks for overflow in an added constant. */
+ 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.
- if (CONSTANT_ADDRESS_P (XEXP (op, 0)))
- return 1;
+ In both cases, treat extended MIPS16 instructions as two instructions. */
- op = adjust_address_nv (op, GET_MODE_CLASS (mode) == MODE_INT
- ? SImode : SFmode, 4);
- return memory_address_p (GET_MODE (op), XEXP (op, 0));
+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);
}
+\f
+/* A for_each_rtx callback. Stop the search if *X references a
+ thread-local symbol. */
-/* Return nonzero if the code of this rtx pattern is EQ or NE. */
-
-int
-equality_op (op, mode)
- rtx op;
- enum machine_mode mode;
+static int
+mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
{
- if (mode != GET_MODE (op))
- return 0;
-
- return GET_CODE (op) == EQ || GET_CODE (op) == NE;
+ return mips_tls_symbol_p (*x);
}
-/* Return nonzero if the code is a relational operations (EQ, LE, etc.) */
+/* Implement TARGET_CANNOT_FORCE_CONST_MEM. */
-int
-cmp_op (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_cannot_force_const_mem (rtx x)
{
- if (mode != GET_MODE (op))
- return 0;
+ enum mips_symbol_type type;
+ rtx base, offset;
- return GET_RTX_CLASS (GET_CODE (op)) == '<';
-}
+ /* There is no assembler syntax for expressing an address-sized
+ high part. */
+ if (GET_CODE (x) == HIGH)
+ return true;
-/* Return nonzero if the code is a relational operation suitable for a
- conditional trap instructuion (only EQ, NE, LT, LTU, GE, GEU).
- We need this in the insn that expands `trap_if' in order to prevent
- combine from erroneously altering the condition. */
+ /* As an optimization, reject constants that mips_legitimize_move
+ can expand inline.
-int
-trap_cmp_op (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (mode != GET_MODE (op))
- return 0;
+ Suppose we have a multi-instruction sequence that loads constant C
+ into register R. If R does not get allocated a hard register, and
+ R is used in an operand that allows both registers and memory
+ references, reload will consider forcing C into memory and using
+ one of the instruction's memory alternatives. Returning false
+ here will force it to use an input reload instead. */
+ if (GET_CODE (x) == CONST_INT && LEGITIMATE_CONSTANT_P (x))
+ return true;
- switch (GET_CODE (op))
+ split_const (x, &base, &offset);
+ if (mips_symbolic_constant_p (base, SYMBOL_CONTEXT_LEA, &type)
+ && type != SYMBOL_FORCE_TO_MEM)
{
- case EQ:
- case NE:
- case LT:
- case LTU:
- case GE:
- case GEU:
- return 1;
+ /* The same optimization as for CONST_INT. */
+ if (SMALL_INT (offset) && mips_symbol_insns (type, MAX_MACHINE_MODE) > 0)
+ return true;
- default:
- return 0;
+ /* If MIPS16 constant pools live in the text section, they should
+ not refer to anything that might need run-time relocation. */
+ if (TARGET_MIPS16_PCREL_LOADS && mips_got_symbol_type_p (type))
+ return true;
}
+
+ /* TLS symbols must be computed by mips_legitimize_move. */
+ if (for_each_rtx (&x, &mips_tls_symbol_ref_1, NULL))
+ return true;
+
+ return false;
}
-/* Return nonzero if the operand is either the PC or a label_ref. */
+/* 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,
+ const_rtx x ATTRIBUTE_UNUSED)
+{
+ return !TARGET_MIPS16_PCREL_LOADS;
+}
+\f
+/* Return true if register REGNO is a valid base register for mode MODE.
+ STRICT_P is true if REG_OK_STRICT is in effect. */
int
-pc_or_label_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode,
+ bool strict_p)
{
- if (op == pc_rtx)
- return 1;
+ if (!HARD_REGISTER_NUM_P (regno))
+ {
+ if (!strict_p)
+ return true;
+ regno = reg_renumber[regno];
+ }
- if (GET_CODE (op) == LABEL_REF)
- return 1;
+ /* These fake registers will be eliminated to either the stack or
+ hard frame pointer, both of which are usually valid base registers.
+ Reload deals with the cases where the eliminated form isn't valid. */
+ if (regno == ARG_POINTER_REGNUM || regno == FRAME_POINTER_REGNUM)
+ return true;
- return 0;
-}
+ /* In MIPS16 mode, the stack pointer can only address word and doubleword
+ values, nothing smaller. There are two problems here:
-/* Test for a valid operand for a call instruction.
- Don't allow the arg pointer register or virtual regs
- since they may change into reg + const, which the patterns
- can't handle yet. */
+ (a) Instantiating virtual registers can introduce new uses of the
+ stack pointer. If these virtual registers are valid addresses,
+ the stack pointer should be too.
-int
-call_insn_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return (CONSTANT_ADDRESS_P (op)
- || (GET_CODE (op) == REG && op != arg_pointer_rtx
- && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER
- && REGNO (op) <= LAST_VIRTUAL_REGISTER)));
-}
+ (b) Most uses of the stack pointer are not made explicit until
+ FRAME_POINTER_REGNUM and ARG_POINTER_REGNUM have been eliminated.
+ We don't know until that stage whether we'll be eliminating to the
+ stack pointer (which needs the restriction) or the hard frame
+ pointer (which doesn't).
-/* Return nonzero if OPERAND is valid as a source operand for a move
- instruction. */
+ All in all, it seems more consistent to only enforce this restriction
+ during and after reload. */
+ if (TARGET_MIPS16 && regno == STACK_POINTER_REGNUM)
+ return !strict_p || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
-int
-move_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- /* Accept any general operand after reload has started; doing so
- avoids losing if reload does an in-place replacement of a register
- with a SYMBOL_REF or CONST. */
- return (general_operand (op, mode)
- && (! (mips_split_addresses && mips_check_split (op, mode))
- || reload_in_progress || reload_completed)
- && ! (TARGET_MIPS16
- && GET_CODE (op) == SYMBOL_REF
- && ! mips16_constant (op, mode, 1, 0)));
-}
-
-/* Return nonzero if OPERAND is valid as a source operand for movdi.
- This accepts not only general_operand, but also sign extended
- constants and registers. We need to accept sign extended constants
- in case a sign extended register which is used in an expression,
- and is equivalent to a constant, is spilled. */
+ return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+}
-int
-movdi_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && (GET_MODE (XEXP (op, 0)) == SImode
- || (GET_CODE (XEXP (op, 0)) == CONST_INT
- && GET_MODE (XEXP (op, 0)) == VOIDmode))
- && (register_operand (XEXP (op, 0), SImode)
- || immediate_operand (XEXP (op, 0), SImode)))
- return 1;
-
- return (general_operand (op, mode)
- && ! (TARGET_MIPS16
- && GET_CODE (op) == SYMBOL_REF
- && ! mips16_constant (op, mode, 1, 0)));
-}
-
-/* Like register_operand, but when in 64 bit mode also accept a sign
- extend of a 32 bit register, since the value is known to be already
- sign extended. */
+/* Return true if X is a valid base register for mode MODE.
+ STRICT_P is true if REG_OK_STRICT is in effect. */
-int
-se_register_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_valid_base_register_p (rtx x, enum machine_mode mode, bool strict_p)
{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && GET_MODE (XEXP (op, 0)) == SImode
- && register_operand (XEXP (op, 0), SImode))
- return 1;
+ if (!strict_p && GET_CODE (x) == SUBREG)
+ x = SUBREG_REG (x);
- return register_operand (op, mode);
+ return (REG_P (x)
+ && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict_p));
}
-/* Like reg_or_0_operand, but when in 64 bit mode also accept a sign
- extend of a 32 bit register, since the value is known to be already
- sign extended. */
+/* Return true if, for every base register BASE_REG, (plus BASE_REG X)
+ can address a value of mode MODE. */
-int
-se_reg_or_0_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_valid_offset_p (rtx x, enum machine_mode mode)
{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && GET_MODE (XEXP (op, 0)) == SImode
- && register_operand (XEXP (op, 0), SImode))
- return 1;
+ /* Check that X is a signed 16-bit number. */
+ if (!const_arith_operand (x, Pmode))
+ return false;
- return reg_or_0_operand (op, mode);
+ /* We may need to split multiword moves, so make sure that every word
+ is accessible. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && !SMALL_OPERAND (INTVAL (x) + GET_MODE_SIZE (mode) - UNITS_PER_WORD))
+ return false;
+
+ return true;
}
-/* Like uns_arith_operand, but when in 64 bit mode also accept a sign
- extend of a 32 bit register, since the value is known to be already
- sign extended. */
+/* Return true if a LO_SUM can address a value of mode MODE when the
+ LO_SUM symbol has type SYMBOL_TYPE. */
-int
-se_uns_arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_valid_lo_sum_p (enum mips_symbol_type symbol_type, enum machine_mode mode)
{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && GET_MODE (XEXP (op, 0)) == SImode
- && register_operand (XEXP (op, 0), SImode))
- return 1;
+ /* Check that symbols of type SYMBOL_TYPE can be used to access values
+ of mode MODE. */
+ if (mips_symbol_insns (symbol_type, mode) == 0)
+ return false;
+
+ /* Check that there is a known low-part relocation. */
+ if (mips_lo_relocs[symbol_type] == NULL)
+ return false;
- return uns_arith_operand (op, mode);
+ /* We may need to split multiword moves, so make sure that each word
+ can be accessed without inducing a carry. This is mainly needed
+ for o64, which has historically only guaranteed 64-bit alignment
+ for 128-bit types. */
+ if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+ && GET_MODE_BITSIZE (mode) > GET_MODE_ALIGNMENT (mode))
+ return false;
+
+ return true;
}
-/* Like arith_operand, but when in 64 bit mode also accept a sign
- extend of a 32 bit register, since the value is known to be already
- sign extended. */
+/* Return true if X is a valid address for machine mode MODE. If it is,
+ fill in INFO appropriately. STRICT_P is true if REG_OK_STRICT is in
+ effect. */
-int
-se_arith_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips_classify_address (struct mips_address_info *info, rtx x,
+ enum machine_mode mode, bool strict_p)
{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && GET_MODE (XEXP (op, 0)) == SImode
- && register_operand (XEXP (op, 0), SImode))
- return 1;
+ switch (GET_CODE (x))
+ {
+ case REG:
+ case SUBREG:
+ info->type = ADDRESS_REG;
+ info->reg = x;
+ info->offset = const0_rtx;
+ return mips_valid_base_register_p (info->reg, mode, strict_p);
+
+ case PLUS:
+ info->type = ADDRESS_REG;
+ info->reg = XEXP (x, 0);
+ info->offset = XEXP (x, 1);
+ return (mips_valid_base_register_p (info->reg, mode, strict_p)
+ && mips_valid_offset_p (info->offset, mode));
+
+ case LO_SUM:
+ 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_p)
+ && mips_valid_lo_sum_p (info->symbol_type, mode));
+
+ case CONST_INT:
+ /* Small-integer addresses don't occur very often, but they
+ are legitimate if $0 is a valid base register. */
+ info->type = ADDRESS_CONST_INT;
+ return !TARGET_MIPS16 && SMALL_INT (x);
+
+ case CONST:
+ case LABEL_REF:
+ case SYMBOL_REF:
+ info->type = ADDRESS_SYMBOLIC;
+ 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]);
- return arith_operand (op, mode);
+ default:
+ return false;
+ }
}
-/* Like nonmemory_operand, but when in 64 bit mode also accept a sign
- extend of a 32 bit register, since the value is known to be already
- sign extended. */
+/* Return true if X is a legitimate address for a memory operand of mode
+ MODE. STRICT_P is true if REG_OK_STRICT is in effect. */
-int
-se_nonmemory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+bool
+mips_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p)
{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && GET_MODE (XEXP (op, 0)) == SImode
- && register_operand (XEXP (op, 0), SImode))
- return 1;
+ struct mips_address_info addr;
- return nonmemory_operand (op, mode);
+ return mips_classify_address (&addr, x, mode, strict_p);
}
-/* Like nonimmediate_operand, but when in 64 bit mode also accept a
- sign extend of a 32 bit register, since the value is known to be
- already sign extended. */
+/* Return true if X is a legitimate $sp-based address for mode MDOE. */
-int
-se_nonimmediate_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+bool
+mips_stack_address_p (rtx x, enum machine_mode mode)
{
- if (TARGET_64BIT
- && mode == DImode
- && GET_CODE (op) == SIGN_EXTEND
- && GET_MODE (op) == DImode
- && GET_MODE (XEXP (op, 0)) == SImode
- && register_operand (XEXP (op, 0), SImode))
- return 1;
+ struct mips_address_info addr;
- return nonimmediate_operand (op, mode);
+ return (mips_classify_address (&addr, x, mode, false)
+ && addr.type == ADDRESS_REG
+ && addr.reg == stack_pointer_rtx);
}
-/* Accept any operand that can appear in a mips16 constant table
- instruction. We can't use any of the standard operand functions
- because for these instructions we accept values that are not
- accepted by LEGITIMATE_CONSTANT, such as arbitrary SYMBOL_REFs. */
+/* Return true if ADDR matches the pattern for the LWXS load scaled indexed
+ address instruction. Note that such addresses are not considered
+ legitimate in the GO_IF_LEGITIMATE_ADDRESS sense, because their use
+ is so restricted. */
-int
-consttable_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+mips_lwxs_address_p (rtx addr)
{
- return CONSTANT_P (op);
+ if (ISA_HAS_LWXS
+ && GET_CODE (addr) == PLUS
+ && REG_P (XEXP (addr, 1)))
+ {
+ rtx offset = XEXP (addr, 0);
+ if (GET_CODE (offset) == MULT
+ && REG_P (XEXP (offset, 0))
+ && GET_CODE (XEXP (offset, 1)) == CONST_INT
+ && INTVAL (XEXP (offset, 1)) == 4)
+ return true;
+ }
+ return false;
}
+\f
+/* Return true if a value at OFFSET bytes from base register BASE can be
+ accessed using an unextended MIPS16 instruction. MODE is the mode of
+ the value.
-/* Return nonzero if we split the address into high and low parts. */
+ Usually the offset in an unextended instruction is a 5-bit field.
+ The offset is unsigned and shifted left once for LH and SH, twice
+ for LW and SW, and so on. An exception is LWSP and SWSP, which have
+ an 8-bit immediate field that's shifted left twice. */
+
+static bool
+mips16_unextended_reference_p (enum machine_mode mode, rtx base,
+ unsigned HOST_WIDE_INT offset)
+{
+ if (offset % GET_MODE_SIZE (mode) == 0)
+ {
+ if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx)
+ return offset < 256U * GET_MODE_SIZE (mode);
+ return offset < 32U * GET_MODE_SIZE (mode);
+ }
+ return false;
+}
-/* ??? We should also handle reg+array somewhere. We get four
- instructions currently, lui %hi/addui %lo/addui reg/lw. Better is
- lui %hi/addui reg/lw %lo. Fixing GO_IF_LEGITIMATE_ADDRESS to accept
- (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken
- out of the address, then we have 4 instructions to combine. Perhaps
- add a 3->2 define_split for combine. */
+/* Return the number of instructions needed to load or store a value
+ of mode MODE at address 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.
-/* ??? We could also split a CONST_INT here if it is a large_int().
- However, it doesn't seem to be very useful to have %hi(constant).
- We would be better off by doing the masking ourselves and then putting
- the explicit high part of the constant in the RTL. This will give better
- optimization. Also, %hi(constant) needs assembler changes to work.
- There is already a define_split that does this. */
+ For MIPS16 code, count extended instructions as two instructions. */
int
-mips_check_split (address, mode)
- rtx address;
- enum machine_mode mode;
+mips_address_insns (rtx x, enum machine_mode mode, bool might_split_p)
{
- /* ??? This is the same check used in simple_memory_operand.
- We use it here because LO_SUM is not offsettable. */
- if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD)
- return 0;
+ struct mips_address_info addr;
+ int factor;
- if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address))
- || (GET_CODE (address) == CONST
- && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF
- && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0)))
- || GET_CODE (address) == LABEL_REF)
- return 1;
+ /* 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;
- return 0;
-}
+ if (mips_classify_address (&addr, x, mode, false))
+ switch (addr.type)
+ {
+ case ADDRESS_REG:
+ if (TARGET_MIPS16
+ && !mips16_unextended_reference_p (mode, addr.reg,
+ UINTVAL (addr.offset)))
+ return factor * 2;
+ return factor;
-/* This function is used to implement REG_MODE_OK_FOR_BASE_P. */
+ case ADDRESS_LO_SUM:
+ return TARGET_MIPS16 ? factor * 2 : factor;
-int
-mips_reg_mode_ok_for_base_p (reg, mode, strict)
- rtx reg;
- enum machine_mode mode;
- int strict;
-{
- return (strict
- ? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode)
- : GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));
+ case ADDRESS_CONST_INT:
+ return factor;
+
+ case ADDRESS_SYMBOLIC:
+ return factor * mips_symbol_insns (addr.symbol_type, mode);
+ }
+ return 0;
}
-/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It
- returns a nonzero value if XINSN is a legitimate address for a
- memory operand of the indicated MODE. STRICT is non-zero if this
- function is called during reload. */
+/* Return the number of instructions needed to load constant X.
+ Return 0 if X isn't a valid constant. */
int
-mips_legitimate_address_p (mode, xinsn, strict)
- enum machine_mode mode;
- rtx xinsn;
- int strict;
+mips_const_insns (rtx x)
{
- if (TARGET_DEBUG_B_MODE)
- {
- GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n",
- strict ? "" : "not ");
- GO_DEBUG_RTX (xinsn);
- }
+ struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+ enum mips_symbol_type symbol_type;
+ rtx offset;
- /* Check for constant before stripping off SUBREG, so that we don't
- accept (subreg (const_int)) which will fail to reload. */
- if (CONSTANT_ADDRESS_P (xinsn)
- && ! (mips_split_addresses && mips_check_split (xinsn, mode))
- && (! TARGET_MIPS16 || mips16_constant (xinsn, mode, 1, 0)))
- return 1;
-
- while (GET_CODE (xinsn) == SUBREG)
- xinsn = SUBREG_REG (xinsn);
-
- /* The mips16 can only use the stack pointer as a base register when
- loading SImode or DImode values. */
- if (GET_CODE (xinsn) == REG
- && mips_reg_mode_ok_for_base_p (xinsn, mode, strict))
- return 1;
-
- if (GET_CODE (xinsn) == LO_SUM && mips_split_addresses)
+ switch (GET_CODE (x))
{
- register rtx xlow0 = XEXP (xinsn, 0);
- register rtx xlow1 = XEXP (xinsn, 1);
-
- while (GET_CODE (xlow0) == SUBREG)
- xlow0 = SUBREG_REG (xlow0);
- if (GET_CODE (xlow0) == REG
- && mips_reg_mode_ok_for_base_p (xlow0, mode, strict)
- && mips_check_split (xlow1, mode))
- return 1;
- }
+ case HIGH:
+ if (!mips_symbolic_constant_p (XEXP (x, 0), SYMBOL_CONTEXT_LEA,
+ &symbol_type)
+ || !mips_split_p[symbol_type])
+ return 0;
- if (GET_CODE (xinsn) == PLUS)
- {
- register rtx xplus0 = XEXP (xinsn, 0);
- register rtx xplus1 = XEXP (xinsn, 1);
- register enum rtx_code code0;
- register enum rtx_code code1;
+ /* 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;
- while (GET_CODE (xplus0) == SUBREG)
- xplus0 = SUBREG_REG (xplus0);
- code0 = GET_CODE (xplus0);
+ case CONST_INT:
+ if (TARGET_MIPS16)
+ /* Unsigned 8-bit constants can be loaded using an unextended
+ LI instruction. Unsigned 16-bit constants can be loaded
+ using an extended LI. Negative constants must be loaded
+ using LI and then negated. */
+ return (IN_RANGE (INTVAL (x), 0, 255) ? 1
+ : SMALL_OPERAND_UNSIGNED (INTVAL (x)) ? 2
+ : IN_RANGE (-INTVAL (x), 0, 255) ? 2
+ : SMALL_OPERAND_UNSIGNED (-INTVAL (x)) ? 3
+ : 0);
+
+ return mips_build_integer (codes, INTVAL (x));
- while (GET_CODE (xplus1) == SUBREG)
- xplus1 = SUBREG_REG (xplus1);
- code1 = GET_CODE (xplus1);
+ case CONST_DOUBLE:
+ case CONST_VECTOR:
+ /* Allow zeros for normal mode, where we can use $0. */
+ return !TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0;
- /* The mips16 can only use the stack pointer as a base register
- when loading SImode or DImode values. */
- if (code0 == REG
- && mips_reg_mode_ok_for_base_p (xplus0, mode, strict))
- {
- if (code1 == CONST_INT && SMALL_INT (xplus1))
- return 1;
+ case CONST:
+ if (CONST_GP_P (x))
+ return 1;
- /* On the mips16, we represent GP relative offsets in RTL.
- These are 16 bit signed values, and can serve as register
- offsets. */
- if (TARGET_MIPS16
- && mips16_gp_offset_p (xplus1))
- return 1;
-
- /* For some code sequences, you actually get better code by
- pretending that the MIPS supports an address mode of a
- constant address + a register, even though the real
- machine doesn't support it. This is because the
- assembler can use $r1 to load just the high 16 bits, add
- in the register, and fold the low 16 bits into the memory
- reference, whereas the compiler generates a 4 instruction
- sequence. On the other hand, CSE is not as effective.
- It would be a win to generate the lui directly, but the
- MIPS assembler does not have syntax to generate the
- appropriate relocation. */
-
- /* Also accept CONST_INT addresses here, so no else. */
- /* Reject combining an embedded PIC text segment reference
- with a register. That requires an additional
- instruction. */
- /* ??? Reject combining an address with a register for the MIPS
- 64 bit ABI, because the SGI assembler can not handle this. */
- if (!TARGET_DEBUG_A_MODE
- && (mips_abi == ABI_32
- || mips_abi == ABI_O64
- || mips_abi == ABI_EABI)
- && CONSTANT_ADDRESS_P (xplus1)
- && ! mips_split_addresses
- && (!TARGET_EMBEDDED_PIC
- || code1 != CONST
- || GET_CODE (XEXP (xplus1, 0)) != MINUS)
- /* When assembling for machines with 64 bit registers,
- the assembler will sign-extend the constant "foo"
- in "la x, foo(x)" yielding the wrong result for:
- (set (blah:DI) (plus x y)). */
- && (!TARGET_64BIT
- || (code1 == CONST_INT
- && trunc_int_for_mode (INTVAL (xplus1),
- SImode) == INTVAL (xplus1)))
- && !TARGET_MIPS16)
- return 1;
+ /* See if we can refer to X directly. */
+ 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.
+ If the offset is a 16-bit value, we can load the base address
+ into a register and then use (D)ADDIU to add in the offset.
+ If the offset is larger, we can load the base and offset
+ into separate registers and add them together with (D)ADDU.
+ However, the latter is only possible before reload; during
+ and after reload, we must have the option of forcing the
+ constant into the pool instead. */
+ split_const (x, &x, &offset);
+ if (offset != 0)
+ {
+ int n = mips_const_insns (x);
+ if (n != 0)
+ {
+ if (SMALL_INT (offset))
+ return n + 1;
+ else if (!targetm.cannot_force_const_mem (x))
+ return n + 1 + mips_build_integer (codes, INTVAL (offset));
+ }
}
- }
+ return 0;
- if (TARGET_DEBUG_B_MODE)
- GO_PRINTF ("Not a legitimate address\n");
+ case SYMBOL_REF:
+ case LABEL_REF:
+ return mips_symbol_insns (mips_classify_symbol (x, SYMBOL_CONTEXT_LEA),
+ MAX_MACHINE_MODE);
- /* The address was not legitimate. */
- return 0;
+ default:
+ return 0;
+ }
}
-\f
-/* We need a lot of little routines to check constant values on the
- mips16. These are used to figure out how long the instruction will
- be. It would be much better to do this using constraints, but
- there aren't nearly enough letters available. */
-
-static int
-m16_check_op (op, low, high, mask)
- rtx op;
- int low;
- int high;
- int mask;
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= low
- && INTVAL (op) <= high
- && (INTVAL (op) & mask) == 0);
-}
+/* X is a doubleword constant that can be handled by splitting it into
+ two words and loading each word separately. Return the number of
+ instructions required to do this. */
int
-m16_uimm3_b (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_split_const_insns (rtx x)
{
- return m16_check_op (op, 0x1, 0x8, 0);
-}
+ unsigned int low, high;
-int
-m16_simm4_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, - 0x8, 0x7, 0);
+ low = mips_const_insns (mips_subword (x, false));
+ high = mips_const_insns (mips_subword (x, true));
+ gcc_assert (low > 0 && high > 0);
+ return low + high;
}
-int
-m16_nsimm4_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, - 0x7, 0x8, 0);
-}
+/* 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
-m16_simm5_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_load_store_insns (rtx mem, rtx insn)
{
- return m16_check_op (op, - 0x10, 0xf, 0);
-}
+ enum machine_mode mode;
+ bool might_split_p;
+ rtx set;
-int
-m16_nsimm5_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, - 0xf, 0x10, 0);
-}
+ gcc_assert (MEM_P (mem));
+ mode = GET_MODE (mem);
-int
-m16_uimm5_4 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3);
-}
+ /* 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;
+ }
-int
-m16_nuimm5_4 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3);
+ return mips_address_insns (XEXP (mem, 0), mode, might_split_p);
}
-int
-m16_simm8_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, - 0x80, 0x7f, 0);
-}
+/* Return the number of instructions needed for an integer division. */
int
-m16_nsimm8_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+mips_idiv_insns (void)
{
- return m16_check_op (op, - 0x7f, 0x80, 0);
-}
+ int count;
-int
-m16_uimm8_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, 0x0, 0xff, 0);
-}
+ count = 1;
+ if (TARGET_CHECK_ZERO_DIV)
+ {
+ if (GENERATE_DIVIDE_TRAPS)
+ count++;
+ else
+ count += 2;
+ }
-int
-m16_nuimm8_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, - 0xff, 0x0, 0);
+ if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+ count++;
+ return count;
}
+\f
+/* 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. */
-int
-m16_uimm8_m1_1 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+rtx
+mips_emit_move (rtx dest, rtx src)
{
- return m16_check_op (op, - 0x1, 0xfe, 0);
+ return (can_create_pseudo_p ()
+ ? emit_move_insn (dest, src)
+ : emit_move_insn_1 (dest, src));
}
-int
-m16_uimm8_4 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return m16_check_op (op, 0x0, 0xff << 2, 3);
-}
+/* Emit an instruction of the form (set TARGET (CODE OP0)). */
-int
-m16_nuimm8_4 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static void
+mips_emit_unary (enum rtx_code code, rtx target, rtx op0)
{
- return m16_check_op (op, (- 0xff) << 2, 0x0, 3);
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_fmt_e (code, GET_MODE (op0), op0)));
}
-int
-m16_simm8_8 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Compute (CODE OP0) and store the result in a new register of mode MODE.
+ Return that new register. */
+
+static rtx
+mips_force_unary (enum machine_mode mode, enum rtx_code code, rtx op0)
{
- return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7);
+ rtx reg;
+
+ reg = gen_reg_rtx (mode);
+ mips_emit_unary (code, reg, op0);
+ return reg;
}
-int
-m16_nsimm8_8 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Emit an instruction of the form (set TARGET (CODE OP0 OP1)). */
+
+static void
+mips_emit_binary (enum rtx_code code, rtx target, rtx op0, rtx op1)
{
- return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);
+ emit_insn (gen_rtx_SET (VOIDmode, target,
+ gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
}
-/* References to the string table on the mips16 only use a small
- offset if the function is small. See the comment in the SYMBOL_REF
- case in simple_memory_operand. We can't check for LABEL_REF here,
- because the offset is always large if the label is before the
- referencing instruction. */
+/* Compute (CODE OP0 OP1) and store the result in a new register
+ of mode MODE. Return that new register. */
-int
-m16_usym8_4 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static rtx
+mips_force_binary (enum machine_mode mode, enum rtx_code code, rtx op0, rtx op1)
{
- if (GET_CODE (op) == SYMBOL_REF
- && SYMBOL_REF_FLAG (op)
- && current_frame_info.insns_len > 0
- && XSTR (op, 0)[0] == '*'
- && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
- sizeof LOCAL_LABEL_PREFIX - 1) == 0
- && (current_frame_info.insns_len + get_pool_size () + mips_string_length
- < 4 * 0x100))
- {
- struct string_constant *l;
-
- /* Make sure this symbol is on thelist of string constants to be
- output for this function. It is possible that it has already
- been output, in which case this requires a large offset. */
- for (l = string_constants; l != NULL; l = l->next)
- if (strcmp (l->label, XSTR (op, 0)) == 0)
- return 1;
- }
+ rtx reg;
- return 0;
+ reg = gen_reg_rtx (mode);
+ mips_emit_binary (code, reg, op0, op1);
+ return reg;
}
-int
-m16_usym5_4 (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* Copy VALUE to a register and return that register. If new pseudos
+ are allowed, copy it into a new register, otherwise use DEST. */
+
+static rtx
+mips_force_temporary (rtx dest, rtx value)
{
- if (GET_CODE (op) == SYMBOL_REF
- && SYMBOL_REF_FLAG (op)
- && current_frame_info.insns_len > 0
- && XSTR (op, 0)[0] == '*'
- && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX,
- sizeof LOCAL_LABEL_PREFIX - 1) == 0
- && (current_frame_info.insns_len + get_pool_size () + mips_string_length
- < 4 * 0x20))
+ if (can_create_pseudo_p ())
+ return force_reg (Pmode, value);
+ else
{
- struct string_constant *l;
-
- /* Make sure this symbol is on thelist of string constants to be
- output for this function. It is possible that it has already
- been output, in which case this requires a large offset. */
- for (l = string_constants; l != NULL; l = l->next)
- if (strcmp (l->label, XSTR (op, 0)) == 0)
- return 1;
+ mips_emit_move (dest, value);
+ return dest;
}
-
- return 0;
}
-\f
-/* Returns an operand string for the given instruction's delay slot,
- after updating filled delay slot statistics.
-
- We assume that operands[0] is the target register that is set.
- In order to check the next insn, most of this functionality is moved
- to FINAL_PRESCAN_INSN, and we just set the global variables that
- it needs. */
+/* Emit a call sequence with call pattern PATTERN and return the call
+ instruction itself (which is not necessarily the last instruction
+ emitted). ORIG_ADDR is the original, unlegitimized address,
+ ADDR is the legitimized form, and LAZY_P is true if the call
+ address is lazily-bound. */
-/* ??? This function no longer does anything useful, because final_prescan_insn
- now will never emit a nop. */
-
-const char *
-mips_fill_delay_slot (ret, type, operands, cur_insn)
- const char *ret; /* normal string to return */
- enum delay_type type; /* type of delay */
- rtx operands[]; /* operands to use */
- rtx cur_insn; /* current insn */
+static rtx
+mips_emit_call_insn (rtx pattern, rtx orig_addr, rtx addr, bool lazy_p)
{
- register rtx set_reg;
- register enum machine_mode mode;
- register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX;
- register int num_nops;
+ rtx insn, reg;
- if (type == DELAY_LOAD || type == DELAY_FCMP)
- num_nops = 1;
+ insn = emit_call_insn (pattern);
- else if (type == DELAY_HILO)
- num_nops = 2;
+ if (TARGET_MIPS16 && mips_use_pic_fn_addr_reg_p (orig_addr))
+ {
+ /* MIPS16 JALRs only take MIPS16 registers. If the target
+ function requires $25 to be valid on entry, we must copy it
+ there separately. The move instruction can be put in the
+ call's delay slot. */
+ reg = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ emit_insn_before (gen_move_insn (reg, addr), insn);
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+ }
- else
- num_nops = 0;
-
- /* Make sure that we don't put nop's after labels. */
- next_insn = NEXT_INSN (cur_insn);
- while (next_insn != 0 && GET_CODE (next_insn) == NOTE)
- next_insn = NEXT_INSN (next_insn);
-
- dslots_load_total += num_nops;
- if (TARGET_DEBUG_F_MODE
- || !optimize
- || type == DELAY_NONE
- || operands == 0
- || cur_insn == 0
- || next_insn == 0
- || GET_CODE (next_insn) == CODE_LABEL
- || (set_reg = operands[0]) == 0)
- {
- dslots_number_nops = 0;
- mips_load_reg = 0;
- mips_load_reg2 = 0;
- mips_load_reg3 = 0;
- mips_load_reg4 = 0;
- return ret;
- }
-
- set_reg = operands[0];
- if (set_reg == 0)
- return ret;
-
- while (GET_CODE (set_reg) == SUBREG)
- set_reg = SUBREG_REG (set_reg);
-
- mode = GET_MODE (set_reg);
- dslots_number_nops = num_nops;
- mips_load_reg = set_reg;
- if (GET_MODE_SIZE (mode)
- > (unsigned) (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD))
- mips_load_reg2 = gen_rtx_REG (SImode, REGNO (set_reg) + 1);
- else
- mips_load_reg2 = 0;
+ if (lazy_p)
+ /* Lazy-binding stubs require $gp to be valid on entry. */
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
- if (type == DELAY_HILO)
+ if (TARGET_USE_GOT)
{
- mips_load_reg3 = gen_rtx_REG (SImode, MD_REG_FIRST);
- mips_load_reg4 = gen_rtx_REG (SImode, MD_REG_FIRST+1);
+ /* See the comment above load_call<mode> for details. */
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn),
+ gen_rtx_REG (Pmode, GOT_VERSION_REGNUM));
+ emit_insn (gen_update_got_version ());
}
- else
- {
- mips_load_reg3 = 0;
- mips_load_reg4 = 0;
- }
-
- return ret;
+ return insn;
}
-
\f
-/* Determine whether a memory reference takes one (based off of the GP
- pointer), two (normal), or three (label + reg) instructions, and bump the
- appropriate counter for -mstats. */
+/* Wrap symbol or label BASE in an UNSPEC address of type SYMBOL_TYPE,
+ then add CONST_INT OFFSET to the result. */
-void
-mips_count_memory_refs (op, num)
- rtx op;
- int num;
+static rtx
+mips_unspec_address_offset (rtx base, rtx offset,
+ enum mips_symbol_type symbol_type)
{
- int additional = 0;
- int n_words = 0;
- rtx addr, plus0, plus1;
- enum rtx_code code0, code1;
- int looping;
+ 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);
+}
- if (TARGET_DEBUG_B_MODE)
- {
- fprintf (stderr, "\n========== mips_count_memory_refs:\n");
- debug_rtx (op);
- }
+/* Return an UNSPEC address with underlying address ADDRESS and symbol
+ type SYMBOL_TYPE. */
- /* Skip MEM if passed, otherwise handle movsi of address. */
- addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0);
+rtx
+mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
+{
+ rtx base, offset;
- /* Loop, going through the address RTL. */
- do
- {
- looping = FALSE;
- switch (GET_CODE (addr))
- {
- case REG:
- case CONST_INT:
- case LO_SUM:
- break;
+ split_const (address, &base, &offset);
+ return mips_unspec_address_offset (base, offset, symbol_type);
+}
- case PLUS:
- plus0 = XEXP (addr, 0);
- plus1 = XEXP (addr, 1);
- code0 = GET_CODE (plus0);
- code1 = GET_CODE (plus1);
+/* If mips_unspec_address (ADDR, SYMBOL_TYPE) is a 32-bit value, add the
+ high part to BASE and return the result. Just return BASE otherwise.
+ TEMP is as for mips_force_temporary.
- if (code0 == REG)
- {
- additional++;
- addr = plus1;
- looping = 1;
- continue;
- }
+ The returned expression can be used as the first operand to a LO_SUM. */
- if (code0 == CONST_INT)
- {
- addr = plus1;
- looping = 1;
- continue;
- }
+static rtx
+mips_unspec_offset_high (rtx temp, rtx base, rtx addr,
+ enum mips_symbol_type symbol_type)
+{
+ if (mips_split_p[symbol_type])
+ {
+ addr = gen_rtx_HIGH (Pmode, mips_unspec_address (addr, symbol_type));
+ addr = mips_force_temporary (temp, addr);
+ base = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
+ }
+ return base;
+}
+\f
+/* Return an instruction that copies $gp into register REG. We want
+ GCC to treat the register's value as constant, so that its value
+ can be rematerialized on demand. */
- if (code1 == REG)
- {
- additional++;
- addr = plus0;
- looping = 1;
- continue;
- }
+static rtx
+gen_load_const_gp (rtx reg)
+{
+ return (Pmode == SImode
+ ? gen_load_const_gp_si (reg)
+ : gen_load_const_gp_di (reg));
+}
- if (code1 == CONST_INT)
- {
- addr = plus0;
- looping = 1;
- continue;
- }
+/* Return a pseudo register that contains the value of $gp throughout
+ the current function. Such registers are needed by MIPS16 functions,
+ for which $gp itself is not a valid base register or addition operand. */
- if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST)
- {
- addr = plus0;
- looping = 1;
- continue;
- }
+static rtx
+mips16_gp_pseudo_reg (void)
+{
+ if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
+ cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
- if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST)
- {
- addr = plus1;
- looping = 1;
- continue;
- }
+ /* Don't emit an instruction to 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 || currently_expanding_to_rtl))
+ {
+ rtx insn, scan;
- break;
+ push_topmost_sequence ();
- case LABEL_REF:
- n_words = 2; /* always 2 words */
- break;
+ scan = get_insns ();
+ while (NEXT_INSN (scan) && !INSN_P (NEXT_INSN (scan)))
+ scan = NEXT_INSN (scan);
- case CONST:
- addr = XEXP (addr, 0);
- looping = 1;
- continue;
+ insn = gen_load_const_gp (cfun->machine->mips16_gp_pseudo_rtx);
+ emit_insn_after (insn, scan);
- case SYMBOL_REF:
- n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2;
- break;
+ pop_topmost_sequence ();
- default:
- break;
- }
+ cfun->machine->initialized_mips16_gp_pseudo_p = true;
}
- while (looping);
-
- if (n_words == 0)
- return;
-
- n_words += additional;
- if (n_words > 3)
- n_words = 3;
- num_refs[n_words-1] += num;
+ return cfun->machine->mips16_gp_pseudo_rtx;
}
-\f
-/* Return RTL for the offset from the current function to the argument.
-
- ??? Which argument is this? */
+/* Return a base register that holds pic_offset_table_rtx.
+ TEMP, if nonnull, is a scratch Pmode base register. */
rtx
-embedded_pic_offset (x)
- rtx x;
-{
- if (embedded_pic_fnaddr_rtx == NULL)
- {
- rtx seq;
-
- embedded_pic_fnaddr_rtx = gen_reg_rtx (Pmode);
-
- /* Output code at function start to initialize the pseudo-reg. */
- /* ??? We used to do this in FINALIZE_PIC, but that does not work for
- inline functions, because it is called after RTL for the function
- has been copied. The pseudo-reg in embedded_pic_fnaddr_rtx however
- does not get copied, and ends up not matching the rest of the RTL.
- This solution works, but means that we get unnecessary code to
- initialize this value every time a function is inlined into another
- function. */
- start_sequence ();
- emit_insn (gen_get_fnaddr (embedded_pic_fnaddr_rtx,
- XEXP (DECL_RTL (current_function_decl), 0)));
- seq = gen_sequence ();
- end_sequence ();
- push_topmost_sequence ();
- emit_insn_after (seq, get_insns ());
- pop_topmost_sequence ();
- }
+mips_pic_base_register (rtx temp)
+{
+ if (!TARGET_MIPS16)
+ return pic_offset_table_rtx;
- return
- gen_rtx_CONST (Pmode,
- gen_rtx_MINUS (Pmode, x,
- XEXP (DECL_RTL (current_function_decl), 0)));
-}
+ if (can_create_pseudo_p ())
+ return mips16_gp_pseudo_reg ();
-/* Return the appropriate instructions to move one operand to another. */
+ if (TARGET_USE_GOT)
+ /* The first post-reload split exposes all references to $gp
+ (both uses and definitions). All references must remain
+ explicit after that point.
-const char *
-mips_move_1word (operands, insn, unsignedp)
- rtx operands[];
- rtx insn;
- int unsignedp;
-{
- const char *ret = 0;
- rtx op0 = operands[0];
- rtx op1 = operands[1];
- enum rtx_code code0 = GET_CODE (op0);
- enum rtx_code code1 = GET_CODE (op1);
- enum machine_mode mode = GET_MODE (op0);
- int subreg_offset0 = 0;
- int subreg_offset1 = 0;
- enum delay_type delay = DELAY_NONE;
-
- while (code0 == SUBREG)
- {
- subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
- GET_MODE (SUBREG_REG (op0)),
- SUBREG_BYTE (op0),
- GET_MODE (op0));
- op0 = SUBREG_REG (op0);
- code0 = GET_CODE (op0);
- }
-
- while (code1 == SUBREG)
- {
- subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
- GET_MODE (SUBREG_REG (op1)),
- SUBREG_BYTE (op1),
- GET_MODE (op1));
- op1 = SUBREG_REG (op1);
- code1 = GET_CODE (op1);
- }
-
- /* For our purposes, a condition code mode is the same as SImode. */
- if (mode == CCmode)
- mode = SImode;
+ It is safe to introduce uses of $gp at any time, so for
+ simplicity, we do that before the split too. */
+ mips_emit_move (temp, pic_offset_table_rtx);
+ else
+ emit_insn (gen_load_const_gp (temp));
+ return temp;
+}
- if (code0 == REG)
- {
- int regno0 = REGNO (op0) + subreg_offset0;
+/* Create and return a GOT reference of type TYPE for address ADDR.
+ TEMP, if nonnull, is a scratch Pmode base register. */
- if (code1 == REG)
- {
- int regno1 = REGNO (op1) + subreg_offset1;
+rtx
+mips_got_load (rtx temp, rtx addr, enum mips_symbol_type type)
+{
+ rtx base, high, lo_sum_symbol;
- /* Just in case, don't do anything for assigning a register
- to itself, unless we are filling a delay slot. */
- if (regno0 == regno1 && set_nomacro == 0)
- ret = "";
+ base = mips_pic_base_register (temp);
- else if (GP_REG_P (regno0))
- {
- if (GP_REG_P (regno1))
- ret = "move\t%0,%1";
+ /* If we used the temporary register to load $gp, we can't use
+ it for the high part as well. */
+ if (temp != NULL && reg_overlap_mentioned_p (base, temp))
+ temp = NULL;
- else if (MD_REG_P (regno1))
- {
- delay = DELAY_HILO;
- if (regno1 != HILO_REGNUM)
- ret = "mf%1\t%0";
- else
- ret = "mflo\t%0";
- }
+ high = mips_unspec_offset_high (temp, base, addr, type);
+ lo_sum_symbol = mips_unspec_address (addr, type);
- else if (ST_REG_P (regno1) && ISA_HAS_8CC)
- ret = "li\t%0,1\n\tmovf\t%0,%.,%1";
+ if (type == SYMBOL_GOTOFF_CALL)
+ return (Pmode == SImode
+ ? gen_unspec_callsi (high, lo_sum_symbol)
+ : gen_unspec_calldi (high, lo_sum_symbol));
+ else
+ return (Pmode == SImode
+ ? gen_unspec_gotsi (high, lo_sum_symbol)
+ : gen_unspec_gotdi (high, lo_sum_symbol));
+}
- else
- {
- delay = DELAY_LOAD;
- if (FP_REG_P (regno1))
- ret = "mfc1\t%0,%1";
+/* 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 high and low parts.
+ If so, and if LOW_OUT is nonnull, emit the high part and store the
+ low part in *LOW_OUT. Leave *LOW_OUT unchanged otherwise.
- else if (regno1 == FPSW_REGNUM && ! ISA_HAS_8CC)
- ret = "cfc1\t%0,$31";
- }
- }
+ TEMP is as for mips_force_temporary and is used to load the high
+ part into a register.
- else if (FP_REG_P (regno0))
- {
- if (GP_REG_P (regno1))
- {
- delay = DELAY_LOAD;
- ret = "mtc1\t%1,%0";
- }
+ When MODE is MAX_MACHINE_MODE, the low part is guaranteed to be
+ a legitimize SET_SRC for an .md pattern, otherwise the low part
+ is guaranteed to be a legitimate address for mode MODE. */
- if (FP_REG_P (regno1))
- ret = "mov.s\t%0,%1";
- }
+bool
+mips_split_symbol (rtx temp, rtx addr, enum machine_mode mode, rtx *low_out)
+{
+ enum mips_symbol_context context;
+ enum mips_symbol_type symbol_type;
+ rtx high;
- else if (MD_REG_P (regno0))
- {
- if (GP_REG_P (regno1))
- {
- delay = DELAY_HILO;
- if (regno0 != HILO_REGNUM && ! TARGET_MIPS16)
- ret = "mt%0\t%1";
- }
- }
+ context = (mode == MAX_MACHINE_MODE
+ ? SYMBOL_CONTEXT_LEA
+ : SYMBOL_CONTEXT_MEM);
+ if (GET_CODE (addr) == HIGH && context == SYMBOL_CONTEXT_LEA)
+ {
+ addr = XEXP (addr, 0);
+ if (mips_symbolic_constant_p (addr, context, &symbol_type)
+ && mips_symbol_insns (symbol_type, mode) > 0
+ && mips_split_hi_p[symbol_type])
+ {
+ if (low_out)
+ switch (symbol_type)
+ {
+ case SYMBOL_GOT_PAGE_OFST:
+ /* The high part of a page/ofst pair is loaded from the GOT. */
+ *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_PAGE);
+ break;
- else if (regno0 == FPSW_REGNUM && ! ISA_HAS_8CC)
- {
- if (GP_REG_P (regno1))
- {
- delay = DELAY_LOAD;
- ret = "ctc1\t%0,$31";
- }
- }
+ default:
+ gcc_unreachable ();
+ }
+ return true;
}
-
- else if (code1 == MEM)
+ }
+ else
+ {
+ if (mips_symbolic_constant_p (addr, context, &symbol_type)
+ && mips_symbol_insns (symbol_type, mode) > 0
+ && mips_split_p[symbol_type])
{
- delay = DELAY_LOAD;
-
- if (TARGET_STATS)
- mips_count_memory_refs (op1, 1);
-
- if (GP_REG_P (regno0))
- {
- /* For loads, use the mode of the memory item, instead of the
- target, so zero/sign extend can use this code as well. */
- switch (GET_MODE (op1))
- {
- default:
- break;
- case SFmode:
- ret = "lw\t%0,%1";
- break;
- case SImode:
- case CCmode:
- ret = ((unsignedp && TARGET_64BIT)
- ? "lwu\t%0,%1"
- : "lw\t%0,%1");
- break;
- case HImode:
- ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1";
- break;
- case QImode:
- ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1";
- break;
- }
- }
+ if (low_out)
+ switch (symbol_type)
+ {
+ case SYMBOL_GOT_DISP:
+ /* SYMBOL_GOT_DISP symbols are loaded from the GOT. */
+ *low_out = mips_got_load (temp, addr, SYMBOL_GOTOFF_DISP);
+ break;
+
+ case SYMBOL_GP_RELATIVE:
+ high = mips_pic_base_register (temp);
+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+ break;
+
+ default:
+ high = gen_rtx_HIGH (Pmode, copy_rtx (addr));
+ high = mips_force_temporary (temp, high);
+ *low_out = gen_rtx_LO_SUM (Pmode, high, addr);
+ break;
+ }
+ return true;
+ }
+ }
+ return false;
+}
- else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode))
- ret = "l.s\t%0,%1";
+/* Return a legitimate address for REG + OFFSET. TEMP is as for
+ mips_force_temporary; it is only needed when OFFSET is not a
+ SMALL_OPERAND. */
- if (ret != (char *)0 && MEM_VOLATILE_P (op1))
- {
- size_t i = strlen (ret);
- if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
- abort ();
+static rtx
+mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
+{
+ if (!SMALL_OPERAND (offset))
+ {
+ rtx high;
- sprintf (volatile_buffer, "%%{%s%%}", ret);
- ret = volatile_buffer;
- }
+ if (TARGET_MIPS16)
+ {
+ /* Load the full offset into a register so that we can use
+ an unextended instruction for the address itself. */
+ high = GEN_INT (offset);
+ offset = 0;
}
-
- else if (code1 == CONST_INT
- || (code1 == CONST_DOUBLE
- && GET_MODE (op1) == VOIDmode))
+ else
{
- if (code1 == CONST_DOUBLE)
- {
- /* This can happen when storing constants into long long
- bitfields. Just store the least significant word of
- the value. */
- operands[1] = op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
- }
+ /* Leave OFFSET as a 16-bit offset and put the excess in HIGH. */
+ high = GEN_INT (CONST_HIGH_PART (offset));
+ offset = CONST_LOW_PART (offset);
+ }
+ high = mips_force_temporary (temp, high);
+ reg = mips_force_temporary (temp, gen_rtx_PLUS (Pmode, high, reg));
+ }
+ return plus_constant (reg, offset);
+}
+\f
+/* The __tls_get_attr symbol. */
+static GTY(()) rtx mips_tls_symbol;
- if (INTVAL (op1) == 0 && ! TARGET_MIPS16)
- {
- if (GP_REG_P (regno0))
- ret = "move\t%0,%z1";
+/* Return an instruction sequence that calls __tls_get_addr. SYM is
+ the TLS symbol we are referencing and TYPE is the symbol type to use
+ (either global dynamic or local dynamic). V0 is an RTX for the
+ return value location. */
- else if (FP_REG_P (regno0))
- {
- delay = DELAY_LOAD;
- ret = "mtc1\t%z1,%0";
- }
+static rtx
+mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
+{
+ rtx insn, loc, a0;
- else if (MD_REG_P (regno0))
- {
- delay = DELAY_HILO;
- ret = "mt%0\t%.";
- }
- }
+ a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
- else if (GP_REG_P (regno0))
- {
- /* Don't use X format, because that will give out of
- range numbers for 64 bit host and 32 bit target. */
- if (! TARGET_MIPS16)
- ret = "li\t%0,%1\t\t\t# %X1";
- else
- {
- if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
- ret = "li\t%0,%1";
- else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
- ret = "li\t%0,%n1\n\tneg\t%0";
- }
- }
- }
+ if (!mips_tls_symbol)
+ mips_tls_symbol = init_one_libfunc ("__tls_get_addr");
- else if (code1 == CONST_DOUBLE && mode == SFmode)
- {
- if (op1 == CONST0_RTX (SFmode))
- {
- if (GP_REG_P (regno0))
- ret = "move\t%0,%.";
+ loc = mips_unspec_address (sym, type);
- else if (FP_REG_P (regno0))
- {
- delay = DELAY_LOAD;
- ret = "mtc1\t%.,%0";
- }
- }
+ start_sequence ();
- else
- {
- delay = DELAY_LOAD;
- ret = "li.s\t%0,%1";
- }
- }
+ emit_insn (gen_rtx_SET (Pmode, a0,
+ gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
+ insn = mips_expand_call (MIPS_CALL_NORMAL, v0, mips_tls_symbol,
+ const0_rtx, NULL_RTX, false);
+ RTL_CONST_CALL_P (insn) = 1;
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
+ insn = get_insns ();
- else if (code1 == LABEL_REF)
- {
- if (TARGET_STATS)
- mips_count_memory_refs (op1, 1);
+ end_sequence ();
- ret = "la\t%0,%a1";
- }
+ return insn;
+}
- else if (code1 == SYMBOL_REF || code1 == CONST)
- {
- if (HALF_PIC_P () && CONSTANT_P (op1) && HALF_PIC_ADDRESS_P (op1))
- {
- rtx offset = const0_rtx;
+/* Return a pseudo register that contains the current thread pointer. */
- if (GET_CODE (op1) == CONST)
- op1 = eliminate_constant_term (XEXP (op1, 0), &offset);
+static rtx
+mips_get_tp (void)
+{
+ rtx tp;
- if (GET_CODE (op1) == SYMBOL_REF)
- {
- operands[2] = HALF_PIC_PTR (op1);
+ tp = gen_reg_rtx (Pmode);
+ if (Pmode == DImode)
+ emit_insn (gen_tls_get_tp_di (tp));
+ else
+ emit_insn (gen_tls_get_tp_si (tp));
+ return tp;
+}
- if (TARGET_STATS)
- mips_count_memory_refs (operands[2], 1);
+/* Generate the code to access LOC, a thread-local SYMBOL_REF, and return
+ its address. The return value will be both a valid address and a valid
+ SET_SRC (either a REG or a LO_SUM). */
- if (INTVAL (offset) == 0)
- {
- delay = DELAY_LOAD;
- ret = (unsignedp && TARGET_64BIT
- ? "lwu\t%0,%2"
- : "lw\t%0,%2");
- }
- else
- {
- dslots_load_total++;
- operands[3] = offset;
- if (unsignedp && TARGET_64BIT)
- ret = (SMALL_INT (offset)
- ? "lwu\t%0,%2%#\n\tadd\t%0,%0,%3"
- : "lwu\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]");
- else
- ret = (SMALL_INT (offset)
- ? "lw\t%0,%2%#\n\tadd\t%0,%0,%3"
- : "lw\t%0,%2%#\n\t%[li\t%@,%3\n\tadd\t%0,%0,%@%]");
- }
- }
- }
- else if (TARGET_MIPS16
- && code1 == CONST
- && GET_CODE (XEXP (op1, 0)) == REG
- && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
- {
- /* This case arises on the mips16; see
- mips16_gp_pseudo_reg. */
- ret = "move\t%0,%+";
- }
- else if (TARGET_MIPS16
- && code1 == SYMBOL_REF
- && SYMBOL_REF_FLAG (op1)
- && (XSTR (op1, 0)[0] != '*'
- || strncmp (XSTR (op1, 0) + 1,
- LOCAL_LABEL_PREFIX,
- sizeof LOCAL_LABEL_PREFIX - 1) != 0))
- {
- /* This can occur when reloading the address of a GP
- relative symbol on the mips16. */
- ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
- }
- else
- {
- if (TARGET_STATS)
- mips_count_memory_refs (op1, 1);
+static rtx
+mips_legitimize_tls_address (rtx loc)
+{
+ rtx dest, insn, v0, tp, tmp1, tmp2, eqv;
+ enum tls_model model;
- ret = "la\t%0,%a1";
- }
- }
+ if (TARGET_MIPS16)
+ {
+ sorry ("MIPS16 TLS");
+ return gen_reg_rtx (Pmode);
+ }
- else if (code1 == PLUS)
- {
- rtx add_op0 = XEXP (op1, 0);
- rtx add_op1 = XEXP (op1, 1);
+ model = SYMBOL_REF_TLS_MODEL (loc);
+ /* Only TARGET_ABICALLS code can have more than one module; other
+ code must be be static and should not use a GOT. All TLS models
+ reduce to local exec in this situation. */
+ if (!TARGET_ABICALLS)
+ model = TLS_MODEL_LOCAL_EXEC;
- if (GET_CODE (XEXP (op1, 1)) == REG
- && GET_CODE (XEXP (op1, 0)) == CONST_INT)
- add_op0 = XEXP (op1, 1), add_op1 = XEXP (op1, 0);
+ switch (model)
+ {
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ v0 = gen_rtx_REG (Pmode, GP_RETURN);
+ insn = mips_call_tls_get_addr (loc, SYMBOL_TLSGD, v0);
+ dest = gen_reg_rtx (Pmode);
+ emit_libcall_block (insn, dest, v0, loc);
+ break;
- operands[2] = add_op0;
- operands[3] = add_op1;
- ret = "add%:\t%0,%2,%3";
- }
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ v0 = gen_rtx_REG (Pmode, GP_RETURN);
+ insn = mips_call_tls_get_addr (loc, SYMBOL_TLSLDM, v0);
+ tmp1 = gen_reg_rtx (Pmode);
- else if (code1 == HIGH)
- {
- operands[1] = XEXP (op1, 0);
- ret = "lui\t%0,%%hi(%1)";
- }
- }
+ /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+ share the LDM result with other LD model accesses. */
+ eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+ UNSPEC_TLS_LDM);
+ emit_libcall_block (insn, tmp1, v0, eqv);
- else if (code0 == MEM)
- {
- if (TARGET_STATS)
- mips_count_memory_refs (op0, 1);
+ tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL);
+ dest = gen_rtx_LO_SUM (Pmode, tmp2,
+ mips_unspec_address (loc, SYMBOL_DTPREL));
+ break;
- if (code1 == REG)
- {
- int regno1 = REGNO (op1) + subreg_offset1;
+ case TLS_MODEL_INITIAL_EXEC:
+ tp = mips_get_tp ();
+ tmp1 = gen_reg_rtx (Pmode);
+ tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL);
+ if (Pmode == DImode)
+ emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2));
+ else
+ emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2));
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_add3_insn (dest, tmp1, tp));
+ break;
- if (GP_REG_P (regno1))
- {
- switch (mode)
- {
- case SFmode: ret = "sw\t%1,%0"; break;
- case SImode: ret = "sw\t%1,%0"; break;
- case HImode: ret = "sh\t%1,%0"; break;
- case QImode: ret = "sb\t%1,%0"; break;
- default: break;
- }
- }
+ case TLS_MODEL_LOCAL_EXEC:
+ tp = mips_get_tp ();
+ tmp1 = mips_unspec_offset_high (NULL, tp, loc, SYMBOL_TPREL);
+ dest = gen_rtx_LO_SUM (Pmode, tmp1,
+ mips_unspec_address (loc, SYMBOL_TPREL));
+ break;
- else if (FP_REG_P (regno1) && (mode == SImode || mode == SFmode))
- ret = "s.s\t%1,%0";
- }
+ default:
+ gcc_unreachable ();
+ }
+ return dest;
+}
+\f
+/* If X is not a valid address for mode MODE, force it into a register. */
- else if (code1 == CONST_INT && INTVAL (op1) == 0)
- {
- switch (mode)
- {
- case SFmode: ret = "sw\t%z1,%0"; break;
- case SImode: ret = "sw\t%z1,%0"; break;
- case HImode: ret = "sh\t%z1,%0"; break;
- case QImode: ret = "sb\t%z1,%0"; break;
- default: break;
- }
- }
+static rtx
+mips_force_address (rtx x, enum machine_mode mode)
+{
+ if (!mips_legitimate_address_p (mode, x, false))
+ x = force_reg (Pmode, x);
+ return x;
+}
- else if (code1 == CONST_DOUBLE && op1 == CONST0_RTX (mode))
- {
- switch (mode)
- {
- case SFmode: ret = "sw\t%.,%0"; break;
- case SImode: ret = "sw\t%.,%0"; break;
- case HImode: ret = "sh\t%.,%0"; break;
- case QImode: ret = "sb\t%.,%0"; break;
- default: break;
- }
- }
+/* This function is used to implement LEGITIMIZE_ADDRESS. If *XLOC can
+ be legitimized in a way that the generic machinery might not expect,
+ put the new address in *XLOC and return true. MODE is the mode of
+ the memory being accessed. */
- if (ret != 0 && MEM_VOLATILE_P (op0))
- {
- size_t i = strlen (ret);
+bool
+mips_legitimize_address (rtx *xloc, enum machine_mode mode)
+{
+ rtx base, addr;
+ HOST_WIDE_INT offset;
- if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
- abort ();
+ if (mips_tls_symbol_p (*xloc))
+ {
+ *xloc = mips_legitimize_tls_address (*xloc);
+ return true;
+ }
- sprintf (volatile_buffer, "%%{%s%%}", ret);
- ret = volatile_buffer;
- }
+ /* See if the address can split into a high part and a LO_SUM. */
+ if (mips_split_symbol (NULL, *xloc, mode, &addr))
+ {
+ *xloc = mips_force_address (addr, mode);
+ return true;
}
- if (ret == 0)
+ /* Handle BASE + OFFSET using mips_add_offset. */
+ mips_split_plus (*xloc, &base, &offset);
+ if (offset != 0)
{
- abort_with_insn (insn, "bad move");
- return 0;
+ if (!mips_valid_base_register_p (base, mode, false))
+ base = copy_to_mode_reg (Pmode, base);
+ addr = mips_add_offset (NULL, base, offset);
+ *xloc = mips_force_address (addr, mode);
+ return true;
}
+ return false;
+}
+
+/* Load VALUE into DEST. TEMP is as for mips_force_temporary. */
+
+void
+mips_move_integer (rtx temp, rtx dest, unsigned HOST_WIDE_INT value)
+{
+ struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
+ enum machine_mode mode;
+ unsigned int i, num_ops;
+ rtx x;
- if (delay != DELAY_NONE)
- return mips_fill_delay_slot (ret, delay, operands, insn);
+ mode = GET_MODE (dest);
+ num_ops = mips_build_integer (codes, value);
- return ret;
+ /* Apply each binary operation to X. Invariant: X is a legitimate
+ source operand for a SET pattern. */
+ x = GEN_INT (codes[0].value);
+ for (i = 1; i < num_ops; i++)
+ {
+ if (!can_create_pseudo_p ())
+ {
+ emit_insn (gen_rtx_SET (VOIDmode, temp, x));
+ x = temp;
+ }
+ else
+ x = force_reg (mode, x);
+ x = gen_rtx_fmt_ee (codes[i].code, mode, x, GEN_INT (codes[i].value));
+ }
+
+ emit_insn (gen_rtx_SET (VOIDmode, dest, x));
}
-\f
-/* Return the appropriate instructions to move 2 words */
+/* Subroutine of mips_legitimize_move. Move constant SRC into register
+ DEST given that SRC satisfies immediate_operand but doesn't satisfy
+ move_operand. */
-const char *
-mips_move_2words (operands, insn)
- rtx operands[];
- rtx insn;
+static void
+mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
{
- const char *ret = 0;
- rtx op0 = operands[0];
- rtx op1 = operands[1];
- enum rtx_code code0 = GET_CODE (operands[0]);
- enum rtx_code code1 = GET_CODE (operands[1]);
- int subreg_offset0 = 0;
- int subreg_offset1 = 0;
- enum delay_type delay = DELAY_NONE;
+ rtx base, offset;
- while (code0 == SUBREG)
+ /* Split moves of big integers into smaller pieces. */
+ if (splittable_const_int_operand (src, mode))
{
- subreg_offset0 += subreg_regno_offset (REGNO (SUBREG_REG (op0)),
- GET_MODE (SUBREG_REG (op0)),
- SUBREG_BYTE (op0),
- GET_MODE (op0));
- op0 = SUBREG_REG (op0);
- code0 = GET_CODE (op0);
+ mips_move_integer (dest, dest, INTVAL (src));
+ return;
}
- if (code1 == SIGN_EXTEND)
+ /* Split moves of symbolic constants into high/low pairs. */
+ if (mips_split_symbol (dest, src, MAX_MACHINE_MODE, &src))
{
- op1 = XEXP (op1, 0);
- code1 = GET_CODE (op1);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, src));
+ return;
}
- while (code1 == SUBREG)
+ /* Generate the appropriate access sequences for TLS symbols. */
+ if (mips_tls_symbol_p (src))
{
- subreg_offset1 += subreg_regno_offset (REGNO (SUBREG_REG (op1)),
- GET_MODE (SUBREG_REG (op1)),
- SUBREG_BYTE (op1),
- GET_MODE (op1));
- op1 = SUBREG_REG (op1);
- code1 = GET_CODE (op1);
+ mips_emit_move (dest, mips_legitimize_tls_address (src));
+ return;
}
- /* Sanity check. */
- if (GET_CODE (operands[1]) == SIGN_EXTEND
- && code1 != REG
- && code1 != CONST_INT
- /* The following three can happen as the result of a questionable
- cast. */
- && code1 != LABEL_REF
- && code1 != SYMBOL_REF
- && code1 != CONST)
- abort ();
-
- if (code0 == REG)
+ /* 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 (offset != const0_rtx
+ && (targetm.cannot_force_const_mem (src)
+ || (!TARGET_MIPS16 && can_create_pseudo_p ())))
{
- int regno0 = REGNO (op0) + subreg_offset0;
-
- if (code1 == REG)
- {
- int regno1 = REGNO (op1) + subreg_offset1;
+ base = mips_force_temporary (dest, base);
+ mips_emit_move (dest, mips_add_offset (NULL, base, INTVAL (offset)));
+ return;
+ }
- /* Just in case, don't do anything for assigning a register
- to itself, unless we are filling a delay slot. */
- if (regno0 == regno1 && set_nomacro == 0)
- ret = "";
+ src = force_const_mem (mode, src);
- else if (FP_REG_P (regno0))
- {
- if (FP_REG_P (regno1))
- ret = "mov.d\t%0,%1";
+ /* When using explicit relocs, constant pool references are sometimes
+ not legitimate addresses. */
+ mips_split_symbol (dest, XEXP (src, 0), mode, &XEXP (src, 0));
+ mips_emit_move (dest, src);
+}
- else
- {
- delay = DELAY_LOAD;
- if (TARGET_FLOAT64)
- {
- if (!TARGET_64BIT)
- abort_with_insn (insn, "bad move");
+/* If (set DEST SRC) is not a valid move instruction, emit an equivalent
+ sequence that is valid. */
-#ifdef TARGET_FP_CALL_32
- if (FP_CALL_GP_REG_P (regno1))
- ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tdmtc1\t%1,%0";
- else
-#endif
- ret = "dmtc1\t%1,%0";
- }
- else
- ret = "mtc1\t%L1,%0\n\tmtc1\t%M1,%D0";
- }
+bool
+mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
+{
+ if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
+ {
+ mips_emit_move (dest, force_reg (mode, src));
+ return true;
+ }
+
+ /* We need to deal with constants that would be legitimate
+ immediate_operands but aren't legitimate move_operands. */
+ if (CONSTANT_P (src) && !move_operand (src, mode))
+ {
+ mips_legitimize_const_move (mode, dest, src);
+ set_unique_reg_note (get_last_insn (), REG_EQUAL, copy_rtx (src));
+ return true;
+ }
+ return false;
+}
+\f
+/* Return true if value 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, enum mips_symbol_context context)
+{
+ enum mips_symbol_type symbol_type;
+
+ return (mips_lo_relocs[SYMBOL_GP_RELATIVE]
+ && !mips_split_p[SYMBOL_GP_RELATIVE]
+ && mips_symbolic_constant_p (x, context, &symbol_type)
+ && symbol_type == SYMBOL_GP_RELATIVE);
+}
+
+/* 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)
+{
+ enum mips_symbol_context context;
+
+ if (GET_CODE (*loc) == LO_SUM)
+ return -1;
+
+ 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
+ a LO_SUM. */
+
+bool
+mips_small_data_pattern_p (rtx op)
+{
+ return for_each_rtx (&op, mips_small_data_pattern_1, NULL);
+}
+
+/* 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)
+{
+ 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)
+ return -1;
+
+ return 0;
+}
+
+/* Rewrite instruction pattern PATTERN so that it refers to small data
+ using explicit relocations. */
+
+rtx
+mips_rewrite_small_data (rtx pattern)
+{
+ pattern = copy_insn (pattern);
+ for_each_rtx (&pattern, mips_rewrite_small_data_1, NULL);
+ return pattern;
+}
+\f
+/* We need a lot of little routines to check the range of MIPS16 immediate
+ operands. */
+
+static int
+m16_check_op (rtx op, int low, int high, int mask)
+{
+ return (GET_CODE (op) == CONST_INT
+ && IN_RANGE (INTVAL (op), low, high)
+ && (INTVAL (op) & mask) == 0);
+}
+
+int
+m16_uimm3_b (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, 0x1, 0x8, 0);
+}
+
+int
+m16_simm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x8, 0x7, 0);
+}
+
+int
+m16_nsimm4_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x7, 0x8, 0);
+}
+
+int
+m16_simm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x10, 0xf, 0);
+}
+
+int
+m16_nsimm5_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xf, 0x10, 0);
+}
+
+int
+m16_uimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x10 << 2, 0xf << 2, 3);
+}
+
+int
+m16_nuimm5_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xf << 2, 0x10 << 2, 3);
+}
+
+int
+m16_simm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x80, 0x7f, 0);
+}
+
+int
+m16_nsimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x7f, 0x80, 0);
+}
+
+int
+m16_uimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, 0x0, 0xff, 0);
+}
+
+int
+m16_nuimm8_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xff, 0x0, 0);
+}
+
+int
+m16_uimm8_m1_1 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x1, 0xfe, 0);
+}
+
+int
+m16_uimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, 0x0, 0xff << 2, 3);
+}
+
+int
+m16_nuimm8_4 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0xff << 2, 0x0, 3);
+}
+
+int
+m16_simm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x80 << 3, 0x7f << 3, 7);
+}
+
+int
+m16_nsimm8_8 (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ return m16_check_op (op, -0x7f << 3, 0x80 << 3, 7);
+}
+\f
+/* The cost of loading values from the constant pool. It should be
+ larger than the cost of any constant we want to synthesize inline. */
+#define CONSTANT_POOL_COST COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 8)
+
+/* Return the cost of X when used as an operand to the MIPS16 instruction
+ that implements CODE. Return -1 if there is no such instruction, or if
+ X is not a valid immediate operand for it. */
+
+static int
+mips16_constant_cost (int code, HOST_WIDE_INT x)
+{
+ switch (code)
+ {
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* Shifts by between 1 and 8 bits (inclusive) are unextended,
+ other shifts are extended. The shift patterns truncate the shift
+ count to the right size, so there are no out-of-range values. */
+ if (IN_RANGE (x, 1, 8))
+ return 0;
+ return COSTS_N_INSNS (1);
+
+ case PLUS:
+ if (IN_RANGE (x, -128, 127))
+ return 0;
+ if (SMALL_OPERAND (x))
+ return COSTS_N_INSNS (1);
+ return -1;
+
+ case LEU:
+ /* Like LE, but reject the always-true case. */
+ if (x == -1)
+ return -1;
+ case LE:
+ /* We add 1 to the immediate and use SLT. */
+ x += 1;
+ case XOR:
+ /* We can use CMPI for an xor with an unsigned 16-bit X. */
+ case LT:
+ case LTU:
+ if (IN_RANGE (x, 0, 255))
+ return 0;
+ if (SMALL_OPERAND_UNSIGNED (x))
+ return COSTS_N_INSNS (1);
+ return -1;
+
+ case EQ:
+ case NE:
+ /* Equality comparisons with 0 are cheap. */
+ if (x == 0)
+ return 0;
+ return -1;
+
+ default:
+ return -1;
+ }
+}
+
+/* Return true if there is a non-MIPS16 instruction that implements CODE
+ and if that instruction accepts X as an immediate operand. */
+
+static int
+mips_immediate_operand_p (int code, HOST_WIDE_INT x)
+{
+ switch (code)
+ {
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ /* All shift counts are truncated to a valid constant. */
+ return true;
+
+ case ROTATE:
+ case ROTATERT:
+ /* Likewise rotates, if the target supports rotates at all. */
+ return ISA_HAS_ROR;
+
+ case AND:
+ case IOR:
+ case XOR:
+ /* These instructions take 16-bit unsigned immediates. */
+ return SMALL_OPERAND_UNSIGNED (x);
+
+ case PLUS:
+ case LT:
+ case LTU:
+ /* These instructions take 16-bit signed immediates. */
+ return SMALL_OPERAND (x);
+
+ case EQ:
+ case NE:
+ case GT:
+ case GTU:
+ /* The "immediate" forms of these instructions are really
+ implemented as comparisons with register 0. */
+ return x == 0;
+
+ case GE:
+ case GEU:
+ /* Likewise, meaning that the only valid immediate operand is 1. */
+ return x == 1;
+
+ case LE:
+ /* We add 1 to the immediate and use SLT. */
+ return SMALL_OPERAND (x + 1);
+
+ case LEU:
+ /* Likewise SLTU, but reject the always-true case. */
+ return SMALL_OPERAND (x + 1) && x + 1 != 0;
+
+ case SIGN_EXTRACT:
+ case ZERO_EXTRACT:
+ /* The bit position and size are immediate operands. */
+ return ISA_HAS_EXT_INS;
+
+ default:
+ /* By default assume that $0 can be used for 0. */
+ return x == 0;
+ }
+}
+
+/* Return the cost of binary operation X, given that the instruction
+ sequence for a word-sized or smaller operation has cost SINGLE_COST
+ and that the sequence of a double-word operation has cost DOUBLE_COST. */
+
+static int
+mips_binary_cost (rtx x, int single_cost, int double_cost)
+{
+ int cost;
+
+ if (GET_MODE_SIZE (GET_MODE (x)) == UNITS_PER_WORD * 2)
+ cost = double_cost;
+ else
+ cost = single_cost;
+ return (cost
+ + rtx_cost (XEXP (x, 0), 0, !optimize_size)
+ + rtx_cost (XEXP (x, 1), GET_CODE (x), !optimize_size));
+}
+
+/* Return the cost of floating-point multiplications of mode MODE. */
+
+static int
+mips_fp_mult_cost (enum machine_mode mode)
+{
+ return mode == DFmode ? mips_cost->fp_mult_df : mips_cost->fp_mult_sf;
+}
+
+/* Return the cost of floating-point divisions of mode MODE. */
+
+static int
+mips_fp_div_cost (enum machine_mode mode)
+{
+ return mode == DFmode ? mips_cost->fp_div_df : mips_cost->fp_div_sf;
+}
+
+/* Return the cost of sign-extending OP to mode MODE, not including the
+ cost of OP itself. */
+
+static int
+mips_sign_extend_cost (enum machine_mode mode, rtx op)
+{
+ if (MEM_P (op))
+ /* Extended loads are as cheap as unextended ones. */
+ return 0;
+
+ if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+ /* A sign extension from SImode to DImode in 64-bit mode is free. */
+ return 0;
+
+ if (ISA_HAS_SEB_SEH || GENERATE_MIPS16E)
+ /* We can use SEB or SEH. */
+ return COSTS_N_INSNS (1);
+
+ /* We need to use a shift left and a shift right. */
+ return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+}
+
+/* Return the cost of zero-extending OP to mode MODE, not including the
+ cost of OP itself. */
+
+static int
+mips_zero_extend_cost (enum machine_mode mode, rtx op)
+{
+ if (MEM_P (op))
+ /* Extended loads are as cheap as unextended ones. */
+ return 0;
+
+ if (TARGET_64BIT && mode == DImode && GET_MODE (op) == SImode)
+ /* We need a shift left by 32 bits and a shift right by 32 bits. */
+ return COSTS_N_INSNS (TARGET_MIPS16 ? 4 : 2);
+
+ if (GENERATE_MIPS16E)
+ /* We can use ZEB or ZEH. */
+ return COSTS_N_INSNS (1);
+
+ if (TARGET_MIPS16)
+ /* We need to load 0xff or 0xffff into a register and use AND. */
+ return COSTS_N_INSNS (GET_MODE (op) == QImode ? 2 : 3);
+
+ /* We can use ANDI. */
+ return COSTS_N_INSNS (1);
+}
+
+/* Implement TARGET_RTX_COSTS. */
+
+static bool
+mips_rtx_costs (rtx x, int code, int outer_code, int *total,
+ bool speed)
+{
+ enum machine_mode mode = GET_MODE (x);
+ bool float_mode_p = FLOAT_MODE_P (mode);
+ int cost;
+ rtx addr;
+
+ /* The cost of a COMPARE is hard to define for MIPS. COMPAREs don't
+ appear in the instruction stream, and the cost of a comparison is
+ really the cost of the branch or scc condition. At the time of
+ writing, GCC only uses an explicit outer COMPARE code when optabs
+ is testing whether a constant is expensive enough to force into a
+ register. We want optabs to pass such constants through the MIPS
+ expanders instead, so make all constants very cheap here. */
+ if (outer_code == COMPARE)
+ {
+ gcc_assert (CONSTANT_P (x));
+ *total = 0;
+ return true;
+ }
+
+ switch (code)
+ {
+ case CONST_INT:
+ /* Treat *clear_upper32-style ANDs as having zero cost in the
+ second operand. The cost is entirely in the first operand.
+
+ ??? This is needed because we would otherwise try to CSE
+ the constant operand. Although that's the right thing for
+ instructions that continue to be a register operation throughout
+ compilation, it is disastrous for instructions that could
+ later be converted into a memory operation. */
+ if (TARGET_64BIT
+ && outer_code == AND
+ && UINTVAL (x) == 0xffffffff)
+ {
+ *total = 0;
+ return true;
+ }
+
+ if (TARGET_MIPS16)
+ {
+ cost = mips16_constant_cost (outer_code, INTVAL (x));
+ if (cost >= 0)
+ {
+ *total = cost;
+ return true;
+ }
+ }
+ else
+ {
+ /* When not optimizing for size, we care more about the cost
+ of hot code, and hot code is often in a loop. If a constant
+ operand needs to be forced into a register, we will often be
+ able to hoist the constant load out of the loop, so the load
+ should not contribute to the cost. */
+ if (!optimize_size
+ || mips_immediate_operand_p (outer_code, INTVAL (x)))
+ {
+ *total = 0;
+ return true;
}
+ }
+ /* Fall through. */
+
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ case CONST_DOUBLE:
+ if (force_to_mem_operand (x, VOIDmode))
+ {
+ *total = COSTS_N_INSNS (1);
+ return true;
+ }
+ cost = mips_const_insns (x);
+ if (cost > 0)
+ {
+ /* If the constant is likely to be stored in a GPR, SETs of
+ single-insn constants are as cheap as register sets; we
+ never want to CSE them.
+
+ Don't reduce the cost of storing a floating-point zero in
+ FPRs. If we have a zero in an FPR for other reasons, we
+ can get better cfg-cleanup and delayed-branch results by
+ using it consistently, rather than using $0 sometimes and
+ an FPR at other times. Also, moves between floating-point
+ registers are sometimes cheaper than (D)MTC1 $0. */
+ if (cost == 1
+ && outer_code == SET
+ && !(float_mode_p && TARGET_HARD_FLOAT))
+ cost = 0;
+ /* When non-MIPS16 code loads a constant N>1 times, we rarely
+ want to CSE the constant itself. It is usually better to
+ have N copies of the last operation in the sequence and one
+ shared copy of the other operations. (Note that this is
+ not true for MIPS16 code, where the final operation in the
+ sequence is often an extended instruction.)
+
+ Also, if we have a CONST_INT, we don't know whether it is
+ for a word or doubleword operation, so we cannot rely on
+ the result of mips_build_integer. */
+ else if (!TARGET_MIPS16
+ && (outer_code == SET || mode == VOIDmode))
+ cost = 1;
+ *total = COSTS_N_INSNS (cost);
+ return true;
+ }
+ /* The value will need to be fetched from the constant pool. */
+ *total = CONSTANT_POOL_COST;
+ return true;
+
+ case MEM:
+ /* If the address is legitimate, return the number of
+ instructions it needs. */
+ addr = XEXP (x, 0);
+ cost = mips_address_insns (addr, mode, true);
+ if (cost > 0)
+ {
+ *total = COSTS_N_INSNS (cost + 1);
+ return true;
+ }
+ /* Check for a scaled indexed address. */
+ if (mips_lwxs_address_p (addr))
+ {
+ *total = COSTS_N_INSNS (2);
+ return true;
+ }
+ /* Otherwise use the default handling. */
+ return false;
+
+ case FFS:
+ *total = COSTS_N_INSNS (6);
+ return false;
+
+ case NOT:
+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 2 : 1);
+ return false;
+
+ case AND:
+ /* Check for a *clear_upper32 pattern and treat it like a zero
+ extension. See the pattern's comment for details. */
+ if (TARGET_64BIT
+ && mode == DImode
+ && CONST_INT_P (XEXP (x, 1))
+ && UINTVAL (XEXP (x, 1)) == 0xffffffff)
+ {
+ *total = (mips_zero_extend_cost (mode, XEXP (x, 0))
+ + rtx_cost (XEXP (x, 0), 0, speed));
+ return true;
+ }
+ /* Fall through. */
+
+ case IOR:
+ case XOR:
+ /* Double-word operations use two single-word operations. */
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (2));
+ return true;
+
+ case ASHIFT:
+ case ASHIFTRT:
+ case LSHIFTRT:
+ case ROTATE:
+ case ROTATERT:
+ if (CONSTANT_P (XEXP (x, 1)))
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+ else
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (12));
+ return true;
+
+ case ABS:
+ if (float_mode_p)
+ *total = mips_cost->fp_add;
+ else
+ *total = COSTS_N_INSNS (4);
+ return false;
+
+ case LO_SUM:
+ /* Low-part immediates need an extended MIPS16 instruction. */
+ *total = (COSTS_N_INSNS (TARGET_MIPS16 ? 2 : 1)
+ + rtx_cost (XEXP (x, 0), 0, speed));
+ return true;
+
+ case LT:
+ case LTU:
+ case LE:
+ case LEU:
+ case GT:
+ case GTU:
+ case GE:
+ case GEU:
+ case EQ:
+ case NE:
+ case UNORDERED:
+ case LTGT:
+ /* Branch comparisons have VOIDmode, so use the first operand's
+ mode instead. */
+ mode = GET_MODE (XEXP (x, 0));
+ if (FLOAT_MODE_P (mode))
+ {
+ *total = mips_cost->fp_add;
+ return false;
+ }
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1), COSTS_N_INSNS (4));
+ return true;
+
+ case MINUS:
+ if (float_mode_p
+ && (ISA_HAS_NMADD4_NMSUB4 (mode) || ISA_HAS_NMADD3_NMSUB3 (mode))
+ && TARGET_FUSED_MADD
+ && !HONOR_NANS (mode)
+ && !HONOR_SIGNED_ZEROS (mode))
+ {
+ /* See if we can use NMADD or NMSUB. See mips.md for the
+ associated patterns. */
+ rtx op0 = XEXP (x, 0);
+ rtx op1 = XEXP (x, 1);
+ if (GET_CODE (op0) == MULT && GET_CODE (XEXP (op0, 0)) == NEG)
+ {
+ *total = (mips_fp_mult_cost (mode)
+ + rtx_cost (XEXP (XEXP (op0, 0), 0), 0, speed)
+ + rtx_cost (XEXP (op0, 1), 0, speed)
+ + rtx_cost (op1, 0, speed));
+ return true;
+ }
+ if (GET_CODE (op1) == MULT)
+ {
+ *total = (mips_fp_mult_cost (mode)
+ + rtx_cost (op0, 0, speed)
+ + rtx_cost (XEXP (op1, 0), 0, speed)
+ + rtx_cost (XEXP (op1, 1), 0, speed));
+ return true;
+ }
+ }
+ /* Fall through. */
+
+ case PLUS:
+ if (float_mode_p)
+ {
+ /* If this is part of a MADD or MSUB, treat the PLUS as
+ being free. */
+ if (ISA_HAS_FP4
+ && TARGET_FUSED_MADD
+ && GET_CODE (XEXP (x, 0)) == MULT)
+ *total = 0;
+ else
+ *total = mips_cost->fp_add;
+ return false;
+ }
+
+ /* Double-word operations require three single-word operations and
+ an SLTU. The MIPS16 version then needs to move the result of
+ the SLTU from $24 to a MIPS16 register. */
+ *total = mips_binary_cost (x, COSTS_N_INSNS (1),
+ COSTS_N_INSNS (TARGET_MIPS16 ? 5 : 4));
+ return true;
+
+ case NEG:
+ if (float_mode_p
+ && (ISA_HAS_NMADD4_NMSUB4 (mode) || ISA_HAS_NMADD3_NMSUB3 (mode))
+ && TARGET_FUSED_MADD
+ && !HONOR_NANS (mode)
+ && HONOR_SIGNED_ZEROS (mode))
+ {
+ /* See if we can use NMADD or NMSUB. See mips.md for the
+ associated patterns. */
+ rtx op = XEXP (x, 0);
+ if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
+ && GET_CODE (XEXP (op, 0)) == MULT)
+ {
+ *total = (mips_fp_mult_cost (mode)
+ + rtx_cost (XEXP (XEXP (op, 0), 0), 0, speed)
+ + rtx_cost (XEXP (XEXP (op, 0), 1), 0, speed)
+ + rtx_cost (XEXP (op, 1), 0, speed));
+ return true;
+ }
+ }
+
+ if (float_mode_p)
+ *total = mips_cost->fp_add;
+ else
+ *total = COSTS_N_INSNS (GET_MODE_SIZE (mode) > UNITS_PER_WORD ? 4 : 1);
+ return false;
+
+ case MULT:
+ if (float_mode_p)
+ *total = mips_fp_mult_cost (mode);
+ else if (mode == DImode && !TARGET_64BIT)
+ /* Synthesized from 2 mulsi3s, 1 mulsidi3 and two additions,
+ where the mulsidi3 always includes an MFHI and an MFLO. */
+ *total = (optimize_size
+ ? COSTS_N_INSNS (ISA_HAS_MUL3 ? 7 : 9)
+ : mips_cost->int_mult_si * 3 + 6);
+ else if (optimize_size)
+ *total = (ISA_HAS_MUL3 ? 1 : 2);
+ else if (mode == DImode)
+ *total = mips_cost->int_mult_di;
+ else
+ *total = mips_cost->int_mult_si;
+ return false;
+
+ case DIV:
+ /* Check for a reciprocal. */
+ if (float_mode_p
+ && ISA_HAS_FP4
+ && flag_unsafe_math_optimizations
+ && XEXP (x, 0) == CONST1_RTX (mode))
+ {
+ if (outer_code == SQRT || GET_CODE (XEXP (x, 1)) == SQRT)
+ /* An rsqrt<mode>a or rsqrt<mode>b pattern. Count the
+ division as being free. */
+ *total = rtx_cost (XEXP (x, 1), 0, speed);
+ else
+ *total = mips_fp_div_cost (mode) + rtx_cost (XEXP (x, 1), 0, speed);
+ return true;
+ }
+ /* Fall through. */
+
+ case SQRT:
+ case MOD:
+ if (float_mode_p)
+ {
+ *total = mips_fp_div_cost (mode);
+ return false;
+ }
+ /* Fall through. */
+
+ case UDIV:
+ case UMOD:
+ if (optimize_size)
+ {
+ /* It is our responsibility to make division by a power of 2
+ as cheap as 2 register additions if we want the division
+ expanders to be used for such operations; see the setting
+ of sdiv_pow2_cheap in optabs.c. Using (D)DIV for MIPS16
+ should always produce shorter code than using
+ expand_sdiv2_pow2. */
+ if (TARGET_MIPS16
+ && CONST_INT_P (XEXP (x, 1))
+ && exact_log2 (INTVAL (XEXP (x, 1))) >= 0)
+ {
+ *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), 0, speed);
+ return true;
+ }
+ *total = COSTS_N_INSNS (mips_idiv_insns ());
+ }
+ else if (mode == DImode)
+ *total = mips_cost->int_div_di;
+ else
+ *total = mips_cost->int_div_si;
+ return false;
+
+ case SIGN_EXTEND:
+ *total = mips_sign_extend_cost (mode, XEXP (x, 0));
+ return false;
+
+ case ZERO_EXTEND:
+ *total = mips_zero_extend_cost (mode, XEXP (x, 0));
+ return false;
+
+ case FLOAT:
+ case UNSIGNED_FLOAT:
+ case FIX:
+ case FLOAT_EXTEND:
+ case FLOAT_TRUNCATE:
+ *total = mips_cost->fp_add;
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+/* Implement TARGET_ADDRESS_COST. */
+
+static int
+mips_address_cost (rtx addr, bool speed ATTRIBUTE_UNUSED)
+{
+ return mips_address_insns (addr, SImode, false);
+}
+\f
+/* Return one word of double-word value OP, taking into account the fixed
+ endianness of certain registers. HIGH_P is true to select the high part,
+ false to select the low part. */
+
+rtx
+mips_subword (rtx op, bool high_p)
+{
+ unsigned int byte, offset;
+ enum machine_mode mode;
+
+ mode = GET_MODE (op);
+ if (mode == VOIDmode)
+ mode = TARGET_64BIT ? TImode : DImode;
+
+ if (TARGET_BIG_ENDIAN ? !high_p : high_p)
+ byte = UNITS_PER_WORD;
+ else
+ byte = 0;
+
+ if (FP_REG_RTX_P (op))
+ {
+ /* Paired FPRs are always ordered little-endian. */
+ offset = (UNITS_PER_WORD < UNITS_PER_HWFPVALUE ? high_p : byte != 0);
+ return gen_rtx_REG (word_mode, REGNO (op) + offset);
+ }
+
+ if (MEM_P (op))
+ return mips_rewrite_small_data (adjust_address (op, word_mode, byte));
+
+ return simplify_gen_subreg (word_mode, op, mode, byte);
+}
+
+/* Return true if a 64-bit move from SRC to DEST should be split into two. */
+
+bool
+mips_split_64bit_move_p (rtx dest, rtx src)
+{
+ if (TARGET_64BIT)
+ return false;
+
+ /* FPR-to-FPR moves can be done in a single instruction, if they're
+ allowed at all. */
+ if (FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
+ return false;
+
+ /* Check for floating-point loads and stores. */
+ if (ISA_HAS_LDC1_SDC1)
+ {
+ if (FP_REG_RTX_P (dest) && MEM_P (src))
+ return false;
+ if (FP_REG_RTX_P (src) && MEM_P (dest))
+ return false;
+ }
+ return true;
+}
+
+/* Split a doubleword move from SRC to DEST. On 32-bit targets,
+ this function handles 64-bit moves for which mips_split_64bit_move_p
+ holds. For 64-bit targets, this function handles 128-bit moves. */
+
+void
+mips_split_doubleword_move (rtx dest, rtx src)
+{
+ rtx low_dest;
+
+ if (FP_REG_RTX_P (dest) || FP_REG_RTX_P (src))
+ {
+ if (!TARGET_64BIT && GET_MODE (dest) == DImode)
+ emit_insn (gen_move_doubleword_fprdi (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == DFmode)
+ emit_insn (gen_move_doubleword_fprdf (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V2SFmode)
+ emit_insn (gen_move_doubleword_fprv2sf (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V2SImode)
+ emit_insn (gen_move_doubleword_fprv2si (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V4HImode)
+ emit_insn (gen_move_doubleword_fprv4hi (dest, src));
+ else if (!TARGET_64BIT && GET_MODE (dest) == V8QImode)
+ emit_insn (gen_move_doubleword_fprv8qi (dest, src));
+ else if (TARGET_64BIT && GET_MODE (dest) == TFmode)
+ emit_insn (gen_move_doubleword_fprtf (dest, src));
+ else
+ gcc_unreachable ();
+ }
+ else if (REG_P (dest) && REGNO (dest) == MD_REG_FIRST)
+ {
+ low_dest = mips_subword (dest, false);
+ mips_emit_move (low_dest, mips_subword (src, false));
+ if (TARGET_64BIT)
+ emit_insn (gen_mthidi_ti (dest, mips_subword (src, true), low_dest));
+ else
+ emit_insn (gen_mthisi_di (dest, mips_subword (src, true), low_dest));
+ }
+ else if (REG_P (src) && REGNO (src) == MD_REG_FIRST)
+ {
+ mips_emit_move (mips_subword (dest, false), mips_subword (src, false));
+ if (TARGET_64BIT)
+ emit_insn (gen_mfhidi_ti (mips_subword (dest, true), src));
+ else
+ emit_insn (gen_mfhisi_di (mips_subword (dest, true), src));
+ }
+ else
+ {
+ /* The operation can be split into two normal moves. Decide in
+ which order to do them. */
+ low_dest = mips_subword (dest, false);
+ if (REG_P (low_dest)
+ && reg_overlap_mentioned_p (low_dest, src))
+ {
+ mips_emit_move (mips_subword (dest, true), mips_subword (src, true));
+ mips_emit_move (low_dest, mips_subword (src, false));
+ }
+ else
+ {
+ mips_emit_move (low_dest, mips_subword (src, false));
+ mips_emit_move (mips_subword (dest, true), mips_subword (src, true));
+ }
+ }
+}
+\f
+/* Return the appropriate instructions to move SRC into DEST. Assume
+ that SRC is operand 1 and DEST is operand 0. */
+
+const char *
+mips_output_move (rtx dest, rtx src)
+{
+ enum rtx_code dest_code, src_code;
+ enum machine_mode mode;
+ enum mips_symbol_type symbol_type;
+ bool dbl_p;
+
+ dest_code = GET_CODE (dest);
+ src_code = GET_CODE (src);
+ mode = GET_MODE (dest);
+ dbl_p = (GET_MODE_SIZE (mode) == 8);
+
+ if (dbl_p && mips_split_64bit_move_p (dest, src))
+ return "#";
+
+ if ((src_code == REG && GP_REG_P (REGNO (src)))
+ || (!TARGET_MIPS16 && src == CONST0_RTX (mode)))
+ {
+ if (dest_code == REG)
+ {
+ if (GP_REG_P (REGNO (dest)))
+ return "move\t%0,%z1";
+
+ /* Moves to HI are handled by special .md insns. */
+ if (REGNO (dest) == LO_REGNUM)
+ return "mtlo\t%z1";
+
+ if (DSP_ACC_REG_P (REGNO (dest)))
+ {
+ static char retval[] = "mt__\t%z1,%q0";
+
+ retval[2] = reg_names[REGNO (dest)][4];
+ retval[3] = reg_names[REGNO (dest)][5];
+ return retval;
+ }
+
+ if (FP_REG_P (REGNO (dest)))
+ return dbl_p ? "dmtc1\t%z1,%0" : "mtc1\t%z1,%0";
+
+ if (ALL_COP_REG_P (REGNO (dest)))
+ {
+ static char retval[] = "dmtc_\t%z1,%0";
+
+ retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest));
+ return dbl_p ? retval : retval + 1;
+ }
+ }
+ if (dest_code == MEM)
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 1: return "sb\t%z1,%0";
+ case 2: return "sh\t%z1,%0";
+ case 4: return "sw\t%z1,%0";
+ case 8: return "sd\t%z1,%0";
+ }
+ }
+ if (dest_code == REG && GP_REG_P (REGNO (dest)))
+ {
+ if (src_code == REG)
+ {
+ /* Moves from HI are handled by special .md insns. */
+ if (REGNO (src) == LO_REGNUM)
+ {
+ /* When generating VR4120 or VR4130 code, we use MACC and
+ DMACC instead of MFLO. This avoids both the normal
+ MIPS III HI/LO hazards and the errata related to
+ -mfix-vr4130. */
+ if (ISA_HAS_MACCHI)
+ return dbl_p ? "dmacc\t%0,%.,%." : "macc\t%0,%.,%.";
+ return "mflo\t%0";
+ }
+
+ if (DSP_ACC_REG_P (REGNO (src)))
+ {
+ static char retval[] = "mf__\t%0,%q1";
+
+ retval[2] = reg_names[REGNO (src)][4];
+ retval[3] = reg_names[REGNO (src)][5];
+ return retval;
+ }
+
+ if (FP_REG_P (REGNO (src)))
+ return dbl_p ? "dmfc1\t%0,%1" : "mfc1\t%0,%1";
+
+ if (ALL_COP_REG_P (REGNO (src)))
+ {
+ static char retval[] = "dmfc_\t%0,%1";
+
+ retval[4] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src));
+ return dbl_p ? retval : retval + 1;
+ }
+
+ if (ST_REG_P (REGNO (src)) && ISA_HAS_8CC)
+ return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1";
+ }
+
+ if (src_code == MEM)
+ switch (GET_MODE_SIZE (mode))
+ {
+ case 1: return "lbu\t%0,%1";
+ case 2: return "lhu\t%0,%1";
+ case 4: return "lw\t%0,%1";
+ case 8: return "ld\t%0,%1";
+ }
+
+ if (src_code == CONST_INT)
+ {
+ /* Don't use the X format for the operand itself, because that
+ will give out-of-range numbers for 64-bit hosts and 32-bit
+ targets. */
+ if (!TARGET_MIPS16)
+ return "li\t%0,%1\t\t\t# %X1";
+
+ if (SMALL_OPERAND_UNSIGNED (INTVAL (src)))
+ return "li\t%0,%1";
+
+ if (SMALL_OPERAND_UNSIGNED (-INTVAL (src)))
+ return "#";
+ }
+
+ if (src_code == HIGH)
+ 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))
+ {
+ 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)))
+ {
+ if (dest_code == REG && FP_REG_P (REGNO (dest)))
+ {
+ if (GET_MODE (dest) == V2SFmode)
+ return "mov.ps\t%0,%1";
+ else
+ return dbl_p ? "mov.d\t%0,%1" : "mov.s\t%0,%1";
+ }
+
+ if (dest_code == MEM)
+ return dbl_p ? "sdc1\t%1,%0" : "swc1\t%1,%0";
+ }
+ if (dest_code == REG && FP_REG_P (REGNO (dest)))
+ {
+ if (src_code == MEM)
+ return dbl_p ? "ldc1\t%0,%1" : "lwc1\t%0,%1";
+ }
+ if (dest_code == REG && ALL_COP_REG_P (REGNO (dest)) && src_code == MEM)
+ {
+ static char retval[] = "l_c_\t%0,%1";
+
+ retval[1] = (dbl_p ? 'd' : 'w');
+ retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (dest));
+ return retval;
+ }
+ if (dest_code == MEM && src_code == REG && ALL_COP_REG_P (REGNO (src)))
+ {
+ static char retval[] = "s_c_\t%1,%0";
+
+ retval[1] = (dbl_p ? 'd' : 'w');
+ retval[3] = COPNUM_AS_CHAR_FROM_REGNUM (REGNO (src));
+ return retval;
+ }
+ gcc_unreachable ();
+}
+\f
+/* Return true if CMP1 is a suitable second operand for integer ordering
+ test CODE. See also the *sCC patterns in mips.md. */
+
+static bool
+mips_int_order_operand_ok_p (enum rtx_code code, rtx cmp1)
+{
+ switch (code)
+ {
+ case GT:
+ case GTU:
+ return reg_or_0_operand (cmp1, VOIDmode);
+
+ case GE:
+ case GEU:
+ return !TARGET_MIPS16 && cmp1 == const1_rtx;
+
+ case LT:
+ case LTU:
+ return arith_operand (cmp1, VOIDmode);
+
+ case LE:
+ return sle_operand (cmp1, VOIDmode);
+
+ case LEU:
+ return sleu_operand (cmp1, VOIDmode);
+
+ default:
+ gcc_unreachable ();
+ }
+}
+
+/* Return true if *CMP1 (of mode MODE) is a valid second operand for
+ integer ordering test *CODE, or if an equivalent combination can
+ be formed by adjusting *CODE and *CMP1. When returning true, update
+ *CODE and *CMP1 with the chosen code and operand, otherwise leave
+ them alone. */
+
+static bool
+mips_canonicalize_int_order_test (enum rtx_code *code, rtx *cmp1,
+ enum machine_mode mode)
+{
+ HOST_WIDE_INT plus_one;
+
+ if (mips_int_order_operand_ok_p (*code, *cmp1))
+ return true;
+
+ if (GET_CODE (*cmp1) == CONST_INT)
+ switch (*code)
+ {
+ case LE:
+ plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+ if (INTVAL (*cmp1) < plus_one)
+ {
+ *code = LT;
+ *cmp1 = force_reg (mode, GEN_INT (plus_one));
+ return true;
+ }
+ break;
+
+ case LEU:
+ plus_one = trunc_int_for_mode (UINTVAL (*cmp1) + 1, mode);
+ if (plus_one != 0)
+ {
+ *code = LTU;
+ *cmp1 = force_reg (mode, GEN_INT (plus_one));
+ return true;
+ }
+ break;
+
+ default:
+ break;
+ }
+ return false;
+}
+
+/* Compare CMP0 and CMP1 using ordering test CODE and store the result
+ in TARGET. CMP0 and TARGET are register_operands. If INVERT_PTR
+ is nonnull, it's OK to set TARGET to the inverse of the result and
+ flip *INVERT_PTR instead. */
+
+static void
+mips_emit_int_order_test (enum rtx_code code, bool *invert_ptr,
+ rtx target, rtx cmp0, rtx cmp1)
+{
+ enum machine_mode mode;
+
+ /* First see if there is a MIPS instruction that can do this operation.
+ If not, try doing the same for the inverse operation. If that also
+ fails, force CMP1 into a register and try again. */
+ mode = GET_MODE (cmp0);
+ if (mips_canonicalize_int_order_test (&code, &cmp1, mode))
+ mips_emit_binary (code, target, cmp0, cmp1);
+ else
+ {
+ enum rtx_code inv_code = reverse_condition (code);
+ if (!mips_canonicalize_int_order_test (&inv_code, &cmp1, mode))
+ {
+ cmp1 = force_reg (mode, cmp1);
+ mips_emit_int_order_test (code, invert_ptr, target, cmp0, cmp1);
+ }
+ else if (invert_ptr == 0)
+ {
+ rtx inv_target;
+
+ inv_target = mips_force_binary (GET_MODE (target),
+ inv_code, cmp0, cmp1);
+ mips_emit_binary (XOR, target, inv_target, const1_rtx);
+ }
+ else
+ {
+ *invert_ptr = !*invert_ptr;
+ mips_emit_binary (inv_code, target, cmp0, cmp1);
+ }
+ }
+}
+
+/* Return a register that is zero iff CMP0 and CMP1 are equal.
+ The register will have the same mode as CMP0. */
+
+static rtx
+mips_zero_if_equal (rtx cmp0, rtx cmp1)
+{
+ if (cmp1 == const0_rtx)
+ return cmp0;
+
+ if (uns_arith_operand (cmp1, VOIDmode))
+ return expand_binop (GET_MODE (cmp0), xor_optab,
+ cmp0, cmp1, 0, 0, OPTAB_DIRECT);
+
+ return expand_binop (GET_MODE (cmp0), sub_optab,
+ cmp0, cmp1, 0, 0, OPTAB_DIRECT);
+}
+
+/* Convert *CODE into a code that can be used in a floating-point
+ scc instruction (C.cond.fmt). Return true if the values of
+ the condition code registers will be inverted, with 0 indicating
+ that the condition holds. */
+
+static bool
+mips_reversed_fp_cond (enum rtx_code *code)
+{
+ switch (*code)
+ {
+ case NE:
+ case LTGT:
+ case ORDERED:
+ *code = reverse_condition_maybe_unordered (*code);
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+/* Convert a comparison into something that can be used in a branch or
+ conditional move. cmp_operands[0] and cmp_operands[1] are the values
+ being compared and *CODE is the code used to compare them.
+
+ Update *CODE, *OP0 and *OP1 so that they describe the final comparison.
+ If NEED_EQ_NE_P, then only EQ or NE comparisons against zero are possible,
+ otherwise any standard branch condition can be used. The standard branch
+ conditions are:
+
+ - EQ or NE between two registers.
+ - any comparison between a register and zero. */
+
+static void
+mips_emit_compare (enum rtx_code *code, rtx *op0, rtx *op1, bool need_eq_ne_p)
+{
+ if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) == MODE_INT)
+ {
+ if (!need_eq_ne_p && cmp_operands[1] == const0_rtx)
+ {
+ *op0 = cmp_operands[0];
+ *op1 = cmp_operands[1];
+ }
+ else if (*code == EQ || *code == NE)
+ {
+ if (need_eq_ne_p)
+ {
+ *op0 = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
+ *op1 = const0_rtx;
+ }
+ else
+ {
+ *op0 = cmp_operands[0];
+ *op1 = force_reg (GET_MODE (*op0), cmp_operands[1]);
+ }
+ }
+ else
+ {
+ /* The comparison needs a separate scc instruction. Store the
+ result of the scc in *OP0 and compare it against zero. */
+ bool invert = false;
+ *op0 = gen_reg_rtx (GET_MODE (cmp_operands[0]));
+ mips_emit_int_order_test (*code, &invert, *op0,
+ cmp_operands[0], cmp_operands[1]);
+ *code = (invert ? EQ : NE);
+ *op1 = const0_rtx;
+ }
+ }
+ else if (ALL_FIXED_POINT_MODE_P (GET_MODE (cmp_operands[0])))
+ {
+ *op0 = gen_rtx_REG (CCDSPmode, CCDSP_CC_REGNUM);
+ mips_emit_binary (*code, *op0, cmp_operands[0], cmp_operands[1]);
+ *code = NE;
+ *op1 = const0_rtx;
+ }
+ else
+ {
+ enum rtx_code cmp_code;
+
+ /* Floating-point tests use a separate C.cond.fmt comparison to
+ set a condition code register. The branch or conditional move
+ will then compare that register against zero.
+
+ Set CMP_CODE to the code of the comparison instruction and
+ *CODE to the code that the branch or move should use. */
+ cmp_code = *code;
+ *code = mips_reversed_fp_cond (&cmp_code) ? EQ : NE;
+ *op0 = (ISA_HAS_8CC
+ ? gen_reg_rtx (CCmode)
+ : gen_rtx_REG (CCmode, FPSW_REGNUM));
+ *op1 = const0_rtx;
+ mips_emit_binary (cmp_code, *op0, cmp_operands[0], cmp_operands[1]);
+ }
+}
+\f
+/* Try comparing cmp_operands[0] and cmp_operands[1] using rtl code CODE.
+ Store the result in TARGET and return true if successful.
+
+ On 64-bit targets, TARGET may be narrower than cmp_operands[0]. */
+
+bool
+mips_expand_scc (enum rtx_code code, rtx target)
+{
+ if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) != MODE_INT)
+ return false;
+
+ if (code == EQ || code == NE)
+ {
+ if (ISA_HAS_SEQ_SNE
+ && reg_imm10_operand (cmp_operands[1], GET_MODE (cmp_operands[1])))
+ mips_emit_binary (code, target, cmp_operands[0], cmp_operands[1]);
+ else
+ {
+ rtx zie = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
+ mips_emit_binary (code, target, zie, const0_rtx);
+ }
+ }
+ else
+ mips_emit_int_order_test (code, 0, target,
+ cmp_operands[0], cmp_operands[1]);
+ return true;
+}
+
+/* Compare cmp_operands[0] with cmp_operands[1] using comparison code
+ CODE and jump to OPERANDS[0] if the condition holds. */
+
+void
+mips_expand_conditional_branch (rtx *operands, enum rtx_code code)
+{
+ rtx op0, op1, condition;
+
+ mips_emit_compare (&code, &op0, &op1, TARGET_MIPS16);
+ condition = gen_rtx_fmt_ee (code, VOIDmode, op0, op1);
+ emit_jump_insn (gen_condjump (condition, operands[0]));
+}
+
+/* Implement:
+
+ (set temp (COND:CCV2 CMP_OP0 CMP_OP1))
+ (set DEST (unspec [TRUE_SRC FALSE_SRC temp] UNSPEC_MOVE_TF_PS)) */
+
+void
+mips_expand_vcondv2sf (rtx dest, rtx true_src, rtx false_src,
+ enum rtx_code cond, rtx cmp_op0, rtx cmp_op1)
+{
+ rtx cmp_result;
+ bool reversed_p;
+
+ reversed_p = mips_reversed_fp_cond (&cond);
+ cmp_result = gen_reg_rtx (CCV2mode);
+ emit_insn (gen_scc_ps (cmp_result,
+ gen_rtx_fmt_ee (cond, VOIDmode, cmp_op0, cmp_op1)));
+ if (reversed_p)
+ emit_insn (gen_mips_cond_move_tf_ps (dest, false_src, true_src,
+ cmp_result));
+ else
+ emit_insn (gen_mips_cond_move_tf_ps (dest, true_src, false_src,
+ cmp_result));
+}
+
+/* Compare cmp_operands[0] with cmp_operands[1] using the code of
+ OPERANDS[1]. Move OPERANDS[2] into OPERANDS[0] if the condition
+ holds, otherwise move OPERANDS[3] into OPERANDS[0]. */
+
+void
+mips_expand_conditional_move (rtx *operands)
+{
+ enum rtx_code code;
+ rtx cond, op0, op1;
+
+ code = GET_CODE (operands[1]);
+ mips_emit_compare (&code, &op0, &op1, true);
+ cond = gen_rtx_fmt_ee (code, GET_MODE (op0), op0, op1),
+ emit_insn (gen_rtx_SET (VOIDmode, operands[0],
+ gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]), cond,
+ operands[2], operands[3])));
+}
+
+/* Compare cmp_operands[0] with cmp_operands[1] using rtl code CODE,
+ then trap if the condition holds. */
+
+void
+mips_expand_conditional_trap (enum rtx_code code)
+{
+ rtx op0, op1;
+ enum machine_mode mode;
+
+ /* MIPS conditional trap instructions don't have GT or LE flavors,
+ so we must swap the operands and convert to LT and GE respectively. */
+ switch (code)
+ {
+ case GT:
+ case LE:
+ case GTU:
+ case LEU:
+ code = swap_condition (code);
+ op0 = cmp_operands[1];
+ op1 = cmp_operands[0];
+ break;
+
+ default:
+ op0 = cmp_operands[0];
+ op1 = cmp_operands[1];
+ break;
+ }
+
+ mode = GET_MODE (cmp_operands[0]);
+ op0 = force_reg (mode, op0);
+ if (!arith_operand (op1, mode))
+ op1 = force_reg (mode, op1);
+
+ emit_insn (gen_rtx_TRAP_IF (VOIDmode,
+ gen_rtx_fmt_ee (code, mode, op0, op1),
+ const0_rtx));
+}
+\f
+/* Initialize *CUM for a call to a function of type FNTYPE. */
+
+void
+mips_init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype)
+{
+ memset (cum, 0, sizeof (*cum));
+ cum->prototype = (fntype && prototype_p (fntype));
+ cum->gp_reg_found = (cum->prototype && stdarg_p (fntype));
+}
+
+/* Fill INFO with information about a single argument. CUM is the
+ cumulative state for earlier arguments. MODE is the mode of this
+ argument and TYPE is its type (if known). NAMED is true if this
+ is a named (fixed) argument rather than a variable one. */
+
+static void
+mips_get_arg_info (struct mips_arg_info *info, const CUMULATIVE_ARGS *cum,
+ enum machine_mode mode, tree type, int named)
+{
+ bool doubleword_aligned_p;
+ unsigned int num_bytes, num_words, max_regs;
+
+ /* Work out the size of the argument. */
+ num_bytes = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ num_words = (num_bytes + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+
+ /* Decide whether it should go in a floating-point register, assuming
+ one is free. Later code checks for availability.
+
+ The checks against UNITS_PER_FPVALUE handle the soft-float and
+ single-float cases. */
+ switch (mips_abi)
+ {
+ case ABI_EABI:
+ /* The EABI conventions have traditionally been defined in terms
+ of TYPE_MODE, regardless of the actual type. */
+ info->fpr_p = ((GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+ && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
+ break;
+
+ case ABI_32:
+ case ABI_O64:
+ /* Only leading floating-point scalars are passed in
+ floating-point registers. We also handle vector floats the same
+ say, which is OK because they are not covered by the standard ABI. */
+ info->fpr_p = (!cum->gp_reg_found
+ && cum->arg_number < 2
+ && (type == 0
+ || SCALAR_FLOAT_TYPE_P (type)
+ || VECTOR_FLOAT_TYPE_P (type))
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+ && GET_MODE_SIZE (mode) <= UNITS_PER_FPVALUE);
+ break;
+
+ case ABI_N32:
+ case ABI_64:
+ /* Scalar, complex and vector floating-point types are passed in
+ floating-point registers, as long as this is a named rather
+ than a variable argument. */
+ info->fpr_p = (named
+ && (type == 0 || FLOAT_TYPE_P (type))
+ && (GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+ && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_FPVALUE);
+
+ /* ??? According to the ABI documentation, the real and imaginary
+ parts of complex floats should be passed in individual registers.
+ The real and imaginary parts of stack arguments are supposed
+ to be contiguous and there should be an extra word of padding
+ at the end.
+
+ This has two problems. First, it makes it impossible to use a
+ single "void *" va_list type, since register and stack arguments
+ are passed differently. (At the time of writing, MIPSpro cannot
+ handle complex float varargs correctly.) Second, it's unclear
+ what should happen when there is only one register free.
+
+ For now, we assume that named complex floats should go into FPRs
+ if there are two FPRs free, otherwise they should be passed in the
+ same way as a struct containing two floats. */
+ if (info->fpr_p
+ && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
+ && GET_MODE_UNIT_SIZE (mode) < UNITS_PER_FPVALUE)
+ {
+ if (cum->num_gprs >= MAX_ARGS_IN_REGISTERS - 1)
+ info->fpr_p = false;
+ else
+ num_words = 2;
+ }
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+
+ /* See whether the argument has doubleword alignment. */
+ doubleword_aligned_p = FUNCTION_ARG_BOUNDARY (mode, type) > BITS_PER_WORD;
+
+ /* Set REG_OFFSET to the register count we're interested in.
+ The EABI allocates the floating-point registers separately,
+ but the other ABIs allocate them like integer registers. */
+ info->reg_offset = (mips_abi == ABI_EABI && info->fpr_p
+ ? cum->num_fprs
+ : cum->num_gprs);
+
+ /* Advance to an even register if the argument is doubleword-aligned. */
+ if (doubleword_aligned_p)
+ info->reg_offset += info->reg_offset & 1;
+
+ /* Work out the offset of a stack argument. */
+ info->stack_offset = cum->stack_words;
+ if (doubleword_aligned_p)
+ info->stack_offset += info->stack_offset & 1;
+
+ max_regs = MAX_ARGS_IN_REGISTERS - info->reg_offset;
+
+ /* Partition the argument between registers and stack. */
+ info->reg_words = MIN (num_words, max_regs);
+ info->stack_words = num_words - info->reg_words;
+}
+
+/* INFO describes a register argument that has the normal format for the
+ argument's mode. Return the register it uses, assuming that FPRs are
+ available if HARD_FLOAT_P. */
+
+static unsigned int
+mips_arg_regno (const struct mips_arg_info *info, bool hard_float_p)
+{
+ if (!info->fpr_p || !hard_float_p)
+ return GP_ARG_FIRST + info->reg_offset;
+ else if (mips_abi == ABI_32 && TARGET_DOUBLE_FLOAT && info->reg_offset > 0)
+ /* In o32, the second argument is always passed in $f14
+ for TARGET_DOUBLE_FLOAT, regardless of whether the
+ first argument was a word or doubleword. */
+ return FP_ARG_FIRST + 2;
+ else
+ return FP_ARG_FIRST + info->reg_offset;
+}
+
+/* Implement TARGET_STRICT_ARGUMENT_NAMING. */
+
+static bool
+mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
+{
+ return !TARGET_OLDABI;
+}
+
+/* Implement FUNCTION_ARG. */
+
+rtx
+mips_function_arg (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int named)
+{
+ struct mips_arg_info info;
+
+ /* We will be called with a mode of VOIDmode after the last argument
+ has been seen. Whatever we return will be passed to the call expander.
+ If we need a MIPS16 fp_code, return a REG with the code stored as
+ the mode. */
+ if (mode == VOIDmode)
+ {
+ if (TARGET_MIPS16 && cum->fp_code != 0)
+ return gen_rtx_REG ((enum machine_mode) cum->fp_code, 0);
+ else
+ return NULL;
+ }
+
+ mips_get_arg_info (&info, cum, mode, type, named);
+
+ /* Return straight away if the whole argument is passed on the stack. */
+ if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
+ return NULL;
+
+ /* The n32 and n64 ABIs say that if any 64-bit chunk of the structure
+ contains a double in its entirety, then that 64-bit chunk is passed
+ in a floating-point register. */
+ if (TARGET_NEWABI
+ && TARGET_HARD_FLOAT
+ && named
+ && type != 0
+ && TREE_CODE (type) == RECORD_TYPE
+ && TYPE_SIZE_UNIT (type)
+ && host_integerp (TYPE_SIZE_UNIT (type), 1))
+ {
+ tree field;
+
+ /* First check to see if there is any such field. */
+ for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
+ && host_integerp (bit_position (field), 0)
+ && int_bit_position (field) % BITS_PER_WORD == 0)
+ break;
+
+ if (field != 0)
+ {
+ /* Now handle the special case by returning a PARALLEL
+ indicating where each 64-bit chunk goes. INFO.REG_WORDS
+ chunks are passed in registers. */
+ unsigned int i;
+ HOST_WIDE_INT bitpos;
+ rtx ret;
+
+ /* assign_parms checks the mode of ENTRY_PARM, so we must
+ use the actual mode here. */
+ ret = gen_rtx_PARALLEL (mode, rtvec_alloc (info.reg_words));
+
+ bitpos = 0;
+ field = TYPE_FIELDS (type);
+ for (i = 0; i < info.reg_words; i++)
+ {
+ rtx reg;
+
+ for (; field; field = TREE_CHAIN (field))
+ if (TREE_CODE (field) == FIELD_DECL
+ && int_bit_position (field) >= bitpos)
+ break;
+
+ if (field
+ && int_bit_position (field) == bitpos
+ && SCALAR_FLOAT_TYPE_P (TREE_TYPE (field))
+ && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
+ reg = gen_rtx_REG (DFmode, FP_ARG_FIRST + info.reg_offset + i);
+ else
+ reg = gen_rtx_REG (DImode, GP_ARG_FIRST + info.reg_offset + i);
+
+ XVECEXP (ret, 0, i)
+ = gen_rtx_EXPR_LIST (VOIDmode, reg,
+ GEN_INT (bitpos / BITS_PER_UNIT));
+
+ bitpos += BITS_PER_WORD;
+ }
+ return ret;
+ }
+ }
+
+ /* Handle the n32/n64 conventions for passing complex floating-point
+ arguments in FPR pairs. The real part goes in the lower register
+ and the imaginary part goes in the upper register. */
+ if (TARGET_NEWABI
+ && info.fpr_p
+ && GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ {
+ rtx real, imag;
+ enum machine_mode inner;
+ unsigned int regno;
+
+ inner = GET_MODE_INNER (mode);
+ regno = FP_ARG_FIRST + info.reg_offset;
+ if (info.reg_words * UNITS_PER_WORD == GET_MODE_SIZE (inner))
+ {
+ /* Real part in registers, imaginary part on stack. */
+ gcc_assert (info.stack_words == info.reg_words);
+ return gen_rtx_REG (inner, regno);
+ }
+ else
+ {
+ gcc_assert (info.stack_words == 0);
+ real = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (inner, regno),
+ const0_rtx);
+ imag = gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (inner,
+ regno + info.reg_words / 2),
+ GEN_INT (GET_MODE_SIZE (inner)));
+ return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag));
+ }
+ }
+
+ return gen_rtx_REG (mode, mips_arg_regno (&info, TARGET_HARD_FLOAT));
+}
+
+/* Implement FUNCTION_ARG_ADVANCE. */
+
+void
+mips_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int named)
+{
+ struct mips_arg_info info;
+
+ mips_get_arg_info (&info, cum, mode, type, named);
+
+ if (!info.fpr_p)
+ cum->gp_reg_found = true;
+
+ /* See the comment above the CUMULATIVE_ARGS structure in mips.h for
+ an explanation of what this code does. It assumes that we're using
+ either the o32 or the o64 ABI, both of which pass at most 2 arguments
+ in FPRs. */
+ if (cum->arg_number < 2 && info.fpr_p)
+ cum->fp_code += (mode == SFmode ? 1 : 2) << (cum->arg_number * 2);
+
+ /* Advance the register count. This has the effect of setting
+ num_gprs to MAX_ARGS_IN_REGISTERS if a doubleword-aligned
+ argument required us to skip the final GPR and pass the whole
+ argument on the stack. */
+ if (mips_abi != ABI_EABI || !info.fpr_p)
+ cum->num_gprs = info.reg_offset + info.reg_words;
+ else if (info.reg_words > 0)
+ cum->num_fprs += MAX_FPRS_PER_FMT;
+
+ /* Advance the stack word count. */
+ if (info.stack_words > 0)
+ cum->stack_words = info.stack_offset + info.stack_words;
+
+ cum->arg_number++;
+}
+
+/* Implement TARGET_ARG_PARTIAL_BYTES. */
+
+static int
+mips_arg_partial_bytes (CUMULATIVE_ARGS *cum,
+ enum machine_mode mode, tree type, bool named)
+{
+ struct mips_arg_info info;
+
+ mips_get_arg_info (&info, cum, mode, type, named);
+ return info.stack_words > 0 ? info.reg_words * UNITS_PER_WORD : 0;
+}
+
+/* Implement FUNCTION_ARG_BOUNDARY. Every parameter gets at least
+ PARM_BOUNDARY bits of alignment, but will be given anything up
+ to STACK_BOUNDARY bits if the type requires it. */
+
+int
+mips_function_arg_boundary (enum machine_mode mode, tree type)
+{
+ unsigned int alignment;
+
+ alignment = type ? TYPE_ALIGN (type) : GET_MODE_ALIGNMENT (mode);
+ if (alignment < PARM_BOUNDARY)
+ alignment = PARM_BOUNDARY;
+ if (alignment > STACK_BOUNDARY)
+ alignment = STACK_BOUNDARY;
+ return alignment;
+}
+
+/* Return true if FUNCTION_ARG_PADDING (MODE, TYPE) should return
+ upward rather than downward. In other words, return true if the
+ first byte of the stack slot has useful data, false if the last
+ byte does. */
+
+bool
+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. */
+ if (!BYTES_BIG_ENDIAN)
+ return true;
+
+ /* Otherwise, integral types are padded downward: the last byte of a
+ stack argument is passed in the last byte of the stack slot. */
+ if (type != 0
+ ? (INTEGRAL_TYPE_P (type)
+ || POINTER_TYPE_P (type)
+ || FIXED_POINT_TYPE_P (type))
+ : (SCALAR_INT_MODE_P (mode)
+ || ALL_SCALAR_FIXED_POINT_MODE_P (mode)))
+ return false;
+
+ /* Big-endian o64 pads floating-point arguments downward. */
+ if (mips_abi == ABI_O64)
+ if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
+ return false;
+
+ /* Other types are padded upward for o32, o64, n32 and n64. */
+ if (mips_abi != ABI_EABI)
+ return true;
+
+ /* Arguments smaller than a stack slot are padded downward. */
+ if (mode != BLKmode)
+ return GET_MODE_BITSIZE (mode) >= PARM_BOUNDARY;
+ else
+ return int_size_in_bytes (type) >= (PARM_BOUNDARY / BITS_PER_UNIT);
+}
+
+/* Likewise BLOCK_REG_PADDING (MODE, TYPE, ...). Return !BYTES_BIG_ENDIAN
+ if the least significant byte of the register has useful data. Return
+ the opposite if the most significant byte does. */
+
+bool
+mips_pad_reg_upward (enum machine_mode mode, tree type)
+{
+ /* No shifting is required for floating-point arguments. */
+ if (type != 0 ? FLOAT_TYPE_P (type) : GET_MODE_CLASS (mode) == MODE_FLOAT)
+ return !BYTES_BIG_ENDIAN;
+
+ /* Otherwise, apply the same padding to register arguments as we do
+ to stack arguments. */
+ return mips_pad_arg_upward (mode, type);
+}
+
+/* Return nonzero when an argument must be passed by reference. */
+
+static bool
+mips_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode, const_tree type,
+ bool named ATTRIBUTE_UNUSED)
+{
+ if (mips_abi == ABI_EABI)
+ {
+ int size;
+
+ /* ??? How should SCmode be handled? */
+ if (mode == DImode || mode == DFmode
+ || mode == DQmode || mode == UDQmode
+ || mode == DAmode || mode == UDAmode)
+ return 0;
+
+ size = type ? int_size_in_bytes (type) : GET_MODE_SIZE (mode);
+ return size == -1 || size > UNITS_PER_WORD;
+ }
+ else
+ {
+ /* If we have a variable-sized parameter, we have no choice. */
+ return targetm.calls.must_pass_in_stack (mode, type);
+ }
+}
+
+/* Implement TARGET_CALLEE_COPIES. */
+
+static bool
+mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ const_tree type ATTRIBUTE_UNUSED, bool named)
+{
+ return mips_abi == ABI_EABI && named;
+}
+\f
+/* See whether VALTYPE is a record whose fields should be returned in
+ floating-point registers. If so, return the number of fields and
+ list them in FIELDS (which should have two elements). Return 0
+ otherwise.
+
+ For n32 & n64, a structure with one or two fields is returned in
+ floating-point registers as long as every field has a floating-point
+ type. */
+
+static int
+mips_fpr_return_fields (const_tree valtype, tree *fields)
+{
+ tree field;
+ int i;
+
+ if (!TARGET_NEWABI)
+ return 0;
+
+ if (TREE_CODE (valtype) != RECORD_TYPE)
+ return 0;
+
+ i = 0;
+ for (field = TYPE_FIELDS (valtype); field != 0; field = TREE_CHAIN (field))
+ {
+ if (TREE_CODE (field) != FIELD_DECL)
+ continue;
+
+ if (!SCALAR_FLOAT_TYPE_P (TREE_TYPE (field)))
+ return 0;
+
+ if (i == 2)
+ return 0;
+
+ fields[i++] = field;
+ }
+ return i;
+}
+
+/* Implement TARGET_RETURN_IN_MSB. For n32 & n64, we should return
+ a value in the most significant part of $2/$3 if:
+
+ - the target is big-endian;
+
+ - the value has a structure or union type (we generalize this to
+ cover aggregates from other languages too); and
+
+ - the structure is not returned in floating-point registers. */
+
+static bool
+mips_return_in_msb (const_tree valtype)
+{
+ tree fields[2];
+
+ return (TARGET_NEWABI
+ && TARGET_BIG_ENDIAN
+ && AGGREGATE_TYPE_P (valtype)
+ && mips_fpr_return_fields (valtype, fields) == 0);
+}
+
+/* Return true if the function return value MODE will get returned in a
+ floating-point register. */
+
+static bool
+mips_return_mode_in_fpr_p (enum machine_mode mode)
+{
+ return ((GET_MODE_CLASS (mode) == MODE_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
+ || GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ && GET_MODE_UNIT_SIZE (mode) <= UNITS_PER_HWFPVALUE);
+}
+
+/* Return the representation of an FPR return register when the
+ value being returned in FP_RETURN has mode VALUE_MODE and the
+ return type itself has mode TYPE_MODE. On NewABI targets,
+ the two modes may be different for structures like:
+
+ struct __attribute__((packed)) foo { float f; }
+
+ where we return the SFmode value of "f" in FP_RETURN, but where
+ the structure itself has mode BLKmode. */
+
+static rtx
+mips_return_fpr_single (enum machine_mode type_mode,
+ enum machine_mode value_mode)
+{
+ rtx x;
+
+ x = gen_rtx_REG (value_mode, FP_RETURN);
+ if (type_mode != value_mode)
+ {
+ x = gen_rtx_EXPR_LIST (VOIDmode, x, const0_rtx);
+ x = gen_rtx_PARALLEL (type_mode, gen_rtvec (1, x));
+ }
+ return x;
+}
+
+/* Return a composite value in a pair of floating-point registers.
+ MODE1 and OFFSET1 are the mode and byte offset for the first value,
+ likewise MODE2 and OFFSET2 for the second. MODE is the mode of the
+ complete value.
+
+ For n32 & n64, $f0 always holds the first value and $f2 the second.
+ Otherwise the values are packed together as closely as possible. */
+
+static rtx
+mips_return_fpr_pair (enum machine_mode mode,
+ enum machine_mode mode1, HOST_WIDE_INT offset1,
+ enum machine_mode mode2, HOST_WIDE_INT offset2)
+{
+ int inc;
+
+ inc = (TARGET_NEWABI ? 2 : MAX_FPRS_PER_FMT);
+ return gen_rtx_PARALLEL
+ (mode,
+ gen_rtvec (2,
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode1, FP_RETURN),
+ GEN_INT (offset1)),
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_REG (mode2, FP_RETURN + inc),
+ GEN_INT (offset2))));
+
+}
+
+/* Implement FUNCTION_VALUE and LIBCALL_VALUE. For normal calls,
+ VALTYPE is the return type and MODE is VOIDmode. For libcalls,
+ VALTYPE is null and MODE is the mode of the return value. */
+
+rtx
+mips_function_value (const_tree valtype, enum machine_mode mode)
+{
+ if (valtype)
+ {
+ tree fields[2];
+ int unsigned_p;
+
+ mode = TYPE_MODE (valtype);
+ unsigned_p = TYPE_UNSIGNED (valtype);
+
+ /* Since TARGET_PROMOTE_FUNCTION_RETURN unconditionally returns true,
+ we must promote the mode just as PROMOTE_MODE does. */
+ mode = promote_mode (valtype, mode, &unsigned_p, 1);
+
+ /* Handle structures whose fields are returned in $f0/$f2. */
+ switch (mips_fpr_return_fields (valtype, fields))
+ {
+ case 1:
+ return mips_return_fpr_single (mode,
+ TYPE_MODE (TREE_TYPE (fields[0])));
+
+ case 2:
+ return mips_return_fpr_pair (mode,
+ TYPE_MODE (TREE_TYPE (fields[0])),
+ int_byte_position (fields[0]),
+ TYPE_MODE (TREE_TYPE (fields[1])),
+ int_byte_position (fields[1]));
+ }
+
+ /* If a value is passed in the most significant part of a register, see
+ whether we have to round the mode up to a whole number of words. */
+ if (mips_return_in_msb (valtype))
+ {
+ HOST_WIDE_INT size = int_size_in_bytes (valtype);
+ if (size % UNITS_PER_WORD != 0)
+ {
+ size += UNITS_PER_WORD - size % UNITS_PER_WORD;
+ mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
+ }
+ }
+
+ /* For EABI, the class of return register depends entirely on MODE.
+ For example, "struct { some_type x; }" and "union { some_type x; }"
+ are returned in the same way as a bare "some_type" would be.
+ Other ABIs only use FPRs for scalar, complex or vector types. */
+ if (mips_abi != ABI_EABI && !FLOAT_TYPE_P (valtype))
+ return gen_rtx_REG (mode, GP_RETURN);
+ }
+
+ if (!TARGET_MIPS16)
+ {
+ /* Handle long doubles for n32 & n64. */
+ if (mode == TFmode)
+ return mips_return_fpr_pair (mode,
+ DImode, 0,
+ DImode, GET_MODE_SIZE (mode) / 2);
+
+ if (mips_return_mode_in_fpr_p (mode))
+ {
+ if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+ return mips_return_fpr_pair (mode,
+ GET_MODE_INNER (mode), 0,
+ GET_MODE_INNER (mode),
+ GET_MODE_SIZE (mode) / 2);
+ else
+ return gen_rtx_REG (mode, FP_RETURN);
+ }
+ }
+
+ return gen_rtx_REG (mode, GP_RETURN);
+}
+
+/* Implement TARGET_RETURN_IN_MEMORY. Under the o32 and o64 ABIs,
+ all BLKmode objects are returned in memory. Under the n32, n64
+ and embedded ABIs, small structures are returned in a register.
+ Objects with varying size must still be returned in memory, of
+ course. */
+
+static bool
+mips_return_in_memory (const_tree type, const_tree fndecl ATTRIBUTE_UNUSED)
+{
+ return (TARGET_OLDABI
+ ? TYPE_MODE (type) == BLKmode
+ : !IN_RANGE (int_size_in_bytes (type), 0, 2 * UNITS_PER_WORD));
+}
+\f
+/* Implement TARGET_SETUP_INCOMING_VARARGS. */
+
+static void
+mips_setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type, int *pretend_size ATTRIBUTE_UNUSED,
+ int no_rtl)
+{
+ CUMULATIVE_ARGS local_cum;
+ int gp_saved, fp_saved;
+
+ /* The caller has advanced CUM up to, but not beyond, the last named
+ argument. Advance a local copy of CUM past the last "real" named
+ argument, to find out how many registers are left over. */
+ local_cum = *cum;
+ FUNCTION_ARG_ADVANCE (local_cum, mode, type, true);
+
+ /* Found out how many registers we need to save. */
+ gp_saved = MAX_ARGS_IN_REGISTERS - local_cum.num_gprs;
+ fp_saved = (EABI_FLOAT_VARARGS_P
+ ? MAX_ARGS_IN_REGISTERS - local_cum.num_fprs
+ : 0);
+
+ if (!no_rtl)
+ {
+ if (gp_saved > 0)
+ {
+ rtx ptr, mem;
+
+ ptr = plus_constant (virtual_incoming_args_rtx,
+ REG_PARM_STACK_SPACE (cfun->decl)
+ - gp_saved * UNITS_PER_WORD);
+ mem = gen_frame_mem (BLKmode, ptr);
+ set_mem_alias_set (mem, get_varargs_alias_set ());
+
+ move_block_from_reg (local_cum.num_gprs + GP_ARG_FIRST,
+ mem, gp_saved);
+ }
+ if (fp_saved > 0)
+ {
+ /* We can't use move_block_from_reg, because it will use
+ the wrong mode. */
+ enum machine_mode mode;
+ int off, i;
+
+ /* Set OFF to the offset from virtual_incoming_args_rtx of
+ the first float register. The FP save area lies below
+ the integer one, and is aligned to UNITS_PER_FPVALUE bytes. */
+ off = (-gp_saved * UNITS_PER_WORD) & -UNITS_PER_FPVALUE;
+ off -= fp_saved * UNITS_PER_FPREG;
+
+ mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
+
+ for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS;
+ i += MAX_FPRS_PER_FMT)
+ {
+ rtx ptr, mem;
+
+ ptr = plus_constant (virtual_incoming_args_rtx, off);
+ mem = gen_frame_mem (mode, ptr);
+ set_mem_alias_set (mem, get_varargs_alias_set ());
+ mips_emit_move (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
+ off += UNITS_PER_HWFPVALUE;
+ }
+ }
+ }
+ if (REG_PARM_STACK_SPACE (cfun->decl) == 0)
+ cfun->machine->varargs_size = (gp_saved * UNITS_PER_WORD
+ + fp_saved * UNITS_PER_FPREG);
+}
+
+/* Implement TARGET_BUILTIN_VA_LIST. */
+
+static tree
+mips_build_builtin_va_list (void)
+{
+ if (EABI_FLOAT_VARARGS_P)
+ {
+ /* We keep 3 pointers, and two offsets.
+
+ Two pointers are to the overflow area, which starts at the CFA.
+ One of these is constant, for addressing into the GPR save area
+ below it. The other is advanced up the stack through the
+ overflow region.
+
+ The third pointer is to the bottom of the GPR save area.
+ Since the FPR save area is just below it, we can address
+ FPR slots off this pointer.
+
+ We also keep two one-byte offsets, which are to be subtracted
+ from the constant pointers to yield addresses in the GPR and
+ FPR save areas. These are downcounted as float or non-float
+ arguments are used, and when they get to zero, the argument
+ must be obtained from the overflow region. */
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, f_res, record;
+ tree array, index;
+
+ record = lang_hooks.types.make_type (RECORD_TYPE);
+
+ f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
+ ptr_type_node);
+ f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"),
+ ptr_type_node);
+ f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"),
+ ptr_type_node);
+ f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"),
+ unsigned_char_type_node);
+ f_foff = build_decl (FIELD_DECL, get_identifier ("__fpr_offset"),
+ unsigned_char_type_node);
+ /* Explicitly pad to the size of a pointer, so that -Wpadded won't
+ warn on every user file. */
+ index = build_int_cst (NULL_TREE, GET_MODE_SIZE (ptr_mode) - 2 - 1);
+ array = build_array_type (unsigned_char_type_node,
+ build_index_type (index));
+ f_res = build_decl (FIELD_DECL, get_identifier ("__reserved"), array);
+
+ DECL_FIELD_CONTEXT (f_ovfl) = record;
+ DECL_FIELD_CONTEXT (f_gtop) = record;
+ DECL_FIELD_CONTEXT (f_ftop) = record;
+ DECL_FIELD_CONTEXT (f_goff) = record;
+ DECL_FIELD_CONTEXT (f_foff) = record;
+ DECL_FIELD_CONTEXT (f_res) = record;
+
+ TYPE_FIELDS (record) = f_ovfl;
+ TREE_CHAIN (f_ovfl) = f_gtop;
+ TREE_CHAIN (f_gtop) = f_ftop;
+ TREE_CHAIN (f_ftop) = f_goff;
+ TREE_CHAIN (f_goff) = f_foff;
+ TREE_CHAIN (f_foff) = f_res;
+
+ layout_type (record);
+ return record;
+ }
+ else if (TARGET_IRIX && TARGET_IRIX6)
+ /* On IRIX 6, this type is 'char *'. */
+ return build_pointer_type (char_type_node);
+ else
+ /* Otherwise, we use 'void *'. */
+ return ptr_type_node;
+}
+
+/* Implement TARGET_EXPAND_BUILTIN_VA_START. */
+
+static void
+mips_va_start (tree valist, rtx nextarg)
+{
+ if (EABI_FLOAT_VARARGS_P)
+ {
+ const CUMULATIVE_ARGS *cum;
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+ tree ovfl, gtop, ftop, goff, foff;
+ tree t;
+ int gpr_save_area_size;
+ int fpr_save_area_size;
+ int fpr_offset;
+
+ cum = &crtl->args.info;
+ gpr_save_area_size
+ = (MAX_ARGS_IN_REGISTERS - cum->num_gprs) * UNITS_PER_WORD;
+ fpr_save_area_size
+ = (MAX_ARGS_IN_REGISTERS - cum->num_fprs) * UNITS_PER_FPREG;
+
+ f_ovfl = TYPE_FIELDS (va_list_type_node);
+ f_gtop = TREE_CHAIN (f_ovfl);
+ f_ftop = TREE_CHAIN (f_gtop);
+ f_goff = TREE_CHAIN (f_ftop);
+ f_foff = TREE_CHAIN (f_goff);
+
+ ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+ NULL_TREE);
+ gtop = build3 (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop,
+ NULL_TREE);
+ ftop = build3 (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop,
+ NULL_TREE);
+ goff = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff,
+ NULL_TREE);
+ foff = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff,
+ NULL_TREE);
+
+ /* Emit code to initialize OVFL, which points to the next varargs
+ stack argument. CUM->STACK_WORDS gives the number of stack
+ words used by named arguments. */
+ t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
+ if (cum->stack_words > 0)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl), t,
+ size_int (cum->stack_words * UNITS_PER_WORD));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Emit code to initialize GTOP, the top of the GPR save area. */
+ t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
+ t = build2 (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Emit code to initialize FTOP, the top of the FPR save area.
+ This address is gpr_save_area_bytes below GTOP, rounded
+ down to the next fp-aligned boundary. */
+ t = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
+ fpr_offset = gpr_save_area_size + UNITS_PER_FPVALUE - 1;
+ fpr_offset &= -UNITS_PER_FPVALUE;
+ if (fpr_offset)
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ftop), t,
+ size_int (-fpr_offset));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (ftop), ftop, t);
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Emit code to initialize GOFF, the offset from GTOP of the
+ next GPR argument. */
+ t = build2 (MODIFY_EXPR, TREE_TYPE (goff), goff,
+ build_int_cst (TREE_TYPE (goff), gpr_save_area_size));
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+
+ /* Likewise emit code to initialize FOFF, the offset from FTOP
+ of the next FPR argument. */
+ t = build2 (MODIFY_EXPR, TREE_TYPE (foff), foff,
+ build_int_cst (TREE_TYPE (foff), fpr_save_area_size));
+ expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ }
+ else
+ {
+ nextarg = plus_constant (nextarg, -cfun->machine->varargs_size);
+ std_expand_builtin_va_start (valist, nextarg);
+ }
+}
+
+/* Implement TARGET_GIMPLIFY_VA_ARG_EXPR. */
+
+static tree
+mips_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+ gimple_seq *post_p)
+{
+ tree addr;
+ bool indirect_p;
+
+ indirect_p = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+ if (indirect_p)
+ type = build_pointer_type (type);
+
+ if (!EABI_FLOAT_VARARGS_P)
+ addr = std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
+ else
+ {
+ tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
+ tree ovfl, top, off, align;
+ HOST_WIDE_INT size, rsize, osize;
+ tree t, u;
+
+ f_ovfl = TYPE_FIELDS (va_list_type_node);
+ f_gtop = TREE_CHAIN (f_ovfl);
+ f_ftop = TREE_CHAIN (f_gtop);
+ f_goff = TREE_CHAIN (f_ftop);
+ f_foff = TREE_CHAIN (f_goff);
+
+ /* Let:
+
+ TOP be the top of the GPR or FPR save area;
+ OFF be the offset from TOP of the next register;
+ ADDR_RTX be the address of the argument;
+ SIZE be the number of bytes in the argument type;
+ RSIZE be the number of bytes used to store the argument
+ when it's in the register save area; and
+ OSIZE be the number of bytes used to store it when it's
+ in the stack overflow area.
+
+ The code we want is:
+
+ 1: off &= -rsize; // round down
+ 2: if (off != 0)
+ 3: {
+ 4: addr_rtx = top - off + (BYTES_BIG_ENDIAN ? RSIZE - SIZE : 0);
+ 5: off -= rsize;
+ 6: }
+ 7: else
+ 8: {
+ 9: ovfl = ((intptr_t) ovfl + osize - 1) & -osize;
+ 10: addr_rtx = ovfl + (BYTES_BIG_ENDIAN ? OSIZE - SIZE : 0);
+ 11: ovfl += osize;
+ 14: }
+
+ [1] and [9] can sometimes be optimized away. */
+
+ ovfl = build3 (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl,
+ NULL_TREE);
+ size = int_size_in_bytes (type);
+
+ if (GET_MODE_CLASS (TYPE_MODE (type)) == MODE_FLOAT
+ && GET_MODE_SIZE (TYPE_MODE (type)) <= UNITS_PER_FPVALUE)
+ {
+ top = build3 (COMPONENT_REF, TREE_TYPE (f_ftop),
+ unshare_expr (valist), f_ftop, NULL_TREE);
+ off = build3 (COMPONENT_REF, TREE_TYPE (f_foff),
+ unshare_expr (valist), f_foff, NULL_TREE);
+
+ /* When va_start saves FPR arguments to the stack, each slot
+ takes up UNITS_PER_HWFPVALUE bytes, regardless of the
+ argument's precision. */
+ rsize = UNITS_PER_HWFPVALUE;
+
+ /* Overflow arguments are padded to UNITS_PER_WORD bytes
+ (= PARM_BOUNDARY bits). This can be different from RSIZE
+ in two cases:
+
+ (1) On 32-bit targets when TYPE is a structure such as:
+
+ struct s { float f; };
+
+ Such structures are passed in paired FPRs, so RSIZE
+ will be 8 bytes. However, the structure only takes
+ up 4 bytes of memory, so OSIZE will only be 4.
+
+ (2) In combinations such as -mgp64 -msingle-float
+ -fshort-double. Doubles passed in registers will then take
+ up 4 (UNITS_PER_HWFPVALUE) bytes, but those passed on the
+ stack take up UNITS_PER_WORD bytes. */
+ osize = MAX (GET_MODE_SIZE (TYPE_MODE (type)), UNITS_PER_WORD);
+ }
+ else
+ {
+ top = build3 (COMPONENT_REF, TREE_TYPE (f_gtop),
+ unshare_expr (valist), f_gtop, NULL_TREE);
+ off = build3 (COMPONENT_REF, TREE_TYPE (f_goff),
+ unshare_expr (valist), f_goff, NULL_TREE);
+ rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+ if (rsize > UNITS_PER_WORD)
+ {
+ /* [1] Emit code for: off &= -rsize. */
+ t = build2 (BIT_AND_EXPR, TREE_TYPE (off), unshare_expr (off),
+ build_int_cst (TREE_TYPE (off), -rsize));
+ gimplify_assign (unshare_expr (off), t, pre_p);
+ }
+ osize = rsize;
+ }
+
+ /* [2] Emit code to branch if off == 0. */
+ t = build2 (NE_EXPR, boolean_type_node, off,
+ build_int_cst (TREE_TYPE (off), 0));
+ addr = build3 (COND_EXPR, ptr_type_node, t, NULL_TREE, NULL_TREE);
+
+ /* [5] Emit code for: off -= rsize. We do this as a form of
+ post-decrement not available to C. */
+ t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize));
+ t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t);
+
+ /* [4] Emit code for:
+ addr_rtx = top - off + (BYTES_BIG_ENDIAN ? RSIZE - SIZE : 0). */
+ t = fold_convert (sizetype, t);
+ t = fold_build1 (NEGATE_EXPR, sizetype, t);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (top), top, t);
+ if (BYTES_BIG_ENDIAN && rsize > size)
+ {
+ u = size_int (rsize - size);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
+ }
+ COND_EXPR_THEN (addr) = t;
+
+ if (osize > UNITS_PER_WORD)
+ {
+ /* [9] Emit: ovfl = ((intptr_t) ovfl + osize - 1) & -osize. */
+ u = size_int (osize - 1);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovfl),
+ unshare_expr (ovfl), u);
+ t = fold_convert (sizetype, t);
+ u = size_int (-osize);
+ t = build2 (BIT_AND_EXPR, sizetype, t, u);
+ t = fold_convert (TREE_TYPE (ovfl), t);
+ align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl),
+ unshare_expr (ovfl), t);
+ }
+ else
+ align = NULL;
+
+ /* [10, 11] Emit code for:
+ addr_rtx = ovfl + (BYTES_BIG_ENDIAN ? OSIZE - SIZE : 0)
+ ovfl += osize. */
+ u = fold_convert (TREE_TYPE (ovfl), build_int_cst (NULL_TREE, osize));
+ t = build2 (POSTINCREMENT_EXPR, TREE_TYPE (ovfl), ovfl, u);
+ if (BYTES_BIG_ENDIAN && osize > size)
+ {
+ u = size_int (osize - size);
+ t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (t), t, u);
+ }
+
+ /* String [9] and [10, 11] together. */
+ if (align)
+ t = build2 (COMPOUND_EXPR, TREE_TYPE (t), align, t);
+ COND_EXPR_ELSE (addr) = t;
+
+ addr = fold_convert (build_pointer_type (type), addr);
+ addr = build_va_arg_indirect_ref (addr);
+ }
+
+ if (indirect_p)
+ addr = build_va_arg_indirect_ref (addr);
+
+ return addr;
+}
+\f
+/* Start a definition of function NAME. MIPS16_P indicates whether the
+ function contains MIPS16 code. */
+
+static void
+mips_start_function_definition (const char *name, bool mips16_p)
+{
+ if (mips16_p)
+ fprintf (asm_out_file, "\t.set\tmips16\n");
+ else
+ fprintf (asm_out_file, "\t.set\tnomips16\n");
+
+ if (!flag_inhibit_size_directive)
+ {
+ fputs ("\t.ent\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n", asm_out_file);
+ }
+
+ ASM_OUTPUT_TYPE_DIRECTIVE (asm_out_file, name, "function");
+
+ /* Start the definition proper. */
+ assemble_name (asm_out_file, name);
+ fputs (":\n", asm_out_file);
+}
+
+/* End a function definition started by mips_start_function_definition. */
+
+static void
+mips_end_function_definition (const char *name)
+{
+ if (!flag_inhibit_size_directive)
+ {
+ fputs ("\t.end\t", asm_out_file);
+ assemble_name (asm_out_file, name);
+ fputs ("\n", asm_out_file);
+ }
+}
+\f
+/* Return true if calls to X can use R_MIPS_CALL* relocations. */
+
+static bool
+mips_ok_for_lazy_binding_p (rtx x)
+{
+ return (TARGET_USE_GOT
+ && GET_CODE (x) == SYMBOL_REF
+ && !SYMBOL_REF_BIND_NOW_P (x)
+ && !mips_symbol_binds_local_p (x));
+}
+
+/* Load function address ADDR into register DEST. TYPE is as for
+ mips_expand_call. Return true if we used an explicit lazy-binding
+ sequence. */
+
+static bool
+mips_load_call_address (enum mips_call_type type, rtx dest, rtx addr)
+{
+ /* If we're generating PIC, and this call is to a global function,
+ try to allow its address to be resolved lazily. This isn't
+ possible for sibcalls when $gp is call-saved because the value
+ of $gp on entry to the stub would be our caller's gp, not ours. */
+ if (TARGET_EXPLICIT_RELOCS
+ && !(type == MIPS_CALL_SIBCALL && TARGET_CALL_SAVED_GP)
+ && mips_ok_for_lazy_binding_p (addr))
+ {
+ addr = mips_got_load (dest, addr, SYMBOL_GOTOFF_CALL);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, addr));
+ return true;
+ }
+ else
+ {
+ mips_emit_move (dest, addr);
+ return false;
+ }
+}
+\f
+/* Each locally-defined hard-float MIPS16 function has a local symbol
+ associated with it. This hash table maps the function symbol (FUNC)
+ to the local symbol (LOCAL). */
+struct mips16_local_alias GTY(()) {
+ rtx func;
+ rtx local;
+};
+static GTY ((param_is (struct mips16_local_alias))) htab_t mips16_local_aliases;
+
+/* Hash table callbacks for mips16_local_aliases. */
+
+static hashval_t
+mips16_local_aliases_hash (const void *entry)
+{
+ const struct mips16_local_alias *alias;
+
+ alias = (const struct mips16_local_alias *) entry;
+ return htab_hash_string (XSTR (alias->func, 0));
+}
+
+static int
+mips16_local_aliases_eq (const void *entry1, const void *entry2)
+{
+ const struct mips16_local_alias *alias1, *alias2;
+
+ alias1 = (const struct mips16_local_alias *) entry1;
+ alias2 = (const struct mips16_local_alias *) entry2;
+ return rtx_equal_p (alias1->func, alias2->func);
+}
+
+/* FUNC is the symbol for a locally-defined hard-float MIPS16 function.
+ Return a local alias for it, creating a new one if necessary. */
+
+static rtx
+mips16_local_alias (rtx func)
+{
+ struct mips16_local_alias *alias, tmp_alias;
+ void **slot;
+
+ /* Create the hash table if this is the first call. */
+ if (mips16_local_aliases == NULL)
+ mips16_local_aliases = htab_create_ggc (37, mips16_local_aliases_hash,
+ mips16_local_aliases_eq, NULL);
+
+ /* Look up the function symbol, creating a new entry if need be. */
+ tmp_alias.func = func;
+ slot = htab_find_slot (mips16_local_aliases, &tmp_alias, INSERT);
+ gcc_assert (slot != NULL);
+
+ alias = (struct mips16_local_alias *) *slot;
+ if (alias == NULL)
+ {
+ const char *func_name, *local_name;
+ rtx local;
+
+ /* Create a new SYMBOL_REF for the local symbol. The choice of
+ __fn_local_* is based on the __fn_stub_* names that we've
+ traditionally used for the non-MIPS16 stub. */
+ func_name = targetm.strip_name_encoding (XSTR (func, 0));
+ local_name = ACONCAT (("__fn_local_", func_name, NULL));
+ local = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (local_name));
+ SYMBOL_REF_FLAGS (local) = SYMBOL_REF_FLAGS (func) | SYMBOL_FLAG_LOCAL;
+
+ /* Create a new structure to represent the mapping. */
+ alias = GGC_NEW (struct mips16_local_alias);
+ alias->func = func;
+ alias->local = local;
+ *slot = alias;
+ }
+ return alias->local;
+}
+\f
+/* A chained list of functions for which mips16_build_call_stub has already
+ generated a stub. NAME is the name of the function and FP_RET_P is true
+ if the function returns a value in floating-point registers. */
+struct mips16_stub {
+ struct mips16_stub *next;
+ char *name;
+ bool fp_ret_p;
+};
+static struct mips16_stub *mips16_stubs;
+
+/* Return a SYMBOL_REF for a MIPS16 function called NAME. */
+
+static rtx
+mips16_stub_function (const char *name)
+{
+ rtx x;
+
+ x = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
+ SYMBOL_REF_FLAGS (x) |= (SYMBOL_FLAG_EXTERNAL | SYMBOL_FLAG_FUNCTION);
+ return x;
+}
+
+/* Return the two-character string that identifies floating-point
+ return mode MODE in the name of a MIPS16 function stub. */
+
+static const char *
+mips16_call_stub_mode_suffix (enum machine_mode mode)
+{
+ if (mode == SFmode)
+ return "sf";
+ else if (mode == DFmode)
+ return "df";
+ else if (mode == SCmode)
+ return "sc";
+ else if (mode == DCmode)
+ return "dc";
+ else if (mode == V2SFmode)
+ return "df";
+ else
+ gcc_unreachable ();
+}
+
+/* Write instructions to move a 32-bit value between general register
+ GPREG and floating-point register FPREG. DIRECTION is 't' to move
+ from GPREG to FPREG and 'f' to move in the opposite direction. */
+
+static void
+mips_output_32bit_xfer (char direction, unsigned int gpreg, unsigned int fpreg)
+{
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg], reg_names[fpreg]);
+}
+
+/* Likewise for 64-bit values. */
+
+static void
+mips_output_64bit_xfer (char direction, unsigned int gpreg, unsigned int fpreg)
+{
+ if (TARGET_64BIT)
+ fprintf (asm_out_file, "\tdm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg], reg_names[fpreg]);
+ else if (TARGET_FLOAT64)
+ {
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]);
+ fprintf (asm_out_file, "\tm%chc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg]);
+ }
+ else
+ {
+ /* Move the least-significant word. */
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_BIG_ENDIAN], reg_names[fpreg]);
+ /* ...then the most significant word. */
+ fprintf (asm_out_file, "\tm%cc1\t%s,%s\n", direction,
+ reg_names[gpreg + TARGET_LITTLE_ENDIAN], reg_names[fpreg + 1]);
+ }
+}
+
+/* Write out code to move floating-point arguments into or out of
+ general registers. FP_CODE is the code describing which arguments
+ are present (see the comment above the definition of CUMULATIVE_ARGS
+ in mips.h). DIRECTION is as for mips_output_32bit_xfer. */
+
+static void
+mips_output_args_xfer (int fp_code, char direction)
+{
+ unsigned int gparg, fparg, f;
+ CUMULATIVE_ARGS cum;
+
+ /* This code only works for o32 and o64. */
+ gcc_assert (TARGET_OLDABI);
+
+ mips_init_cumulative_args (&cum, NULL);
+
+ for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+ {
+ enum machine_mode mode;
+ struct mips_arg_info info;
+
+ if ((f & 3) == 1)
+ mode = SFmode;
+ else if ((f & 3) == 2)
+ mode = DFmode;
+ else
+ gcc_unreachable ();
+
+ mips_get_arg_info (&info, &cum, mode, NULL, true);
+ gparg = mips_arg_regno (&info, false);
+ fparg = mips_arg_regno (&info, true);
+
+ if (mode == SFmode)
+ mips_output_32bit_xfer (direction, gparg, fparg);
+ else
+ mips_output_64bit_xfer (direction, gparg, fparg);
+
+ mips_function_arg_advance (&cum, mode, NULL, true);
+ }
+}
+
+/* Write a MIPS16 stub for the current function. This stub is used
+ for functions which take arguments in the floating-point registers.
+ It is normal-mode code that moves the floating-point arguments
+ into the general registers and then jumps to the MIPS16 code. */
+
+static void
+mips16_build_function_stub (void)
+{
+ const char *fnname, *alias_name, *separator;
+ char *secname, *stubname;
+ tree stubdecl;
+ unsigned int f;
+ rtx symbol, alias;
+
+ /* Create the name of the stub, and its unique section. */
+ symbol = XEXP (DECL_RTL (current_function_decl), 0);
+ alias = mips16_local_alias (symbol);
+
+ fnname = targetm.strip_name_encoding (XSTR (symbol, 0));
+ alias_name = targetm.strip_name_encoding (XSTR (alias, 0));
+ secname = ACONCAT ((".mips16.fn.", fnname, NULL));
+ stubname = ACONCAT (("__fn_stub_", fnname, NULL));
+
+ /* Build a decl for the stub. */
+ stubdecl = build_decl (FUNCTION_DECL, get_identifier (stubname),
+ build_function_type (void_type_node, NULL_TREE));
+ DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+ DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE, void_type_node);
+
+ /* Output a comment. */
+ fprintf (asm_out_file, "\t# Stub function for %s (",
+ current_function_name ());
+ separator = "";
+ for (f = (unsigned int) crtl->args.info.fp_code; f != 0; f >>= 2)
+ {
+ fprintf (asm_out_file, "%s%s", separator,
+ (f & 3) == 1 ? "float" : "double");
+ separator = ", ";
+ }
+ fprintf (asm_out_file, ")\n");
+
+ /* Start the function definition. */
+ assemble_start_function (stubdecl, stubname);
+ mips_start_function_definition (stubname, false);
+
+ /* If generating pic2 code, either set up the global pointer or
+ switch to pic0. */
+ if (TARGET_ABICALLS_PIC2)
+ {
+ if (TARGET_ABSOLUTE_ABICALLS)
+ fprintf (asm_out_file, "\t.option\tpic0\n");
+ else
+ {
+ output_asm_insn ("%(.cpload\t%^%)", NULL);
+ /* Emit an R_MIPS_NONE relocation to tell the linker what the
+ target function is. Use a local GOT access when loading the
+ symbol, to cut down on the number of unnecessary GOT entries
+ for stubs that aren't needed. */
+ output_asm_insn (".reloc\t0,R_MIPS_NONE,%0", &symbol);
+ symbol = alias;
+ }
+ }
+
+ /* Load the address of the MIPS16 function into $25. Do this first so
+ that targets with coprocessor interlocks can use an MFC1 to fill the
+ delay slot. */
+ output_asm_insn ("la\t%^,%0", &symbol);
+
+ /* Move the arguments from floating-point registers to general registers. */
+ mips_output_args_xfer (crtl->args.info.fp_code, 'f');
+
+ /* Jump to the MIPS16 function. */
+ output_asm_insn ("jr\t%^", NULL);
+
+ if (TARGET_ABICALLS_PIC2 && TARGET_ABSOLUTE_ABICALLS)
+ fprintf (asm_out_file, "\t.option\tpic2\n");
+
+ mips_end_function_definition (stubname);
+
+ /* If the linker needs to create a dynamic symbol for the target
+ function, it will associate the symbol with the stub (which,
+ unlike the target function, follows the proper calling conventions).
+ It is therefore useful to have a local alias for the target function,
+ so that it can still be identified as MIPS16 code. As an optimization,
+ this symbol can also be used for indirect MIPS16 references from
+ within this file. */
+ ASM_OUTPUT_DEF (asm_out_file, alias_name, fnname);
+
+ switch_to_section (function_section (current_function_decl));
+}
+
+/* The current function is a MIPS16 function that returns a value in an FPR.
+ Copy the return value from its soft-float to its hard-float location.
+ libgcc2 has special non-MIPS16 helper functions for each case. */
+
+static void
+mips16_copy_fpr_return_value (void)
+{
+ rtx fn, insn, retval;
+ tree return_type;
+ enum machine_mode return_mode;
+ const char *name;
+
+ return_type = DECL_RESULT (current_function_decl);
+ return_mode = DECL_MODE (return_type);
+
+ name = ACONCAT (("__mips16_ret_",
+ mips16_call_stub_mode_suffix (return_mode),
+ NULL));
+ fn = mips16_stub_function (name);
+
+ /* The function takes arguments in $2 (and possibly $3), so calls
+ to it cannot be lazily bound. */
+ SYMBOL_REF_FLAGS (fn) |= SYMBOL_FLAG_BIND_NOW;
+
+ /* Model the call as something that takes the GPR return value as
+ argument and returns an "updated" value. */
+ retval = gen_rtx_REG (return_mode, GP_RETURN);
+ insn = mips_expand_call (MIPS_CALL_EPILOGUE, retval, fn,
+ const0_rtx, NULL_RTX, false);
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), retval);
+}
+
+/* Consider building a stub for a MIPS16 call to function *FN_PTR.
+ RETVAL is the location of the return value, or null if this is
+ a "call" rather than a "call_value". ARGS_SIZE is the size of the
+ arguments and FP_CODE is the code built by mips_function_arg;
+ see the comment above CUMULATIVE_ARGS for details.
+
+ There are three alternatives:
+
+ - If a stub was needed, emit the call and return the call insn itself.
+
+ - If we can avoid using a stub by redirecting the call, set *FN_PTR
+ to the new target and return null.
+
+ - If *FN_PTR doesn't need a stub, return null and leave *FN_PTR
+ unmodified.
+
+ A stub is needed for calls to functions that, in normal mode,
+ receive arguments in FPRs or return values in FPRs. The stub
+ copies the arguments from their soft-float positions to their
+ hard-float positions, calls the real function, then copies the
+ return value from its hard-float position to its soft-float
+ position.
+
+ We can emit a JAL to *FN_PTR even when *FN_PTR might need a stub.
+ If *FN_PTR turns out to be to a non-MIPS16 function, the linker
+ automatically redirects the JAL to the stub, otherwise the JAL
+ continues to call FN directly. */
+
+static rtx
+mips16_build_call_stub (rtx retval, rtx *fn_ptr, rtx args_size, int fp_code)
+{
+ const char *fnname;
+ bool fp_ret_p;
+ struct mips16_stub *l;
+ rtx insn, fn;
+
+ /* 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 (!TARGET_MIPS16 || TARGET_SOFT_FLOAT_ABI)
+ return NULL_RTX;
+
+ /* Figure out whether the value might come back in a floating-point
+ register. */
+ fp_ret_p = retval && mips_return_mode_in_fpr_p (GET_MODE (retval));
+
+ /* We don't need to do anything if there were no floating-point
+ arguments and the value will not be returned in a floating-point
+ register. */
+ if (fp_code == 0 && !fp_ret_p)
+ return NULL_RTX;
+
+ /* We don't need to do anything if this is a call to a special
+ MIPS16 support function. */
+ fn = *fn_ptr;
+ if (mips16_stub_function_p (fn))
+ return NULL_RTX;
+
+ /* This code will only work for o32 and o64 abis. The other ABI's
+ require more sophisticated support. */
+ gcc_assert (TARGET_OLDABI);
+
+ /* If we're calling via a function pointer, use one of the magic
+ libgcc.a stubs provided for each (FP_CODE, FP_RET_P) combination.
+ Each stub expects the function address to arrive in register $2. */
+ if (GET_CODE (fn) != SYMBOL_REF
+ || !call_insn_operand (fn, VOIDmode))
+ {
+ char buf[30];
+ rtx stub_fn, insn, addr;
+ bool lazy_p;
+
+ /* If this is a locally-defined and locally-binding function,
+ avoid the stub by calling the local alias directly. */
+ if (mips16_local_function_p (fn))
+ {
+ *fn_ptr = mips16_local_alias (fn);
+ return NULL_RTX;
+ }
+
+ /* Create a SYMBOL_REF for the libgcc.a function. */
+ if (fp_ret_p)
+ sprintf (buf, "__mips16_call_stub_%s_%d",
+ mips16_call_stub_mode_suffix (GET_MODE (retval)),
+ fp_code);
+ else
+ sprintf (buf, "__mips16_call_stub_%d", fp_code);
+ stub_fn = mips16_stub_function (buf);
+
+ /* The function uses $2 as an argument, so calls to it
+ cannot be lazily bound. */
+ SYMBOL_REF_FLAGS (stub_fn) |= SYMBOL_FLAG_BIND_NOW;
+
+ /* Load the target function into $2. */
+ addr = gen_rtx_REG (Pmode, GP_REG_FIRST + 2);
+ lazy_p = mips_load_call_address (MIPS_CALL_NORMAL, addr, fn);
+
+ /* Emit the call. */
+ insn = mips_expand_call (MIPS_CALL_NORMAL, retval, stub_fn,
+ args_size, NULL_RTX, lazy_p);
+
+ /* Tell GCC that this call does indeed use the value of $2. */
+ use_reg (&CALL_INSN_FUNCTION_USAGE (insn), addr);
+
+ /* If we are handling a floating-point return value, we need to
+ save $18 in the function prologue. Putting a note on the
+ call will mean that df_regs_ever_live_p ($18) will be true if the
+ call is not eliminated, and we can check that in the prologue
+ code. */
+ if (fp_ret_p)
+ CALL_INSN_FUNCTION_USAGE (insn) =
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_REG (word_mode, 18)),
+ CALL_INSN_FUNCTION_USAGE (insn));
+
+ return insn;
+ }
+
+ /* We know the function we are going to call. If we have already
+ built a stub, we don't need to do anything further. */
+ fnname = targetm.strip_name_encoding (XSTR (fn, 0));
+ for (l = mips16_stubs; l != NULL; l = l->next)
+ if (strcmp (l->name, fnname) == 0)
+ break;
+
+ if (l == NULL)
+ {
+ const char *separator;
+ char *secname, *stubname;
+ tree stubid, stubdecl;
+ unsigned int f;
+
+ /* If the function does not return in FPRs, the special stub
+ section is named
+ .mips16.call.FNNAME
+
+ If the function does return in FPRs, the stub section is named
+ .mips16.call.fp.FNNAME
+
+ Build a decl for the stub. */
+ secname = ACONCAT ((".mips16.call.", fp_ret_p ? "fp." : "",
+ fnname, NULL));
+ stubname = ACONCAT (("__call_stub_", fp_ret_p ? "fp_" : "",
+ fnname, NULL));
+ stubid = get_identifier (stubname);
+ stubdecl = build_decl (FUNCTION_DECL, stubid,
+ build_function_type (void_type_node, NULL_TREE));
+ DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+ DECL_RESULT (stubdecl) = build_decl (RESULT_DECL, NULL_TREE,
+ void_type_node);
+
+ /* Output a comment. */
+ fprintf (asm_out_file, "\t# Stub function to call %s%s (",
+ (fp_ret_p
+ ? (GET_MODE (retval) == SFmode ? "float " : "double ")
+ : ""),
+ fnname);
+ separator = "";
+ for (f = (unsigned int) fp_code; f != 0; f >>= 2)
+ {
+ fprintf (asm_out_file, "%s%s", separator,
+ (f & 3) == 1 ? "float" : "double");
+ separator = ", ";
+ }
+ fprintf (asm_out_file, ")\n");
+
+ /* Start the function definition. */
+ assemble_start_function (stubdecl, stubname);
+ mips_start_function_definition (stubname, false);
+
+ if (!fp_ret_p)
+ {
+ /* Load the address of the MIPS16 function into $25. Do this
+ first so that targets with coprocessor interlocks can use
+ an MFC1 to fill the delay slot. */
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ output_asm_insn ("lui\t%^,%%hi(%0)", &fn);
+ output_asm_insn ("addiu\t%^,%^,%%lo(%0)", &fn);
+ }
+ else
+ output_asm_insn ("la\t%^,%0", &fn);
+ }
+
+ /* Move the arguments from general registers to floating-point
+ registers. */
+ mips_output_args_xfer (fp_code, 't');
+
+ if (!fp_ret_p)
+ {
+ /* Jump to the previously-loaded address. */
+ output_asm_insn ("jr\t%^", NULL);
+ }
+ else
+ {
+ /* Save the return address in $18 and call the non-MIPS16 function.
+ The stub's caller knows that $18 might be clobbered, even though
+ $18 is usually a call-saved register. */
+ fprintf (asm_out_file, "\tmove\t%s,%s\n",
+ reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
+ output_asm_insn (MIPS_CALL ("jal", &fn, 0), &fn);
+
+ /* Move the result from floating-point registers to
+ general registers. */
+ switch (GET_MODE (retval))
+ {
+ case SCmode:
+ mips_output_32bit_xfer ('f', GP_RETURN + 1,
+ FP_REG_FIRST + MAX_FPRS_PER_FMT);
+ /* Fall though. */
+ case SFmode:
+ mips_output_32bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+ if (GET_MODE (retval) == SCmode && TARGET_64BIT)
+ {
+ /* On 64-bit targets, complex floats are returned in
+ a single GPR, such that "sd" on a suitably-aligned
+ target would store the value correctly. */
+ fprintf (asm_out_file, "\tdsll\t%s,%s,32\n",
+ reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN],
+ reg_names[GP_RETURN + TARGET_LITTLE_ENDIAN]);
+ fprintf (asm_out_file, "\tor\t%s,%s,%s\n",
+ reg_names[GP_RETURN],
+ reg_names[GP_RETURN],
+ reg_names[GP_RETURN + 1]);
+ }
+ break;
+
+ case DCmode:
+ mips_output_64bit_xfer ('f', GP_RETURN + (8 / UNITS_PER_WORD),
+ FP_REG_FIRST + MAX_FPRS_PER_FMT);
+ /* Fall though. */
+ case DFmode:
+ case V2SFmode:
+ mips_output_64bit_xfer ('f', GP_RETURN, FP_REG_FIRST);
+ break;
+
+ default:
+ gcc_unreachable ();
+ }
+ fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 18]);
+ }
+
+#ifdef ASM_DECLARE_FUNCTION_SIZE
+ ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
+#endif
+
+ mips_end_function_definition (stubname);
+
+ /* Record this stub. */
+ l = XNEW (struct mips16_stub);
+ l->name = xstrdup (fnname);
+ l->fp_ret_p = fp_ret_p;
+ l->next = mips16_stubs;
+ mips16_stubs = l;
+ }
+
+ /* If we expect a floating-point return value, but we've built a
+ stub which does not expect one, then we're in trouble. We can't
+ use the existing stub, because it won't handle the floating-point
+ value. We can't build a new stub, because the linker won't know
+ which stub to use for the various calls in this object file.
+ Fortunately, this case is illegal, since it means that a function
+ was declared in two different ways in a single compilation. */
+ if (fp_ret_p && !l->fp_ret_p)
+ error ("cannot handle inconsistent calls to %qs", fnname);
+
+ if (retval == NULL_RTX)
+ insn = gen_call_internal_direct (fn, args_size);
+ else
+ insn = gen_call_value_internal_direct (retval, fn, args_size);
+ insn = mips_emit_call_insn (insn, fn, fn, false);
+
+ /* If we are calling a stub which handles a floating-point return
+ value, we need to arrange to save $18 in the prologue. We do this
+ by marking the function call as using the register. The prologue
+ will later see that it is used, and emit code to save it. */
+ if (fp_ret_p)
+ CALL_INSN_FUNCTION_USAGE (insn) =
+ gen_rtx_EXPR_LIST (VOIDmode,
+ gen_rtx_CLOBBER (VOIDmode,
+ gen_rtx_REG (word_mode, 18)),
+ CALL_INSN_FUNCTION_USAGE (insn));
+
+ return insn;
+}
+\f
+/* Expand a call of type TYPE. RESULT is where the result will go (null
+ for "call"s and "sibcall"s), ADDR is the address of the function,
+ ARGS_SIZE is the size of the arguments and AUX is the value passed
+ to us by mips_function_arg. LAZY_P is true if this call already
+ involves a lazily-bound function address (such as when calling
+ functions through a MIPS16 hard-float stub).
+
+ Return the call itself. */
+
+rtx
+mips_expand_call (enum mips_call_type type, rtx result, rtx addr,
+ rtx args_size, rtx aux, bool lazy_p)
+{
+ rtx orig_addr, pattern, insn;
+ int fp_code;
+
+ fp_code = aux == 0 ? 0 : (int) GET_MODE (aux);
+ insn = mips16_build_call_stub (result, &addr, args_size, fp_code);
+ if (insn)
+ {
+ gcc_assert (!lazy_p && type == MIPS_CALL_NORMAL);
+ return insn;
+ }
+ ;
+ orig_addr = addr;
+ if (!call_insn_operand (addr, VOIDmode))
+ {
+ if (type == MIPS_CALL_EPILOGUE)
+ addr = MIPS_EPILOGUE_TEMP (Pmode);
+ else
+ addr = gen_reg_rtx (Pmode);
+ lazy_p |= mips_load_call_address (type, addr, orig_addr);
+ }
+
+ if (result == 0)
+ {
+ rtx (*fn) (rtx, rtx);
+
+ if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+ fn = gen_call_split;
+ else if (type == MIPS_CALL_SIBCALL)
+ fn = gen_sibcall_internal;
+ else
+ fn = gen_call_internal;
+
+ pattern = fn (addr, args_size);
+ }
+ else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
+ {
+ /* Handle return values created by mips_return_fpr_pair. */
+ rtx (*fn) (rtx, rtx, rtx, rtx);
+ rtx reg1, reg2;
+
+ if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+ fn = gen_call_value_multiple_split;
+ else if (type == MIPS_CALL_SIBCALL)
+ fn = gen_sibcall_value_multiple_internal;
+ else
+ fn = gen_call_value_multiple_internal;
+
+ reg1 = XEXP (XVECEXP (result, 0, 0), 0);
+ reg2 = XEXP (XVECEXP (result, 0, 1), 0);
+ pattern = fn (reg1, addr, args_size, reg2);
+ }
+ else
+ {
+ rtx (*fn) (rtx, rtx, rtx);
+
+ if (type == MIPS_CALL_EPILOGUE && TARGET_SPLIT_CALLS)
+ fn = gen_call_value_split;
+ else if (type == MIPS_CALL_SIBCALL)
+ fn = gen_sibcall_value_internal;
+ else
+ fn = gen_call_value_internal;
+
+ /* Handle return values created by mips_return_fpr_single. */
+ if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 1)
+ result = XEXP (XVECEXP (result, 0, 0), 0);
+ pattern = fn (result, addr, args_size);
+ }
+
+ return mips_emit_call_insn (pattern, orig_addr, addr, lazy_p);
+}
+
+/* Split call instruction INSN into a $gp-clobbering call and
+ (where necessary) an instruction to restore $gp from its save slot.
+ CALL_PATTERN is the pattern of the new call. */
+
+void
+mips_split_call (rtx insn, rtx call_pattern)
+{
+ rtx new_insn;
+
+ new_insn = emit_call_insn (call_pattern);
+ CALL_INSN_FUNCTION_USAGE (new_insn)
+ = copy_rtx (CALL_INSN_FUNCTION_USAGE (insn));
+ if (!find_reg_note (insn, REG_NORETURN, 0))
+ /* Pick a temporary register that is suitable for both MIPS16 and
+ non-MIPS16 code. $4 and $5 are used for returning complex double
+ values in soft-float code, so $6 is the first suitable candidate. */
+ mips_restore_gp (gen_rtx_REG (Pmode, GP_ARG_FIRST + 2));
+}
+
+/* Implement TARGET_FUNCTION_OK_FOR_SIBCALL. */
+
+static bool
+mips_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+{
+ 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. We only care about cases where the sibling
+ and normal calls would both be direct. */
+ if (decl
+ && mips_use_mips16_mode_p (decl)
+ && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+ return false;
+
+ /* When -minterlink-mips16 is in effect, assume that non-locally-binding
+ functions could be MIPS16 ones unless an attribute explicitly tells
+ us otherwise. */
+ if (TARGET_INTERLINK_MIPS16
+ && decl
+ && (DECL_EXTERNAL (decl) || !targetm.binds_local_p (decl))
+ && !mips_nomips16_decl_p (decl)
+ && const_call_insn_operand (XEXP (DECL_RTL (decl), 0), VOIDmode))
+ return false;
+
+ /* Otherwise OK. */
+ return true;
+}
+\f
+/* Emit code to move general operand SRC into condition-code
+ register DEST given that SCRATCH is a scratch TFmode FPR.
+ The sequence is:
+
+ FP1 = SRC
+ FP2 = 0.0f
+ DEST = FP2 < FP1
+
+ where FP1 and FP2 are single-precision FPRs taken from SCRATCH. */
+
+void
+mips_expand_fcc_reload (rtx dest, rtx src, rtx scratch)
+{
+ rtx fp1, fp2;
+
+ /* Change the source to SFmode. */
+ if (MEM_P (src))
+ src = adjust_address (src, SFmode, 0);
+ else if (REG_P (src) || GET_CODE (src) == SUBREG)
+ src = gen_rtx_REG (SFmode, true_regnum (src));
+
+ fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
+ fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + MAX_FPRS_PER_FMT);
+
+ 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
+/* Emit straight-line code to move LENGTH bytes from SRC to DEST.
+ Assume that the areas do not overlap. */
+
+static void
+mips_block_move_straight (rtx dest, rtx src, HOST_WIDE_INT length)
+{
+ HOST_WIDE_INT offset, delta;
+ unsigned HOST_WIDE_INT bits;
+ int i;
+ enum machine_mode mode;
+ rtx *regs;
+
+ /* Work out how many bits to move at a time. If both operands have
+ half-word alignment, it is usually better to move in half words.
+ For instance, lh/lh/sh/sh is usually better than lwl/lwr/swl/swr
+ and lw/lw/sw/sw is usually better than ldl/ldr/sdl/sdr.
+ Otherwise move word-sized chunks. */
+ if (MEM_ALIGN (src) == BITS_PER_WORD / 2
+ && MEM_ALIGN (dest) == BITS_PER_WORD / 2)
+ bits = BITS_PER_WORD / 2;
+ else
+ bits = BITS_PER_WORD;
+
+ mode = mode_for_size (bits, MODE_INT, 0);
+ delta = bits / BITS_PER_UNIT;
+
+ /* Allocate a buffer for the temporary registers. */
+ regs = XALLOCAVEC (rtx, length / delta);
+
+ /* Load as many BITS-sized chunks as possible. Use a normal load if
+ the source has enough alignment, otherwise use left/right pairs. */
+ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+ {
+ regs[i] = gen_reg_rtx (mode);
+ if (MEM_ALIGN (src) >= bits)
+ mips_emit_move (regs[i], adjust_address (src, mode, offset));
+ else
+ {
+ rtx part = adjust_address (src, BLKmode, offset);
+ if (!mips_expand_ext_as_unaligned_load (regs[i], part, bits, 0))
+ gcc_unreachable ();
+ }
+ }
+
+ /* Copy the chunks to the destination. */
+ for (offset = 0, i = 0; offset + delta <= length; offset += delta, i++)
+ if (MEM_ALIGN (dest) >= bits)
+ mips_emit_move (adjust_address (dest, mode, offset), regs[i]);
+ else
+ {
+ rtx part = adjust_address (dest, BLKmode, offset);
+ if (!mips_expand_ins_as_unaligned_store (part, regs[i], bits, 0))
+ gcc_unreachable ();
+ }
+
+ /* Mop up any left-over bytes. */
+ if (offset < length)
+ {
+ src = adjust_address (src, BLKmode, offset);
+ dest = adjust_address (dest, BLKmode, offset);
+ move_by_pieces (dest, src, length - offset,
+ MIN (MEM_ALIGN (src), MEM_ALIGN (dest)), 0);
+ }
+}
+
+/* Helper function for doing a loop-based block operation on memory
+ reference MEM. Each iteration of the loop will operate on LENGTH
+ bytes of MEM.
+
+ Create a new base register for use within the loop and point it to
+ the start of MEM. Create a new memory reference that uses this
+ register. Store them in *LOOP_REG and *LOOP_MEM respectively. */
+
+static void
+mips_adjust_block_mem (rtx mem, HOST_WIDE_INT length,
+ rtx *loop_reg, rtx *loop_mem)
+{
+ *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
+
+ /* Although the new mem does not refer to a known location,
+ it does keep up to LENGTH bytes of alignment. */
+ *loop_mem = change_address (mem, BLKmode, *loop_reg);
+ set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT));
+}
+
+/* Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER
+ bytes at a time. LENGTH must be at least BYTES_PER_ITER. Assume that
+ the memory regions do not overlap. */
+
+static void
+mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length,
+ HOST_WIDE_INT bytes_per_iter)
+{
+ rtx label, src_reg, dest_reg, final_src;
+ HOST_WIDE_INT leftover;
+
+ leftover = length % bytes_per_iter;
+ length -= leftover;
+
+ /* Create registers and memory references for use within the loop. */
+ mips_adjust_block_mem (src, bytes_per_iter, &src_reg, &src);
+ mips_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest);
+
+ /* Calculate the value that SRC_REG should have after the last iteration
+ of the loop. */
+ final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length),
+ 0, 0, OPTAB_WIDEN);
+
+ /* Emit the start of the loop. */
+ label = gen_label_rtx ();
+ emit_label (label);
+
+ /* Emit the loop body. */
+ mips_block_move_straight (dest, src, bytes_per_iter);
+
+ /* Move on to the next block. */
+ mips_emit_move (src_reg, plus_constant (src_reg, bytes_per_iter));
+ mips_emit_move (dest_reg, plus_constant (dest_reg, bytes_per_iter));
+
+ /* Emit the loop condition. */
+ if (Pmode == DImode)
+ emit_insn (gen_cmpdi (src_reg, final_src));
+ else
+ emit_insn (gen_cmpsi (src_reg, final_src));
+ emit_jump_insn (gen_bne (label));
+
+ /* Mop up any left-over bytes. */
+ if (leftover)
+ mips_block_move_straight (dest, src, leftover);
+}
+
+/* Expand a movmemsi instruction, which copies LENGTH bytes from
+ memory reference SRC to memory reference DEST. */
+
+bool
+mips_expand_block_move (rtx dest, rtx src, rtx length)
+{
+ if (GET_CODE (length) == CONST_INT)
+ {
+ if (INTVAL (length) <= MIPS_MAX_MOVE_BYTES_STRAIGHT)
+ {
+ mips_block_move_straight (dest, src, INTVAL (length));
+ return true;
+ }
+ else if (optimize)
+ {
+ mips_block_move_loop (dest, src, INTVAL (length),
+ MIPS_MAX_MOVE_BYTES_PER_LOOP_ITER);
+ return true;
+ }
+ }
+ return false;
+}
+\f
+/* Expand a loop of synci insns for the address range [BEGIN, END). */
+
+void
+mips_expand_synci_loop (rtx begin, rtx end)
+{
+ rtx inc, label, end_label, cmp_result, mask, length;
+
+ /* Create end_label. */
+ end_label = gen_label_rtx ();
+
+ /* Check if begin equals end. */
+ cmp_result = gen_rtx_EQ (VOIDmode, begin, end);
+ emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+ /* Load INC with the cache line size (rdhwr INC,$1). */
+ inc = gen_reg_rtx (Pmode);
+ emit_insn (Pmode == SImode
+ ? gen_rdhwr_synci_step_si (inc)
+ : gen_rdhwr_synci_step_di (inc));
+
+ /* Check if inc is 0. */
+ cmp_result = gen_rtx_EQ (VOIDmode, inc, const0_rtx);
+ emit_jump_insn (gen_condjump (cmp_result, end_label));
+
+ /* Calculate mask. */
+ mask = mips_force_unary (Pmode, NEG, inc);
+
+ /* Mask out begin by mask. */
+ begin = mips_force_binary (Pmode, AND, begin, mask);
+
+ /* Calculate length. */
+ length = mips_force_binary (Pmode, MINUS, end, begin);
+
+ /* Loop back to here. */
+ label = gen_label_rtx ();
+ emit_label (label);
+
+ emit_insn (gen_synci (begin));
+
+ /* Update length. */
+ mips_emit_binary (MINUS, length, length, inc);
+
+ /* Update begin. */
+ mips_emit_binary (PLUS, begin, begin, inc);
+
+ /* Check if length is greater than 0. */
+ cmp_result = gen_rtx_GT (VOIDmode, length, const0_rtx);
+ emit_jump_insn (gen_condjump (cmp_result, label));
+
+ emit_label (end_label);
+}
+\f
+/* Expand a QI or HI mode atomic memory operation.
+
+ GENERATOR contains a pointer to the gen_* function that generates
+ the SI mode underlying atomic operation using masks that we
+ calculate.
+
+ RESULT is the return register for the operation. Its value is NULL
+ if unused.
+
+ MEM is the location of the atomic access.
+
+ OLDVAL is the first operand for the operation.
+
+ NEWVAL is the optional second operand for the operation. Its value
+ is NULL if unused. */
+
+void
+mips_expand_atomic_qihi (union mips_gen_fn_ptrs generator,
+ rtx result, rtx mem, rtx oldval, rtx newval)
+{
+ rtx orig_addr, memsi_addr, memsi, shift, shiftsi, unshifted_mask;
+ rtx unshifted_mask_reg, mask, inverted_mask, si_op;
+ rtx res = NULL;
+ enum machine_mode mode;
+
+ mode = GET_MODE (mem);
+
+ /* Compute the address of the containing SImode value. */
+ orig_addr = force_reg (Pmode, XEXP (mem, 0));
+ memsi_addr = mips_force_binary (Pmode, AND, orig_addr,
+ force_reg (Pmode, GEN_INT (-4)));
+
+ /* Create a memory reference for it. */
+ memsi = gen_rtx_MEM (SImode, memsi_addr);
+ set_mem_alias_set (memsi, ALIAS_SET_MEMORY_BARRIER);
+ MEM_VOLATILE_P (memsi) = MEM_VOLATILE_P (mem);
+
+ /* Work out the byte offset of the QImode or HImode value,
+ counting from the least significant byte. */
+ shift = mips_force_binary (Pmode, AND, orig_addr, GEN_INT (3));
+ if (TARGET_BIG_ENDIAN)
+ mips_emit_binary (XOR, shift, shift, GEN_INT (mode == QImode ? 3 : 2));
+
+ /* Multiply by eight to convert the shift value from bytes to bits. */
+ mips_emit_binary (ASHIFT, shift, shift, GEN_INT (3));
+
+ /* Make the final shift an SImode value, so that it can be used in
+ SImode operations. */
+ shiftsi = force_reg (SImode, gen_lowpart (SImode, shift));
+
+ /* Set MASK to an inclusive mask of the QImode or HImode value. */
+ unshifted_mask = GEN_INT (GET_MODE_MASK (mode));
+ unshifted_mask_reg = force_reg (SImode, unshifted_mask);
+ mask = mips_force_binary (SImode, ASHIFT, unshifted_mask_reg, shiftsi);
+
+ /* Compute the equivalent exclusive mask. */
+ inverted_mask = gen_reg_rtx (SImode);
+ emit_insn (gen_rtx_SET (VOIDmode, inverted_mask,
+ gen_rtx_NOT (SImode, mask)));
+
+ /* Shift the old value into place. */
+ if (oldval != const0_rtx)
+ {
+ oldval = convert_modes (SImode, mode, oldval, true);
+ oldval = force_reg (SImode, oldval);
+ oldval = mips_force_binary (SImode, ASHIFT, oldval, shiftsi);
+ }
+
+ /* Do the same for the new value. */
+ if (newval && newval != const0_rtx)
+ {
+ newval = convert_modes (SImode, mode, newval, true);
+ newval = force_reg (SImode, newval);
+ newval = mips_force_binary (SImode, ASHIFT, newval, shiftsi);
+ }
+
+ /* Do the SImode atomic access. */
+ if (result)
+ res = gen_reg_rtx (SImode);
+ if (newval)
+ si_op = generator.fn_6 (res, memsi, mask, inverted_mask, oldval, newval);
+ else if (result)
+ si_op = generator.fn_5 (res, memsi, mask, inverted_mask, oldval);
+ else
+ si_op = generator.fn_4 (memsi, mask, inverted_mask, oldval);
+
+ emit_insn (si_op);
+
+ if (result)
+ {
+ /* Shift and convert the result. */
+ mips_emit_binary (AND, res, res, mask);
+ mips_emit_binary (LSHIFTRT, res, res, shiftsi);
+ mips_emit_move (result, gen_lowpart (GET_MODE (result), res));
+ }
+}
+
+/* Return true if it is possible to use left/right accesses for a
+ bitfield of WIDTH bits starting BITPOS bits into *OP. When
+ returning true, update *OP, *LEFT and *RIGHT as follows:
+
+ *OP is a BLKmode reference to the whole field.
+
+ *LEFT is a QImode reference to the first byte if big endian or
+ the last byte if little endian. This address can be used in the
+ left-side instructions (LWL, SWL, LDL, SDL).
+
+ *RIGHT is a QImode reference to the opposite end of the field and
+ can be used in the patterning right-side instruction. */
+
+static bool
+mips_get_unaligned_mem (rtx *op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos,
+ rtx *left, rtx *right)
+{
+ rtx first, last;
+
+ /* Check that the operand really is a MEM. Not all the extv and
+ extzv predicates are checked. */
+ if (!MEM_P (*op))
+ return false;
+
+ /* Check that the size is valid. */
+ if (width != 32 && (!TARGET_64BIT || width != 64))
+ return false;
+
+ /* We can only access byte-aligned values. Since we are always passed
+ a reference to the first byte of the field, it is not necessary to
+ do anything with BITPOS after this check. */
+ if (bitpos % BITS_PER_UNIT != 0)
+ return false;
+
+ /* Reject aligned bitfields: we want to use a normal load or store
+ instead of a left/right pair. */
+ if (MEM_ALIGN (*op) >= width)
+ return false;
+
+ /* Adjust *OP to refer to the whole field. This also has the effect
+ of legitimizing *OP's address for BLKmode, possibly simplifying it. */
+ *op = adjust_address (*op, BLKmode, 0);
+ set_mem_size (*op, GEN_INT (width / BITS_PER_UNIT));
+
+ /* Get references to both ends of the field. We deliberately don't
+ use the original QImode *OP for FIRST since the new BLKmode one
+ might have a simpler address. */
+ first = adjust_address (*op, QImode, 0);
+ last = adjust_address (*op, QImode, width / BITS_PER_UNIT - 1);
+
+ /* Allocate to LEFT and RIGHT according to endianness. LEFT should
+ correspond to the MSB and RIGHT to the LSB. */
+ if (TARGET_BIG_ENDIAN)
+ *left = first, *right = last;
+ else
+ *left = last, *right = first;
+
+ return true;
+}
+
+/* Try to use left/right loads to expand an "extv" or "extzv" pattern.
+ DEST, SRC, WIDTH and BITPOS are the operands passed to the expander;
+ the operation is the equivalent of:
+
+ (set DEST (*_extract SRC WIDTH BITPOS))
+
+ Return true on success. */
+
+bool
+mips_expand_ext_as_unaligned_load (rtx dest, rtx src, HOST_WIDE_INT width,
+ HOST_WIDE_INT bitpos)
+{
+ rtx left, right, temp;
+
+ /* If TARGET_64BIT, the destination of a 32-bit "extz" or "extzv" will
+ be a paradoxical word_mode subreg. This is the only case in which
+ we allow the destination to be larger than the source. */
+ if (GET_CODE (dest) == SUBREG
+ && GET_MODE (dest) == DImode
+ && GET_MODE (SUBREG_REG (dest)) == SImode)
+ dest = SUBREG_REG (dest);
+
+ /* After the above adjustment, the destination must be the same
+ width as the source. */
+ if (GET_MODE_BITSIZE (GET_MODE (dest)) != width)
+ return false;
+
+ if (!mips_get_unaligned_mem (&src, width, bitpos, &left, &right))
+ return false;
+
+ temp = gen_reg_rtx (GET_MODE (dest));
+ if (GET_MODE (dest) == DImode)
+ {
+ emit_insn (gen_mov_ldl (temp, src, left));
+ emit_insn (gen_mov_ldr (dest, copy_rtx (src), right, temp));
+ }
+ else
+ {
+ emit_insn (gen_mov_lwl (temp, src, left));
+ emit_insn (gen_mov_lwr (dest, copy_rtx (src), right, temp));
+ }
+ return true;
+}
+
+/* Try to use left/right stores to expand an "ins" pattern. DEST, WIDTH,
+ BITPOS and SRC are the operands passed to the expander; the operation
+ is the equivalent of:
+
+ (set (zero_extract DEST WIDTH BITPOS) SRC)
+
+ Return true on success. */
+
+bool
+mips_expand_ins_as_unaligned_store (rtx dest, rtx src, HOST_WIDE_INT width,
+ HOST_WIDE_INT bitpos)
+{
+ rtx left, right;
+ enum machine_mode mode;
+
+ if (!mips_get_unaligned_mem (&dest, width, bitpos, &left, &right))
+ return false;
+
+ mode = mode_for_size (width, MODE_INT, 0);
+ src = gen_lowpart (mode, src);
+ if (mode == DImode)
+ {
+ emit_insn (gen_mov_sdl (dest, src, left));
+ emit_insn (gen_mov_sdr (copy_rtx (dest), copy_rtx (src), right));
+ }
+ else
+ {
+ emit_insn (gen_mov_swl (dest, src, left));
+ emit_insn (gen_mov_swr (copy_rtx (dest), copy_rtx (src), right));
+ }
+ return true;
+}
+
+/* Return true if X is a MEM with the same size as MODE. */
+
+bool
+mips_mem_fits_mode_p (enum machine_mode mode, rtx x)
+{
+ rtx size;
+
+ if (!MEM_P (x))
+ return false;
+
+ size = MEM_SIZE (x);
+ return size && INTVAL (size) == GET_MODE_SIZE (mode);
+}
+
+/* Return true if (zero_extract OP WIDTH BITPOS) can be used as the
+ source of an "ext" instruction or the destination of an "ins"
+ instruction. OP must be a register operand and the following
+ conditions must hold:
+
+ 0 <= BITPOS < GET_MODE_BITSIZE (GET_MODE (op))
+ 0 < WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+ 0 < BITPOS + WIDTH <= GET_MODE_BITSIZE (GET_MODE (op))
+
+ Also reject lengths equal to a word as they are better handled
+ by the move patterns. */
+
+bool
+mips_use_ins_ext_p (rtx op, HOST_WIDE_INT width, HOST_WIDE_INT bitpos)
+{
+ if (!ISA_HAS_EXT_INS
+ || !register_operand (op, VOIDmode)
+ || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
+ return false;
+
+ if (!IN_RANGE (width, 1, GET_MODE_BITSIZE (GET_MODE (op)) - 1))
+ return false;
+
+ if (bitpos < 0 || bitpos + width > GET_MODE_BITSIZE (GET_MODE (op)))
+ return false;
+
+ return true;
+}
+
+/* Check if MASK and SHIFT are valid in mask-low-and-shift-left
+ operation if MAXLEN is the maxium length of consecutive bits that
+ can make up MASK. MODE is the mode of the operation. See
+ mask_low_and_shift_len for the actual definition. */
+
+bool
+mask_low_and_shift_p (enum machine_mode mode, rtx mask, rtx shift, int maxlen)
+{
+ return IN_RANGE (mask_low_and_shift_len (mode, mask, shift), 1, maxlen);
+}
+
+/* The canonical form of a mask-low-and-shift-left operation is
+ (and (ashift X SHIFT) MASK) where MASK has the lower SHIFT number of bits
+ cleared. Thus we need to shift MASK to the right before checking if it
+ is a valid mask value. MODE is the mode of the operation. If true
+ return the length of the mask, otherwise return -1. */
+
+int
+mask_low_and_shift_len (enum machine_mode mode, rtx mask, rtx shift)
+{
+ HOST_WIDE_INT shval;
+
+ shval = INTVAL (shift) & (GET_MODE_BITSIZE (mode) - 1);
+ return exact_log2 ((UINTVAL (mask) >> shval) + 1);
+}
+\f
+/* Return true if -msplit-addresses is selected and should be honored.
+
+ -msplit-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 bool
+mips_split_addresses_p (void)
+{
+ return (TARGET_SPLIT_ADDRESSES
+ && optimize
+ && !TARGET_MIPS16
+ && !flag_pic
+ && !ABI_HAS_64BIT_SYMBOLS);
+}
+
+/* (Re-)Initialize mips_split_p, mips_lo_relocs and mips_hi_relocs. */
+
+static void
+mips_init_relocs (void)
+{
+ memset (mips_split_p, '\0', sizeof (mips_split_p));
+ memset (mips_split_hi_p, '\0', sizeof (mips_split_hi_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_p () || 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(";
+ }
+ else 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(";
+
+ if (TARGET_EXPLICIT_RELOCS)
+ {
+ 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_MIPS16)
+ /* Expose the use of $28 as soon as possible. */
+ mips_split_hi_p[SYMBOL_GOT_PAGE_OFST] = true;
+
+ 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_MIPS16)
+ /* Expose the use of $28 as soon as possible. */
+ mips_split_p[SYMBOL_GOT_DISP] = true;
+ }
+ }
- else if (FP_REG_P (regno1))
- {
- delay = DELAY_LOAD;
- if (TARGET_FLOAT64)
- {
- if (!TARGET_64BIT)
- abort_with_insn (insn, "bad move");
+ 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(";
+ }
-#ifdef TARGET_FP_CALL_32
- if (FP_CALL_GP_REG_P (regno0))
- ret = "dmfc1\t%0,%1\n\tmfc1\t%D0,%1\n\tdsrl\t%0,32";
- else
-#endif
- ret = "dmfc1\t%0,%1";
- }
- else
- ret = "mfc1\t%L0,%1\n\tmfc1\t%M0,%D1";
- }
+ mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
+ mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
- else if (MD_REG_P (regno0) && GP_REG_P (regno1) && !TARGET_MIPS16)
- {
- delay = DELAY_HILO;
- if (TARGET_64BIT)
- {
- if (regno0 != HILO_REGNUM)
- ret = "mt%0\t%1";
- else if (regno1 == 0)
- ret = "mtlo\t%.\n\tmthi\t%.";
- }
- else
- ret = "mthi\t%M1\n\tmtlo\t%L1";
- }
+ mips_split_p[SYMBOL_DTPREL] = true;
+ mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
+ mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
- else if (GP_REG_P (regno0) && MD_REG_P (regno1))
- {
- delay = DELAY_HILO;
- if (TARGET_64BIT)
- {
- if (regno1 != HILO_REGNUM)
- ret = "mf%1\t%0";
- }
- else
- ret = "mfhi\t%M0\n\tmflo\t%L0";
- }
+ mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
- else if (TARGET_64BIT)
- ret = "move\t%0,%1";
+ mips_split_p[SYMBOL_TPREL] = true;
+ mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
+ mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
- else if (regno0 != (regno1+1))
- ret = "move\t%0,%1\n\tmove\t%D0,%D1";
+ mips_lo_relocs[SYMBOL_HALF] = "%half(";
+}
- else
- ret = "move\t%D0,%D1\n\tmove\t%0,%1";
- }
+/* If OP is an UNSPEC address, return the address to which it refers,
+ otherwise return OP itself. */
- else if (code1 == CONST_DOUBLE)
- {
- /* Move zero from $0 unless !TARGET_64BIT and recipient
- is 64-bit fp reg, in which case generate a constant. */
- if (op1 != CONST0_RTX (GET_MODE (op1))
- || (TARGET_FLOAT64 && !TARGET_64BIT && FP_REG_P (regno0)))
- {
- if (GET_MODE (op1) == DFmode)
- {
- delay = DELAY_LOAD;
+static rtx
+mips_strip_unspec_address (rtx op)
+{
+ rtx base, offset;
-#ifdef TARGET_FP_CALL_32
- if (FP_CALL_GP_REG_P (regno0))
- {
- if (TARGET_FLOAT64 && !TARGET_64BIT)
- {
- split_double (op1, operands + 2, operands + 3);
- ret = "li\t%0,%2\n\tli\t%D0,%3";
- }
- else
- ret = "li.d\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32";
- }
- else
-#endif
- /* GNU as emits 64-bit code for li.d if the ISA is 3
- or higher. For !TARGET_64BIT && gp registers we
- need to avoid this by using two li instructions
- instead. */
- if (ISA_HAS_64BIT_REGS
- && ! TARGET_64BIT
- && ! FP_REG_P (regno0))
- {
- split_double (op1, operands + 2, operands + 3);
- ret = "li\t%0,%2\n\tli\t%D0,%3";
- }
- else
- ret = "li.d\t%0,%1";
- }
+ split_const (op, &base, &offset);
+ if (UNSPEC_ADDRESS_P (base))
+ op = plus_constant (UNSPEC_ADDRESS (base), INTVAL (offset));
+ return op;
+}
- else if (TARGET_64BIT)
- {
- if (! TARGET_MIPS16)
- ret = "dli\t%0,%1";
- }
+/* Print symbolic operand OP, which is part of a HIGH or LO_SUM
+ in context CONTEXT. RELOCS is the array of relocations to use. */
- else
- {
- split_double (op1, operands + 2, operands + 3);
- ret = "li\t%0,%2\n\tli\t%D0,%3";
- }
- }
+static void
+mips_print_operand_reloc (FILE *file, rtx op, enum mips_symbol_context context,
+ const char **relocs)
+{
+ enum mips_symbol_type symbol_type;
+ const char *p;
+
+ symbol_type = mips_classify_symbolic_expression (op, context);
+ gcc_assert (relocs[symbol_type]);
+
+ fputs (relocs[symbol_type], file);
+ output_addr_const (file, mips_strip_unspec_address (op));
+ for (p = relocs[symbol_type]; *p != 0; p++)
+ if (*p == '(')
+ fputc (')', file);
+}
+
+/* Print the text for PRINT_OPERAND punctation character CH to FILE.
+ The punctuation characters are:
+
+ '(' Start a nested ".set noreorder" block.
+ ')' End a nested ".set noreorder" block.
+ '[' Start a nested ".set noat" block.
+ ']' End a nested ".set noat" block.
+ '<' Start a nested ".set nomacro" block.
+ '>' End a nested ".set nomacro" block.
+ '*' Behave like %(%< if generating a delayed-branch sequence.
+ '#' Print a nop if in a ".set noreorder" block.
+ '/' Like '#', but do nothing within a delayed-branch sequence.
+ '?' Print "l" if mips_branch_likely is true
+ '~' Print a nop if mips_branch_likely is true
+ '.' Print the name of the register with a hard-wired zero (zero or $0).
+ '@' Print the name of the assembler temporary register (at or $1).
+ '^' Print the name of the pic call-through register (t9 or $25).
+ '+' Print the name of the gp register (usually gp or $28).
+ '$' Print the name of the stack pointer register (sp or $29).
+ '|' Print ".set push; .set mips2" if !ISA_HAS_LL_SC.
+ '-' Print ".set pop" under the same conditions for '|'.
- else
- {
- if (GP_REG_P (regno0))
- ret = (TARGET_64BIT
-#ifdef TARGET_FP_CALL_32
- && ! FP_CALL_GP_REG_P (regno0)
-#endif
- ? "move\t%0,%."
- : "move\t%0,%.\n\tmove\t%D0,%.");
+ See also mips_init_print_operand_pucnt. */
- else if (FP_REG_P (regno0))
- {
- delay = DELAY_LOAD;
- ret = (TARGET_64BIT
- ? "dmtc1\t%.,%0"
- : "mtc1\t%.,%0\n\tmtc1\t%.,%D0");
- }
- }
- }
+static void
+mips_print_operand_punctuation (FILE *file, int ch)
+{
+ switch (ch)
+ {
+ case '(':
+ if (set_noreorder++ == 0)
+ fputs (".set\tnoreorder\n\t", file);
+ break;
- else if (code1 == CONST_INT && INTVAL (op1) == 0 && ! TARGET_MIPS16)
- {
- if (GP_REG_P (regno0))
- ret = (TARGET_64BIT
- ? "move\t%0,%."
- : "move\t%0,%.\n\tmove\t%D0,%.");
+ case ')':
+ gcc_assert (set_noreorder > 0);
+ if (--set_noreorder == 0)
+ fputs ("\n\t.set\treorder", file);
+ break;
- else if (FP_REG_P (regno0))
- {
- delay = DELAY_LOAD;
- ret = (TARGET_64BIT
- ? "dmtc1\t%.,%0"
- : (TARGET_FLOAT64
- ? "li.d\t%0,%1"
- : "mtc1\t%.,%0\n\tmtc1\t%.,%D0"));
- }
- else if (MD_REG_P (regno0))
- {
- delay = DELAY_HILO;
- ret = (regno0 == HILO_REGNUM
- ? "mtlo\t%.\n\tmthi\t%."
- : "mt%0\t%.\n");
- }
- }
+ case '[':
+ if (set_noat++ == 0)
+ fputs (".set\tnoat\n\t", file);
+ break;
+
+ case ']':
+ gcc_assert (set_noat > 0);
+ if (--set_noat == 0)
+ fputs ("\n\t.set\tat", file);
+ break;
+
+ case '<':
+ if (set_nomacro++ == 0)
+ fputs (".set\tnomacro\n\t", file);
+ break;
+
+ case '>':
+ gcc_assert (set_nomacro > 0);
+ if (--set_nomacro == 0)
+ fputs ("\n\t.set\tmacro", file);
+ break;
- else if (code1 == CONST_INT && GET_MODE (op0) == DImode
- && GP_REG_P (regno0))
+ case '*':
+ if (final_sequence != 0)
{
- if (TARGET_64BIT)
- {
- if (TARGET_MIPS16)
- {
- if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
- ret = "li\t%0,%1";
- else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
- ret = "li\t%0,%n1\n\tneg\t%0";
- }
- else if (GET_CODE (operands[1]) == SIGN_EXTEND)
- ret = "li\t%0,%1\t\t# %X1";
- else if (HOST_BITS_PER_WIDE_INT < 64)
- /* We can't use 'X' for negative numbers, because then we won't
- get the right value for the upper 32 bits. */
- ret = (INTVAL (op1) < 0
- ? "dli\t%0,%1\t\t\t# %X1"
- : "dli\t%0,%X1\t\t# %1");
- else
- /* We must use 'X', because otherwise LONG_MIN will print as
- a number that the assembler won't accept. */
- ret = "dli\t%0,%X1\t\t# %1";
- }
- else if (HOST_BITS_PER_WIDE_INT < 64)
- {
- operands[2] = GEN_INT (INTVAL (operands[1]) >= 0 ? 0 : -1);
- if (TARGET_MIPS16)
- {
- if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
- ret = "li\t%M0,%2\n\tli\t%L0,%1";
- else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
- {
- operands[2] = GEN_INT (1);
- ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
- }
- }
- else
- ret = "li\t%M0,%2\n\tli\t%L0,%1";
- }
- else
- {
- /* We use multiple shifts here, to avoid warnings about out
- of range shifts on 32 bit hosts. */
- operands[2] = GEN_INT (INTVAL (operands[1]) >> 16 >> 16);
- operands[1]
- = GEN_INT (INTVAL (operands[1]) << 16 << 16 >> 16 >> 16);
- if (TARGET_MIPS16)
- {
- if (INTVAL (op1) >= 0 && INTVAL (op1) <= 0xffff)
- ret = "li\t%M0,%2\n\tli\t%L0,%1";
- else if (INTVAL (op1) < 0 && INTVAL (op1) >= -0xffff)
- {
- operands[2] = GEN_INT (1);
- ret = "li\t%M0,%2\n\tneg\t%M0\n\tli\t%L0,%n1\n\tneg\t%L0";
- }
- }
- else
- ret = "li\t%M0,%2\n\tli\t%L0,%1";
- }
+ mips_print_operand_punctuation (file, '(');
+ mips_print_operand_punctuation (file, '<');
}
+ break;
- else if (code1 == MEM)
- {
- delay = DELAY_LOAD;
+ case '#':
+ if (set_noreorder != 0)
+ fputs ("\n\tnop", file);
+ break;
+
+ case '/':
+ /* Print an extra newline so that the delayed insn is separated
+ from the following ones. This looks neater and is consistent
+ with non-nop delayed sequences. */
+ if (set_noreorder != 0 && final_sequence == 0)
+ fputs ("\n\tnop\n", file);
+ break;
- if (TARGET_STATS)
- mips_count_memory_refs (op1, 2);
+ case '?':
+ if (mips_branch_likely)
+ putc ('l', file);
+ break;
- if (FP_REG_P (regno0))
- ret = "l.d\t%0,%1";
+ case '~':
+ if (mips_branch_likely)
+ fputs ("\n\tnop", file);
+ break;
- else if (TARGET_64BIT)
- {
+ case '.':
+ fputs (reg_names[GP_REG_FIRST + 0], file);
+ break;
-#ifdef TARGET_FP_CALL_32
- if (FP_CALL_GP_REG_P (regno0))
- ret = (double_memory_operand (op1, GET_MODE (op1))
- ? "lwu\t%0,%1\n\tlwu\t%D0,4+%1"
- : "ld\t%0,%1\n\tdsll\t%D0,%0,32\n\tdsrl\t%D0,32\n\tdsrl\t%0,32");
- else
-#endif
- ret = "ld\t%0,%1";
- }
+ case '@':
+ fputs (reg_names[GP_REG_FIRST + 1], file);
+ break;
- else if (double_memory_operand (op1, GET_MODE (op1)))
- ret = (reg_mentioned_p (op0, op1)
- ? "lw\t%D0,%D1\n\tlw\t%0,%1"
- : "lw\t%0,%1\n\tlw\t%D0,%D1");
+ case '^':
+ fputs (reg_names[PIC_FUNCTION_ADDR_REGNUM], file);
+ break;
- if (ret != 0 && MEM_VOLATILE_P (op1))
- {
- size_t i = strlen (ret);
+ case '+':
+ fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
+ break;
- if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
- abort ();
+ case '$':
+ fputs (reg_names[STACK_POINTER_REGNUM], file);
+ break;
- sprintf (volatile_buffer, "%%{%s%%}", ret);
- ret = volatile_buffer;
- }
- }
+ case '|':
+ if (!ISA_HAS_LL_SC)
+ fputs (".set\tpush\n\t.set\tmips2\n\t", file);
+ break;
- else if (code1 == LABEL_REF)
- {
- if (TARGET_STATS)
- mips_count_memory_refs (op1, 2);
-
- if (GET_CODE (operands[1]) == SIGN_EXTEND)
- /* We deliberately remove the 'a' from '%1', so that we don't
- have to add SIGN_EXTEND support to print_operand_address.
- print_operand will just call print_operand_address in this
- case, so there is no problem. */
- ret = "la\t%0,%1";
- else
- ret = "dla\t%0,%a1";
- }
- else if (code1 == SYMBOL_REF || code1 == CONST)
- {
- if (TARGET_MIPS16
- && code1 == CONST
- && GET_CODE (XEXP (op1, 0)) == REG
- && REGNO (XEXP (op1, 0)) == GP_REG_FIRST + 28)
- {
- /* This case arises on the mips16; see
- mips16_gp_pseudo_reg. */
- ret = "move\t%0,%+";
- }
- else if (TARGET_MIPS16
- && code1 == SYMBOL_REF
- && SYMBOL_REF_FLAG (op1)
- && (XSTR (op1, 0)[0] != '*'
- || strncmp (XSTR (op1, 0) + 1,
- LOCAL_LABEL_PREFIX,
- sizeof LOCAL_LABEL_PREFIX - 1) != 0))
- {
- /* This can occur when reloading the address of a GP
- relative symbol on the mips16. */
- ret = "move\t%0,%+\n\taddu\t%0,%%gprel(%a1)";
- }
- else
- {
- if (TARGET_STATS)
- mips_count_memory_refs (op1, 2);
-
- if (GET_CODE (operands[1]) == SIGN_EXTEND)
- /* We deliberately remove the 'a' from '%1', so that we don't
- have to add SIGN_EXTEND support to print_operand_address.
- print_operand will just call print_operand_address in this
- case, so there is no problem. */
- ret = "la\t%0,%1";
- else
- ret = "dla\t%0,%a1";
- }
- }
+ case '-':
+ if (!ISA_HAS_LL_SC)
+ fputs ("\n\t.set\tpop", file);
+ break;
+
+ default:
+ gcc_unreachable ();
+ break;
}
+}
+
+/* Initialize mips_print_operand_punct. */
+
+static void
+mips_init_print_operand_punct (void)
+{
+ const char *p;
+
+ for (p = "()[]<>*#/?~.@^+$|-"; *p; p++)
+ mips_print_operand_punct[(unsigned char) *p] = true;
+}
+
+/* PRINT_OPERAND prefix LETTER refers to the integer branch instruction
+ associated with condition CODE. Print the condition part of the
+ opcode to FILE. */
- else if (code0 == MEM)
+static void
+mips_print_int_branch_condition (FILE *file, enum rtx_code code, int letter)
+{
+ switch (code)
{
- if (code1 == REG)
- {
- int regno1 = REGNO (op1) + subreg_offset1;
+ case EQ:
+ case NE:
+ case GT:
+ case GE:
+ case LT:
+ case LE:
+ case GTU:
+ case GEU:
+ case LTU:
+ case LEU:
+ /* Conveniently, the MIPS names for these conditions are the same
+ as their RTL equivalents. */
+ fputs (GET_RTX_NAME (code), file);
+ break;
- if (FP_REG_P (regno1))
- ret = "s.d\t%1,%0";
+ default:
+ output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+ break;
+ }
+}
- else if (TARGET_64BIT)
- {
+/* Likewise floating-point branches. */
-#ifdef TARGET_FP_CALL_32
- if (FP_CALL_GP_REG_P (regno1))
- ret = "dsll\t%1,32\n\tor\t%1,%D1\n\tsd\t%1,%0";
- else
-#endif
- ret = "sd\t%1,%0";
- }
+static void
+mips_print_float_branch_condition (FILE *file, enum rtx_code code, int letter)
+{
+ switch (code)
+ {
+ case EQ:
+ fputs ("c1f", file);
+ break;
- else if (double_memory_operand (op0, GET_MODE (op0)))
- ret = "sw\t%1,%0\n\tsw\t%D1,%D0";
- }
+ case NE:
+ fputs ("c1t", file);
+ break;
+
+ default:
+ output_operand_lossage ("'%%%c' is not a valid operand prefix", letter);
+ break;
+ }
+}
+
+/* Implement the PRINT_OPERAND macro. The MIPS-specific operand codes are:
+
+ 'X' Print CONST_INT OP in hexadecimal format.
+ 'x' Print the low 16 bits of CONST_INT OP in hexadecimal format.
+ 'd' Print CONST_INT OP in decimal.
+ 'm' Print one less than CONST_INT OP in decimal.
+ 'h' Print the high-part relocation associated with OP, after stripping
+ any outermost HIGH.
+ 'R' Print the low-part relocation associated with OP.
+ 'C' Print the integer branch condition for comparison OP.
+ 'N' Print the inverse of the integer branch condition for comparison OP.
+ 'F' Print the FPU branch condition for comparison OP.
+ 'W' Print the inverse of the FPU branch condition for comparison OP.
+ 'T' Print 'f' for (eq:CC ...), 't' for (ne:CC ...),
+ 'z' for (eq:?I ...), 'n' for (ne:?I ...).
+ 't' Like 'T', but with the EQ/NE cases reversed
+ 'Y' Print mips_fp_conditions[INTVAL (OP)]
+ 'Z' Print OP and a comma for ISA_HAS_8CC, otherwise print nothing.
+ 'q' Print a DSP accumulator register.
+ 'D' Print the second part of a double-word register or memory operand.
+ 'L' Print the low-order register in a double-word register operand.
+ 'M' Print high-order register in a double-word register operand.
+ 'z' Print $0 if OP is zero, otherwise print OP normally. */
+
+void
+mips_print_operand (FILE *file, rtx op, int letter)
+{
+ enum rtx_code code;
+
+ if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+ {
+ mips_print_operand_punctuation (file, letter);
+ return;
+ }
+
+ gcc_assert (op);
+ code = GET_CODE (op);
+
+ switch (letter)
+ {
+ case 'X':
+ if (GET_CODE (op) == CONST_INT)
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'x':
+ if (GET_CODE (op) == CONST_INT)
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op) & 0xffff);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'd':
+ if (GET_CODE (op) == CONST_INT)
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op));
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'm':
+ if (GET_CODE (op) == CONST_INT)
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (op) - 1);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
+
+ case 'h':
+ if (code == HIGH)
+ op = XEXP (op, 0);
+ mips_print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_hi_relocs);
+ break;
+
+ case 'R':
+ mips_print_operand_reloc (file, op, SYMBOL_CONTEXT_LEA, mips_lo_relocs);
+ break;
+
+ case 'C':
+ mips_print_int_branch_condition (file, code, letter);
+ break;
+
+ case 'N':
+ mips_print_int_branch_condition (file, reverse_condition (code), letter);
+ break;
+
+ case 'F':
+ mips_print_float_branch_condition (file, code, letter);
+ break;
+
+ case 'W':
+ mips_print_float_branch_condition (file, reverse_condition (code),
+ letter);
+ break;
+
+ case 'T':
+ case 't':
+ {
+ int truth = (code == NE) == (letter == 'T');
+ fputc ("zfnt"[truth * 2 + (GET_MODE (op) == CCmode)], file);
+ }
+ break;
+
+ case 'Y':
+ if (code == CONST_INT && UINTVAL (op) < ARRAY_SIZE (mips_fp_conditions))
+ fputs (mips_fp_conditions[UINTVAL (op)], file);
+ else
+ output_operand_lossage ("'%%%c' is not a valid operand prefix",
+ letter);
+ break;
- else if (((code1 == CONST_INT && INTVAL (op1) == 0)
- || (code1 == CONST_DOUBLE
- && op1 == CONST0_RTX (GET_MODE (op1))))
- && (TARGET_64BIT
- || double_memory_operand (op0, GET_MODE (op0))))
+ case 'Z':
+ if (ISA_HAS_8CC)
{
- if (TARGET_64BIT)
- ret = "sd\t%.,%0";
- else
- ret = "sw\t%.,%0\n\tsw\t%.,%D0";
+ mips_print_operand (file, op, 0);
+ fputc (',', file);
}
+ break;
- if (TARGET_STATS)
- mips_count_memory_refs (op0, 2);
+ case 'q':
+ if (code == REG && MD_REG_P (REGNO (op)))
+ fprintf (file, "$ac0");
+ else if (code == REG && DSP_ACC_REG_P (REGNO (op)))
+ fprintf (file, "$ac%c", reg_names[REGNO (op)][3]);
+ else
+ output_operand_lossage ("invalid use of '%%%c'", letter);
+ break;
- if (ret != 0 && MEM_VOLATILE_P (op0))
+ default:
+ switch (code)
{
- size_t i = strlen (ret);
+ case REG:
+ {
+ unsigned int regno = REGNO (op);
+ if ((letter == 'M' && TARGET_LITTLE_ENDIAN)
+ || (letter == 'L' && TARGET_BIG_ENDIAN)
+ || letter == 'D')
+ regno++;
+ fprintf (file, "%s", reg_names[regno]);
+ }
+ break;
- if (i > sizeof (volatile_buffer) - sizeof ("%{%}"))
- abort ();
+ case MEM:
+ if (letter == 'D')
+ output_address (plus_constant (XEXP (op, 0), 4));
+ else
+ output_address (XEXP (op, 0));
+ break;
- sprintf (volatile_buffer, "%%{%s%%}", ret);
- ret = volatile_buffer;
+ default:
+ if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
+ fputs (reg_names[GP_REG_FIRST], file);
+ else if (CONST_GP_P (op))
+ fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
+ else
+ output_addr_const (file, mips_strip_unspec_address (op));
+ break;
}
}
+}
- if (ret == 0)
- {
- abort_with_insn (insn, "bad move");
- return 0;
- }
+/* Output address operand X to FILE. */
+
+void
+mips_print_operand_address (FILE *file, rtx x)
+{
+ struct mips_address_info addr;
+
+ if (mips_classify_address (&addr, x, word_mode, true))
+ switch (addr.type)
+ {
+ case ADDRESS_REG:
+ mips_print_operand (file, addr.offset, 0);
+ fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+ return;
+
+ case ADDRESS_LO_SUM:
+ mips_print_operand_reloc (file, addr.offset, SYMBOL_CONTEXT_MEM,
+ mips_lo_relocs);
+ fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
+ return;
- if (delay != DELAY_NONE)
- return mips_fill_delay_slot (ret, delay, operands, insn);
+ case ADDRESS_CONST_INT:
+ output_addr_const (file, x);
+ fprintf (file, "(%s)", reg_names[GP_REG_FIRST]);
+ return;
- return ret;
+ case ADDRESS_SYMBOLIC:
+ output_addr_const (file, mips_strip_unspec_address (x));
+ return;
+ }
+ gcc_unreachable ();
}
\f
-/* Provide the costs of an addressing mode that contains ADDR.
- If ADDR is not a valid address, its cost is irrelevant. */
+/* Implement TARGET_ENCODE_SECTION_INFO. */
-int
-mips_address_cost (addr)
- rtx addr;
+static void
+mips_encode_section_info (tree decl, rtx rtl, int first)
{
- switch (GET_CODE (addr))
+ default_encode_section_info (decl, rtl, first);
+
+ if (TREE_CODE (decl) == FUNCTION_DECL)
{
- case LO_SUM:
- return 1;
+ rtx symbol = XEXP (rtl, 0);
+ tree type = TREE_TYPE (decl);
+
+ /* Encode whether the symbol is short or long. */
+ if ((TARGET_LONG_CALLS && !mips_near_type_p (type))
+ || mips_far_type_p (type))
+ SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
+ }
+}
+
+/* Implement TARGET_SELECT_RTX_SECTION. */
+
+static section *
+mips_select_rtx_section (enum machine_mode mode, rtx x,
+ unsigned HOST_WIDE_INT align)
+{
+ /* ??? Consider using mergeable small data sections. */
+ if (mips_rtx_constant_in_small_data_p (mode))
+ return get_named_section (NULL, ".sdata", 0);
+
+ return default_elf_select_rtx_section (mode, x, align);
+}
- case LABEL_REF:
- return 2;
+/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
- case CONST:
- {
- rtx offset = const0_rtx;
- addr = eliminate_constant_term (XEXP (addr, 0), &offset);
- if (GET_CODE (addr) == LABEL_REF)
- return 2;
+ The complication here is that, with the combination TARGET_ABICALLS
+ && !TARGET_ABSOLUTE_ABICALLS && !TARGET_GPWORD, jump tables will use
+ absolute addresses, and should therefore not be included in the
+ read-only part of a DSO. Handle such cases by selecting a normal
+ data section instead of a read-only one. The logic apes that in
+ default_function_rodata_section. */
- if (GET_CODE (addr) != SYMBOL_REF)
- return 4;
+static section *
+mips_function_rodata_section (tree decl)
+{
+ if (!TARGET_ABICALLS || TARGET_ABSOLUTE_ABICALLS || TARGET_GPWORD)
+ return default_function_rodata_section (decl);
- if (! SMALL_INT (offset))
- return 2;
- }
+ if (decl && DECL_SECTION_NAME (decl))
+ {
+ const char *name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ if (DECL_ONE_ONLY (decl) && strncmp (name, ".gnu.linkonce.t.", 16) == 0)
+ {
+ char *rname = ASTRDUP (name);
+ rname[14] = 'd';
+ return get_section (rname, SECTION_LINKONCE | SECTION_WRITE, decl);
+ }
+ else if (flag_function_sections
+ && flag_data_sections
+ && strncmp (name, ".text.", 6) == 0)
+ {
+ char *rname = ASTRDUP (name);
+ memcpy (rname + 1, "data", 4);
+ return get_section (rname, SECTION_WRITE, decl);
+ }
+ }
+ return data_section;
+}
- /* ... fall through ... */
+/* Implement TARGET_IN_SMALL_DATA_P. */
- case SYMBOL_REF:
- return SYMBOL_REF_FLAG (addr) ? 1 : 2;
+static bool
+mips_in_small_data_p (const_tree decl)
+{
+ unsigned HOST_WIDE_INT size;
- case PLUS:
- {
- register rtx plus0 = XEXP (addr, 0);
- register rtx plus1 = XEXP (addr, 1);
+ if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
+ return false;
- if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG)
- plus0 = XEXP (addr, 1), plus1 = XEXP (addr, 0);
+ /* We don't yet generate small-data references for -mabicalls
+ or VxWorks RTP code. See the related -G handling in
+ mips_override_options. */
+ if (TARGET_ABICALLS || TARGET_VXWORKS_RTP)
+ return false;
- if (GET_CODE (plus0) != REG)
- break;
+ if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
+ {
+ const char *name;
- switch (GET_CODE (plus1))
- {
- case CONST_INT:
- return SMALL_INT (plus1) ? 1 : 2;
+ /* Reject anything that isn't in a known small-data section. */
+ name = TREE_STRING_POINTER (DECL_SECTION_NAME (decl));
+ if (strcmp (name, ".sdata") != 0 && strcmp (name, ".sbss") != 0)
+ return false;
+
+ /* If a symbol is defined externally, the assembler will use the
+ usual -G rules when deciding how to implement macros. */
+ if (mips_lo_relocs[SYMBOL_GP_RELATIVE] || !DECL_EXTERNAL (decl))
+ return true;
+ }
+ else if (TARGET_EMBEDDED_DATA)
+ {
+ /* Don't put constants into the small data section: we want them
+ to be in ROM rather than RAM. */
+ if (TREE_CODE (decl) != VAR_DECL)
+ return false;
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- case HIGH:
- case LO_SUM:
- return mips_address_cost (plus1) + 1;
+ if (TREE_READONLY (decl)
+ && !TREE_SIDE_EFFECTS (decl)
+ && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (decl))))
+ return false;
+ }
- default:
- break;
- }
- }
+ /* Enforce -mlocal-sdata. */
+ if (!TARGET_LOCAL_SDATA && !TREE_PUBLIC (decl))
+ return false;
- default:
- break;
+ /* 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;
}
- return 4;
+ /* We have traditionally not treated zero-sized objects as small data,
+ so this is now effectively part of the ABI. */
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ return size > 0 && size <= mips_small_data_threshold;
}
-/* Return nonzero if X is an address which needs a temporary register when
- reloaded while generating PIC code. */
+/* Implement TARGET_USE_ANCHORS_FOR_SYMBOL_P. We don't want to use
+ anchors for small data: the GP register acts as an anchor in that
+ case. We also don't want to use them for PC-relative accesses,
+ where the PC acts as an anchor. */
-int
-pic_address_needs_scratch (x)
- rtx x;
+static bool
+mips_use_anchors_for_symbol_p (const_rtx symbol)
{
- /* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */
- if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS
- && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF
- && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
- && ! SMALL_INT (XEXP (XEXP (x, 0), 1)))
- return 1;
+ switch (mips_classify_symbol (symbol, SYMBOL_CONTEXT_MEM))
+ {
+ case SYMBOL_PC_RELATIVE:
+ case SYMBOL_GP_RELATIVE:
+ return false;
- return 0;
+ default:
+ return default_use_anchors_for_symbol_p (symbol);
+ }
}
\f
-/* Make normal rtx_code into something we can index from an array */
+/* The MIPS debug format wants all automatic variables and arguments
+ to be in terms of the virtual frame pointer (stack pointer before
+ any adjustment in the function), while the MIPS 3.0 linker wants
+ the frame pointer to be the stack pointer after the initial
+ adjustment. So, we do the adjustment here. The arg pointer (which
+ is eliminated) points to the virtual frame pointer, while the frame
+ pointer (which may be eliminated) points to the stack pointer after
+ the initial adjustments. */
-static enum internal_test
-map_test_to_internal_test (test_code)
- enum rtx_code test_code;
+HOST_WIDE_INT
+mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
{
- enum internal_test test = ITEST_MAX;
+ rtx offset2 = const0_rtx;
+ rtx reg = eliminate_constant_term (addr, &offset2);
+
+ if (offset == 0)
+ offset = INTVAL (offset2);
- switch (test_code)
+ if (reg == stack_pointer_rtx
+ || reg == frame_pointer_rtx
+ || reg == hard_frame_pointer_rtx)
{
- case EQ: test = ITEST_EQ; break;
- case NE: test = ITEST_NE; break;
- case GT: test = ITEST_GT; break;
- case GE: test = ITEST_GE; break;
- case LT: test = ITEST_LT; break;
- case LE: test = ITEST_LE; break;
- case GTU: test = ITEST_GTU; break;
- case GEU: test = ITEST_GEU; break;
- case LTU: test = ITEST_LTU; break;
- case LEU: test = ITEST_LEU; break;
- default: break;
+ offset -= cfun->machine->frame.total_size;
+ if (reg == hard_frame_pointer_rtx)
+ offset += cfun->machine->frame.hard_frame_pointer_offset;
}
- return test;
-}
+ /* sdbout_parms does not want this to crash for unrecognized cases. */
+#if 0
+ else if (reg != arg_pointer_rtx)
+ fatal_insn ("mips_debugger_offset called with non stack/frame/arg pointer",
+ addr);
+#endif
+ return offset;
+}
\f
-/* Generate the code to compare two integer values. The return value is:
- (reg:SI xx) The pseudo register the comparison is in
- 0 No register, generate a simple branch.
-
- ??? This is called with result nonzero by the Scond patterns in
- mips.md. These patterns are called with a target in the mode of
- the Scond instruction pattern. Since this must be a constant, we
- must use SImode. This means that if RESULT is non-zero, it will
- always be an SImode register, even if TARGET_64BIT is true. We
- cope with this by calling convert_move rather than emit_move_insn.
- This will sometimes lead to an unnecessary extension of the result;
- for example:
-
- long long
- foo (long long i)
- {
- return i < 5;
- }
-
- */
-
-rtx
-gen_int_relational (test_code, result, cmp0, cmp1, p_invert)
- enum rtx_code test_code; /* relational test (EQ, etc) */
- rtx result; /* result to store comp. or 0 if branch */
- rtx cmp0; /* first operand to compare */
- rtx cmp1; /* second operand to compare */
- int *p_invert; /* NULL or ptr to hold whether branch needs */
- /* to reverse its test */
-{
- struct cmp_info
- {
- enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */
- int const_low; /* low bound of constant we can accept */
- int const_high; /* high bound of constant we can accept */
- int const_add; /* constant to add (convert LE -> LT) */
- int reverse_regs; /* reverse registers in test */
- int invert_const; /* != 0 if invert value if cmp1 is constant */
- int invert_reg; /* != 0 if invert value if cmp1 is register */
- int unsignedp; /* != 0 for unsigned comparisons. */
- };
-
- static struct cmp_info info[ (int)ITEST_MAX ] = {
-
- { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */
- { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */
- { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */
- { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */
- { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */
- { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */
- { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */
- { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */
- { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */
- { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */
- };
-
- enum internal_test test;
- enum machine_mode mode;
- struct cmp_info *p_info;
- int branch_p;
- int eqne_p;
- int invert;
- rtx reg;
- rtx reg2;
-
- test = map_test_to_internal_test (test_code);
- if (test == ITEST_MAX)
- abort ();
+/* Implement ASM_OUTPUT_EXTERNAL. */
- p_info = &info[(int) test];
- eqne_p = (p_info->test_code == XOR);
-
- mode = GET_MODE (cmp0);
- if (mode == VOIDmode)
- mode = GET_MODE (cmp1);
+void
+mips_output_external (FILE *file, tree decl, const char *name)
+{
+ default_elf_asm_output_external (file, decl, name);
- /* Eliminate simple branches */
- branch_p = (result == 0);
- if (branch_p)
+ /* We output the name if and only if TREE_SYMBOL_REFERENCED is
+ set in order to avoid putting out names that are never really
+ used. */
+ if (TREE_SYMBOL_REFERENCED (DECL_ASSEMBLER_NAME (decl)))
{
- if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG)
+ if (!TARGET_EXPLICIT_RELOCS && mips_in_small_data_p (decl))
{
- /* Comparisons against zero are simple branches */
- if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0
- && (! TARGET_MIPS16 || eqne_p))
- return 0;
-
- /* Test for beq/bne. */
- if (eqne_p && ! TARGET_MIPS16)
- return 0;
- }
+ /* When using assembler macros, emit .extern directives for
+ all small-data externs so that the assembler knows how
+ big they are.
- /* allocate a pseudo to calculate the value in. */
- result = gen_reg_rtx (mode);
+ In most cases it would be safe (though pointless) to emit
+ .externs for other symbols too. One exception is when an
+ object is within the -G limit but declared by the user to
+ be in a section other than .sbss or .sdata. */
+ fputs ("\t.extern\t", file);
+ assemble_name (file, name);
+ fprintf (file, ", " HOST_WIDE_INT_PRINT_DEC "\n",
+ int_size_in_bytes (TREE_TYPE (decl)));
+ }
+ else if (TARGET_IRIX
+ && mips_abi == ABI_32
+ && TREE_CODE (decl) == FUNCTION_DECL)
+ {
+ /* In IRIX 5 or IRIX 6 for the O32 ABI, we must output a
+ `.global name .text' directive for every used but
+ undefined function. If we don't, the linker may perform
+ an optimization (skipping over the insns that set $gp)
+ when it is unsafe. */
+ fputs ("\t.globl ", file);
+ assemble_name (file, name);
+ fputs (" .text\n", file);
+ }
}
+}
- /* Make sure we can handle any constants given to us. */
- if (GET_CODE (cmp0) == CONST_INT)
- cmp0 = force_reg (mode, cmp0);
+/* Implement ASM_OUTPUT_SOURCE_FILENAME. */
- if (GET_CODE (cmp1) == CONST_INT)
+void
+mips_output_filename (FILE *stream, const char *name)
+{
+ /* If we are emitting DWARF-2, let dwarf2out handle the ".file"
+ directives. */
+ if (write_symbols == DWARF2_DEBUG)
+ return;
+ else if (mips_output_filename_first_time)
{
- HOST_WIDE_INT value = INTVAL (cmp1);
-
- if (value < p_info->const_low
- || value > p_info->const_high
- /* ??? Why? And why wasn't the similar code below modified too? */
- || (TARGET_64BIT
- && HOST_BITS_PER_WIDE_INT < 64
- && p_info->const_add != 0
- && ((p_info->unsignedp
- ? ((unsigned HOST_WIDE_INT) (value + p_info->const_add)
- > (unsigned HOST_WIDE_INT) INTVAL (cmp1))
- : (value + p_info->const_add) > INTVAL (cmp1))
- != (p_info->const_add > 0))))
- cmp1 = force_reg (mode, cmp1);
+ mips_output_filename_first_time = 0;
+ num_source_filenames += 1;
+ current_function_file = name;
+ fprintf (stream, "\t.file\t%d ", num_source_filenames);
+ output_quoted_string (stream, name);
+ putc ('\n', stream);
}
-
- /* See if we need to invert the result. */
- invert = (GET_CODE (cmp1) == CONST_INT
- ? p_info->invert_const : p_info->invert_reg);
-
- if (p_invert != (int *)0)
+ /* If we are emitting stabs, let dbxout.c handle this (except for
+ the mips_output_filename_first_time case). */
+ else if (write_symbols == DBX_DEBUG)
+ return;
+ else if (name != current_function_file
+ && strcmp (name, current_function_file) != 0)
{
- *p_invert = invert;
- invert = 0;
+ num_source_filenames += 1;
+ current_function_file = name;
+ fprintf (stream, "\t.file\t%d ", num_source_filenames);
+ output_quoted_string (stream, name);
+ putc ('\n', stream);
}
+}
- /* Comparison to constants, may involve adding 1 to change a LT into LE.
- Comparison between two registers, may involve switching operands. */
- if (GET_CODE (cmp1) == CONST_INT)
- {
- if (p_info->const_add != 0)
- {
- HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add;
-
- /* If modification of cmp1 caused overflow,
- we would get the wrong answer if we follow the usual path;
- thus, x > 0xffffffffU would turn into x > 0U. */
- if ((p_info->unsignedp
- ? (unsigned HOST_WIDE_INT) new >
- (unsigned HOST_WIDE_INT) INTVAL (cmp1)
- : new > INTVAL (cmp1))
- != (p_info->const_add > 0))
- {
- /* This test is always true, but if INVERT is true then
- the result of the test needs to be inverted so 0 should
- be returned instead. */
- emit_move_insn (result, invert ? const0_rtx : const_true_rtx);
- return result;
- }
- else
- cmp1 = GEN_INT (new);
- }
- }
+/* Implement TARGET_ASM_OUTPUT_DWARF_DTPREL. */
- else if (p_info->reverse_regs)
+static void ATTRIBUTE_UNUSED
+mips_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+ switch (size)
{
- rtx temp = cmp0;
- cmp0 = cmp1;
- cmp1 = temp;
+ case 4:
+ fputs ("\t.dtprelword\t", file);
+ break;
+
+ case 8:
+ fputs ("\t.dtpreldword\t", file);
+ break;
+
+ default:
+ gcc_unreachable ();
}
+ output_addr_const (file, x);
+ fputs ("+0x8000", file);
+}
- if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0)
- reg = cmp0;
- else
+/* Implement TARGET_DWARF_REGISTER_SPAN. */
+
+static rtx
+mips_dwarf_register_span (rtx reg)
+{
+ rtx high, low;
+ enum machine_mode mode;
+
+ /* By default, GCC maps increasing register numbers to increasing
+ memory locations, but paired FPRs are always little-endian,
+ regardless of the prevailing endianness. */
+ mode = GET_MODE (reg);
+ if (FP_REG_P (REGNO (reg))
+ && TARGET_BIG_ENDIAN
+ && MAX_FPRS_PER_FMT > 1
+ && GET_MODE_SIZE (mode) > UNITS_PER_FPREG)
{
- reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result;
- convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0);
+ gcc_assert (GET_MODE_SIZE (mode) == UNITS_PER_HWFPVALUE);
+ high = mips_subword (reg, true);
+ low = mips_subword (reg, false);
+ return gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, high, low));
}
- if (test == ITEST_NE)
+ return NULL_RTX;
+}
+
+/* Implement ASM_OUTPUT_ASCII. */
+
+void
+mips_output_ascii (FILE *stream, const char *string, size_t len)
+{
+ size_t i;
+ int cur_pos;
+
+ cur_pos = 17;
+ fprintf (stream, "\t.ascii\t\"");
+ for (i = 0; i < len; i++)
{
- if (! TARGET_MIPS16)
+ int c;
+
+ c = (unsigned char) string[i];
+ if (ISPRINT (c))
{
- convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0);
- if (p_invert != NULL)
- *p_invert = 0;
- invert = 0;
+ if (c == '\\' || c == '\"')
+ {
+ putc ('\\', stream);
+ cur_pos++;
+ }
+ putc (c, stream);
+ cur_pos++;
}
else
{
- reg2 = invert ? gen_reg_rtx (mode) : result;
- convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0);
- reg = reg2;
+ fprintf (stream, "\\%03o", c);
+ cur_pos += 4;
+ }
+
+ if (cur_pos > 72 && i+1 < len)
+ {
+ cur_pos = 17;
+ fprintf (stream, "\"\n\t.ascii\t\"");
}
}
+ fprintf (stream, "\"\n");
+}
+
+/* Emit either a label, .comm, or .lcomm directive. When using assembler
+ macros, mark the symbol as written so that mips_asm_output_external
+ won't emit an .extern for it. STREAM is the output file, NAME is the
+ name of the symbol, INIT_STRING is the string that should be written
+ before the symbol and FINAL_STRING is the string that should be
+ written after it. FINAL_STRING is a printf format that consumes the
+ remaining arguments. */
+
+void
+mips_declare_object (FILE *stream, const char *name, const char *init_string,
+ const char *final_string, ...)
+{
+ va_list ap;
+
+ fputs (init_string, stream);
+ assemble_name (stream, name);
+ va_start (ap, final_string);
+ vfprintf (stream, final_string, ap);
+ va_end (ap);
- else if (test == ITEST_EQ)
+ if (!TARGET_EXPLICIT_RELOCS)
{
- reg2 = invert ? gen_reg_rtx (mode) : result;
- convert_move (reg2, gen_rtx_LTU (mode, reg, const1_rtx), 0);
- reg = reg2;
+ tree name_tree = get_identifier (name);
+ TREE_ASM_WRITTEN (name_tree) = 1;
}
+}
- if (invert)
- {
- rtx one;
+/* Declare a common object of SIZE bytes using asm directive INIT_STRING.
+ NAME is the name of the object and ALIGN is the required alignment
+ in bytes. TAKES_ALIGNMENT_P is true if the directive takes a third
+ alignment argument. */
- if (! TARGET_MIPS16)
- one = const1_rtx;
- else
- {
- /* The value is in $24. Copy it to another register, so
- that reload doesn't think it needs to store the $24 and
- the input to the XOR in the same location. */
- reg2 = gen_reg_rtx (mode);
- emit_move_insn (reg2, reg);
- reg = reg2;
- one = force_reg (mode, const1_rtx);
- }
- convert_move (result, gen_rtx (XOR, mode, reg, one), 0);
+void
+mips_declare_common_object (FILE *stream, const char *name,
+ const char *init_string,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align, bool takes_alignment_p)
+{
+ if (!takes_alignment_p)
+ {
+ size += (align / BITS_PER_UNIT) - 1;
+ size -= size % (align / BITS_PER_UNIT);
+ mips_declare_object (stream, name, init_string,
+ "," HOST_WIDE_INT_PRINT_UNSIGNED "\n", size);
}
+ else
+ mips_declare_object (stream, name, init_string,
+ "," HOST_WIDE_INT_PRINT_UNSIGNED ",%u\n",
+ size, align / BITS_PER_UNIT);
+}
+
+/* Implement ASM_OUTPUT_ALIGNED_DECL_COMMON. This is usually the same as the
+ elfos.h version, but we also need to handle -muninit-const-in-rodata. */
- return result;
+void
+mips_output_aligned_decl_common (FILE *stream, tree decl, const char *name,
+ unsigned HOST_WIDE_INT size,
+ unsigned int align)
+{
+ /* If the target wants uninitialized const declarations in
+ .rdata then don't put them in .comm. */
+ if (TARGET_EMBEDDED_DATA
+ && TARGET_UNINIT_CONST_IN_RODATA
+ && TREE_CODE (decl) == VAR_DECL
+ && TREE_READONLY (decl)
+ && (DECL_INITIAL (decl) == 0 || DECL_INITIAL (decl) == error_mark_node))
+ {
+ if (TREE_PUBLIC (decl) && DECL_NAME (decl))
+ targetm.asm_out.globalize_label (stream, name);
+
+ switch_to_section (readonly_data_section);
+ ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT));
+ mips_declare_object (stream, name, "",
+ ":\n\t.space\t" HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+ size);
+ }
+ else
+ mips_declare_common_object (stream, name, "\n\t.comm\t",
+ size, align, true);
}
-\f
-/* Emit the common code for doing conditional branches.
- operand[0] is the label to jump to.
- The comparison operands are saved away by cmp{si,di,sf,df}. */
+
+#ifdef ASM_OUTPUT_SIZE_DIRECTIVE
+extern int size_directive_output;
+
+/* Implement ASM_DECLARE_OBJECT_NAME. This is like most of the standard ELF
+ definitions except that it uses mips_declare_object to emit the label. */
void
-gen_conditional_branch (operands, test_code)
- rtx operands[];
- enum rtx_code test_code;
+mips_declare_object_name (FILE *stream, const char *name,
+ tree decl ATTRIBUTE_UNUSED)
{
- enum cmp_type type = branch_type;
- rtx cmp0 = branch_cmp[0];
- rtx cmp1 = branch_cmp[1];
- enum machine_mode mode;
- rtx reg;
- int invert;
- rtx label1, label2;
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+ ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+#endif
- switch (type)
+ size_directive_output = 0;
+ if (!flag_inhibit_size_directive && DECL_SIZE (decl))
{
- case CMP_SI:
- case CMP_DI:
- mode = type == CMP_SI ? SImode : DImode;
- invert = 0;
- reg = gen_int_relational (test_code, NULL_RTX, cmp0, cmp1, &invert);
+ HOST_WIDE_INT size;
- if (reg)
- {
- cmp0 = reg;
- cmp1 = const0_rtx;
- test_code = NE;
- }
- else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0)
- /* We don't want to build a comparison against a non-zero
- constant. */
- cmp1 = force_reg (mode, cmp1);
+ size_directive_output = 1;
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+ }
- break;
+ mips_declare_object (stream, name, "", ":\n");
+}
- case CMP_SF:
- case CMP_DF:
- if (! ISA_HAS_8CC)
- reg = gen_rtx_REG (CCmode, FPSW_REGNUM);
- else
- reg = gen_reg_rtx (CCmode);
-
- /* For cmp0 != cmp1, build cmp0 == cmp1, and test for result ==
- 0 in the instruction built below. The MIPS FPU handles
- inequality testing by testing for equality and looking for a
- false result. */
- emit_insn (gen_rtx_SET (VOIDmode, reg,
- gen_rtx (test_code == NE ? EQ : test_code,
- CCmode, cmp0, cmp1)));
-
- test_code = test_code == NE ? EQ : NE;
- mode = CCmode;
- cmp0 = reg;
- cmp1 = const0_rtx;
- invert = 0;
- break;
+/* Implement ASM_FINISH_DECLARE_OBJECT. This is generic ELF stuff. */
+
+void
+mips_finish_declare_object (FILE *stream, tree decl, int top_level, int at_end)
+{
+ const char *name;
+
+ name = XSTR (XEXP (DECL_RTL (decl), 0), 0);
+ if (!flag_inhibit_size_directive
+ && DECL_SIZE (decl) != 0
+ && !at_end
+ && top_level
+ && DECL_INITIAL (decl) == error_mark_node
+ && !size_directive_output)
+ {
+ HOST_WIDE_INT size;
+ size_directive_output = 1;
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+ }
+}
+#endif
+\f
+/* Return the FOO in the name of the ".mdebug.FOO" section associated
+ with the current ABI. */
+
+static const char *
+mips_mdebug_abi_name (void)
+{
+ switch (mips_abi)
+ {
+ case ABI_32:
+ return "abi32";
+ case ABI_O64:
+ return "abiO64";
+ case ABI_N32:
+ return "abiN32";
+ case ABI_64:
+ return "abi64";
+ case ABI_EABI:
+ return TARGET_64BIT ? "eabi64" : "eabi32";
default:
- abort_with_insn (gen_rtx (test_code, VOIDmode, cmp0, cmp1), "bad test");
+ gcc_unreachable ();
}
+}
- /* Generate the branch. */
+/* Implement TARGET_ASM_FILE_START. */
- label1 = gen_rtx_LABEL_REF (VOIDmode, operands[0]);
- label2 = pc_rtx;
+static void
+mips_file_start (void)
+{
+ default_file_start ();
+
+ /* Generate a special section to describe the ABI switches used to
+ produce the resultant binary. This is unnecessary on IRIX and
+ causes unwanted warnings from the native linker. */
+ if (!TARGET_IRIX)
+ {
+ /* Record the ABI itself. Modern versions of binutils encode
+ this information in the ELF header flags, but GDB needs the
+ information in order to correctly debug binaries produced by
+ older binutils. See the function mips_gdbarch_init in
+ gdb/mips-tdep.c. */
+ fprintf (asm_out_file, "\t.section .mdebug.%s\n\t.previous\n",
+ mips_mdebug_abi_name ());
+
+ /* There is no ELF header flag to distinguish long32 forms of the
+ EABI from long64 forms. Emit a special section to help tools
+ such as GDB. Do the same for o64, which is sometimes used with
+ -mlong64. */
+ if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
+ fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n"
+ "\t.previous\n", TARGET_LONG64 ? 64 : 32);
+
+#ifdef HAVE_AS_GNU_ATTRIBUTE
+ fprintf (asm_out_file, "\t.gnu_attribute 4, %d\n",
+ (TARGET_HARD_FLOAT_ABI
+ ? (TARGET_DOUBLE_FLOAT
+ ? ((!TARGET_64BIT && TARGET_FLOAT64) ? 4 : 1) : 2) : 3));
+#endif
+ }
- if (invert)
+ /* If TARGET_ABICALLS, tell GAS to generate -KPIC code. */
+ if (TARGET_ABICALLS)
{
- label2 = label1;
- label1 = pc_rtx;
+ fprintf (asm_out_file, "\t.abicalls\n");
+ if (TARGET_ABICALLS_PIC0)
+ fprintf (asm_out_file, "\t.option\tpic0\n");
}
- emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
- gen_rtx_IF_THEN_ELSE (VOIDmode,
- gen_rtx (test_code, mode,
- cmp0, cmp1),
- label1, label2)));
+ if (flag_verbose_asm)
+ fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
+ ASM_COMMENT_START,
+ mips_small_data_threshold, mips_arch_info->name, mips_isa);
}
+\f
+/* Make the last instruction frame-related and note that it performs
+ the operation described by FRAME_PATTERN. */
-/* Emit the common code for conditional moves. OPERANDS is the array
- of operands passed to the conditional move defined_expand. */
+static void
+mips_set_frame_expr (rtx frame_pattern)
+{
+ rtx insn;
-void
-gen_conditional_move (operands)
- rtx *operands;
+ insn = get_last_insn ();
+ RTX_FRAME_RELATED_P (insn) = 1;
+ REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+ frame_pattern,
+ REG_NOTES (insn));
+}
+
+/* Return a frame-related rtx that stores REG at MEM.
+ REG must be a single register. */
+
+static rtx
+mips_frame_set (rtx mem, rtx reg)
{
- rtx op0 = branch_cmp[0];
- rtx op1 = branch_cmp[1];
- enum machine_mode mode = GET_MODE (branch_cmp[0]);
- enum rtx_code cmp_code = GET_CODE (operands[1]);
- enum rtx_code move_code = NE;
- enum machine_mode op_mode = GET_MODE (operands[0]);
- enum machine_mode cmp_mode;
- rtx cmp_reg;
+ rtx set;
- if (GET_MODE_CLASS (mode) != MODE_FLOAT)
- {
- switch (cmp_code)
- {
- case EQ:
- cmp_code = XOR;
- move_code = EQ;
- break;
- case NE:
- cmp_code = XOR;
- break;
- case LT:
- break;
- case GE:
- cmp_code = LT;
- move_code = EQ;
- break;
- case GT:
- cmp_code = LT;
- op0 = force_reg (mode, branch_cmp[1]);
- op1 = branch_cmp[0];
- break;
- case LE:
- cmp_code = LT;
- op0 = force_reg (mode, branch_cmp[1]);
- op1 = branch_cmp[0];
- move_code = EQ;
- break;
- case LTU:
- break;
- case GEU:
- cmp_code = LTU;
- move_code = EQ;
- break;
- case GTU:
- cmp_code = LTU;
- op0 = force_reg (mode, branch_cmp[1]);
- op1 = branch_cmp[0];
- break;
- case LEU:
- cmp_code = LTU;
- op0 = force_reg (mode, branch_cmp[1]);
- op1 = branch_cmp[0];
- move_code = EQ;
- break;
- default:
- abort ();
- }
- }
- else if (cmp_code == NE)
- cmp_code = EQ, move_code = EQ;
+ /* If we're saving the return address register and the DWARF return
+ address column differs from the hard register number, adjust the
+ note reg to refer to the former. */
+ if (REGNO (reg) == GP_REG_FIRST + 31
+ && DWARF_FRAME_RETURN_COLUMN != GP_REG_FIRST + 31)
+ reg = gen_rtx_REG (GET_MODE (reg), DWARF_FRAME_RETURN_COLUMN);
- if (mode == SImode || mode == DImode)
- cmp_mode = mode;
- else if (mode == SFmode || mode == DFmode)
- cmp_mode = CCmode;
- else
- abort ();
+ set = gen_rtx_SET (VOIDmode, mem, reg);
+ RTX_FRAME_RELATED_P (set) = 1;
+
+ return set;
+}
+\f
+/* If a MIPS16e SAVE or RESTORE instruction saves or restores register
+ mips16e_s2_s8_regs[X], it must also save the registers in indexes
+ X + 1 onwards. Likewise mips16e_a0_a3_regs. */
+static const unsigned char mips16e_s2_s8_regs[] = {
+ 30, 23, 22, 21, 20, 19, 18
+};
+static const unsigned char mips16e_a0_a3_regs[] = {
+ 4, 5, 6, 7
+};
+
+/* A list of the registers that can be saved by the MIPS16e SAVE instruction,
+ ordered from the uppermost in memory to the lowest in memory. */
+static const unsigned char mips16e_save_restore_regs[] = {
+ 31, 30, 23, 22, 21, 20, 19, 18, 17, 16, 7, 6, 5, 4
+};
- cmp_reg = gen_reg_rtx (cmp_mode);
- emit_insn (gen_rtx_SET (cmp_mode, cmp_reg,
- gen_rtx (cmp_code, cmp_mode, op0, op1)));
+/* Return the index of the lowest X in the range [0, SIZE) for which
+ bit REGS[X] is set in MASK. Return SIZE if there is no such X. */
- emit_insn (gen_rtx_SET (op_mode, operands[0],
- gen_rtx_IF_THEN_ELSE (op_mode,
- gen_rtx (move_code, VOIDmode,
- cmp_reg,
- CONST0_RTX (SImode)),
- operands[2], operands[3])));
+static unsigned int
+mips16e_find_first_register (unsigned int mask, const unsigned char *regs,
+ unsigned int size)
+{
+ unsigned int i;
+
+ for (i = 0; i < size; i++)
+ if (BITSET_P (mask, regs[i]))
+ break;
+
+ return i;
}
-/* Emit the common code for conditional moves. OPERANDS is the array
- of operands passed to the conditional move defined_expand. */
+/* *MASK_PTR is a mask of general-purpose registers and *NUM_REGS_PTR
+ is the number of set bits. If *MASK_PTR contains REGS[X] for some X
+ in [0, SIZE), adjust *MASK_PTR and *NUM_REGS_PTR so that the same
+ is true for all indexes (X, SIZE). */
-void
-mips_gen_conditional_trap (operands)
- rtx operands[];
+static void
+mips16e_mask_registers (unsigned int *mask_ptr, const unsigned char *regs,
+ unsigned int size, unsigned int *num_regs_ptr)
{
- rtx op0, op1;
- enum rtx_code cmp_code = GET_CODE (operands[0]);
- enum machine_mode mode = GET_MODE (branch_cmp[0]);
+ unsigned int i;
+
+ i = mips16e_find_first_register (*mask_ptr, regs, size);
+ for (i++; i < size; i++)
+ if (!BITSET_P (*mask_ptr, regs[i]))
+ {
+ *num_regs_ptr += 1;
+ *mask_ptr |= 1 << regs[i];
+ }
+}
+
+/* Return a simplified form of X using the register values in REG_VALUES.
+ REG_VALUES[R] is the last value assigned to hard register R, or null
+ if R has not been modified.
+
+ This function is rather limited, but is good enough for our purposes. */
+
+static rtx
+mips16e_collect_propagate_value (rtx x, rtx *reg_values)
+{
+ x = avoid_constant_pool_reference (x);
- /* MIPS conditional trap machine instructions don't have GT or LE
- flavors, so we must invert the comparison and convert to LT and
- GE, respectively. */
- switch (cmp_code)
+ if (UNARY_P (x))
{
- case GT: cmp_code = LT; break;
- case LE: cmp_code = GE; break;
- case GTU: cmp_code = LTU; break;
- case LEU: cmp_code = GEU; break;
- default: break;
+ rtx x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+ return simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+ x0, GET_MODE (XEXP (x, 0)));
}
- if (cmp_code == GET_CODE (operands[0]))
- {
- op0 = force_reg (mode, branch_cmp[0]);
- op1 = branch_cmp[1];
- }
- else
+
+ if (ARITHMETIC_P (x))
{
- op0 = force_reg (mode, branch_cmp[1]);
- op1 = branch_cmp[0];
+ rtx x0 = mips16e_collect_propagate_value (XEXP (x, 0), reg_values);
+ rtx x1 = mips16e_collect_propagate_value (XEXP (x, 1), reg_values);
+ return simplify_gen_binary (GET_CODE (x), GET_MODE (x), x0, x1);
}
- if (GET_CODE (op1) == CONST_INT && ! SMALL_INT (op1))
- op1 = force_reg (mode, op1);
- emit_insn (gen_rtx_TRAP_IF (VOIDmode,
- gen_rtx (cmp_code, GET_MODE (operands[0]), op0, op1),
- operands[1]));
+ if (REG_P (x)
+ && reg_values[REGNO (x)]
+ && !rtx_unstable_p (reg_values[REGNO (x)]))
+ return reg_values[REGNO (x)];
+
+ return x;
}
-/* Return true if operand OP is a condition code register.
- Only for use during or after reload. */
+/* Return true if (set DEST SRC) stores an argument register into its
+ caller-allocated save slot, storing the number of that argument
+ register in *REGNO_PTR if so. REG_VALUES is as for
+ mips16e_collect_propagate_value. */
-int
-fcc_register_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+mips16e_collect_argument_save_p (rtx dest, rtx src, rtx *reg_values,
+ unsigned int *regno_ptr)
{
- return ((mode == VOIDmode || mode == GET_MODE (op))
- && (reload_in_progress || reload_completed)
- && (GET_CODE (op) == REG || GET_CODE (op) == SUBREG)
- && ST_REG_P (true_regnum (op)));
-}
+ unsigned int argno, regno;
+ HOST_WIDE_INT offset, required_offset;
+ rtx addr, base;
-/* Emit code to move general operand SRC into condition-code
- register DEST. SCRATCH is a scratch TFmode float register.
- The sequence is:
+ /* Check that this is a word-mode store. */
+ if (!MEM_P (dest) || !REG_P (src) || GET_MODE (dest) != word_mode)
+ return false;
- FP1 = SRC
- FP2 = 0.0f
- DEST = FP2 < FP1
+ /* Check that the register being saved is an unmodified argument
+ register. */
+ regno = REGNO (src);
+ if (!IN_RANGE (regno, GP_ARG_FIRST, GP_ARG_LAST) || reg_values[regno])
+ return false;
+ argno = regno - GP_ARG_FIRST;
+
+ /* Check whether the address is an appropriate stack-pointer or
+ frame-pointer access. */
+ addr = mips16e_collect_propagate_value (XEXP (dest, 0), reg_values);
+ mips_split_plus (addr, &base, &offset);
+ required_offset = cfun->machine->frame.total_size + argno * UNITS_PER_WORD;
+ if (base == hard_frame_pointer_rtx)
+ required_offset -= cfun->machine->frame.hard_frame_pointer_offset;
+ else if (base != stack_pointer_rtx)
+ return false;
+ if (offset != required_offset)
+ return false;
+
+ *regno_ptr = regno;
+ return true;
+}
+
+/* A subroutine of mips_expand_prologue, called only when generating
+ MIPS16e SAVE instructions. Search the start of the function for any
+ instructions that save argument registers into their caller-allocated
+ save slots. Delete such instructions and return a value N such that
+ saving [GP_ARG_FIRST, GP_ARG_FIRST + N) would make all the deleted
+ instructions redundant. */
+
+static unsigned int
+mips16e_collect_argument_saves (void)
+{
+ rtx reg_values[FIRST_PSEUDO_REGISTER];
+ rtx insn, next, set, dest, src;
+ unsigned int nargs, regno;
+
+ push_topmost_sequence ();
+ nargs = 0;
+ memset (reg_values, 0, sizeof (reg_values));
+ for (insn = get_insns (); insn; insn = next)
+ {
+ next = NEXT_INSN (insn);
+ if (NOTE_P (insn))
+ continue;
- where FP1 and FP2 are single-precision float registers
- taken from SCRATCH. */
+ if (!INSN_P (insn))
+ break;
-void
-mips_emit_fcc_reload (dest, src, scratch)
- rtx dest, src, scratch;
-{
- rtx fp1, fp2;
+ set = PATTERN (insn);
+ if (GET_CODE (set) != SET)
+ break;
- /* Change the source to SFmode. */
- if (GET_CODE (src) == MEM)
- src = adjust_address (src, SFmode, 0);
- else if (GET_CODE (src) == REG || GET_CODE (src) == SUBREG)
- src = gen_rtx_REG (SFmode, true_regnum (src));
+ dest = SET_DEST (set);
+ src = SET_SRC (set);
+ if (mips16e_collect_argument_save_p (dest, src, reg_values, ®no))
+ {
+ if (!BITSET_P (cfun->machine->frame.mask, regno))
+ {
+ delete_insn (insn);
+ nargs = MAX (nargs, (regno - GP_ARG_FIRST) + 1);
+ }
+ }
+ else if (REG_P (dest) && GET_MODE (dest) == word_mode)
+ reg_values[REGNO (dest)]
+ = mips16e_collect_propagate_value (src, reg_values);
+ else
+ break;
+ }
+ pop_topmost_sequence ();
- fp1 = gen_rtx_REG (SFmode, REGNO (scratch));
- fp2 = gen_rtx_REG (SFmode, REGNO (scratch) + FP_INC);
+ return nargs;
+}
- emit_move_insn (copy_rtx (fp1), src);
- emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
- emit_insn (gen_slt_sf (dest, fp2, fp1));
+/* Return a move between register REGNO and memory location SP + OFFSET.
+ Make the move a load if RESTORE_P, otherwise make it a frame-related
+ store. */
+
+static rtx
+mips16e_save_restore_reg (bool restore_p, HOST_WIDE_INT offset,
+ unsigned int regno)
+{
+ rtx reg, mem;
+
+ mem = gen_frame_mem (SImode, plus_constant (stack_pointer_rtx, offset));
+ reg = gen_rtx_REG (SImode, regno);
+ return (restore_p
+ ? gen_rtx_SET (VOIDmode, reg, mem)
+ : mips_frame_set (mem, reg));
}
-\f
-/* Write a loop to move a constant number of bytes.
- Generate load/stores as follows:
-
- do {
- temp1 = src[0];
- temp2 = src[1];
- ...
- temp<last> = src[MAX_MOVE_REGS-1];
- dest[0] = temp1;
- dest[1] = temp2;
- ...
- dest[MAX_MOVE_REGS-1] = temp<last>;
- src += MAX_MOVE_REGS;
- dest += MAX_MOVE_REGS;
- } while (src != final);
-
- This way, no NOP's are needed, and only MAX_MOVE_REGS+3 temp
- registers are needed.
-
- Aligned moves move MAX_MOVE_REGS*4 bytes every (2*MAX_MOVE_REGS)+3
- cycles, unaligned moves move MAX_MOVE_REGS*4 bytes every
- (4*MAX_MOVE_REGS)+3 cycles, assuming no cache misses. */
-
-#define MAX_MOVE_REGS 4
-#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
-static void
-block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src)
- rtx dest_reg; /* register holding destination address */
- rtx src_reg; /* register holding source address */
- unsigned int bytes; /* # bytes to move */
- int align; /* alignment */
- rtx orig_dest; /* original dest */
- rtx orig_src; /* original source for making a reg note */
-{
- rtx dest_mem = replace_equiv_address (orig_dest, dest_reg);
- rtx src_mem = replace_equiv_address (orig_src, src_reg);
- rtx align_rtx = GEN_INT (align);
- rtx label;
- rtx final_src;
- rtx bytes_rtx;
- int leftover;
+/* Return RTL for a MIPS16e SAVE or RESTORE instruction; RESTORE_P says which.
+ The instruction must:
- if (bytes < (unsigned)2 * MAX_MOVE_BYTES)
- abort ();
+ - Allocate or deallocate SIZE bytes in total; SIZE is known
+ to be nonzero.
- leftover = bytes % MAX_MOVE_BYTES;
- bytes -= leftover;
+ - Save or restore as many registers in *MASK_PTR as possible.
+ The instruction saves the first registers at the top of the
+ allocated area, with the other registers below it.
- label = gen_label_rtx ();
- final_src = gen_reg_rtx (Pmode);
- bytes_rtx = GEN_INT (bytes);
+ - Save NARGS argument registers above the allocated area.
- if (bytes > 0x7fff)
- {
- if (Pmode == DImode)
- {
- emit_insn (gen_movdi (final_src, bytes_rtx));
- emit_insn (gen_adddi3 (final_src, final_src, src_reg));
- }
- else
- {
- emit_insn (gen_movsi (final_src, bytes_rtx));
- emit_insn (gen_addsi3 (final_src, final_src, src_reg));
- }
- }
- else
- {
- if (Pmode == DImode)
- emit_insn (gen_adddi3 (final_src, src_reg, bytes_rtx));
- else
- emit_insn (gen_addsi3 (final_src, src_reg, bytes_rtx));
- }
+ (NARGS is always zero if RESTORE_P.)
- emit_label (label);
+ The SAVE and RESTORE instructions cannot save and restore all general
+ registers, so there may be some registers left over for the caller to
+ handle. Destructively modify *MASK_PTR so that it contains the registers
+ that still need to be saved or restored. The caller can save these
+ registers in the memory immediately below *OFFSET_PTR, which is a
+ byte offset from the bottom of the allocated stack area. */
+
+static rtx
+mips16e_build_save_restore (bool restore_p, unsigned int *mask_ptr,
+ HOST_WIDE_INT *offset_ptr, unsigned int nargs,
+ HOST_WIDE_INT size)
+{
+ rtx pattern, set;
+ HOST_WIDE_INT offset, top_offset;
+ unsigned int i, regno;
+ int n;
+
+ gcc_assert (cfun->machine->frame.num_fp == 0);
+
+ /* Calculate the number of elements in the PARALLEL. We need one element
+ for the stack adjustment, one for each argument register save, and one
+ for each additional register move. */
+ n = 1 + nargs;
+ for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
+ if (BITSET_P (*mask_ptr, mips16e_save_restore_regs[i]))
+ n++;
+
+ /* Create the final PARALLEL. */
+ pattern = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (n));
+ n = 0;
+
+ /* Add the stack pointer adjustment. */
+ set = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx,
+ restore_p ? size : -size));
+ RTX_FRAME_RELATED_P (set) = 1;
+ XVECEXP (pattern, 0, n++) = set;
- bytes_rtx = GEN_INT (MAX_MOVE_BYTES);
- emit_insn (gen_movstrsi_internal (dest_mem, src_mem, bytes_rtx, align_rtx));
+ /* Stack offsets in the PARALLEL are relative to the old stack pointer. */
+ top_offset = restore_p ? size : 0;
- if (Pmode == DImode)
+ /* Save the arguments. */
+ for (i = 0; i < nargs; i++)
{
- emit_insn (gen_adddi3 (src_reg, src_reg, bytes_rtx));
- emit_insn (gen_adddi3 (dest_reg, dest_reg, bytes_rtx));
- emit_insn (gen_cmpdi (src_reg, final_src));
+ offset = top_offset + i * UNITS_PER_WORD;
+ set = mips16e_save_restore_reg (restore_p, offset, GP_ARG_FIRST + i);
+ XVECEXP (pattern, 0, n++) = set;
}
- else
+
+ /* Then fill in the other register moves. */
+ offset = top_offset;
+ for (i = 0; i < ARRAY_SIZE (mips16e_save_restore_regs); i++)
{
- emit_insn (gen_addsi3 (src_reg, src_reg, bytes_rtx));
- emit_insn (gen_addsi3 (dest_reg, dest_reg, bytes_rtx));
- emit_insn (gen_cmpsi (src_reg, final_src));
+ regno = mips16e_save_restore_regs[i];
+ if (BITSET_P (*mask_ptr, regno))
+ {
+ offset -= UNITS_PER_WORD;
+ set = mips16e_save_restore_reg (restore_p, offset, regno);
+ XVECEXP (pattern, 0, n++) = set;
+ *mask_ptr &= ~(1 << regno);
+ }
}
- emit_jump_insn (gen_bne (label));
+ /* Tell the caller what offset it should use for the remaining registers. */
+ *offset_ptr = size + (offset - top_offset);
- if (leftover)
- emit_insn (gen_movstrsi_internal (dest_mem, src_mem, GEN_INT (leftover),
- align_rtx));
-}
-\f
-/* Use a library function to move some bytes. */
+ gcc_assert (n == XVECLEN (pattern, 0));
-static void
-block_move_call (dest_reg, src_reg, bytes_rtx)
- rtx dest_reg;
- rtx src_reg;
- rtx bytes_rtx;
-{
- /* We want to pass the size as Pmode, which will normally be SImode
- but will be DImode if we are using 64 bit longs and pointers. */
- if (GET_MODE (bytes_rtx) != VOIDmode
- && GET_MODE (bytes_rtx) != (unsigned) Pmode)
- bytes_rtx = convert_to_mode (Pmode, bytes_rtx, 1);
-
-#ifdef TARGET_MEM_FUNCTIONS
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "memcpy"), 0,
- VOIDmode, 3, dest_reg, Pmode, src_reg, Pmode,
- convert_to_mode (TYPE_MODE (sizetype), bytes_rtx,
- TREE_UNSIGNED (sizetype)),
- TYPE_MODE (sizetype));
-#else
- emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "bcopy"), 0,
- VOIDmode, 3, src_reg, Pmode, dest_reg, Pmode,
- convert_to_mode (TYPE_MODE (integer_type_node), bytes_rtx,
- TREE_UNSIGNED (integer_type_node)),
- TYPE_MODE (integer_type_node));
-#endif
+ return pattern;
}
-\f
-/* Expand string/block move operations.
-
- operands[0] is the pointer to the destination.
- operands[1] is the pointer to the source.
- operands[2] is the number of bytes to move.
- operands[3] is the alignment. */
-void
-expand_block_move (operands)
- rtx operands[];
-{
- rtx bytes_rtx = operands[2];
- rtx align_rtx = operands[3];
- int constp = GET_CODE (bytes_rtx) == CONST_INT;
- unsigned HOST_WIDE_INT bytes = constp ? INTVAL (bytes_rtx) : 0;
- unsigned int align = INTVAL (align_rtx);
- rtx orig_src = operands[1];
- rtx orig_dest = operands[0];
- rtx src_reg;
- rtx dest_reg;
-
- if (constp && bytes == 0)
- return;
+/* PATTERN is a PARALLEL whose first element adds ADJUST to the stack
+ pointer. Return true if PATTERN matches the kind of instruction
+ generated by mips16e_build_save_restore. If INFO is nonnull,
+ initialize it when returning true. */
- if (align > (unsigned) UNITS_PER_WORD)
- align = UNITS_PER_WORD;
+bool
+mips16e_save_restore_pattern_p (rtx pattern, HOST_WIDE_INT adjust,
+ struct mips16e_save_restore_info *info)
+{
+ unsigned int i, nargs, mask, extra;
+ HOST_WIDE_INT top_offset, save_offset, offset;
+ rtx set, reg, mem, base;
+ int n;
- /* Move the address into scratch registers. */
- dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
- src_reg = copy_addr_to_reg (XEXP (orig_src, 0));
+ if (!GENERATE_MIPS16E_SAVE_RESTORE)
+ return false;
- if (TARGET_MEMCPY)
- block_move_call (dest_reg, src_reg, bytes_rtx);
+ /* Stack offsets in the PARALLEL are relative to the old stack pointer. */
+ top_offset = adjust > 0 ? adjust : 0;
- else if (constp && bytes <= (unsigned)2 * MAX_MOVE_BYTES
- && align == (unsigned) UNITS_PER_WORD)
- move_by_pieces (orig_dest, orig_src, bytes, align * BITS_PER_WORD);
+ /* Interpret all other members of the PARALLEL. */
+ save_offset = top_offset - UNITS_PER_WORD;
+ mask = 0;
+ nargs = 0;
+ i = 0;
+ for (n = 1; n < XVECLEN (pattern, 0); n++)
+ {
+ /* Check that we have a SET. */
+ set = XVECEXP (pattern, 0, n);
+ if (GET_CODE (set) != SET)
+ return false;
+
+ /* Check that the SET is a load (if restoring) or a store
+ (if saving). */
+ mem = adjust > 0 ? SET_SRC (set) : SET_DEST (set);
+ if (!MEM_P (mem))
+ return false;
+
+ /* Check that the address is the sum of the stack pointer and a
+ possibly-zero constant offset. */
+ mips_split_plus (XEXP (mem, 0), &base, &offset);
+ if (base != stack_pointer_rtx)
+ return false;
+
+ /* Check that SET's other operand is a register. */
+ reg = adjust > 0 ? SET_DEST (set) : SET_SRC (set);
+ if (!REG_P (reg))
+ return false;
+
+ /* Check for argument saves. */
+ if (offset == top_offset + nargs * UNITS_PER_WORD
+ && REGNO (reg) == GP_ARG_FIRST + nargs)
+ nargs++;
+ else if (offset == save_offset)
+ {
+ while (mips16e_save_restore_regs[i++] != REGNO (reg))
+ if (i == ARRAY_SIZE (mips16e_save_restore_regs))
+ return false;
+
+ mask |= 1 << REGNO (reg);
+ save_offset -= UNITS_PER_WORD;
+ }
+ else
+ return false;
+ }
- else if (constp && bytes <= (unsigned)2 * MAX_MOVE_BYTES)
- emit_insn (gen_movstrsi_internal (replace_equiv_address (orig_dest,
- dest_reg),
- replace_equiv_address (orig_src,
- src_reg),
- bytes_rtx, align_rtx));
+ /* Check that the restrictions on register ranges are met. */
+ extra = 0;
+ mips16e_mask_registers (&mask, mips16e_s2_s8_regs,
+ ARRAY_SIZE (mips16e_s2_s8_regs), &extra);
+ mips16e_mask_registers (&mask, mips16e_a0_a3_regs,
+ ARRAY_SIZE (mips16e_a0_a3_regs), &extra);
+ if (extra != 0)
+ return false;
- else if (constp && align >= (unsigned) UNITS_PER_WORD && optimize)
- block_move_loop (dest_reg, src_reg, bytes, align, orig_dest, orig_src);
+ /* Make sure that the topmost argument register is not saved twice.
+ The checks above ensure that the same is then true for the other
+ argument registers. */
+ if (nargs > 0 && BITSET_P (mask, GP_ARG_FIRST + nargs - 1))
+ return false;
- else if (constp && optimize)
+ /* Pass back information, if requested. */
+ if (info)
{
- /* If the alignment is not word aligned, generate a test at
- runtime, to see whether things wound up aligned, and we
- can use the faster lw/sw instead ulw/usw. */
+ info->nargs = nargs;
+ info->mask = mask;
+ info->size = (adjust > 0 ? adjust : -adjust);
+ }
- rtx temp = gen_reg_rtx (Pmode);
- rtx aligned_label = gen_label_rtx ();
- rtx join_label = gen_label_rtx ();
- int leftover = bytes % MAX_MOVE_BYTES;
+ return true;
+}
- bytes -= leftover;
+/* Add a MIPS16e SAVE or RESTORE register-range argument to string S
+ for the register range [MIN_REG, MAX_REG]. Return a pointer to
+ the null terminator. */
- if (Pmode == DImode)
- {
- emit_insn (gen_iordi3 (temp, src_reg, dest_reg));
- emit_insn (gen_anddi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1)));
- emit_insn (gen_cmpdi (temp, const0_rtx));
- }
- else
- {
- emit_insn (gen_iorsi3 (temp, src_reg, dest_reg));
- emit_insn (gen_andsi3 (temp, temp, GEN_INT (UNITS_PER_WORD - 1)));
- emit_insn (gen_cmpsi (temp, const0_rtx));
- }
+static char *
+mips16e_add_register_range (char *s, unsigned int min_reg,
+ unsigned int max_reg)
+{
+ if (min_reg != max_reg)
+ s += sprintf (s, ",%s-%s", reg_names[min_reg], reg_names[max_reg]);
+ else
+ s += sprintf (s, ",%s", reg_names[min_reg]);
+ return s;
+}
- emit_jump_insn (gen_beq (aligned_label));
+/* Return the assembly instruction for a MIPS16e SAVE or RESTORE instruction.
+ PATTERN and ADJUST are as for mips16e_save_restore_pattern_p. */
- /* Unaligned loop. */
- block_move_loop (dest_reg, src_reg, bytes, 1, orig_dest, orig_src);
- emit_jump_insn (gen_jump (join_label));
- emit_barrier ();
+const char *
+mips16e_output_save_restore (rtx pattern, HOST_WIDE_INT adjust)
+{
+ static char buffer[300];
+
+ struct mips16e_save_restore_info info;
+ unsigned int i, end;
+ char *s;
+
+ /* Parse the pattern. */
+ if (!mips16e_save_restore_pattern_p (pattern, adjust, &info))
+ gcc_unreachable ();
+
+ /* Add the mnemonic. */
+ s = strcpy (buffer, adjust > 0 ? "restore\t" : "save\t");
+ s += strlen (s);
+
+ /* Save the arguments. */
+ if (info.nargs > 1)
+ s += sprintf (s, "%s-%s,", reg_names[GP_ARG_FIRST],
+ reg_names[GP_ARG_FIRST + info.nargs - 1]);
+ else if (info.nargs == 1)
+ s += sprintf (s, "%s,", reg_names[GP_ARG_FIRST]);
+
+ /* Emit the amount of stack space to allocate or deallocate. */
+ s += sprintf (s, "%d", (int) info.size);
+
+ /* Save or restore $16. */
+ if (BITSET_P (info.mask, 16))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 16]);
+
+ /* Save or restore $17. */
+ if (BITSET_P (info.mask, 17))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 17]);
+
+ /* Save or restore registers in the range $s2...$s8, which
+ mips16e_s2_s8_regs lists in decreasing order. Note that this
+ is a software register range; the hardware registers are not
+ numbered consecutively. */
+ end = ARRAY_SIZE (mips16e_s2_s8_regs);
+ i = mips16e_find_first_register (info.mask, mips16e_s2_s8_regs, end);
+ if (i < end)
+ s = mips16e_add_register_range (s, mips16e_s2_s8_regs[end - 1],
+ mips16e_s2_s8_regs[i]);
+
+ /* Save or restore registers in the range $a0...$a3. */
+ end = ARRAY_SIZE (mips16e_a0_a3_regs);
+ i = mips16e_find_first_register (info.mask, mips16e_a0_a3_regs, end);
+ if (i < end)
+ s = mips16e_add_register_range (s, mips16e_a0_a3_regs[i],
+ mips16e_a0_a3_regs[end - 1]);
+
+ /* Save or restore $31. */
+ if (BITSET_P (info.mask, 31))
+ s += sprintf (s, ",%s", reg_names[GP_REG_FIRST + 31]);
+
+ return buffer;
+}
+\f
+/* Return true if the current function has an insn that implicitly
+ refers to $gp. */
- /* Aligned loop. */
- emit_label (aligned_label);
- block_move_loop (dest_reg, src_reg, bytes, UNITS_PER_WORD, orig_dest,
- orig_src);
- emit_label (join_label);
+static bool
+mips_function_has_gp_insn (void)
+{
+ /* Don't bother rechecking if we found one last time. */
+ if (!cfun->machine->has_gp_insn_p)
+ {
+ rtx insn;
- /* Bytes at the end of the loop. */
- if (leftover)
- emit_insn (gen_movstrsi_internal (replace_equiv_address (orig_dest,
- dest_reg),
- replace_equiv_address (orig_src,
- src_reg),
- GEN_INT (leftover),
- GEN_INT (align)));
+ push_topmost_sequence ();
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+ if (USEFUL_INSN_P (insn)
+ && (get_attr_got (insn) != GOT_UNSET
+ || mips_small_data_pattern_p (PATTERN (insn))))
+ {
+ cfun->machine->has_gp_insn_p = true;
+ break;
+ }
+ pop_topmost_sequence ();
}
-
- else
- block_move_call (dest_reg, src_reg, bytes_rtx);
+ return cfun->machine->has_gp_insn_p;
}
-\f
-/* Emit load/stores for a small constant block_move.
-
- operands[0] is the memory address of the destination.
- operands[1] is the memory address of the source.
- operands[2] is the number of bytes to move.
- operands[3] is the alignment.
- operands[4] is a temp register.
- operands[5] is a temp register.
- ...
- operands[3+num_regs] is the last temp register.
-
- The block move type can be one of the following:
- BLOCK_MOVE_NORMAL Do all of the block move.
- BLOCK_MOVE_NOT_LAST Do all but the last store.
- BLOCK_MOVE_LAST Do just the last store. */
-const char *
-output_block_move (insn, operands, num_regs, move_type)
- rtx insn;
- rtx operands[];
- int num_regs;
- enum block_move_type move_type;
-{
- rtx dest_reg = XEXP (operands[0], 0);
- rtx src_reg = XEXP (operands[1], 0);
- HOST_WIDE_INT bytes = INTVAL (operands[2]);
- int align = INTVAL (operands[3]);
- int num = 0;
- int offset = 0;
- int use_lwl_lwr = 0;
- int last_operand = num_regs + 4;
- int safe_regs = 4;
- int i;
- rtx xoperands[10];
+/* Return true if the current function returns its value in a floating-point
+ register in MIPS16 mode. */
- struct {
- const char *load; /* load insn without nop */
- const char *load_nop; /* load insn with trailing nop */
- const char *store; /* store insn */
- const char *final; /* if last_store used: NULL or swr */
- const char *last_store; /* last store instruction */
- int offset; /* current offset */
- enum machine_mode mode; /* mode to use on (MEM) */
- } load_store[4];
-
- /* ??? Detect a bug in GCC, where it can give us a register
- the same as one of the addressing registers and reduce
- the number of registers available. */
- for (i = 4; i < last_operand && safe_regs < (int) ARRAY_SIZE (xoperands); i++)
- if (! reg_mentioned_p (operands[i], operands[0])
- && ! reg_mentioned_p (operands[i], operands[1]))
- xoperands[safe_regs++] = operands[i];
-
- if (safe_regs < last_operand)
- {
- xoperands[0] = operands[0];
- xoperands[1] = operands[1];
- xoperands[2] = operands[2];
- xoperands[3] = operands[3];
- return output_block_move (insn, xoperands, safe_regs - 4, move_type);
- }
-
- /* If we are given global or static addresses, and we would be
- emitting a few instructions, try to save time by using a
- temporary register for the pointer. */
- /* ??? The SGI Irix6 assembler fails when a SYMBOL_REF is used in
- an ldl/ldr instruction pair. We play it safe, and always move
- constant addresses into registers when generating N32/N64 code, just
- in case we might emit an unaligned load instruction. */
- if (num_regs > 2 && (bytes > 2 * align || move_type != BLOCK_MOVE_NORMAL
- || mips_abi == ABI_MEABI
- || mips_abi == ABI_N32
- || mips_abi == ABI_64))
- {
- if (CONSTANT_P (src_reg))
- {
- if (TARGET_STATS)
- mips_count_memory_refs (operands[1], 1);
+static bool
+mips16_cfun_returns_in_fpr_p (void)
+{
+ tree return_type = DECL_RESULT (current_function_decl);
+ 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)));
+}
- src_reg = operands[3 + num_regs--];
- if (move_type != BLOCK_MOVE_LAST)
- {
- xoperands[1] = operands[1];
- xoperands[0] = src_reg;
- if (Pmode == DImode)
- output_asm_insn ("dla\t%0,%1", xoperands);
- else
- output_asm_insn ("la\t%0,%1", xoperands);
- }
- }
+/* Return the register that should be used as the global pointer
+ within this function. Return INVALID_REGNUM if the function
+ doesn't need a global pointer. */
- if (CONSTANT_P (dest_reg))
- {
- if (TARGET_STATS)
- mips_count_memory_refs (operands[0], 1);
+static unsigned int
+mips_global_pointer (void)
+{
+ unsigned int regno;
- dest_reg = operands[3 + num_regs--];
- if (move_type != BLOCK_MOVE_LAST)
- {
- xoperands[1] = operands[0];
- xoperands[0] = dest_reg;
- if (Pmode == DImode)
- output_asm_insn ("dla\t%0,%1", xoperands);
- else
- output_asm_insn ("la\t%0,%1", xoperands);
- }
- }
- }
+ /* $gp is always available unless we're using a GOT. */
+ if (!TARGET_USE_GOT)
+ return GLOBAL_POINTER_REGNUM;
- /* ??? We really shouldn't get any LO_SUM addresses here, because they
- are not offsettable, however, offsettable_address_p says they are
- offsettable. I think this is a bug in offsettable_address_p.
- For expediency, we fix this by just loading the address into a register
- if we happen to get one. */
+ /* We must always provide $gp when it is used implicitly. */
+ if (!TARGET_EXPLICIT_RELOCS)
+ return GLOBAL_POINTER_REGNUM;
- if (GET_CODE (src_reg) == LO_SUM)
- {
- src_reg = operands[3 + num_regs--];
- if (move_type != BLOCK_MOVE_LAST)
- {
- xoperands[2] = XEXP (XEXP (operands[1], 0), 1);
- xoperands[1] = XEXP (XEXP (operands[1], 0), 0);
- xoperands[0] = src_reg;
- if (Pmode == DImode)
- output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
- else
- output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
- }
- }
+ /* FUNCTION_PROFILER includes a jal macro, so we need to give it
+ a valid gp. */
+ if (crtl->profile)
+ return GLOBAL_POINTER_REGNUM;
+
+ /* If the function has a nonlocal goto, $gp must hold the correct
+ global pointer for the target function. */
+ if (crtl->has_nonlocal_goto)
+ return GLOBAL_POINTER_REGNUM;
- if (GET_CODE (dest_reg) == LO_SUM)
+ /* There's no need to initialize $gp if it isn't referenced now,
+ and if we can be sure that no new references will be added during
+ or after reload. */
+ if (!df_regs_ever_live_p (GLOBAL_POINTER_REGNUM)
+ && !mips_function_has_gp_insn ())
{
- dest_reg = operands[3 + num_regs--];
- if (move_type != BLOCK_MOVE_LAST)
- {
- xoperands[2] = XEXP (XEXP (operands[0], 0), 1);
- xoperands[1] = XEXP (XEXP (operands[0], 0), 0);
- xoperands[0] = dest_reg;
- if (Pmode == DImode)
- output_asm_insn ("daddiu\t%0,%1,%%lo(%2)", xoperands);
- else
- output_asm_insn ("addiu\t%0,%1,%%lo(%2)", xoperands);
- }
- }
+ /* The function doesn't use $gp at the moment. If we're generating
+ -call_nonpic code, no new uses will be introduced during or after
+ reload. */
+ if (TARGET_ABICALLS_PIC0)
+ return INVALID_REGNUM;
- if (num_regs > (int) ARRAY_SIZE (load_store))
- num_regs = ARRAY_SIZE (load_store);
+ /* We need to handle the following implicit gp references:
- else if (num_regs < 1)
- abort_with_insn (insn,
- "cannot do block move, not enough scratch registers");
+ - Reload can sometimes introduce constant pool references
+ into a function that otherwise didn't need them. For example,
+ suppose we have an instruction like:
- while (bytes > 0)
- {
- load_store[num].offset = offset;
+ (set (reg:DF R1) (float:DF (reg:SI R2)))
- if (TARGET_64BIT && bytes >= 8 && align >= 8)
- {
- load_store[num].load = "ld\t%0,%1";
- load_store[num].load_nop = "ld\t%0,%1%#";
- load_store[num].store = "sd\t%0,%1";
- load_store[num].last_store = "sd\t%0,%1";
- load_store[num].final = 0;
- load_store[num].mode = DImode;
- offset += 8;
- bytes -= 8;
- }
+ If R2 turns out to be constant such as 1, the instruction may
+ have a REG_EQUAL note saying that R1 == 1.0. Reload then has
+ the option of using this constant if R2 doesn't get allocated
+ to a register.
- /* ??? Fails because of a MIPS assembler bug? */
- else if (TARGET_64BIT && bytes >= 8 && ! TARGET_MIPS16)
- {
- if (BYTES_BIG_ENDIAN)
- {
- load_store[num].load = "ldl\t%0,%1\n\tldr\t%0,%2";
- load_store[num].load_nop = "ldl\t%0,%1\n\tldr\t%0,%2%#";
- load_store[num].store = "sdl\t%0,%1\n\tsdr\t%0,%2";
- load_store[num].last_store = "sdr\t%0,%2";
- load_store[num].final = "sdl\t%0,%1";
- }
- else
- {
- load_store[num].load = "ldl\t%0,%2\n\tldr\t%0,%1";
- load_store[num].load_nop = "ldl\t%0,%2\n\tldr\t%0,%1%#";
- load_store[num].store = "sdl\t%0,%2\n\tsdr\t%0,%1";
- load_store[num].last_store = "sdr\t%0,%1";
- load_store[num].final = "sdl\t%0,%2";
- }
+ In cases like these, reload will have added the constant to the
+ pool but no instruction will yet refer to it.
- load_store[num].mode = DImode;
- offset += 8;
- bytes -= 8;
- use_lwl_lwr = 1;
- }
+ - MIPS16 functions that return in FPRs need to call an
+ external libgcc routine. */
+ if (!crtl->uses_const_pool
+ && !mips16_cfun_returns_in_fpr_p ())
+ return INVALID_REGNUM;
+ }
- else if (bytes >= 4 && align >= 4)
- {
- load_store[num].load = "lw\t%0,%1";
- load_store[num].load_nop = "lw\t%0,%1%#";
- load_store[num].store = "sw\t%0,%1";
- load_store[num].last_store = "sw\t%0,%1";
- load_store[num].final = 0;
- load_store[num].mode = SImode;
- offset += 4;
- bytes -= 4;
- }
+ /* We need a global pointer, but perhaps we can use a call-clobbered
+ register instead of $gp. */
+ if (TARGET_CALL_SAVED_GP && current_function_is_leaf)
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (!df_regs_ever_live_p (regno)
+ && call_really_used_regs[regno]
+ && !fixed_regs[regno]
+ && regno != PIC_FUNCTION_ADDR_REGNUM)
+ return regno;
- else if (bytes >= 4 && ! TARGET_MIPS16)
- {
- if (BYTES_BIG_ENDIAN)
- {
- load_store[num].load = "lwl\t%0,%1\n\tlwr\t%0,%2";
- load_store[num].load_nop = "lwl\t%0,%1\n\tlwr\t%0,%2%#";
- load_store[num].store = "swl\t%0,%1\n\tswr\t%0,%2";
- load_store[num].last_store = "swr\t%0,%2";
- load_store[num].final = "swl\t%0,%1";
- }
- else
- {
- load_store[num].load = "lwl\t%0,%2\n\tlwr\t%0,%1";
- load_store[num].load_nop = "lwl\t%0,%2\n\tlwr\t%0,%1%#";
- load_store[num].store = "swl\t%0,%2\n\tswr\t%0,%1";
- load_store[num].last_store = "swr\t%0,%1";
- load_store[num].final = "swl\t%0,%2";
- }
+ return GLOBAL_POINTER_REGNUM;
+}
- load_store[num].mode = SImode;
- offset += 4;
- bytes -= 4;
- use_lwl_lwr = 1;
- }
+/* Return true if the current function should treat register REGNO
+ as call-saved. */
- else if (bytes >= 2 && align >= 2)
- {
- load_store[num].load = "lh\t%0,%1";
- load_store[num].load_nop = "lh\t%0,%1%#";
- load_store[num].store = "sh\t%0,%1";
- load_store[num].last_store = "sh\t%0,%1";
- load_store[num].final = 0;
- load_store[num].mode = HImode;
- offset += 2;
- bytes -= 2;
- }
- else
- {
- load_store[num].load = "lb\t%0,%1";
- load_store[num].load_nop = "lb\t%0,%1%#";
- load_store[num].store = "sb\t%0,%1";
- load_store[num].last_store = "sb\t%0,%1";
- load_store[num].final = 0;
- load_store[num].mode = QImode;
- offset++;
- bytes--;
- }
+static bool
+mips_cfun_call_saved_reg_p (unsigned int regno)
+{
+ /* call_insns preserve $28 unless they explicitly say otherwise,
+ so call_really_used_regs[] treats $28 as call-saved. However,
+ we want the ABI property rather than the default call_insn
+ property here. */
+ return (regno == GLOBAL_POINTER_REGNUM
+ ? TARGET_CALL_SAVED_GP
+ : !call_really_used_regs[regno]);
+}
- if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
- {
- dslots_load_total++;
- dslots_load_filled++;
+/* Return true if the function body might clobber register REGNO.
+ We know that REGNO is call-saved. */
- if (CONSTANT_P (src_reg))
- mips_count_memory_refs (src_reg, 1);
+static bool
+mips_cfun_might_clobber_call_saved_reg_p (unsigned int regno)
+{
+ /* Some functions should be treated as clobbering all call-saved
+ registers. */
+ if (crtl->saves_all_registers)
+ return true;
- if (CONSTANT_P (dest_reg))
- mips_count_memory_refs (dest_reg, 1);
- }
+ /* DF handles cases where a register is explicitly referenced in
+ the rtl. Incoming values are passed in call-clobbered registers,
+ so we can assume that any live call-saved register is set within
+ the function. */
+ if (df_regs_ever_live_p (regno))
+ return true;
- /* Emit load/stores now if we have run out of registers or are
- at the end of the move. */
+ /* Check for registers that are clobbered by FUNCTION_PROFILER.
+ These clobbers are not explicit in the rtl. */
+ if (crtl->profile && MIPS_SAVE_REG_FOR_PROFILING_P (regno))
+ return true;
- if (++num == num_regs || bytes == 0)
- {
- /* If only load/store, we need a NOP after the load. */
- if (num == 1)
- {
- load_store[0].load = load_store[0].load_nop;
- if (TARGET_STATS && move_type != BLOCK_MOVE_LAST)
- dslots_load_filled--;
- }
+ /* If we're using a call-saved global pointer, the function's
+ prologue will need to set it up. */
+ if (cfun->machine->global_pointer == regno)
+ return true;
- if (move_type != BLOCK_MOVE_LAST)
- {
- for (i = 0; i < num; i++)
- {
- int offset;
+ /* The function's prologue will need to set the frame pointer if
+ frame_pointer_needed. */
+ if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
+ return true;
- if (!operands[i + 4])
- abort ();
+ /* If a MIPS16 function returns a value in FPRs, its epilogue
+ will need to call an external libgcc routine. This yet-to-be
+ generated call_insn will clobber $31. */
+ if (regno == GP_REG_FIRST + 31 && mips16_cfun_returns_in_fpr_p ())
+ return true;
- if (GET_MODE (operands[i + 4]) != load_store[i].mode)
- operands[i + 4] = gen_rtx_REG (load_store[i].mode,
- REGNO (operands[i + 4]));
+ return false;
+}
- offset = load_store[i].offset;
- xoperands[0] = operands[i + 4];
- xoperands[1] = gen_rtx_MEM (load_store[i].mode,
- plus_constant (src_reg, offset));
+/* Return true if the current function must save register REGNO. */
- if (use_lwl_lwr)
- {
- int extra_offset
- = GET_MODE_SIZE (load_store[i].mode) - 1;
+static bool
+mips_save_reg_p (unsigned int regno)
+{
+ if (mips_cfun_call_saved_reg_p (regno))
+ {
+ if (mips_cfun_might_clobber_call_saved_reg_p (regno))
+ return true;
+
+ /* Save both registers in an FPR pair if either one is used. This is
+ needed for the case when MIN_FPRS_PER_FMT == 1, which allows the odd
+ register to be used without the even register. */
+ if (FP_REG_P (regno)
+ && MAX_FPRS_PER_FMT == 2
+ && mips_cfun_might_clobber_call_saved_reg_p (regno + 1))
+ return true;
+ }
+
+ /* We need to save the incoming return address if __builtin_eh_return
+ is being used to set a different return address. */
+ if (regno == GP_REG_FIRST + 31 && crtl->calls_eh_return)
+ return true;
+
+ return false;
+}
+
+/* Populate the current function's mips_frame_info structure.
+
+ MIPS stack frames look like:
+
+ +-------------------------------+
+ | |
+ | incoming stack arguments |
+ | |
+ +-------------------------------+
+ | |
+ | caller-allocated save area |
+ A | for register arguments |
+ | |
+ +-------------------------------+ <-- incoming stack pointer
+ | |
+ | callee-allocated save area |
+ B | for arguments that are |
+ | split between registers and |
+ | the stack |
+ | |
+ +-------------------------------+ <-- arg_pointer_rtx
+ | |
+ C | callee-allocated save area |
+ | for register varargs |
+ | |
+ +-------------------------------+ <-- frame_pointer_rtx + fp_sp_offset
+ | | + UNITS_PER_HWFPVALUE
+ | FPR save area |
+ | |
+ +-------------------------------+ <-- frame_pointer_rtx + gp_sp_offset
+ | | + UNITS_PER_WORD
+ | GPR save area |
+ | |
+ +-------------------------------+
+ | | \
+ | local variables | | var_size
+ | | /
+ +-------------------------------+
+ | | \
+ | $gp save area | | cprestore_size
+ | | /
+ P +-------------------------------+ <-- hard_frame_pointer_rtx for
+ | | MIPS16 code
+ | outgoing stack arguments |
+ | |
+ +-------------------------------+
+ | |
+ | caller-allocated save area |
+ | for register arguments |
+ | |
+ +-------------------------------+ <-- stack_pointer_rtx
+ frame_pointer_rtx
+ hard_frame_pointer_rtx for
+ non-MIPS16 code.
+
+ At least two of A, B and C will be empty.
+
+ Dynamic stack allocations such as alloca insert data at point P.
+ They decrease stack_pointer_rtx but leave frame_pointer_rtx and
+ hard_frame_pointer_rtx unchanged. */
- xoperands[2] = gen_rtx_MEM (load_store[i].mode,
- plus_constant (src_reg,
- extra_offset
- + offset));
- }
+static void
+mips_compute_frame_info (void)
+{
+ struct mips_frame_info *frame;
+ HOST_WIDE_INT offset, size;
+ unsigned int regno, i;
+
+ frame = &cfun->machine->frame;
+ memset (frame, 0, sizeof (*frame));
+ size = get_frame_size ();
+
+ cfun->machine->global_pointer = mips_global_pointer ();
+
+ /* The first STARTING_FRAME_OFFSET bytes contain the outgoing argument
+ area and the $gp save slot. This area isn't needed in leaf functions,
+ but if the target-independent frame size is nonzero, we're committed
+ to allocating it anyway. */
+ if (size == 0 && current_function_is_leaf)
+ {
+ /* The MIPS 3.0 linker does not like functions that dynamically
+ allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
+ looks like we are trying to create a second frame pointer to the
+ function, so allocate some stack space to make it happy. */
+ if (cfun->calls_alloca)
+ frame->args_size = REG_PARM_STACK_SPACE (cfun->decl);
+ else
+ frame->args_size = 0;
+ frame->cprestore_size = 0;
+ }
+ else
+ {
+ frame->args_size = crtl->outgoing_args_size;
+ frame->cprestore_size = STARTING_FRAME_OFFSET - frame->args_size;
+ }
+ offset = frame->args_size + frame->cprestore_size;
- output_asm_insn (load_store[i].load, xoperands);
- }
- }
+ /* Move above the local variables. */
+ frame->var_size = MIPS_STACK_ALIGN (size);
+ offset += frame->var_size;
- for (i = 0; i < num; i++)
- {
- int last_p = (i == num-1 && bytes == 0);
- int offset = load_store[i].offset;
+ /* Find out which GPRs we need to save. */
+ for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+ if (mips_save_reg_p (regno))
+ {
+ frame->num_gp++;
+ frame->mask |= 1 << (regno - GP_REG_FIRST);
+ }
- xoperands[0] = operands[i + 4];
- xoperands[1] = gen_rtx_MEM (load_store[i].mode,
- plus_constant (dest_reg, offset));
+ /* If this function calls eh_return, we must also save and restore the
+ EH data registers. */
+ if (crtl->calls_eh_return)
+ for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; i++)
+ {
+ frame->num_gp++;
+ frame->mask |= 1 << (EH_RETURN_DATA_REGNO (i) - GP_REG_FIRST);
+ }
+ /* The MIPS16e SAVE and RESTORE instructions have two ranges of registers:
+ $a3-$a0 and $s2-$s8. If we save one register in the range, we must
+ save all later registers too. */
+ if (GENERATE_MIPS16E_SAVE_RESTORE)
+ {
+ mips16e_mask_registers (&frame->mask, mips16e_s2_s8_regs,
+ ARRAY_SIZE (mips16e_s2_s8_regs), &frame->num_gp);
+ mips16e_mask_registers (&frame->mask, mips16e_a0_a3_regs,
+ ARRAY_SIZE (mips16e_a0_a3_regs), &frame->num_gp);
+ }
- if (use_lwl_lwr)
- {
- int extra_offset = GET_MODE_SIZE (load_store[i].mode) - 1;
- xoperands[2] = gen_rtx_MEM (load_store[i].mode,
- plus_constant (dest_reg,
- extra_offset
- + offset));
- }
+ /* Move above the GPR save area. */
+ if (frame->num_gp > 0)
+ {
+ offset += MIPS_STACK_ALIGN (frame->num_gp * UNITS_PER_WORD);
+ frame->gp_sp_offset = offset - UNITS_PER_WORD;
+ }
- if (move_type == BLOCK_MOVE_NORMAL)
- output_asm_insn (load_store[i].store, xoperands);
+ /* Find out which FPRs we need to save. This loop must iterate over
+ the same space as its companion in mips_for_each_saved_reg. */
+ if (TARGET_HARD_FLOAT)
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno += MAX_FPRS_PER_FMT)
+ if (mips_save_reg_p (regno))
+ {
+ frame->num_fp += MAX_FPRS_PER_FMT;
+ frame->fmask |= ~(~0 << MAX_FPRS_PER_FMT) << (regno - FP_REG_FIRST);
+ }
- else if (move_type == BLOCK_MOVE_NOT_LAST)
- {
- if (!last_p)
- output_asm_insn (load_store[i].store, xoperands);
+ /* Move above the FPR save area. */
+ if (frame->num_fp > 0)
+ {
+ offset += MIPS_STACK_ALIGN (frame->num_fp * UNITS_PER_FPREG);
+ frame->fp_sp_offset = offset - UNITS_PER_HWFPVALUE;
+ }
- else if (load_store[i].final != 0)
- output_asm_insn (load_store[i].final, xoperands);
- }
+ /* Move above the callee-allocated varargs save area. */
+ offset += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
+ frame->arg_pointer_offset = offset;
- else if (last_p)
- output_asm_insn (load_store[i].last_store, xoperands);
- }
+ /* Move above the callee-allocated area for pretend stack arguments. */
+ offset += crtl->args.pretend_args_size;
+ frame->total_size = offset;
- num = 0; /* reset load_store */
- use_lwl_lwr = 0;
- }
- }
+ /* Work out the offsets of the save areas from the top of the frame. */
+ if (frame->gp_sp_offset > 0)
+ frame->gp_save_offset = frame->gp_sp_offset - offset;
+ if (frame->fp_sp_offset > 0)
+ frame->fp_save_offset = frame->fp_sp_offset - offset;
- return "";
+ /* MIPS16 code offsets the frame pointer by the size of the outgoing
+ arguments. This tends to increase the chances of using unextended
+ instructions for local variables and incoming arguments. */
+ if (TARGET_MIPS16)
+ frame->hard_frame_pointer_offset = frame->args_size;
}
-\f
-/* Argument support functions. */
-/* Initialize CUMULATIVE_ARGS for a function. */
+/* Return the style of GP load sequence that is being used for the
+ current function. */
-void
-init_cumulative_args (cum, fntype, libname)
- CUMULATIVE_ARGS *cum; /* argument info to initialize */
- tree fntype; /* tree ptr for function decl */
- rtx libname ATTRIBUTE_UNUSED; /* SYMBOL_REF of library name or 0 */
+enum mips_loadgp_style
+mips_current_loadgp_style (void)
{
- static CUMULATIVE_ARGS zero_cum;
- tree param, next_param;
+ if (!TARGET_USE_GOT || cfun->machine->global_pointer == INVALID_REGNUM)
+ return LOADGP_NONE;
- if (TARGET_DEBUG_E_MODE)
- {
- fprintf (stderr,
- "\ninit_cumulative_args, fntype = 0x%.8lx", (long)fntype);
+ if (TARGET_RTP_PIC)
+ return LOADGP_RTP;
- if (!fntype)
- fputc ('\n', stderr);
+ if (TARGET_ABSOLUTE_ABICALLS)
+ return LOADGP_ABSOLUTE;
- else
- {
- tree ret_type = TREE_TYPE (fntype);
- fprintf (stderr, ", fntype code = %s, ret code = %s\n",
- tree_code_name[(int)TREE_CODE (fntype)],
- tree_code_name[(int)TREE_CODE (ret_type)]);
- }
- }
+ return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
+}
- *cum = zero_cum;
- cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
+/* Implement FRAME_POINTER_REQUIRED. */
- /* Determine if this function has variable arguments. This is
- indicated by the last argument being 'void_type_mode' if there
- are no variable arguments. The standard MIPS calling sequence
- passes all arguments in the general purpose registers in this case. */
+bool
+mips_frame_pointer_required (void)
+{
+ /* If the function contains dynamic stack allocations, we need to
+ use the frame pointer to access the static parts of the frame. */
+ if (cfun->calls_alloca)
+ return true;
- for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
- param != 0; param = next_param)
+ /* In MIPS16 mode, we need a frame pointer for a large frame; otherwise,
+ reload may be unable to compute the address of a local variable,
+ since there is no way to add a large constant to the stack pointer
+ without using a second temporary register. */
+ if (TARGET_MIPS16)
{
- next_param = TREE_CHAIN (param);
- if (next_param == 0 && TREE_VALUE (param) != void_type_node)
- cum->gp_reg_found = 1;
+ mips_compute_frame_info ();
+ if (!SMALL_OPERAND (cfun->machine->frame.total_size))
+ return true;
}
+
+ return false;
}
-/* Advance the argument to the next argument position. */
+/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame pointer
+ or argument pointer. TO is either the stack pointer or hard frame
+ pointer. */
-void
-function_arg_advance (cum, mode, type, named)
- CUMULATIVE_ARGS *cum; /* current arg information */
- enum machine_mode mode; /* current arg mode */
- tree type; /* type of the argument or 0 if lib support */
- int named; /* whether or not the argument was named */
+HOST_WIDE_INT
+mips_initial_elimination_offset (int from, int to)
{
- if (TARGET_DEBUG_E_MODE)
- {
- fprintf (stderr,
- "function_adv({gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
- cum->gp_reg_found, cum->arg_number, cum->arg_words,
- GET_MODE_NAME (mode));
- fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
- fprintf (stderr, ", %d )\n\n", named);
- }
+ HOST_WIDE_INT offset;
- cum->arg_number++;
- switch (mode)
+ mips_compute_frame_info ();
+
+ /* Set OFFSET to the offset from the soft frame pointer, which is also
+ the offset from the end-of-prologue stack pointer. */
+ switch (from)
{
- case VOIDmode:
+ case FRAME_POINTER_REGNUM:
+ offset = 0;
+ break;
+
+ case ARG_POINTER_REGNUM:
+ offset = cfun->machine->frame.arg_pointer_offset;
break;
default:
- if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
- && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
- abort ();
+ gcc_unreachable ();
+ }
- cum->gp_reg_found = 1;
- cum->arg_words += ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1)
- / UNITS_PER_WORD);
- break;
+ if (to == HARD_FRAME_POINTER_REGNUM)
+ offset -= cfun->machine->frame.hard_frame_pointer_offset;
- case BLKmode:
- cum->gp_reg_found = 1;
- cum->arg_words += ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
- / UNITS_PER_WORD);
- break;
+ return offset;
+}
+\f
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. */
- case SFmode:
- if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
- cum->fp_arg_words++;
- else
- cum->arg_words++;
- if (! cum->gp_reg_found && cum->arg_number <= 2)
- cum->fp_code += 1 << ((cum->arg_number - 1) * 2);
- break;
+static void
+mips_extra_live_on_entry (bitmap regs)
+{
+ if (TARGET_USE_GOT)
+ {
+ /* PIC_FUNCTION_ADDR_REGNUM is live if we need it to set up
+ the global pointer. */
+ if (!TARGET_ABSOLUTE_ABICALLS)
+ bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
+
+ /* The prologue may set MIPS16_PIC_TEMP_REGNUM to the value of
+ the global pointer. */
+ if (TARGET_MIPS16)
+ bitmap_set_bit (regs, MIPS16_PIC_TEMP_REGNUM);
+
+ /* See the comment above load_call<mode> for details. */
+ bitmap_set_bit (regs, GOT_VERSION_REGNUM);
+ }
+}
+
+/* Implement RETURN_ADDR_RTX. We do not support moving back to a
+ previous frame. */
+
+rtx
+mips_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
+{
+ if (count != 0)
+ return const0_rtx;
+
+ return get_hard_reg_initial_val (Pmode, GP_REG_FIRST + 31);
+}
- case DFmode:
- if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
- cum->fp_arg_words += (TARGET_64BIT ? 1 : 2);
- else
- cum->arg_words += (TARGET_64BIT ? 1 : 2);
- if (! cum->gp_reg_found && ! TARGET_SINGLE_FLOAT && cum->arg_number <= 2)
- cum->fp_code += 2 << ((cum->arg_number - 1) * 2);
- break;
+/* Emit code to change the current function's return address to
+ ADDRESS. SCRATCH is available as a scratch register, if needed.
+ ADDRESS and SCRATCH are both word-mode GPRs. */
- case DImode:
- case TImode:
- cum->gp_reg_found = 1;
- cum->arg_words += (TARGET_64BIT ? 1 : 2);
- break;
+void
+mips_set_return_address (rtx address, rtx scratch)
+{
+ rtx slot_address;
- case QImode:
- case HImode:
- case SImode:
- cum->gp_reg_found = 1;
- cum->arg_words++;
- break;
- }
+ gcc_assert (BITSET_P (cfun->machine->frame.mask, 31));
+ slot_address = mips_add_offset (scratch, stack_pointer_rtx,
+ cfun->machine->frame.gp_sp_offset);
+ mips_emit_move (gen_frame_mem (GET_MODE (address), slot_address), address);
}
-/* Return an RTL expression containing the register for the given mode,
- or 0 if the argument is to be passed on the stack. */
+/* Return a MEM rtx for the cprestore slot, using TEMP as a temporary base
+ register if need be. */
-struct rtx_def *
-function_arg (cum, mode, type, named)
- CUMULATIVE_ARGS *cum; /* current arg information */
- enum machine_mode mode; /* current arg mode */
- tree type; /* type of the argument or 0 if lib support */
- int named; /* != 0 for normal args, == 0 for ... args */
+static rtx
+mips_cprestore_slot (rtx temp)
{
- rtx ret;
- int regbase = -1;
- int bias = 0;
- unsigned int *arg_words = &cum->arg_words;
- int struct_p = (type != 0
- && (TREE_CODE (type) == RECORD_TYPE
- || TREE_CODE (type) == UNION_TYPE
- || TREE_CODE (type) == QUAL_UNION_TYPE));
+ const struct mips_frame_info *frame;
+ rtx base;
+ HOST_WIDE_INT offset;
- if (TARGET_DEBUG_E_MODE)
+ frame = &cfun->machine->frame;
+ if (frame_pointer_needed)
+ {
+ base = hard_frame_pointer_rtx;
+ offset = frame->args_size - frame->hard_frame_pointer_offset;
+ }
+ else
{
- fprintf (stderr,
- "function_arg( {gp reg found = %d, arg # = %2d, words = %2d}, %4s, ",
- cum->gp_reg_found, cum->arg_number, cum->arg_words,
- GET_MODE_NAME (mode));
- fprintf (stderr, HOST_PTR_PRINTF, (const PTR) type);
- fprintf (stderr, ", %d ) = ", named);
+ base = stack_pointer_rtx;
+ offset = frame->args_size;
}
+ return gen_frame_mem (Pmode, mips_add_offset (temp, base, offset));
+}
+/* Restore $gp from its save slot, using TEMP as a temporary base register
+ if need be. This function is for o32 and o64 abicalls only. */
- cum->last_arg_fp = 0;
- switch (mode)
+void
+mips_restore_gp (rtx temp)
+{
+ gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
+
+ if (cfun->machine->global_pointer == INVALID_REGNUM)
+ return;
+
+ if (TARGET_MIPS16)
{
- case SFmode:
- if (mips_abi == ABI_32 || mips_abi == ABI_O64)
- {
- if (cum->gp_reg_found || cum->arg_number >= 2 || TARGET_SOFT_FLOAT)
- regbase = GP_ARG_FIRST;
- else
- {
- regbase = FP_ARG_FIRST;
+ mips_emit_move (temp, mips_cprestore_slot (temp));
+ mips_emit_move (pic_offset_table_rtx, temp);
+ }
+ else
+ mips_emit_move (pic_offset_table_rtx, mips_cprestore_slot (temp));
+ if (!TARGET_EXPLICIT_RELOCS)
+ emit_insn (gen_blockage ());
+}
+\f
+/* A function to save or store a register. The first argument is the
+ register and the second is the stack slot. */
+typedef void (*mips_save_restore_fn) (rtx, rtx);
- /* If the first arg was a float in a floating point register,
- then set bias to align this float arg properly. */
- if (cum->arg_words == 1)
- bias = 1;
- }
- }
- else if (mips_abi == ABI_EABI && ! TARGET_SOFT_FLOAT)
- {
- if (! TARGET_64BIT)
- cum->fp_arg_words += cum->fp_arg_words & 1;
- cum->last_arg_fp = 1;
- arg_words = &cum->fp_arg_words;
- regbase = FP_ARG_FIRST;
- }
- /* The MIPS eabi says only structures containing doubles get passed in a
- fp register, so force a structure containing a float to be passed in
- the integer registers. */
- else if (mips_abi == ABI_MEABI && struct_p)
- regbase = GP_ARG_FIRST;
- else
- regbase = (TARGET_SOFT_FLOAT || ! named ? GP_ARG_FIRST : FP_ARG_FIRST);
- break;
+/* Use FN to save or restore register REGNO. MODE is the register's
+ mode and OFFSET is the offset of its save slot from the current
+ stack pointer. */
- case DFmode:
- if (! TARGET_64BIT)
- {
- if (mips_abi == ABI_EABI
- && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
- cum->fp_arg_words += cum->fp_arg_words & 1;
- else
- cum->arg_words += cum->arg_words & 1;
- }
+static void
+mips_save_restore_reg (enum machine_mode mode, int regno,
+ HOST_WIDE_INT offset, mips_save_restore_fn fn)
+{
+ rtx mem;
- if (mips_abi == ABI_32 || mips_abi == ABI_O64)
- regbase = ((cum->gp_reg_found
- || TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT
- || cum->arg_number >= 2)
- ? GP_ARG_FIRST : FP_ARG_FIRST);
- else if (mips_abi == ABI_EABI
- && ! TARGET_SOFT_FLOAT && ! TARGET_SINGLE_FLOAT)
- {
- cum->last_arg_fp = 1;
- arg_words = &cum->fp_arg_words;
- regbase = FP_ARG_FIRST;
- }
- else
- regbase = (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT || ! named
- ? GP_ARG_FIRST : FP_ARG_FIRST);
- break;
+ mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset));
+ fn (gen_rtx_REG (mode, regno), mem);
+}
- default:
- if (GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
- && GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
- abort ();
-
- /* Drops through. */
- case BLKmode:
- if (type != NULL_TREE && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD
- && ! TARGET_64BIT && mips_abi != ABI_EABI)
- cum->arg_words += (cum->arg_words & 1);
- regbase = GP_ARG_FIRST;
- break;
+/* Call FN for each register that is saved by the current function.
+ SP_OFFSET is the offset of the current stack pointer from the start
+ of the frame. */
- case VOIDmode:
- case QImode:
- case HImode:
- case SImode:
- regbase = GP_ARG_FIRST;
- break;
+static void
+mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
+{
+ enum machine_mode fpr_mode;
+ HOST_WIDE_INT offset;
+ int regno;
- case DImode:
- case TImode:
- if (! TARGET_64BIT)
- cum->arg_words += (cum->arg_words & 1);
- regbase = GP_ARG_FIRST;
- }
+ /* Save registers starting from high to low. The debuggers prefer at least
+ the return register be stored at func+4, and also it allows us not to
+ need a nop in the epilogue if at least one register is reloaded in
+ addition to return address. */
+ offset = cfun->machine->frame.gp_sp_offset - sp_offset;
+ for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
+ if (BITSET_P (cfun->machine->frame.mask, regno - GP_REG_FIRST))
+ {
+ mips_save_restore_reg (word_mode, regno, offset, fn);
+ offset -= UNITS_PER_WORD;
+ }
- if (*arg_words >= (unsigned) MAX_ARGS_IN_REGISTERS)
- {
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "<stack>%s\n", struct_p ? ", [struct]" : "");
+ /* This loop must iterate over the same space as its companion in
+ mips_compute_frame_info. */
+ offset = cfun->machine->frame.fp_sp_offset - sp_offset;
+ fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
+ for (regno = FP_REG_LAST - MAX_FPRS_PER_FMT + 1;
+ regno >= FP_REG_FIRST;
+ regno -= MAX_FPRS_PER_FMT)
+ if (BITSET_P (cfun->machine->frame.fmask, regno - FP_REG_FIRST))
+ {
+ mips_save_restore_reg (fpr_mode, regno, offset, fn);
+ offset -= GET_MODE_SIZE (fpr_mode);
+ }
+}
+\f
+/* If we're generating n32 or n64 abicalls, and the current function
+ does not use $28 as its global pointer, emit a cplocal directive.
+ Use pic_offset_table_rtx as the argument to the directive. */
- ret = 0;
- }
- else
- {
- if (regbase == -1)
- abort ();
+static void
+mips_output_cplocal (void)
+{
+ if (!TARGET_EXPLICIT_RELOCS
+ && cfun->machine->global_pointer != INVALID_REGNUM
+ && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
+ output_asm_insn (".cplocal %+", 0);
+}
- if (! type || TREE_CODE (type) != RECORD_TYPE
- || mips_abi == ABI_32 || mips_abi == ABI_EABI
- || mips_abi == ABI_O64 || mips_abi == ABI_MEABI
- || ! named
- || ! TYPE_SIZE_UNIT (type)
- || ! host_integerp (TYPE_SIZE_UNIT (type), 1))
- {
+/* Implement TARGET_OUTPUT_FUNCTION_PROLOGUE. */
- unsigned int arg_reg = (regbase + *arg_words + bias);
- ret = gen_rtx_REG (mode, arg_reg);
- if (mips_abi == ABI_MEABI
- && regbase == FP_ARG_FIRST
- && ! cum->prototype)
- {
- /* To make K&R varargs work we need to pass floating
- point arguments in both integer and FP registers. */
- ret = gen_rtx_PARALLEL (mode,
- gen_rtvec (2,
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (mode,
- arg_reg + GP_ARG_FIRST - FP_ARG_FIRST),
- const0_rtx), gen_rtx_EXPR_LIST (VOIDmode, ret, const0_rtx)));
- }
- }
- else
- {
- /* The Irix 6 n32/n64 ABIs say that if any 64 bit chunk of the
- structure contains a double in its entirety, then that 64 bit
- chunk is passed in a floating point register. */
- tree field;
-
- /* First check to see if there is any such field. */
- for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL
- && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
- && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD
- && host_integerp (bit_position (field), 0)
- && int_bit_position (field) % BITS_PER_WORD == 0)
- break;
+static void
+mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+ const char *fnname;
- /* If the whole struct fits a DFmode register,
- we don't need the PARALLEL. */
- if (! field || mode == DFmode)
- ret = gen_rtx_REG (mode, regbase + *arg_words + bias);
- else
- {
- /* Now handle the special case by returning a PARALLEL
- indicating where each 64 bit chunk goes. */
- unsigned int chunks;
- HOST_WIDE_INT bitpos;
- unsigned int regno;
- unsigned int i;
-
- /* ??? If this is a packed structure, then the last hunk won't
- be 64 bits. */
-
- chunks
- = tree_low_cst (TYPE_SIZE_UNIT (type), 1) / UNITS_PER_WORD;
- if (chunks + *arg_words + bias > (unsigned) MAX_ARGS_IN_REGISTERS)
- chunks = MAX_ARGS_IN_REGISTERS - *arg_words - bias;
-
- /* assign_parms checks the mode of ENTRY_PARM, so we must
- use the actual mode here. */
- ret = gen_rtx_PARALLEL (mode, rtvec_alloc (chunks));
-
- bitpos = 0;
- regno = regbase + *arg_words + bias;
- field = TYPE_FIELDS (type);
- for (i = 0; i < chunks; i++)
- {
- rtx reg;
-
- for (; field; field = TREE_CHAIN (field))
- if (TREE_CODE (field) == FIELD_DECL
- && int_bit_position (field) >= bitpos)
- break;
-
- if (field
- && int_bit_position (field) == bitpos
- && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
- && !TARGET_SOFT_FLOAT
- && TYPE_PRECISION (TREE_TYPE (field)) == BITS_PER_WORD)
- reg = gen_rtx_REG (DFmode,
- regno + FP_ARG_FIRST - GP_ARG_FIRST);
- else
- reg = gen_rtx_REG (word_mode, regno);
-
- XVECEXP (ret, 0, i)
- = gen_rtx_EXPR_LIST (VOIDmode, reg,
- GEN_INT (bitpos / BITS_PER_UNIT));
-
- bitpos += 64;
- regno++;
- }
- }
- }
+#ifdef SDB_DEBUGGING_INFO
+ if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
+ SDB_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
+#endif
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "%s%s\n", reg_names[regbase + *arg_words + bias],
- struct_p ? ", [struct]" : "");
-
- /* The following is a hack in order to pass 1 byte structures
- the same way that the MIPS compiler does (namely by passing
- the structure in the high byte or half word of the register).
- This also makes varargs work. If we have such a structure,
- we save the adjustment RTL, and the call define expands will
- emit them. For the VOIDmode argument (argument after the
- last real argument), pass back a parallel vector holding each
- of the adjustments. */
-
- /* ??? function_arg can be called more than once for each argument.
- As a result, we compute more adjustments than we need here.
- See the CUMULATIVE_ARGS definition in mips.h. */
-
- /* ??? This scheme requires everything smaller than the word size to
- shifted to the left, but when TARGET_64BIT and ! TARGET_INT64,
- that would mean every int needs to be shifted left, which is very
- inefficient. Let's not carry this compatibility to the 64 bit
- calling convention for now. */
-
- if (struct_p && int_size_in_bytes (type) < UNITS_PER_WORD
- && ! TARGET_64BIT
- && mips_abi != ABI_EABI
- && mips_abi != ABI_MEABI)
- {
- rtx amount = GEN_INT (BITS_PER_WORD
- - int_size_in_bytes (type) * BITS_PER_UNIT);
- rtx reg = gen_rtx_REG (word_mode, regbase + *arg_words + bias);
+ /* In MIPS16 mode, we may need to generate a non-MIPS16 stub to handle
+ floating-point arguments. */
+ if (TARGET_MIPS16
+ && TARGET_HARD_FLOAT_ABI
+ && crtl->args.info.fp_code != 0)
+ mips16_build_function_stub ();
- if (TARGET_64BIT)
- cum->adjust[cum->num_adjusts++] = gen_ashldi3 (reg, reg, amount);
- else
- cum->adjust[cum->num_adjusts++] = gen_ashlsi3 (reg, reg, amount);
- }
- }
+ /* Get the function name the same way that toplev.c does before calling
+ assemble_start_function. This is needed so that the name used here
+ exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
+ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ mips_start_function_definition (fnname, TARGET_MIPS16);
- /* We will be called with a mode of VOIDmode after the last argument
- has been seen. Whatever we return will be passed to the call
- insn. If we need any shifts for small structures, return them in
- a PARALLEL; in that case, stuff the mips16 fp_code in as the
- mode. Otherwise, if we have need a mips16 fp_code, return a REG
- with the code stored as the mode. */
- if (mode == VOIDmode)
- {
- if (cum->num_adjusts > 0)
- ret = gen_rtx (PARALLEL, (enum machine_mode) cum->fp_code,
- gen_rtvec_v (cum->num_adjusts, cum->adjust));
- else if (TARGET_MIPS16 && cum->fp_code != 0)
- ret = gen_rtx (REG, (enum machine_mode) cum->fp_code, 0);
- }
+ /* Stop mips_file_end from treating this function as external. */
+ if (TARGET_IRIX && mips_abi == ABI_32)
+ TREE_ASM_WRITTEN (DECL_NAME (cfun->decl)) = 1;
- return ret;
-}
+ /* Output MIPS-specific frame information. */
+ if (!flag_inhibit_size_directive)
+ {
+ const struct mips_frame_info *frame;
-int
-function_arg_partial_nregs (cum, mode, type, named)
- CUMULATIVE_ARGS *cum; /* current arg information */
- enum machine_mode mode; /* current arg mode */
- tree type; /* type of the argument or 0 if lib support */
- int named ATTRIBUTE_UNUSED;/* != 0 for normal args, == 0 for ... args */
-{
- if ((mode == BLKmode
- || GET_MODE_CLASS (mode) != MODE_COMPLEX_INT
- || GET_MODE_CLASS (mode) != MODE_COMPLEX_FLOAT)
- && cum->arg_words < (unsigned) MAX_ARGS_IN_REGISTERS
- && mips_abi != ABI_EABI)
- {
- int words;
- if (mode == BLKmode)
- words = ((int_size_in_bytes (type) + UNITS_PER_WORD - 1)
- / UNITS_PER_WORD);
- else
- words = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ frame = &cfun->machine->frame;
- if (words + cum->arg_words <= (unsigned) MAX_ARGS_IN_REGISTERS)
- return 0; /* structure fits in registers */
+ /* .frame FRAMEREG, FRAMESIZE, RETREG. */
+ fprintf (file,
+ "\t.frame\t%s," HOST_WIDE_INT_PRINT_DEC ",%s\t\t"
+ "# vars= " HOST_WIDE_INT_PRINT_DEC
+ ", regs= %d/%d"
+ ", args= " HOST_WIDE_INT_PRINT_DEC
+ ", gp= " HOST_WIDE_INT_PRINT_DEC "\n",
+ reg_names[frame_pointer_needed
+ ? HARD_FRAME_POINTER_REGNUM
+ : STACK_POINTER_REGNUM],
+ (frame_pointer_needed
+ ? frame->total_size - frame->hard_frame_pointer_offset
+ : frame->total_size),
+ reg_names[GP_REG_FIRST + 31],
+ frame->var_size,
+ frame->num_gp, frame->num_fp,
+ frame->args_size,
+ frame->cprestore_size);
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "function_arg_partial_nregs = %d\n",
- MAX_ARGS_IN_REGISTERS - cum->arg_words);
+ /* .mask MASK, OFFSET. */
+ fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+ frame->mask, frame->gp_save_offset);
- return MAX_ARGS_IN_REGISTERS - cum->arg_words;
+ /* .fmask MASK, OFFSET. */
+ fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
+ frame->fmask, frame->fp_save_offset);
}
- else if (mode == DImode
- && cum->arg_words == MAX_ARGS_IN_REGISTERS - (unsigned)1
- && ! TARGET_64BIT && mips_abi != ABI_EABI)
+ /* Handle the initialization of $gp for SVR4 PIC, if applicable.
+ Also emit the ".set noreorder; .set nomacro" sequence for functions
+ that need it. */
+ if (mips_current_loadgp_style () == LOADGP_OLDABI)
{
- if (TARGET_DEBUG_E_MODE)
- fprintf (stderr, "function_arg_partial_nregs = 1\n");
-
- return 1;
+ if (TARGET_MIPS16)
+ {
+ /* This is a fixed-form sequence. The position of the
+ first two instructions is important because of the
+ way _gp_disp is defined. */
+ output_asm_insn ("li\t$2,%%hi(_gp_disp)", 0);
+ output_asm_insn ("addiu\t$3,$pc,%%lo(_gp_disp)", 0);
+ output_asm_insn ("sll\t$2,16", 0);
+ output_asm_insn ("addu\t$2,$3", 0);
+ }
+ /* .cpload must be in a .set noreorder but not a .set nomacro block. */
+ else if (!cfun->machine->all_noreorder_p)
+ output_asm_insn ("%(.cpload\t%^%)", 0);
+ else
+ output_asm_insn ("%(.cpload\t%^\n\t%<", 0);
}
+ else if (cfun->machine->all_noreorder_p)
+ output_asm_insn ("%(%<", 0);
- return 0;
+ /* Tell the assembler which register we're using as the global
+ pointer. This is needed for thunks, since they can use either
+ explicit relocs or assembler macros. */
+ mips_output_cplocal ();
}
-\f
-/* Create the va_list data type.
- We keep 3 pointers, and two offsets.
- Two pointers are to the overflow area, which starts at the CFA.
- One of these is constant, for addressing into the GPR save area below it.
- The other is advanced up the stack through the overflow region.
- The third pointer is to the GPR save area. Since the FPR save area
- is just below it, we can address FPR slots off this pointer.
- We also keep two one-byte offsets, which are to be subtracted from the
- constant pointers to yield addresses in the GPR and FPR save areas.
- These are downcounted as float or non-float arguments are used,
- and when they get to zero, the argument must be obtained from the
- overflow region.
- If TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT, then no FPR save area exists,
- and a single pointer is enough. It's started at the GPR save area,
- and is advanced, period.
- Note that the GPR save area is not constant size, due to optimization
- in the prologue. Hence, we can't use a design with two pointers
- and two offsets, although we could have designed this with two pointers
- and three offsets. */
-
-
-tree
-mips_build_va_list ()
-{
- if (mips_abi == ABI_EABI && !TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
- {
- tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff, record;
-
- record = make_node (RECORD_TYPE);
-
- f_ovfl = build_decl (FIELD_DECL, get_identifier ("__overflow_argptr"),
- ptr_type_node);
- f_gtop = build_decl (FIELD_DECL, get_identifier ("__gpr_top"),
- ptr_type_node);
- f_ftop = build_decl (FIELD_DECL, get_identifier ("__fpr_top"),
- ptr_type_node);
- f_goff = build_decl (FIELD_DECL, get_identifier ("__gpr_offset"),
- unsigned_char_type_node);
- f_foff = build_decl (FIELD_DECL, get_identifier ("__fpr_offset"),
- unsigned_char_type_node);
+/* Implement TARGET_OUTPUT_FUNCTION_EPILOGUE. */
- DECL_FIELD_CONTEXT (f_ovfl) = record;
- DECL_FIELD_CONTEXT (f_gtop) = record;
- DECL_FIELD_CONTEXT (f_ftop) = record;
- DECL_FIELD_CONTEXT (f_goff) = record;
- DECL_FIELD_CONTEXT (f_foff) = record;
+static void
+mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+ const char *fnname;
- TYPE_FIELDS (record) = f_ovfl;
- TREE_CHAIN (f_ovfl) = f_gtop;
- TREE_CHAIN (f_gtop) = f_ftop;
- TREE_CHAIN (f_ftop) = f_goff;
- TREE_CHAIN (f_goff) = f_foff;
+ /* Reinstate the normal $gp. */
+ SET_REGNO (pic_offset_table_rtx, GLOBAL_POINTER_REGNUM);
+ mips_output_cplocal ();
- layout_type (record);
- return record;
+ if (cfun->machine->all_noreorder_p)
+ {
+ /* Avoid using %>%) since it adds excess whitespace. */
+ output_asm_insn (".set\tmacro", 0);
+ output_asm_insn (".set\treorder", 0);
+ set_noreorder = set_nomacro = 0;
}
- else
- return ptr_type_node;
-}
-/* Implement va_start. stdarg_p is 0 if implementing
- __builtin_varargs_va_start, 1 if implementing __builtin_stdarg_va_start.
- Note that this routine isn't called when compiling e.g. "_vfprintf_r".
- (It doesn't have "...", so it inherits the pointers of its caller.) */
+ /* Get the function name the same way that toplev.c does before calling
+ assemble_start_function. This is needed so that the name used here
+ exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
+ fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ mips_end_function_definition (fnname);
+}
+\f
+/* Save register REG to MEM. Make the instruction frame-related. */
-void
-mips_va_start (stdarg_p, valist, nextarg)
- int stdarg_p;
- tree valist;
- rtx nextarg;
+static void
+mips_save_reg (rtx reg, rtx mem)
{
- int int_arg_words;
- tree t;
-
- /* Find out how many non-float named formals */
- int_arg_words = current_function_args_info.arg_words;
-
- if (mips_abi == ABI_EABI)
+ if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
{
- int gpr_save_area_size;
- /* Note UNITS_PER_WORD is 4 bytes or 8, depending on TARGET_64BIT. */
- if (int_arg_words < 8 )
- /* Adjust for the prologue's economy measure */
- gpr_save_area_size = (8 - int_arg_words) * UNITS_PER_WORD;
- else
- gpr_save_area_size = 0;
+ rtx x1, x2;
- if (!TARGET_SOFT_FLOAT && !TARGET_SINGLE_FLOAT)
- {
- tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
- tree ovfl, gtop, ftop, goff, foff;
- tree gprv;
- int float_formals, fpr_offset, size_excess, floats_passed_in_regs;
- int fpr_save_offset;
-
- float_formals = current_function_args_info.fp_arg_words;
- /* If mips2, the number of formals is half the reported # of words */
- if (!TARGET_64BIT)
- float_formals /= 2;
- floats_passed_in_regs = (TARGET_64BIT ? 8 : 4);
-
- f_ovfl = TYPE_FIELDS (va_list_type_node);
- f_gtop = TREE_CHAIN (f_ovfl);
- f_ftop = TREE_CHAIN (f_gtop);
- f_goff = TREE_CHAIN (f_ftop);
- f_foff = TREE_CHAIN (f_goff);
-
- ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
- gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
- ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
- goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
- foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
-
- /* Emit code setting a pointer into the overflow (shared-stack) area.
- If there were more than 8 non-float formals, or more than 8
- float formals, then this pointer isn't to the base of the area.
- In that case, it must point to where the first vararg is. */
- size_excess = 0;
- if (float_formals > floats_passed_in_regs)
- size_excess += (float_formals-floats_passed_in_regs) * 8;
- if (int_arg_words > 8)
- size_excess += (int_arg_words-8) * UNITS_PER_WORD;
-
- /* FIXME: for mips2, the above size_excess can be wrong. Because the
- overflow stack holds mixed size items, there can be alignments,
- so that an 8 byte double following a 4 byte int will be on an
- 8 byte boundary. This means that the above calculation should
- take into account the exact sequence of floats and non-floats
- which make up the excess. That calculation should be rolled
- into the code which sets the current_function_args_info struct.
- The above then reduces to a fetch from that struct. */
-
-
- t = make_tree (TREE_TYPE (ovfl), virtual_incoming_args_rtx);
- if (size_excess)
- t = build (PLUS_EXPR, TREE_TYPE (ovfl), t,
- build_int_2 (size_excess, 0));
- t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- /* Emit code setting a ptr to the base of the overflow area. */
- t = make_tree (TREE_TYPE (gtop), virtual_incoming_args_rtx);
- t = build (MODIFY_EXPR, TREE_TYPE (gtop), gtop, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- /* Emit code setting a pointer to the GPR save area.
- More precisely, a pointer to off-the-end of the FPR save area.
- If mips4, this is gpr_save_area_size below the overflow area.
- If mips2, also round down to an 8-byte boundary, since the FPR
- save area is 8-byte aligned, and GPR is 4-byte-aligned.
- Therefore there can be a 4-byte gap between the save areas. */
- gprv = make_tree (TREE_TYPE (ftop), virtual_incoming_args_rtx);
- fpr_save_offset = gpr_save_area_size;
- if (!TARGET_64BIT)
- {
- if (fpr_save_offset & 7)
- fpr_save_offset += 4;
- }
- if (fpr_save_offset)
- gprv = build (PLUS_EXPR, TREE_TYPE (ftop), gprv,
- build_int_2 (-fpr_save_offset,-1));
- t = build (MODIFY_EXPR, TREE_TYPE (ftop), ftop, gprv);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- /* Emit code initting an offset to the size of the GPR save area */
- t = build (MODIFY_EXPR, TREE_TYPE (goff), goff,
- build_int_2 (gpr_save_area_size,0));
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- /* Emit code initting an offset from ftop to the first float
- vararg. This varies in size, since any float
- varargs are put in the FPR save area after the formals.
- Note it's 8 bytes/formal regardless of TARGET_64BIT.
- However, mips2 stores 4 GPRs, mips4 stores 8 GPRs.
- If there are 8 or more float formals, init to zero.
- (In fact, the formals aren't stored in the bottom of the
- FPR save area: they are elsewhere, and the size of the FPR
- save area is economized by the prologue. But this code doesn't
- care. This design is unaffected by that fact.) */
- if (float_formals >= floats_passed_in_regs)
- fpr_offset = 0;
- else
- fpr_offset = (floats_passed_in_regs - float_formals) * 8;
- t = build (MODIFY_EXPR, TREE_TYPE (foff), foff,
- build_int_2 (fpr_offset,0));
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
+ if (mips_split_64bit_move_p (mem, reg))
+ mips_split_doubleword_move (mem, reg);
else
- {
- /* TARGET_SOFT_FLOAT or TARGET_SINGLE_FLOAT */
-
- /* Everything is in the GPR save area, or in the overflow
- area which is contiguous with it. */
+ mips_emit_move (mem, reg);
- int offset = -gpr_save_area_size;
- if (gpr_save_area_size == 0)
- offset = (stdarg_p ? 0 : -UNITS_PER_WORD);
- nextarg = plus_constant (nextarg, offset);
- std_expand_builtin_va_start (1, valist, nextarg);
- }
+ x1 = mips_frame_set (mips_subword (mem, false),
+ mips_subword (reg, false));
+ x2 = mips_frame_set (mips_subword (mem, true),
+ mips_subword (reg, true));
+ mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
}
else
{
- /* not EABI */
- int ofs;
-
- if (stdarg_p)
- ofs = 0;
- else
+ if (TARGET_MIPS16
+ && REGNO (reg) != GP_REG_FIRST + 31
+ && !M16_REG_P (REGNO (reg)))
{
- /* ??? This had been conditional on
- _MIPS_SIM == _MIPS_SIM_ABI64 || _MIPS_SIM == _MIPS_SIM_NABI32
- and both iris5.h and iris6.h define _MIPS_SIM. */
- if (mips_abi == ABI_N32 || mips_abi == ABI_64)
- ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
- else if (mips_abi == ABI_MEABI)
- ofs = (int_arg_words >= 8 ? -UNITS_PER_WORD : 0);
- else
- ofs = -UNITS_PER_WORD;
+ /* 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. */
+ mips_emit_move (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
+ mips_emit_move (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
}
+ else
+ mips_emit_move (mem, reg);
- nextarg = plus_constant (nextarg, ofs);
- std_expand_builtin_va_start (1, valist, nextarg);
+ mips_set_frame_expr (mips_frame_set (mem, reg));
}
}
-/* Implement va_arg. */
+/* The __gnu_local_gp symbol. */
-rtx
-mips_va_arg (valist, type)
- tree valist, type;
-{
- HOST_WIDE_INT size, rsize;
- rtx addr_rtx;
- tree t;
+static GTY(()) rtx mips_gnu_local_gp;
- size = int_size_in_bytes (type);
- rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
+/* If we're generating n32 or n64 abicalls, emit instructions
+ to set up the global pointer. */
- if (mips_abi == ABI_EABI)
- {
- int indirect;
- rtx r, lab_over = NULL_RTX, lab_false;
- tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
- tree ovfl, gtop, ftop, goff, foff;
+static void
+mips_emit_loadgp (void)
+{
+ rtx addr, offset, incoming_address, base, index, pic_reg;
- indirect
- = function_arg_pass_by_reference (NULL, TYPE_MODE (type), type, 0);
- if (indirect)
+ pic_reg = TARGET_MIPS16 ? MIPS16_PIC_TEMP : pic_offset_table_rtx;
+ switch (mips_current_loadgp_style ())
+ {
+ case LOADGP_ABSOLUTE:
+ if (mips_gnu_local_gp == NULL)
{
- size = POINTER_SIZE / BITS_PER_UNIT;
- rsize = UNITS_PER_WORD;
+ mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
+ SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
}
+ emit_insn (Pmode == SImode
+ ? gen_loadgp_absolute_si (pic_reg, mips_gnu_local_gp)
+ : gen_loadgp_absolute_di (pic_reg, mips_gnu_local_gp));
+ break;
- addr_rtx = gen_reg_rtx (Pmode);
-
- if (TARGET_SOFT_FLOAT || TARGET_SINGLE_FLOAT)
- {
- /* Case of all args in a merged stack. No need to check bounds,
- just advance valist along the stack. */
-
- tree gpr = valist;
- if (! indirect
- && ! TARGET_64BIT
- && TYPE_ALIGN (type) > (unsigned) BITS_PER_WORD)
- {
- t = build (PLUS_EXPR, TREE_TYPE (gpr), gpr,
- build_int_2 (2*UNITS_PER_WORD - 1, 0));
- t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
- build_int_2 (-2*UNITS_PER_WORD, -1));
- t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
-
- t = build (POSTINCREMENT_EXPR, TREE_TYPE (gpr), gpr,
- size_int (rsize));
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- /* flush the POSTINCREMENT */
- emit_queue();
+ case LOADGP_OLDABI:
+ /* Added by mips_output_function_prologue. */
+ break;
- if (indirect)
- {
- r = gen_rtx_MEM (Pmode, addr_rtx);
- set_mem_alias_set (r, get_varargs_alias_set ());
- emit_move_insn (addr_rtx, r);
- }
- else
- {
- if (BYTES_BIG_ENDIAN && rsize != size)
- addr_rtx = plus_constant (addr_rtx, rsize - size);
- }
- return addr_rtx;
- }
+ case LOADGP_NEWABI:
+ addr = XEXP (DECL_RTL (current_function_decl), 0);
+ offset = mips_unspec_address (addr, SYMBOL_GOTOFF_LOADGP);
+ incoming_address = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ emit_insn (Pmode == SImode
+ ? gen_loadgp_newabi_si (pic_reg, offset, incoming_address)
+ : gen_loadgp_newabi_di (pic_reg, offset, incoming_address));
+ break;
- /* Not a simple merged stack. Need ptrs and indexes left by va_start. */
+ case LOADGP_RTP:
+ base = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_BASE));
+ index = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (VXWORKS_GOTT_INDEX));
+ emit_insn (Pmode == SImode
+ ? gen_loadgp_rtp_si (pic_reg, base, index)
+ : gen_loadgp_rtp_di (pic_reg, base, index));
+ break;
- f_ovfl = TYPE_FIELDS (va_list_type_node);
- f_gtop = TREE_CHAIN (f_ovfl);
- f_ftop = TREE_CHAIN (f_gtop);
- f_goff = TREE_CHAIN (f_ftop);
- f_foff = TREE_CHAIN (f_goff);
+ default:
+ return;
+ }
- ovfl = build (COMPONENT_REF, TREE_TYPE (f_ovfl), valist, f_ovfl);
- gtop = build (COMPONENT_REF, TREE_TYPE (f_gtop), valist, f_gtop);
- ftop = build (COMPONENT_REF, TREE_TYPE (f_ftop), valist, f_ftop);
- goff = build (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff);
- foff = build (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff);
+ if (TARGET_MIPS16)
+ emit_insn (gen_copygp_mips16 (pic_offset_table_rtx, pic_reg));
- lab_false = gen_label_rtx ();
- lab_over = gen_label_rtx ();
+ /* Emit a blockage if there are implicit uses of the GP register.
+ This includes profiled functions, because FUNCTION_PROFILE uses
+ a jal macro. */
+ if (!TARGET_EXPLICIT_RELOCS || crtl->profile)
+ emit_insn (gen_loadgp_blockage ());
+}
- if (TREE_CODE (type) == REAL_TYPE)
- {
+/* Expand the "prologue" pattern. */
- /* Emit code to branch if foff == 0. */
- r = expand_expr (foff, NULL_RTX, TYPE_MODE (TREE_TYPE (foff)),
- EXPAND_NORMAL);
- emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
- 1, lab_false);
-
- /* Emit code for addr_rtx = ftop - foff */
- t = build (MINUS_EXPR, TREE_TYPE (ftop), ftop, foff );
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- /* Emit code for foff-=8.
- Advances the offset up FPR save area by one double */
- t = build (MINUS_EXPR, TREE_TYPE (foff), foff, build_int_2 (8, 0));
- t = build (MODIFY_EXPR, TREE_TYPE (foff), foff, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- emit_queue();
- emit_jump (lab_over);
- emit_barrier ();
- emit_label (lab_false);
-
- if (!TARGET_64BIT)
- {
- /* For mips2, the overflow area contains mixed size items.
- If a 4-byte int is followed by an 8-byte float, then
- natural alignment causes a 4 byte gap.
- So, dynamically adjust ovfl up to a multiple of 8. */
- t = build (BIT_AND_EXPR, TREE_TYPE (ovfl), ovfl,
- build_int_2 (7, 0));
- t = build (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, t);
- t = build (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
- }
+void
+mips_expand_prologue (void)
+{
+ const struct mips_frame_info *frame;
+ HOST_WIDE_INT size;
+ unsigned int nargs;
+ rtx insn;
- /* Emit code for addr_rtx = the ovfl pointer into overflow area.
- Regardless of mips2, postincrement the ovfl pointer by 8. */
- t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
- size_int (8));
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- emit_queue();
- emit_label (lab_over);
- return addr_rtx;
- }
+ if (cfun->machine->global_pointer != INVALID_REGNUM)
+ SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
+
+ frame = &cfun->machine->frame;
+ size = frame->total_size;
+
+ /* Save the registers. Allocate up to MIPS_MAX_FIRST_STACK_STEP
+ bytes beforehand; this is enough to cover the register save area
+ without going out of range. */
+ if ((frame->mask | frame->fmask) != 0)
+ {
+ HOST_WIDE_INT step1;
+
+ step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
+ if (GENERATE_MIPS16E_SAVE_RESTORE)
+ {
+ HOST_WIDE_INT offset;
+ unsigned int mask, regno;
+
+ /* Try to merge argument stores into the save instruction. */
+ nargs = mips16e_collect_argument_saves ();
+
+ /* Build the save instruction. */
+ mask = frame->mask;
+ insn = mips16e_build_save_restore (false, &mask, &offset,
+ nargs, step1);
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+
+ /* Check if we need to save other registers. */
+ for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
+ {
+ offset -= UNITS_PER_WORD;
+ mips_save_restore_reg (word_mode, regno,
+ offset, mips_save_reg);
+ }
+ }
else
- {
- /* not REAL_TYPE */
- int step_size;
+ {
+ insn = gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-step1));
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ size -= step1;
+ mips_for_each_saved_reg (size, mips_save_reg);
+ }
+ }
- if (! TARGET_64BIT
- && TREE_CODE (type) == INTEGER_TYPE
- && TYPE_PRECISION (type) == 64)
+ /* Allocate the rest of the frame. */
+ if (size > 0)
+ {
+ if (SMALL_OPERAND (-size))
+ RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (-size)))) = 1;
+ else
+ {
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
+ if (TARGET_MIPS16)
{
- /* In mips2, int takes 32 bits of the GPR save area, but
- longlong takes an aligned 64 bits. So, emit code
- to zero the low order bits of goff, thus aligning
- the later calculation of (gtop-goff) upwards. */
- t = build (BIT_AND_EXPR, TREE_TYPE (goff), goff,
- build_int_2 (-8, -1));
- t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ /* There are no instructions to add or subtract registers
+ from the stack pointer, so use the frame pointer as a
+ temporary. We should always be using a frame pointer
+ in this case anyway. */
+ gcc_assert (frame_pointer_needed);
+ 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)));
+ mips_emit_move (stack_pointer_rtx, hard_frame_pointer_rtx);
}
-
- /* Emit code to branch if goff == 0. */
- r = expand_expr (goff, NULL_RTX, TYPE_MODE (TREE_TYPE (goff)),
- EXPAND_NORMAL);
- emit_cmp_and_jump_insns (r, const0_rtx, EQ, const1_rtx, GET_MODE (r),
- 1, lab_false);
-
- /* Emit code for addr_rtx = gtop - goff. */
- t = build (MINUS_EXPR, TREE_TYPE (gtop), gtop, goff);
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- /* Note that mips2 int is 32 bit, but mips2 longlong is 64. */
- if (! TARGET_64BIT && TYPE_PRECISION (type) == 64)
- step_size = 8;
else
- step_size = UNITS_PER_WORD;
-
- /* Emit code for goff = goff - step_size.
- Advances the offset up GPR save area over the item. */
- t = build (MINUS_EXPR, TREE_TYPE (goff), goff,
- build_int_2 (step_size, 0));
- t = build (MODIFY_EXPR, TREE_TYPE (goff), goff, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
- emit_queue();
- emit_jump (lab_over);
- emit_barrier ();
- emit_label (lab_false);
-
- /* Emit code for addr_rtx -> overflow area, postinc by step_size */
- t = build (POSTINCREMENT_EXPR, TREE_TYPE(ovfl), ovfl,
- size_int (step_size));
- r = expand_expr (t, addr_rtx, Pmode, EXPAND_NORMAL);
- if (r != addr_rtx)
- emit_move_insn (addr_rtx, r);
-
- emit_queue();
- emit_label (lab_over);
-
- if (BYTES_BIG_ENDIAN && rsize != size)
- addr_rtx = plus_constant (addr_rtx, rsize - size);
-
- if (indirect)
- {
- addr_rtx = force_reg (Pmode, addr_rtx);
- r = gen_rtx_MEM (Pmode, addr_rtx);
- set_mem_alias_set (r, get_varargs_alias_set ());
- emit_move_insn (addr_rtx, r);
- }
+ emit_insn (gen_sub3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ MIPS_PROLOGUE_TEMP (Pmode)));
- return addr_rtx;
+ /* Describe the combined effect of the previous instructions. */
+ mips_set_frame_expr
+ (gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+ plus_constant (stack_pointer_rtx, -size)));
}
}
- else
- {
- /* Not EABI. */
- int align;
- /* ??? The original va-mips.h did always align, despite the fact
- that alignments <= UNITS_PER_WORD are preserved by the va_arg
- increment mechanism. */
+ /* Set up the frame pointer, if we're using one. */
+ if (frame_pointer_needed)
+ {
+ HOST_WIDE_INT offset;
- if (TARGET_64BIT)
- align = 8;
- else if (TYPE_ALIGN (type) > 32)
- align = 8;
+ offset = frame->hard_frame_pointer_offset;
+ if (offset == 0)
+ {
+ insn = mips_emit_move (hard_frame_pointer_rtx, stack_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ else if (SMALL_OPERAND (offset))
+ {
+ insn = gen_add3_insn (hard_frame_pointer_rtx,
+ stack_pointer_rtx, GEN_INT (offset));
+ RTX_FRAME_RELATED_P (emit_insn (insn)) = 1;
+ }
else
- align = 4;
+ {
+ mips_emit_move (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (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)));
+ mips_set_frame_expr
+ (gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
+ plus_constant (stack_pointer_rtx, offset)));
+ }
+ }
- t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
- build_int_2 (align - 1, 0));
- t = build (BIT_AND_EXPR, TREE_TYPE (t), t, build_int_2 (-align, -1));
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
- expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+ mips_emit_loadgp ();
- /* Everything past the alignment is standard. */
- return std_expand_builtin_va_arg (valist, type);
+ /* Initialize the $gp save slot. */
+ if (frame->cprestore_size > 0
+ && cfun->machine->global_pointer != INVALID_REGNUM)
+ {
+ if (TARGET_MIPS16)
+ mips_emit_move (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+ MIPS16_PIC_TEMP);
+ else if (TARGET_ABICALLS_PIC2)
+ emit_insn (gen_cprestore (GEN_INT (frame->args_size)));
+ else
+ emit_move_insn (mips_cprestore_slot (MIPS_PROLOGUE_TEMP (Pmode)),
+ pic_offset_table_rtx);
}
+
+ /* If we are profiling, make sure no instructions are scheduled before
+ the call to mcount. */
+ if (crtl->profile)
+ emit_insn (gen_blockage ());
}
\f
-/* Abort after printing out a specific insn. */
+/* Emit instructions to restore register REG from slot MEM. */
static void
-abort_with_insn (insn, reason)
- rtx insn;
- const char *reason;
+mips_restore_reg (rtx reg, rtx mem)
+{
+ /* There's no MIPS16 instruction to load $31 directly. Load into
+ $7 instead and adjust the return insn appropriately. */
+ if (TARGET_MIPS16 && REGNO (reg) == GP_REG_FIRST + 31)
+ reg = gen_rtx_REG (GET_MODE (reg), GP_REG_FIRST + 7);
+
+ if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
+ {
+ /* Can't restore directly; move through a temporary. */
+ mips_emit_move (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
+ mips_emit_move (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
+ }
+ else
+ mips_emit_move (reg, mem);
+}
+
+/* Emit any instructions needed before a return. */
+
+void
+mips_expand_before_return (void)
{
- error (reason);
- debug_rtx (insn);
- abort ();
+ /* When using a call-clobbered gp, we start out with unified call
+ insns that include instructions to restore the gp. We then split
+ these unified calls after reload. These split calls explicitly
+ clobber gp, so there is no need to define
+ PIC_OFFSET_TABLE_REG_CALL_CLOBBERED.
+
+ For consistency, we should also insert an explicit clobber of $28
+ before return insns, so that the post-reload optimizers know that
+ the register is not live on exit. */
+ if (TARGET_CALL_CLOBBERED_GP)
+ emit_clobber (pic_offset_table_rtx);
}
-\f
-/* Set up the threshold for data to go into the small data area, instead
- of the normal data area, and detect any conflicts in the switches. */
-void
-override_options ()
-{
- register int i, start;
- register int regno;
- register enum machine_mode mode;
- register enum processor_type mips_cpu;
-
- mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
-
- if (mips_section_threshold <= 0)
- target_flags &= ~MASK_GPOPT;
- else if (optimize)
- target_flags |= MASK_GPOPT;
-
- /* If both single-float and soft-float are set, then clear the one that
- was set by TARGET_DEFAULT, leaving the one that was set by the
- user. We assume here that the specs prevent both being set by the
- user. */
-#ifdef TARGET_DEFAULT
- if (TARGET_SINGLE_FLOAT && TARGET_SOFT_FLOAT)
- target_flags &= ~((TARGET_DEFAULT) & (MASK_SOFT_FLOAT | MASK_SINGLE_FLOAT));
-#endif
+/* Expand an "epilogue" or "sibcall_epilogue" pattern; SIBCALL_P
+ says which. */
+
+void
+mips_expand_epilogue (bool sibcall_p)
+{
+ const struct mips_frame_info *frame;
+ HOST_WIDE_INT step1, step2;
+ rtx base, target;
+
+ if (!sibcall_p && mips_can_use_return_insn ())
+ {
+ emit_jump_insn (gen_return ());
+ return;
+ }
+
+ /* In MIPS16 mode, if the return value should go into a floating-point
+ register, we need to call a helper routine to copy it over. */
+ if (mips16_cfun_returns_in_fpr_p ())
+ mips16_copy_fpr_return_value ();
- /* Get the architectural level. */
- if (mips_isa_string == 0)
- mips_isa = MIPS_ISA_DEFAULT;
+ /* Split the frame into two. STEP1 is the amount of stack we should
+ deallocate before restoring the registers. STEP2 is the amount we
+ should deallocate afterwards.
- else if (mips_isa_string != 0
- && mips_arch_string != 0)
- warning ("The -march option is incompatible to -mipsN and therefore ignored.");
+ Start off by assuming that no registers need to be restored. */
+ frame = &cfun->machine->frame;
+ step1 = frame->total_size;
+ step2 = 0;
- else if (ISDIGIT (*mips_isa_string))
+ /* Work out which register holds the frame address. */
+ if (!frame_pointer_needed)
+ base = stack_pointer_rtx;
+ else
{
- mips_isa = atoi (mips_isa_string);
- if (mips_isa == 16)
- {
- /* -mno-mips16 overrides -mips16. */
- if (mips_no_mips16_string == NULL)
- {
- target_flags |= MASK_MIPS16;
- if (TARGET_64BIT)
- mips_isa = 3;
- else
- mips_isa = MIPS_ISA_DEFAULT;
- }
- else
- {
- mips_isa = MIPS_ISA_DEFAULT;
- }
- }
- else if (mips_isa < 1
- || (mips_isa > 4
- && mips_isa != 32
- && mips_isa != 64))
- {
- error ("-mips%d not supported", mips_isa);
- mips_isa = 1;
- }
+ base = hard_frame_pointer_rtx;
+ step1 -= frame->hard_frame_pointer_offset;
}
- else
+ /* If we need to restore registers, deallocate as much stack as
+ possible in the second step without going out of range. */
+ if ((frame->mask | frame->fmask) != 0)
{
- error ("bad value (%s) for -mips switch", mips_isa_string);
- mips_isa = 1;
- }
-
-#ifdef MIPS_ABI_DEFAULT
- /* Get the ABI to use. */
- if (mips_abi_string == (char *) 0)
- mips_abi = MIPS_ABI_DEFAULT;
- else if (! strcmp (mips_abi_string, "32"))
- mips_abi = ABI_32;
- else if (! strcmp (mips_abi_string, "o64"))
- mips_abi = ABI_O64;
- else if (! strcmp (mips_abi_string, "n32"))
- mips_abi = ABI_N32;
- else if (! strcmp (mips_abi_string, "64"))
- mips_abi = ABI_64;
- else if (! strcmp (mips_abi_string, "eabi"))
- mips_abi = ABI_EABI;
- else if (! strcmp (mips_abi_string, "meabi"))
- mips_abi = ABI_MEABI;
- else
- error ("bad value (%s) for -mabi= switch", mips_abi_string);
+ step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
+ step1 -= step2;
+ }
- /* A specified ISA defaults the ABI if it was not specified. */
- if (mips_abi_string == 0 && mips_isa_string
- && mips_abi != ABI_EABI
- && mips_abi != ABI_O64
- && mips_abi != ABI_MEABI)
+ /* Set TARGET to BASE + STEP1. */
+ target = base;
+ if (step1 > 0)
{
- if (mips_isa == 64)
- mips_abi = ABI_O64;
- else
+ rtx adjust;
+
+ /* Get an rtx for STEP1 that we can add to BASE. */
+ adjust = GEN_INT (step1);
+ if (!SMALL_OPERAND (step1))
{
- if (! ISA_HAS_64BIT_REGS)
- mips_abi = ABI_32;
- else if (mips_abi != ABI_N32)
- mips_abi = ABI_64;
+ mips_emit_move (MIPS_EPILOGUE_TEMP (Pmode), adjust);
+ adjust = MIPS_EPILOGUE_TEMP (Pmode);
}
- }
-#ifdef MIPS_CPU_STRING_DEFAULT
- /* A specified ABI defaults the ISA if it was not specified. */
- else if (mips_isa_string == 0 && mips_abi_string
- && mips_abi != ABI_EABI && mips_abi != ABI_O64)
- {
- if (mips_abi == ABI_32)
- mips_isa = 1;
- else if (mips_abi == ABI_N32)
- mips_isa = 3;
- else
- mips_isa = 4;
- }
-#endif
+ /* Normal mode code can copy the result straight into $sp. */
+ if (!TARGET_MIPS16)
+ target = stack_pointer_rtx;
- /* If both ABI and ISA were specified, check for conflicts. */
- else if (mips_isa_string && mips_abi_string)
- {
- if (! ISA_HAS_64BIT_REGS && (mips_abi == ABI_N32 || mips_abi == ABI_64
- || mips_abi == ABI_O64))
- error ("-mabi=%s does not support -mips%d", mips_abi_string, mips_isa);
+ emit_insn (gen_add3_insn (target, base, adjust));
}
- /* Override TARGET_DEFAULT if necessary. */
- if (mips_abi == ABI_32)
- target_flags &= ~ (MASK_FLOAT64|MASK_64BIT);
-
- /* If no type size setting options (-mlong64,-mint64,-mlong32) were used
- then set the type sizes. In the EABI in 64 bit mode, longs and
- pointers are 64 bits. Likewise for the SGI Irix6 N64 ABI. */
- if (mips_explicit_type_size_string == NULL
- && ((mips_abi == ABI_EABI && TARGET_64BIT)
- || mips_abi == ABI_64))
- target_flags |= MASK_LONG64;
+ /* Copy TARGET into the stack pointer. */
+ if (target != stack_pointer_rtx)
+ mips_emit_move (stack_pointer_rtx, target);
-#else
- if (mips_abi_string)
- error ("this target does not support the -mabi switch");
-#endif
+ /* If we're using addressing macros, $gp is implicitly used by all
+ SYMBOL_REFs. We must emit a blockage insn before restoring $gp
+ from the stack. */
+ if (TARGET_CALL_SAVED_GP && !TARGET_EXPLICIT_RELOCS)
+ emit_insn (gen_blockage ());
-#ifdef MIPS_CPU_STRING_DEFAULT
- /* ??? There is a minor inconsistency here. If the user specifies an ISA
- greater than that supported by the default processor, then the user gets
- an error. Normally, the compiler will just default to the base level cpu
- for the indicated isa. */
- if (mips_arch_string == 0)
- mips_arch_string = MIPS_CPU_STRING_DEFAULT;
- if (mips_tune_string == 0)
- mips_tune_string = MIPS_CPU_STRING_DEFAULT;
-#endif
+ if (GENERATE_MIPS16E_SAVE_RESTORE && frame->mask != 0)
+ {
+ unsigned int regno, mask;
+ HOST_WIDE_INT offset;
+ rtx restore;
- /* Identify the processor type. */
+ /* Generate the restore instruction. */
+ mask = frame->mask;
+ restore = mips16e_build_save_restore (true, &mask, &offset, 0, step2);
- if (mips_cpu_string != 0)
- {
- mips_cpu = mips_parse_cpu (mips_cpu_string);
- if (mips_cpu == PROCESSOR_DEFAULT)
- {
- error ("bad value (%s) for -mcpu= switch", mips_cpu_string);
- mips_cpu_string = "default";
- }
- mips_arch = mips_cpu;
- mips_tune = mips_cpu;
- }
+ /* Restore any other registers manually. */
+ for (regno = GP_REG_FIRST; regno < GP_REG_LAST; regno++)
+ if (BITSET_P (mask, regno - GP_REG_FIRST))
+ {
+ offset -= UNITS_PER_WORD;
+ mips_save_restore_reg (word_mode, regno, offset, mips_restore_reg);
+ }
- if (mips_arch_string == 0
- || ! strcmp (mips_arch_string, "default")
- || ! strcmp (mips_arch_string, "DEFAULT"))
- {
- switch (mips_isa)
- {
- default:
- mips_arch_string = "3000";
- mips_arch = PROCESSOR_R3000;
- break;
- case 2:
- mips_arch_string = "6000";
- mips_arch = PROCESSOR_R6000;
- break;
- case 3:
- mips_arch_string = "4000";
- mips_arch = PROCESSOR_R4000;
- break;
- case 4:
- mips_arch_string = "8000";
- mips_arch = PROCESSOR_R8000;
- break;
- case 32:
- mips_arch_string = "4kc";
- mips_arch = PROCESSOR_R4KC;
- break;
- case 64:
- mips_arch_string = "5kc";
- mips_arch = PROCESSOR_R5KC;
- break;
- }
+ /* Restore the remaining registers and deallocate the final bit
+ of the frame. */
+ emit_insn (restore);
}
else
{
- mips_arch = mips_parse_cpu (mips_arch_string);
- if (mips_arch == PROCESSOR_DEFAULT)
- {
- error ("bad value (%s) for -march= switch", mips_arch_string);
- mips_arch_string = "default";
- }
- }
- if (mips_tune_string == 0
- || ! strcmp (mips_tune_string, "default")
- || ! strcmp (mips_tune_string, "DEFAULT"))
- {
- if (mips_arch != PROCESSOR_DEFAULT)
- mips_tune = mips_arch;
- else
- switch (mips_isa)
- {
- default:
- mips_tune_string = "3000";
- mips_tune = PROCESSOR_R3000;
- break;
- case 2:
- mips_tune_string = "6000";
- mips_tune = PROCESSOR_R6000;
- break;
- case 3:
- mips_tune_string = "4000";
- mips_tune = PROCESSOR_R4000;
- break;
- case 4:
- mips_tune_string = "8000";
- mips_tune = PROCESSOR_R8000;
- break;
- case 32:
- mips_tune_string = "4kc";
- mips_tune = PROCESSOR_R4KC;
- break;
- case 64:
- mips_tune_string = "5kc";
- mips_tune = PROCESSOR_R5KC;
- break;
- }
+ /* Restore the registers. */
+ mips_for_each_saved_reg (frame->total_size - step2, mips_restore_reg);
+ /* Deallocate the final bit of the frame. */
+ if (step2 > 0)
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ GEN_INT (step2)));
}
- else
+
+ /* Add in the __builtin_eh_return stack adjustment. We need to
+ use a temporary in MIPS16 code. */
+ if (crtl->calls_eh_return)
{
- mips_tune = mips_parse_cpu (mips_tune_string);
- if (mips_tune == PROCESSOR_DEFAULT)
+ if (TARGET_MIPS16)
{
- error ("bad value (%s) for -mtune= switch", mips_tune_string);
- mips_tune_string = "default";
+ 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));
+ mips_emit_move (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
}
+ else
+ emit_insn (gen_add3_insn (stack_pointer_rtx,
+ stack_pointer_rtx,
+ EH_RETURN_STACKADJ_RTX));
}
- /* make sure sizes of ints/longs/etc. are ok */
- if (! ISA_HAS_64BIT_REGS)
+ if (!sibcall_p)
{
- if (TARGET_FLOAT64)
- {
- error ("-mips%d does not support 64 bit fp registers", mips_isa);
- target_flags &= ~ MASK_FLOAT64;
- }
+ unsigned int regno;
- else if (TARGET_64BIT)
- {
- error ("-mips%d does not support 64 bit gp registers", mips_isa);
- target_flags &= ~MASK_64BIT;
- }
+ /* When generating MIPS16 code, the normal mips_for_each_saved_reg
+ path will restore the return address into $7 rather than $31. */
+ if (TARGET_MIPS16
+ && !GENERATE_MIPS16E_SAVE_RESTORE
+ && BITSET_P (frame->mask, 31))
+ regno = GP_REG_FIRST + 7;
+ else
+ regno = GP_REG_FIRST + 31;
+ mips_expand_before_return ();
+ emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, regno)));
}
+}
+\f
+/* Return nonzero if this function is known to have a null epilogue.
+ This allows the optimizer to omit jumps to jumps if no stack
+ was created. */
- if (mips_abi != ABI_32 && mips_abi != ABI_O64)
- flag_pcc_struct_return = 0;
+bool
+mips_can_use_return_insn (void)
+{
+ if (!reload_completed)
+ return false;
- /* Tell halfpic.c that we have half-pic code if we do. */
- if (TARGET_HALF_PIC)
- HALF_PIC_INIT ();
+ if (crtl->profile)
+ return false;
- /* -fpic (-KPIC) is the default when TARGET_ABICALLS is defined. We need
- to set flag_pic so that the LEGITIMATE_PIC_OPERAND_P macro will work. */
- /* ??? -non_shared turns off pic code generation, but this is not
- implemented. */
- if (TARGET_ABICALLS)
- {
- mips_abicalls = MIPS_ABICALLS_YES;
- flag_pic = 1;
- if (mips_section_threshold > 0)
- warning ("-G is incompatible with PIC code which is the default");
- }
- else
- mips_abicalls = MIPS_ABICALLS_NO;
-
- /* -membedded-pic is a form of PIC code suitable for embedded
- systems. All calls are made using PC relative addressing, and
- all data is addressed using the $gp register. This requires gas,
- which does most of the work, and GNU ld, which automatically
- expands PC relative calls which are out of range into a longer
- instruction sequence. All gcc really does differently is
- generate a different sequence for a switch. */
- if (TARGET_EMBEDDED_PIC)
- {
- flag_pic = 1;
- if (TARGET_ABICALLS)
- warning ("-membedded-pic and -mabicalls are incompatible");
-
- if (g_switch_set)
- warning ("-G and -membedded-pic are incompatible");
-
- /* Setting mips_section_threshold is not required, because gas
- will force everything to be GP addressable anyhow, but
- setting it will cause gcc to make better estimates of the
- number of instructions required to access a particular data
- item. */
- mips_section_threshold = 0x7fffffff;
- }
-
- /* This optimization requires a linker that can support a R_MIPS_LO16
- relocation which is not immediately preceded by a R_MIPS_HI16 relocation.
- GNU ld has this support, but not all other MIPS linkers do, so we enable
- this optimization only if the user requests it, or if GNU ld is the
- standard linker for this configuration. */
- /* ??? This does not work when target addresses are DImode.
- This is because we are missing DImode high/lo_sum patterns. */
- if (TARGET_GAS && ! TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES && optimize && ! flag_pic
- && Pmode == SImode)
- mips_split_addresses = 1;
- else
- mips_split_addresses = 0;
+ /* In MIPS16 mode, a function that returns a floating-point value
+ needs to arrange to copy the return value into the floating-point
+ registers. */
+ if (mips16_cfun_returns_in_fpr_p ())
+ return false;
- /* -mrnames says to use the MIPS software convention for register
- names instead of the hardware names (ie, $a0 instead of $4).
- We do this by switching the names in mips_reg_names, which the
- reg_names points into via the REGISTER_NAMES macro. */
+ return cfun->machine->frame.total_size == 0;
+}
+\f
+/* Return true if register REGNO can store a value of mode MODE.
+ The result of this function is cached in mips_hard_regno_mode_ok. */
- if (TARGET_NAME_REGS)
- memcpy (mips_reg_names, mips_sw_reg_names, sizeof (mips_reg_names));
+static bool
+mips_hard_regno_mode_ok_p (unsigned int regno, enum machine_mode mode)
+{
+ unsigned int size;
+ enum mode_class mclass;
- /* When compiling for the mips16, we can not 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;
+ if (mode == CCV2mode)
+ return (ISA_HAS_8CC
+ && ST_REG_P (regno)
+ && (regno - ST_REG_FIRST) % 2 == 0);
- /* Don't run the scheduler before reload, since it tends to
- increase register pressure. */
- flag_schedule_insns = 0;
- }
+ if (mode == CCV4mode)
+ return (ISA_HAS_8CC
+ && ST_REG_P (regno)
+ && (regno - ST_REG_FIRST) % 4 == 0);
- /* We put -mentry in TARGET_OPTIONS rather than TARGET_SWITCHES only
- to avoid using up another bit in target_flags. */
- if (mips_entry_string != NULL)
+ if (mode == CCmode)
{
- if (*mips_entry_string != '\0')
- error ("invalid option `entry%s'", mips_entry_string);
+ if (!ISA_HAS_8CC)
+ return regno == FPSW_REGNUM;
- if (! TARGET_MIPS16)
- warning ("-mentry is only meaningful with -mips-16");
- else
- mips_entry = 1;
+ return (ST_REG_P (regno)
+ || GP_REG_P (regno)
+ || FP_REG_P (regno));
}
- /* We copy TARGET_MIPS16 into the mips16 global variable, so that
- attributes can access it. */
- if (TARGET_MIPS16)
- mips16 = 1;
- else
- mips16 = 0;
-
- /* Initialize the high and low values for legitimate floating point
- constants. Rather than trying to get the accuracy down to the
- last bit, just use approximate ranges. */
- dfhigh = REAL_VALUE_ATOF ("1.0e300", DFmode);
- dflow = REAL_VALUE_ATOF ("1.0e-300", DFmode);
- sfhigh = REAL_VALUE_ATOF ("1.0e38", SFmode);
- sflow = REAL_VALUE_ATOF ("1.0e-38", SFmode);
-
- mips_print_operand_punct['?'] = 1;
- mips_print_operand_punct['#'] = 1;
- mips_print_operand_punct['&'] = 1;
- mips_print_operand_punct['!'] = 1;
- mips_print_operand_punct['*'] = 1;
- mips_print_operand_punct['@'] = 1;
- mips_print_operand_punct['.'] = 1;
- mips_print_operand_punct['('] = 1;
- mips_print_operand_punct[')'] = 1;
- mips_print_operand_punct['['] = 1;
- mips_print_operand_punct[']'] = 1;
- mips_print_operand_punct['<'] = 1;
- mips_print_operand_punct['>'] = 1;
- mips_print_operand_punct['{'] = 1;
- mips_print_operand_punct['}'] = 1;
- mips_print_operand_punct['^'] = 1;
- mips_print_operand_punct['$'] = 1;
- mips_print_operand_punct['+'] = 1;
- mips_print_operand_punct['~'] = 1;
-
- mips_char_to_class['d'] = TARGET_MIPS16 ? M16_REGS : GR_REGS;
- mips_char_to_class['e'] = M16_NA_REGS;
- mips_char_to_class['t'] = T_REG;
- mips_char_to_class['f'] = (TARGET_HARD_FLOAT ? FP_REGS : NO_REGS);
- mips_char_to_class['h'] = HI_REG;
- mips_char_to_class['l'] = LO_REG;
- mips_char_to_class['a'] = HILO_REG;
- mips_char_to_class['x'] = MD_REGS;
- mips_char_to_class['b'] = ALL_REGS;
- mips_char_to_class['y'] = GR_REGS;
- mips_char_to_class['z'] = ST_REGS;
+ size = GET_MODE_SIZE (mode);
+ mclass = GET_MODE_CLASS (mode);
- /* Set up array to map GCC register number to debug register number.
- Ignore the special purpose register numbers. */
+ if (GP_REG_P (regno))
+ return ((regno - GP_REG_FIRST) & 1) == 0 || size <= UNITS_PER_WORD;
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- mips_dbx_regno[i] = -1;
+ if (FP_REG_P (regno)
+ && (((regno - FP_REG_FIRST) % MAX_FPRS_PER_FMT) == 0
+ || (MIN_FPRS_PER_FMT == 1 && size <= UNITS_PER_FPREG)))
+ {
+ /* Allow TFmode for CCmode reloads. */
+ if (mode == TFmode && ISA_HAS_8CC)
+ return true;
- start = GP_DBX_FIRST - GP_REG_FIRST;
- for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
- mips_dbx_regno[i] = i + start;
+ /* Allow 64-bit vector modes for Loongson-2E/2F. */
+ if (TARGET_LOONGSON_VECTORS
+ && (mode == V2SImode
+ || mode == V4HImode
+ || mode == V8QImode
+ || mode == DImode))
+ return true;
- start = FP_DBX_FIRST - FP_REG_FIRST;
- for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
- mips_dbx_regno[i] = i + start;
+ if (mclass == MODE_FLOAT
+ || mclass == MODE_COMPLEX_FLOAT
+ || mclass == MODE_VECTOR_FLOAT)
+ return size <= UNITS_PER_FPVALUE;
- /* Set up array giving whether a given register can hold a given mode.
- At present, restrict ints from being in FP registers, because reload
- is a little enthusiastic about storing extra values in FP registers,
- and this is not good for things like OS kernels. Also, due to the
- mandatory delay, it is as fast to load from cached memory as to move
- from the FP register. */
+ /* Allow integer modes that fit into a single register. We need
+ to put integers into FPRs when using instructions like CVT
+ and TRUNC. There's no point allowing sizes smaller than a word,
+ because the FPU has no appropriate load/store instructions. */
+ if (mclass == MODE_INT)
+ return size >= MIN_UNITS_PER_WORD && size <= UNITS_PER_FPREG;
+ }
- for (mode = VOIDmode;
- mode != MAX_MACHINE_MODE;
- mode = (enum machine_mode) ((int)mode + 1))
+ if (ACC_REG_P (regno)
+ && (INTEGRAL_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode)))
{
- register int size = GET_MODE_SIZE (mode);
- register enum mode_class class = GET_MODE_CLASS (mode);
-
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if (MD_REG_P (regno))
{
- register int temp;
-
- if (mode == CCmode)
- {
- if (! ISA_HAS_8CC)
- temp = (regno == FPSW_REGNUM);
- else
- temp = (ST_REG_P (regno) || GP_REG_P (regno)
- || FP_REG_P (regno));
- }
-
- else if (GP_REG_P (regno))
- temp = ((regno & 1) == 0 || size <= UNITS_PER_WORD);
-
- else if (FP_REG_P (regno))
- temp = ((TARGET_FLOAT64 || ((regno & 1) == 0)
- /* I think this change is OK regardless of abi, but
- I'm being cautions untill I can test this more.
- HARD_REGNO_MODE_OK is about whether or not you
- can move to and from a register without changing
- the value, not about whether math works on the
- register. */
- || (mips_abi == ABI_MEABI && size <= 4))
- && (class == MODE_FLOAT
- || class == MODE_COMPLEX_FLOAT
- || (TARGET_DEBUG_H_MODE && class == MODE_INT))
- && (! TARGET_SINGLE_FLOAT || size <= 4));
-
- else if (MD_REG_P (regno))
- temp = (class == MODE_INT
- && (size <= UNITS_PER_WORD
- || (regno == MD_REG_FIRST
- && size == 2 * UNITS_PER_WORD)));
+ /* After a multiplication or division, clobbering HI makes
+ the value of LO unpredictable, and vice versa. This means
+ that, for all interesting cases, HI and LO are effectively
+ a single register.
- else
- temp = 0;
+ We model this by requiring that any value that uses HI
+ also uses LO. */
+ if (size <= UNITS_PER_WORD * 2)
+ return regno == (size <= UNITS_PER_WORD ? LO_REGNUM : MD_REG_FIRST);
+ }
+ else
+ {
+ /* DSP accumulators do not have the same restrictions as
+ HI and LO, so we can treat them as normal doubleword
+ registers. */
+ if (size <= UNITS_PER_WORD)
+ return true;
- mips_hard_regno_mode_ok[(int)mode][regno] = temp;
+ if (size <= UNITS_PER_WORD * 2
+ && ((regno - DSP_ACC_REG_FIRST) & 1) == 0)
+ return true;
}
}
- /* Save GPR registers in word_mode sized hunks. word_mode hasn't been
- initialized yet, so we can't use that here. */
- gpr_mode = TARGET_64BIT ? DImode : SImode;
+ if (ALL_COP_REG_P (regno))
+ return mclass == MODE_INT && size <= UNITS_PER_WORD;
- /* 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;
- }
+ if (regno == GOT_VERSION_REGNUM)
+ return mode == SImode;
- /* Register global variables with the garbage collector. */
- mips_add_gc_roots ();
+ return false;
}
-/* On the mips16, we want to allocate $24 (T_REG) before other
- registers for instructions for which it is possible. This helps
- avoid shuffling registers around in order to set up for an xor,
- encouraging the compiler to use a cmp instead. */
+/* Implement HARD_REGNO_NREGS. */
-void
-mips_order_regs_for_local_alloc ()
+unsigned int
+mips_hard_regno_nregs (int regno, enum machine_mode mode)
{
- register int i;
+ if (ST_REG_P (regno))
+ /* The size of FP status registers is always 4, because they only hold
+ CCmode values, and CCmode is always considered to be 4 bytes wide. */
+ return (GET_MODE_SIZE (mode) + 3) / 4;
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- reg_alloc_order[i] = i;
+ if (FP_REG_P (regno))
+ return (GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG;
- if (TARGET_MIPS16)
+ /* All other registers are word-sized. */
+ return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
+
+/* Implement CLASS_MAX_NREGS, taking the maximum of the cases
+ in mips_hard_regno_nregs. */
+
+int
+mips_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
+{
+ int size;
+ HARD_REG_SET left;
+
+ size = 0x8000;
+ COPY_HARD_REG_SET (left, reg_class_contents[(int) rclass]);
+ if (hard_reg_set_intersect_p (left, reg_class_contents[(int) ST_REGS]))
{
- /* It really doesn't matter where we put register 0, since it is
- a fixed register anyhow. */
- reg_alloc_order[0] = 24;
- reg_alloc_order[24] = 0;
+ size = MIN (size, 4);
+ AND_COMPL_HARD_REG_SET (left, reg_class_contents[(int) ST_REGS]);
+ }
+ if (hard_reg_set_intersect_p (left, reg_class_contents[(int) FP_REGS]))
+ {
+ size = MIN (size, UNITS_PER_FPREG);
+ AND_COMPL_HARD_REG_SET (left, reg_class_contents[(int) FP_REGS]);
}
+ if (!hard_reg_set_empty_p (left))
+ size = MIN (size, UNITS_PER_WORD);
+ return (GET_MODE_SIZE (mode) + size - 1) / size;
}
-\f
-/* The MIPS debug format wants all automatic variables and arguments
- to be in terms of the virtual frame pointer (stack pointer before
- any adjustment in the function), while the MIPS 3.0 linker wants
- the frame pointer to be the stack pointer after the initial
- adjustment. So, we do the adjustment here. The arg pointer (which
- is eliminated) points to the virtual frame pointer, while the frame
- pointer (which may be eliminated) points to the stack pointer after
- the initial adjustments. */
+/* Implement CANNOT_CHANGE_MODE_CLASS. */
-HOST_WIDE_INT
-mips_debugger_offset (addr, offset)
- rtx addr;
- HOST_WIDE_INT offset;
+bool
+mips_cannot_change_mode_class (enum machine_mode from ATTRIBUTE_UNUSED,
+ enum machine_mode to ATTRIBUTE_UNUSED,
+ enum reg_class rclass)
{
- rtx offset2 = const0_rtx;
- rtx reg = eliminate_constant_term (addr, &offset2);
-
- if (offset == 0)
- offset = INTVAL (offset2);
+ /* There are several problems with changing the modes of values
+ in floating-point registers:
- if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
- || reg == hard_frame_pointer_rtx)
- {
- HOST_WIDE_INT frame_size = (!current_frame_info.initialized)
- ? compute_frame_size (get_frame_size ())
- : current_frame_info.total_size;
+ - When a multi-word value is stored in paired floating-point
+ registers, the first register always holds the low word.
+ We therefore can't allow FPRs to change between single-word
+ and multi-word modes on big-endian targets.
- /* MIPS16 frame is smaller */
- if (frame_pointer_needed && TARGET_MIPS16)
- frame_size -= current_function_outgoing_args_size;
+ - GCC assumes that each word of a multiword register can be accessed
+ individually using SUBREGs. This is not true for floating-point
+ registers if they are bigger than a word.
- offset = offset - frame_size;
- }
+ - Loading a 32-bit value into a 64-bit floating-point register
+ will not sign-extend the value, despite what LOAD_EXTEND_OP says.
+ We can't allow FPRs to change from SImode to to a wider mode on
+ 64-bit targets.
- /* sdbout_parms does not want this to crash for unrecognized cases. */
-#if 0
- else if (reg != arg_pointer_rtx)
- abort_with_insn (addr, "mips_debugger_offset called with non stack/frame/arg pointer");
-#endif
+ - If the FPU has already interpreted a value in one format, we must
+ not ask it to treat the value as having a different format.
- return offset;
+ We therefore disallow all mode changes involving FPRs. */
+ return reg_classes_intersect_p (FP_REGS, rclass);
}
-\f
-/* A C compound statement to output to stdio stream STREAM the
- assembler syntax for an instruction operand X. X is an RTL
- expression.
-
- CODE is a value that can be used to specify one of several ways
- of printing the operand. It is used when identical operands
- must be printed differently depending on the context. CODE
- comes from the `%' specification that was used to request
- printing of the operand. If the specification was just `%DIGIT'
- then CODE is 0; if the specification was `%LTR DIGIT' then CODE
- is the ASCII code for LTR.
-
- If X is a register, this macro should print the register's name.
- The names can be found in an array `reg_names' whose type is
- `char *[]'. `reg_names' is initialized from `REGISTER_NAMES'.
-
- When the machine description has a specification `%PUNCT' (a `%'
- followed by a punctuation character), this macro is called with
- a null pointer for X and the punctuation character for CODE.
-
- The MIPS specific codes are:
-
- 'X' X is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
- 'x' X is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
- 'd' output integer constant in decimal,
- 'z' if the operand is 0, use $0 instead of normal operand.
- 'D' print second part of double-word register or memory operand.
- 'L' print low-order register of double-word register operand.
- 'M' print high-order register of double-word register operand.
- 'C' print part of opcode for a branch condition.
- 'F' print part of opcode for a floating-point branch condition.
- 'N' print part of opcode for a branch condition, inverted.
- 'W' print part of opcode for a floating-point branch condition, inverted.
- 'S' X is CODE_LABEL, print with prefix of "LS" (for embedded switch).
- 'B' print 'z' for EQ, 'n' for NE
- 'b' print 'n' for EQ, 'z' for NE
- 'T' print 'f' for EQ, 't' for NE
- 't' print 't' for EQ, 'f' for NE
- 'Z' print register and a comma, but print nothing for $fcc0
- '(' Turn on .set noreorder
- ')' Turn on .set reorder
- '[' Turn on .set noat
- ']' Turn on .set at
- '<' Turn on .set nomacro
- '>' Turn on .set macro
- '{' Turn on .set volatile (not GAS)
- '}' Turn on .set novolatile (not GAS)
- '&' Turn on .set noreorder if filling delay slots
- '*' Turn on both .set noreorder and .set nomacro if filling delay slots
- '!' Turn on .set nomacro if filling delay slots
- '#' Print nop if in a .set noreorder section.
- '?' Print 'l' if we are to use a branch likely instead of normal branch.
- '@' Print the name of the assembler temporary register (at or $1).
- '.' Print the name of the register with a hard-wired zero (zero or $0).
- '^' Print the name of the pic call-through register (t9 or $25).
- '$' Print the name of the stack pointer register (sp or $29).
- '+' Print the name of the gp register (gp or $28).
- '~' Output an branch alignment to LABEL_ALIGN(NULL). */
-void
-print_operand (file, op, letter)
- FILE *file; /* file to write to */
- rtx op; /* operand to print */
- int letter; /* %<letter> or 0 */
-{
- register enum rtx_code code;
+/* Return true if moves in mode MODE can use the FPU's mov.fmt instruction. */
- if (PRINT_OPERAND_PUNCT_VALID_P (letter))
+static bool
+mips_mode_ok_for_mov_fmt_p (enum machine_mode mode)
+{
+ switch (mode)
{
- switch (letter)
- {
- case '?':
- if (mips_branch_likely)
- putc ('l', file);
- break;
+ case SFmode:
+ return TARGET_HARD_FLOAT;
- case '@':
- fputs (reg_names [GP_REG_FIRST + 1], file);
- break;
+ case DFmode:
+ return TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT;
- case '^':
- fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file);
- break;
+ case V2SFmode:
+ return TARGET_HARD_FLOAT && TARGET_PAIRED_SINGLE_FLOAT;
- case '.':
- fputs (reg_names [GP_REG_FIRST + 0], file);
- break;
+ default:
+ return false;
+ }
+}
- case '$':
- fputs (reg_names[STACK_POINTER_REGNUM], file);
- break;
+/* Implement MODES_TIEABLE_P. */
- case '+':
- fputs (reg_names[GP_REG_FIRST + 28], file);
- break;
+bool
+mips_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+ /* FPRs allow no mode punning, so it's not worth tying modes if we'd
+ prefer to put one of them in FPRs. */
+ return (mode1 == mode2
+ || (!mips_mode_ok_for_mov_fmt_p (mode1)
+ && !mips_mode_ok_for_mov_fmt_p (mode2)));
+}
- case '&':
- if (final_sequence != 0 && set_noreorder++ == 0)
- fputs (".set\tnoreorder\n\t", file);
- break;
+/* Implement PREFERRED_RELOAD_CLASS. */
- case '*':
- if (final_sequence != 0)
- {
- if (set_noreorder++ == 0)
- fputs (".set\tnoreorder\n\t", file);
+enum reg_class
+mips_preferred_reload_class (rtx x, enum reg_class rclass)
+{
+ if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, rclass))
+ return LEA_REGS;
- if (set_nomacro++ == 0)
- fputs (".set\tnomacro\n\t", file);
- }
- break;
+ if (reg_class_subset_p (FP_REGS, rclass)
+ && mips_mode_ok_for_mov_fmt_p (GET_MODE (x)))
+ return FP_REGS;
- case '!':
- if (final_sequence != 0 && set_nomacro++ == 0)
- fputs ("\n\t.set\tnomacro", file);
- break;
+ if (reg_class_subset_p (GR_REGS, rclass))
+ rclass = GR_REGS;
- case '#':
- if (set_noreorder != 0)
- fputs ("\n\tnop", file);
- else if (TARGET_STATS)
- fputs ("\n\t#nop", file);
+ if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, rclass))
+ rclass = M16_REGS;
- break;
+ return rclass;
+}
- case '(':
- if (set_noreorder++ == 0)
- fputs (".set\tnoreorder\n\t", file);
- break;
+/* RCLASS is a class involved in a REGISTER_MOVE_COST calculation.
+ Return a "canonical" class to represent it in later calculations. */
- case ')':
- if (set_noreorder == 0)
- error ("internal error: %%) found without a %%( in assembler pattern");
+static enum reg_class
+mips_canonicalize_move_class (enum reg_class rclass)
+{
+ /* All moves involving accumulator registers have the same cost. */
+ if (reg_class_subset_p (rclass, ACC_REGS))
+ rclass = ACC_REGS;
- else if (--set_noreorder == 0)
- fputs ("\n\t.set\treorder", file);
+ /* Likewise promote subclasses of general registers to the most
+ interesting containing class. */
+ if (TARGET_MIPS16 && reg_class_subset_p (rclass, M16_REGS))
+ rclass = M16_REGS;
+ else if (reg_class_subset_p (rclass, GENERAL_REGS))
+ rclass = GENERAL_REGS;
- break;
+ return rclass;
+}
- case '[':
- if (set_noat++ == 0)
- fputs (".set\tnoat\n\t", file);
- break;
+/* Return the cost of moving a value of mode MODE from a register of
+ class FROM to a GPR. Return 0 for classes that are unions of other
+ classes handled by this function. */
- case ']':
- if (set_noat == 0)
- error ("internal error: %%] found without a %%[ in assembler pattern");
- else if (--set_noat == 0)
- fputs ("\n\t.set\tat", file);
+static int
+mips_move_to_gpr_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ enum reg_class from)
+{
+ switch (from)
+ {
+ case GENERAL_REGS:
+ /* A MIPS16 MOVE instruction, or a non-MIPS16 MOVE macro. */
+ return 2;
- break;
+ case ACC_REGS:
+ /* MFLO and MFHI. */
+ return 6;
- case '<':
- if (set_nomacro++ == 0)
- fputs (".set\tnomacro\n\t", file);
- break;
+ case FP_REGS:
+ /* MFC1, etc. */
+ return 4;
- case '>':
- if (set_nomacro == 0)
- error ("internal error: %%> found without a %%< in assembler pattern");
- else if (--set_nomacro == 0)
- fputs ("\n\t.set\tmacro", file);
+ case ST_REGS:
+ /* LUI followed by MOVF. */
+ return 4;
- break;
+ case COP0_REGS:
+ case COP2_REGS:
+ case COP3_REGS:
+ /* This choice of value is historical. */
+ return 5;
- case '{':
- if (set_volatile++ == 0)
- fprintf (file, "%s.set\tvolatile\n\t", TARGET_MIPS_AS ? "" : "#");
- break;
+ default:
+ return 0;
+ }
+}
- case '}':
- if (set_volatile == 0)
- error ("internal error: %%} found without a %%{ in assembler pattern");
- else if (--set_volatile == 0)
- fprintf (file, "\n\t%s.set\tnovolatile", (TARGET_MIPS_AS) ? "" : "#");
+/* Return the cost of moving a value of mode MODE from a GPR to a
+ register of class TO. Return 0 for classes that are unions of
+ other classes handled by this function. */
- break;
+static int
+mips_move_from_gpr_cost (enum machine_mode mode, enum reg_class to)
+{
+ switch (to)
+ {
+ case GENERAL_REGS:
+ /* A MIPS16 MOVE instruction, or a non-MIPS16 MOVE macro. */
+ return 2;
- case '~':
- {
- if (align_labels_log > 0)
- ASM_OUTPUT_ALIGN (file, align_labels_log);
- }
- break;
+ case ACC_REGS:
+ /* MTLO and MTHI. */
+ return 6;
- default:
- error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
- break;
- }
+ case FP_REGS:
+ /* MTC1, etc. */
+ return 4;
- return;
- }
+ case ST_REGS:
+ /* A secondary reload through an FPR scratch. */
+ return (mips_register_move_cost (mode, GENERAL_REGS, FP_REGS)
+ + mips_register_move_cost (mode, FP_REGS, ST_REGS));
- if (! op)
- {
- error ("PRINT_OPERAND null pointer");
- return;
- }
+ case COP0_REGS:
+ case COP2_REGS:
+ case COP3_REGS:
+ /* This choice of value is historical. */
+ return 5;
- code = GET_CODE (op);
+ default:
+ return 0;
+ }
+}
- if (code == SIGN_EXTEND)
- op = XEXP (op, 0), code = GET_CODE (op);
+/* Implement REGISTER_MOVE_COST. Return 0 for classes that are the
+ maximum of the move costs for subclasses; regclass will work out
+ the maximum for us. */
- if (letter == 'C')
- switch (code)
- {
- case EQ: fputs ("eq", file); break;
- case NE: fputs ("ne", file); break;
- case GT: fputs ("gt", file); break;
- case GE: fputs ("ge", file); break;
- case LT: fputs ("lt", file); break;
- case LE: fputs ("le", file); break;
- case GTU: fputs ("gtu", file); break;
- case GEU: fputs ("geu", file); break;
- case LTU: fputs ("ltu", file); break;
- case LEU: fputs ("leu", file); break;
- default:
- abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%C");
- }
+int
+mips_register_move_cost (enum machine_mode mode,
+ enum reg_class from, enum reg_class to)
+{
+ enum reg_class dregs;
+ int cost1, cost2;
- else if (letter == 'N')
- switch (code)
- {
- case EQ: fputs ("ne", file); break;
- case NE: fputs ("eq", file); break;
- case GT: fputs ("le", file); break;
- case GE: fputs ("lt", file); break;
- case LT: fputs ("ge", file); break;
- case LE: fputs ("gt", file); break;
- case GTU: fputs ("leu", file); break;
- case GEU: fputs ("ltu", file); break;
- case LTU: fputs ("geu", file); break;
- case LEU: fputs ("gtu", file); break;
- default:
- abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%N");
- }
+ from = mips_canonicalize_move_class (from);
+ to = mips_canonicalize_move_class (to);
- else if (letter == 'F')
- switch (code)
- {
- case EQ: fputs ("c1f", file); break;
- case NE: fputs ("c1t", file); break;
- default:
- abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%F");
- }
+ /* Handle moves that can be done without using general-purpose registers. */
+ if (from == FP_REGS)
+ {
+ if (to == FP_REGS && mips_mode_ok_for_mov_fmt_p (mode))
+ /* MOV.FMT. */
+ return 4;
+ if (to == ST_REGS)
+ /* The sequence generated by mips_expand_fcc_reload. */
+ return 8;
+ }
- else if (letter == 'W')
- switch (code)
- {
- case EQ: fputs ("c1t", file); break;
- case NE: fputs ("c1f", file); break;
- default:
- abort_with_insn (op, "PRINT_OPERAND, invalid insn for %%W");
- }
+ /* Handle cases in which only one class deviates from the ideal. */
+ dregs = TARGET_MIPS16 ? M16_REGS : GENERAL_REGS;
+ if (from == dregs)
+ return mips_move_from_gpr_cost (mode, to);
+ if (to == dregs)
+ return mips_move_to_gpr_cost (mode, from);
- else if (letter == 'S')
+ /* Handles cases that require a GPR temporary. */
+ cost1 = mips_move_to_gpr_cost (mode, from);
+ if (cost1 != 0)
{
- char buffer[100];
-
- ASM_GENERATE_INTERNAL_LABEL (buffer, "LS", CODE_LABEL_NUMBER (op));
- assemble_name (file, buffer);
+ cost2 = mips_move_from_gpr_cost (mode, to);
+ if (cost2 != 0)
+ return cost1 + cost2;
}
- else if (letter == 'Z')
- {
- register int regnum;
+ return 0;
+}
- if (code != REG)
- abort ();
+/* Implement TARGET_IRA_COVER_CLASSES. */
- regnum = REGNO (op);
- if (! ST_REG_P (regnum))
- abort ();
+static const enum reg_class *
+mips_ira_cover_classes (void)
+{
+ static const enum reg_class acc_classes[] = {
+ GR_AND_ACC_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
+ ST_REGS, LIM_REG_CLASSES
+ };
+ static const enum reg_class no_acc_classes[] = {
+ GR_REGS, FP_REGS, COP0_REGS, COP2_REGS, COP3_REGS,
+ ST_REGS, LIM_REG_CLASSES
+ };
- if (regnum != ST_REG_FIRST)
- fprintf (file, "%s,", reg_names[regnum]);
- }
+ /* Don't allow the register allocators to use LO and HI in MIPS16 mode,
+ which has no MTLO or MTHI instructions. Also, using GR_AND_ACC_REGS
+ as a cover class only works well when we keep per-register costs.
+ Using it when not optimizing can cause us to think accumulators
+ have the same cost as GPRs in cases where GPRs are actually much
+ cheaper. */
+ return TARGET_MIPS16 || !optimize ? no_acc_classes : acc_classes;
+}
- else if (code == REG || code == SUBREG)
- {
- register int regnum;
+/* Return the register class required for a secondary register when
+ copying between one of the registers in RCLASS and value X, which
+ has mode MODE. X is the source of the move if IN_P, otherwise it
+ is the destination. Return NO_REGS if no secondary register is
+ needed. */
- if (code == REG)
- regnum = REGNO (op);
- else
- regnum = true_regnum (op);
+enum reg_class
+mips_secondary_reload_class (enum reg_class rclass,
+ enum machine_mode mode, rtx x, bool in_p)
+{
+ int regno;
- if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
- || (letter == 'L' && WORDS_BIG_ENDIAN)
- || letter == 'D')
- regnum++;
+ /* If X is a constant that cannot be loaded into $25, it must be loaded
+ into some other GPR. No other register class allows a direct move. */
+ if (mips_dangerous_for_la25_p (x))
+ return reg_class_subset_p (rclass, LEA_REGS) ? NO_REGS : LEA_REGS;
+
+ regno = true_regnum (x);
+ if (TARGET_MIPS16)
+ {
+ /* In MIPS16 mode, every move must involve a member of M16_REGS. */
+ if (!reg_class_subset_p (rclass, M16_REGS) && !M16_REG_P (regno))
+ return M16_REGS;
- fprintf (file, "%s", reg_names[regnum]);
+ return NO_REGS;
}
- else if (code == MEM)
+ /* Copying from accumulator registers to anywhere other than a general
+ register requires a temporary general register. */
+ if (reg_class_subset_p (rclass, ACC_REGS))
+ return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+ if (ACC_REG_P (regno))
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
+
+ /* We can only copy a value to a condition code register from a
+ floating-point register, and even then we require a scratch
+ floating-point register. We can only copy a value out of a
+ condition-code register into a general register. */
+ if (reg_class_subset_p (rclass, ST_REGS))
{
- if (letter == 'D')
- output_address (plus_constant (XEXP (op, 0), 4));
- else
- output_address (XEXP (op, 0));
+ if (in_p)
+ return FP_REGS;
+ return GP_REG_P (regno) ? NO_REGS : GR_REGS;
}
-
- else if (code == CONST_DOUBLE
- && GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT)
+ if (ST_REG_P (regno))
{
- REAL_VALUE_TYPE d;
- char s[30];
-
- REAL_VALUE_FROM_CONST_DOUBLE (d, op);
- REAL_VALUE_TO_DECIMAL (d, "%.20e", s);
- fprintf (file, s);
+ if (!in_p)
+ return FP_REGS;
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
}
- else if (letter == 'x' && GET_CODE (op) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
+ if (reg_class_subset_p (rclass, FP_REGS))
+ {
+ if (MEM_P (x)
+ && (GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8))
+ /* In this case we can use lwc1, swc1, ldc1 or sdc1. We'll use
+ pairs of lwc1s and swc1s if ldc1 and sdc1 are not supported. */
+ return NO_REGS;
- else if (letter == 'X' && GET_CODE(op) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
+ if (GP_REG_P (regno) || x == CONST0_RTX (mode))
+ /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */
+ return NO_REGS;
- else if (letter == 'd' && GET_CODE(op) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
+ if (CONSTANT_P (x) && !targetm.cannot_force_const_mem (x))
+ /* We can force the constant to memory and use lwc1
+ and ldc1. As above, we will use pairs of lwc1s if
+ ldc1 is not supported. */
+ return NO_REGS;
- else if (letter == 'z' && GET_CODE (op) == CONST_INT && INTVAL (op) == 0)
- fputs (reg_names[GP_REG_FIRST], file);
+ if (FP_REG_P (regno) && mips_mode_ok_for_mov_fmt_p (mode))
+ /* In this case we can use mov.fmt. */
+ return NO_REGS;
- else if (letter == 'd' || letter == 'x' || letter == 'X')
- output_operand_lossage ("invalid use of %%d, %%x, or %%X");
+ /* Otherwise, we need to reload through an integer register. */
+ return GR_REGS;
+ }
+ if (FP_REG_P (regno))
+ return reg_class_subset_p (rclass, GR_REGS) ? NO_REGS : GR_REGS;
- else if (letter == 'B')
- fputs (code == EQ ? "z" : "n", file);
- else if (letter == 'b')
- fputs (code == EQ ? "n" : "z", file);
- else if (letter == 'T')
- fputs (code == EQ ? "f" : "t", file);
- else if (letter == 't')
- fputs (code == EQ ? "t" : "f", file);
+ return NO_REGS;
+}
- else if (code == CONST && GET_CODE (XEXP (op, 0)) == REG)
- {
- /* This case arises on the mips16; see mips16_gp_pseudo_reg. */
- print_operand (file, XEXP (op, 0), letter);
- }
+/* Implement TARGET_MODE_REP_EXTENDED. */
- else if (TARGET_MIPS16 && code == CONST && mips16_gp_offset_p (op))
- {
- fputs ("%gprel(", file);
- mips16_output_gp_offset (file, op);
- fputs (")", file);
- }
+static int
+mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
+{
+ /* On 64-bit targets, SImode register values are sign-extended to DImode. */
+ if (TARGET_64BIT && mode == SImode && mode_rep == DImode)
+ return SIGN_EXTEND;
- else
- output_addr_const (file, op);
+ return UNKNOWN;
}
\f
-/* A C compound statement to output to stdio stream STREAM the
- assembler syntax for an instruction operand that is a memory
- reference whose address is ADDR. ADDR is an RTL expression.
+/* Implement TARGET_VALID_POINTER_MODE. */
+
+static bool
+mips_valid_pointer_mode (enum machine_mode mode)
+{
+ return mode == SImode || (TARGET_64BIT && mode == DImode);
+}
- On some machines, the syntax for a symbolic address depends on
- the section that the address refers to. On these machines,
- define the macro `ENCODE_SECTION_INFO' to store the information
- into the `symbol_ref', and then check for it here. */
+/* Implement TARGET_VECTOR_MODE_SUPPORTED_P. */
-void
-print_operand_address (file, addr)
- FILE *file;
- rtx addr;
+static bool
+mips_vector_mode_supported_p (enum machine_mode mode)
{
- if (!addr)
- error ("PRINT_OPERAND_ADDRESS, null pointer");
+ switch (mode)
+ {
+ case V2SFmode:
+ return TARGET_PAIRED_SINGLE_FLOAT;
- else
- switch (GET_CODE (addr))
- {
- case REG:
- if (! TARGET_MIPS16 && REGNO (addr) == ARG_POINTER_REGNUM)
- abort_with_insn (addr, "arg pointer not eliminated");
+ case V2HImode:
+ case V4QImode:
+ case V2HQmode:
+ case V2UHQmode:
+ case V2HAmode:
+ case V2UHAmode:
+ case V4QQmode:
+ case V4UQQmode:
+ return TARGET_DSP;
- fprintf (file, "0(%s)", reg_names [REGNO (addr)]);
- break;
+ case V2SImode:
+ case V4HImode:
+ case V8QImode:
+ return TARGET_LOONGSON_VECTORS;
- case LO_SUM:
- {
- register rtx arg0 = XEXP (addr, 0);
- register rtx arg1 = XEXP (addr, 1);
+ default:
+ return false;
+ }
+}
- if (! mips_split_addresses)
- abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, Spurious LO_SUM");
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P. */
- if (GET_CODE (arg0) != REG)
- abort_with_insn (addr,
- "PRINT_OPERAND_ADDRESS, LO_SUM with #1 not REG");
+static bool
+mips_scalar_mode_supported_p (enum machine_mode mode)
+{
+ if (ALL_FIXED_POINT_MODE_P (mode)
+ && GET_MODE_PRECISION (mode) <= 2 * BITS_PER_WORD)
+ return true;
- fprintf (file, "%%lo(");
- print_operand_address (file, arg1);
- fprintf (file, ")(%s)", reg_names [REGNO (arg0)]);
- }
- break;
+ return default_scalar_mode_supported_p (mode);
+}
+\f
+/* Implement TARGET_INIT_LIBFUNCS. */
- case PLUS:
- {
- register rtx reg = 0;
- register rtx offset = 0;
- register rtx arg0 = XEXP (addr, 0);
- register rtx arg1 = XEXP (addr, 1);
+#include "config/gofast.h"
- if (GET_CODE (arg0) == REG)
- {
- reg = arg0;
- offset = arg1;
- if (GET_CODE (offset) == REG)
- abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, 2 regs");
- }
+static void
+mips_init_libfuncs (void)
+{
+ if (TARGET_FIX_VR4120)
+ {
+ /* Register the special divsi3 and modsi3 functions needed to work
+ around VR4120 division errata. */
+ set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
+ set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
+ }
+
+ if (TARGET_MIPS16 && TARGET_HARD_FLOAT_ABI)
+ {
+ /* Register the MIPS16 -mhard-float stubs. */
+ set_optab_libfunc (add_optab, SFmode, "__mips16_addsf3");
+ set_optab_libfunc (sub_optab, SFmode, "__mips16_subsf3");
+ set_optab_libfunc (smul_optab, SFmode, "__mips16_mulsf3");
+ set_optab_libfunc (sdiv_optab, SFmode, "__mips16_divsf3");
+
+ set_optab_libfunc (eq_optab, SFmode, "__mips16_eqsf2");
+ set_optab_libfunc (ne_optab, SFmode, "__mips16_nesf2");
+ set_optab_libfunc (gt_optab, SFmode, "__mips16_gtsf2");
+ set_optab_libfunc (ge_optab, SFmode, "__mips16_gesf2");
+ set_optab_libfunc (lt_optab, SFmode, "__mips16_ltsf2");
+ set_optab_libfunc (le_optab, SFmode, "__mips16_lesf2");
+ set_optab_libfunc (unord_optab, SFmode, "__mips16_unordsf2");
+
+ set_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi");
+ set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf");
+ set_conv_libfunc (ufloat_optab, SFmode, SImode, "__mips16_floatunsisf");
+
+ if (TARGET_DOUBLE_FLOAT)
+ {
+ set_optab_libfunc (add_optab, DFmode, "__mips16_adddf3");
+ set_optab_libfunc (sub_optab, DFmode, "__mips16_subdf3");
+ set_optab_libfunc (smul_optab, DFmode, "__mips16_muldf3");
+ set_optab_libfunc (sdiv_optab, DFmode, "__mips16_divdf3");
+
+ set_optab_libfunc (eq_optab, DFmode, "__mips16_eqdf2");
+ set_optab_libfunc (ne_optab, DFmode, "__mips16_nedf2");
+ set_optab_libfunc (gt_optab, DFmode, "__mips16_gtdf2");
+ set_optab_libfunc (ge_optab, DFmode, "__mips16_gedf2");
+ set_optab_libfunc (lt_optab, DFmode, "__mips16_ltdf2");
+ set_optab_libfunc (le_optab, DFmode, "__mips16_ledf2");
+ set_optab_libfunc (unord_optab, DFmode, "__mips16_unorddf2");
+
+ set_conv_libfunc (sext_optab, DFmode, SFmode,
+ "__mips16_extendsfdf2");
+ set_conv_libfunc (trunc_optab, SFmode, DFmode,
+ "__mips16_truncdfsf2");
+ set_conv_libfunc (sfix_optab, SImode, DFmode,
+ "__mips16_fix_truncdfsi");
+ set_conv_libfunc (sfloat_optab, DFmode, SImode,
+ "__mips16_floatsidf");
+ set_conv_libfunc (ufloat_optab, DFmode, SImode,
+ "__mips16_floatunsidf");
+ }
+ }
+ else
+ /* Register the gofast functions if selected using --enable-gofast. */
+ gofast_maybe_init_libfuncs ();
- else if (GET_CODE (arg1) == REG)
- reg = arg1, offset = arg0;
- else if (CONSTANT_P (arg0) && CONSTANT_P (arg1))
- {
- output_addr_const (file, addr);
- break;
- }
- else
- abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, no regs");
+ /* The MIPS16 ISA does not have an encoding for "sync", so we rely
+ on an external non-MIPS16 routine to implement __sync_synchronize. */
+ if (TARGET_MIPS16)
+ synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
+}
- if (! CONSTANT_P (offset))
- abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #2");
+/* Return the length of INSN. LENGTH is the initial length computed by
+ attributes in the machine-description file. */
- if (REGNO (reg) == ARG_POINTER_REGNUM)
- abort_with_insn (addr, "arg pointer not eliminated");
+int
+mips_adjust_insn_length (rtx insn, int length)
+{
+ /* A unconditional jump has an unfilled delay slot if it is not part
+ of a sequence. A conditional jump normally has a delay slot, but
+ does not on MIPS16. */
+ if (CALL_P (insn) || (TARGET_MIPS16 ? simplejump_p (insn) : JUMP_P (insn)))
+ length += 4;
- if (TARGET_MIPS16
- && GET_CODE (offset) == CONST
- && mips16_gp_offset_p (offset))
- {
- fputs ("%gprel(", file);
- mips16_output_gp_offset (file, offset);
- fputs (")", file);
- }
- else
- output_addr_const (file, offset);
- fprintf (file, "(%s)", reg_names [REGNO (reg)]);
- }
+ /* See how many nops might be needed to avoid hardware hazards. */
+ if (!cfun->machine->ignore_hazard_length_p && INSN_CODE (insn) >= 0)
+ switch (get_attr_hazard (insn))
+ {
+ case HAZARD_NONE:
break;
- case LABEL_REF:
- case SYMBOL_REF:
- case CONST_INT:
- case CONST:
- output_addr_const (file, addr);
+ case HAZARD_DELAY:
+ length += 4;
break;
- default:
- abort_with_insn (addr, "PRINT_OPERAND_ADDRESS, invalid insn #1");
+ case HAZARD_HILO:
+ length += 8;
break;
- }
+ }
+
+ /* In order to make it easier to share MIPS16 and non-MIPS16 patterns,
+ the .md file length attributes are 4-based for both modes.
+ Adjust the MIPS16 ones here. */
+ if (TARGET_MIPS16)
+ length /= 2;
+
+ return length;
}
-\f
-/* Target hook for assembling integer objects. It appears that the Irix
- 6 assembler can't handle 64-bit decimal integers, so avoid printing
- such an integer here. */
-static bool
-mips_assemble_integer (x, size, aligned_p)
- rtx x;
- unsigned int size;
- int aligned_p;
+/* Return an asm sequence to start a noat block and load the address
+ of a label into $1. */
+
+const char *
+mips_output_load_label (void)
{
- if ((TARGET_64BIT || TARGET_GAS) && size == 8 && aligned_p)
+ if (TARGET_EXPLICIT_RELOCS)
+ switch (mips_abi)
+ {
+ case ABI_N32:
+ return "%[lw\t%@,%%got_page(%0)(%+)\n\taddiu\t%@,%@,%%got_ofst(%0)";
+
+ case ABI_64:
+ return "%[ld\t%@,%%got_page(%0)(%+)\n\tdaddiu\t%@,%@,%%got_ofst(%0)";
+
+ default:
+ if (ISA_HAS_LOAD_DELAY)
+ return "%[lw\t%@,%%got(%0)(%+)%#\n\taddiu\t%@,%@,%%lo(%0)";
+ return "%[lw\t%@,%%got(%0)(%+)\n\taddiu\t%@,%@,%%lo(%0)";
+ }
+ else
{
- fputs ("\t.dword\t", asm_out_file);
- if (HOST_BITS_PER_WIDE_INT < 64 || GET_CODE (x) != CONST_INT)
- output_addr_const (asm_out_file, x);
+ if (Pmode == DImode)
+ return "%[dla\t%@,%0";
else
- print_operand (asm_out_file, x, 'X');
- fputc ('\n', asm_out_file);
- return true;
+ return "%[la\t%@,%0";
}
- return default_assemble_integer (x, size, aligned_p);
}
-\f
-/* If optimizing for the global pointer, keep track of all of the externs, so
- that at the end of the file, we can emit the appropriate .extern
- declaration for them, before writing out the text section. We assume all
- names passed to us are in the permanent obstack, so they will be valid at
- the end of the compilation.
-
- If we have -G 0, or the extern size is unknown, or the object is in a user
- specified section that is not .sbss/.sdata, don't bother emitting the
- .externs. In the case of user specified sections this behaviour is
- required as otherwise GAS will think the object lives in .sbss/.sdata. */
-
-int
-mips_output_external (file, decl, name)
- FILE *file ATTRIBUTE_UNUSED;
- tree decl;
- const char *name;
-{
- register struct extern_list *p;
- int len;
- tree section_name;
-
- if (TARGET_GP_OPT
- && TREE_CODE (decl) != FUNCTION_DECL
- && !DECL_COMDAT (decl)
- && (len = int_size_in_bytes (TREE_TYPE (decl))) > 0
- && ((section_name = DECL_SECTION_NAME (decl)) == NULL
- || strcmp (TREE_STRING_POINTER (section_name), ".sbss") == 0
- || strcmp (TREE_STRING_POINTER (section_name), ".sdata") == 0))
- {
- p = (struct extern_list *) permalloc (sizeof (struct extern_list));
- p->next = extern_head;
- p->name = name;
- p->size = len;
- extern_head = p;
- }
-
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
- if (TREE_CODE (decl) == FUNCTION_DECL
- /* ??? Don't include alloca, since gcc will always expand it
- inline. If we don't do this, the C++ library fails to build. */
- && strcmp (name, "alloca")
- /* ??? Don't include __builtin_next_arg, because then gcc will not
- bootstrap under Irix 5.1. */
- && strcmp (name, "__builtin_next_arg"))
- {
- p = (struct extern_list *) permalloc (sizeof (struct extern_list));
- p->next = extern_head;
- p->name = name;
- p->size = -1;
- extern_head = p;
- }
-#endif
- return 0;
-}
+/* Return the assembly code for INSN, which has the operands given by
+ OPERANDS, and which branches to OPERANDS[1] if some condition is true.
+ BRANCH_IF_TRUE is the asm template that should be used if OPERANDS[1]
+ is in range of a direct branch. BRANCH_IF_FALSE is an inverted
+ version of BRANCH_IF_TRUE. */
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
-int
-mips_output_external_libcall (file, name)
- FILE *file ATTRIBUTE_UNUSED;
- const char *name;
+const char *
+mips_output_conditional_branch (rtx insn, rtx *operands,
+ const char *branch_if_true,
+ const char *branch_if_false)
{
- register struct extern_list *p;
+ unsigned int length;
+ rtx taken, not_taken;
- p = (struct extern_list *) permalloc (sizeof (struct extern_list));
- p->next = extern_head;
- p->name = name;
- p->size = -1;
- extern_head = p;
+ gcc_assert (LABEL_P (operands[1]));
- return 0;
-}
-#endif
-\f
-/* Emit a new filename to a stream. If this is MIPS ECOFF, watch out
- for .file's that start within a function. If we are smuggling stabs, try to
- put out a MIPS ECOFF file and a stab. */
+ length = get_attr_length (insn);
+ if (length <= 8)
+ {
+ /* Just a simple conditional branch. */
+ mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
+ return branch_if_true;
+ }
-void
-mips_output_filename (stream, name)
- FILE *stream;
- const char *name;
-{
- static int first_time = 1;
- char ltext_label_name[100];
+ /* Generate a reversed branch around a direct jump. This fallback does
+ not use branch-likely instructions. */
+ mips_branch_likely = false;
+ not_taken = gen_label_rtx ();
+ taken = operands[1];
- /* If we are emitting DWARF-2, let dwarf2out handle the ".file"
- directives. */
- if (write_symbols == DWARF2_DEBUG)
- return;
- else if (first_time)
+ /* Generate the reversed branch to NOT_TAKEN. */
+ operands[1] = not_taken;
+ output_asm_insn (branch_if_false, operands);
+
+ /* If INSN has a delay slot, we must provide delay slots for both the
+ branch to NOT_TAKEN and the conditional jump. We must also ensure
+ that INSN's delay slot is executed in the appropriate cases. */
+ if (final_sequence)
{
- first_time = 0;
- SET_FILE_NUMBER ();
- current_function_file = name;
- ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
- /* This tells mips-tfile that stabs will follow. */
- if (!TARGET_GAS && write_symbols == DBX_DEBUG)
- fprintf (stream, "\t#@stabs\n");
+ /* This first delay slot will always be executed, so use INSN's
+ delay slot if is not annulled. */
+ if (!INSN_ANNULLED_BRANCH_P (insn))
+ {
+ final_scan_insn (XVECEXP (final_sequence, 0, 1),
+ asm_out_file, optimize, 1, NULL);
+ INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
+ }
+ else
+ output_asm_insn ("nop", 0);
+ fprintf (asm_out_file, "\n");
}
- else if (write_symbols == DBX_DEBUG)
+ /* Output the unconditional branch to TAKEN. */
+ if (length <= 16)
+ output_asm_insn ("j\t%0%/", &taken);
+ else
{
- ASM_GENERATE_INTERNAL_LABEL (ltext_label_name, "Ltext", 0);
- fprintf (stream, "%s", ASM_STABS_OP);
- output_quoted_string (stream, name);
- fprintf (stream, ",%d,0,0,%s\n", N_SOL, <ext_label_name[1]);
+ output_asm_insn (mips_output_load_label (), &taken);
+ output_asm_insn ("jr\t%@%]%/", 0);
}
- else if (name != current_function_file
- && strcmp (name, current_function_file) != 0)
+ /* Now deal with its delay slot; see above. */
+ if (final_sequence)
{
- if (inside_function && !TARGET_GAS)
+ /* This delay slot will only be executed if the branch is taken.
+ Use INSN's delay slot if is annulled. */
+ if (INSN_ANNULLED_BRANCH_P (insn))
{
- if (!file_in_function_warning)
- {
- file_in_function_warning = 1;
- ignore_line_number = 1;
- warning ("MIPS ECOFF format does not allow changing filenames within functions with #line");
- }
+ final_scan_insn (XVECEXP (final_sequence, 0, 1),
+ asm_out_file, optimize, 1, NULL);
+ INSN_DELETED_P (XVECEXP (final_sequence, 0, 1)) = 1;
}
else
- {
- SET_FILE_NUMBER ();
- current_function_file = name;
- ASM_OUTPUT_FILENAME (stream, num_source_filenames, name);
- }
+ output_asm_insn ("nop", 0);
+ fprintf (asm_out_file, "\n");
}
+
+ /* Output NOT_TAKEN. */
+ targetm.asm_out.internal_label (asm_out_file, "L",
+ CODE_LABEL_NUMBER (not_taken));
+ return "";
}
-\f
-/* Emit a linenumber. For encapsulated stabs, we need to put out a stab
- as well as a .loc, since it is possible that MIPS ECOFF might not be
- able to represent the location for inlines that come from a different
- file. */
-void
-mips_output_lineno (stream, line)
- FILE *stream;
- int line;
+/* Return the assembly code for INSN, which branches to OPERANDS[1]
+ if some ordering condition is true. The condition is given by
+ OPERANDS[0] if !INVERTED_P, otherwise it is the inverse of
+ OPERANDS[0]. OPERANDS[2] is the comparison's first operand;
+ its second is always zero. */
+
+const char *
+mips_output_order_conditional_branch (rtx insn, rtx *operands, bool inverted_p)
{
- if (write_symbols == DBX_DEBUG)
- {
- ++sym_lineno;
- fprintf (stream, "%sLM%d:\n%s%d,0,%d,%sLM%d\n",
- LOCAL_LABEL_PREFIX, sym_lineno, ASM_STABN_OP, N_SLINE, line,
- LOCAL_LABEL_PREFIX, sym_lineno);
- }
- else
+ const char *branch[2];
+
+ /* Make BRANCH[1] branch to OPERANDS[1] when the condition is true.
+ Make BRANCH[0] branch on the inverse condition. */
+ switch (GET_CODE (operands[0]))
{
- fprintf (stream, "\n\t%s.loc\t%d %d\n",
- (ignore_line_number) ? "#" : "",
- num_source_filenames, line);
+ /* These cases are equivalent to comparisons against zero. */
+ case LEU:
+ inverted_p = !inverted_p;
+ /* Fall through. */
+ case GTU:
+ branch[!inverted_p] = MIPS_BRANCH ("bne", "%2,%.,%1");
+ branch[inverted_p] = MIPS_BRANCH ("beq", "%2,%.,%1");
+ break;
+
+ /* These cases are always true or always false. */
+ case LTU:
+ inverted_p = !inverted_p;
+ /* Fall through. */
+ case GEU:
+ branch[!inverted_p] = MIPS_BRANCH ("beq", "%.,%.,%1");
+ branch[inverted_p] = MIPS_BRANCH ("bne", "%.,%.,%1");
+ break;
- LABEL_AFTER_LOC (stream);
+ default:
+ branch[!inverted_p] = MIPS_BRANCH ("b%C0z", "%2,%1");
+ branch[inverted_p] = MIPS_BRANCH ("b%N0z", "%2,%1");
+ break;
}
+ return mips_output_conditional_branch (insn, operands, branch[1], branch[0]);
}
\f
-/* Output an ASCII string, in a space-saving way. */
+/* Return the assembly code for __sync_*() loop LOOP. The loop should support
+ both normal and likely branches, using %? and %~ where appropriate. */
-void
-mips_output_ascii (stream, string_param, len)
- FILE *stream;
- const char *string_param;
- size_t len;
+const char *
+mips_output_sync_loop (const char *loop)
{
- size_t i;
- int cur_pos = 17;
- register const unsigned char *string =
- (const unsigned char *)string_param;
+ /* Use branch-likely instructions to work around the LL/SC R10000 errata. */
+ mips_branch_likely = TARGET_FIX_R10000;
+ return loop;
+}
+\f
+/* Return the assembly code for DIV or DDIV instruction DIVISION, which has
+ the operands given by OPERANDS. Add in a divide-by-zero check if needed.
+
+ When working around R4000 and R4400 errata, we need to make sure that
+ the division is not immediately followed by a shift[1][2]. We also
+ need to stop the division from being put into a branch delay slot[3].
+ The easiest way to avoid both problems is to add a nop after the
+ division. When a divide-by-zero check is needed, this nop can be
+ used to fill the branch delay slot.
+
+ [1] If a double-word or a variable shift executes immediately
+ after starting an integer division, the shift may give an
+ incorrect result. See quotations of errata #16 and #28 from
+ "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+ in mips.md for details.
+
+ [2] A similar bug to [1] exists for all revisions of the
+ R4000 and the R4400 when run in an MC configuration.
+ From "MIPS R4000MC Errata, Processor Revision 2.2 and 3.0":
+
+ "19. In this following sequence:
+
+ ddiv (or ddivu or div or divu)
+ dsll32 (or dsrl32, dsra32)
+
+ if an MPT stall occurs, while the divide is slipping the cpu
+ pipeline, then the following double shift would end up with an
+ incorrect result.
+
+ Workaround: The compiler needs to avoid generating any
+ sequence with divide followed by extended double shift."
+
+ This erratum is also present in "MIPS R4400MC Errata, Processor
+ Revision 1.0" and "MIPS R4400MC Errata, Processor Revision 2.0
+ & 3.0" as errata #10 and #4, respectively.
+
+ [3] From "MIPS R4000PC/SC Errata, Processor Revision 2.2 and 3.0"
+ (also valid for MIPS R4000MC processors):
+
+ "52. R4000SC: This bug does not apply for the R4000PC.
+
+ There are two flavors of this bug:
+
+ 1) If the instruction just after divide takes an RF exception
+ (tlb-refill, tlb-invalid) and gets an instruction cache
+ miss (both primary and secondary) and the line which is
+ currently in secondary cache at this index had the first
+ data word, where the bits 5..2 are set, then R4000 would
+ get a wrong result for the div.
+
+ ##1
+ nop
+ div r8, r9
+ ------------------- # end-of page. -tlb-refill
+ nop
+ ##2
+ nop
+ div r8, r9
+ ------------------- # end-of page. -tlb-invalid
+ nop
+
+ 2) If the divide is in the taken branch delay slot, where the
+ target takes RF exception and gets an I-cache miss for the
+ exception vector or where I-cache miss occurs for the
+ target address, under the above mentioned scenarios, the
+ div would get wrong results.
+
+ ##1
+ j r2 # to next page mapped or unmapped
+ div r8,r9 # this bug would be there as long
+ # as there is an ICache miss and
+ nop # the "data pattern" is present
+
+ ##2
+ beq r0, r0, NextPage # to Next page
+ div r8,r9
+ nop
+
+ This bug is present for div, divu, ddiv, and ddivu
+ instructions.
+
+ Workaround: For item 1), OS could make sure that the next page
+ after the divide instruction is also mapped. For item 2), the
+ compiler could make sure that the divide instruction is not in
+ the branch delay slot."
+
+ These processors have PRId values of 0x00004220 and 0x00004300 for
+ the R4000 and 0x00004400, 0x00004500 and 0x00004600 for the R4400. */
- fprintf (stream, "\t.ascii\t\"");
- for (i = 0; i < len; i++)
- {
- register int c = string[i];
+const char *
+mips_output_division (const char *division, rtx *operands)
+{
+ const char *s;
- switch (c)
+ s = division;
+ if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
+ {
+ output_asm_insn (s, operands);
+ s = "nop";
+ }
+ if (TARGET_CHECK_ZERO_DIV)
+ {
+ if (TARGET_MIPS16)
{
- case '\"':
- case '\\':
- putc ('\\', stream);
- putc (c, stream);
- cur_pos += 2;
- break;
-
- case TARGET_NEWLINE:
- fputs ("\\n", stream);
- if (i+1 < len
- && (((c = string[i+1]) >= '\040' && c <= '~')
- || c == TARGET_TAB))
- cur_pos = 32767; /* break right here */
- else
- cur_pos += 2;
- break;
-
- case TARGET_TAB:
- fputs ("\\t", stream);
- cur_pos += 2;
- break;
-
- case TARGET_FF:
- fputs ("\\f", stream);
- cur_pos += 2;
- break;
-
- case TARGET_BS:
- fputs ("\\b", stream);
- cur_pos += 2;
- break;
-
- case TARGET_CR:
- fputs ("\\r", stream);
- cur_pos += 2;
- break;
-
- default:
- if (c >= ' ' && c < 0177)
- {
- putc (c, stream);
- cur_pos++;
- }
- else
- {
- fprintf (stream, "\\%03o", c);
- cur_pos += 4;
- }
+ output_asm_insn (s, operands);
+ s = "bnez\t%2,1f\n\tbreak\t7\n1:";
}
-
- if (cur_pos > 72 && i+1 < len)
+ else if (GENERATE_DIVIDE_TRAPS)
+ {
+ output_asm_insn (s, operands);
+ s = "teq\t%2,%.,7";
+ }
+ else
{
- cur_pos = 17;
- fprintf (stream, "\"\n\t.ascii\t\"");
+ output_asm_insn ("%(bne\t%2,%.,1f", operands);
+ output_asm_insn (s, operands);
+ s = "break\t7%)\n1:";
}
}
- fprintf (stream, "\"\n");
+ return s;
}
\f
-/* If defined, a C statement to be executed just prior to the output of
- assembler code for INSN, to modify the extracted operands so they will be
- output differently.
-
- Here the argument OPVEC is the vector containing the operands extracted
- from INSN, and NOPERANDS is the number of elements of the vector which
- contain meaningful data for this insn. The contents of this vector are
- what will be used to convert the insn template into assembler code, so you
- can change the assembler output by changing the contents of the vector.
+/* Return true if IN_INSN is a multiply-add or multiply-subtract
+ instruction and if OUT_INSN assigns to the accumulator operand. */
- We use it to check if the current insn needs a nop in front of it because
- of load delays, and also to update the delay slot statistics. */
-
-/* ??? There is no real need for this function, because it never actually
- emits a NOP anymore. */
-
-void
-final_prescan_insn (insn, opvec, noperands)
- rtx insn;
- rtx opvec[] ATTRIBUTE_UNUSED;
- int noperands ATTRIBUTE_UNUSED;
+bool
+mips_linked_madd_p (rtx out_insn, rtx in_insn)
{
- if (dslots_number_nops > 0)
- {
- rtx pattern = PATTERN (insn);
- int length = get_attr_length (insn);
+ rtx x;
- /* Do we need to emit a NOP? */
- if (length == 0
- || (mips_load_reg != 0 && reg_mentioned_p (mips_load_reg, pattern))
- || (mips_load_reg2 != 0 && reg_mentioned_p (mips_load_reg2, pattern))
- || (mips_load_reg3 != 0 && reg_mentioned_p (mips_load_reg3, pattern))
- || (mips_load_reg4 != 0
- && reg_mentioned_p (mips_load_reg4, pattern)))
- fputs ("\t#nop\n", asm_out_file);
+ x = single_set (in_insn);
+ if (x == 0)
+ return false;
- else
- dslots_load_filled++;
+ x = SET_SRC (x);
- while (--dslots_number_nops > 0)
- fputs ("\t#nop\n", asm_out_file);
+ if (GET_CODE (x) == PLUS
+ && GET_CODE (XEXP (x, 0)) == MULT
+ && reg_set_p (XEXP (x, 1), out_insn))
+ return true;
- mips_load_reg = 0;
- mips_load_reg2 = 0;
- mips_load_reg3 = 0;
- mips_load_reg4 = 0;
- }
+ if (GET_CODE (x) == MINUS
+ && GET_CODE (XEXP (x, 1)) == MULT
+ && reg_set_p (XEXP (x, 0), out_insn))
+ return true;
- if (TARGET_STATS
- && (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))
- dslots_jump_total++;
+ return false;
}
-\f
-/* Output at beginning of assembler file.
- If we are optimizing to use the global pointer, create a temporary file to
- hold all of the text stuff, and write it out to the end. This is needed
- because the MIPS assembler is evidently one pass, and if it hasn't seen the
- relevant .comm/.lcomm/.extern/.sdata declaration when the code is
- processed, it generates a two instruction sequence. */
+/* True if the dependency between OUT_INSN and IN_INSN is on the store
+ data rather than the address. We need this because the cprestore
+ pattern is type "store", but is defined using an UNSPEC_VOLATILE,
+ which causes the default routine to abort. We just return false
+ for that case. */
-void
-mips_asm_file_start (stream)
- FILE *stream;
+bool
+mips_store_data_bypass_p (rtx out_insn, rtx in_insn)
{
- ASM_OUTPUT_SOURCE_FILENAME (stream, main_input_filename);
-
- /* Versions of the MIPS assembler before 2.20 generate errors if a branch
- inside of a .set noreorder section jumps to a label outside of the .set
- noreorder section. Revision 2.20 just set nobopt silently rather than
- fixing the bug. */
+ if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE)
+ return false;
- if (TARGET_MIPS_AS && optimize && flag_delayed_branch)
- fprintf (stream, "\t.set\tnobopt\n");
-
- if (TARGET_GAS)
- {
-#if defined(OBJECT_FORMAT_ELF)
- /* Generate a special section to describe the ABI switches used to
- produce the resultant binary. This used to be done by the assembler
- setting bits in the ELF header's flags field, but we have run out of
- bits. GDB needs this information in order to be able to correctly
- debug these binaries. See the function mips_gdbarch_init() in
- gdb/mips-tdep.c. */
- const char * abi_string = NULL;
+ return !store_data_bypass_p (out_insn, in_insn);
+}
+\f
- switch (mips_abi)
- {
- case ABI_32: abi_string = "abi32"; break;
- case ABI_N32: abi_string = "abiN32"; break;
- case ABI_64: abi_string = "abi64"; break;
- case ABI_O64: abi_string = "abiO64"; break;
- case ABI_EABI: abi_string = TARGET_64BIT ? "eabi64" : "eabi32"; break;
- case ABI_MEABI:abi_string = TARGET_64BIT ? "meabi64" : "meabi32"; break;
- default:
- abort ();
- }
- /* Note - we use fprintf directly rather than called named_section()
- because in this way we can avoid creating an allocated section. We
- do not want this section to take up any space in the running
- executable. */
- fprintf (stream, "\t.section .mdebug.%s\n", abi_string);
-
- /* Restore the default section. */
- fprintf (stream, "\t.previous\n");
-#endif
- }
+/* Variables and flags used in scheduler hooks when tuning for
+ Loongson 2E/2F. */
+static struct
+{
+ /* Variables to support Loongson 2E/2F round-robin [F]ALU1/2 dispatch
+ strategy. */
+ /* If true, then next ALU1/2 instruction will go to ALU1. */
+ bool alu1_turn_p;
+ /* If true, then next FALU1/2 unstruction will go to FALU1. */
+ bool falu1_turn_p;
- /* Generate the pseudo ops that System V.4 wants. */
-#ifndef ABICALLS_ASM_OP
-#define ABICALLS_ASM_OP "\t.abicalls"
-#endif
- if (TARGET_ABICALLS)
- /* ??? but do not want this (or want pic0) if -non-shared? */
- fprintf (stream, "%s\n", ABICALLS_ASM_OP);
+ /* Codes to query if [f]alu{1,2}_core units are subscribed or not. */
+ int alu1_core_unit_code;
+ int alu2_core_unit_code;
+ int falu1_core_unit_code;
+ int falu2_core_unit_code;
- if (TARGET_MIPS16)
- fprintf (stream, "\t.set\tmips16\n");
+ /* True if current cycle has a multi instruction.
+ This flag is used in mips_ls2_dfa_post_advance_cycle. */
+ bool cycle_has_multi_p;
- /* This code exists so that we can put all externs before all symbol
- references. This is necessary for the MIPS assembler's global pointer
- optimizations to work. */
- if (TARGET_FILE_SWITCHING)
- {
- asm_out_data_file = stream;
- asm_out_text_file = tmpfile ();
- }
- else
- asm_out_data_file = asm_out_text_file = stream;
+ /* Instructions to subscribe ls2_[f]alu{1,2}_turn_enabled units.
+ These are used in mips_ls2_dfa_post_advance_cycle to initialize
+ DFA state.
+ E.g., when alu1_turn_enabled_insn is issued it makes next ALU1/2
+ instruction to go ALU1. */
+ rtx alu1_turn_enabled_insn;
+ rtx alu2_turn_enabled_insn;
+ rtx falu1_turn_enabled_insn;
+ rtx falu2_turn_enabled_insn;
+} mips_ls2;
- if (flag_verbose_asm)
- fprintf (stream, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
- ASM_COMMENT_START,
- mips_section_threshold, mips_arch_string, mips_isa);
-}
-\f
-/* If we are optimizing the global pointer, emit the text section now and any
- small externs which did not have .comm, etc that are needed. Also, give a
- warning if the data area is more than 32K and -pic because 3 instructions
- are needed to reference the data pointers. */
+/* Implement TARGET_SCHED_ADJUST_COST. We assume that anti and output
+ dependencies have no cost, except on the 20Kc where output-dependence
+ is treated like input-dependence. */
-void
-mips_asm_file_end (file)
- FILE *file;
+static int
+mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
+ rtx dep ATTRIBUTE_UNUSED, int cost)
{
- tree name_tree;
- struct extern_list *p;
-
- if (HALF_PIC_P ())
- {
- HALF_PIC_FINISH (file);
- }
+ if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT
+ && TUNE_20KC)
+ return cost;
+ if (REG_NOTE_KIND (link) != 0)
+ return 0;
+ return cost;
+}
- if (extern_head)
- {
- fputs ("\n", file);
+/* Return the number of instructions that can be issued per cycle. */
- for (p = extern_head; p != 0; p = p->next)
- {
- name_tree = get_identifier (p->name);
+static int
+mips_issue_rate (void)
+{
+ switch (mips_tune)
+ {
+ case PROCESSOR_74KC:
+ case PROCESSOR_74KF2_1:
+ case PROCESSOR_74KF1_1:
+ case PROCESSOR_74KF3_2:
+ /* The 74k is not strictly quad-issue cpu, but can be seen as one
+ by the scheduler. It can issue 1 ALU, 1 AGEN and 2 FPU insns,
+ but in reality only a maximum of 3 insns can be issued as
+ floating-point loads and stores also require a slot in the
+ AGEN pipe. */
+ case PROCESSOR_R10000:
+ /* All R10K Processors are quad-issue (being the first MIPS
+ processors to support this feature). */
+ return 4;
+
+ case PROCESSOR_20KC:
+ case PROCESSOR_R4130:
+ case PROCESSOR_R5400:
+ case PROCESSOR_R5500:
+ case PROCESSOR_R7000:
+ case PROCESSOR_R9000:
+ case PROCESSOR_OCTEON:
+ return 2;
- /* Positively ensure only one .extern for any given symbol. */
- if (! TREE_ASM_WRITTEN (name_tree))
- {
- TREE_ASM_WRITTEN (name_tree) = 1;
-#ifdef ASM_OUTPUT_UNDEF_FUNCTION
- if (p->size == -1)
- ASM_OUTPUT_UNDEF_FUNCTION (file, p->name);
- else
-#endif
- {
- fputs ("\t.extern\t", file);
- assemble_name (file, p->name);
- fprintf (file, ", %d\n", p->size);
- }
- }
- }
- }
+ case PROCESSOR_SB1:
+ case PROCESSOR_SB1A:
+ /* This is actually 4, but we get better performance if we claim 3.
+ This is partly because of unwanted speculative code motion with the
+ larger number, and partly because in most common cases we can't
+ reach the theoretical max of 4. */
+ return 3;
- if (TARGET_FILE_SWITCHING)
- {
- fprintf (file, "\n\t.text\n");
- copy_file_data (file, asm_out_text_file);
+ case PROCESSOR_LOONGSON_2E:
+ case PROCESSOR_LOONGSON_2F:
+ return 4;
+
+ default:
+ return 1;
}
}
+/* Implement TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN hook for Loongson2. */
+
static void
-copy_file_data (to, from)
- FILE *to, *from;
+mips_ls2_init_dfa_post_cycle_insn (void)
{
- char buffer[8192];
- size_t len;
- rewind (from);
- if (ferror (from))
- fatal_io_error ("can't rewind temp file");
-
- while ((len = fread (buffer, 1, sizeof (buffer), from)) > 0)
- if (fwrite (buffer, 1, len, to) != len)
- fatal_io_error ("can't write to output file");
+ start_sequence ();
+ emit_insn (gen_ls2_alu1_turn_enabled_insn ());
+ mips_ls2.alu1_turn_enabled_insn = get_insns ();
+ end_sequence ();
- if (ferror (from))
- fatal_io_error ("can't read from temp file");
-
- if (fclose (from))
- fatal_io_error ("can't close temp file");
-}
+ start_sequence ();
+ emit_insn (gen_ls2_alu2_turn_enabled_insn ());
+ mips_ls2.alu2_turn_enabled_insn = get_insns ();
+ end_sequence ();
-/* Emit either a label, .comm, or .lcomm directive, and mark that the symbol
- is used, so that we don't emit an .extern for it in mips_asm_file_end. */
+ start_sequence ();
+ emit_insn (gen_ls2_falu1_turn_enabled_insn ());
+ mips_ls2.falu1_turn_enabled_insn = get_insns ();
+ end_sequence ();
-void
-mips_declare_object (stream, name, init_string, final_string, size)
- FILE *stream;
- const char *name;
- const char *init_string;
- const char *final_string;
- int size;
-{
- fputs (init_string, stream); /* "", "\t.comm\t", or "\t.lcomm\t" */
- assemble_name (stream, name);
- fprintf (stream, final_string, size); /* ":\n", ",%u\n", ",%u\n" */
+ start_sequence ();
+ emit_insn (gen_ls2_falu2_turn_enabled_insn ());
+ mips_ls2.falu2_turn_enabled_insn = get_insns ();
+ end_sequence ();
- if (TARGET_GP_OPT)
- {
- tree name_tree = get_identifier (name);
- TREE_ASM_WRITTEN (name_tree) = 1;
- }
+ mips_ls2.alu1_core_unit_code = get_cpu_unit_code ("ls2_alu1_core");
+ mips_ls2.alu2_core_unit_code = get_cpu_unit_code ("ls2_alu2_core");
+ mips_ls2.falu1_core_unit_code = get_cpu_unit_code ("ls2_falu1_core");
+ mips_ls2.falu2_core_unit_code = get_cpu_unit_code ("ls2_falu2_core");
}
-\f
-/* Return the bytes needed to compute the frame pointer from the current
- stack pointer.
-
- Mips stack frames look like:
-
- Before call After call
- +-----------------------+ +-----------------------+
- high | | | |
- mem. | | | |
- | caller's temps. | | caller's temps. |
- | | | |
- +-----------------------+ +-----------------------+
- | | | |
- | arguments on stack. | | arguments on stack. |
- | | | |
- +-----------------------+ +-----------------------+
- | 4 words to save | | 4 words to save |
- | arguments passed | | arguments passed |
- | in registers, even | | in registers, even |
- SP->| if not passed. | VFP->| if not passed. |
- +-----------------------+ +-----------------------+
- | |
- | fp register save |
- | |
- +-----------------------+
- | |
- | gp register save |
- | |
- +-----------------------+
- | |
- | local variables |
- | |
- +-----------------------+
- | |
- | alloca allocations |
- | |
- +-----------------------+
- | |
- | GP save for V.4 abi |
- | |
- +-----------------------+
- | |
- | arguments on stack |
- | |
- +-----------------------+
- | 4 words to save |
- | arguments passed |
- | in registers, even |
- low SP->| if not passed. |
- memory +-----------------------+
-
-*/
-
-HOST_WIDE_INT
-compute_frame_size (size)
- HOST_WIDE_INT size; /* # of var. bytes allocated */
-{
- unsigned int regno;
- HOST_WIDE_INT total_size; /* # bytes that the entire frame takes up */
- HOST_WIDE_INT var_size; /* # bytes that variables take up */
- HOST_WIDE_INT args_size; /* # bytes that outgoing arguments take up */
- HOST_WIDE_INT extra_size; /* # extra bytes */
- HOST_WIDE_INT gp_reg_rounded; /* # bytes needed to store gp after rounding */
- HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */
- HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */
- long mask; /* mask of saved gp registers */
- long fmask; /* mask of saved fp registers */
- int fp_inc; /* 1 or 2 depending on the size of fp regs */
- long fp_bits; /* bitmask to use for each fp register */
-
- gp_reg_size = 0;
- fp_reg_size = 0;
- mask = 0;
- fmask = 0;
- extra_size = MIPS_STACK_ALIGN (((TARGET_ABICALLS) ? UNITS_PER_WORD : 0));
- var_size = MIPS_STACK_ALIGN (size);
- args_size = MIPS_STACK_ALIGN (current_function_outgoing_args_size);
- /* The MIPS 3.0 linker does not like functions that dynamically
- allocate the stack and have 0 for STACK_DYNAMIC_OFFSET, since it
- looks like we are trying to create a second frame pointer to the
- function, so allocate some stack space to make it happy. */
+/* Implement TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN hook.
+ Init data used in mips_dfa_post_advance_cycle. */
- if (args_size == 0 && current_function_calls_alloca)
- args_size = 4 * UNITS_PER_WORD;
+static void
+mips_init_dfa_post_cycle_insn (void)
+{
+ if (TUNE_LOONGSON_2EF)
+ mips_ls2_init_dfa_post_cycle_insn ();
+}
- total_size = var_size + args_size + extra_size;
+/* Initialize STATE when scheduling for Loongson 2E/2F.
+ Support round-robin dispatch scheme by enabling only one of
+ ALU1/ALU2 and one of FALU1/FALU2 units for ALU1/2 and FALU1/2 instructions
+ respectively. */
- /* Calculate space needed for gp registers. */
- for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
+static void
+mips_ls2_dfa_post_advance_cycle (state_t state)
+{
+ if (cpu_unit_reservation_p (state, mips_ls2.alu1_core_unit_code))
{
- /* $18 is a special case on the mips16. It may be used to call
- a function which returns a floating point value, but it is
- marked in call_used_regs. $31 is also a special case. When
- not using -mentry, it will be used to copy a return value
- into the floating point registers if the return value is
- floating point. */
- if (MUST_SAVE_REGISTER (regno)
- || (TARGET_MIPS16
- && regno == GP_REG_FIRST + 18
- && regs_ever_live[regno])
- || (TARGET_MIPS16
- && regno == GP_REG_FIRST + 31
- && mips16_hard_float
- && ! mips_entry
- && ! aggregate_value_p (DECL_RESULT (current_function_decl))
- && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
- == MODE_FLOAT)
- && (! TARGET_SINGLE_FLOAT
- || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
- <= 4))))
- {
- gp_reg_size += GET_MODE_SIZE (gpr_mode);
- mask |= 1L << (regno - GP_REG_FIRST);
-
- /* The entry and exit pseudo instructions can not save $17
- without also saving $16. */
- if (mips_entry
- && regno == GP_REG_FIRST + 17
- && ! MUST_SAVE_REGISTER (GP_REG_FIRST + 16))
- {
- gp_reg_size += UNITS_PER_WORD;
- mask |= 1L << 16;
- }
- }
+ /* Though there are no non-pipelined ALU1 insns,
+ we can get an instruction of type 'multi' before reload. */
+ gcc_assert (mips_ls2.cycle_has_multi_p);
+ mips_ls2.alu1_turn_p = false;
}
- /* We need to restore these for the handler. */
- if (current_function_calls_eh_return)
- {
- unsigned int i;
- for (i = 0; ; ++i)
- {
- regno = EH_RETURN_DATA_REGNO (i);
- if (regno == INVALID_REGNUM)
- break;
- gp_reg_size += GET_MODE_SIZE (gpr_mode);
- mask |= 1L << (regno - GP_REG_FIRST);
- }
- }
+ mips_ls2.cycle_has_multi_p = false;
+
+ if (cpu_unit_reservation_p (state, mips_ls2.alu2_core_unit_code))
+ /* We have a non-pipelined alu instruction in the core,
+ adjust round-robin counter. */
+ mips_ls2.alu1_turn_p = true;
- /* Calculate space needed for fp registers. */
- if (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT)
+ if (mips_ls2.alu1_turn_p)
{
- fp_inc = 1;
- fp_bits = 1;
+ if (state_transition (state, mips_ls2.alu1_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
}
else
{
- fp_inc = 2;
- fp_bits = 3;
+ if (state_transition (state, mips_ls2.alu2_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
}
- /* This loop must iterate over the same space as its companion in
- save_restore_insns. */
- for (regno = (FP_REG_LAST - fp_inc + 1);
- regno >= FP_REG_FIRST;
- regno -= fp_inc)
+ if (cpu_unit_reservation_p (state, mips_ls2.falu1_core_unit_code))
{
- if (regs_ever_live[regno] && !call_used_regs[regno])
- {
- fp_reg_size += fp_inc * UNITS_PER_FPREG;
- fmask |= fp_bits << (regno - FP_REG_FIRST);
- }
+ /* There are no non-pipelined FALU1 insns. */
+ gcc_unreachable ();
+ mips_ls2.falu1_turn_p = false;
}
- gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
- total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
-
- /* The gp reg is caller saved in the 32 bit ABI, so there is no need
- for leaf routines (total_size == extra_size) to save the gp reg.
- The gp reg is callee saved in the 64 bit ABI, so all routines must
- save the gp reg. This is not a leaf routine if -p, because of the
- call to mcount. */
- if (total_size == extra_size
- && (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI)
- && ! current_function_profile)
- total_size = extra_size = 0;
- else if (TARGET_ABICALLS)
- {
- /* Add the context-pointer to the saved registers. */
- gp_reg_size += UNITS_PER_WORD;
- mask |= 1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST);
- total_size -= gp_reg_rounded;
- gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
- total_size += gp_reg_rounded;
- }
-
- /* Add in space reserved on the stack by the callee for storing arguments
- passed in registers. */
- if (mips_abi != ABI_32 && mips_abi != ABI_O64)
- total_size += MIPS_STACK_ALIGN (current_function_pretend_args_size);
-
- /* The entry pseudo instruction will allocate 32 bytes on the stack. */
- if (mips_entry && total_size > 0 && total_size < 32)
- total_size = 32;
-
- /* Save other computed information. */
- current_frame_info.total_size = total_size;
- current_frame_info.var_size = var_size;
- current_frame_info.args_size = args_size;
- current_frame_info.extra_size = extra_size;
- current_frame_info.gp_reg_size = gp_reg_size;
- current_frame_info.fp_reg_size = fp_reg_size;
- current_frame_info.mask = mask;
- current_frame_info.fmask = fmask;
- current_frame_info.initialized = reload_completed;
- current_frame_info.num_gp = gp_reg_size / UNITS_PER_WORD;
- current_frame_info.num_fp = fp_reg_size / (fp_inc * UNITS_PER_FPREG);
-
- if (mask)
- {
- unsigned long offset;
-
- /* When using mips_entry, the registers are always saved at the
- top of the stack. */
- if (! mips_entry)
- offset = (args_size + extra_size + var_size
- + gp_reg_size - GET_MODE_SIZE (gpr_mode));
- else
- offset = total_size - GET_MODE_SIZE (gpr_mode);
-
- current_frame_info.gp_sp_offset = offset;
- current_frame_info.gp_save_offset = offset - total_size;
- }
- else
- {
- current_frame_info.gp_sp_offset = 0;
- current_frame_info.gp_save_offset = 0;
- }
+ if (cpu_unit_reservation_p (state, mips_ls2.falu2_core_unit_code))
+ /* We have a non-pipelined falu instruction in the core,
+ adjust round-robin counter. */
+ mips_ls2.falu1_turn_p = true;
- if (fmask)
+ if (mips_ls2.falu1_turn_p)
{
- unsigned long offset = (args_size + extra_size + var_size
- + gp_reg_rounded + fp_reg_size
- - fp_inc * UNITS_PER_FPREG);
- current_frame_info.fp_sp_offset = offset;
- current_frame_info.fp_save_offset = offset - total_size;
+ if (state_transition (state, mips_ls2.falu1_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
}
else
{
- current_frame_info.fp_sp_offset = 0;
- current_frame_info.fp_save_offset = 0;
+ if (state_transition (state, mips_ls2.falu2_turn_enabled_insn) >= 0)
+ gcc_unreachable ();
}
-
- /* Ok, we're done. */
- return total_size;
}
-\f
-/* Common code to emit the insns (or to write the instructions to a file)
- to save/restore registers.
- Other parts of the code assume that MIPS_TEMP1_REGNUM (aka large_reg)
- is not modified within save_restore_insns. */
+/* Implement TARGET_SCHED_DFA_POST_ADVANCE_CYCLE.
+ This hook is being called at the start of each cycle. */
-#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)
-
-/* Emit instructions to load the value (SP + OFFSET) into MIPS_TEMP2_REGNUM
- and return an rtl expression for the register. Write the assembly
- instructions directly to FILE if it is not null, otherwise emit them as
- rtl.
+static void
+mips_dfa_post_advance_cycle (void)
+{
+ if (TUNE_LOONGSON_2EF)
+ mips_ls2_dfa_post_advance_cycle (curr_state);
+}
- This function is a subroutine of save_restore_insns. It is used when
- OFFSET is too large to add in a single instruction. */
+/* Implement TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD. This should
+ be as wide as the scheduling freedom in the DFA. */
-static rtx
-mips_add_large_offset_to_sp (offset, file)
- HOST_WIDE_INT offset;
- FILE *file;
+static int
+mips_multipass_dfa_lookahead (void)
{
- rtx reg = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
- if (file == 0)
- {
- rtx offset_rtx = GEN_INT (offset);
+ /* Can schedule up to 4 of the 6 function units in any one cycle. */
+ if (TUNE_SB1)
+ return 4;
- emit_move_insn (reg, offset_rtx);
- if (Pmode == DImode)
- emit_insn (gen_adddi3 (reg, reg, stack_pointer_rtx));
- else
- emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx));
- }
- else
- {
- fprintf (file, "\tli\t%s,0x%.08lx\t# ",
- reg_names[MIPS_TEMP2_REGNUM], (long) offset);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset);
- fprintf (file, "\n\t%s\t%s,%s,%s\n",
- Pmode == DImode ? "daddu" : "addu",
- reg_names[MIPS_TEMP2_REGNUM],
- reg_names[MIPS_TEMP2_REGNUM],
- reg_names[STACK_POINTER_REGNUM]);
- }
- return reg;
-}
+ if (TUNE_LOONGSON_2EF)
+ return 4;
-/* Make INSN frame related and note that it performs the frame-related
- operation DWARF_PATTERN. */
+ if (TUNE_OCTEON)
+ return 2;
-static void
-mips_annotate_frame_insn (insn, dwarf_pattern)
- rtx insn, dwarf_pattern;
-{
- RTX_FRAME_RELATED_P (insn) = 1;
- REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR,
- dwarf_pattern,
- REG_NOTES (insn));
+ return 0;
}
+\f
+/* Remove the instruction at index LOWER from ready queue READY and
+ reinsert it in front of the instruction at index HIGHER. LOWER must
+ be <= HIGHER. */
-/* Return a frame-related rtx that stores register REGNO at (SP + OFFSET).
- The expression should only be used to store single registers. */
+static void
+mips_promote_ready (rtx *ready, int lower, int higher)
+{
+ rtx new_head;
+ int i;
-static rtx
-mips_frame_set (mode, regno, offset)
- enum machine_mode mode;
- int regno;
- int offset;
-{
- rtx address = plus_constant (stack_pointer_rtx, offset);
- rtx set = gen_rtx_SET (mode,
- gen_rtx_MEM (mode, address),
- gen_rtx_REG (mode, regno));
- RTX_FRAME_RELATED_P (set) = 1;
- return set;
+ new_head = ready[lower];
+ for (i = lower; i < higher; i++)
+ ready[i] = ready[i + 1];
+ ready[i] = new_head;
}
-
-/* Emit a move instruction that stores REG in MEM. Make the instruction
- frame related and note that it stores REG at (SP + OFFSET). This
- function may be asked to store an FPR pair. */
+/* 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 void
-mips_emit_frame_related_store (mem, reg, offset)
- rtx mem;
- rtx reg;
- HOST_WIDE_INT offset;
+mips_maybe_swap_ready (rtx *ready, int pos1, int pos2, int limit)
{
- rtx dwarf_expr;
-
- if (GET_MODE (reg) == DFmode && ! TARGET_FLOAT64)
+ if (pos1 < pos2
+ && INSN_PRIORITY (ready[pos1]) + limit >= INSN_PRIORITY (ready[pos2]))
{
- /* Two registers are being stored, so the frame-related expression
- must be a PARALLEL rtx with one SET for each register. The
- higher numbered register is stored in the lower address on
- big-endian targets. */
- int regno1 = TARGET_BIG_ENDIAN ? REGNO (reg) + 1 : REGNO (reg);
- int regno2 = TARGET_BIG_ENDIAN ? REGNO (reg) : REGNO (reg) + 1;
- rtx set1 = mips_frame_set (SFmode, regno1, offset);
- rtx set2 = mips_frame_set (SFmode, regno2, offset + UNITS_PER_FPREG);
- dwarf_expr = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set1, set2));
- }
- else
- dwarf_expr = mips_frame_set (GET_MODE (reg), REGNO (reg), offset);
+ rtx temp;
- mips_annotate_frame_insn (emit_move_insn (mem, reg), dwarf_expr);
+ temp = ready[pos1];
+ ready[pos1] = ready[pos2];
+ ready[pos2] = temp;
+ }
}
+\f
+/* Used by TUNE_MACC_CHAINS to record the last scheduled instruction
+ that may clobber hi or lo. */
+static rtx mips_macc_chains_last_hilo;
+
+/* A TUNE_MACC_CHAINS helper function. Record that instruction INSN has
+ been scheduled, updating mips_macc_chains_last_hilo appropriately. */
static void
-save_restore_insns (store_p, large_reg, large_offset, file)
- int store_p; /* true if this is prologue */
- rtx large_reg; /* register holding large offset constant or NULL */
- long large_offset; /* large constant offset value */
- FILE *file; /* file to write instructions instead of making RTL */
-{
- long mask = current_frame_info.mask;
- long fmask = current_frame_info.fmask;
- long real_mask = mask;
- int regno;
- rtx base_reg_rtx;
- HOST_WIDE_INT base_offset;
- HOST_WIDE_INT gp_offset;
- HOST_WIDE_INT fp_offset;
- HOST_WIDE_INT end_offset;
- rtx insn;
+mips_macc_chains_record (rtx insn)
+{
+ if (get_attr_may_clobber_hilo (insn))
+ mips_macc_chains_last_hilo = insn;
+}
- if (frame_pointer_needed
- && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST))
- abort ();
+/* A TUNE_MACC_CHAINS helper function. Search ready queue READY, which
+ has NREADY elements, looking for a multiply-add or multiply-subtract
+ instruction that is cumulative with mips_macc_chains_last_hilo.
+ If there is one, promote it ahead of anything else that might
+ clobber hi or lo. */
- /* Do not restore GP under certain conditions. */
- if (! store_p
- && TARGET_ABICALLS
- && (mips_abi == ABI_32 || mips_abi == ABI_O64))
- mask &= ~(1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST));
+static void
+mips_macc_chains_reorder (rtx *ready, int nready)
+{
+ int i, j;
- if (mask == 0 && fmask == 0)
- return;
+ if (mips_macc_chains_last_hilo != 0)
+ for (i = nready - 1; i >= 0; i--)
+ if (mips_linked_madd_p (mips_macc_chains_last_hilo, ready[i]))
+ {
+ for (j = nready - 1; j > i; j--)
+ if (recog_memoized (ready[j]) >= 0
+ && get_attr_may_clobber_hilo (ready[j]))
+ {
+ mips_promote_ready (ready, i, j);
+ break;
+ }
+ break;
+ }
+}
+\f
+/* The last instruction to be scheduled. */
+static rtx vr4130_last_insn;
- /* Save registers starting from high to low. The debuggers prefer at least
- the return register be stored at func+4, and also it allows us not to
- need a nop in the epilog if at least one register is reloaded in
- addition to return address. */
+/* A note_stores callback used by vr4130_true_reg_dependence_p. DATA
+ points to an rtx that is initially an instruction. Nullify the rtx
+ if the instruction uses the value of register X. */
- /* Save GP registers if needed. */
- if (mask)
- {
- /* Pick which pointer to use as a base register. For small frames, just
- use the stack pointer. Otherwise, use a temporary register. Save 2
- cycles if the save area is near the end of a large frame, by reusing
- the constant created in the prologue/epilogue to adjust the stack
- frame. */
+static void
+vr4130_true_reg_dependence_p_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED,
+ void *data)
+{
+ rtx *insn_ptr;
- gp_offset = current_frame_info.gp_sp_offset;
- end_offset
- = gp_offset - (current_frame_info.gp_reg_size
- - GET_MODE_SIZE (gpr_mode));
+ insn_ptr = (rtx *) data;
+ if (REG_P (x)
+ && *insn_ptr != 0
+ && reg_referenced_p (x, PATTERN (*insn_ptr)))
+ *insn_ptr = 0;
+}
- if (gp_offset < 0 || end_offset < 0)
- internal_error
- ("gp_offset (%ld) or end_offset (%ld) is less than zero",
- (long) gp_offset, (long) end_offset);
+/* Return true if there is true register dependence between vr4130_last_insn
+ and INSN. */
- /* If we see a large frame in mips16 mode, we save the registers
- before adjusting the stack pointer, and load them afterward. */
- else if (TARGET_MIPS16 && large_offset > 32767)
- base_reg_rtx = stack_pointer_rtx, base_offset = large_offset;
+static bool
+vr4130_true_reg_dependence_p (rtx insn)
+{
+ note_stores (PATTERN (vr4130_last_insn),
+ vr4130_true_reg_dependence_p_1, &insn);
+ return insn == 0;
+}
- else if (gp_offset < 32768)
- base_reg_rtx = stack_pointer_rtx, base_offset = 0;
+/* A TUNE_MIPS4130 helper function. Given that INSN1 is at the head of
+ the ready queue and that INSN2 is the instruction after it, return
+ true if it is worth promoting INSN2 ahead of INSN1. Look for cases
+ in which INSN1 and INSN2 can probably issue in parallel, but for
+ which (INSN2, INSN1) should be less sensitive to instruction
+ alignment than (INSN1, INSN2). See 4130.md for more details. */
- else if (large_reg != 0
- && (unsigned HOST_WIDE_INT) (large_offset - gp_offset) < 32768
- && (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
- {
- base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
- base_offset = large_offset;
- if (file == 0)
- {
- if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
- else
- insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
- }
- else
- fprintf (file, "\t%s\t%s,%s,%s\n",
- Pmode == DImode ? "daddu" : "addu",
- reg_names[MIPS_TEMP2_REGNUM],
- reg_names[REGNO (large_reg)],
- reg_names[STACK_POINTER_REGNUM]);
- }
- else
- {
- base_offset = gp_offset;
- base_reg_rtx = mips_add_large_offset_to_sp (base_offset, file);
- }
+static bool
+vr4130_swap_insns_p (rtx insn1, rtx insn2)
+{
+ sd_iterator_def sd_it;
+ dep_t dep;
- /* When we restore the registers in MIPS16 mode, then if we are
- using a frame pointer, and this is not a large frame, the
- current stack pointer will be offset by
- current_function_outgoing_args_size. Doing it this way lets
- us avoid offsetting the frame pointer before copying it into
- the stack pointer; there is no instruction to set the stack
- pointer to the sum of a register and a constant. */
- if (TARGET_MIPS16
- && ! store_p
- && frame_pointer_needed
- && large_offset <= 32767)
- base_offset += current_function_outgoing_args_size;
+ /* Check for the following case:
- for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--)
- if (BITSET_P (mask, regno - GP_REG_FIRST))
- {
- if (file == 0)
- {
- rtx reg_rtx;
- rtx mem_rtx
- = gen_rtx (MEM, gpr_mode,
- gen_rtx (PLUS, Pmode, base_reg_rtx,
- GEN_INT (gp_offset - base_offset)));
-
- if (! current_function_calls_eh_return)
- RTX_UNCHANGING_P (mem_rtx) = 1;
-
- /* The mips16 does not have an instruction to load
- $31, so we load $7 instead, and work things out
- in mips_expand_epilogue. */
- if (TARGET_MIPS16 && ! store_p && regno == GP_REG_FIRST + 31)
- reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 7);
- /* The mips16 sometimes needs to save $18. */
- else if (TARGET_MIPS16
- && regno != GP_REG_FIRST + 31
- && ! M16_REG_P (regno))
- {
- if (! store_p)
- reg_rtx = gen_rtx (REG, gpr_mode, 6);
- else
- {
- reg_rtx = gen_rtx (REG, gpr_mode, 3);
- emit_move_insn (reg_rtx,
- gen_rtx (REG, gpr_mode, regno));
- }
- }
- else
- reg_rtx = gen_rtx (REG, gpr_mode, regno);
+ 1) there is some other instruction X with an anti dependence on INSN1;
+ 2) X has a higher priority than INSN2; and
+ 3) X is an arithmetic instruction (and thus has no unit restrictions).
- if (store_p)
- mips_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset);
- else
- {
- emit_move_insn (reg_rtx, mem_rtx);
- if (TARGET_MIPS16
- && regno != GP_REG_FIRST + 31
- && ! M16_REG_P (regno))
- emit_move_insn (gen_rtx (REG, gpr_mode, regno),
- reg_rtx);
- }
- }
- else
- {
- int r = regno;
-
- /* The mips16 does not have an instruction to
- load $31, so we load $7 instead, and work
- things out in the caller. */
- if (TARGET_MIPS16 && ! store_p && r == GP_REG_FIRST + 31)
- r = GP_REG_FIRST + 7;
- /* The mips16 sometimes needs to save $18. */
- if (TARGET_MIPS16
- && regno != GP_REG_FIRST + 31
- && ! M16_REG_P (regno))
- {
- if (! store_p)
- r = GP_REG_FIRST + 6;
- else
- {
- r = GP_REG_FIRST + 3;
- fprintf (file, "\tmove\t%s,%s\n",
- reg_names[r], reg_names[regno]);
- }
- }
- fprintf (file, "\t%s\t%s,",
- (TARGET_64BIT
- ? (store_p) ? "sd" : "ld"
- : (store_p) ? "sw" : "lw"),
- reg_names[r]);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC,
- gp_offset - base_offset);
- fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]);
- if (! store_p
- && TARGET_MIPS16
- && regno != GP_REG_FIRST + 31
- && ! M16_REG_P (regno))
- fprintf (file, "\tmove\t%s,%s\n",
- reg_names[regno], reg_names[r]);
- }
- gp_offset -= GET_MODE_SIZE (gpr_mode);
- }
- /* If the restore is being supressed, still take into account
- the offset at which it is stored. */
- else if (BITSET_P (real_mask, regno - GP_REG_FIRST))
- {
- gp_offset -= GET_MODE_SIZE (gpr_mode);
- }
- }
- else
- base_reg_rtx = 0, base_offset = 0;
+ If INSN1 is the last instruction blocking X, it would better to
+ choose (INSN1, X) over (INSN2, INSN1). */
+ 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;
- /* Save floating point registers if needed. */
- if (fmask)
+ if (vr4130_last_insn != 0
+ && recog_memoized (insn1) >= 0
+ && recog_memoized (insn2) >= 0)
{
- int fp_inc = (TARGET_FLOAT64 || TARGET_SINGLE_FLOAT) ? 1 : 2;
- int fp_size = fp_inc * UNITS_PER_FPREG;
-
- /* Pick which pointer to use as a base register. */
- fp_offset = current_frame_info.fp_sp_offset;
- end_offset = fp_offset - (current_frame_info.fp_reg_size - fp_size);
-
- if (fp_offset < 0 || end_offset < 0)
- internal_error
- ("fp_offset (%ld) or end_offset (%ld) is less than zero",
- (long) fp_offset, (long) end_offset);
-
- else if (fp_offset < 32768)
- base_reg_rtx = stack_pointer_rtx, base_offset = 0;
-
- else if (base_reg_rtx != 0
- && (unsigned HOST_WIDE_INT) (base_offset - fp_offset) < 32768
- && (unsigned HOST_WIDE_INT) (base_offset - end_offset) < 32768)
- ; /* already set up for gp registers above */
-
- else if (large_reg != 0
- && (unsigned HOST_WIDE_INT) (large_offset - fp_offset) < 32768
- && (unsigned HOST_WIDE_INT) (large_offset - end_offset) < 32768)
+ /* See whether INSN1 and INSN2 use different execution units,
+ or if they are both ALU-type instructions. If so, they can
+ probably execute in parallel. */
+ enum attr_vr4130_class class1 = get_attr_vr4130_class (insn1);
+ enum attr_vr4130_class class2 = get_attr_vr4130_class (insn2);
+ if (class1 != class2 || class1 == VR4130_CLASS_ALU)
{
- base_reg_rtx = gen_rtx_REG (Pmode, MIPS_TEMP2_REGNUM);
- base_offset = large_offset;
- if (file == 0)
- {
- if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
- else
- insn = emit_insn (gen_addsi3 (base_reg_rtx, large_reg,
- stack_pointer_rtx));
- }
+ /* If only one of the instructions has a dependence on
+ vr4130_last_insn, prefer to schedule the other one first. */
+ bool dep1_p = vr4130_true_reg_dependence_p (insn1);
+ bool dep2_p = vr4130_true_reg_dependence_p (insn2);
+ if (dep1_p != dep2_p)
+ return dep1_p;
- else
- fprintf (file, "\t%s\t%s,%s,%s\n",
- Pmode == DImode ? "daddu" : "addu",
- reg_names[MIPS_TEMP2_REGNUM],
- reg_names[REGNO (large_reg)],
- reg_names[STACK_POINTER_REGNUM]);
+ /* Prefer to schedule INSN2 ahead of INSN1 if vr4130_last_insn
+ is not an ALU-type instruction and if INSN1 uses the same
+ execution unit. (Note that if this condition holds, we already
+ know that INSN2 uses a different execution unit.) */
+ if (class1 != VR4130_CLASS_ALU
+ && recog_memoized (vr4130_last_insn) >= 0
+ && class1 == get_attr_vr4130_class (vr4130_last_insn))
+ return true;
}
- else
- {
- base_offset = fp_offset;
- base_reg_rtx = mips_add_large_offset_to_sp (fp_offset, file);
- }
-
- /* This loop must iterate over the same space as its companion in
- compute_frame_size. */
- for (regno = (FP_REG_LAST - fp_inc + 1);
- regno >= FP_REG_FIRST;
- regno -= fp_inc)
- if (BITSET_P (fmask, regno - FP_REG_FIRST))
- {
- if (file == 0)
- {
- enum machine_mode sz
- = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
- rtx reg_rtx = gen_rtx (REG, sz, regno);
- rtx mem_rtx = gen_rtx (MEM, sz,
- gen_rtx (PLUS, Pmode, base_reg_rtx,
- GEN_INT (fp_offset
- - base_offset)));
- if (! current_function_calls_eh_return)
- RTX_UNCHANGING_P (mem_rtx) = 1;
-
- if (store_p)
- mips_emit_frame_related_store (mem_rtx, reg_rtx, fp_offset);
- else
- emit_move_insn (reg_rtx, mem_rtx);
- }
- else
- {
- fprintf (file, "\t%s\t%s,",
- (TARGET_SINGLE_FLOAT
- ? (store_p ? "s.s" : "l.s")
- : (store_p ? "s.d" : "l.d")),
- reg_names[regno]);
- fprintf (file, HOST_WIDE_INT_PRINT_DEC,
- fp_offset - base_offset);
- fprintf (file, "(%s)\n", reg_names[REGNO(base_reg_rtx)]);
- }
-
- fp_offset -= fp_size;
- }
}
+ return false;
}
-\f
-/* Set up the stack and frame (if desired) for the function. */
+
+/* A TUNE_MIPS4130 helper function. (READY, NREADY) describes a ready
+ queue with at least two instructions. Swap the first two if
+ vr4130_swap_insns_p says that it could be worthwhile. */
static void
-mips_output_function_prologue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+vr4130_reorder (rtx *ready, int nready)
{
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- const char *fnname;
-#endif
- HOST_WIDE_INT tsize = current_frame_info.total_size;
-
- /* ??? When is this really needed? At least the GNU assembler does not
- need the source filename more than once in the file, beyond what is
- emitted by the debug information. */
- if (!TARGET_GAS)
- ASM_OUTPUT_SOURCE_FILENAME (file, DECL_SOURCE_FILE (current_function_decl));
-
-#ifdef SDB_DEBUGGING_INFO
- if (debug_info_level != DINFO_LEVEL_TERSE && write_symbols == SDB_DEBUG)
- ASM_OUTPUT_SOURCE_LINE (file, DECL_SOURCE_LINE (current_function_decl));
-#endif
-
- /* In mips16 mode, we may need to generate a 32 bit to handle
- 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 (TARGET_MIPS16 && !TARGET_SOFT_FLOAT
- && current_function_args_info.fp_code != 0)
- build_mips16_function_stub (file);
-
- inside_function = 1;
+ if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2]))
+ mips_promote_ready (ready, nready - 2, nready - 1);
+}
+\f
+/* Record whether last 74k AGEN instruction was a load or store. */
+static enum attr_type mips_last_74k_agen_insn = TYPE_UNKNOWN;
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- /* Get the function name the same way that toplev.c does before calling
- assemble_start_function. This is needed so that the name used here
- exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
- fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+/* Initialize mips_last_74k_agen_insn from INSN. A null argument
+ resets to TYPE_UNKNOWN state. */
- if (!flag_inhibit_size_directive)
+static void
+mips_74k_agen_init (rtx insn)
+{
+ if (!insn || !NONJUMP_INSN_P (insn))
+ mips_last_74k_agen_insn = TYPE_UNKNOWN;
+ else
{
- fputs ("\t.ent\t", file);
- assemble_name (file, fnname);
- fputs ("\n", file);
+ enum attr_type type = get_attr_type (insn);
+ if (type == TYPE_LOAD || type == TYPE_STORE)
+ mips_last_74k_agen_insn = type;
}
+}
- assemble_name (file, fnname);
- fputs (":\n", file);
-#endif
+/* 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. */
- if (!flag_inhibit_size_directive)
- {
- /* .frame FRAMEREG, FRAMESIZE, RETREG */
- fprintf (file,
- "\t.frame\t%s,%ld,%s\t\t# vars= %ld, regs= %d/%d, args= %d, extra= %ld\n",
- (reg_names[(frame_pointer_needed)
- ? HARD_FRAME_POINTER_REGNUM : STACK_POINTER_REGNUM]),
- ((frame_pointer_needed && TARGET_MIPS16)
- ? ((long) tsize - current_function_outgoing_args_size)
- : (long) tsize),
- reg_names[GP_REG_FIRST + 31],
- current_frame_info.var_size,
- current_frame_info.num_gp,
- current_frame_info.num_fp,
- current_function_outgoing_args_size,
- current_frame_info.extra_size);
-
- /* .mask MASK, GPOFFSET; .fmask FPOFFSET */
- fprintf (file, "\t.mask\t0x%08lx,%ld\n\t.fmask\t0x%08lx,%ld\n",
- current_frame_info.mask,
- current_frame_info.gp_save_offset,
- current_frame_info.fmask,
- current_frame_info.fp_save_offset);
-
- /* Require:
- OLD_SP == *FRAMEREG + FRAMESIZE => can find old_sp from nominated FP reg.
- HIGHEST_GP_SAVED == *FRAMEREG + FRAMESIZE + GPOFFSET => can find saved regs. */
- }
-
- if (mips_entry && ! mips_can_use_return_insn ())
- {
- int save16 = BITSET_P (current_frame_info.mask, 16);
- int save17 = BITSET_P (current_frame_info.mask, 17);
- int save31 = BITSET_P (current_frame_info.mask, 31);
- int savearg = 0;
- rtx insn;
+static void
+mips_74k_agen_reorder (rtx *ready, int nready)
+{
+ int i;
+ int store_pos, load_pos;
- /* Look through the initial insns to see if any of them store
- the function parameters into the incoming parameter storage
- area. If they do, we delete the insn, and save the register
- using the entry pseudo-instruction instead. We don't try to
- look past a label, jump, or call. */
- for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
- {
- rtx note, set, src, dest, base, offset;
- int hireg;
+ store_pos = -1;
+ load_pos = -1;
- if (GET_CODE (insn) == CODE_LABEL
- || GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CALL_INSN)
+ for (i = nready - 1; i >= 0; i--)
+ {
+ 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;
- if (GET_CODE (insn) != INSN)
- continue;
- set = PATTERN (insn);
- if (GET_CODE (set) != SET)
- continue;
-
- /* An insn storing a function parameter will still have a
- REG_EQUIV note on it mentioning the argument pointer. */
- note = find_reg_note (insn, REG_EQUIV, NULL_RTX);
- if (note == NULL_RTX)
- continue;
- if (! reg_mentioned_p (arg_pointer_rtx, XEXP (note, 0)))
- continue;
-
- src = SET_SRC (set);
- if (GET_CODE (src) != REG
- || REGNO (src) < GP_REG_FIRST + 4
- || REGNO (src) > GP_REG_FIRST + 7)
- continue;
-
- dest = SET_DEST (set);
- if (GET_CODE (dest) != MEM)
- continue;
- if (GET_MODE_SIZE (GET_MODE (dest)) == (unsigned) UNITS_PER_WORD)
- ;
- else if (GET_MODE_SIZE (GET_MODE (dest)) == (unsigned)2 * UNITS_PER_WORD
- && REGNO (src) < GP_REG_FIRST + 7)
- ;
- else
- continue;
- offset = const0_rtx;
- base = eliminate_constant_term (XEXP (dest, 0), &offset);
- if (GET_CODE (base) != REG
- || GET_CODE (offset) != CONST_INT)
- continue;
- if (REGNO (base) == (unsigned) STACK_POINTER_REGNUM
- && INTVAL (offset) == tsize + (REGNO (src) - 4) * UNITS_PER_WORD)
- ;
- else if (REGNO (base) == (unsigned) HARD_FRAME_POINTER_REGNUM
- && (INTVAL (offset)
- == (tsize
- + (REGNO (src) - 4) * UNITS_PER_WORD
- - current_function_outgoing_args_size)))
- ;
- else
- continue;
-
- /* This insn stores a parameter onto the stack, in the same
- location where the entry pseudo-instruction will put it.
- Delete the insn, and arrange to tell the entry
- instruction to save the register. */
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
-
- hireg = (REGNO (src)
- + HARD_REGNO_NREGS (REGNO (src), GET_MODE (dest))
- - 1);
- if (hireg > savearg)
- savearg = hireg;
- }
- /* If this is a varargs function, we need to save all the
- registers onto the stack anyhow. */
- if (current_function_stdarg || current_function_varargs)
- savearg = GP_REG_FIRST + 7;
+ case TYPE_LOAD:
+ if (load_pos == -1)
+ load_pos = i;
+ break;
- fprintf (file, "\tentry\t");
- if (savearg > 0)
- {
- if (savearg == GP_REG_FIRST + 4)
- fprintf (file, "%s", reg_names[savearg]);
- else
- fprintf (file, "%s-%s", reg_names[GP_REG_FIRST + 4],
- reg_names[savearg]);
- }
- if (save16 || save17)
- {
- if (savearg > 0)
- fprintf (file, ",");
- fprintf (file, "%s", reg_names[GP_REG_FIRST + 16]);
- if (save17)
- fprintf (file, "-%s", reg_names[GP_REG_FIRST + 17]);
- }
- if (save31)
- {
- if (savearg > 0 || save16 || save17)
- fprintf (file, ",");
- fprintf (file, "%s", reg_names[GP_REG_FIRST + 31]);
- }
- fprintf (file, "\n");
+ default:
+ break;
+ }
}
- if (TARGET_ABICALLS && (mips_abi == ABI_32 || mips_abi == ABI_O64))
- {
- const char *const sp_str = reg_names[STACK_POINTER_REGNUM];
-
- fprintf (file, "\t.set\tnoreorder\n\t.cpload\t%s\n\t.set\treorder\n",
- reg_names[PIC_FUNCTION_ADDR_REGNUM]);
- if (tsize > 0)
- {
- fprintf (file, "\t%s\t%s,%s,%ld\n",
- (Pmode == DImode ? "dsubu" : "subu"),
- sp_str, sp_str, (long) tsize);
- fprintf (file, "\t.cprestore %ld\n", current_frame_info.args_size);
- }
+ if (load_pos == -1 || store_pos == -1)
+ return;
- if (dwarf2out_do_frame ())
- dwarf2out_def_cfa ("", STACK_POINTER_REGNUM, tsize);
+ switch (mips_last_74k_agen_insn)
+ {
+ 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;
}
}
\f
-/* Expand the prologue into a bunch of separate insns. */
+/* Implement TARGET_SCHED_INIT. */
-void
-mips_expand_prologue ()
+static void
+mips_sched_init (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ int max_ready ATTRIBUTE_UNUSED)
{
- int regno;
- HOST_WIDE_INT tsize;
- rtx tmp_rtx = 0;
- int last_arg_is_vararg_marker = 0;
- tree fndecl = current_function_decl;
- tree fntype = TREE_TYPE (fndecl);
- tree fnargs = DECL_ARGUMENTS (fndecl);
- rtx next_arg_reg;
- int i;
- tree next_arg;
- tree cur_arg;
- CUMULATIVE_ARGS args_so_far;
- rtx reg_18_save = NULL_RTX;
- int store_args_on_stack = (mips_abi == ABI_32 || mips_abi == ABI_O64)
- && (! mips_entry || mips_can_use_return_insn ());
-
- /* If struct value address is treated as the first argument, make it so. */
- if (aggregate_value_p (DECL_RESULT (fndecl))
- && ! current_function_returns_pcc_struct
- && struct_value_incoming_rtx == 0)
- {
- tree type = build_pointer_type (fntype);
- tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type);
-
- DECL_ARG_TYPE (function_result_decl) = type;
- TREE_CHAIN (function_result_decl) = fnargs;
- fnargs = function_result_decl;
- }
-
- /* For arguments passed in registers, find the register number
- of the first argument in the variable part of the argument list,
- otherwise GP_ARG_LAST+1. Note also if the last argument is
- the varargs special argument, and treat it as part of the
- variable arguments.
+ mips_macc_chains_last_hilo = 0;
+ vr4130_last_insn = 0;
+ mips_74k_agen_init (NULL_RTX);
- This is only needed if store_args_on_stack is true. */
-
- INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0);
- regno = GP_ARG_FIRST;
+ /* When scheduling for Loongson2, branch instructions go to ALU1,
+ therefore basic block is most likely to start with round-robin counter
+ pointed to ALU2. */
+ mips_ls2.alu1_turn_p = false;
+ mips_ls2.falu1_turn_p = true;
+}
- for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg)
- {
- tree passed_type = DECL_ARG_TYPE (cur_arg);
- enum machine_mode passed_mode = TYPE_MODE (passed_type);
- rtx entry_parm;
+/* Implement TARGET_SCHED_REORDER and TARGET_SCHED_REORDER2. */
- if (TREE_ADDRESSABLE (passed_type))
- {
- passed_type = build_pointer_type (passed_type);
- passed_mode = Pmode;
- }
+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);
- entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1);
+ if (reload_completed
+ && TUNE_MIPS4130
+ && !TARGET_VR4130_ALIGN
+ && *nreadyp > 1)
+ vr4130_reorder (ready, *nreadyp);
- FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1);
- next_arg = TREE_CHAIN (cur_arg);
+ if (TUNE_74K)
+ mips_74k_agen_reorder (ready, *nreadyp);
- if (entry_parm && store_args_on_stack)
- {
- if (next_arg == 0
- && DECL_NAME (cur_arg)
- && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
- "__builtin_va_alist"))
- || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)),
- "va_alist"))))
- {
- last_arg_is_vararg_marker = 1;
- break;
- }
- else
- {
- int words;
+ return mips_issue_rate ();
+}
- if (GET_CODE (entry_parm) != REG)
- abort ();
+/* Update round-robin counters for ALU1/2 and FALU1/2. */
- /* passed in a register, so will get homed automatically */
- if (GET_MODE (entry_parm) == BLKmode)
- words = (int_size_in_bytes (passed_type) + 3) / 4;
- else
- words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4;
+static void
+mips_ls2_variable_issue (rtx insn)
+{
+ if (mips_ls2.alu1_turn_p)
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.alu1_core_unit_code))
+ mips_ls2.alu1_turn_p = false;
+ }
+ else
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.alu2_core_unit_code))
+ mips_ls2.alu1_turn_p = true;
+ }
- regno = REGNO (entry_parm) + words - 1;
- }
- }
- else
- {
- regno = GP_ARG_LAST+1;
- break;
- }
+ if (mips_ls2.falu1_turn_p)
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.falu1_core_unit_code))
+ mips_ls2.falu1_turn_p = false;
+ }
+ else
+ {
+ if (cpu_unit_reservation_p (curr_state, mips_ls2.falu2_core_unit_code))
+ mips_ls2.falu1_turn_p = true;
}
- /* In order to pass small structures by value in registers compatibly with
- the MIPS compiler, we need to shift the value into the high part of the
- register. Function_arg has encoded a PARALLEL rtx, holding a vector of
- adjustments to be made as the next_arg_reg variable, so we split up the
- insns, and emit them separately. */
+ if (recog_memoized (insn) >= 0)
+ mips_ls2.cycle_has_multi_p |= (get_attr_type (insn) == TYPE_MULTI);
+}
- next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1);
- if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL)
- {
- rtvec adjust = XVEC (next_arg_reg, 0);
- int num = GET_NUM_ELEM (adjust);
+/* Implement TARGET_SCHED_VARIABLE_ISSUE. */
- for (i = 0; i < num; i++)
- {
- rtx insn, pattern;
-
- pattern = RTVEC_ELT (adjust, i);
- if (GET_CODE (pattern) != SET
- || GET_CODE (SET_SRC (pattern)) != ASHIFT)
- abort_with_insn (pattern, "insn is not a shift");
- PUT_CODE (SET_SRC (pattern), ASHIFTRT);
-
- insn = emit_insn (pattern);
-
- /* Global life information isn't valid at this point, so we
- can't check whether these shifts are actually used. Mark
- them MAYBE_DEAD so that flow2 will remove them, and not
- complain about dead code in the prologue. */
- REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
- REG_NOTES (insn));
- }
+static int
+mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+ rtx insn, int more)
+{
+ /* Ignore USEs and CLOBBERs; don't count them against the issue rate. */
+ if (USEFUL_INSN_P (insn))
+ {
+ more--;
+ if (!reload_completed && TUNE_MACC_CHAINS)
+ mips_macc_chains_record (insn);
+ vr4130_last_insn = insn;
+ if (TUNE_74K)
+ mips_74k_agen_init (insn);
+ else if (TUNE_LOONGSON_2EF)
+ mips_ls2_variable_issue (insn);
}
- tsize = compute_frame_size (get_frame_size ());
+ /* Instructions of type 'multi' should all be split before
+ the second scheduling pass. */
+ gcc_assert (!reload_completed
+ || recog_memoized (insn) < 0
+ || get_attr_type (insn) != TYPE_MULTI);
- /* If this function is a varargs function, store any registers that
- would normally hold arguments ($4 - $7) on the stack. */
- if (store_args_on_stack
- && ((TYPE_ARG_TYPES (fntype) != 0
- && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
- != void_type_node))
- || last_arg_is_vararg_marker))
- {
- int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD;
- rtx ptr = stack_pointer_rtx;
+ return more;
+}
+\f
+/* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY),
+ return the first operand of the associated PREF or PREFX insn. */
- /* If we are doing svr4-abi, sp has already been decremented by tsize. */
- if (TARGET_ABICALLS)
- offset += tsize;
+rtx
+mips_prefetch_cookie (rtx write, rtx locality)
+{
+ /* store_streamed / load_streamed. */
+ if (INTVAL (locality) <= 0)
+ return GEN_INT (INTVAL (write) + 4);
- for (; regno <= GP_ARG_LAST; regno++)
- {
- if (offset != 0)
- ptr = gen_rtx (PLUS, Pmode, stack_pointer_rtx, GEN_INT (offset));
- emit_move_insn (gen_rtx (MEM, gpr_mode, ptr),
- gen_rtx (REG, gpr_mode, regno));
+ /* store / load. */
+ if (INTVAL (locality) <= 2)
+ return write;
- offset += GET_MODE_SIZE (gpr_mode);
- }
- }
+ /* store_retained / load_retained. */
+ return GEN_INT (INTVAL (write) + 6);
+}
+\f
+/* Flags that indicate when a built-in function is available.
+
+ BUILTIN_AVAIL_NON_MIPS16
+ The function is available on the current target, but only
+ in non-MIPS16 mode. */
+#define BUILTIN_AVAIL_NON_MIPS16 1
+
+/* Declare an availability predicate for built-in functions that
+ require non-MIPS16 mode and also require COND to be true.
+ NAME is the main part of the predicate's name. */
+#define AVAIL_NON_MIPS16(NAME, COND) \
+ static unsigned int \
+ mips_builtin_avail_##NAME (void) \
+ { \
+ return (COND) ? BUILTIN_AVAIL_NON_MIPS16 : 0; \
+ }
+
+/* This structure describes a single built-in function. */
+struct mips_builtin_description {
+ /* The code of the main .md file instruction. See mips_builtin_type
+ for more information. */
+ enum insn_code icode;
+
+ /* The floating-point comparison code to use with ICODE, if any. */
+ enum mips_fp_condition cond;
+
+ /* The name of the built-in function. */
+ const char *name;
- /* If we are using the entry pseudo instruction, it will
- automatically subtract 32 from the stack pointer, so we don't
- need to. The entry pseudo instruction is emitted by
- function_prologue. */
- if (mips_entry && ! mips_can_use_return_insn ())
- {
- if (tsize > 0 && tsize <= 32 && frame_pointer_needed)
- {
- rtx insn;
+ /* Specifies how the function should be expanded. */
+ enum mips_builtin_type builtin_type;
- /* If we are using a frame pointer with a small stack frame,
- we need to initialize it here since it won't be done
- below. */
- if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
- {
- rtx incr = GEN_INT (current_function_outgoing_args_size);
- if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
- stack_pointer_rtx,
- incr));
- else
- insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- stack_pointer_rtx,
- incr));
- }
- else if (Pmode == DImode)
- insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
- stack_pointer_rtx));
- else
- insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
- stack_pointer_rtx));
+ /* The function's prototype. */
+ enum mips_function_type function_type;
- RTX_FRAME_RELATED_P (insn) = 1;
- }
+ /* Whether the function is available. */
+ unsigned int (*avail) (void);
+};
- /* We may need to save $18, if it is used to call a function
- which may return a floating point value. Set up a sequence
- of instructions to do so. Later on we emit them at the right
- moment. */
- if (TARGET_MIPS16 && BITSET_P (current_frame_info.mask, 18))
- {
- rtx reg_rtx = gen_rtx (REG, gpr_mode, GP_REG_FIRST + 3);
- long gp_offset, base_offset;
-
- gp_offset = current_frame_info.gp_sp_offset;
- if (BITSET_P (current_frame_info.mask, 16))
- gp_offset -= UNITS_PER_WORD;
- if (BITSET_P (current_frame_info.mask, 17))
- gp_offset -= UNITS_PER_WORD;
- if (BITSET_P (current_frame_info.mask, 31))
- gp_offset -= UNITS_PER_WORD;
- if (tsize > 32767)
- base_offset = tsize;
- else
- base_offset = 0;
- start_sequence ();
- emit_move_insn (reg_rtx,
- gen_rtx (REG, gpr_mode, GP_REG_FIRST + 18));
- emit_move_insn (gen_rtx (MEM, gpr_mode,
- gen_rtx (PLUS, Pmode, stack_pointer_rtx,
- GEN_INT (gp_offset
- - base_offset))),
- reg_rtx);
- reg_18_save = gen_sequence ();
- end_sequence ();
- }
+AVAIL_NON_MIPS16 (paired_single, TARGET_PAIRED_SINGLE_FLOAT)
+AVAIL_NON_MIPS16 (sb1_paired_single, TARGET_SB1 && TARGET_PAIRED_SINGLE_FLOAT)
+AVAIL_NON_MIPS16 (mips3d, TARGET_MIPS3D)
+AVAIL_NON_MIPS16 (dsp, TARGET_DSP)
+AVAIL_NON_MIPS16 (dspr2, TARGET_DSPR2)
+AVAIL_NON_MIPS16 (dsp_32, !TARGET_64BIT && TARGET_DSP)
+AVAIL_NON_MIPS16 (dspr2_32, !TARGET_64BIT && TARGET_DSPR2)
+AVAIL_NON_MIPS16 (loongson, TARGET_LOONGSON_VECTORS)
+AVAIL_NON_MIPS16 (cache, TARGET_CACHE_BUILTIN)
+
+/* Construct a mips_builtin_description from the given arguments.
+
+ INSN is the name of the associated instruction pattern, without the
+ leading CODE_FOR_mips_.
+
+ CODE is the floating-point condition code associated with the
+ function. It can be 'f' if the field is not applicable.
+
+ NAME is the name of the function itself, without the leading
+ "__builtin_mips_".
+
+ BUILTIN_TYPE and FUNCTION_TYPE are mips_builtin_description fields.
+
+ AVAIL is the name of the availability predicate, without the leading
+ mips_builtin_avail_. */
+#define MIPS_BUILTIN(INSN, COND, NAME, BUILTIN_TYPE, \
+ FUNCTION_TYPE, AVAIL) \
+ { CODE_FOR_mips_ ## INSN, MIPS_FP_COND_ ## COND, \
+ "__builtin_mips_" NAME, BUILTIN_TYPE, FUNCTION_TYPE, \
+ mips_builtin_avail_ ## AVAIL }
+
+/* Define __builtin_mips_<INSN>, which is a MIPS_BUILTIN_DIRECT function
+ mapped to instruction CODE_FOR_mips_<INSN>, FUNCTION_TYPE and AVAIL
+ are as for MIPS_BUILTIN. */
+#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
+ MIPS_BUILTIN (INSN, f, #INSN, MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_mips_<INSN>_<COND>_{s,d} functions, both of which
+ are subject to mips_builtin_avail_<AVAIL>. */
+#define CMP_SCALAR_BUILTINS(INSN, COND, AVAIL) \
+ MIPS_BUILTIN (INSN ## _cond_s, COND, #INSN "_" #COND "_s", \
+ MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, AVAIL), \
+ MIPS_BUILTIN (INSN ## _cond_d, COND, #INSN "_" #COND "_d", \
+ MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, AVAIL)
+
+/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps.
+ The lower and upper forms are subject to mips_builtin_avail_<AVAIL>
+ while the any and all forms are subject to mips_builtin_avail_mips3d. */
+#define CMP_PS_BUILTINS(INSN, COND, AVAIL) \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "any_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, \
+ mips3d), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "all_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, \
+ mips3d), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "lower_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, \
+ AVAIL), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "upper_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, \
+ AVAIL)
+
+/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s. The functions
+ are subject to mips_builtin_avail_mips3d. */
+#define CMP_4S_BUILTINS(INSN, COND) \
+ MIPS_BUILTIN (INSN ## _cond_4s, COND, "any_" #INSN "_" #COND "_4s", \
+ MIPS_BUILTIN_CMP_ANY, \
+ MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, mips3d), \
+ MIPS_BUILTIN (INSN ## _cond_4s, COND, "all_" #INSN "_" #COND "_4s", \
+ MIPS_BUILTIN_CMP_ALL, \
+ MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, mips3d)
+
+/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps. The comparison
+ instruction requires mips_builtin_avail_<AVAIL>. */
+#define MOVTF_BUILTINS(INSN, COND, AVAIL) \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "movt_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
+ AVAIL), \
+ MIPS_BUILTIN (INSN ## _cond_ps, COND, "movf_" #INSN "_" #COND "_ps", \
+ MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
+ AVAIL)
+
+/* Define all the built-in functions related to C.cond.fmt condition COND. */
+#define CMP_BUILTINS(COND) \
+ MOVTF_BUILTINS (c, COND, paired_single), \
+ MOVTF_BUILTINS (cabs, COND, mips3d), \
+ CMP_SCALAR_BUILTINS (cabs, COND, mips3d), \
+ CMP_PS_BUILTINS (c, COND, paired_single), \
+ CMP_PS_BUILTINS (cabs, COND, mips3d), \
+ CMP_4S_BUILTINS (c, COND), \
+ CMP_4S_BUILTINS (cabs, COND)
+
+/* Define __builtin_mips_<INSN>, which is a MIPS_BUILTIN_DIRECT_NO_TARGET
+ function mapped to instruction CODE_FOR_mips_<INSN>, FUNCTION_TYPE
+ and AVAIL are as for MIPS_BUILTIN. */
+#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, AVAIL) \
+ MIPS_BUILTIN (INSN, f, #INSN, MIPS_BUILTIN_DIRECT_NO_TARGET, \
+ FUNCTION_TYPE, AVAIL)
+
+/* Define __builtin_mips_bposge<VALUE>. <VALUE> is 32 for the MIPS32 DSP
+ branch instruction. AVAIL is as for MIPS_BUILTIN. */
+#define BPOSGE_BUILTIN(VALUE, AVAIL) \
+ MIPS_BUILTIN (bposge, f, "bposge" #VALUE, \
+ MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, AVAIL)
+
+/* Define a Loongson MIPS_BUILTIN_DIRECT function __builtin_loongson_<FN_NAME>
+ for instruction CODE_FOR_loongson_<INSN>. FUNCTION_TYPE is a
+ builtin_description field. */
+#define LOONGSON_BUILTIN_ALIAS(INSN, FN_NAME, FUNCTION_TYPE) \
+ { CODE_FOR_loongson_ ## INSN, 0, "__builtin_loongson_" #FN_NAME, \
+ MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, mips_builtin_avail_loongson }
+
+/* Define a Loongson MIPS_BUILTIN_DIRECT function __builtin_loongson_<INSN>
+ for instruction CODE_FOR_loongson_<INSN>. FUNCTION_TYPE is a
+ builtin_description field. */
+#define LOONGSON_BUILTIN(INSN, FUNCTION_TYPE) \
+ LOONGSON_BUILTIN_ALIAS (INSN, INSN, FUNCTION_TYPE)
+
+/* Like LOONGSON_BUILTIN, but add _<SUFFIX> to the end of the function name.
+ We use functions of this form when the same insn can be usefully applied
+ to more than one datatype. */
+#define LOONGSON_BUILTIN_SUFFIX(INSN, SUFFIX, FUNCTION_TYPE) \
+ LOONGSON_BUILTIN_ALIAS (INSN, INSN ## _ ## SUFFIX, FUNCTION_TYPE)
+
+#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2
+#define CODE_FOR_mips_addq_ph CODE_FOR_addv2hi3
+#define CODE_FOR_mips_addu_qb CODE_FOR_addv4qi3
+#define CODE_FOR_mips_subq_ph CODE_FOR_subv2hi3
+#define CODE_FOR_mips_subu_qb CODE_FOR_subv4qi3
+#define CODE_FOR_mips_mul_ph CODE_FOR_mulv2hi3
+
+#define CODE_FOR_loongson_packsswh CODE_FOR_vec_pack_ssat_v2si
+#define CODE_FOR_loongson_packsshb CODE_FOR_vec_pack_ssat_v4hi
+#define CODE_FOR_loongson_packushb CODE_FOR_vec_pack_usat_v4hi
+#define CODE_FOR_loongson_paddw CODE_FOR_addv2si3
+#define CODE_FOR_loongson_paddh CODE_FOR_addv4hi3
+#define CODE_FOR_loongson_paddb CODE_FOR_addv8qi3
+#define CODE_FOR_loongson_paddsh CODE_FOR_ssaddv4hi3
+#define CODE_FOR_loongson_paddsb CODE_FOR_ssaddv8qi3
+#define CODE_FOR_loongson_paddush CODE_FOR_usaddv4hi3
+#define CODE_FOR_loongson_paddusb CODE_FOR_usaddv8qi3
+#define CODE_FOR_loongson_pmaxsh CODE_FOR_smaxv4hi3
+#define CODE_FOR_loongson_pmaxub CODE_FOR_umaxv8qi3
+#define CODE_FOR_loongson_pminsh CODE_FOR_sminv4hi3
+#define CODE_FOR_loongson_pminub CODE_FOR_uminv8qi3
+#define CODE_FOR_loongson_pmulhuh CODE_FOR_umulv4hi3_highpart
+#define CODE_FOR_loongson_pmulhh CODE_FOR_smulv4hi3_highpart
+#define CODE_FOR_loongson_psubw CODE_FOR_subv2si3
+#define CODE_FOR_loongson_psubh CODE_FOR_subv4hi3
+#define CODE_FOR_loongson_psubb CODE_FOR_subv8qi3
+#define CODE_FOR_loongson_psubsh CODE_FOR_sssubv4hi3
+#define CODE_FOR_loongson_psubsb CODE_FOR_sssubv8qi3
+#define CODE_FOR_loongson_psubush CODE_FOR_ussubv4hi3
+#define CODE_FOR_loongson_psubusb CODE_FOR_ussubv8qi3
+#define CODE_FOR_loongson_punpckhbh CODE_FOR_vec_interleave_highv8qi
+#define CODE_FOR_loongson_punpckhhw CODE_FOR_vec_interleave_highv4hi
+#define CODE_FOR_loongson_punpckhwd CODE_FOR_vec_interleave_highv2si
+#define CODE_FOR_loongson_punpcklbh CODE_FOR_vec_interleave_lowv8qi
+#define CODE_FOR_loongson_punpcklhw CODE_FOR_vec_interleave_lowv4hi
+#define CODE_FOR_loongson_punpcklwd CODE_FOR_vec_interleave_lowv2si
+
+static const struct mips_builtin_description mips_builtins[] = {
+ DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, paired_single),
+ DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, paired_single),
+ DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, paired_single),
+ DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, paired_single),
+ DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, paired_single),
+
+ DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT, paired_single),
+ DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+ DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+ DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+ DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, mips3d),
+
+ DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, mips3d),
+ DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, mips3d),
+ DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+ DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, mips3d),
+ DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, mips3d),
+ DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+
+ DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, mips3d),
+ DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, mips3d),
+ DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, mips3d),
+ DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, mips3d),
+ DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, mips3d),
+ DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, mips3d),
+
+ MIPS_FP_CONDITIONS (CMP_BUILTINS),
+
+ /* Built-in functions for the SB-1 processor. */
+ DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, sb1_paired_single),
+
+ /* Built-in functions for the DSP ASE (32-bit and 64-bit). */
+ DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, dsp),
+ DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, dsp),
+ DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, dsp),
+ DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, dsp),
+ DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, dsp),
+ DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, dsp),
+ DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, dsp),
+ DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, dsp),
+ DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dsp),
+ DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dsp),
+ DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, dsp),
+ DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, dsp),
+ DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_POINTER_SI, dsp),
+ DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_POINTER_SI, dsp),
+ DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_POINTER_SI, dsp),
+ BPOSGE_BUILTIN (32, dsp),
+
+ /* The following are for the MIPS DSP ASE REV 2 (32-bit and 64-bit). */
+ DIRECT_BUILTIN (absq_s_qb, MIPS_V4QI_FTYPE_V4QI, dspr2),
+ DIRECT_BUILTIN (addu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (addu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (adduh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (adduh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (append, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (balign, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (cmpgdu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (cmpgdu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (cmpgdu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (mul_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (mul_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (mulq_rs_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (mulq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (mulq_s_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (precr_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (precr_sra_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (precr_sra_r_ph_w, MIPS_V2HI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (prepend, MIPS_SI_FTYPE_SI_SI_SI, dspr2),
+ DIRECT_BUILTIN (shra_qb, MIPS_V4QI_FTYPE_V4QI_SI, dspr2),
+ DIRECT_BUILTIN (shra_r_qb, MIPS_V4QI_FTYPE_V4QI_SI, dspr2),
+ DIRECT_BUILTIN (shrl_ph, MIPS_V2HI_FTYPE_V2HI_SI, dspr2),
+ DIRECT_BUILTIN (subu_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subu_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subuh_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (subuh_r_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, dspr2),
+ DIRECT_BUILTIN (addqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (addqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (addqh_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (addqh_r_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (subqh_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subqh_r_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, dspr2),
+ DIRECT_BUILTIN (subqh_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+ DIRECT_BUILTIN (subqh_r_w, MIPS_SI_FTYPE_SI_SI, dspr2),
+
+ /* Built-in functions for the DSP ASE (32-bit only). */
+ DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, dsp_32),
+ DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, dsp_32),
+ DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, dsp_32),
+ DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, dsp_32),
+ DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, dsp_32),
+
+ /* The following are for the MIPS DSP ASE REV 2 (32-bit only). */
+ DIRECT_BUILTIN (dpa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dps_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (madd, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
+ DIRECT_BUILTIN (maddu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
+ DIRECT_BUILTIN (msub, MIPS_DI_FTYPE_DI_SI_SI, dspr2_32),
+ DIRECT_BUILTIN (msubu, MIPS_DI_FTYPE_DI_USI_USI, dspr2_32),
+ DIRECT_BUILTIN (mulsa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (mult, MIPS_DI_FTYPE_SI_SI, dspr2_32),
+ DIRECT_BUILTIN (multu, MIPS_DI_FTYPE_USI_USI, dspr2_32),
+ DIRECT_BUILTIN (dpax_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpsx_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpaqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpaqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpsqx_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+ DIRECT_BUILTIN (dpsqx_sa_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, dspr2_32),
+
+ /* Builtin functions for ST Microelectronics Loongson-2E/2F cores. */
+ LOONGSON_BUILTIN (packsswh, MIPS_V4HI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN (packsshb, MIPS_V8QI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (packushb, MIPS_UV8QI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (paddw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (paddh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (paddb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (paddw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (paddh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (paddb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (paddd, u, MIPS_UDI_FTYPE_UDI_UDI),
+ LOONGSON_BUILTIN_SUFFIX (paddd, s, MIPS_DI_FTYPE_DI_DI),
+ LOONGSON_BUILTIN (paddsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (paddsb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN (paddush, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (paddusb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_ALIAS (pandn_d, pandn_ud, MIPS_UDI_FTYPE_UDI_UDI),
+ LOONGSON_BUILTIN_ALIAS (pandn_w, pandn_uw, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_ALIAS (pandn_h, pandn_uh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_ALIAS (pandn_b, pandn_ub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_ALIAS (pandn_d, pandn_sd, MIPS_DI_FTYPE_DI_DI),
+ LOONGSON_BUILTIN_ALIAS (pandn_w, pandn_sw, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_ALIAS (pandn_h, pandn_sh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_ALIAS (pandn_b, pandn_sb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN (pavgh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (pavgb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpeqb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgth, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgth, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pcmpgtb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (pextrh, u, MIPS_UV4HI_FTYPE_UV4HI_USI),
+ LOONGSON_BUILTIN_SUFFIX (pextrh, s, MIPS_V4HI_FTYPE_V4HI_USI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_0, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_1, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_2, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_3, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_0, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_1, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_2, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (pinsrh_3, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmaddhw, MIPS_V2SI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmaxsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmaxub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN (pminsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pminub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pmovmskb, u, MIPS_UV8QI_FTYPE_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pmovmskb, s, MIPS_V8QI_FTYPE_V8QI),
+ LOONGSON_BUILTIN (pmulhuh, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (pmulhh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmullh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (pmuluw, MIPS_UDI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN (pasubub, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN (biadd, MIPS_UV4HI_FTYPE_UV8QI),
+ LOONGSON_BUILTIN (psadbh, MIPS_UV4HI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (pshufh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (pshufh, s, MIPS_V4HI_FTYPE_V4HI_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllh, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllh, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psllw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrah, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrah, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psraw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psraw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlh, u, MIPS_UV4HI_FTYPE_UV4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlh, s, MIPS_V4HI_FTYPE_V4HI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlw, u, MIPS_UV2SI_FTYPE_UV2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psrlw, s, MIPS_V2SI_FTYPE_V2SI_UQI),
+ LOONGSON_BUILTIN_SUFFIX (psubw, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (psubh, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (psubb, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (psubw, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (psubh, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (psubb, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (psubd, u, MIPS_UDI_FTYPE_UDI_UDI),
+ LOONGSON_BUILTIN_SUFFIX (psubd, s, MIPS_DI_FTYPE_DI_DI),
+ LOONGSON_BUILTIN (psubsh, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN (psubsb, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN (psubush, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN (psubusb, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhbh, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhhw, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhwd, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhbh, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhhw, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpckhwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklbh, u, MIPS_UV8QI_FTYPE_UV8QI_UV8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklhw, u, MIPS_UV4HI_FTYPE_UV4HI_UV4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklwd, u, MIPS_UV2SI_FTYPE_UV2SI_UV2SI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklbh, s, MIPS_V8QI_FTYPE_V8QI_V8QI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklhw, s, MIPS_V4HI_FTYPE_V4HI_V4HI),
+ LOONGSON_BUILTIN_SUFFIX (punpcklwd, s, MIPS_V2SI_FTYPE_V2SI_V2SI),
+
+ /* Sundry other built-in functions. */
+ DIRECT_NO_TARGET_BUILTIN (cache, MIPS_VOID_FTYPE_SI_CVPOINTER, cache)
+};
- if (tsize > 32)
- tsize -= 32;
- else
- {
- tsize = 0;
- if (reg_18_save != NULL_RTX)
- emit_insn (reg_18_save);
- }
- }
+/* MODE is a vector mode whose elements have type TYPE. Return the type
+ of the vector itself. */
- if (tsize > 0)
- {
- rtx tsize_rtx = GEN_INT (tsize);
+static tree
+mips_builtin_vector_type (tree type, enum machine_mode mode)
+{
+ static tree types[2 * (int) MAX_MACHINE_MODE];
+ int mode_index;
- /* If we are doing svr4-abi, sp move is done by
- function_prologue. In mips16 mode with a large frame, we
- save the registers before adjusting the stack. */
- if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
- && (!TARGET_MIPS16 || tsize <= 32767))
- {
- rtx adjustment_rtx, insn, dwarf_pattern;
+ mode_index = (int) mode;
- if (tsize > 32767)
- {
- adjustment_rtx = gen_rtx (REG, Pmode, MIPS_TEMP1_REGNUM);
- emit_move_insn (adjustment_rtx, tsize_rtx);
- }
- else
- adjustment_rtx = tsize_rtx;
+ if (TREE_CODE (type) == INTEGER_TYPE && TYPE_UNSIGNED (type))
+ mode_index += MAX_MACHINE_MODE;
- if (Pmode == DImode)
- insn = emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx,
- adjustment_rtx));
- else
- insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx,
- adjustment_rtx));
+ if (types[mode_index] == NULL_TREE)
+ types[mode_index] = build_vector_type_for_mode (type, mode);
+ return types[mode_index];
+}
- dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx,
- plus_constant (stack_pointer_rtx,
- -tsize));
+/* Return a type for 'const volatile void *'. */
- mips_annotate_frame_insn (insn, dwarf_pattern);
- }
+static tree
+mips_build_cvpointer_type (void)
+{
+ static tree cache;
- if (! mips_entry)
- save_restore_insns (1, tmp_rtx, tsize, (FILE *)0);
- else if (reg_18_save != NULL_RTX)
- emit_insn (reg_18_save);
+ if (cache == NULL_TREE)
+ cache = build_pointer_type (build_qualified_type
+ (void_type_node,
+ TYPE_QUAL_CONST | TYPE_QUAL_VOLATILE));
+ return cache;
+}
- if ((!TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
- && TARGET_MIPS16
- && tsize > 32767)
- {
- rtx reg_rtx;
-
- if (!frame_pointer_needed)
- abort ();
-
- reg_rtx = gen_rtx (REG, Pmode, 3);
- emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
- emit_move_insn (reg_rtx, tsize_rtx);
- if (Pmode == DImode)
- emit_insn (gen_subdi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- reg_rtx));
- else
- emit_insn (gen_subsi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- reg_rtx));
- emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
- }
+/* Source-level argument types. */
+#define MIPS_ATYPE_VOID void_type_node
+#define MIPS_ATYPE_INT integer_type_node
+#define MIPS_ATYPE_POINTER ptr_type_node
+#define MIPS_ATYPE_CVPOINTER mips_build_cvpointer_type ()
- if (frame_pointer_needed)
- {
- rtx insn = 0;
-
- /* On the mips16, we encourage the use of unextended
- instructions when using the frame pointer by pointing the
- frame pointer ahead of the argument space allocated on
- the stack. */
- if ((! TARGET_ABICALLS || (mips_abi != ABI_32 && mips_abi != ABI_O64))
- && TARGET_MIPS16
- && tsize > 32767)
- {
- /* In this case, we have already copied the stack
- pointer into the frame pointer, above. We need only
- adjust for the outgoing argument size. */
- if (current_function_outgoing_args_size != 0)
- {
- rtx incr = GEN_INT (current_function_outgoing_args_size);
- if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- incr));
- else
- insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- incr));
- }
- }
- else if (TARGET_MIPS16 && current_function_outgoing_args_size != 0)
- {
- rtx incr = GEN_INT (current_function_outgoing_args_size);
- if (Pmode == DImode)
- insn = emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
- stack_pointer_rtx,
- incr));
- else
- insn = emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- stack_pointer_rtx,
- incr));
- }
- else if (Pmode == DImode)
- insn = emit_insn (gen_movdi (hard_frame_pointer_rtx,
- stack_pointer_rtx));
- else
- insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
- stack_pointer_rtx));
+/* Standard mode-based argument types. */
+#define MIPS_ATYPE_UQI unsigned_intQI_type_node
+#define MIPS_ATYPE_SI intSI_type_node
+#define MIPS_ATYPE_USI unsigned_intSI_type_node
+#define MIPS_ATYPE_DI intDI_type_node
+#define MIPS_ATYPE_UDI unsigned_intDI_type_node
+#define MIPS_ATYPE_SF float_type_node
+#define MIPS_ATYPE_DF double_type_node
- if (insn)
- RTX_FRAME_RELATED_P (insn) = 1;
- }
+/* Vector argument types. */
+#define MIPS_ATYPE_V2SF mips_builtin_vector_type (float_type_node, V2SFmode)
+#define MIPS_ATYPE_V2HI mips_builtin_vector_type (intHI_type_node, V2HImode)
+#define MIPS_ATYPE_V2SI mips_builtin_vector_type (intSI_type_node, V2SImode)
+#define MIPS_ATYPE_V4QI mips_builtin_vector_type (intQI_type_node, V4QImode)
+#define MIPS_ATYPE_V4HI mips_builtin_vector_type (intHI_type_node, V4HImode)
+#define MIPS_ATYPE_V8QI mips_builtin_vector_type (intQI_type_node, V8QImode)
+#define MIPS_ATYPE_UV2SI \
+ mips_builtin_vector_type (unsigned_intSI_type_node, V2SImode)
+#define MIPS_ATYPE_UV4HI \
+ mips_builtin_vector_type (unsigned_intHI_type_node, V4HImode)
+#define MIPS_ATYPE_UV8QI \
+ mips_builtin_vector_type (unsigned_intQI_type_node, V8QImode)
- if (TARGET_ABICALLS && (mips_abi != ABI_32 && mips_abi != ABI_O64))
- emit_insn (gen_loadgp (XEXP (DECL_RTL (current_function_decl), 0),
- gen_rtx_REG (DImode, 25)));
- }
+/* MIPS_FTYPE_ATYPESN takes N MIPS_FTYPES-like type codes and lists
+ their associated MIPS_ATYPEs. */
+#define MIPS_FTYPE_ATYPES1(A, B) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B
- /* If we are profiling, make sure no instructions are scheduled before
- the call to mcount. */
+#define MIPS_FTYPE_ATYPES2(A, B, C) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C
- if (current_function_profile)
- emit_insn (gen_blockage ());
+#define MIPS_FTYPE_ATYPES3(A, B, C, D) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C, MIPS_ATYPE_##D
+
+#define MIPS_FTYPE_ATYPES4(A, B, C, D, E) \
+ MIPS_ATYPE_##A, MIPS_ATYPE_##B, MIPS_ATYPE_##C, MIPS_ATYPE_##D, \
+ MIPS_ATYPE_##E
+
+/* Return the function type associated with function prototype TYPE. */
+
+static tree
+mips_build_function_type (enum mips_function_type type)
+{
+ static tree types[(int) MIPS_MAX_FTYPE_MAX];
+
+ if (types[(int) type] == NULL_TREE)
+ switch (type)
+ {
+#define DEF_MIPS_FTYPE(NUM, ARGS) \
+ case MIPS_FTYPE_NAME##NUM ARGS: \
+ types[(int) type] \
+ = build_function_type_list (MIPS_FTYPE_ATYPES##NUM ARGS, \
+ NULL_TREE); \
+ break;
+#include "config/mips/mips-ftypes.def"
+#undef DEF_MIPS_FTYPE
+ default:
+ gcc_unreachable ();
+ }
+
+ return types[(int) type];
}
-\f
-/* Do any necessary cleanup after a function to restore stack, frame,
- and regs. */
-#define RA_MASK BITMASK_HIGH /* 1 << 31 */
-#define PIC_OFFSET_TABLE_MASK (1 << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))
+/* Implement TARGET_INIT_BUILTINS. */
static void
-mips_output_function_epilogue (file, size)
- FILE *file ATTRIBUTE_UNUSED;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+mips_init_builtins (void)
{
- const char *fnname = ""; /* FIXME: Correct initialisation? */
-
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- /* Get the function name the same way that toplev.c does before calling
- assemble_start_function. This is needed so that the name used here
- exactly matches the name used in ASM_DECLARE_FUNCTION_NAME. */
- fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+ const struct mips_builtin_description *d;
+ unsigned int i;
- if (!flag_inhibit_size_directive)
+ /* Iterate through all of the bdesc arrays, initializing all of the
+ builtin functions. */
+ for (i = 0; i < ARRAY_SIZE (mips_builtins); i++)
{
- fputs ("\t.end\t", file);
- assemble_name (file, fnname);
- fputs ("\n", file);
+ d = &mips_builtins[i];
+ if (d->avail ())
+ add_builtin_function (d->name,
+ mips_build_function_type (d->function_type),
+ i, BUILT_IN_MD, NULL, NULL);
}
-#endif
+}
- if (TARGET_STATS)
- {
- int num_gp_regs = current_frame_info.gp_reg_size / 4;
- int num_fp_regs = current_frame_info.fp_reg_size / 8;
- int num_regs = num_gp_regs + num_fp_regs;
- const char *name = fnname;
+/* Take argument ARGNO from EXP's argument list and convert it into a
+ form suitable for input operand OPNO of instruction ICODE. Return the
+ value. */
- if (name[0] == '*')
- name++;
+static rtx
+mips_prepare_builtin_arg (enum insn_code icode,
+ unsigned int opno, tree exp, unsigned int argno)
+{
+ tree arg;
+ rtx value;
+ enum machine_mode mode;
- dslots_load_total += num_regs;
+ arg = CALL_EXPR_ARG (exp, argno);
+ value = expand_normal (arg);
+ mode = insn_data[icode].operand[opno].mode;
+ if (!insn_data[icode].operand[opno].predicate (value, mode))
+ {
+ /* We need to get the mode from ARG for two reasons:
- fprintf (stderr,
- "%-20s fp=%c leaf=%c alloca=%c setjmp=%c stack=%4ld arg=%3d reg=%2d/%d delay=%3d/%3dL %3d/%3dJ refs=%3d/%3d/%3d",
- name, frame_pointer_needed ? 'y' : 'n',
- (current_frame_info.mask & RA_MASK) != 0 ? 'n' : 'y',
- current_function_calls_alloca ? 'y' : 'n',
- current_function_calls_setjmp ? 'y' : 'n',
- current_frame_info.total_size,
- current_function_outgoing_args_size, num_gp_regs, num_fp_regs,
- dslots_load_total, dslots_load_filled,
- dslots_jump_total, dslots_jump_filled,
- num_refs[0], num_refs[1], num_refs[2]);
+ - to cope with address operands, where MODE is the mode of the
+ memory, rather than of VALUE itself.
- if (HALF_PIC_NUMBER_PTRS > prev_half_pic_ptrs)
- {
- fprintf (stderr,
- " half-pic=%3d", HALF_PIC_NUMBER_PTRS - prev_half_pic_ptrs);
- prev_half_pic_ptrs = HALF_PIC_NUMBER_PTRS;
- }
+ - to cope with special predicates like pmode_register_operand,
+ where MODE is VOIDmode. */
+ value = copy_to_mode_reg (TYPE_MODE (TREE_TYPE (arg)), value);
- if (HALF_PIC_NUMBER_REFS > prev_half_pic_refs)
+ /* Check the predicate again. */
+ if (!insn_data[icode].operand[opno].predicate (value, mode))
{
- fprintf (stderr,
- " pic-ref=%3d", HALF_PIC_NUMBER_REFS - prev_half_pic_refs);
- prev_half_pic_refs = HALF_PIC_NUMBER_REFS;
+ error ("invalid argument to built-in function");
+ return const0_rtx;
}
-
- fputc ('\n', stderr);
}
- /* Reset state info for each function. */
- inside_function = 0;
- ignore_line_number = 0;
- dslots_load_total = 0;
- dslots_jump_total = 0;
- dslots_load_filled = 0;
- dslots_jump_filled = 0;
- num_refs[0] = 0;
- num_refs[1] = 0;
- num_refs[2] = 0;
- mips_load_reg = 0;
- mips_load_reg2 = 0;
- current_frame_info = zero_frame_info;
+ return value;
+}
- while (string_constants != NULL)
- {
- struct string_constant *next;
+/* Return an rtx suitable for output operand OP of instruction ICODE.
+ If TARGET is non-null, try to use it where possible. */
- next = string_constants->next;
- free (string_constants);
- string_constants = next;
- }
+static rtx
+mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target)
+{
+ enum machine_mode mode;
- /* Restore the output file if optimizing the GP (optimizing the GP causes
- the text to be diverted to a tempfile, so that data decls come before
- references to the data). */
- if (TARGET_FILE_SWITCHING)
- {
- asm_out_file = asm_out_data_file;
- data_section ();
- }
+ mode = insn_data[icode].operand[op].mode;
+ if (target == 0 || !insn_data[icode].operand[op].predicate (target, mode))
+ target = gen_reg_rtx (mode);
+
+ return target;
}
-\f
-/* Expand the epilogue into a bunch of separate insns. */
-void
-mips_expand_epilogue ()
+/* Expand a MIPS_BUILTIN_DIRECT or MIPS_BUILTIN_DIRECT_NO_TARGET function;
+ HAS_TARGET_P says which. EXP is the CALL_EXPR that calls the function
+ and ICODE is the code of the associated .md pattern. TARGET, if nonnull,
+ suggests a good place to put the result. */
+
+static rtx
+mips_expand_builtin_direct (enum insn_code icode, rtx target, tree exp,
+ bool has_target_p)
{
- HOST_WIDE_INT tsize = current_frame_info.total_size;
- rtx tsize_rtx = GEN_INT (tsize);
- rtx tmp_rtx = (rtx)0;
+ rtx ops[MAX_RECOG_OPERANDS];
+ int opno, argno;
- if (mips_can_use_return_insn ())
+ /* Map any target to operand 0. */
+ opno = 0;
+ if (has_target_p)
{
- emit_insn (gen_return ());
- return;
+ target = mips_prepare_builtin_target (icode, opno, target);
+ ops[opno] = target;
+ opno++;
}
- if (mips_entry && ! mips_can_use_return_insn ())
- tsize -= 32;
+ /* Map the arguments to the other operands. The n_operands value
+ for an expander includes match_dups and match_scratches as well as
+ match_operands, so n_operands is only an upper bound on the number
+ of arguments to the expander function. */
+ gcc_assert (opno + call_expr_nargs (exp) <= insn_data[icode].n_operands);
+ for (argno = 0; argno < call_expr_nargs (exp); argno++, opno++)
+ ops[opno] = mips_prepare_builtin_arg (icode, opno, exp, argno);
- if (tsize > 32767 && ! TARGET_MIPS16)
+ switch (opno)
{
- tmp_rtx = gen_rtx_REG (Pmode, MIPS_TEMP1_REGNUM);
- emit_move_insn (tmp_rtx, tsize_rtx);
- tsize_rtx = tmp_rtx;
+ case 2:
+ emit_insn (GEN_FCN (icode) (ops[0], ops[1]));
+ break;
+
+ case 3:
+ emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2]));
+ break;
+
+ case 4:
+ emit_insn (GEN_FCN (icode) (ops[0], ops[1], ops[2], ops[3]));
+ break;
+
+ default:
+ gcc_unreachable ();
}
+ return target;
+}
- if (tsize > 0)
- {
- long orig_tsize = tsize;
+/* Expand a __builtin_mips_movt_*_ps or __builtin_mips_movf_*_ps
+ function; TYPE says which. EXP is the CALL_EXPR that calls the
+ function, ICODE is the instruction that should be used to compare
+ the first two arguments, and COND is the condition it should test.
+ TARGET, if nonnull, suggests a good place to put the result. */
- if (frame_pointer_needed)
- {
- emit_insn (gen_blockage ());
+static rtx
+mips_expand_builtin_movtf (enum mips_builtin_type type,
+ enum insn_code icode, enum mips_fp_condition cond,
+ rtx target, tree exp)
+{
+ rtx cmp_result, op0, op1;
- /* On the mips16, the frame pointer is offset from the stack
- pointer by current_function_outgoing_args_size. We
- account for that by changing tsize. Note that this can
- actually make tsize negative. */
- if (TARGET_MIPS16)
- {
- tsize -= current_function_outgoing_args_size;
+ cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+ op0 = mips_prepare_builtin_arg (icode, 1, exp, 0);
+ op1 = mips_prepare_builtin_arg (icode, 2, exp, 1);
+ emit_insn (GEN_FCN (icode) (cmp_result, op0, op1, GEN_INT (cond)));
- /* If we have a large frame, it's easier to add to $6
- than to $sp, since the mips16 has no instruction to
- add a register to $sp. */
- if (orig_tsize > 32767)
- {
- rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
-
- emit_move_insn (g6_rtx, GEN_INT (tsize));
- if (Pmode == DImode)
- emit_insn (gen_adddi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- g6_rtx));
- else
- emit_insn (gen_addsi3 (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- g6_rtx));
- tsize = 0;
- }
+ icode = CODE_FOR_mips_cond_move_tf_ps;
+ target = mips_prepare_builtin_target (icode, 0, target);
+ if (type == MIPS_BUILTIN_MOVT)
+ {
+ op1 = mips_prepare_builtin_arg (icode, 2, exp, 2);
+ op0 = mips_prepare_builtin_arg (icode, 1, exp, 3);
+ }
+ else
+ {
+ op0 = mips_prepare_builtin_arg (icode, 1, exp, 2);
+ op1 = mips_prepare_builtin_arg (icode, 2, exp, 3);
+ }
+ emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result));
+ return target;
+}
- if (tsize && tsize != orig_tsize)
- tsize_rtx = GEN_INT (tsize);
- }
+/* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE
+ into TARGET otherwise. Return TARGET. */
- if (Pmode == DImode)
- emit_insn (gen_movdi (stack_pointer_rtx, hard_frame_pointer_rtx));
- else
- emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
- }
+static rtx
+mips_builtin_branch_and_move (rtx condition, rtx target,
+ rtx value_if_true, rtx value_if_false)
+{
+ rtx true_label, done_label;
- /* The GP/PIC register is implicitly used by all SYMBOL_REFs, so if we
- are going to restore it, then we must emit a blockage insn to
- prevent the scheduler from moving the restore out of the epilogue. */
- else if (TARGET_ABICALLS && mips_abi != ABI_32 && mips_abi != ABI_O64
- && (current_frame_info.mask
- & (1L << (PIC_OFFSET_TABLE_REGNUM - GP_REG_FIRST))))
- emit_insn (gen_blockage ());
+ true_label = gen_label_rtx ();
+ done_label = gen_label_rtx ();
- save_restore_insns (0, tmp_rtx, orig_tsize, (FILE *)0);
+ /* First assume that CONDITION is false. */
+ mips_emit_move (target, value_if_false);
- /* In mips16 mode with a large frame, we adjust the stack
- pointer before restoring the registers. In this case, we
- should always be using a frame pointer, so everything should
- have been handled above. */
- if (tsize > 32767 && TARGET_MIPS16)
- abort ();
+ /* Branch to TRUE_LABEL if CONDITION is true and DONE_LABEL otherwise. */
+ emit_jump_insn (gen_condjump (condition, true_label));
+ emit_jump_insn (gen_jump (done_label));
+ emit_barrier ();
- if (current_function_calls_eh_return)
- {
- rtx eh_ofs = EH_RETURN_STACKADJ_RTX;
- if (Pmode == DImode)
- emit_insn (gen_adddi3 (eh_ofs, eh_ofs, tsize_rtx));
- else
- emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx));
- tsize_rtx = eh_ofs;
- }
+ /* Fix TARGET if CONDITION is true. */
+ emit_label (true_label);
+ mips_emit_move (target, value_if_true);
- emit_insn (gen_blockage ());
+ emit_label (done_label);
+ return target;
+}
- if (tsize != 0 || current_function_calls_eh_return)
- {
- if (!TARGET_MIPS16)
- {
- if (Pmode == DImode)
- emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx,
- tsize_rtx));
- else
- emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
- tsize_rtx));
- }
- else
- {
- /* We need to work around not being able to add a register
- to the stack pointer directly. Use register $6 as an
- intermediate step. */
+/* Expand a comparison built-in function of type BUILTIN_TYPE. EXP is
+ the CALL_EXPR that calls the function, ICODE is the code of the
+ comparison instruction, and COND is the condition it should test.
+ TARGET, if nonnull, suggests a good place to put the boolean result. */
- rtx g6_rtx = gen_rtx (REG, Pmode, GP_REG_FIRST + 6);
+static rtx
+mips_expand_builtin_compare (enum mips_builtin_type builtin_type,
+ enum insn_code icode, enum mips_fp_condition cond,
+ rtx target, tree exp)
+{
+ rtx offset, condition, cmp_result, args[MAX_RECOG_OPERANDS];
+ int argno;
- if (Pmode == DImode)
- {
- emit_insn (gen_movdi (g6_rtx, stack_pointer_rtx));
- emit_insn (gen_adddi3 (g6_rtx, g6_rtx, tsize_rtx));
- emit_insn (gen_movdi (stack_pointer_rtx, g6_rtx));
- }
- else
- {
- emit_insn (gen_movsi (g6_rtx, stack_pointer_rtx));
- emit_insn (gen_addsi3 (g6_rtx, g6_rtx, tsize_rtx));
- emit_insn (gen_movsi (stack_pointer_rtx, g6_rtx));
- }
- }
+ if (target == 0 || GET_MODE (target) != SImode)
+ target = gen_reg_rtx (SImode);
- }
- }
+ /* The instruction should have a target operand, an operand for each
+ argument, and an operand for COND. */
+ gcc_assert (call_expr_nargs (exp) + 2 == insn_data[icode].n_operands);
- /* The mips16 loads the return address into $7, not $31. */
- if (TARGET_MIPS16 && (current_frame_info.mask & RA_MASK) != 0)
- emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
- GP_REG_FIRST + 7)));
- else
- emit_jump_insn (gen_return_internal (gen_rtx (REG, Pmode,
- GP_REG_FIRST + 31)));
-}
-\f
-/* Return nonzero if this function is known to have a null epilogue.
- This allows the optimizer to omit jumps to jumps if no stack
- was created. */
+ /* Prepare the operands to the comparison. */
+ cmp_result = mips_prepare_builtin_target (icode, 0, 0);
+ for (argno = 0; argno < call_expr_nargs (exp); argno++)
+ args[argno] = mips_prepare_builtin_arg (icode, argno + 1, exp, argno);
-int
-mips_can_use_return_insn ()
-{
- if (! reload_completed)
- return 0;
+ switch (insn_data[icode].n_operands)
+ {
+ case 4:
+ emit_insn (GEN_FCN (icode) (cmp_result, args[0], args[1],
+ GEN_INT (cond)));
+ break;
- if (regs_ever_live[31] || current_function_profile)
- return 0;
+ case 6:
+ emit_insn (GEN_FCN (icode) (cmp_result, args[0], args[1],
+ args[2], args[3], GEN_INT (cond)));
+ break;
- /* In mips16 mode, a function which returns a floating point value
- needs to arrange to copy the return value into the floating point
- registers. */
- if (TARGET_MIPS16
- && mips16_hard_float
- && ! aggregate_value_p (DECL_RESULT (current_function_decl))
- && (GET_MODE_CLASS (DECL_MODE (DECL_RESULT (current_function_decl)))
- == MODE_FLOAT)
- && (! TARGET_SINGLE_FLOAT
- || (GET_MODE_SIZE (DECL_MODE (DECL_RESULT (current_function_decl)))
- <= 4)))
- return 0;
+ default:
+ gcc_unreachable ();
+ }
+
+ /* If the comparison sets more than one register, we define the result
+ to be 0 if all registers are false and -1 if all registers are true.
+ The value of the complete result is indeterminate otherwise. */
+ switch (builtin_type)
+ {
+ case MIPS_BUILTIN_CMP_ALL:
+ condition = gen_rtx_NE (VOIDmode, cmp_result, constm1_rtx);
+ return mips_builtin_branch_and_move (condition, target,
+ const0_rtx, const1_rtx);
- if (current_frame_info.initialized)
- return current_frame_info.total_size == 0;
+ case MIPS_BUILTIN_CMP_UPPER:
+ case MIPS_BUILTIN_CMP_LOWER:
+ offset = GEN_INT (builtin_type == MIPS_BUILTIN_CMP_UPPER);
+ condition = gen_single_cc (cmp_result, offset);
+ return mips_builtin_branch_and_move (condition, target,
+ const1_rtx, const0_rtx);
- return compute_frame_size (get_frame_size ()) == 0;
+ default:
+ condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx);
+ return mips_builtin_branch_and_move (condition, target,
+ const1_rtx, const0_rtx);
+ }
}
-\f
-/* Returns non-zero if X contains a SYMBOL_REF. */
-static int
-symbolic_expression_p (x)
- rtx x;
+/* Expand a bposge built-in function of type BUILTIN_TYPE. TARGET,
+ if nonnull, suggests a good place to put the boolean result. */
+
+static rtx
+mips_expand_builtin_bposge (enum mips_builtin_type builtin_type, rtx target)
{
- if (GET_CODE (x) == SYMBOL_REF)
- return 1;
+ rtx condition, cmp_result;
+ int cmp_value;
- if (GET_CODE (x) == CONST)
- return symbolic_expression_p (XEXP (x, 0));
+ if (target == 0 || GET_MODE (target) != SImode)
+ target = gen_reg_rtx (SImode);
- if (GET_RTX_CLASS (GET_CODE (x)) == '1')
- return symbolic_expression_p (XEXP (x, 0));
+ cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM);
- if (GET_RTX_CLASS (GET_CODE (x)) == 'c'
- || GET_RTX_CLASS (GET_CODE (x)) == '2')
- return (symbolic_expression_p (XEXP (x, 0))
- || symbolic_expression_p (XEXP (x, 1)));
+ if (builtin_type == MIPS_BUILTIN_BPOSGE32)
+ cmp_value = 32;
+ else
+ gcc_assert (0);
- return 0;
+ condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value));
+ return mips_builtin_branch_and_move (condition, target,
+ const1_rtx, const0_rtx);
}
-/* Choose the section to use for the constant rtx expression X that has
- mode MODE. */
+/* Implement TARGET_EXPAND_BUILTIN. */
-void
-mips_select_rtx_section (mode, x)
- enum machine_mode mode;
- rtx x ATTRIBUTE_UNUSED;
-{
+static rtx
+mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode, int ignore)
+{
+ tree fndecl;
+ unsigned int fcode, avail;
+ const struct mips_builtin_description *d;
+
+ fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ fcode = DECL_FUNCTION_CODE (fndecl);
+ gcc_assert (fcode < ARRAY_SIZE (mips_builtins));
+ d = &mips_builtins[fcode];
+ avail = d->avail ();
+ gcc_assert (avail != 0);
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. */
- 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. */
- READONLY_DATA_SECTION ();
+ error ("built-in function %qs not supported for MIPS16",
+ IDENTIFIER_POINTER (DECL_NAME (fndecl)));
+ return ignore ? const0_rtx : CONST0_RTX (mode);
}
- else
+ switch (d->builtin_type)
{
- /* For hosted applications, always put constants in small data if
- possible, as this gives the best performance. */
-
- if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold
- && mips_section_threshold > 0)
- SMALL_DATA_SECTION ();
- else if (flag_pic && symbolic_expression_p (x))
- /* Any expression involving a SYMBOL_REF might need a run-time
- relocation. (The symbol might be defined in a shared
- library loaded at an unexpected base address.) So, we must
- put such expressions in the data segment (which is
- writable), rather than the text segment (which is
- read-only). */
- data_section ();
- else
- READONLY_DATA_SECTION ();
- }
-}
+ case MIPS_BUILTIN_DIRECT:
+ return mips_expand_builtin_direct (d->icode, target, exp, true);
-/* Choose the section to use for DECL. RELOC is true if its value contains
- any relocatable expression.
+ case MIPS_BUILTIN_DIRECT_NO_TARGET:
+ return mips_expand_builtin_direct (d->icode, target, exp, false);
- Some of the logic used here needs to be replicated in
- ENCODE_SECTION_INFO in mips.h so that references to these symbols
- are done correctly. Specifically, at least all symbols assigned
- here to rom (.text and/or .rodata) must not be referenced via
- ENCODE_SECTION_INFO with %gprel, as the rom might be too far away.
+ case MIPS_BUILTIN_MOVT:
+ case MIPS_BUILTIN_MOVF:
+ return mips_expand_builtin_movtf (d->builtin_type, d->icode,
+ d->cond, target, exp);
- If you need to make a change here, you probably should check
- ENCODE_SECTION_INFO to see if it needs a similar change. */
+ case MIPS_BUILTIN_CMP_ANY:
+ case MIPS_BUILTIN_CMP_ALL:
+ case MIPS_BUILTIN_CMP_UPPER:
+ case MIPS_BUILTIN_CMP_LOWER:
+ case MIPS_BUILTIN_CMP_SINGLE:
+ return mips_expand_builtin_compare (d->builtin_type, d->icode,
+ d->cond, target, exp);
-void
-mips_select_section (decl, reloc)
- tree decl;
- int reloc;
-{
- int size = int_size_in_bytes (TREE_TYPE (decl));
-
- if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
- && TREE_CODE (decl) == STRING_CST
- && !flag_writable_strings)
- /* For embedded position independent code, put constant strings in the
- text section, because the data section is limited to 64K in size.
- For mips16 code, put strings in the text section so that a PC
- relative load instruction can be used to get their address. */
- text_section ();
- else if (TARGET_EMBEDDED_DATA)
- {
- /* For embedded applications, always put an object in read-only data
- if possible, in order to reduce RAM usage. */
-
- if (((TREE_CODE (decl) == VAR_DECL
- && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
- && DECL_INITIAL (decl)
- && (DECL_INITIAL (decl) == error_mark_node
- || TREE_CONSTANT (DECL_INITIAL (decl))))
- /* Deal with calls from output_constant_def_contents. */
- || (TREE_CODE (decl) != VAR_DECL
- && (TREE_CODE (decl) != STRING_CST
- || !flag_writable_strings)))
- && ! (flag_pic && reloc))
- READONLY_DATA_SECTION ();
- else if (size > 0 && size <= mips_section_threshold)
- SMALL_DATA_SECTION ();
- else
- data_section ();
- }
- else
- {
- /* For hosted applications, always put an object in small data if
- possible, as this gives the best performance. */
-
- if (size > 0 && size <= mips_section_threshold)
- SMALL_DATA_SECTION ();
- else if (((TREE_CODE (decl) == VAR_DECL
- && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl)
- && DECL_INITIAL (decl)
- && (DECL_INITIAL (decl) == error_mark_node
- || TREE_CONSTANT (DECL_INITIAL (decl))))
- /* Deal with calls from output_constant_def_contents. */
- || (TREE_CODE (decl) != VAR_DECL
- && (TREE_CODE (decl) != STRING_CST
- || !flag_writable_strings)))
- && ! (flag_pic && reloc))
- READONLY_DATA_SECTION ();
- else
- data_section ();
+ case MIPS_BUILTIN_BPOSGE32:
+ return mips_expand_builtin_bposge (d->builtin_type, target);
}
+ gcc_unreachable ();
}
\f
-/* Return register to use for a function return value with VALTYPE for
- function FUNC. MODE is used instead of VALTYPE for LIBCALLs. */
+/* An entry in the MIPS16 constant pool. VALUE is the pool constant,
+ MODE is its mode, and LABEL is the CODE_LABEL associated with it. */
+struct mips16_constant {
+ struct mips16_constant *next;
+ rtx value;
+ rtx label;
+ enum machine_mode mode;
+};
-rtx
-mips_function_value (valtype, func, mode)
- tree valtype;
- tree func ATTRIBUTE_UNUSED;
- enum machine_mode mode;
+/* Information about an incomplete MIPS16 constant pool. FIRST is the
+ first constant, HIGHEST_ADDRESS is the highest address that the first
+ byte of the pool can have, and INSN_ADDRESS is the current instruction
+ address. */
+struct mips16_constant_pool {
+ struct mips16_constant *first;
+ int highest_address;
+ int insn_address;
+};
+
+/* Add constant VALUE to POOL and return its label. MODE is the
+ value's mode (used for CONST_INTs, etc.). */
+
+static rtx
+mips16_add_constant (struct mips16_constant_pool *pool,
+ rtx value, enum machine_mode mode)
{
- int reg = GP_RETURN;
- enum mode_class mclass;
- int unsignedp = 1;
+ struct mips16_constant **p, *c;
+ bool first_of_size_p;
- if (valtype)
+ /* See whether the constant is already in the pool. If so, return the
+ existing label, otherwise leave P pointing to the place where the
+ constant should be added.
+
+ Keep the pool sorted in increasing order of mode size so that we can
+ reduce the number of alignments needed. */
+ first_of_size_p = true;
+ for (p = &pool->first; *p != 0; p = &(*p)->next)
{
- mode = TYPE_MODE (valtype);
- unsignedp = TREE_UNSIGNED (valtype);
+ if (mode == (*p)->mode && rtx_equal_p (value, (*p)->value))
+ return (*p)->label;
+ if (GET_MODE_SIZE (mode) < GET_MODE_SIZE ((*p)->mode))
+ break;
+ if (GET_MODE_SIZE (mode) == GET_MODE_SIZE ((*p)->mode))
+ first_of_size_p = false;
+ }
+
+ /* In the worst case, the constant needed by the earliest instruction
+ will end up at the end of the pool. The entire pool must then be
+ accessible from that instruction.
+
+ When adding the first constant, set the pool's highest address to
+ the address of the first out-of-range byte. Adjust this address
+ downwards each time a new constant is added. */
+ if (pool->first == 0)
+ /* For LWPC, ADDIUPC and DADDIUPC, the base PC value is the address
+ of the instruction with the lowest two bits clear. The base PC
+ value for LDPC has the lowest three bits clear. Assume the worst
+ case here; namely that the PC-relative instruction occupies the
+ last 2 bytes in an aligned word. */
+ pool->highest_address = pool->insn_address - (UNITS_PER_WORD - 2) + 0x8000;
+ pool->highest_address -= GET_MODE_SIZE (mode);
+ if (first_of_size_p)
+ /* Take into account the worst possible padding due to alignment. */
+ pool->highest_address -= GET_MODE_SIZE (mode) - 1;
+
+ /* Create a new entry. */
+ c = XNEW (struct mips16_constant);
+ c->value = value;
+ c->mode = mode;
+ c->label = gen_label_rtx ();
+ c->next = *p;
+ *p = c;
+
+ return c->label;
+}
+
+/* Output constant VALUE after instruction INSN and return the last
+ instruction emitted. MODE is the mode of the constant. */
- /* Since we define PROMOTE_FUNCTION_RETURN, we must promote
- the mode just as PROMOTE_MODE does. */
- mode = promote_mode (valtype, mode, &unsignedp, 1);
+static rtx
+mips16_emit_constants_1 (enum machine_mode mode, rtx value, rtx insn)
+{
+ if (SCALAR_INT_MODE_P (mode) || ALL_SCALAR_FIXED_POINT_MODE_P (mode))
+ {
+ rtx size = GEN_INT (GET_MODE_SIZE (mode));
+ return emit_insn_after (gen_consttable_int (value, size), insn);
}
- mclass = GET_MODE_CLASS (mode);
- if (mclass == MODE_FLOAT)
+ if (SCALAR_FLOAT_MODE_P (mode))
+ return emit_insn_after (gen_consttable_float (value), insn);
+
+ if (VECTOR_MODE_P (mode))
{
- if (TARGET_SINGLE_FLOAT
- && (mclass == MODE_FLOAT
- ? GET_MODE_SIZE (mode) > 4 : GET_MODE_SIZE (mode) / 2 > 4))
- reg = GP_RETURN;
- else
- reg = FP_RETURN;
+ int i;
+
+ for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
+ insn = mips16_emit_constants_1 (GET_MODE_INNER (mode),
+ CONST_VECTOR_ELT (value, i), insn);
+ return insn;
}
- else if (mclass == MODE_COMPLEX_FLOAT)
+ gcc_unreachable ();
+}
+
+/* Dump out the constants in CONSTANTS after INSN. */
+
+static void
+mips16_emit_constants (struct mips16_constant *constants, rtx insn)
+{
+ struct mips16_constant *c, *next;
+ int align;
+
+ align = 0;
+ for (c = constants; c != NULL; c = next)
{
- if (TARGET_FLOAT64)
- reg = FP_RETURN;
- else if (mode == SCmode)
+ /* If necessary, increase the alignment of PC. */
+ if (align < GET_MODE_SIZE (c->mode))
{
- /* When FP registers are 32 bits, we can't directly reference
- the odd numbered ones, so let's make a pair of evens. */
-
- enum machine_mode cmode = GET_MODE_INNER (mode);
-
- return gen_rtx_PARALLEL
- (VOIDmode,
- gen_rtvec (2,
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (cmode,
- FP_RETURN),
- GEN_INT (0)),
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (cmode,
- FP_RETURN + 2),
- GEN_INT (4))));
+ int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
+ insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
}
- else
- reg = FP_RETURN;
+ align = GET_MODE_SIZE (c->mode);
+
+ insn = emit_label_after (c->label, insn);
+ insn = mips16_emit_constants_1 (c->mode, c->value, insn);
+
+ next = c->next;
+ free (c);
}
- else if (valtype && TREE_CODE (valtype) == RECORD_TYPE
- && mips_abi != ABI_32
- && mips_abi != ABI_O64
- && mips_abi != ABI_EABI)
- {
- /* A struct with only one or two floating point fields is returned in
- the floating point registers. */
- tree field, fields[2];
- int i;
+ emit_barrier_after (insn);
+}
- for (i = 0, field = TYPE_FIELDS (valtype); field;
- field = TREE_CHAIN (field))
- {
- if (TREE_CODE (field) != FIELD_DECL)
- continue;
+/* Return the length of instruction INSN. */
- if (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE || i >= 2)
- break;
+static int
+mips16_insn_length (rtx insn)
+{
+ if (JUMP_P (insn))
+ {
+ rtx body = PATTERN (insn);
+ if (GET_CODE (body) == ADDR_VEC)
+ return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 0);
+ if (GET_CODE (body) == ADDR_DIFF_VEC)
+ return GET_MODE_SIZE (GET_MODE (body)) * XVECLEN (body, 1);
+ }
+ return get_attr_length (insn);
+}
- fields[i++] = field;
- }
+/* If *X is a symbolic constant that refers to the constant pool, add
+ the constant to POOL and rewrite *X to use the constant's label. */
- /* Must check i, so that we reject structures with no elements. */
- if (! field)
- {
- if (i == 1)
- {
- /* The structure has DImode, but we don't allow DImode values
- in FP registers, so we use a PARALLEL even though it isn't
- strictly necessary. */
- enum machine_mode field_mode = TYPE_MODE (TREE_TYPE (fields[0]));
-
- return gen_rtx_PARALLEL
- (mode,
- gen_rtvec (1,
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (field_mode,
- FP_RETURN),
- const0_rtx)));
- }
+static void
+mips16_rewrite_pool_constant (struct mips16_constant_pool *pool, rtx *x)
+{
+ rtx base, offset, label;
- else if (i == 2)
- {
- enum machine_mode first_mode
- = TYPE_MODE (TREE_TYPE (fields[0]));
- enum machine_mode second_mode
- = TYPE_MODE (TREE_TYPE (fields[1]));
- HOST_WIDE_INT first_offset = int_byte_position (fields[0]);
- HOST_WIDE_INT second_offset = int_byte_position (fields[1]);
-
- return gen_rtx_PARALLEL
- (mode,
- gen_rtvec (2,
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (first_mode,
- FP_RETURN),
- GEN_INT (first_offset)),
- gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (second_mode,
- FP_RETURN + 2),
- GEN_INT (second_offset))));
- }
- }
+ split_const (*x, &base, &offset);
+ if (GET_CODE (base) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (base))
+ {
+ label = mips16_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 gen_rtx_REG (mode, reg);
}
-/* The implementation of FUNCTION_ARG_PASS_BY_REFERENCE. Return
- nonzero when an argument must be passed by reference. */
+/* This structure is used to communicate with mips16_rewrite_pool_refs.
+ INSN is the instruction we're rewriting and POOL points to the current
+ constant pool. */
+struct mips16_rewrite_pool_refs_info {
+ rtx insn;
+ struct mips16_constant_pool *pool;
+};
+
+/* Rewrite *X so that constant pool references refer to the constant's
+ label instead. DATA points to a mips16_rewrite_pool_refs_info
+ structure. */
-int
-function_arg_pass_by_reference (cum, mode, type, named)
- CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
- enum machine_mode mode;
- tree type;
- int named ATTRIBUTE_UNUSED;
+static int
+mips16_rewrite_pool_refs (rtx *x, void *data)
{
- int size;
+ struct mips16_rewrite_pool_refs_info *info =
+ (struct mips16_rewrite_pool_refs_info *) data;
- if (mips_abi == ABI_32 || mips_abi == ABI_O64)
- return 0;
+ if (force_to_mem_operand (*x, Pmode))
+ {
+ rtx mem = force_const_mem (GET_MODE (*x), *x);
+ validate_change (info->insn, x, mem, false);
+ }
- /* We must pass by reference if we would be both passing in registers
- and the stack. This is because any subsequent partial arg would be
- handled incorrectly in this case.
-
- ??? This is really a kludge. We should either fix GCC so that such
- a situation causes an abort and then do something in the MIPS port
- to prevent it, or add code to function.c to properly handle the case. */
- /* ??? cum can be NULL when called from mips_va_arg. The problem handled
- here hopefully is not relevant to mips_va_arg. */
- if (cum && MUST_PASS_IN_STACK (mode, type)
- && mips_abi != ABI_MEABI)
- {
- /* Don't pass the actual CUM to FUNCTION_ARG, because we would
- get double copies of any offsets generated for small structs
- passed in registers. */
- CUMULATIVE_ARGS temp;
- temp = *cum;
- if (FUNCTION_ARG (temp, mode, type, named) != 0)
- return 1;
- }
-
- /* Otherwise, we only do this if EABI is selected. */
- if (mips_abi != ABI_EABI)
- return 0;
+ if (MEM_P (*x))
+ {
+ mips16_rewrite_pool_constant (info->pool, &XEXP (*x, 0));
+ return -1;
+ }
- /* ??? How should SCmode be handled? */
- if (type == NULL_TREE || mode == DImode || mode == DFmode)
- return 0;
+ if (TARGET_MIPS16_TEXT_LOADS)
+ mips16_rewrite_pool_constant (info->pool, x);
- size = int_size_in_bytes (type);
- return size == -1 || size > UNITS_PER_WORD;
+ return GET_CODE (*x) == CONST ? -1 : 0;
}
-/* This function returns the register class required for a secondary
- register when copying between one of the registers in CLASS, and X,
- using MODE. If IN_P is nonzero, the copy is going from X to the
- register, otherwise the register is the source. A return value of
- NO_REGS means that no secondary register is required. */
+/* Build MIPS16 constant pools. */
-enum reg_class
-mips_secondary_reload_class (class, mode, x, in_p)
- enum reg_class class;
- enum machine_mode mode;
- rtx x;
- int in_p;
+static void
+mips16_lay_out_constants (void)
{
- enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
- int regno = -1;
- int gp_reg_p;
+ struct mips16_constant_pool pool;
+ struct mips16_rewrite_pool_refs_info info;
+ rtx insn, barrier;
+
+ if (!TARGET_MIPS16_PCREL_LOADS)
+ return;
- if (GET_CODE (x) == SIGN_EXTEND)
+ split_all_insns_noflow ();
+ barrier = 0;
+ memset (&pool, 0, sizeof (pool));
+ for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
{
- int off = 0;
+ /* Rewrite constant pool references in INSN. */
+ if (INSN_P (insn))
+ {
+ info.insn = insn;
+ info.pool = &pool;
+ for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &info);
+ }
- x = XEXP (x, 0);
+ pool.insn_address += mips16_insn_length (insn);
- /* We may be called with reg_renumber NULL from regclass.
- ??? This is probably a bug. */
- if (reg_renumber)
- regno = true_regnum (x);
- else
+ if (pool.first != NULL)
{
- while (GET_CODE (x) == SUBREG)
+ /* If there are no natural barriers between the first user of
+ the pool and the highest acceptable address, we'll need to
+ create a new instruction to jump around the constant pool.
+ In the worst case, this instruction will be 4 bytes long.
+
+ If it's too late to do this transformation after INSN,
+ do it immediately before INSN. */
+ if (barrier == 0 && pool.insn_address + 4 > pool.highest_address)
{
- off += subreg_regno_offset (REGNO (SUBREG_REG (x)),
- GET_MODE (SUBREG_REG (x)),
- SUBREG_BYTE (x),
- GET_MODE (x));
- x = SUBREG_REG (x);
+ rtx label, jump;
+
+ label = gen_label_rtx ();
+
+ jump = emit_jump_insn_before (gen_jump (label), insn);
+ JUMP_LABEL (jump) = label;
+ LABEL_NUSES (label) = 1;
+ barrier = emit_barrier_after (jump);
+
+ emit_label_after (label, barrier);
+ pool.insn_address += 4;
}
- if (GET_CODE (x) == REG)
- regno = REGNO (x) + off;
+ /* See whether the constant pool is now out of range of the first
+ user. If so, output the constants after the previous barrier.
+ Note that any instructions between BARRIER and INSN (inclusive)
+ will use negative offsets to refer to the pool. */
+ if (pool.insn_address > pool.highest_address)
+ {
+ mips16_emit_constants (pool.first, barrier);
+ pool.first = NULL;
+ barrier = 0;
+ }
+ else if (BARRIER_P (insn))
+ barrier = insn;
}
}
+ mips16_emit_constants (pool.first, get_last_insn ());
+}
+\f
+/* Return true if it is worth r10k_simplify_address's while replacing
+ an address with X. We are looking for constants, and for addresses
+ at a known offset from the incoming stack pointer. */
- else if (GET_CODE (x) == REG || GET_CODE (x) == SUBREG)
- regno = true_regnum (x);
+static bool
+r10k_simplified_address_p (rtx x)
+{
+ if (GET_CODE (x) == PLUS && CONST_INT_P (XEXP (x, 1)))
+ x = XEXP (x, 0);
+ return x == virtual_incoming_args_rtx || CONSTANT_P (x);
+}
- gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
+/* X is an expression that appears in INSN. Try to use the UD chains
+ to simplify it, returning the simplified form on success and the
+ original form otherwise. Replace the incoming value of $sp with
+ virtual_incoming_args_rtx (which should never occur in X otherwise). */
- /* We always require a general register when copying anything to
- HILO_REGNUM, except when copying an SImode value from HILO_REGNUM
- to a general register, or when copying from register 0. */
- if (class == HILO_REG && regno != GP_REG_FIRST + 0)
- return ((! in_p
- && gp_reg_p
- && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
- ? NO_REGS : gr_regs);
- else if (regno == HILO_REGNUM)
- return ((in_p
- && class == gr_regs
- && GET_MODE_SIZE (mode) <= GET_MODE_SIZE (SImode))
- ? NO_REGS : gr_regs);
+static rtx
+r10k_simplify_address (rtx x, rtx insn)
+{
+ rtx newx, op0, op1, set, def_insn, note;
+ df_ref use, def;
+ struct df_link *defs;
- /* Copying from HI or LO to anywhere other than a general register
- requires a general register. */
- if (class == HI_REG || class == LO_REG || class == MD_REGS)
+ newx = NULL_RTX;
+ if (UNARY_P (x))
{
- if (TARGET_MIPS16 && in_p)
- {
- /* We can't really copy to HI or LO at all in mips16 mode. */
- return M16_REGS;
- }
- return gp_reg_p ? NO_REGS : gr_regs;
+ op0 = r10k_simplify_address (XEXP (x, 0), insn);
+ if (op0 != XEXP (x, 0))
+ newx = simplify_gen_unary (GET_CODE (x), GET_MODE (x),
+ op0, GET_MODE (XEXP (x, 0)));
}
- if (MD_REG_P (regno))
+ else if (BINARY_P (x))
{
- if (TARGET_MIPS16 && ! in_p)
- {
- /* We can't really copy to HI or LO at all in mips16 mode. */
- return M16_REGS;
- }
- return class == gr_regs ? NO_REGS : gr_regs;
+ op0 = r10k_simplify_address (XEXP (x, 0), insn);
+ op1 = r10k_simplify_address (XEXP (x, 1), insn);
+ if (op0 != XEXP (x, 0) || op1 != XEXP (x, 1))
+ newx = simplify_gen_binary (GET_CODE (x), GET_MODE (x), op0, op1);
}
-
- /* We can only copy a value to a condition code register from a
- floating point register, and even then we require a scratch
- floating point register. We can only copy a value out of a
- condition code register into a general register. */
- if (class == ST_REGS)
+ else if (GET_CODE (x) == LO_SUM)
{
- if (in_p)
- return FP_REGS;
- return GP_REG_P (regno) ? NO_REGS : GR_REGS;
+ /* LO_SUMs can be offset from HIGHs, if we know they won't
+ overflow. See mips_classify_address for the rationale behind
+ the lax check. */
+ op0 = r10k_simplify_address (XEXP (x, 0), insn);
+ if (GET_CODE (op0) == HIGH)
+ newx = XEXP (x, 1);
}
- if (ST_REG_P (regno))
+ else if (REG_P (x))
{
- if (! in_p)
- return FP_REGS;
- return class == GR_REGS ? NO_REGS : GR_REGS;
- }
+ /* Uses are recorded by regno_reg_rtx, not X itself. */
+ use = df_find_use (insn, regno_reg_rtx[REGNO (x)]);
+ gcc_assert (use);
+ defs = DF_REF_CHAIN (use);
- /* In mips16 mode, going between memory and anything but M16_REGS
- requires an M16_REG. */
- if (TARGET_MIPS16)
- {
- if (class != M16_REGS && class != M16_NA_REGS)
+ /* Require a single definition. */
+ if (defs && defs->next == NULL)
{
- if (gp_reg_p)
- return NO_REGS;
- return M16_REGS;
- }
- if (! gp_reg_p)
- {
- /* The stack pointer isn't a valid operand to an add instruction,
- so we need to load it into M16_REGS first. This can happen as
- a result of register elimination and form_sum converting
- (plus reg (plus SP CONST)) to (plus (plus reg SP) CONST). We
- need an extra register if the dest is the same as the other
- register. In that case, we can't fix the problem by loading SP
- into the dest first. */
- if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == REG
- && GET_CODE (XEXP (x, 1)) == REG
- && (XEXP (x, 0) == stack_pointer_rtx
- || XEXP (x, 1) == stack_pointer_rtx))
- return (class == M16_REGS ? M16_NA_REGS : M16_REGS);
-
- if (class == M16_REGS || class == M16_NA_REGS)
- return NO_REGS;
- return M16_REGS;
+ def = defs->ref;
+ if (DF_REF_IS_ARTIFICIAL (def))
+ {
+ /* Replace the incoming value of $sp with
+ virtual_incoming_args_rtx. */
+ if (x == stack_pointer_rtx
+ && DF_REF_BB (def) == ENTRY_BLOCK_PTR)
+ newx = virtual_incoming_args_rtx;
+ }
+ else if (dominated_by_p (CDI_DOMINATORS, DF_REF_BB (use),
+ DF_REF_BB (def)))
+ {
+ /* Make sure that DEF_INSN is a single set of REG. */
+ def_insn = DF_REF_INSN (def);
+ if (NONJUMP_INSN_P (def_insn))
+ {
+ set = single_set (def_insn);
+ if (set && rtx_equal_p (SET_DEST (set), x))
+ {
+ /* Prefer to use notes, since the def-use chains
+ are often shorter. */
+ note = find_reg_equal_equiv_note (def_insn);
+ if (note)
+ newx = XEXP (note, 0);
+ else
+ newx = SET_SRC (set);
+ newx = r10k_simplify_address (newx, def_insn);
+ }
+ }
+ }
}
}
-
- return NO_REGS;
+ if (newx && r10k_simplified_address_p (newx))
+ return newx;
+ return x;
}
-\f
-/* For each mips16 function which refers to GP relative symbols, we
- use a pseudo register, initialized at the start of the function, to
- hold the $gp value. */
-
-rtx
-mips16_gp_pseudo_reg ()
-{
- if (mips16_gp_pseudo_rtx == NULL_RTX)
- {
- rtx const_gp;
- rtx insn, scan;
- mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
- RTX_UNCHANGING_P (mips16_gp_pseudo_rtx) = 1;
+/* Return true if ADDRESS is known to be an uncached address
+ on R10K systems. */
- /* We want to initialize this to a value which gcc will believe
- is constant. */
- const_gp = gen_rtx (CONST, Pmode,
- gen_rtx (REG, Pmode, GP_REG_FIRST + 28));
+static bool
+r10k_uncached_address_p (unsigned HOST_WIDE_INT address)
+{
+ unsigned HOST_WIDE_INT upper;
- start_sequence ();
- emit_move_insn (mips16_gp_pseudo_rtx, const_gp);
- insn = gen_sequence ();
- end_sequence ();
+ /* Check for KSEG1. */
+ if (address + 0x60000000 < 0x20000000)
+ return true;
- push_topmost_sequence ();
- /* We need to emit the initialization after the FUNCTION_BEG
- note, so that it will be integrated. */
- for (scan = get_insns (); scan != NULL_RTX; scan = NEXT_INSN (scan))
- if (GET_CODE (scan) == NOTE
- && NOTE_LINE_NUMBER (scan) == NOTE_INSN_FUNCTION_BEG)
- break;
- if (scan == NULL_RTX)
- scan = get_insns ();
- insn = emit_insn_after (insn, scan);
- pop_topmost_sequence ();
+ /* Check for uncached XKPHYS addresses. */
+ if (Pmode == DImode)
+ {
+ upper = (address >> 40) & 0xf9ffff;
+ if (upper == 0x900000 || upper == 0xb80000)
+ return true;
}
-
- return mips16_gp_pseudo_rtx;
+ return false;
}
-/* Return an RTX which represents the signed 16 bit offset from the
- $gp register for the given symbol. This is only used on the
- mips16. */
+/* Return true if we can prove that an access to address X in instruction
+ INSN would be safe from R10K speculation. This X is a general
+ expression; it might not be a legitimate address. */
-rtx
-mips16_gp_offset (sym)
- rtx sym;
+static bool
+r10k_safe_address_p (rtx x, rtx insn)
{
- tree gp;
-
- if (GET_CODE (sym) != SYMBOL_REF
- || ! SYMBOL_REF_FLAG (sym))
- abort ();
-
- /* We use a special identifier to represent the value of the gp
- register. */
- gp = get_identifier ("__mips16_gp_value");
+ rtx base, offset;
+ HOST_WIDE_INT offset_val;
- return gen_rtx (CONST, Pmode,
- gen_rtx (MINUS, Pmode, sym,
- gen_rtx (SYMBOL_REF, Pmode,
- IDENTIFIER_POINTER (gp))));
-}
-
-/* Return nonzero if the given RTX represents a signed 16 bit offset
- from the $gp register. */
+ x = r10k_simplify_address (x, insn);
-int
-mips16_gp_offset_p (x)
- rtx x;
-{
- if (GET_CODE (x) == CONST)
- x = XEXP (x, 0);
+ /* Check for references to the stack frame. It doesn't really matter
+ how much of the frame has been allocated at INSN; -mr10k-cache-barrier
+ allows us to assume that accesses to any part of the eventual frame
+ is safe from speculation at any point in the function. */
+ mips_split_plus (x, &base, &offset_val);
+ if (base == virtual_incoming_args_rtx
+ && offset_val >= -cfun->machine->frame.total_size
+ && offset_val < cfun->machine->frame.args_size)
+ return true;
- /* It's OK to add a small integer value to a gp offset. */
- if (GET_CODE (x) == PLUS)
- {
- if (GET_CODE (XEXP (x, 1)) == CONST_INT
- && SMALL_INT (XEXP (x, 1)))
- return mips16_gp_offset_p (XEXP (x, 0));
- if (GET_CODE (XEXP (x, 0)) == CONST_INT
- && SMALL_INT (XEXP (x, 0)))
- return mips16_gp_offset_p (XEXP (x, 1));
- return 0;
- }
+ /* Check for uncached addresses. */
+ if (CONST_INT_P (x))
+ return r10k_uncached_address_p (INTVAL (x));
- /* Make sure it is in the form SYM - __mips16_gp_value. */
- return (GET_CODE (x) == MINUS
- && GET_CODE (XEXP (x, 0)) == SYMBOL_REF
- && SYMBOL_REF_FLAG (XEXP (x, 0))
- && GET_CODE (XEXP (x, 1)) == SYMBOL_REF
- && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0);
+ /* Check for accesses to a static object. */
+ split_const (x, &base, &offset);
+ return offset_within_block_p (base, INTVAL (offset));
}
-/* Output a GP offset. We don't want to print the subtraction of
- __mips16_gp_value; it is implicitly represented by the %gprel which
- should have been printed by the caller. */
+/* Return true if a MEM with MEM_EXPR EXPR and MEM_OFFSET OFFSET is
+ an in-range access to an automatic variable, or to an object with
+ a link-time-constant address. */
-static void
-mips16_output_gp_offset (file, x)
- FILE *file;
- rtx x;
+static bool
+r10k_safe_mem_expr_p (tree expr, rtx offset)
{
- if (GET_CODE (x) == CONST)
- x = XEXP (x, 0);
-
- if (GET_CODE (x) == PLUS)
- {
- mips16_output_gp_offset (file, XEXP (x, 0));
- fputs ("+", file);
- mips16_output_gp_offset (file, XEXP (x, 1));
- return;
- }
+ if (expr == NULL_TREE
+ || offset == NULL_RTX
+ || !CONST_INT_P (offset)
+ || INTVAL (offset) < 0
+ || INTVAL (offset) >= int_size_in_bytes (TREE_TYPE (expr)))
+ return false;
- if (GET_CODE (x) == MINUS
- && GET_CODE (XEXP (x, 1)) == SYMBOL_REF
- && strcmp (XSTR (XEXP (x, 1), 0), "__mips16_gp_value") == 0)
+ while (TREE_CODE (expr) == COMPONENT_REF)
{
- mips16_output_gp_offset (file, XEXP (x, 0));
- return;
+ expr = TREE_OPERAND (expr, 0);
+ if (expr == NULL_TREE)
+ return false;
}
- output_addr_const (file, x);
+ return DECL_P (expr);
}
-/* Return nonzero if a constant should not be output until after the
- function. This is true of most string constants, so that we can
- use a more efficient PC relative reference. However, a static
- inline function may never call assemble_function_end to write out
- the constant pool, so don't try to postpone the constant in that
- case.
+/* A for_each_rtx callback for which DATA points to the instruction
+ containing *X. Stop the search if we find a MEM that is not safe
+ from R10K speculation. */
- ??? It's really a bug that a static inline function can put stuff
- in the constant pool even if the function itself is not output.
+static int
+r10k_needs_protection_p_1 (rtx *loc, void *data)
+{
+ rtx mem;
- We record which string constants we've seen, so that we know which
- ones might use the more efficient reference. */
+ mem = *loc;
+ if (!MEM_P (mem))
+ return 0;
-int
-mips16_constant_after_function_p (x)
- tree x;
-{
- if (TREE_CODE (x) == STRING_CST
- && ! flag_writable_strings
- && current_function_decl != 0
- && ! DECL_DEFER_OUTPUT (current_function_decl)
- && ! (DECL_INLINE (current_function_decl)
- && ((! TREE_PUBLIC (current_function_decl)
- && ! TREE_ADDRESSABLE (current_function_decl)
- && ! flag_keep_inline_functions)
- || DECL_EXTERNAL (current_function_decl))))
- {
- struct string_constant *n;
-
- n = (struct string_constant *) xmalloc (sizeof *n);
- n->label = XSTR (XEXP (TREE_CST_RTL (x), 0), 0);
- n->next = string_constants;
- string_constants = n;
+ if (r10k_safe_mem_expr_p (MEM_EXPR (mem), MEM_OFFSET (mem)))
+ return -1;
- return 1;
- }
+ if (r10k_safe_address_p (XEXP (mem, 0), (rtx) data))
+ return -1;
- return 0;
+ return 1;
}
-/* Validate a constant for the mips16. This rejects general symbolic
- addresses, which must be loaded from memory. If ADDR is nonzero,
- this should reject anything which is not a legal address. If
- ADDEND is nonzero, this is being added to something else. */
+/* A note_stores callback for which DATA points to an instruction pointer.
+ If *DATA is nonnull, make it null if it X contains a MEM that is not
+ safe from R10K speculation. */
-int
-mips16_constant (x, mode, addr, addend)
- rtx x;
- enum machine_mode mode;
- int addr;
- int addend;
+static void
+r10k_needs_protection_p_store (rtx x, const_rtx pat ATTRIBUTE_UNUSED,
+ void *data)
{
- while (GET_CODE (x) == CONST)
- x = XEXP (x, 0);
-
- switch (GET_CODE (x))
- {
- default:
- return 0;
+ rtx *insn_ptr;
- case PLUS:
- return (mips16_constant (XEXP (x, 0), mode, addr, 1)
- && mips16_constant (XEXP (x, 1), mode, addr, 1));
+ insn_ptr = (rtx *) data;
+ if (*insn_ptr && for_each_rtx (&x, r10k_needs_protection_p_1, *insn_ptr))
+ *insn_ptr = NULL_RTX;
+}
- case SYMBOL_REF:
- if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
- return 0;
- if (CONSTANT_POOL_ADDRESS_P (x))
- return 1;
+/* A for_each_rtx callback that iterates over the pattern of a CALL_INSN.
+ Return nonzero if the call is not to a declared function. */
- /* If we aren't looking for a memory address, we can accept a GP
- relative symbol, which will have SYMBOL_REF_FLAG set; movsi
- knows how to handle this. We can always accept a string
- constant, which is the other case in which SYMBOL_REF_FLAG
- will be set. */
- if (! addr
- && ! addend
- && SYMBOL_REF_FLAG (x)
- && mode == (enum machine_mode) Pmode)
- return 1;
+static int
+r10k_needs_protection_p_call (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+ rtx x;
- /* We can accept a string constant, which will have
- SYMBOL_REF_FLAG set but must be recognized by name to
- distinguish from a GP accessible symbol. The name of a
- string constant will have been generated by
- ASM_GENERATE_INTERNAL_LABEL as called by output_constant_def. */
- if (SYMBOL_REF_FLAG (x))
- {
- const char *name = XSTR (x, 0);
+ x = *loc;
+ if (!MEM_P (x))
+ return 0;
- return (name[0] == '*'
- && strncmp (name + 1, LOCAL_LABEL_PREFIX,
- sizeof LOCAL_LABEL_PREFIX - 1) == 0);
- }
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_DECL (x))
+ return -1;
- return 0;
+ return 1;
+}
- case LABEL_REF:
- if (addr && GET_MODE_SIZE (mode) != 4 && GET_MODE_SIZE (mode) != 8)
- return 0;
- return 1;
+/* Return true if instruction INSN needs to be protected by an R10K
+ cache barrier. */
- case CONST_INT:
- if (addr && ! addend)
- return 0;
- return INTVAL (x) > - 0x10000 && INTVAL (x) <= 0xffff;
+static bool
+r10k_needs_protection_p (rtx insn)
+{
+ if (CALL_P (insn))
+ return for_each_rtx (&PATTERN (insn), r10k_needs_protection_p_call, NULL);
- case REG:
- /* We need to treat $gp as a legitimate constant, because
- mips16_gp_pseudo_reg assumes that. */
- return REGNO (x) == GP_REG_FIRST + 28;
+ if (mips_r10k_cache_barrier == R10K_CACHE_BARRIER_STORE)
+ {
+ note_stores (PATTERN (insn), r10k_needs_protection_p_store, &insn);
+ return insn == NULL_RTX;
}
+
+ return for_each_rtx (&PATTERN (insn), r10k_needs_protection_p_1, insn);
}
-/* Write out code to move floating point arguments in or out of
- general registers. Output the instructions to FILE. FP_CODE is
- the code describing which arguments are present (see the comment at
- the definition of CUMULATIVE_ARGS in mips.h). FROM_FP_P is non-zero if
- we are copying from the floating point registers. */
+/* Return true if BB is only reached by blocks in PROTECTED_BBS and if every
+ edge is unconditional. */
-static void
-mips16_fp_args (file, fp_code, from_fp_p)
- FILE *file;
- int fp_code;
- int from_fp_p;
+static bool
+r10k_protected_bb_p (basic_block bb, sbitmap protected_bbs)
{
- const char *s;
- int gparg, fparg;
- unsigned int f;
-
- /* This code only works for the original 32 bit ABI and the O64 ABI. */
- if (mips_abi != ABI_32 && mips_abi != ABI_O64)
- abort ();
-
- if (from_fp_p)
- s = "mfc1";
- else
- s = "mtc1";
- gparg = GP_ARG_FIRST;
- fparg = FP_ARG_FIRST;
- for (f = (unsigned int) fp_code; f != 0; f >>= 2)
- {
- if ((f & 3) == 1)
- {
- if ((fparg & 1) != 0)
- ++fparg;
- fprintf (file, "\t%s\t%s,%s\n", s,
- reg_names[gparg], reg_names[fparg]);
- }
- else if ((f & 3) == 2)
- {
- if (TARGET_64BIT)
- fprintf (file, "\td%s\t%s,%s\n", s,
- reg_names[gparg], reg_names[fparg]);
- else
- {
- if ((fparg & 1) != 0)
- ++fparg;
- if (TARGET_BIG_ENDIAN)
- fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
- reg_names[gparg], reg_names[fparg + 1], s,
- reg_names[gparg + 1], reg_names[fparg]);
- else
- fprintf (file, "\t%s\t%s,%s\n\t%s\t%s,%s\n", s,
- reg_names[gparg], reg_names[fparg], s,
- reg_names[gparg + 1], reg_names[fparg + 1]);
- ++gparg;
- ++fparg;
- }
- }
- else
- abort ();
+ edge_iterator ei;
+ edge e;
- ++gparg;
- ++fparg;
- }
+ FOR_EACH_EDGE (e, ei, bb->preds)
+ if (!single_succ_p (e->src)
+ || !TEST_BIT (protected_bbs, e->src->index)
+ || (e->flags & EDGE_COMPLEX) != 0)
+ return false;
+ return true;
}
-/* Build a mips16 function stub. This is used for functions which
- take aruments in the floating point registers. It is 32 bit code
- that moves the floating point args into the general registers, and
- then jumps to the 16 bit code. */
+/* Implement -mr10k-cache-barrier= for the current function. */
static void
-build_mips16_function_stub (file)
- FILE *file;
+r10k_insert_cache_barriers (void)
{
- const char *fnname;
- char *secname, *stubname;
- tree stubid, stubdecl;
- int need_comma;
- unsigned int f;
-
- fnname = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
- secname = (char *) alloca (strlen (fnname) + 20);
- sprintf (secname, ".mips16.fn.%s", fnname);
- stubname = (char *) alloca (strlen (fnname) + 20);
- sprintf (stubname, "__fn_stub_%s", fnname);
- stubid = get_identifier (stubname);
- stubdecl = build_decl (FUNCTION_DECL, stubid,
- build_function_type (void_type_node, NULL_TREE));
- DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+ int *rev_post_order;
+ unsigned int i, n;
+ basic_block bb;
+ sbitmap protected_bbs;
+ rtx insn, end, unprotected_region;
- fprintf (file, "\t# Stub function for %s (", current_function_name);
- need_comma = 0;
- for (f = (unsigned int) current_function_args_info.fp_code; f != 0; f >>= 2)
+ if (TARGET_MIPS16)
{
- fprintf (file, "%s%s",
- need_comma ? ", " : "",
- (f & 3) == 1 ? "float" : "double");
- need_comma = 1;
+ sorry ("%qs does not support MIPS16 code", "-mr10k-cache-barrier");
+ return;
}
- fprintf (file, ")\n");
-
- fprintf (file, "\t.set\tnomips16\n");
- function_section (stubdecl);
- ASM_OUTPUT_ALIGN (file, floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT));
-
- /* ??? If FUNCTION_NAME_ALREADY_DECLARED is defined, then we are
- within a .ent, and we can not emit another .ent. */
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- fputs ("\t.ent\t", file);
- assemble_name (file, stubname);
- fputs ("\n", file);
-#endif
-
- assemble_name (file, stubname);
- fputs (":\n", file);
-
- /* We don't want the assembler to insert any nops here. */
- fprintf (file, "\t.set\tnoreorder\n");
-
- mips16_fp_args (file, current_function_args_info.fp_code, 1);
-
- fprintf (asm_out_file, "\t.set\tnoat\n");
- fprintf (asm_out_file, "\tla\t%s,", reg_names[GP_REG_FIRST + 1]);
- assemble_name (file, fnname);
- fprintf (file, "\n");
- fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
- fprintf (asm_out_file, "\t.set\tat\n");
-
- /* Unfortunately, we can't fill the jump delay slot. We can't fill
- with one of the mfc1 instructions, because the result is not
- available for one instruction, so if the very first instruction
- in the function refers to the register, it will see the wrong
- value. */
- fprintf (file, "\tnop\n");
-
- fprintf (file, "\t.set\treorder\n");
-
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- fputs ("\t.end\t", file);
- assemble_name (file, stubname);
- fputs ("\n", file);
-#endif
- fprintf (file, "\t.set\tmips16\n");
+ /* Restore the BLOCK_FOR_INSN pointers, which are needed by DF. */
+ compute_bb_for_insn ();
- function_section (current_function_decl);
-}
-
-/* We keep a list of functions for which we have already built stubs
- in build_mips16_call_stub. */
+ /* Create def-use chains. */
+ df_set_flags (DF_EQ_NOTES);
+ df_chain_add_problem (DF_UD_CHAIN);
+ df_analyze ();
-struct mips16_stub
-{
- struct mips16_stub *next;
- char *name;
- int fpret;
-};
+ /* Calculate dominators. */
+ calculate_dominance_info (CDI_DOMINATORS);
-static struct mips16_stub *mips16_stubs;
+ /* Bit X of PROTECTED_BBS is set if the last operation in basic block
+ X is protected by a cache barrier. */
+ protected_bbs = sbitmap_alloc (last_basic_block);
+ sbitmap_zero (protected_bbs);
-/* Build a call stub for a mips16 call. A stub is needed if we are
- passing any floating point values which should go into the floating
- point registers. If we are, and the call turns out to be to a 32
- bit function, the stub will be used to move the values into the
- floating point registers before calling the 32 bit function. The
- linker will magically adjust the function call to either the 16 bit
- function or the 32 bit stub, depending upon where the function call
- is actually defined.
+ /* Iterate over the basic blocks in reverse post-order. */
+ rev_post_order = XNEWVEC (int, last_basic_block);
+ n = pre_and_rev_post_order_compute (NULL, rev_post_order, false);
+ for (i = 0; i < n; i++)
+ {
+ bb = BASIC_BLOCK (rev_post_order[i]);
- Similarly, we need a stub if the return value might come back in a
- floating point register.
+ /* If this block is only reached by unconditional edges, and if the
+ source of every edge is protected, the beginning of the block is
+ also protected. */
+ if (r10k_protected_bb_p (bb, protected_bbs))
+ unprotected_region = NULL_RTX;
+ else
+ unprotected_region = pc_rtx;
+ end = NEXT_INSN (BB_END (bb));
- RETVAL, FNMEM, and ARG_SIZE are the values passed to the call insn
- (RETVAL is NULL if this is call rather than call_value). FP_CODE
- is the code built by function_arg. This function returns a nonzero
- value if it builds the call instruction itself. */
+ /* UNPROTECTED_REGION is:
-int
-build_mips16_call_stub (retval, fnmem, arg_size, fp_code)
- rtx retval;
- rtx fnmem;
- rtx arg_size;
- int fp_code;
-{
- int fpret;
- rtx fn;
- const char *fnname;
- char *secname, *stubname;
- struct mips16_stub *l;
- tree stubid, stubdecl;
- int need_comma;
- unsigned int f;
+ - null if we are processing a protected region,
+ - pc_rtx if we are processing an unprotected region but have
+ not yet found the first instruction in it
+ - the first instruction in an unprotected region otherwise. */
+ for (insn = BB_HEAD (bb); insn != end; insn = NEXT_INSN (insn))
+ {
+ if (unprotected_region && INSN_P (insn))
+ {
+ if (recog_memoized (insn) == CODE_FOR_mips_cache)
+ /* This CACHE instruction protects the following code. */
+ unprotected_region = NULL_RTX;
+ else
+ {
+ /* See if INSN is the first instruction in this
+ unprotected region. */
+ if (unprotected_region == pc_rtx)
+ unprotected_region = insn;
+
+ /* See if INSN needs to be protected. If so,
+ we must insert a cache barrier somewhere between
+ PREV_INSN (UNPROTECTED_REGION) and INSN. It isn't
+ clear which position is better performance-wise,
+ but as a tie-breaker, we assume that it is better
+ to allow delay slots to be back-filled where
+ possible, and that it is better not to insert
+ barriers in the middle of already-scheduled code.
+ We therefore insert the barrier at the beginning
+ of the region. */
+ if (r10k_needs_protection_p (insn))
+ {
+ emit_insn_before (gen_r10k_cache_barrier (),
+ unprotected_region);
+ unprotected_region = NULL_RTX;
+ }
+ }
+ }
- /* 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 (! TARGET_MIPS16 || ! mips16_hard_float)
- return 0;
+ if (CALL_P (insn))
+ /* The called function is not required to protect the exit path.
+ The code that follows a call is therefore unprotected. */
+ unprotected_region = pc_rtx;
+ }
- /* Figure out whether the value might come back in a floating point
- register. */
- fpret = (retval != 0
- && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
- && (! TARGET_SINGLE_FLOAT
- || GET_MODE_SIZE (GET_MODE (retval)) <= 4));
+ /* Record whether the end of this block is protected. */
+ if (unprotected_region == NULL_RTX)
+ SET_BIT (protected_bbs, bb->index);
+ }
+ XDELETEVEC (rev_post_order);
- /* We don't need to do anything if there were no floating point
- arguments and the value will not be returned in a floating point
- register. */
- if (fp_code == 0 && ! fpret)
- return 0;
+ sbitmap_free (protected_bbs);
- if (GET_CODE (fnmem) != MEM)
- abort ();
- fn = XEXP (fnmem, 0);
+ free_dominance_info (CDI_DOMINATORS);
- /* We don't need to do anything if this is a call to a special
- mips16 support function. */
- if (GET_CODE (fn) == SYMBOL_REF
- && strncmp (XSTR (fn, 0), "__mips16_", 9) == 0)
- return 0;
+ df_finish_pass (false);
- /* This code will only work for o32 and o64 abis. The other ABI's
- require more sophisticated support. */
- if (mips_abi != ABI_32 && mips_abi != ABI_O64)
- abort ();
+ free_bb_for_insn ();
+}
+\f
+/* A temporary variable used by for_each_rtx callbacks, etc. */
+static rtx mips_sim_insn;
- /* We can only handle SFmode and DFmode floating point return
- values. */
- if (fpret && GET_MODE (retval) != SFmode && GET_MODE (retval) != DFmode)
- abort ();
+/* A structure representing the state of the processor pipeline.
+ Used by the mips_sim_* family of functions. */
+struct mips_sim {
+ /* The maximum number of instructions that can be issued in a cycle.
+ (Caches mips_issue_rate.) */
+ unsigned int issue_rate;
- /* If we're calling via a function pointer, then we must always call
- via a stub. There are magic stubs provided in libgcc.a for each
- of the required cases. Each of them expects the function address
- to arrive in register $2. */
+ /* The current simulation time. */
+ unsigned int time;
- if (GET_CODE (fn) != SYMBOL_REF)
- {
- char buf[30];
- tree id;
- rtx stub_fn, stub_mem, insn;
+ /* How many more instructions can be issued in the current cycle. */
+ unsigned int insns_left;
- /* ??? If this code is modified to support other ABI's, we need
- to handle PARALLEL return values here. */
+ /* LAST_SET[X].INSN is the last instruction to set register X.
+ LAST_SET[X].TIME is the time at which that instruction was issued.
+ INSN is null if no instruction has yet set register X. */
+ struct {
+ rtx insn;
+ unsigned int time;
+ } last_set[FIRST_PSEUDO_REGISTER];
- sprintf (buf, "__mips16_call_stub_%s%d",
- (fpret
- ? (GET_MODE (retval) == SFmode ? "sf_" : "df_")
- : ""),
- fp_code);
- id = get_identifier (buf);
- stub_fn = gen_rtx (SYMBOL_REF, Pmode, IDENTIFIER_POINTER (id));
- stub_mem = gen_rtx (MEM, Pmode, stub_fn);
+ /* The pipeline's current DFA state. */
+ state_t dfa_state;
+};
- emit_move_insn (gen_rtx (REG, Pmode, 2), fn);
+/* Reset STATE to the initial simulation state. */
- if (retval == NULL_RTX)
- insn = gen_call_internal0 (stub_mem, arg_size,
- gen_rtx (REG, SImode,
- GP_REG_FIRST + 31));
- else
- insn = gen_call_value_internal0 (retval, stub_mem, arg_size,
- gen_rtx (REG, SImode,
- GP_REG_FIRST + 31));
- insn = emit_call_insn (insn);
-
- /* Put the register usage information on the CALL. */
- if (GET_CODE (insn) != CALL_INSN)
- abort ();
- CALL_INSN_FUNCTION_USAGE (insn) =
- gen_rtx (EXPR_LIST, VOIDmode,
- gen_rtx (USE, VOIDmode, gen_rtx (REG, Pmode, 2)),
- CALL_INSN_FUNCTION_USAGE (insn));
-
- /* If we are handling a floating point return value, we need to
- save $18 in the function prologue. Putting a note on the
- call will mean that regs_ever_live[$18] will be true if the
- call is not eliminated, and we can check that in the prologue
- code. */
- if (fpret)
- CALL_INSN_FUNCTION_USAGE (insn) =
- gen_rtx (EXPR_LIST, VOIDmode,
- gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)),
- CALL_INSN_FUNCTION_USAGE (insn));
+static void
+mips_sim_reset (struct mips_sim *state)
+{
+ state->time = 0;
+ state->insns_left = state->issue_rate;
+ memset (&state->last_set, 0, sizeof (state->last_set));
+ state_reset (state->dfa_state);
+}
- /* Return 1 to tell the caller that we've generated the call
- insn. */
- return 1;
- }
+/* Initialize STATE before its first use. DFA_STATE points to an
+ allocated but uninitialized DFA state. */
- /* We know the function we are going to call. If we have already
- built a stub, we don't need to do anything further. */
+static void
+mips_sim_init (struct mips_sim *state, state_t dfa_state)
+{
+ state->issue_rate = mips_issue_rate ();
+ state->dfa_state = dfa_state;
+ mips_sim_reset (state);
+}
- fnname = XSTR (fn, 0);
- for (l = mips16_stubs; l != NULL; l = l->next)
- if (strcmp (l->name, fnname) == 0)
- break;
+/* Advance STATE by one clock cycle. */
- if (l == NULL)
- {
- /* Build a special purpose stub. When the linker sees a
- function call in mips16 code, it will check where the target
- is defined. If the target is a 32 bit call, the linker will
- search for the section defined here. It can tell which
- symbol this section is associated with by looking at the
- relocation information (the name is unreliable, since this
- might be a static function). If such a section is found, the
- linker will redirect the call to the start of the magic
- section.
-
- If the function does not return a floating point value, the
- special stub section is named
- .mips16.call.FNNAME
+static void
+mips_sim_next_cycle (struct mips_sim *state)
+{
+ state->time++;
+ state->insns_left = state->issue_rate;
+ state_transition (state->dfa_state, 0);
+}
- If the function does return a floating point value, the stub
- section is named
- .mips16.call.fp.FNNAME
- */
+/* Advance simulation state STATE until instruction INSN can read
+ register REG. */
- secname = (char *) alloca (strlen (fnname) + 40);
- sprintf (secname, ".mips16.call.%s%s",
- fpret ? "fp." : "",
- fnname);
- stubname = (char *) alloca (strlen (fnname) + 20);
- sprintf (stubname, "__call_stub_%s%s",
- fpret ? "fp_" : "",
- fnname);
- stubid = get_identifier (stubname);
- stubdecl = build_decl (FUNCTION_DECL, stubid,
- build_function_type (void_type_node, NULL_TREE));
- DECL_SECTION_NAME (stubdecl) = build_string (strlen (secname), secname);
+static void
+mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
+{
+ unsigned int regno, end_regno;
- fprintf (asm_out_file, "\t# Stub function to call %s%s (",
- (fpret
- ? (GET_MODE (retval) == SFmode ? "float " : "double ")
- : ""),
- fnname);
- need_comma = 0;
- for (f = (unsigned int) fp_code; f != 0; f >>= 2)
- {
- fprintf (asm_out_file, "%s%s",
- need_comma ? ", " : "",
- (f & 3) == 1 ? "float" : "double");
- need_comma = 1;
- }
- fprintf (asm_out_file, ")\n");
+ end_regno = END_REGNO (reg);
+ for (regno = REGNO (reg); regno < end_regno; regno++)
+ if (state->last_set[regno].insn != 0)
+ {
+ unsigned int t;
- fprintf (asm_out_file, "\t.set\tnomips16\n");
- assemble_start_function (stubdecl, stubname);
+ t = (state->last_set[regno].time
+ + insn_latency (state->last_set[regno].insn, insn));
+ while (state->time < t)
+ mips_sim_next_cycle (state);
+ }
+}
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- fputs ("\t.ent\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
+/* A for_each_rtx callback. If *X is a register, advance simulation state
+ DATA until mips_sim_insn can read the register's value. */
- assemble_name (asm_out_file, stubname);
- fputs (":\n", asm_out_file);
-#endif
+static int
+mips_sim_wait_regs_2 (rtx *x, void *data)
+{
+ if (REG_P (*x))
+ mips_sim_wait_reg ((struct mips_sim *) data, mips_sim_insn, *x);
+ return 0;
+}
- /* We build the stub code by hand. That's the only way we can
- do it, since we can't generate 32 bit code during a 16 bit
- compilation. */
+/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X. */
- /* We don't want the assembler to insert any nops here. */
- fprintf (asm_out_file, "\t.set\tnoreorder\n");
+static void
+mips_sim_wait_regs_1 (rtx *x, void *data)
+{
+ for_each_rtx (x, mips_sim_wait_regs_2, data);
+}
- mips16_fp_args (asm_out_file, fp_code, 0);
+/* Advance simulation state STATE until all of INSN's register
+ dependencies are satisfied. */
- if (! fpret)
- {
- fprintf (asm_out_file, "\t.set\tnoat\n");
- fprintf (asm_out_file, "\tla\t%s,%s\n", reg_names[GP_REG_FIRST + 1],
- fnname);
- fprintf (asm_out_file, "\tjr\t%s\n", reg_names[GP_REG_FIRST + 1]);
- fprintf (asm_out_file, "\t.set\tat\n");
- /* Unfortunately, we can't fill the jump delay slot. We
- can't fill with one of the mtc1 instructions, because the
- result is not available for one instruction, so if the
- very first instruction in the function refers to the
- register, it will see the wrong value. */
- fprintf (asm_out_file, "\tnop\n");
- }
- else
- {
- fprintf (asm_out_file, "\tmove\t%s,%s\n",
- reg_names[GP_REG_FIRST + 18], reg_names[GP_REG_FIRST + 31]);
- fprintf (asm_out_file, "\tjal\t%s\n", fnname);
- /* As above, we can't fill the delay slot. */
- fprintf (asm_out_file, "\tnop\n");
- if (GET_MODE (retval) == SFmode)
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 2], reg_names[FP_REG_FIRST + 0]);
- else
- {
- if (TARGET_BIG_ENDIAN)
- {
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 2],
- reg_names[FP_REG_FIRST + 1]);
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 3],
- reg_names[FP_REG_FIRST + 0]);
- }
- else
- {
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 2],
- reg_names[FP_REG_FIRST + 0]);
- fprintf (asm_out_file, "\tmfc1\t%s,%s\n",
- reg_names[GP_REG_FIRST + 3],
- reg_names[FP_REG_FIRST + 1]);
- }
- }
- fprintf (asm_out_file, "\tj\t%s\n", reg_names[GP_REG_FIRST + 18]);
- /* As above, we can't fill the delay slot. */
- fprintf (asm_out_file, "\tnop\n");
- }
+static void
+mips_sim_wait_regs (struct mips_sim *state, rtx insn)
+{
+ mips_sim_insn = insn;
+ note_uses (&PATTERN (insn), mips_sim_wait_regs_1, state);
+}
- fprintf (asm_out_file, "\t.set\treorder\n");
+/* Advance simulation state STATE until the units required by
+ instruction INSN are available. */
-#ifdef ASM_DECLARE_FUNCTION_SIZE
- ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
-#endif
+static void
+mips_sim_wait_units (struct mips_sim *state, rtx insn)
+{
+ state_t tmp_state;
-#ifndef FUNCTION_NAME_ALREADY_DECLARED
- fputs ("\t.end\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
-#endif
+ tmp_state = alloca (state_size ());
+ while (state->insns_left == 0
+ || (memcpy (tmp_state, state->dfa_state, state_size ()),
+ state_transition (tmp_state, insn) >= 0))
+ mips_sim_next_cycle (state);
+}
- fprintf (asm_out_file, "\t.set\tmips16\n");
+/* Advance simulation state STATE until INSN is ready to issue. */
- /* Record this stub. */
- l = (struct mips16_stub *) xmalloc (sizeof *l);
- l->name = xstrdup (fnname);
- l->fpret = fpret;
- l->next = mips16_stubs;
- mips16_stubs = l;
- }
+static void
+mips_sim_wait_insn (struct mips_sim *state, rtx insn)
+{
+ mips_sim_wait_regs (state, insn);
+ mips_sim_wait_units (state, insn);
+}
- /* If we expect a floating point return value, but we've built a
- stub which does not expect one, then we're in trouble. We can't
- use the existing stub, because it won't handle the floating point
- value. We can't build a new stub, because the linker won't know
- which stub to use for the various calls in this object file.
- Fortunately, this case is illegal, since it means that a function
- was declared in two different ways in a single compilation. */
- if (fpret && ! l->fpret)
- error ("can not handle inconsistent calls to `%s'", fnname);
+/* mips_sim_insn has just set X. Update the LAST_SET array
+ in simulation state DATA. */
- /* If we are calling a stub which handles a floating point return
- value, we need to arrange to save $18 in the prologue. We do
- this by marking the function call as using the register. The
- prologue will later see that it is used, and emit code to save
- it. */
+static void
+mips_sim_record_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ struct mips_sim *state;
- if (l->fpret)
+ state = (struct mips_sim *) data;
+ if (REG_P (x))
{
- rtx insn;
+ unsigned int regno, end_regno;
- if (retval == NULL_RTX)
- insn = gen_call_internal0 (fnmem, arg_size,
- gen_rtx (REG, SImode,
- GP_REG_FIRST + 31));
- else
- insn = gen_call_value_internal0 (retval, fnmem, arg_size,
- gen_rtx (REG, SImode,
- GP_REG_FIRST + 31));
- insn = emit_call_insn (insn);
-
- if (GET_CODE (insn) != CALL_INSN)
- abort ();
+ end_regno = END_REGNO (x);
+ for (regno = REGNO (x); regno < end_regno; regno++)
+ {
+ state->last_set[regno].insn = mips_sim_insn;
+ state->last_set[regno].time = state->time;
+ }
+ }
+}
- CALL_INSN_FUNCTION_USAGE (insn) =
- gen_rtx (EXPR_LIST, VOIDmode,
- gen_rtx (USE, VOIDmode, gen_rtx (REG, word_mode, 18)),
- CALL_INSN_FUNCTION_USAGE (insn));
+/* Issue instruction INSN in scheduler state STATE. Assume that INSN
+ can issue immediately (i.e., that mips_sim_wait_insn has already
+ been called). */
- /* Return 1 to tell the caller that we've generated the call
- insn. */
- return 1;
- }
+static void
+mips_sim_issue_insn (struct mips_sim *state, rtx insn)
+{
+ state_transition (state->dfa_state, insn);
+ state->insns_left--;
- /* Return 0 to let the caller generate the call insn. */
- return 0;
+ mips_sim_insn = insn;
+ note_stores (PATTERN (insn), mips_sim_record_set, state);
}
-/* This function looks through the code for a function, and tries to
- optimize the usage of the $gp register. We arrange to copy $gp
- into a pseudo-register, and then let gcc's normal reload handling
- deal with the pseudo-register. Unfortunately, if reload choose to
- put the pseudo-register into a call-clobbered register, it will
- emit saves and restores for that register around any function
- calls. We don't need the saves, and it's faster to copy $gp than
- to do an actual restore. ??? This still means that we waste a
- stack slot.
-
- This is an optimization, and the code which gcc has actually
- generated is correct, so we do not need to catch all cases. */
+/* Simulate issuing a NOP in state STATE. */
static void
-mips16_optimize_gp (first)
- rtx first;
+mips_sim_issue_nop (struct mips_sim *state)
{
- rtx gpcopy, slot, insn;
+ if (state->insns_left == 0)
+ mips_sim_next_cycle (state);
+ state->insns_left--;
+}
+
+/* Update simulation state STATE so that it's ready to accept the instruction
+ after INSN. INSN should be part of the main rtl chain, not a member of a
+ SEQUENCE. */
- /* Look through the instructions. Set GPCOPY to the register which
- holds a copy of $gp. Set SLOT to the stack slot where it is
- saved. If we find an instruction which sets GPCOPY to anything
- other than $gp or SLOT, then we can't use it. If we find an
- instruction which sets SLOT to anything other than GPCOPY, we
- can't use it. */
+static void
+mips_sim_finish_insn (struct mips_sim *state, rtx insn)
+{
+ /* If INSN is a jump with an implicit delay slot, simulate a nop. */
+ if (JUMP_P (insn))
+ mips_sim_issue_nop (state);
- gpcopy = NULL_RTX;
- slot = NULL_RTX;
- for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
+ switch (GET_CODE (SEQ_BEGIN (insn)))
{
- rtx set;
+ case CODE_LABEL:
+ case CALL_INSN:
+ /* We can't predict the processor state after a call or label. */
+ mips_sim_reset (state);
+ break;
- if (! INSN_P (insn))
- continue;
+ case JUMP_INSN:
+ /* The delay slots of branch likely instructions are only executed
+ when the branch is taken. Therefore, if the caller has simulated
+ the delay slot instruction, STATE does not really reflect the state
+ of the pipeline for the instruction after the delay slot. Also,
+ branch likely instructions tend to incur a penalty when not taken,
+ so there will probably be an extra delay between the branch and
+ the instruction after the delay slot. */
+ if (INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (insn)))
+ mips_sim_reset (state);
+ break;
- set = PATTERN (insn);
+ default:
+ break;
+ }
+}
+\f
+/* The VR4130 pipeline issues aligned pairs of instructions together,
+ but it stalls the second instruction if it depends on the first.
+ In order to cut down the amount of logic required, this dependence
+ check is not based on a full instruction decode. Instead, any non-SPECIAL
+ instruction is assumed to modify the register specified by bits 20-16
+ (which is usually the "rt" field).
+
+ In BEQ, BEQL, BNE and BNEL instructions, the rt field is actually an
+ input, so we can end up with a false dependence between the branch
+ and its delay slot. If this situation occurs in instruction INSN,
+ try to avoid it by swapping rs and rt. */
- /* We know that all references to memory will be inside a SET,
- because there is no other way to access memory on the mips16.
- We don't have to worry about a PARALLEL here, because the
- mips.md file will never generate them for memory references. */
- if (GET_CODE (set) != SET)
- continue;
+static void
+vr4130_avoid_branch_rt_conflict (rtx insn)
+{
+ rtx first, second;
- if (gpcopy == NULL_RTX
- && GET_CODE (SET_SRC (set)) == CONST
- && GET_CODE (XEXP (SET_SRC (set), 0)) == REG
- && REGNO (XEXP (SET_SRC (set), 0)) == GP_REG_FIRST + 28
- && GET_CODE (SET_DEST (set)) == REG
- && GET_MODE (SET_DEST (set)) == (unsigned) Pmode)
- gpcopy = SET_DEST (set);
- else if (slot == NULL_RTX
- && gpcopy != NULL_RTX
- && GET_CODE (SET_DEST (set)) == MEM
- && GET_CODE (SET_SRC (set)) == REG
- && REGNO (SET_SRC (set)) == REGNO (gpcopy)
- && GET_MODE (SET_DEST (set)) == (unsigned) Pmode)
+ first = SEQ_BEGIN (insn);
+ second = SEQ_END (insn);
+ if (JUMP_P (first)
+ && NONJUMP_INSN_P (second)
+ && GET_CODE (PATTERN (first)) == SET
+ && GET_CODE (SET_DEST (PATTERN (first))) == PC
+ && GET_CODE (SET_SRC (PATTERN (first))) == IF_THEN_ELSE)
+ {
+ /* Check for the right kind of condition. */
+ rtx cond = XEXP (SET_SRC (PATTERN (first)), 0);
+ if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+ && REG_P (XEXP (cond, 0))
+ && REG_P (XEXP (cond, 1))
+ && reg_referenced_p (XEXP (cond, 1), PATTERN (second))
+ && !reg_referenced_p (XEXP (cond, 0), PATTERN (second)))
{
- rtx base, offset;
-
- offset = const0_rtx;
- base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
- if (GET_CODE (base) == REG
- && (REGNO (base) == STACK_POINTER_REGNUM
- || REGNO (base) == FRAME_POINTER_REGNUM))
- slot = SET_DEST (set);
+ /* SECOND mentions the rt register but not the rs register. */
+ rtx tmp = XEXP (cond, 0);
+ XEXP (cond, 0) = XEXP (cond, 1);
+ XEXP (cond, 1) = tmp;
}
- else if (gpcopy != NULL_RTX
- && (GET_CODE (SET_DEST (set)) == REG
- || GET_CODE (SET_DEST (set)) == SUBREG)
- && reg_overlap_mentioned_p (SET_DEST (set), gpcopy)
- && (GET_CODE (SET_DEST (set)) != REG
- || REGNO (SET_DEST (set)) != REGNO (gpcopy)
- || GET_MODE (SET_DEST (set)) != (unsigned) Pmode
- || ((GET_CODE (SET_SRC (set)) != CONST
- || GET_CODE (XEXP (SET_SRC (set), 0)) != REG
- || (REGNO (XEXP (SET_SRC (set), 0))
- != GP_REG_FIRST + 28))
- && ! rtx_equal_p (SET_SRC (set), slot))))
- break;
- else if (slot != NULL_RTX
- && GET_CODE (SET_DEST (set)) == MEM
- && rtx_equal_p (SET_DEST (set), slot)
- && (GET_CODE (SET_SRC (set)) != REG
- || REGNO (SET_SRC (set)) != REGNO (gpcopy)))
- break;
}
+}
- /* If we couldn't find a unique value for GPCOPY or SLOT, then try a
- different optimization. Any time we find a copy of $28 into a
- register, followed by an add of a symbol_ref to that register, we
- convert it to load the value from the constant table instead.
- The copy and add will take six bytes, just as the load and
- constant table entry will take six bytes. However, it is
- possible that the constant table entry will be shared.
+/* Implement -mvr4130-align. Go through each basic block and simulate the
+ processor pipeline. If we find that a pair of instructions could execute
+ in parallel, and the first of those instructions is not 8-byte aligned,
+ insert a nop to make it aligned. */
- This could be a peephole optimization, but I don't know if the
- peephole code can call force_const_mem.
+static void
+vr4130_align_insns (void)
+{
+ struct mips_sim state;
+ rtx insn, subinsn, last, last2, next;
+ bool aligned_p;
- Using the same register for the copy of $28 and the add of the
- symbol_ref is actually pretty likely, since the add instruction
- requires the destination and the first addend to be the same
- register. */
+ dfa_start ();
- if (insn != NULL_RTX || gpcopy == NULL_RTX || slot == NULL_RTX)
+ /* LAST is the last instruction before INSN to have a nonzero length.
+ LAST2 is the last such instruction before LAST. */
+ last = 0;
+ last2 = 0;
+
+ /* ALIGNED_P is true if INSN is known to be at an aligned address. */
+ aligned_p = true;
+
+ mips_sim_init (&state, alloca (state_size ()));
+ for (insn = get_insns (); insn != 0; insn = next)
{
- rtx next;
+ unsigned int length;
- /* This optimization is only reasonable if the constant table
- entries are only 4 bytes. */
- if (Pmode != SImode)
- return;
+ next = NEXT_INSN (insn);
- for (insn = first; insn != NULL_RTX; insn = next)
- {
- rtx set1, set2;
+ /* See the comment above vr4130_avoid_branch_rt_conflict for details.
+ This isn't really related to the alignment pass, but we do it on
+ the fly to avoid a separate instruction walk. */
+ vr4130_avoid_branch_rt_conflict (insn);
- next = insn;
- do
+ if (USEFUL_INSN_P (insn))
+ FOR_EACH_SUBINSN (subinsn, insn)
+ {
+ mips_sim_wait_insn (&state, subinsn);
+
+ /* If we want this instruction to issue in parallel with the
+ previous one, make sure that the previous instruction is
+ aligned. There are several reasons why this isn't worthwhile
+ when the second instruction is a call:
+
+ - Calls are less likely to be performance critical,
+ - There's a good chance that the delay slot can execute
+ in parallel with the call.
+ - The return address would then be unaligned.
+
+ In general, if we're going to insert a nop between instructions
+ X and Y, it's better to insert it immediately after X. That
+ way, if the nop makes Y aligned, it will also align any labels
+ between X and Y. */
+ if (state.insns_left != state.issue_rate
+ && !CALL_P (subinsn))
+ {
+ if (subinsn == SEQ_BEGIN (insn) && aligned_p)
+ {
+ /* SUBINSN is the first instruction in INSN and INSN is
+ aligned. We want to align the previous instruction
+ instead, so insert a nop between LAST2 and LAST.
+
+ Note that LAST could be either a single instruction
+ or a branch with a delay slot. In the latter case,
+ LAST, like INSN, is already aligned, but the delay
+ slot must have some extra delay that stops it from
+ issuing at the same time as the branch. We therefore
+ insert a nop before the branch in order to align its
+ delay slot. */
+ emit_insn_after (gen_nop (), last2);
+ aligned_p = false;
+ }
+ else if (subinsn != SEQ_BEGIN (insn) && !aligned_p)
+ {
+ /* SUBINSN is the delay slot of INSN, but INSN is
+ currently unaligned. Insert a nop between
+ LAST and INSN to align it. */
+ emit_insn_after (gen_nop (), last);
+ aligned_p = true;
+ }
+ }
+ mips_sim_issue_insn (&state, subinsn);
+ }
+ mips_sim_finish_insn (&state, insn);
+
+ /* Update LAST, LAST2 and ALIGNED_P for the next instruction. */
+ length = get_attr_length (insn);
+ if (length > 0)
+ {
+ /* If the instruction is an asm statement or multi-instruction
+ mips.md patern, the length is only an estimate. Insert an
+ 8 byte alignment after it so that the following instructions
+ can be handled correctly. */
+ if (NONJUMP_INSN_P (SEQ_BEGIN (insn))
+ && (recog_memoized (insn) < 0 || length >= 8))
{
+ next = emit_insn_after (gen_align (GEN_INT (3)), insn);
next = NEXT_INSN (next);
+ mips_sim_next_cycle (&state);
+ aligned_p = true;
}
- while (next != NULL_RTX
- && (GET_CODE (next) == NOTE
- || (GET_CODE (next) == INSN
- && (GET_CODE (PATTERN (next)) == USE
- || GET_CODE (PATTERN (next)) == CLOBBER))));
+ else if (length & 4)
+ aligned_p = !aligned_p;
+ last2 = last;
+ last = insn;
+ }
- if (next == NULL_RTX)
- break;
+ /* See whether INSN is an aligned label. */
+ if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
+ aligned_p = true;
+ }
+ dfa_finish ();
+}
+\f
+/* This structure records that the current function has a LO_SUM
+ involving SYMBOL_REF or LABEL_REF BASE and that MAX_OFFSET is
+ the largest offset applied to BASE by all such LO_SUMs. */
+struct mips_lo_sum_offset {
+ rtx base;
+ HOST_WIDE_INT offset;
+};
- if (! INSN_P (insn))
- continue;
-
- if (! INSN_P (next))
- continue;
-
- set1 = PATTERN (insn);
- if (GET_CODE (set1) != SET)
- continue;
- set2 = PATTERN (next);
- if (GET_CODE (set2) != SET)
- continue;
-
- if (GET_CODE (SET_DEST (set1)) == REG
- && GET_CODE (SET_SRC (set1)) == CONST
- && GET_CODE (XEXP (SET_SRC (set1), 0)) == REG
- && REGNO (XEXP (SET_SRC (set1), 0)) == GP_REG_FIRST + 28
- && rtx_equal_p (SET_DEST (set1), SET_DEST (set2))
- && GET_CODE (SET_SRC (set2)) == PLUS
- && rtx_equal_p (SET_DEST (set1), XEXP (SET_SRC (set2), 0))
- && mips16_gp_offset_p (XEXP (SET_SRC (set2), 1))
- && GET_CODE (XEXP (XEXP (SET_SRC (set2), 1), 0)) == MINUS)
- {
- rtx sym;
+/* Return a hash value for SYMBOL_REF or LABEL_REF BASE. */
- /* We've found a case we can change to load from the
- constant table. */
+static hashval_t
+mips_hash_base (rtx base)
+{
+ int do_not_record_p;
- sym = XEXP (XEXP (XEXP (SET_SRC (set2), 1), 0), 0);
- if (GET_CODE (sym) != SYMBOL_REF)
- abort ();
- emit_insn_after (gen_rtx (SET, VOIDmode, SET_DEST (set1),
- force_const_mem (Pmode, sym)),
- next);
+ return hash_rtx (base, GET_MODE (base), &do_not_record_p, NULL, false);
+}
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
+/* Hash-table callbacks for mips_lo_sum_offsets. */
- PUT_CODE (next, NOTE);
- NOTE_LINE_NUMBER (next) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (next) = 0;
- }
- }
+static hashval_t
+mips_lo_sum_offset_hash (const void *entry)
+{
+ return mips_hash_base (((const struct mips_lo_sum_offset *) entry)->base);
+}
- return;
- }
+static int
+mips_lo_sum_offset_eq (const void *entry, const void *value)
+{
+ return rtx_equal_p (((const struct mips_lo_sum_offset *) entry)->base,
+ (const_rtx) value);
+}
- /* We can safely remove all assignments to SLOT from GPCOPY, and
- replace all assignments from SLOT to GPCOPY with assignments from
- $28. */
+/* Look up symbolic constant X in HTAB, which is a hash table of
+ mips_lo_sum_offsets. If OPTION is NO_INSERT, return true if X can be
+ paired with a recorded LO_SUM, otherwise record X in the table. */
- for (insn = first; insn != NULL_RTX; insn = next_active_insn (insn))
- {
- rtx set;
+static bool
+mips_lo_sum_offset_lookup (htab_t htab, rtx x, enum insert_option option)
+{
+ rtx base, offset;
+ void **slot;
+ struct mips_lo_sum_offset *entry;
- if (! INSN_P (insn))
- continue;
+ /* Split X into a base and offset. */
+ split_const (x, &base, &offset);
+ if (UNSPEC_ADDRESS_P (base))
+ base = UNSPEC_ADDRESS (base);
- set = PATTERN (insn);
- if (GET_CODE (set) != SET
- || GET_MODE (SET_DEST (set)) != (unsigned) Pmode)
- continue;
+ /* Look up the base in the hash table. */
+ slot = htab_find_slot_with_hash (htab, base, mips_hash_base (base), option);
+ if (slot == NULL)
+ return false;
- if (GET_CODE (SET_DEST (set)) == MEM
- && rtx_equal_p (SET_DEST (set), slot)
- && GET_CODE (SET_SRC (set)) == REG
- && REGNO (SET_SRC (set)) == REGNO (gpcopy))
+ entry = (struct mips_lo_sum_offset *) *slot;
+ if (option == INSERT)
+ {
+ if (entry == NULL)
{
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
+ entry = XNEW (struct mips_lo_sum_offset);
+ entry->base = base;
+ entry->offset = INTVAL (offset);
+ *slot = entry;
}
- else if (GET_CODE (SET_DEST (set)) == REG
- && REGNO (SET_DEST (set)) == REGNO (gpcopy)
- && GET_CODE (SET_SRC (set)) == MEM
- && rtx_equal_p (SET_SRC (set), slot))
+ else
{
- emit_insn_after (gen_rtx (SET, Pmode, SET_DEST (set),
- gen_rtx (CONST, Pmode,
- gen_rtx (REG, Pmode,
- GP_REG_FIRST + 28))),
- insn);
- PUT_CODE (insn, NOTE);
- NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
- NOTE_SOURCE_FILE (insn) = 0;
+ if (INTVAL (offset) > entry->offset)
+ entry->offset = INTVAL (offset);
}
}
+ return INTVAL (offset) <= entry->offset;
}
-/* We keep a list of constants we which we have to add to internal
- constant tables in the middle of large functions. */
+/* A for_each_rtx callback for which DATA is a mips_lo_sum_offset hash table.
+ Record every LO_SUM in *LOC. */
-struct constant
+static int
+mips_record_lo_sum (rtx *loc, void *data)
{
- struct constant *next;
- rtx value;
- rtx label;
- enum machine_mode mode;
-};
+ if (GET_CODE (*loc) == LO_SUM)
+ mips_lo_sum_offset_lookup ((htab_t) data, XEXP (*loc, 1), INSERT);
+ return 0;
+}
-/* Add a constant to the list in *PCONSTANTS. */
+/* Return true if INSN is a SET of an orphaned high-part relocation.
+ HTAB is a hash table of mips_lo_sum_offsets that describes all the
+ LO_SUMs in the current function. */
-static rtx
-add_constant (pconstants, val, mode)
- struct constant **pconstants;
- rtx val;
- enum machine_mode mode;
+static bool
+mips_orphaned_high_part_p (htab_t htab, rtx insn)
{
- struct constant *c;
+ enum mips_symbol_type type;
+ rtx x, set;
- for (c = *pconstants; c != NULL; c = c->next)
- if (mode == c->mode && rtx_equal_p (val, c->value))
- return c->label;
+ set = single_set (insn);
+ if (set)
+ {
+ /* Check for %his. */
+ x = SET_SRC (set);
+ if (GET_CODE (x) == HIGH
+ && absolute_symbolic_operand (XEXP (x, 0), VOIDmode))
+ return !mips_lo_sum_offset_lookup (htab, XEXP (x, 0), NO_INSERT);
- c = (struct constant *) xmalloc (sizeof *c);
- c->value = val;
- c->mode = mode;
- c->label = gen_label_rtx ();
- c->next = *pconstants;
- *pconstants = c;
- return c->label;
+ /* Check for local %gots (and %got_pages, which is redundant but OK). */
+ if (GET_CODE (x) == UNSPEC
+ && XINT (x, 1) == UNSPEC_LOAD_GOT
+ && mips_symbolic_constant_p (XVECEXP (x, 0, 1),
+ SYMBOL_CONTEXT_LEA, &type)
+ && type == SYMBOL_GOTOFF_PAGE)
+ return !mips_lo_sum_offset_lookup (htab, XVECEXP (x, 0, 1), NO_INSERT);
+ }
+ return false;
}
-/* Dump out the constants in CONSTANTS after INSN. */
+/* Subroutine of mips_reorg_process_insns. If there is a hazard between
+ INSN and a previous instruction, avoid it by inserting nops after
+ instruction AFTER.
+
+ *DELAYED_REG and *HILO_DELAY describe the hazards that apply at
+ this point. If *DELAYED_REG is non-null, INSN must wait a cycle
+ before using the value of that register. *HILO_DELAY counts the
+ number of instructions since the last hilo hazard (that is,
+ the number of instructions since the last MFLO or MFHI).
+
+ After inserting nops for INSN, update *DELAYED_REG and *HILO_DELAY
+ for the next instruction.
+
+ LO_REG is an rtx for the LO register, used in dependence checking. */
static void
-dump_constants (constants, insn)
- struct constant *constants;
- rtx insn;
+mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
+ rtx *delayed_reg, rtx lo_reg)
{
- struct constant *c;
- int align;
-
- c = constants;
- align = 0;
- while (c != NULL)
- {
- rtx r;
- struct constant *next;
+ rtx pattern, set;
+ int nops, ninsns;
- switch (GET_MODE_SIZE (c->mode))
- {
- case 1:
- align = 0;
- break;
- case 2:
- if (align < 1)
- insn = emit_insn_after (gen_align_2 (), insn);
- align = 1;
- break;
- case 4:
- if (align < 2)
- insn = emit_insn_after (gen_align_4 (), insn);
- align = 2;
- break;
- default:
- if (align < 3)
- insn = emit_insn_after (gen_align_8 (), insn);
- align = 3;
- break;
- }
+ pattern = PATTERN (insn);
- insn = emit_label_after (c->label, insn);
+ /* Do not put the whole function in .set noreorder if it contains
+ an asm statement. We don't know whether there will be hazards
+ between the asm statement and the gcc-generated code. */
+ if (GET_CODE (pattern) == ASM_INPUT || asm_noperands (pattern) >= 0)
+ cfun->machine->all_noreorder_p = false;
- switch (c->mode)
- {
- case QImode:
- r = gen_consttable_qi (c->value);
- break;
- case HImode:
- r = gen_consttable_hi (c->value);
- break;
- case SImode:
- r = gen_consttable_si (c->value);
- break;
- case SFmode:
- r = gen_consttable_sf (c->value);
- break;
- case DImode:
- r = gen_consttable_di (c->value);
- break;
- case DFmode:
- r = gen_consttable_df (c->value);
- break;
- default:
- abort ();
- }
+ /* Ignore zero-length instructions (barriers and the like). */
+ ninsns = get_attr_length (insn) / 4;
+ if (ninsns == 0)
+ return;
- insn = emit_insn_after (r, insn);
+ /* Work out how many nops are needed. Note that we only care about
+ registers that are explicitly mentioned in the instruction's pattern.
+ It doesn't matter that calls use the argument registers or that they
+ clobber hi and lo. */
+ if (*hilo_delay < 2 && reg_set_p (lo_reg, pattern))
+ nops = 2 - *hilo_delay;
+ else if (*delayed_reg != 0 && reg_referenced_p (*delayed_reg, pattern))
+ nops = 1;
+ else
+ nops = 0;
+
+ /* Insert the nops between this instruction and the previous one.
+ Each new nop takes us further from the last hilo hazard. */
+ *hilo_delay += nops;
+ while (nops-- > 0)
+ emit_insn_after (gen_hazard_nop (), after);
+
+ /* Set up the state for the next instruction. */
+ *hilo_delay += ninsns;
+ *delayed_reg = 0;
+ if (INSN_CODE (insn) >= 0)
+ switch (get_attr_hazard (insn))
+ {
+ case HAZARD_NONE:
+ break;
- next = c->next;
- free (c);
- c = next;
- }
+ case HAZARD_HILO:
+ *hilo_delay = 0;
+ break;
- emit_barrier_after (insn);
+ case HAZARD_DELAY:
+ set = single_set (insn);
+ gcc_assert (set);
+ *delayed_reg = SET_DEST (set);
+ break;
+ }
}
-/* Find the symbol in an address expression. */
+/* Go through the instruction stream and insert nops where necessary.
+ Also delete any high-part relocations whose partnering low parts
+ are now all dead. See if the whole function can then be put into
+ .set noreorder and .set nomacro. */
-static rtx
-mips_find_symbol (addr)
- rtx addr;
-{
- if (GET_CODE (addr) == MEM)
- addr = XEXP (addr, 0);
- while (GET_CODE (addr) == CONST)
- addr = XEXP (addr, 0);
- if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF)
- return addr;
- if (GET_CODE (addr) == PLUS)
- {
- rtx l1, l2;
-
- l1 = mips_find_symbol (XEXP (addr, 0));
- l2 = mips_find_symbol (XEXP (addr, 1));
- if (l1 != NULL_RTX && l2 == NULL_RTX)
- return l1;
- else if (l1 == NULL_RTX && l2 != NULL_RTX)
- return l2;
+static void
+mips_reorg_process_insns (void)
+{
+ rtx insn, last_insn, subinsn, next_insn, lo_reg, delayed_reg;
+ int hilo_delay;
+ htab_t htab;
+
+ /* Force all instructions to be split into their final form. */
+ split_all_insns_noflow ();
+
+ /* Recalculate instruction lengths without taking nops into account. */
+ cfun->machine->ignore_hazard_length_p = true;
+ shorten_branches (get_insns ());
+
+ cfun->machine->all_noreorder_p = true;
+
+ /* We don't track MIPS16 PC-relative offsets closely enough to make
+ a good job of "set .noreorder" code in MIPS16 mode. */
+ if (TARGET_MIPS16)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Code that doesn't use explicit relocs can't be ".set nomacro". */
+ if (!TARGET_EXPLICIT_RELOCS)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Profiled functions can't be all noreorder because the profiler
+ support uses assembler macros. */
+ if (crtl->profile)
+ cfun->machine->all_noreorder_p = false;
+
+ /* Code compiled with -mfix-vr4120 can't be all noreorder because
+ we rely on the assembler to work around some errata. */
+ if (TARGET_FIX_VR4120)
+ cfun->machine->all_noreorder_p = false;
+
+ /* The same is true for -mfix-vr4130 if we might generate MFLO or
+ MFHI instructions. Note that we avoid using MFLO and MFHI if
+ the VR4130 MACC and DMACC instructions are available instead;
+ see the *mfhilo_{si,di}_macc patterns. */
+ if (TARGET_FIX_VR4130 && !ISA_HAS_MACCHI)
+ cfun->machine->all_noreorder_p = false;
+
+ htab = htab_create (37, mips_lo_sum_offset_hash,
+ mips_lo_sum_offset_eq, free);
+
+ /* Make a first pass over the instructions, recording all the LO_SUMs. */
+ for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
+ FOR_EACH_SUBINSN (subinsn, insn)
+ if (INSN_P (subinsn))
+ for_each_rtx (&PATTERN (subinsn), mips_record_lo_sum, htab);
+
+ last_insn = 0;
+ hilo_delay = 2;
+ delayed_reg = 0;
+ lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
+
+ /* Make a second pass over the instructions. Delete orphaned
+ high-part relocations or turn them into NOPs. Avoid hazards
+ by inserting NOPs. */
+ for (insn = get_insns (); insn != 0; insn = next_insn)
+ {
+ next_insn = NEXT_INSN (insn);
+ if (INSN_P (insn))
+ {
+ if (GET_CODE (PATTERN (insn)) == SEQUENCE)
+ {
+ /* If we find an orphaned high-part relocation in a delay
+ slot, it's easier to turn that instruction into a NOP than
+ to delete it. The delay slot will be a NOP either way. */
+ FOR_EACH_SUBINSN (subinsn, insn)
+ if (INSN_P (subinsn))
+ {
+ if (mips_orphaned_high_part_p (htab, subinsn))
+ {
+ PATTERN (subinsn) = gen_nop ();
+ INSN_CODE (subinsn) = CODE_FOR_nop;
+ }
+ mips_avoid_hazard (last_insn, subinsn, &hilo_delay,
+ &delayed_reg, lo_reg);
+ }
+ last_insn = insn;
+ }
+ else
+ {
+ /* INSN is a single instruction. Delete it if it's an
+ orphaned high-part relocation. */
+ if (mips_orphaned_high_part_p (htab, insn))
+ delete_insn (insn);
+ /* Also delete cache barriers if the last instruction
+ was an annulled branch. INSN will not be speculatively
+ executed. */
+ else if (recog_memoized (insn) == CODE_FOR_r10k_cache_barrier
+ && last_insn
+ && INSN_ANNULLED_BRANCH_P (SEQ_BEGIN (last_insn)))
+ delete_insn (insn);
+ else
+ {
+ mips_avoid_hazard (last_insn, insn, &hilo_delay,
+ &delayed_reg, lo_reg);
+ last_insn = insn;
+ }
+ }
+ }
}
- return NULL_RTX;
+
+ htab_delete (htab);
}
-/* Exported to toplev.c.
+/* Implement TARGET_MACHINE_DEPENDENT_REORG. */
- Do a final pass over the function, just before delayed branch
- scheduling. */
+static void
+mips_reorg (void)
+{
+ mips16_lay_out_constants ();
+ if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE)
+ r10k_insert_cache_barriers ();
+ if (optimize > 0 && flag_delayed_branch)
+ dbr_schedule (get_insns ());
+ mips_reorg_process_insns ();
+ if (!TARGET_MIPS16
+ && TARGET_EXPLICIT_RELOCS
+ && TUNE_MIPS4130
+ && TARGET_VR4130_ALIGN)
+ vr4130_align_insns ();
+}
+\f
+/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text
+ in order to avoid duplicating too much logic from elsewhere. */
-void
-machine_dependent_reorg (first)
- rtx first;
+static void
+mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+ tree function)
{
- int insns_len, max_internal_pool_size, pool_size, addr, first_constant_ref;
- rtx insn;
- struct constant *constants;
+ rtx this_rtx, temp1, temp2, insn, fnaddr;
+ bool use_sibcall_p;
- if (! TARGET_MIPS16)
- return;
+ /* Pretend to be a post-reload pass while generating rtl. */
+ reload_completed = 1;
- /* If $gp is used, try to remove stores, and replace loads with
- copies from $gp. */
- if (optimize)
- mips16_optimize_gp (first);
+ /* Mark the end of the (empty) prologue. */
+ emit_note (NOTE_INSN_PROLOGUE_END);
- /* Scan the function looking for PC relative loads which may be out
- of range. All such loads will either be from the constant table,
- or be getting the address of a constant string. If the size of
- the function plus the size of the constant table is less than
- 0x8000, then all loads are in range. */
+ /* Determine if we can use a sibcall to call FUNCTION directly. */
+ fnaddr = XEXP (DECL_RTL (function), 0);
+ use_sibcall_p = (mips_function_ok_for_sibcall (function, NULL)
+ && const_call_insn_operand (fnaddr, Pmode));
- insns_len = 0;
- for (insn = first; insn; insn = NEXT_INSN (insn))
+ /* Determine if we need to load FNADDR from the GOT. */
+ if (!use_sibcall_p
+ && (mips_got_symbol_type_p
+ (mips_classify_symbol (fnaddr, SYMBOL_CONTEXT_LEA))))
{
- insns_len += get_attr_length (insn);
+ /* Pick a global pointer. Use a call-clobbered register if
+ TARGET_CALL_SAVED_GP. */
+ cfun->machine->global_pointer
+ = TARGET_CALL_SAVED_GP ? 15 : GLOBAL_POINTER_REGNUM;
+ SET_REGNO (pic_offset_table_rtx, cfun->machine->global_pointer);
- /* ??? We put switch tables in .text, but we don't define
- JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
- compute their lengths correctly. */
- if (GET_CODE (insn) == JUMP_INSN)
- {
- rtx body;
+ /* Set up the global pointer for n32 or n64 abicalls. */
+ mips_emit_loadgp ();
+ }
- body = PATTERN (insn);
- if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
- insns_len += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
- * GET_MODE_SIZE (GET_MODE (body)));
- insns_len += GET_MODE_SIZE (GET_MODE (body)) - 1;
+ /* We need two temporary registers in some cases. */
+ temp1 = gen_rtx_REG (Pmode, 2);
+ temp2 = gen_rtx_REG (Pmode, 3);
+
+ /* Find out which register contains the "this" pointer. */
+ if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
+ else
+ this_rtx = gen_rtx_REG (Pmode, GP_ARG_FIRST);
+
+ /* Add DELTA to THIS_RTX. */
+ if (delta != 0)
+ {
+ rtx offset = GEN_INT (delta);
+ if (!SMALL_OPERAND (delta))
+ {
+ mips_emit_move (temp1, offset);
+ offset = temp1;
}
+ emit_insn (gen_add3_insn (this_rtx, this_rtx, offset));
+ }
+
+ /* If needed, add *(*THIS_RTX + VCALL_OFFSET) to THIS_RTX. */
+ if (vcall_offset != 0)
+ {
+ rtx addr;
+
+ /* Set TEMP1 to *THIS_RTX. */
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, this_rtx));
+
+ /* Set ADDR to a legitimate address for *THIS_RTX + VCALL_OFFSET. */
+ addr = mips_add_offset (temp2, temp1, vcall_offset);
+
+ /* Load the offset and add it to THIS_RTX. */
+ mips_emit_move (temp1, gen_rtx_MEM (Pmode, addr));
+ emit_insn (gen_add3_insn (this_rtx, this_rtx, temp1));
+ }
+
+ /* Jump to the target function. Use a sibcall if direct jumps are
+ allowed, otherwise load the address into a register first. */
+ if (use_sibcall_p)
+ {
+ insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
+ SIBLING_CALL_P (insn) = 1;
}
+ else
+ {
+ /* This is messy. GAS treats "la $25,foo" as part of a call
+ sequence and may allow a global "foo" to be lazily bound.
+ The general move patterns therefore reject this combination.
+
+ In this context, lazy binding would actually be OK
+ for TARGET_CALL_CLOBBERED_GP, but it's still wrong for
+ TARGET_CALL_SAVED_GP; see mips_load_call_address.
+ We must therefore load the address via a temporary
+ register if mips_dangerous_for_la25_p.
+
+ If we jump to the temporary register rather than $25,
+ the assembler can use the move insn to fill the jump's
+ delay slot.
+
+ We can use the same technique for MIPS16 code, where $25
+ is not a valid JR register. */
+ if (TARGET_USE_PIC_FN_ADDR_REG
+ && !TARGET_MIPS16
+ && !mips_dangerous_for_la25_p (fnaddr))
+ temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
+ mips_load_call_address (MIPS_CALL_SIBCALL, temp1, fnaddr);
+
+ if (TARGET_USE_PIC_FN_ADDR_REG
+ && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
+ mips_emit_move (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
+ emit_jump_insn (gen_indirect_jump (temp1));
+ }
+
+ /* Run just enough of rest_of_compilation. This sequence was
+ "borrowed" from alpha.c. */
+ insn = get_insns ();
+ insn_locators_alloc ();
+ split_all_insns_noflow ();
+ mips16_lay_out_constants ();
+ shorten_branches (insn);
+ final_start_function (insn, file, 1);
+ final (insn, file, 1);
+ final_end_function ();
+ free_after_compilation (cfun);
+
+ /* Clean up the vars set above. Note that final_end_function resets
+ the global pointer for us. */
+ reload_completed = 0;
+}
+\f
+/* The last argument passed to mips_set_mips16_mode, or negative if the
+ function hasn't been called yet.
- /* Store the original value of insns_len in current_frame_info, so
- that simple_memory_operand can look at it. */
- current_frame_info.insns_len = insns_len;
+ There are two copies of this information. One is saved and restored
+ by the PCH process while the other is specific to this compiler
+ invocation. The information calculated by mips_set_mips16_mode
+ is invalid unless the two variables are the same. */
+static int was_mips16_p = -1;
+static GTY(()) int was_mips16_pch_p = -1;
- pool_size = get_pool_size ();
- if (insns_len + pool_size + mips_string_length < 0x8000)
+/* 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
+ && mips16_p == was_mips16_pch_p)
return;
- /* Loop over the insns and figure out what the maximum internal pool
- size could be. */
- max_internal_pool_size = 0;
- for (insn = first; insn; insn = NEXT_INSN (insn))
+ /* Restore base settings of various flags. */
+ target_flags = mips_base_target_flags;
+ flag_schedule_insns = mips_base_schedule_insns;
+ flag_reorder_blocks_and_partition = mips_base_reorder_blocks_and_partition;
+ flag_move_loop_invariants = mips_base_move_loop_invariants;
+ align_loops = mips_base_align_loops;
+ align_jumps = mips_base_align_jumps;
+ align_functions = mips_base_align_functions;
+
+ if (mips16_p)
{
- if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SET)
- {
- rtx src;
-
- src = mips_find_symbol (SET_SRC (PATTERN (insn)));
- if (src == NULL_RTX)
- continue;
- if (CONSTANT_POOL_ADDRESS_P (src))
- max_internal_pool_size += GET_MODE_SIZE (get_pool_mode (src));
- else if (SYMBOL_REF_FLAG (src))
- max_internal_pool_size += GET_MODE_SIZE (Pmode);
- }
- }
+ /* Switch to MIPS16 mode. */
+ target_flags |= MASK_MIPS16;
- constants = NULL;
- addr = 0;
- first_constant_ref = -1;
+ /* Don't run the scheduler before reload, since it tends to
+ increase register pressure. */
+ flag_schedule_insns = 0;
- for (insn = first; insn; insn = NEXT_INSN (insn))
- {
- if (GET_CODE (insn) == INSN
- && GET_CODE (PATTERN (insn)) == SET)
- {
- rtx val, src;
- enum machine_mode mode = VOIDmode;
+ /* Don't do hot/cold partitioning. mips16_lay_out_constants expects
+ the whole function to be in a single section. */
+ flag_reorder_blocks_and_partition = 0;
- val = NULL_RTX;
- src = mips_find_symbol (SET_SRC (PATTERN (insn)));
- if (src != NULL_RTX && CONSTANT_POOL_ADDRESS_P (src))
- {
- /* ??? This is very conservative, which means that we
- will generate too many copies of the constant table.
- The only solution would seem to be some form of
- relaxing. */
- if (((insns_len - addr)
- + max_internal_pool_size
- + get_pool_offset (src))
- >= 0x8000)
- {
- val = get_pool_constant (src);
- mode = get_pool_mode (src);
- }
- max_internal_pool_size -= GET_MODE_SIZE (get_pool_mode (src));
- }
- else if (src != NULL_RTX && SYMBOL_REF_FLAG (src))
- {
- /* Including all of mips_string_length is conservative,
- and so is including all of max_internal_pool_size. */
- if (((insns_len - addr)
- + max_internal_pool_size
- + pool_size
- + mips_string_length)
- >= 0x8000)
- {
- val = src;
- mode = Pmode;
- }
- max_internal_pool_size -= Pmode;
- }
+ /* Don't move loop invariants, because it tends to increase
+ register pressure. It also introduces an extra move in cases
+ where the constant is the first operand in a two-operand binary
+ instruction, or when it forms a register argument to a functon
+ call. */
+ flag_move_loop_invariants = 0;
- if (val != NULL_RTX)
- {
- rtx lab, newsrc;
-
- /* This PC relative load is out of range. ??? In the
- case of a string constant, we are only guessing that
- it is range, since we don't know the offset of a
- particular string constant. */
-
- lab = add_constant (&constants, val, mode);
- newsrc = gen_rtx (MEM, mode,
- gen_rtx (LABEL_REF, VOIDmode, lab));
- RTX_UNCHANGING_P (newsrc) = 1;
- PATTERN (insn) = gen_rtx (SET, VOIDmode,
- SET_DEST (PATTERN (insn)),
- newsrc);
- INSN_CODE (insn) = -1;
-
- if (first_constant_ref < 0)
- first_constant_ref = addr;
- }
- }
+ target_flags |= MASK_EXPLICIT_RELOCS;
- addr += get_attr_length (insn);
+ /* Experiments suggest we get the best overall section-anchor
+ results from using the range of an unextended LW or SW. Code
+ that makes heavy use of byte or short accesses can do better
+ with ranges of 0...31 and 0...63 respectively, but most code is
+ sensitive to the range of LW and SW instead. */
+ targetm.min_anchor_offset = 0;
+ targetm.max_anchor_offset = 127;
- /* ??? We put switch tables in .text, but we don't define
- JUMP_TABLES_IN_TEXT_SECTION, so get_attr_length will not
- compute their lengths correctly. */
- if (GET_CODE (insn) == JUMP_INSN)
- {
- rtx body;
+ if (flag_pic && !TARGET_OLDABI)
+ sorry ("MIPS16 PIC for ABIs other than o32 and o64");
- body = PATTERN (insn);
- if (GET_CODE (body) == ADDR_VEC || GET_CODE (body) == ADDR_DIFF_VEC)
- addr += (XVECLEN (body, GET_CODE (body) == ADDR_DIFF_VEC)
- * GET_MODE_SIZE (GET_MODE (body)));
- addr += GET_MODE_SIZE (GET_MODE (body)) - 1;
- }
+ if (TARGET_XGOT)
+ sorry ("MIPS16 -mxgot code");
- if (GET_CODE (insn) == BARRIER)
- {
- /* Output any constants we have accumulated. Note that we
- don't need to change ADDR, since its only use is
- subtraction from INSNS_LEN, and both would be changed by
- the same amount.
- ??? If the instructions up to the next barrier reuse a
- constant, it would often be better to continue
- accumulating. */
- if (constants != NULL)
- dump_constants (constants, insn);
- constants = NULL;
- first_constant_ref = -1;
- }
+ if (TARGET_HARD_FLOAT_ABI && !TARGET_OLDABI)
+ sorry ("hard-float MIPS16 code for ABIs other than o32 and o64");
+ }
+ else
+ {
+ /* Switch to normal (non-MIPS16) mode. */
+ target_flags &= ~MASK_MIPS16;
- if (constants != NULL
- && (NEXT_INSN (insn) == NULL
- || (first_constant_ref >= 0
- && (((addr - first_constant_ref)
- + 2 /* for alignment */
- + 2 /* for a short jump insn */
- + pool_size)
- >= 0x8000))))
+ /* Provide default values for align_* for 64-bit targets. */
+ if (TARGET_64BIT)
{
- /* If we haven't had a barrier within 0x8000 bytes of a
- constant reference or we are at the end of the function,
- emit a barrier now. */
-
- rtx label, jump, barrier;
-
- label = gen_label_rtx ();
- jump = emit_jump_insn_after (gen_jump (label), insn);
- JUMP_LABEL (jump) = label;
- LABEL_NUSES (label) = 1;
- barrier = emit_barrier_after (jump);
- emit_label_after (label, barrier);
- first_constant_ref = -1;
+ if (align_loops == 0)
+ align_loops = 8;
+ if (align_jumps == 0)
+ align_jumps = 8;
+ if (align_functions == 0)
+ align_functions = 8;
}
- }
- /* ??? If we output all references to a constant in internal
- constants table, we don't need to output the constant in the real
- constant table, but we have no way to prevent that. */
+ targetm.min_anchor_offset = -32768;
+ targetm.max_anchor_offset = 32767;
+ }
+
+ /* (Re)initialize MIPS target internals for new ISA. */
+ mips_init_relocs ();
+
+ if (was_mips16_p >= 0 || was_mips16_pch_p >= 0)
+ /* Reinitialize target-dependent state. */
+ target_reinit ();
+
+ was_mips16_p = mips16_p;
+ was_mips16_pch_p = mips16_p;
}
-/* Return nonzero if X is a SIGN or ZERO extend operator. */
-int
-extend_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+/* 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)
{
- enum rtx_code code = GET_CODE (x);
- return code == SIGN_EXTEND || code == ZERO_EXTEND;
+ mips_set_mips16_mode (mips_use_mips16_mode_p (fndecl));
}
+\f
+/* Allocate a chunk of memory for per-function machine-dependent data. */
-/* Accept any operator that can be used to shift the high half of the
- input value to the lower half, suitable for truncation. The
- remainder (the lower half of the input, and the upper half of the
- output) will be discarded. */
-int
-highpart_shift_operator (x, mode)
- rtx x;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static struct machine_function *
+mips_init_machine_status (void)
{
- enum rtx_code code = GET_CODE (x);
- return (code == LSHIFTRT
- || code == ASHIFTRT
- || code == ROTATERT
- || code == ROTATE);
+ return ((struct machine_function *)
+ ggc_alloc_cleared (sizeof (struct machine_function)));
}
-/* Return the length of INSN. LENGTH is the initial length computed by
- attributes in the machine-description file. */
+/* Return the processor associated with the given ISA level, or null
+ if the ISA isn't valid. */
-int
-mips_adjust_insn_length (insn, length)
- rtx insn;
- int length;
+static const struct mips_cpu_info *
+mips_cpu_info_from_isa (int isa)
{
- /* A unconditional jump has an unfilled delay slot if it is not part
- of a sequence. A conditional jump normally has a delay slot, but
- does not on MIPS16. */
- if (simplejump_p (insn)
- || (!TARGET_MIPS16 && (GET_CODE (insn) == JUMP_INSN
- || GET_CODE (insn) == CALL_INSN)))
- length += 4;
+ unsigned int i;
- /* All MIPS16 instructions are a measly two bytes. */
- if (TARGET_MIPS16)
- length /= 2;
+ for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+ if (mips_cpu_info_table[i].isa == isa)
+ return mips_cpu_info_table + i;
- return length;
+ return NULL;
}
-/* Output assembly instructions to peform a conditional branch.
+/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
+ with a final "000" replaced by "k". Ignore case.
- INSN is the branch instruction. OPERANDS[0] is the condition.
- OPERANDS[1] is the target of the branch. OPERANDS[2] is the target
- of the first operand to the condition. If TWO_OPERANDS_P is
- non-zero the comparison takes two operands; OPERANDS[3] will be the
- second operand.
+ Note: this function is shared between GCC and GAS. */
- If INVERTED_P is non-zero we are to branch if the condition does
- not hold. If FLOAT_P is non-zero this is a floating-point comparison.
+static bool
+mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
+ given++, canonical++;
- LENGTH is the length (in bytes) of the sequence we are to generate.
- That tells us whether to generate a simple conditional branch, or a
- reversed conditional branch around a `jr' instruction. */
-const char *
-mips_output_conditional_branch (insn,
- operands,
- two_operands_p,
- float_p,
- inverted_p,
- length)
- rtx insn;
- rtx *operands;
- int two_operands_p;
- int float_p;
- int inverted_p;
- int length;
-{
- static char buffer[200];
- /* The kind of comparison we are doing. */
- enum rtx_code code = GET_CODE (operands[0]);
- /* Non-zero if the opcode for the comparison needs a `z' indicating
- that it is a comparision against zero. */
- int need_z_p;
- /* A string to use in the assembly output to represent the first
- operand. */
- const char *op1 = "%z2";
- /* A string to use in the assembly output to represent the second
- operand. Use the hard-wired zero register if there's no second
- operand. */
- const char *op2 = (two_operands_p ? ",%z3" : ",%.");
- /* The operand-printing string for the comparison. */
- const char *const comp = (float_p ? "%F0" : "%C0");
- /* The operand-printing string for the inverted comparison. */
- const char *const inverted_comp = (float_p ? "%W0" : "%N0");
-
- /* The MIPS processors (for levels of the ISA at least two), have
- "likely" variants of each branch instruction. These instructions
- annul the instruction in the delay slot if the branch is not
- taken. */
- mips_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn));
-
- if (!two_operands_p)
- {
- /* To compute whether than A > B, for example, we normally
- subtract B from A and then look at the sign bit. But, if we
- are doing an unsigned comparison, and B is zero, we don't
- have to do the subtraction. Instead, we can just check to
- see if A is non-zero. Thus, we change the CODE here to
- reflect the simpler comparison operation. */
- switch (code)
- {
- case GTU:
- code = NE;
- break;
+ return ((*given == 0 && *canonical == 0)
+ || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
+}
- case LEU:
- code = EQ;
- break;
+/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
+ CPU name. We've traditionally allowed a lot of variation here.
- case GEU:
- /* A condition which will always be true. */
- code = EQ;
- op1 = "%.";
- break;
+ Note: this function is shared between GCC and GAS. */
- case LTU:
- /* A condition which will always be false. */
- code = NE;
- op1 = "%.";
- break;
+static bool
+mips_matching_cpu_name_p (const char *canonical, const char *given)
+{
+ /* First see if the name matches exactly, or with a final "000"
+ turned into "k". */
+ if (mips_strict_matching_cpu_name_p (canonical, given))
+ return true;
- default:
- /* Not a special case. */
- break;
- }
- }
+ /* If not, try comparing based on numerical designation alone.
+ See if GIVEN is an unadorned number, or 'r' followed by a number. */
+ if (TOLOWER (*given) == 'r')
+ given++;
+ if (!ISDIGIT (*given))
+ return false;
- /* Relative comparisons are always done against zero. But
- equality comparisons are done between two operands, and therefore
- do not require a `z' in the assembly language output. */
- need_z_p = (!float_p && code != EQ && code != NE);
- /* For comparisons against zero, the zero is not provided
- explicitly. */
- if (need_z_p)
- op2 = "";
+ /* Skip over some well-known prefixes in the canonical name,
+ hoping to find a number there too. */
+ if (TOLOWER (canonical[0]) == 'v' && TOLOWER (canonical[1]) == 'r')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r' && TOLOWER (canonical[1]) == 'm')
+ canonical += 2;
+ else if (TOLOWER (canonical[0]) == 'r')
+ canonical += 1;
- /* Begin by terminating the buffer. That way we can always use
- strcat to add to it. */
- buffer[0] = '\0';
+ return mips_strict_matching_cpu_name_p (canonical, given);
+}
- switch (length)
- {
- case 4:
- case 8:
- /* Just a simple conditional branch. */
- if (float_p)
- sprintf (buffer, "%%*b%s%%?\t%%Z2%%1",
- inverted_p ? inverted_comp : comp);
- else
- sprintf (buffer, "%%*b%s%s%%?\t%s%s,%%1",
- inverted_p ? inverted_comp : comp,
- need_z_p ? "z" : "",
- op1,
- op2);
- return buffer;
-
- case 12:
- case 16:
- {
- /* Generate a reversed conditional branch around ` j'
- instruction:
-
- .set noreorder
- .set nomacro
- bc l
- nop
- j target
- .set macro
- .set reorder
- l:
-
- */
-
- rtx orig_target;
- rtx target = gen_label_rtx ();
-
- output_asm_insn ("%(%<", 0);
- orig_target = operands[1];
- operands[1] = target;
- /* Generate the reversed comparison. This takes four
- bytes. */
- if (float_p)
- sprintf (buffer, "%%*b%s\t%%Z2%%1",
- inverted_p ? comp : inverted_comp);
- else
- sprintf (buffer, "%%*b%s%s\t%s%s,%%1",
- inverted_p ? comp : inverted_comp,
- need_z_p ? "z" : "",
- op1,
- op2);
- output_asm_insn (buffer, operands);
- operands[1] = orig_target;
-
- output_asm_insn ("nop\n\tj\t%1", operands);
-
- if (length == 16)
- output_asm_insn ("nop", 0);
- else
- {
- /* Output delay slot instruction. */
- rtx insn = final_sequence;
- final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file,
- optimize, 0, 1);
- INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
- }
- output_asm_insn ("%>%)", 0);
- ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
- CODE_LABEL_NUMBER (target));
- return "";
- }
+/* Return the mips_cpu_info entry for the processor or ISA given
+ by CPU_STRING. Return null if the string isn't recognized.
- /* We do not currently use this code. It handles jumps to
- arbitrary locations, using `jr', even across a 256MB boundary.
- We could add a -mhuge switch, and then use this code instead of
- the `j' alternative above when -mhuge was used. */
-#if 0
- case 16:
- case 20:
+ A similar function exists in GAS. */
+
+static const struct mips_cpu_info *
+mips_parse_cpu (const char *cpu_string)
+{
+ unsigned int i;
+ const char *s;
+
+ /* In the past, we allowed upper-case CPU names, but it doesn't
+ work well with the multilib machinery. */
+ for (s = cpu_string; *s != 0; s++)
+ if (ISUPPER (*s))
{
- /* Generate a reversed conditional branch around a `jr'
- instruction:
-
- .set noreorder
- .set nomacro
- .set noat
- bc l
- la $at, target
- jr $at
- .set at
- .set macro
- .set reorder
- l:
-
- Not pretty, but allows a conditional branch anywhere in the
- 32-bit address space. If the original branch is annulled,
- then the instruction in the delay slot should be executed
- only if the branch is taken. The la instruction is really
- a macro which will usually take eight bytes, but sometimes
- takes only four, if the instruction to which we're jumping
- gets its own entry in the global pointer table, which will
- happen if its a case label. The assembler will then
- generate only a four-byte sequence, rather than eight, and
- there seems to be no way to tell it not to. Thus, we can't
- just use a `.+x' addressing form; we don't know what value
- to give for `x'.
-
- So, we resort to using the explicit relocation syntax
- available in the assembler and do:
-
- lw $at,%got_page(target)($gp)
- daddiu $at,$at,%got_ofst(target)
-
- That way, this always takes up eight bytes, and we can use
- the `.+x' form. Of course, these explicit machinations
- with relocation will not work with old assemblers. Then
- again, neither do out-of-range branches, so we haven't lost
- anything. */
-
- /* The target of the reversed branch. */
- const char *const target
- = ((mips_branch_likely || length == 20) ? ".+20" : ".+16");
- const char *at_register = mips_reg_names[ASSEMBLER_SCRATCH_REGNUM];
- const char *gp_register = mips_reg_names[PIC_OFFSET_TABLE_REGNUM];
- char *c;
-
- strcpy (buffer, "%(%<%[");
- c = strchr (buffer, '\0');
- /* Generate the reversed comparision. This takes four
- bytes. */
- if (float_p)
- sprintf (c, "%%*b%s\t%%Z2%s",
- inverted_p ? comp : inverted_comp,
- target);
- else
- sprintf (c, "%%*b%s%s\t%s%s,%s",
- inverted_p ? comp : inverted_comp,
- need_z_p ? "z" : "",
- op1,
- op2,
- target);
- c = strchr (buffer, '\0');
- /* Generate the load-address, and jump. This takes twelve
- bytes, for a total of 16. */
- sprintf (c,
- "\n\tlw\t%s,%%%%got_page(%%1)(%s)\n\tdaddiu\t%s,%s,%%%%got_ofst(%%1)\n\tjr\t%s",
- at_register,
- gp_register,
- at_register,
- at_register,
- at_register);
- if (length == 20)
- /* The delay slot was unfilled. Since we're inside
- .noreorder, the assembler will not fill in the NOP for
- us, so we must do it ourselves. */
- strcat (buffer, "\n\tnop");
- strcat (buffer, "%]%>%)");
- return buffer;
+ warning (0, "CPU names must be lower case");
+ break;
}
-#endif
- default:
- abort ();
- }
+ /* 'from-abi' selects the most compatible architecture for the given
+ ABI: MIPS I for 32-bit ABIs and MIPS III for 64-bit ABIs. For the
+ EABIs, we have to decide whether we're using the 32-bit or 64-bit
+ version. */
+ if (strcasecmp (cpu_string, "from-abi") == 0)
+ return mips_cpu_info_from_isa (ABI_NEEDS_32BIT_REGS ? 1
+ : ABI_NEEDS_64BIT_REGS ? 3
+ : (TARGET_64BIT ? 3 : 1));
- /* NOTREACHED */
- return 0;
+ /* 'default' has traditionally been a no-op. Probably not very useful. */
+ if (strcasecmp (cpu_string, "default") == 0)
+ return NULL;
+
+ for (i = 0; i < ARRAY_SIZE (mips_cpu_info_table); i++)
+ if (mips_matching_cpu_name_p (mips_cpu_info_table[i].name, cpu_string))
+ return mips_cpu_info_table + i;
+
+ return NULL;
}
-/* Called to register all of our global variables with the garbage
- collector. */
+/* Set up globals to generate code for the ISA or processor
+ described by INFO. */
static void
-mips_add_gc_roots ()
+mips_set_architecture (const struct mips_cpu_info *info)
{
- ggc_add_rtx_root (&mips_load_reg, 1);
- ggc_add_rtx_root (&mips_load_reg2, 1);
- ggc_add_rtx_root (&mips_load_reg3, 1);
- ggc_add_rtx_root (&mips_load_reg4, 1);
- ggc_add_rtx_root (branch_cmp, sizeof (branch_cmp) / sizeof (rtx));
- ggc_add_rtx_root (&embedded_pic_fnaddr_rtx, 1);
- ggc_add_rtx_root (&mips16_gp_pseudo_rtx, 1);
+ if (info != 0)
+ {
+ mips_arch_info = info;
+ mips_arch = info->cpu;
+ mips_isa = info->isa;
+ }
}
-static enum processor_type
-mips_parse_cpu (cpu_string)
- const char *cpu_string;
-{
- const char *p = cpu_string;
- int seen_v = 0;
- enum processor_type cpu;
-
- /* We need to cope with the various "vr" prefixes for the NEC 4300
- and 4100 processors. */
- if (*p == 'v' || *p == 'V')
- seen_v = 1, p++;
+/* Likewise for tuning. */
- if (*p == 'r' || *p == 'R')
- p++;
+static void
+mips_set_tune (const struct mips_cpu_info *info)
+{
+ if (info != 0)
+ {
+ mips_tune_info = info;
+ mips_tune = info->cpu;
+ }
+}
- /* Since there is no difference between a R2000 and R3000 in
- terms of the scheduler, we collapse them into just an R3000. */
+/* Implement TARGET_HANDLE_OPTION. */
- cpu = PROCESSOR_DEFAULT;
- switch (*p)
+static bool
+mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+ switch (code)
{
- case '2':
- if (!strcmp (p, "2000") || !strcmp (p, "2k") || !strcmp (p, "2K"))
- cpu = PROCESSOR_R3000;
- else if (!strcmp (p, "20kc") || !strcmp (p, "20Kc") )
- cpu = PROCESSOR_R20KC;
- break;
+ case OPT_mabi_:
+ if (strcmp (arg, "32") == 0)
+ mips_abi = ABI_32;
+ else if (strcmp (arg, "o64") == 0)
+ mips_abi = ABI_O64;
+ else if (strcmp (arg, "n32") == 0)
+ mips_abi = ABI_N32;
+ else if (strcmp (arg, "64") == 0)
+ mips_abi = ABI_64;
+ else if (strcmp (arg, "eabi") == 0)
+ mips_abi = ABI_EABI;
+ else
+ return false;
+ return true;
- case '3':
- if (!strcmp (p, "3000") || !strcmp (p, "3k") || !strcmp (p, "3K"))
- cpu = PROCESSOR_R3000;
- else if (!strcmp (p, "3900"))
- cpu = PROCESSOR_R3900;
- break;
+ case OPT_march_:
+ case OPT_mtune_:
+ return mips_parse_cpu (arg) != 0;
- case '4':
- if (!strcmp (p, "4000") || !strcmp (p, "4k") || !strcmp (p, "4K"))
- cpu = PROCESSOR_R4000;
- /* The vr4100 is a non-FP ISA III processor with some extra
- instructions. */
- else if (!strcmp (p, "4100"))
- cpu = PROCESSOR_R4100;
- /* The vr4300 is a standard ISA III processor, but with a different
- pipeline. */
- else if (!strcmp (p, "4300"))
- cpu = PROCESSOR_R4300;
- /* The r4400 is exactly the same as the r4000 from the compiler's
- viewpoint. */
- else if (!strcmp (p, "4400"))
- cpu = PROCESSOR_R4000;
- else if (!strcmp (p, "4600"))
- cpu = PROCESSOR_R4600;
- else if (!strcmp (p, "4650"))
- cpu = PROCESSOR_R4650;
- /* The 4kc and 4kp processor cores are the same for
- scheduling purposes; they both implement the MIPS32
- ISA and only differ in their memory management
- methods. */
- else if (!strcmp (p, "4kc") || !strcmp (p, "4Kc")
- || !strcmp (p, "4kp") || !strcmp (p, "4Kp") )
- cpu = PROCESSOR_R4KC;
- break;
+ case OPT_mips:
+ mips_isa_option_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL)));
+ return mips_isa_option_info != 0;
- case '5':
- if (!strcmp (p, "5000") || !strcmp (p, "5k") || !strcmp (p, "5K"))
- cpu = PROCESSOR_R5000;
- else if (!strcmp (p, "5kc") || !strcmp (p, "5Kc") )
- cpu = PROCESSOR_R5KC;
- break;
+ case OPT_mno_flush_func:
+ mips_cache_flush_func = NULL;
+ return true;
- case '6':
- if (!strcmp (p, "6000") || !strcmp (p, "6k") || !strcmp (p, "6K"))
- cpu = PROCESSOR_R6000;
- break;
+ 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;
- case '8':
- if (!strcmp (p, "8000"))
- cpu = PROCESSOR_R8000;
- break;
+ case OPT_mr10k_cache_barrier_:
+ if (strcmp (arg, "load-store") == 0)
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_LOAD_STORE;
+ else if (strcmp (arg, "store") == 0)
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_STORE;
+ else if (strcmp (arg, "none") == 0)
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_NONE;
+ else
+ return false;
+ return true;
- case 'o':
- if (!strcmp (p, "orion"))
- cpu = PROCESSOR_R4600;
- break;
+ default:
+ return true;
}
-
- if (seen_v
- && cpu != PROCESSOR_R4300
- && cpu != PROCESSOR_R4100
- && cpu != PROCESSOR_R5000)
- cpu = PROCESSOR_DEFAULT;
-
- return cpu;
}
-/* Adjust the cost of INSN based on the relationship between INSN that
- is dependent on DEP_INSN through the dependence LINK. The default
- is to make no adjustment to COST.
+/* Implement OVERRIDE_OPTIONS. */
- On the MIPS, ignore the cost of anti- and output-dependencies. */
-static int
-mips_adjust_cost (insn, link, dep, cost)
- rtx insn ATTRIBUTE_UNUSED;
- rtx link;
- rtx dep ATTRIBUTE_UNUSED;
- int cost;
+void
+mips_override_options (void)
{
- if (REG_NOTE_KIND (link) != 0)
- return 0; /* Anti or output dependence. */
- return cost;
-}
+ int i, start, regno, mode;
-/* Cover function for UNIQUE_SECTION. */
+ /* Process flags as though we were generating non-MIPS16 code. */
+ mips_base_mips16 = TARGET_MIPS16;
+ target_flags &= ~MASK_MIPS16;
-void
-mips_unique_section (decl, reloc)
- tree decl;
- int reloc;
-{
- int len, size, sec;
- const char *name, *prefix;
- char *string;
- static const char *const prefixes[4][2] = {
- { ".text.", ".gnu.linkonce.t." },
- { ".rodata.", ".gnu.linkonce.r." },
- { ".data.", ".gnu.linkonce.d." },
- { ".sdata.", ".gnu.linkonce.s." }
- };
+#ifdef SUBTARGET_OVERRIDE_OPTIONS
+ SUBTARGET_OVERRIDE_OPTIONS;
+#endif
- name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
- size = int_size_in_bytes (TREE_TYPE (decl));
+ /* Set the small data limit. */
+ mips_small_data_threshold = (g_switch_set
+ ? g_switch_value
+ : MIPS_DEFAULT_GVALUE);
- /* Determine the base section we are interested in:
- 0=text, 1=rodata, 2=data, 3=sdata, [4=bss]. */
- if (TREE_CODE (decl) == FUNCTION_DECL)
- sec = 0;
- else if (DECL_INITIAL (decl) == 0
- || DECL_INITIAL (decl) == error_mark_node)
- sec = 2;
- else if ((TARGET_EMBEDDED_PIC || TARGET_MIPS16)
- && TREE_CODE (decl) == STRING_CST
- && !flag_writable_strings)
- {
- /* For embedded position independent code, put constant
- strings in the text section, because the data section
- is limited to 64K in size. For mips16 code, put
- strings in the text section so that a PC relative load
- instruction can be used to get their address. */
- sec = 0;
+ /* The following code determines the architecture and register size.
+ Similar code was added to GAS 2.14 (see tc-mips.c:md_after_parse_args()).
+ The GAS and GCC code should be kept in sync as much as possible. */
+
+ if (mips_arch_string != 0)
+ mips_set_architecture (mips_parse_cpu (mips_arch_string));
+
+ if (mips_isa_option_info != 0)
+ {
+ if (mips_arch_info == 0)
+ mips_set_architecture (mips_isa_option_info);
+ else if (mips_arch_info->isa != mips_isa_option_info->isa)
+ error ("%<-%s%> conflicts with the other architecture options, "
+ "which specify a %s processor",
+ mips_isa_option_info->name,
+ mips_cpu_info_from_isa (mips_arch_info->isa)->name);
}
- else if (TARGET_EMBEDDED_DATA)
+
+ if (mips_arch_info == 0)
{
- /* For embedded applications, always put an object in
- read-only data if possible, in order to reduce RAM
- usage. */
+#ifdef MIPS_CPU_STRING_DEFAULT
+ mips_set_architecture (mips_parse_cpu (MIPS_CPU_STRING_DEFAULT));
+#else
+ mips_set_architecture (mips_cpu_info_from_isa (MIPS_ISA_DEFAULT));
+#endif
+ }
- if (DECL_READONLY_SECTION (decl, reloc))
- sec = 1;
- else if (size > 0 && size <= mips_section_threshold)
- sec = 3;
- else
- sec = 2;
+ if (ABI_NEEDS_64BIT_REGS && !ISA_HAS_64BIT_REGS)
+ error ("%<-march=%s%> is not compatible with the selected ABI",
+ mips_arch_info->name);
+
+ /* Optimize for mips_arch, unless -mtune selects a different processor. */
+ if (mips_tune_string != 0)
+ mips_set_tune (mips_parse_cpu (mips_tune_string));
+
+ if (mips_tune_info == 0)
+ mips_set_tune (mips_arch_info);
+
+ if ((target_flags_explicit & MASK_64BIT) != 0)
+ {
+ /* The user specified the size of the integer registers. Make sure
+ it agrees with the ABI and ISA. */
+ if (TARGET_64BIT && !ISA_HAS_64BIT_REGS)
+ error ("%<-mgp64%> used with a 32-bit processor");
+ else if (!TARGET_64BIT && ABI_NEEDS_64BIT_REGS)
+ error ("%<-mgp32%> used with a 64-bit ABI");
+ else if (TARGET_64BIT && ABI_NEEDS_32BIT_REGS)
+ error ("%<-mgp64%> used with a 32-bit ABI");
}
else
{
- /* For hosted applications, always put an object in
- small data if possible, as this gives the best
- performance. */
+ /* Infer the integer register size from the ABI and processor.
+ Restrict ourselves to 32-bit registers if that's all the
+ processor has, or if the ABI cannot handle 64-bit registers. */
+ if (ABI_NEEDS_32BIT_REGS || !ISA_HAS_64BIT_REGS)
+ target_flags &= ~MASK_64BIT;
+ else
+ target_flags |= MASK_64BIT;
+ }
- if (size > 0 && size <= mips_section_threshold)
- sec = 3;
- else if (DECL_READONLY_SECTION (decl, reloc))
- sec = 1;
+ if ((target_flags_explicit & MASK_FLOAT64) != 0)
+ {
+ if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
+ error ("unsupported combination: %s", "-mfp64 -msingle-float");
+ else if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
+ error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
+ else if (!TARGET_64BIT && TARGET_FLOAT64)
+ {
+ if (!ISA_HAS_MXHC1)
+ error ("%<-mgp32%> and %<-mfp64%> can only be combined if"
+ " the target supports the mfhc1 and mthc1 instructions");
+ else if (mips_abi != ABI_32)
+ error ("%<-mgp32%> and %<-mfp64%> can only be combined when using"
+ " the o32 ABI");
+ }
+ }
+ else
+ {
+ /* -msingle-float selects 32-bit float registers. Otherwise the
+ float registers should be the same size as the integer ones. */
+ if (TARGET_64BIT && TARGET_DOUBLE_FLOAT)
+ target_flags |= MASK_FLOAT64;
else
- sec = 2;
+ target_flags &= ~MASK_FLOAT64;
}
- prefix = prefixes[sec][DECL_ONE_ONLY (decl)];
- len = strlen (name) + strlen (prefix);
- string = alloca (len + 1);
- sprintf (string, "%s%s", prefix, name);
+ /* End of code shared with GAS. */
- DECL_SECTION_NAME (decl) = build_string (len, string);
-}
+ /* If no -mlong* option was given, infer it from the other options. */
+ if ((target_flags_explicit & MASK_LONG64) == 0)
+ {
+ if ((mips_abi == ABI_EABI && TARGET_64BIT) || mips_abi == ABI_64)
+ target_flags |= MASK_LONG64;
+ else
+ target_flags &= ~MASK_LONG64;
+ }
-unsigned int
-mips_hard_regno_nregs (regno, mode)
- int regno;
- enum machine_mode mode;
-{
- if (! FP_REG_P (regno))
- return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
+ if (!TARGET_OLDABI)
+ flag_pcc_struct_return = 0;
+
+ /* Decide which rtx_costs structure to use. */
+ if (optimize_size)
+ mips_cost = &mips_rtx_cost_optimize_size;
else
- return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
-}
+ mips_cost = &mips_rtx_cost_data[mips_tune];
+
+ /* If the user hasn't specified a branch cost, use the processor's
+ default. */
+ if (mips_branch_cost == 0)
+ mips_branch_cost = mips_cost->branch_cost;
+
+ /* If neither -mbranch-likely nor -mno-branch-likely was given
+ on the command line, set MASK_BRANCHLIKELY based on the target
+ architecture and tuning flags. Annulled delay slots are a
+ size win, so we only consider the processor-specific tuning
+ for !optimize_size. */
+ if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
+ {
+ if (ISA_HAS_BRANCHLIKELY
+ && (optimize_size
+ || (mips_tune_info->tune_flags & PTF_AVOID_BRANCHLIKELY) == 0))
+ target_flags |= MASK_BRANCHLIKELY;
+ else
+ target_flags &= ~MASK_BRANCHLIKELY;
+ }
+ else if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
+ warning (0, "the %qs architecture does not support branch-likely"
+ " instructions", mips_arch_info->name);
-\f
-#ifdef TARGET_IRIX6
-/* Output assembly to switch to section NAME with attribute FLAGS. */
+ /* The effect of -mabicalls isn't defined for the EABI. */
+ if (mips_abi == ABI_EABI && TARGET_ABICALLS)
+ {
+ error ("unsupported combination: %s", "-mabicalls -mabi=eabi");
+ target_flags &= ~MASK_ABICALLS;
+ }
-static void
-iris6_asm_named_section_1 (name, flags, align)
- const char *name;
- unsigned int flags;
- unsigned int align;
-{
- unsigned int sh_type, sh_flags, sh_entsize;
-
- sh_flags = 0;
- if (!(flags & SECTION_DEBUG))
- sh_flags |= 2; /* SHF_ALLOC */
- if (flags & SECTION_WRITE)
- sh_flags |= 1; /* SHF_WRITE */
- if (flags & SECTION_CODE)
- sh_flags |= 4; /* SHF_EXECINSTR */
- if (flags & SECTION_SMALL)
- sh_flags |= 0x10000000; /* SHF_MIPS_GPREL */
- if (strcmp (name, ".debug_frame") == 0)
- sh_flags |= 0x08000000; /* SHF_MIPS_NOSTRIP */
- if (flags & SECTION_DEBUG)
- sh_type = 0x7000001e; /* SHT_MIPS_DWARF */
- else if (flags & SECTION_BSS)
- sh_type = 8; /* SHT_NOBITS */
- else
- sh_type = 1; /* SHT_PROGBITS */
+ if (TARGET_ABICALLS_PIC2)
+ /* 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.)
- if (flags & SECTION_CODE)
- sh_entsize = 4;
- else
- sh_entsize = 0;
+ There is a body of code that uses __PIC__ to distinguish
+ between -mabicalls and -mno-abicalls code. The non-__PIC__
+ variant is usually appropriate for TARGET_ABICALLS_PIC0, as
+ long as any indirect jumps use $25. */
+ flag_pic = 1;
- fprintf (asm_out_file, "\t.section %s,%#x,%#x,%u,%u\n",
- name, sh_type, sh_flags, sh_entsize, align);
-}
+ /* -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
+ above. */
+ if (optimize > 2 && (target_flags_explicit & MASK_VR4130_ALIGN) == 0)
+ target_flags |= MASK_VR4130_ALIGN;
-static void
-iris6_asm_named_section (name, flags)
- const char *name;
- unsigned int flags;
-{
- if (TARGET_FILE_SWITCHING && (flags & SECTION_CODE))
- asm_out_file = asm_out_text_file;
- iris6_asm_named_section_1 (name, flags, 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;
+
+ /* If we have a nonzero small-data limit, check that the -mgpopt
+ setting is consistent with the other target flags. */
+ if (mips_small_data_threshold > 0)
+ {
+ if (!TARGET_GPOPT)
+ {
+ if (!TARGET_EXPLICIT_RELOCS)
+ error ("%<-mno-gpopt%> needs %<-mexplicit-relocs%>");
-/* In addition to emitting a .align directive, record the maximum
- alignment requested for the current section. */
+ TARGET_LOCAL_SDATA = false;
+ TARGET_EXTERN_SDATA = false;
+ }
+ else
+ {
+ if (TARGET_VXWORKS_RTP)
+ warning (0, "cannot use small-data accesses for %qs", "-mrtp");
-struct iris_section_align_entry
-{
- const char *name;
- unsigned int log;
- unsigned int flags;
-};
+ if (TARGET_ABICALLS)
+ warning (0, "cannot use small-data accesses for %qs",
+ "-mabicalls");
+ }
+ }
-static htab_t iris_section_align_htab;
-static FILE *iris_orig_asm_out_file;
+#ifdef MIPS_TFMODE_FORMAT
+ REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
+#endif
-static int
-iris_section_align_entry_eq (p1, p2)
- const PTR p1;
- const PTR p2;
-{
- const struct iris_section_align_entry *old = p1;
- const char *new = p2;
+ /* Make sure that the user didn't turn off paired single support when
+ MIPS-3D support is requested. */
+ if (TARGET_MIPS3D
+ && (target_flags_explicit & MASK_PAIRED_SINGLE_FLOAT)
+ && !TARGET_PAIRED_SINGLE_FLOAT)
+ error ("%<-mips3d%> requires %<-mpaired-single%>");
+
+ /* If TARGET_MIPS3D, enable MASK_PAIRED_SINGLE_FLOAT. */
+ if (TARGET_MIPS3D)
+ target_flags |= MASK_PAIRED_SINGLE_FLOAT;
+
+ /* Make sure that when TARGET_PAIRED_SINGLE_FLOAT is true, TARGET_FLOAT64
+ and TARGET_HARD_FLOAT_ABI are both true. */
+ if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT_ABI))
+ error ("%qs must be used with %qs",
+ TARGET_MIPS3D ? "-mips3d" : "-mpaired-single",
+ TARGET_HARD_FLOAT_ABI ? "-mfp64" : "-mhard-float");
+
+ /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
+ enabled. */
+ if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_HAS_PAIRED_SINGLE)
+ warning (0, "the %qs architecture does not support paired-single"
+ " instructions", mips_arch_info->name);
+
+ if (mips_r10k_cache_barrier != R10K_CACHE_BARRIER_NONE
+ && !TARGET_CACHE_BUILTIN)
+ {
+ error ("%qs requires a target that provides the %qs instruction",
+ "-mr10k-cache-barrier", "cache");
+ mips_r10k_cache_barrier = R10K_CACHE_BARRIER_NONE;
+ }
+
+ /* If TARGET_DSPR2, enable MASK_DSP. */
+ if (TARGET_DSPR2)
+ target_flags |= MASK_DSP;
+
+ /* Use the traditional method of generating .eh_frames.
+ We need this for two reasons:
+
+ - .eh_frame addresses should be the same width as a C pointer.
+ Most MIPS ABIs support only one pointer size, so the assembler
+ will usually know exactly how big an .eh_frame address is.
+
+ Unfortunately, this is not true of the 64-bit EABI. The ABI was
+ originally defined to use 64-bit pointers (i.e. it is LP64), and
+ this is still the default mode. However, we also support an n32-like
+ ILP32 mode, which is selected by -mlong32. The problem is that the
+ assembler has traditionally not had an -mlong option, so it has
+ traditionally not known whether we're using the ILP32 or LP64 form.
+
+ As it happens, gas versions up to and including 2.19 use _32-bit_
+ addresses for EABI64 .cfi_* directives. This is wrong for the
+ default LP64 mode, so we can't use the directives by default.
+ Moreover, since gas's current behavior is at odds with gcc's
+ default behavior, it seems unwise to rely on future versions
+ of gas behaving the same way. We therefore avoid using .cfi
+ directives for -mlong32 as well.
+
+ - .cfi* directives generate read-only .eh_frame sections.
+ However, MIPS has traditionally not allowed directives like:
+
+ .long x-.
+
+ in cases where "x" is in a different section, or is not defined
+ in the same assembly file. We have therefore traditionally
+ used absolute addresses and a writable .eh_frame instead.
+
+ The linker is able to convert most of these absolute addresses
+ into PC-relative form where doing so is necessary to avoid
+ relocations. However, until 2.21, it wasn't able to do this
+ for indirect encodings or personality routines.
+
+ GNU ld 2.21 and GCC 4.5 have support for read-only .eh_frames,
+ but for the time being, we should stick to the approach used
+ in 4.3 and earlier. */
+ flag_dwarf2_cfi_asm = 0;
+
+ mips_init_print_operand_punct ();
- return strcmp (old->name, new) == 0;
-}
+ /* Set up array to map GCC register number to debug register number.
+ Ignore the special purpose register numbers. */
-static hashval_t
-iris_section_align_entry_hash (p)
- const PTR p;
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ {
+ mips_dbx_regno[i] = INVALID_REGNUM;
+ if (GP_REG_P (i) || FP_REG_P (i) || ALL_COP_REG_P (i))
+ mips_dwarf_regno[i] = i;
+ else
+ mips_dwarf_regno[i] = INVALID_REGNUM;
+ }
+
+ start = GP_DBX_FIRST - GP_REG_FIRST;
+ for (i = GP_REG_FIRST; i <= GP_REG_LAST; i++)
+ mips_dbx_regno[i] = i + start;
+
+ start = FP_DBX_FIRST - FP_REG_FIRST;
+ for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+ mips_dbx_regno[i] = i + start;
+
+ /* Accumulator debug registers use big-endian ordering. */
+ mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0;
+ mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1;
+ mips_dwarf_regno[HI_REGNUM] = MD_REG_FIRST + 0;
+ mips_dwarf_regno[LO_REGNUM] = MD_REG_FIRST + 1;
+ for (i = DSP_ACC_REG_FIRST; i <= DSP_ACC_REG_LAST; i += 2)
+ {
+ mips_dwarf_regno[i + TARGET_LITTLE_ENDIAN] = i;
+ mips_dwarf_regno[i + TARGET_BIG_ENDIAN] = i + 1;
+ }
+
+ /* Set up mips_hard_regno_mode_ok. */
+ for (mode = 0; mode < MAX_MACHINE_MODE; mode++)
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ mips_hard_regno_mode_ok[(int)mode][regno]
+ = mips_hard_regno_mode_ok_p (regno, mode);
+
+ /* Function to allocate machine-dependent function status. */
+ init_machine_status = &mips_init_machine_status;
+
+ /* Default to working around R4000 errata only if the processor
+ was selected explicitly. */
+ if ((target_flags_explicit & MASK_FIX_R4000) == 0
+ && mips_matching_cpu_name_p (mips_arch_info->name, "r4000"))
+ target_flags |= MASK_FIX_R4000;
+
+ /* Default to working around R4400 errata only if the processor
+ was selected explicitly. */
+ if ((target_flags_explicit & MASK_FIX_R4400) == 0
+ && mips_matching_cpu_name_p (mips_arch_info->name, "r4400"))
+ target_flags |= MASK_FIX_R4400;
+
+ /* Default to working around R10000 errata only if the processor
+ was selected explicitly. */
+ if ((target_flags_explicit & MASK_FIX_R10000) == 0
+ && mips_matching_cpu_name_p (mips_arch_info->name, "r10000"))
+ target_flags |= MASK_FIX_R10000;
+
+ /* Make sure that branch-likely instructions available when using
+ -mfix-r10000. The instructions are not available if either:
+
+ 1. -mno-branch-likely was passed.
+ 2. The selected ISA does not support branch-likely and
+ the command line does not include -mbranch-likely. */
+ if (TARGET_FIX_R10000
+ && ((target_flags_explicit & MASK_BRANCHLIKELY) == 0
+ ? !ISA_HAS_BRANCHLIKELY
+ : !TARGET_BRANCHLIKELY))
+ sorry ("%qs requires branch-likely instructions", "-mfix-r10000");
+
+ /* Save base state of options. */
+ 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_move_loop_invariants = flag_move_loop_invariants;
+ mips_base_align_loops = align_loops;
+ mips_base_align_jumps = align_jumps;
+ mips_base_align_functions = align_functions;
+
+ /* Now select the ISA mode.
+
+ Do all CPP-sensitive stuff in non-MIPS16 mode; we'll switch to
+ MIPS16 mode afterwards if need be. */
+ mips_set_mips16_mode (false);
+}
+
+/* Swap the register information for registers I and I + 1, which
+ currently have the wrong endianness. Note that the registers'
+ fixedness and call-clobberedness might have been set on the
+ command line. */
+
+static void
+mips_swap_registers (unsigned int i)
{
- const struct iris_section_align_entry *old = p;
- return htab_hash_string (old->name);
+ int tmpi;
+ const char *tmps;
+
+#define SWAP_INT(X, Y) (tmpi = (X), (X) = (Y), (Y) = tmpi)
+#define SWAP_STRING(X, Y) (tmps = (X), (X) = (Y), (Y) = tmps)
+
+ SWAP_INT (fixed_regs[i], fixed_regs[i + 1]);
+ SWAP_INT (call_used_regs[i], call_used_regs[i + 1]);
+ SWAP_INT (call_really_used_regs[i], call_really_used_regs[i + 1]);
+ SWAP_STRING (reg_names[i], reg_names[i + 1]);
+
+#undef SWAP_STRING
+#undef SWAP_INT
}
+/* Implement CONDITIONAL_REGISTER_USAGE. */
+
void
-iris6_asm_output_align (file, log)
- FILE *file;
- unsigned int log;
+mips_conditional_register_usage (void)
{
- const char *section = current_section_name ();
- struct iris_section_align_entry **slot, *entry;
- if (! section)
- abort ();
+ if (ISA_HAS_DSP)
+ {
+ /* These DSP control register fields are global. */
+ global_regs[CCDSP_PO_REGNUM] = 1;
+ global_regs[CCDSP_SC_REGNUM] = 1;
+ }
+ else
+ {
+ int regno;
+
+ for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ }
+ if (!TARGET_HARD_FLOAT)
+ {
+ int regno;
- slot = (struct iris_section_align_entry **)
- htab_find_slot_with_hash (iris_section_align_htab, section,
- htab_hash_string (section), INSERT);
- entry = *slot;
- if (! entry)
+ for (regno = FP_REG_FIRST; regno <= FP_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ }
+ else if (! ISA_HAS_8CC)
{
- entry = (struct iris_section_align_entry *)
- xmalloc (sizeof (struct iris_section_align_entry));
- *slot = entry;
- entry->name = section;
- entry->log = log;
- entry->flags = current_section_flags ();
+ int regno;
+
+ /* We only have a single condition-code register. We implement
+ this by fixing all the condition-code registers and generating
+ RTL that refers directly to ST_REG_FIRST. */
+ for (regno = ST_REG_FIRST; regno <= ST_REG_LAST; regno++)
+ fixed_regs[regno] = call_used_regs[regno] = 1;
+ }
+ /* In MIPS16 mode, we permit the $t temporary registers to be used
+ for reload. We prohibit the unused $s registers, since they
+ are call-saved, and saving them via a MIPS16 register would
+ probably waste more time than just reloading the value. */
+ if (TARGET_MIPS16)
+ {
+ fixed_regs[18] = call_used_regs[18] = 1;
+ fixed_regs[19] = call_used_regs[19] = 1;
+ fixed_regs[20] = call_used_regs[20] = 1;
+ fixed_regs[21] = call_used_regs[21] = 1;
+ fixed_regs[22] = call_used_regs[22] = 1;
+ fixed_regs[23] = call_used_regs[23] = 1;
+ fixed_regs[26] = call_used_regs[26] = 1;
+ fixed_regs[27] = call_used_regs[27] = 1;
+ fixed_regs[30] = call_used_regs[30] = 1;
}
- else if (entry->log < log)
- entry->log = log;
+ /* $f20-$f23 are call-clobbered for n64. */
+ if (mips_abi == ABI_64)
+ {
+ int regno;
+ for (regno = FP_REG_FIRST + 20; regno < FP_REG_FIRST + 24; regno++)
+ call_really_used_regs[regno] = call_used_regs[regno] = 1;
+ }
+ /* Odd registers in the range $f21-$f31 (inclusive) are call-clobbered
+ for n32. */
+ if (mips_abi == ABI_N32)
+ {
+ int regno;
+ for (regno = FP_REG_FIRST + 21; regno <= FP_REG_FIRST + 31; regno+=2)
+ call_really_used_regs[regno] = call_used_regs[regno] = 1;
+ }
+ /* Make sure that double-register accumulator values are correctly
+ ordered for the current endianness. */
+ if (TARGET_LITTLE_ENDIAN)
+ {
+ unsigned int regno;
- fprintf (file, "\t.align\t%u\n", log);
+ mips_swap_registers (MD_REG_FIRST);
+ for (regno = DSP_ACC_REG_FIRST; regno <= DSP_ACC_REG_LAST; regno += 2)
+ mips_swap_registers (regno);
+ }
}
-/* The Iris assembler does not record alignment from .align directives,
- but takes it from the first .section directive seen. Play yet more
- file switching games so that we can emit a .section directive at the
- beginning of the file with the proper alignment attached. */
+/* Initialize vector TARGET to VALS. */
void
-iris6_asm_file_start (stream)
- FILE *stream;
+mips_expand_vector_init (rtx target, rtx vals)
{
- mips_asm_file_start (stream);
+ enum machine_mode mode;
+ enum machine_mode inner;
+ unsigned int i, n_elts;
+ rtx mem;
- iris_orig_asm_out_file = asm_out_file;
- stream = tmpfile ();
- asm_out_file = stream;
- asm_out_data_file = stream;
- if (! TARGET_FILE_SWITCHING)
- asm_out_text_file = stream;
+ mode = GET_MODE (target);
+ inner = GET_MODE_INNER (mode);
+ n_elts = GET_MODE_NUNITS (mode);
- iris_section_align_htab = htab_create (31, iris_section_align_entry_hash,
- iris_section_align_entry_eq, NULL);
-}
+ gcc_assert (VECTOR_MODE_P (mode));
-static int
-iris6_section_align_1 (slot, data)
- void **slot;
- void *data ATTRIBUTE_UNUSED;
-{
- const struct iris_section_align_entry *entry
- = *(const struct iris_section_align_entry **) slot;
+ mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+ for (i = 0; i < n_elts; i++)
+ emit_move_insn (adjust_address_nv (mem, inner, i * GET_MODE_SIZE (inner)),
+ XVECEXP (vals, 0, i));
- iris6_asm_named_section_1 (entry->name, entry->flags, 1 << entry->log);
- return 1;
+ emit_move_insn (target, mem);
}
+/* When generating MIPS16 code, we want to allocate $24 (T_REG) before
+ other registers for instructions for which it is possible. This
+ encourages the compiler to use CMP in cases where an XOR would
+ require some register shuffling. */
+
void
-iris6_asm_file_end (stream)
- FILE *stream;
+mips_order_regs_for_local_alloc (void)
{
- /* Emit section directives with the proper alignment at the top of the
- real output file. */
- asm_out_file = iris_orig_asm_out_file;
- htab_traverse (iris_section_align_htab, iris6_section_align_1, NULL);
+ int i;
- /* Copy the data emitted to the temp file to the real output file. */
- copy_file_data (asm_out_file, stream);
+ for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+ reg_alloc_order[i] = i;
- mips_asm_file_end (stream);
+ if (TARGET_MIPS16)
+ {
+ /* It really doesn't matter where we put register 0, since it is
+ a fixed register anyhow. */
+ reg_alloc_order[0] = 24;
+ reg_alloc_order[24] = 0;
+ }
}
-#endif /* TARGET_IRIX6 */
+\f
+/* Initialize the GCC target structure. */
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
+
+#undef TARGET_ASM_FUNCTION_PROLOGUE
+#define TARGET_ASM_FUNCTION_PROLOGUE mips_output_function_prologue
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE mips_output_function_epilogue
+#undef TARGET_ASM_SELECT_RTX_SECTION
+#define TARGET_ASM_SELECT_RTX_SECTION mips_select_rtx_section
+#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
+#define TARGET_SCHED_ADJUST_COST mips_adjust_cost
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE mips_issue_rate
+#undef TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN
+#define TARGET_SCHED_INIT_DFA_POST_CYCLE_INSN mips_init_dfa_post_cycle_insn
+#undef TARGET_SCHED_DFA_POST_ADVANCE_CYCLE
+#define TARGET_SCHED_DFA_POST_ADVANCE_CYCLE mips_dfa_post_advance_cycle
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD \
+ mips_multipass_dfa_lookahead
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS \
+ (TARGET_DEFAULT \
+ | TARGET_CPU_DEFAULT \
+ | TARGET_ENDIAN_DEFAULT \
+ | TARGET_FP_EXCEPTIONS_DEFAULT \
+ | MASK_CHECK_ZERO_DIV \
+ | MASK_FUSED_MADD)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION mips_handle_option
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL mips_function_ok_for_sibcall
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES mips_insert_attributes
+#undef TARGET_MERGE_DECL_ATTRIBUTES
+#define TARGET_MERGE_DECL_ATTRIBUTES mips_merge_decl_attributes
+#undef TARGET_SET_CURRENT_FUNCTION
+#define TARGET_SET_CURRENT_FUNCTION mips_set_current_function
+
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE mips_valid_pointer_mode
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS mips_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST mips_address_cost
+
+#undef TARGET_IN_SMALL_DATA_P
+#define TARGET_IN_SMALL_DATA_P mips_in_small_data_p
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG mips_reorg
+
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START mips_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS mips_init_libfuncs
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST mips_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START mips_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR mips_gimplify_va_arg_expr
+
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#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_RETURN_IN_MSB
+#define TARGET_RETURN_IN_MSB mips_return_in_msb
+
+#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_const_tree_hwi_hwi_const_tree_true
+
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS mips_setup_incoming_varargs
+#undef TARGET_STRICT_ARGUMENT_NAMING
+#define TARGET_STRICT_ARGUMENT_NAMING mips_strict_argument_naming
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE mips_pass_by_reference
+#undef TARGET_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES mips_callee_copies
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES mips_arg_partial_bytes
+
+#undef TARGET_MODE_REP_EXTENDED
+#define TARGET_MODE_REP_EXTENDED mips_mode_rep_extended
+
+#undef TARGET_VECTOR_MODE_SUPPORTED_P
+#define TARGET_VECTOR_MODE_SUPPORTED_P mips_vector_mode_supported_p
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P mips_scalar_mode_supported_p
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS mips_init_builtins
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN mips_expand_builtin
+
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
+
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM mips_cannot_force_const_mem
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO mips_encode_section_info
+
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE mips_attribute_table
+/* All our function attributes are related to how out-of-line copies should
+ be compiled or called. They don't in themselves prevent inlining. */
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
+
+#undef TARGET_EXTRA_LIVE_ON_ENTRY
+#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
+
+#undef TARGET_USE_BLOCKS_FOR_CONSTANT_P
+#define TARGET_USE_BLOCKS_FOR_CONSTANT_P mips_use_blocks_for_constant_p
+#undef TARGET_USE_ANCHORS_FOR_SYMBOL_P
+#define TARGET_USE_ANCHORS_FOR_SYMBOL_P mips_use_anchors_for_symbol_p
+
+#undef TARGET_COMP_TYPE_ATTRIBUTES
+#define TARGET_COMP_TYPE_ATTRIBUTES mips_comp_type_attributes
+
+#ifdef HAVE_AS_DTPRELWORD
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL mips_output_dwarf_dtprel
+#endif
+#undef TARGET_DWARF_REGISTER_SPAN
+#define TARGET_DWARF_REGISTER_SPAN mips_dwarf_register_span
+
+#undef TARGET_IRA_COVER_CLASSES
+#define TARGET_IRA_COVER_CLASSES mips_ira_cover_classes
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+\f
+#include "gt-mips.h"