aboutsummaryrefslogtreecommitdiff
path: root/contrib/gcc/config/mips/mips.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/gcc/config/mips/mips.c')
-rw-r--r--contrib/gcc/config/mips/mips.c10870
1 files changed, 0 insertions, 10870 deletions
diff --git a/contrib/gcc/config/mips/mips.c b/contrib/gcc/config/mips/mips.c
deleted file mode 100644
index 5a7792ccb7c0..000000000000
--- a/contrib/gcc/config/mips/mips.c
+++ /dev/null
@@ -1,10870 +0,0 @@
-/* Subroutines used for MIPS code generation.
- Copyright (C) 1989, 1990, 1991, 1993, 1994, 1995, 1996, 1997, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004, 2005 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
- Brendan Eich, brendan@microunity.com.
-
-This file is part of GCC.
-
-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)
-any later version.
-
-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 GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
-
-#include "config.h"
-#include "system.h"
-#include "coretypes.h"
-#include "tm.h"
-#include <signal.h>
-#include "rtl.h"
-#include "regs.h"
-#include "hard-reg-set.h"
-#include "real.h"
-#include "insn-config.h"
-#include "conditions.h"
-#include "insn-attr.h"
-#include "recog.h"
-#include "toplev.h"
-#include "output.h"
-#include "tree.h"
-#include "function.h"
-#include "expr.h"
-#include "optabs.h"
-#include "flags.h"
-#include "reload.h"
-#include "tm_p.h"
-#include "ggc.h"
-#include "gstab.h"
-#include "hashtab.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 "tree-gimple.h"
-#include "bitmap.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 & restore registers.
-
- Use a maximum gap of 0x100 in the mips16 case. We can then use
- unextended instructions to save and restore registers, and to
- allocate and deallocate the top part of the frame.
-
- The value in the !mips16 case must be a SMALL_OPERAND and must
- preserve the maximum stack alignment. */
-#define MIPS_MAX_FIRST_STACK_STEP (TARGET_MIPS16 ? 0x100 : 0x7ff0)
-
-/* 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))
-
-/* 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 (equivalent to CONSTANT_SYMBOLIC). */
-enum mips_address_type {
- ADDRESS_REG,
- ADDRESS_LO_SUM,
- ADDRESS_CONST_INT,
- ADDRESS_SYMBOLIC
-};
-
-/* Classifies the prototype of a builtin function. */
-enum mips_function_type
-{
- MIPS_V2SF_FTYPE_V2SF,
- MIPS_V2SF_FTYPE_V2SF_V2SF,
- MIPS_V2SF_FTYPE_V2SF_V2SF_INT,
- MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF,
- MIPS_V2SF_FTYPE_SF_SF,
- MIPS_INT_FTYPE_V2SF_V2SF,
- MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF,
- MIPS_INT_FTYPE_SF_SF,
- MIPS_INT_FTYPE_DF_DF,
- MIPS_SF_FTYPE_V2SF,
- MIPS_SF_FTYPE_SF,
- MIPS_SF_FTYPE_SF_SF,
- MIPS_DF_FTYPE_DF,
- MIPS_DF_FTYPE_DF_DF,
-
- /* For MIPS DSP ASE */
- MIPS_DI_FTYPE_DI_SI,
- MIPS_DI_FTYPE_DI_SI_SI,
- MIPS_DI_FTYPE_DI_V2HI_V2HI,
- MIPS_DI_FTYPE_DI_V4QI_V4QI,
- MIPS_SI_FTYPE_DI_SI,
- MIPS_SI_FTYPE_PTR_SI,
- MIPS_SI_FTYPE_SI,
- MIPS_SI_FTYPE_SI_SI,
- MIPS_SI_FTYPE_V2HI,
- MIPS_SI_FTYPE_V2HI_V2HI,
- MIPS_SI_FTYPE_V4QI,
- MIPS_SI_FTYPE_V4QI_V4QI,
- MIPS_SI_FTYPE_VOID,
- MIPS_V2HI_FTYPE_SI,
- MIPS_V2HI_FTYPE_SI_SI,
- MIPS_V2HI_FTYPE_V2HI,
- MIPS_V2HI_FTYPE_V2HI_SI,
- MIPS_V2HI_FTYPE_V2HI_V2HI,
- MIPS_V2HI_FTYPE_V4QI,
- MIPS_V2HI_FTYPE_V4QI_V2HI,
- MIPS_V4QI_FTYPE_SI,
- MIPS_V4QI_FTYPE_V2HI_V2HI,
- MIPS_V4QI_FTYPE_V4QI_SI,
- MIPS_V4QI_FTYPE_V4QI_V4QI,
- MIPS_VOID_FTYPE_SI_SI,
- MIPS_VOID_FTYPE_V2HI_V2HI,
- MIPS_VOID_FTYPE_V4QI_V4QI,
-
- /* The last type. */
- MIPS_MAX_FTYPE_MAX
-};
-
-/* Specifies how a builtin function should be converted into rtl. */
-enum mips_builtin_type
-{
- /* The builtin 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 builtin 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 builtin 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 builtin 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
-};
-
-/* Invokes 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)
-};
-
-/* 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)
-};
-
-/* 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);
-
-struct mips16_constant;
-struct mips_arg_info;
-struct mips_address_info;
-struct mips_integer_op;
-struct mips_sim;
-
-static enum mips_symbol_type mips_classify_symbol (rtx);
-static void mips_split_const (rtx, rtx *, HOST_WIDE_INT *);
-static bool mips_offset_within_object_p (rtx, HOST_WIDE_INT);
-static bool mips_valid_base_register_p (rtx, enum machine_mode, int);
-static bool mips_symbolic_address_p (enum mips_symbol_type, enum machine_mode);
-static bool mips_classify_address (struct mips_address_info *, rtx,
- enum machine_mode, int);
-static bool mips_cannot_force_const_mem (rtx);
-static bool mips_use_blocks_for_constant_p (enum machine_mode, rtx);
-static int mips_symbol_insns (enum mips_symbol_type);
-static bool mips16_unextended_reference_p (enum machine_mode mode, rtx, rtx);
-static rtx mips_force_temporary (rtx, rtx);
-static rtx mips_unspec_offset_high (rtx, rtx, rtx, enum mips_symbol_type);
-static rtx mips_add_offset (rtx, rtx, HOST_WIDE_INT);
-static unsigned int mips_build_shift (struct mips_integer_op *, HOST_WIDE_INT);
-static unsigned int mips_build_lower (struct mips_integer_op *,
- unsigned HOST_WIDE_INT);
-static unsigned int mips_build_integer (struct mips_integer_op *,
- unsigned HOST_WIDE_INT);
-static void mips_legitimize_const_move (enum machine_mode, rtx, rtx);
-static int m16_check_op (rtx, int, int, int);
-static bool mips_rtx_costs (rtx, int, int, int *);
-static int mips_address_cost (rtx);
-static void mips_emit_compare (enum rtx_code *, rtx *, rtx *, bool);
-static void mips_load_call_address (rtx, rtx, int);
-static bool mips_function_ok_for_sibcall (tree, tree);
-static void mips_block_move_straight (rtx, rtx, HOST_WIDE_INT);
-static void mips_adjust_block_mem (rtx, HOST_WIDE_INT, rtx *, rtx *);
-static void mips_block_move_loop (rtx, rtx, HOST_WIDE_INT);
-static void mips_arg_info (const CUMULATIVE_ARGS *, enum machine_mode,
- tree, int, struct mips_arg_info *);
-static bool mips_get_unaligned_mem (rtx *, unsigned int, int, rtx *, rtx *);
-static void mips_set_architecture (const struct mips_cpu_info *);
-static void mips_set_tune (const struct mips_cpu_info *);
-static bool mips_handle_option (size_t, const char *, int);
-static struct machine_function *mips_init_machine_status (void);
-static void print_operand_reloc (FILE *, rtx, const char **);
-#if TARGET_IRIX
-static void irix_output_external_libcall (rtx);
-#endif
-static void mips_file_start (void);
-static void mips_file_end (void);
-static bool mips_rewrite_small_data_p (rtx);
-static int mips_small_data_pattern_1 (rtx *, void *);
-static int mips_rewrite_small_data_1 (rtx *, void *);
-static bool mips_function_has_gp_insn (void);
-static unsigned int mips_global_pointer (void);
-static bool mips_save_reg_p (unsigned int);
-static void mips_save_restore_reg (enum machine_mode, int, HOST_WIDE_INT,
- mips_save_restore_fn);
-static void mips_for_each_saved_reg (HOST_WIDE_INT, mips_save_restore_fn);
-static void mips_output_cplocal (void);
-static void mips_emit_loadgp (void);
-static void mips_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void mips_set_frame_expr (rtx);
-static rtx mips_frame_set (rtx, rtx);
-static void mips_save_reg (rtx, rtx);
-static void mips_output_function_epilogue (FILE *, HOST_WIDE_INT);
-static void mips_restore_reg (rtx, rtx);
-static void mips_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
- HOST_WIDE_INT, tree);
-static int symbolic_expression_p (rtx);
-static section *mips_select_rtx_section (enum machine_mode, rtx,
- unsigned HOST_WIDE_INT);
-static section *mips_function_rodata_section (tree);
-static bool mips_in_small_data_p (tree);
-static bool mips_use_anchors_for_symbol_p (rtx);
-static int mips_fpr_return_fields (tree, tree *);
-static bool mips_return_in_msb (tree);
-static rtx mips_return_fpr_pair (enum machine_mode mode,
- enum machine_mode mode1, HOST_WIDE_INT,
- enum machine_mode mode2, HOST_WIDE_INT);
-static rtx mips16_gp_pseudo_reg (void);
-static void mips16_fp_args (FILE *, int, int);
-static void build_mips16_function_stub (FILE *);
-static rtx dump_constants_1 (enum machine_mode, rtx, rtx);
-static void dump_constants (struct mips16_constant *, rtx);
-static int mips16_insn_length (rtx);
-static int mips16_rewrite_pool_refs (rtx *, void *);
-static void mips16_lay_out_constants (void);
-static void mips_sim_reset (struct mips_sim *);
-static void mips_sim_init (struct mips_sim *, state_t);
-static void mips_sim_next_cycle (struct mips_sim *);
-static void mips_sim_wait_reg (struct mips_sim *, rtx, rtx);
-static int mips_sim_wait_regs_2 (rtx *, void *);
-static void mips_sim_wait_regs_1 (rtx *, void *);
-static void mips_sim_wait_regs (struct mips_sim *, rtx);
-static void mips_sim_wait_units (struct mips_sim *, rtx);
-static void mips_sim_wait_insn (struct mips_sim *, rtx);
-static void mips_sim_record_set (rtx, rtx, void *);
-static void mips_sim_issue_insn (struct mips_sim *, rtx);
-static void mips_sim_issue_nop (struct mips_sim *);
-static void mips_sim_finish_insn (struct mips_sim *, rtx);
-static void vr4130_avoid_branch_rt_conflict (rtx);
-static void vr4130_align_insns (void);
-static void mips_avoid_hazard (rtx, rtx, int *, rtx *, rtx);
-static void mips_avoid_hazards (void);
-static void mips_reorg (void);
-static bool mips_strict_matching_cpu_name_p (const char *, const char *);
-static bool mips_matching_cpu_name_p (const char *, const char *);
-static const struct mips_cpu_info *mips_parse_cpu (const char *);
-static const struct mips_cpu_info *mips_cpu_info_from_isa (int);
-static bool mips_return_in_memory (tree, tree);
-static bool mips_strict_argument_naming (CUMULATIVE_ARGS *);
-static void mips_macc_chains_record (rtx);
-static void mips_macc_chains_reorder (rtx *, int);
-static void vr4130_true_reg_dependence_p_1 (rtx, rtx, void *);
-static bool vr4130_true_reg_dependence_p (rtx);
-static bool vr4130_swap_insns_p (rtx, rtx);
-static void vr4130_reorder (rtx *, int);
-static void mips_promote_ready (rtx *, int, int);
-static int mips_sched_reorder (FILE *, int, rtx *, int *, int);
-static int mips_variable_issue (FILE *, int, rtx, int);
-static int mips_adjust_cost (rtx, rtx, rtx, int);
-static int mips_issue_rate (void);
-static int mips_multipass_dfa_lookahead (void);
-static void mips_init_libfuncs (void);
-static void mips_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
- tree, int *, int);
-static tree mips_build_builtin_va_list (void);
-static tree mips_gimplify_va_arg_expr (tree, tree, tree *, tree *);
-static bool mips_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode,
- tree, bool);
-static bool mips_callee_copies (CUMULATIVE_ARGS *, enum machine_mode mode,
- tree, bool);
-static int mips_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode mode,
- tree, bool);
-static bool mips_valid_pointer_mode (enum machine_mode);
-static bool mips_vector_mode_supported_p (enum machine_mode);
-static rtx mips_prepare_builtin_arg (enum insn_code, unsigned int, tree *);
-static rtx mips_prepare_builtin_target (enum insn_code, unsigned int, rtx);
-static rtx mips_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
-static void mips_init_builtins (void);
-static rtx mips_expand_builtin_direct (enum insn_code, rtx, tree, bool);
-static rtx mips_expand_builtin_movtf (enum mips_builtin_type,
- enum insn_code, enum mips_fp_condition,
- rtx, tree);
-static rtx mips_expand_builtin_compare (enum mips_builtin_type,
- enum insn_code, enum mips_fp_condition,
- rtx, tree);
-static rtx mips_expand_builtin_bposge (enum mips_builtin_type, rtx);
-static void mips_encode_section_info (tree, rtx, int);
-static void mips_extra_live_on_entry (bitmap);
-static int mips_mode_rep_extended (enum machine_mode, enum machine_mode);
-
-/* Structure to be filled in by compute_frame_size with register
- save masks, and offsets for the current function. */
-
-struct mips_frame_info GTY(())
-{
- 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 cprestore_size; /* # bytes that the .cprestore slot takes up */
- HOST_WIDE_INT gp_reg_size; /* # bytes needed to store gp regs */
- HOST_WIDE_INT fp_reg_size; /* # bytes needed to store fp regs */
- unsigned int mask; /* mask of saved gp registers */
- unsigned int fmask; /* mask of saved fp registers */
- HOST_WIDE_INT gp_save_offset; /* offset from vfp to store gp registers */
- HOST_WIDE_INT fp_save_offset; /* offset from vfp to store fp registers */
- HOST_WIDE_INT gp_sp_offset; /* offset from new sp to store gp registers */
- HOST_WIDE_INT fp_sp_offset; /* offset from new sp to store fp registers */
- bool initialized; /* true if frame size already calculated */
- int num_gp; /* number of gp registers saved */
- int num_fp; /* number of fp registers saved */
-};
-
-struct machine_function GTY(()) {
- /* Pseudo-reg holding the value of $28 in a mips16 function which
- refers to GP relative global variables. */
- rtx mips16_gp_pseudo_rtx;
-
- /* 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;
-
- /* Current frame information, calculated by compute_frame_size. */
- struct mips_frame_info frame;
-
- /* The register to use as the global pointer within this function. */
- unsigned int global_pointer;
-
- /* True if mips_adjust_insn_length should ignore an instruction's
- hazard attribute. */
- bool ignore_hazard_length_p;
-
- /* True if the whole function is suitable for .set noreorder and
- .set nomacro. */
- bool all_noreorder_p;
-
- /* True if the function is known to have an instruction that needs $gp. */
- bool has_gp_insn_p;
-};
-
-/* 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;
-
- /* The number of words passed in registers, rounded up. */
- unsigned int reg_words;
-
- /* 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 value is MAX_ARGS_IN_REGISTERS if the argument is passed entirely
- on the stack. */
- unsigned int reg_offset;
-
- /* The number of words that must be passed on the stack, rounded up. */
- unsigned int stack_words;
-
- /* 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 is the register that contains the high part of the address,
- OFFSET is the symbolic address being referenced and SYMBOL_TYPE
- is the type of OFFSET's symbol.
-
- ADDRESS_SYMBOLIC
- SYMBOL_TYPE is the type of symbol being referenced. */
-
-struct mips_address_info
-{
- enum mips_address_type type;
- rtx reg;
- rtx offset;
- enum mips_symbol_type symbol_type;
-};
-
-
-/* One stage in a constant building sequence. These sequences have
- the form:
-
- A = VALUE[0]
- A = A CODE[1] VALUE[1]
- A = A CODE[2] VALUE[2]
- ...
-
- where A is an accumulator, each CODE[i] is a binary rtl operation
- and each VALUE[i] is a constant integer. */
-struct mips_integer_op {
- enum rtx_code code;
- unsigned HOST_WIDE_INT value;
-};
-
-
-/* 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
-
-
-/* Global variables for machine-dependent things. */
-
-/* Threshold for data being put into the small data/bss area, instead
- of the normal data area. */
-int mips_section_threshold = -1;
-
-/* Count the number of .file directives, so that .loc is up to date. */
-int num_source_filenames = 0;
-
-/* Count the number of sdb related labels are generated (to find block
- start and end boundaries). */
-int sdb_label_count = 0;
-
-/* Next label # for each statement for Silicon Graphics IRIS systems. */
-int sym_lineno = 0;
-
-/* 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. */
-
-struct extern_list GTY (())
-{
- struct extern_list *next; /* next external */
- const char *name; /* name of the external */
- int size; /* size in bytes */
-};
-
-static GTY (()) struct extern_list *extern_head = 0;
-
-/* Name of the file containing the current function. */
-const char *current_function_file = "";
-
-/* Number of nested .set noreorder, noat, nomacro, and volatile requests. */
-int set_noreorder;
-int set_noat;
-int set_nomacro;
-int set_volatile;
-
-/* The next branch instruction is a branch likely, not branch normal. */
-int mips_branch_likely;
-
-/* The operands passed to the last cmpMM expander. */
-rtx cmp_operands[2];
-
-/* The target cpu for code generation. */
-enum processor_type mips_arch;
-const struct mips_cpu_info *mips_arch_info;
-
-/* The target cpu for optimization and scheduling. */
-enum processor_type mips_tune;
-const struct mips_cpu_info *mips_tune_info;
-
-/* Which instruction set architecture to use. */
-int mips_isa;
-
-/* Which ABI to use. */
-int mips_abi = MIPS_ABI_DEFAULT;
-
-/* Cost information to use. */
-const struct mips_rtx_cost_data *mips_cost;
-
-/* Whether we are generating mips16 hard float code. In mips16 mode
- we always set TARGET_SOFT_FLOAT; this variable is nonzero if
- -msoft-float was not specified by the user, which means that we
- should arrange to call mips32 hard floating point code. */
-int mips16_hard_float;
-
-/* The architecture selected by -mipsN. */
-static const struct mips_cpu_info *mips_isa_info;
-
-/* If TRUE, we split addresses into their high and low parts in the RTL. */
-int mips_split_addresses;
-
-/* Mode used for saving/restoring general purpose registers. */
-static enum machine_mode gpr_mode;
-
-/* 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];
-
-/* List of all MIPS punctuation characters used by print_operand. */
-char mips_print_operand_punct[256];
-
-/* Map GCC register number to debugger register number. */
-int mips_dbx_regno[FIRST_PSEUDO_REGISTER];
-
-/* A copy of the original flag_delayed_branch: see override_options. */
-static int mips_flag_delayed_branch;
-
-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_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];
-
-/* Map hard register number to register class */
-const enum reg_class mips_regno_to_class[] =
-{
- LEA_REGS, LEA_REGS, M16_NA_REGS, V1_REG,
- M16_REGS, M16_REGS, M16_REGS, M16_REGS,
- LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
- LEA_REGS, LEA_REGS, LEA_REGS, LEA_REGS,
- M16_NA_REGS, M16_NA_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,
- FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- FP_REGS, FP_REGS, FP_REGS, FP_REGS,
- HI_REG, LO_REG, NO_REGS, ST_REGS,
- ST_REGS, ST_REGS, ST_REGS, ST_REGS,
- ST_REGS, ST_REGS, ST_REGS, NO_REGS,
- NO_REGS, ALL_REGS, ALL_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
-};
-
-/* Table of machine dependent attributes. */
-const struct attribute_spec mips_attribute_table[] =
-{
- { "long_call", 0, 0, false, true, true, NULL },
- { NULL, 0, 0, false, false, false, NULL }
-};
-
-/* 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[]. */
-const struct mips_cpu_info mips_cpu_info_table[] = {
- /* Entries for generic ISAs */
- { "mips1", PROCESSOR_R3000, 1 },
- { "mips2", PROCESSOR_R6000, 2 },
- { "mips3", PROCESSOR_R4000, 3 },
- { "mips4", PROCESSOR_R8000, 4 },
- { "mips32", PROCESSOR_4KC, 32 },
- { "mips32r2", PROCESSOR_M4K, 33 },
- { "mips64", PROCESSOR_5KC, 64 },
- { "mips64r2", PROCESSOR_5KC, 65 },
-
- /* MIPS I */
- { "r3000", PROCESSOR_R3000, 1 },
- { "r2000", PROCESSOR_R3000, 1 }, /* = r3000 */
- { "r3900", PROCESSOR_R3900, 1 },
-
- /* MIPS II */
- { "r6000", PROCESSOR_R6000, 2 },
-
- /* MIPS III */
- { "r4000", PROCESSOR_R4000, 3 },
- { "vr4100", PROCESSOR_R4100, 3 },
- { "vr4111", PROCESSOR_R4111, 3 },
- { "vr4120", PROCESSOR_R4120, 3 },
- { "vr4130", PROCESSOR_R4130, 3 },
- { "vr4300", PROCESSOR_R4300, 3 },
- { "r4400", PROCESSOR_R4000, 3 }, /* = r4000 */
- { "r4600", PROCESSOR_R4600, 3 },
- { "orion", PROCESSOR_R4600, 3 }, /* = r4600 */
- { "r4650", PROCESSOR_R4650, 3 },
-
- /* MIPS IV */
- { "r8000", PROCESSOR_R8000, 4 },
- { "vr5000", PROCESSOR_R5000, 4 },
- { "vr5400", PROCESSOR_R5400, 4 },
- { "vr5500", PROCESSOR_R5500, 4 },
- { "rm7000", PROCESSOR_R7000, 4 },
- { "rm9000", PROCESSOR_R9000, 4 },
-
- /* MIPS32 */
- { "4kc", PROCESSOR_4KC, 32 },
- { "4km", PROCESSOR_4KC, 32 }, /* = 4kc */
- { "4kp", PROCESSOR_4KP, 32 },
-
- /* MIPS32 Release 2 */
- { "m4k", PROCESSOR_M4K, 33 },
- { "24k", PROCESSOR_24K, 33 },
- { "24kc", PROCESSOR_24K, 33 }, /* 24K no FPU */
- { "24kf", PROCESSOR_24K, 33 }, /* 24K 1:2 FPU */
- { "24kx", PROCESSOR_24KX, 33 }, /* 24K 1:1 FPU */
-
- /* MIPS64 */
- { "5kc", PROCESSOR_5KC, 64 },
- { "5kf", PROCESSOR_5KF, 64 },
- { "20kc", PROCESSOR_20KC, 64 },
- { "sb1", PROCESSOR_SB1, 64 },
- { "sb1a", PROCESSOR_SB1A, 64 },
- { "sr71000", PROCESSOR_SR71000, 64 },
-
- /* MIPS64R2 */
- { "octeon", PROCESSOR_OCTEON, 65 },
- { "octeon+", PROCESSOR_OCTEON, 65 },
-
- /* End marker */
- { 0, 0, 0 }
-};
-
-/* 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 */
-
-/* Need to replace these with the costs of calling the appropriate
- libgcc routine. */
-#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 */
-
-static struct mips_rtx_cost_data const 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 */
- DEFAULT_COSTS
- },
- { /* 24k */
- 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 */
- },
- { /* 24kx */
- 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 */
- },
- { /* M4k */
- DEFAULT_COSTS
- },
- { /* 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 */
- },
- { /* 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
- },
- };
-
-
-/* Nonzero if -march should decide the default value of MASK_SOFT_FLOAT. */
-#ifndef MIPS_MARCH_CONTROLS_SOFT_FLOAT
-#define MIPS_MARCH_CONTROLS_SOFT_FLOAT 0
-#endif
-
-/* 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_REORDER
-#define TARGET_SCHED_REORDER 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_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_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
-#undef TARGET_ASM_FILE_END
-#define TARGET_ASM_FILE_START mips_file_start
-#define TARGET_ASM_FILE_END mips_file_end
-#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_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_tree_true
-#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
-#undef TARGET_PROMOTE_PROTOTYPES
-#define TARGET_PROMOTE_PROTOTYPES hook_bool_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_tree_hwi_hwi_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_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
-
-#undef TARGET_EXTRA_LIVE_ON_ENTRY
-#define TARGET_EXTRA_LIVE_ON_ENTRY mips_extra_live_on_entry
-
-#undef TARGET_MIN_ANCHOR_OFFSET
-#define TARGET_MIN_ANCHOR_OFFSET -32768
-#undef TARGET_MAX_ANCHOR_OFFSET
-#define TARGET_MAX_ANCHOR_OFFSET 32767
-#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
-
-struct gcc_target targetm = TARGET_INITIALIZER;
-
-/* Classify symbol X, which must be a SYMBOL_REF or a LABEL_REF. */
-
-static enum mips_symbol_type
-mips_classify_symbol (rtx x)
-{
- if (GET_CODE (x) == LABEL_REF)
- {
- if (TARGET_MIPS16)
- return SYMBOL_CONSTANT_POOL;
- if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
- return SYMBOL_GOT_LOCAL;
- return SYMBOL_GENERAL;
- }
-
- gcc_assert (GET_CODE (x) == SYMBOL_REF);
-
- if (SYMBOL_REF_TLS_MODEL (x))
- return SYMBOL_TLS;
-
- if (CONSTANT_POOL_ADDRESS_P (x))
- {
- if (TARGET_MIPS16)
- return SYMBOL_CONSTANT_POOL;
-
- if (GET_MODE_SIZE (get_pool_mode (x)) <= mips_section_threshold)
- return SYMBOL_SMALL_DATA;
- }
-
- /* Do not use small-data accesses for weak symbols; they may end up
- being zero. */
- if (SYMBOL_REF_SMALL_P (x)
- && !SYMBOL_REF_WEAK (x))
- return SYMBOL_SMALL_DATA;
-
- if (TARGET_ABICALLS)
- {
- if (SYMBOL_REF_DECL (x) == 0)
- {
- if (!SYMBOL_REF_LOCAL_P (x))
- return SYMBOL_GOT_GLOBAL;
- }
- else
- {
- /* Don't use GOT accesses for locally-binding symbols if
- TARGET_ABSOLUTE_ABICALLS. Otherwise, there are three
- cases to consider:
-
- - o32 PIC (either with or without explicit relocs)
- - n32/n64 PIC without explicit relocs
- - n32/n64 PIC with explicit relocs
-
- 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 doesn't depend on the symbol's
- visibility, so we deliberately ignore decl_visibility and
- binds_local_p here.
-
- 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.
-
- 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 (DECL_P (SYMBOL_REF_DECL (x))
- && TREE_PUBLIC (SYMBOL_REF_DECL (x))
- && !(TARGET_ABSOLUTE_ABICALLS
- && targetm.binds_local_p (SYMBOL_REF_DECL (x))))
- return SYMBOL_GOT_GLOBAL;
- }
-
- if (!TARGET_ABSOLUTE_ABICALLS)
- return SYMBOL_GOT_LOCAL;
- }
-
- return SYMBOL_GENERAL;
-}
-
-
-/* Split X into a base and a constant offset, storing them in *BASE
- and *OFFSET respectively. */
-
-static void
-mips_split_const (rtx x, rtx *base, HOST_WIDE_INT *offset)
-{
- *offset = 0;
-
- if (GET_CODE (x) == CONST)
- {
- x = XEXP (x, 0);
- if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
- {
- *offset += INTVAL (XEXP (x, 1));
- x = XEXP (x, 0);
- }
- }
- *base = x;
-}
-
-/* Classify symbolic expression X, given that it appears in context
- CONTEXT. */
-
-static enum mips_symbol_type
-mips_classify_symbolic_expression (rtx x)
-{
- HOST_WIDE_INT offset;
-
- mips_split_const (x, &x, &offset);
- if (UNSPEC_ADDRESS_P (x))
- return UNSPEC_ADDRESS_TYPE (x);
-
- return mips_classify_symbol (x);
-}
-
-/* Return true if SYMBOL is a SYMBOL_REF and OFFSET + SYMBOL points
- to the same object as SYMBOL, or to the same object_block. */
-
-static bool
-mips_offset_within_object_p (rtx symbol, HOST_WIDE_INT offset)
-{
- if (GET_CODE (symbol) != SYMBOL_REF)
- return false;
-
- if (CONSTANT_POOL_ADDRESS_P (symbol)
- && offset >= 0
- && offset < (int) GET_MODE_SIZE (get_pool_mode (symbol)))
- return true;
-
- if (SYMBOL_REF_DECL (symbol) != 0
- && offset >= 0
- && offset < int_size_in_bytes (TREE_TYPE (SYMBOL_REF_DECL (symbol))))
- return true;
-
- if (SYMBOL_REF_HAS_BLOCK_INFO_P (symbol)
- && SYMBOL_REF_BLOCK (symbol)
- && SYMBOL_REF_BLOCK_OFFSET (symbol) >= 0
- && ((unsigned HOST_WIDE_INT) offset + SYMBOL_REF_BLOCK_OFFSET (symbol)
- < (unsigned HOST_WIDE_INT) SYMBOL_REF_BLOCK (symbol)->size))
- return true;
-
- return false;
-}
-
-
-/* Return true if X is a symbolic constant that can be calculated in
- the same way as a bare symbol. If it is, store the type of the
- symbol in *SYMBOL_TYPE. */
-
-bool
-mips_symbolic_constant_p (rtx x, enum mips_symbol_type *symbol_type)
-{
- HOST_WIDE_INT offset;
-
- mips_split_const (x, &x, &offset);
- if (UNSPEC_ADDRESS_P (x))
- *symbol_type = UNSPEC_ADDRESS_TYPE (x);
- else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
- {
- *symbol_type = mips_classify_symbol (x);
- if (*symbol_type == SYMBOL_TLS)
- return false;
- }
- else
- return false;
-
- if (offset == 0)
- return true;
-
- /* Check whether a nonzero offset is valid for the underlying
- relocations. */
- switch (*symbol_type)
- {
- case SYMBOL_GENERAL:
- 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 mips_offset_within_object_p (x, offset);
-
- /* In other cases the relocations can handle any offset. */
- return true;
-
- case SYMBOL_CONSTANT_POOL:
- /* 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_SMALL_DATA:
- /* Make sure that the offset refers to something within the
- underlying object. This should guarantee that the final
- PC- or GP-relative offset is within the 16-bit limit. */
- return mips_offset_within_object_p (x, offset);
-
- case SYMBOL_GOT_LOCAL:
- case SYMBOL_GOTOFF_PAGE:
- /* The linker should provide enough local GOT entries for a
- 16-bit offset. Larger offsets may lead to GOT overflow. */
- return SMALL_OPERAND (offset);
-
- case SYMBOL_GOT_GLOBAL:
- case SYMBOL_GOTOFF_GLOBAL:
- case SYMBOL_GOTOFF_CALL:
- case SYMBOL_GOTOFF_LOADGP:
- case SYMBOL_TLSGD:
- case SYMBOL_TLSLDM:
- case SYMBOL_DTPREL:
- case SYMBOL_TPREL:
- case SYMBOL_GOTTPREL:
- case SYMBOL_TLS:
- return false;
- }
- gcc_unreachable ();
-}
-
-
-/* This function is used to implement REG_MODE_OK_FOR_BASE_P. */
-
-int
-mips_regno_mode_ok_for_base_p (int regno, enum machine_mode mode, int strict)
-{
- if (regno >= FIRST_PSEUDO_REGISTER)
- {
- if (!strict)
- return true;
- regno = reg_renumber[regno];
- }
-
- /* 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;
-
- /* In mips16 mode, the stack pointer can only address word and doubleword
- values, nothing smaller. There are two problems here:
-
- (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.
-
- (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).
-
- 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 || GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
-
- return TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
-}
-
-
-/* Return true if X is a valid base register for the given mode.
- Allow only hard registers if STRICT. */
-
-static bool
-mips_valid_base_register_p (rtx x, enum machine_mode mode, int strict)
-{
- if (!strict && GET_CODE (x) == SUBREG)
- x = SUBREG_REG (x);
-
- return (REG_P (x)
- && mips_regno_mode_ok_for_base_p (REGNO (x), mode, strict));
-}
-
-
-/* Return true if symbols of type SYMBOL_TYPE can directly address a value
- with mode MODE. This is used for both symbolic and LO_SUM addresses. */
-
-static bool
-mips_symbolic_address_p (enum mips_symbol_type symbol_type,
- enum machine_mode mode)
-{
- switch (symbol_type)
- {
- case SYMBOL_GENERAL:
- return !TARGET_MIPS16;
-
- case SYMBOL_SMALL_DATA:
- return true;
-
- case SYMBOL_CONSTANT_POOL:
- /* PC-relative addressing is only available for lw and ld. */
- return GET_MODE_SIZE (mode) == 4 || GET_MODE_SIZE (mode) == 8;
-
- case SYMBOL_GOT_LOCAL:
- return true;
-
- case SYMBOL_GOT_GLOBAL:
- /* The address will have to be loaded from the GOT first. */
- return false;
-
- case SYMBOL_GOTOFF_PAGE:
- case SYMBOL_GOTOFF_GLOBAL:
- case SYMBOL_GOTOFF_CALL:
- case SYMBOL_GOTOFF_LOADGP:
- case SYMBOL_TLS:
- case SYMBOL_TLSGD:
- case SYMBOL_TLSLDM:
- case SYMBOL_DTPREL:
- case SYMBOL_GOTTPREL:
- case SYMBOL_TPREL:
- case SYMBOL_64_HIGH:
- case SYMBOL_64_MID:
- case SYMBOL_64_LOW:
- return true;
- }
- gcc_unreachable ();
-}
-
-
-/* Return true if X is a valid address for machine mode MODE. If it is,
- fill in INFO appropriately. STRICT is true if we should only accept
- hard base registers. */
-
-static bool
-mips_classify_address (struct mips_address_info *info, rtx x,
- enum machine_mode mode, int strict)
-{
- 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);
-
- 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)
- && const_arith_operand (info->offset, VOIDmode));
-
- 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);
- return (mips_valid_base_register_p (info->reg, mode, strict)
- && mips_symbolic_address_p (info->symbol_type, mode)
- && mips_lo_relocs[info->symbol_type] != 0);
-
- 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, &info->symbol_type)
- && mips_symbolic_address_p (info->symbol_type, mode)
- && !mips_split_p[info->symbol_type]);
-
- default:
- return false;
- }
-}
-
-/* Return true if X is a thread-local symbol. */
-
-static bool
-mips_tls_operand_p (rtx x)
-{
- return GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (x) != 0;
-}
-
-/* Return true if X can not be forced into a constant pool. */
-
-static int
-mips_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
-{
- return mips_tls_operand_p (*x);
-}
-
-/* Return true if X can not be forced into a constant pool. */
-
-static bool
-mips_cannot_force_const_mem (rtx x)
-{
- rtx base;
- HOST_WIDE_INT offset;
-
- if (!TARGET_MIPS16)
- {
- /* As an optimization, reject constants that mips_legitimize_move
- can expand inline.
-
- 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)
- return true;
-
- mips_split_const (x, &base, &offset);
- if (symbolic_operand (base, VOIDmode) && SMALL_OPERAND (offset))
- return true;
- }
-
- if (TARGET_HAVE_TLS && for_each_rtx (&x, &mips_tls_symbol_ref_1, 0))
- return true;
-
- return false;
-}
-
-/* Implement TARGET_USE_BLOCKS_FOR_CONSTANT_P. MIPS16 uses per-function
- constant pools, but normal-mode code doesn't need to. */
-
-static bool
-mips_use_blocks_for_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED,
- rtx x ATTRIBUTE_UNUSED)
-{
- return !TARGET_MIPS16;
-}
-
-/* Return the number of instructions needed to load a symbol of the
- given type into a register. If valid in an address, the same number
- of instructions are needed for loads and stores. Treat extended
- mips16 instructions as two instructions. */
-
-static int
-mips_symbol_insns (enum mips_symbol_type type)
-{
- switch (type)
- {
- case SYMBOL_GENERAL:
- /* In mips16 code, general symbols must be fetched from the
- constant pool. */
- if (TARGET_MIPS16)
- return 0;
-
- /* When using 64-bit symbols, we need 5 preparatory instructions,
- such as:
-
- 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. */
- return (ABI_HAS_64BIT_SYMBOLS ? 6 : 2);
-
- case SYMBOL_SMALL_DATA:
- return 1;
-
- case SYMBOL_CONSTANT_POOL:
- /* This case is for mips16 only. Assume we'll need an
- extended instruction. */
- return 2;
-
- case SYMBOL_GOT_LOCAL:
- case SYMBOL_GOT_GLOBAL:
- /* Unless -funit-at-a-time is in effect, we can't be sure whether
- the local/global classification is accurate. See override_options
- for details.
-
- 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_GLOBAL:
- case SYMBOL_GOTOFF_CALL:
- case SYMBOL_GOTOFF_LOADGP:
- 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:
- /* Check whether the offset is a 16- or 32-bit value. */
- return mips_split_p[type] ? 2 : 1;
-
- case SYMBOL_TLS:
- /* We don't treat a bare TLS symbol as a constant. */
- return 0;
- }
- gcc_unreachable ();
-}
-
-/* Return true if X is a legitimate $sp-based address for mode MDOE. */
-
-bool
-mips_stack_address_p (rtx x, enum machine_mode mode)
-{
- struct mips_address_info addr;
-
- return (mips_classify_address (&addr, x, mode, false)
- && addr.type == ADDRESS_REG
- && addr.reg == stack_pointer_rtx);
-}
-
-/* Return true if a value at OFFSET bytes from BASE can be accessed
- using an unextended mips16 instruction. MODE is the mode of the
- value.
-
- Usually the offset in an unextended instruction is a 5-bit field.
- The offset is unsigned and shifted left once for HIs, twice
- for SIs, and so on. An exception is SImode accesses off the
- stack pointer, which have an 8-bit immediate field. */
-
-static bool
-mips16_unextended_reference_p (enum machine_mode mode, rtx base, rtx offset)
-{
- if (TARGET_MIPS16
- && GET_CODE (offset) == CONST_INT
- && INTVAL (offset) >= 0
- && (INTVAL (offset) & (GET_MODE_SIZE (mode) - 1)) == 0)
- {
- if (GET_MODE_SIZE (mode) == 4 && base == stack_pointer_rtx)
- return INTVAL (offset) < 256 * GET_MODE_SIZE (mode);
- return INTVAL (offset) < 32 * GET_MODE_SIZE (mode);
- }
- return false;
-}
-
-
-/* Return the number of instructions needed to load or store a value
- of mode MODE at X. Return 0 if X isn't valid for MODE.
-
- For mips16 code, count extended instructions as two instructions. */
-
-int
-mips_address_insns (rtx x, enum machine_mode mode)
-{
- struct mips_address_info addr;
- int factor;
-
- if (mode == BLKmode)
- /* BLKmode is used for single unaligned loads and stores. */
- factor = 1;
- else
- /* Each word of a multi-word value will be accessed individually. */
- factor = (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-
- if (mips_classify_address (&addr, x, mode, false))
- switch (addr.type)
- {
- case ADDRESS_REG:
- if (TARGET_MIPS16
- && !mips16_unextended_reference_p (mode, addr.reg, addr.offset))
- return factor * 2;
- return factor;
-
- case ADDRESS_LO_SUM:
- return (TARGET_MIPS16 ? factor * 2 : factor);
-
- case ADDRESS_CONST_INT:
- return factor;
-
- case ADDRESS_SYMBOLIC:
- return factor * mips_symbol_insns (addr.symbol_type);
- }
- return 0;
-}
-
-
-/* Likewise for constant X. */
-
-int
-mips_const_insns (rtx x)
-{
- struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
- enum mips_symbol_type symbol_type;
- HOST_WIDE_INT offset;
-
- switch (GET_CODE (x))
- {
- case HIGH:
- if (TARGET_MIPS16
- || !mips_symbolic_constant_p (XEXP (x, 0), &symbol_type)
- || !mips_split_p[symbol_type])
- return 0;
-
- return 1;
-
- 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 (INTVAL (x) >= 0 && INTVAL (x) < 256 ? 1
- : SMALL_OPERAND_UNSIGNED (INTVAL (x)) ? 2
- : INTVAL (x) > -256 && INTVAL (x) < 0 ? 2
- : SMALL_OPERAND_UNSIGNED (-INTVAL (x)) ? 3
- : 0);
-
- return mips_build_integer (codes, INTVAL (x));
-
- case CONST_DOUBLE:
- case CONST_VECTOR:
- return (!TARGET_MIPS16 && x == CONST0_RTX (GET_MODE (x)) ? 1 : 0);
-
- case CONST:
- if (CONST_GP_P (x))
- return 1;
-
- /* See if we can refer to X directly. */
- if (mips_symbolic_constant_p (x, &symbol_type))
- return mips_symbol_insns (symbol_type);
-
- /* Otherwise try splitting the constant into a base and offset.
- 16-bit offsets can be added using an extra addiu. Larger offsets
- must be calculated separately and then added to the base. */
- mips_split_const (x, &x, &offset);
- if (offset != 0)
- {
- int n = mips_const_insns (x);
- if (n != 0)
- {
- if (SMALL_OPERAND (offset))
- return n + 1;
- else
- return n + 1 + mips_build_integer (codes, offset);
- }
- }
- return 0;
-
- case SYMBOL_REF:
- case LABEL_REF:
- return mips_symbol_insns (mips_classify_symbol (x));
-
- default:
- return 0;
- }
-}
-
-
-/* Return the number of instructions needed for memory reference X.
- Count extended mips16 instructions as two instructions. */
-
-int
-mips_fetch_insns (rtx x)
-{
- gcc_assert (MEM_P (x));
- return mips_address_insns (XEXP (x, 0), GET_MODE (x));
-}
-
-
-/* Return the number of instructions needed for an integer division. */
-
-int
-mips_idiv_insns (void)
-{
- int count;
-
- count = 1;
- if (TARGET_CHECK_ZERO_DIV)
- {
- if (GENERATE_DIVIDE_TRAPS)
- count++;
- else
- count += 2;
- }
-
- if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
- count++;
- return count;
-}
-
-/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It
- returns a nonzero value if X is a legitimate address for a memory
- operand of the indicated MODE. STRICT is nonzero if this function
- is called during reload. */
-
-bool
-mips_legitimate_address_p (enum machine_mode mode, rtx x, int strict)
-{
- struct mips_address_info addr;
-
- return mips_classify_address (&addr, x, mode, strict);
-}
-
-
-/* Copy VALUE to a register and return that register. If new psuedos
- are allowed, copy it into a new register, otherwise use DEST. */
-
-static rtx
-mips_force_temporary (rtx dest, rtx value)
-{
- if (!no_new_pseudos)
- return force_reg (Pmode, value);
- else
- {
- emit_move_insn (copy_rtx (dest), value);
- return dest;
- }
-}
-
-
-/* Return a LO_SUM expression for ADDR. TEMP is as for mips_force_temporary
- and is used to load the high part into a register. */
-
-rtx
-mips_split_symbol (rtx temp, rtx addr)
-{
- rtx high;
-
- if (TARGET_MIPS16)
- high = mips16_gp_pseudo_reg ();
- else
- high = mips_force_temporary (temp, gen_rtx_HIGH (Pmode, copy_rtx (addr)));
- return gen_rtx_LO_SUM (Pmode, high, addr);
-}
-
-
-/* Return an UNSPEC address with underlying address ADDRESS and symbol
- type SYMBOL_TYPE. */
-
-rtx
-mips_unspec_address (rtx address, enum mips_symbol_type symbol_type)
-{
- rtx base;
- HOST_WIDE_INT offset;
-
- mips_split_const (address, &base, &offset);
- base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base),
- UNSPEC_ADDRESS_FIRST + symbol_type);
- return plus_constant (gen_rtx_CONST (Pmode, base), offset);
-}
-
-
-/* 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 available as a temporary register if needed.
-
- The returned expression can be used as the first operand to a LO_SUM. */
-
-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);
- return mips_force_temporary (temp, gen_rtx_PLUS (Pmode, addr, base));
- }
- return base;
-}
-
-
-/* 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. */
-
-static rtx
-mips_add_offset (rtx temp, rtx reg, HOST_WIDE_INT offset)
-{
- if (!SMALL_OPERAND (offset))
- {
- rtx high;
- 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
- {
- /* 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);
-}
-
-/* Emit a call to __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. The entire insn sequence is returned. */
-
-static GTY(()) rtx mips_tls_symbol;
-
-static rtx
-mips_call_tls_get_addr (rtx sym, enum mips_symbol_type type, rtx v0)
-{
- rtx insn, loc, tga, a0;
-
- a0 = gen_rtx_REG (Pmode, GP_ARG_FIRST);
-
- if (!mips_tls_symbol)
- mips_tls_symbol = init_one_libfunc ("__tls_get_addr");
-
- loc = mips_unspec_address (sym, type);
-
- start_sequence ();
-
- emit_insn (gen_rtx_SET (Pmode, a0,
- gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, loc)));
- tga = gen_rtx_MEM (Pmode, mips_tls_symbol);
- insn = emit_call_insn (gen_call_value (v0, tga, const0_rtx, const0_rtx));
- CONST_OR_PURE_CALL_P (insn) = 1;
- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), v0);
- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), a0);
- insn = get_insns ();
-
- end_sequence ();
-
- return insn;
-}
-
-/* Generate the code to access LOC, a thread local SYMBOL_REF. The
- return value will be a valid address and move_operand (either a REG
- or a LO_SUM). */
-
-static rtx
-mips_legitimize_tls_address (rtx loc)
-{
- rtx dest, insn, v0, v1, tmp1, tmp2, eqv;
- enum tls_model model;
-
- v0 = gen_rtx_REG (Pmode, GP_RETURN);
- v1 = gen_rtx_REG (Pmode, GP_RETURN + 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;
-
- switch (model)
- {
- case TLS_MODEL_GLOBAL_DYNAMIC:
- insn = mips_call_tls_get_addr (loc, SYMBOL_TLSGD, v0);
- dest = gen_reg_rtx (Pmode);
- emit_libcall_block (insn, dest, v0, loc);
- break;
-
- case TLS_MODEL_LOCAL_DYNAMIC:
- insn = mips_call_tls_get_addr (loc, SYMBOL_TLSLDM, v0);
- tmp1 = gen_reg_rtx (Pmode);
-
- /* 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);
-
- tmp2 = mips_unspec_offset_high (NULL, tmp1, loc, SYMBOL_DTPREL);
- dest = gen_rtx_LO_SUM (Pmode, tmp2,
- mips_unspec_address (loc, SYMBOL_DTPREL));
- break;
-
- case TLS_MODEL_INITIAL_EXEC:
- tmp1 = gen_reg_rtx (Pmode);
- tmp2 = mips_unspec_address (loc, SYMBOL_GOTTPREL);
- if (Pmode == DImode)
- {
- emit_insn (gen_tls_get_tp_di (v1));
- emit_insn (gen_load_gotdi (tmp1, pic_offset_table_rtx, tmp2));
- }
- else
- {
- emit_insn (gen_tls_get_tp_si (v1));
- emit_insn (gen_load_gotsi (tmp1, pic_offset_table_rtx, tmp2));
- }
- dest = gen_reg_rtx (Pmode);
- emit_insn (gen_add3_insn (dest, tmp1, v1));
- break;
-
- case TLS_MODEL_LOCAL_EXEC:
- if (Pmode == DImode)
- emit_insn (gen_tls_get_tp_di (v1));
- else
- emit_insn (gen_tls_get_tp_si (v1));
-
- tmp1 = mips_unspec_offset_high (NULL, v1, loc, SYMBOL_TPREL);
- dest = gen_rtx_LO_SUM (Pmode, tmp1,
- mips_unspec_address (loc, SYMBOL_TPREL));
- break;
-
- default:
- gcc_unreachable ();
- }
-
- return dest;
-}
-
-/* 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. */
-
-bool
-mips_legitimize_address (rtx *xloc, enum machine_mode mode)
-{
- enum mips_symbol_type symbol_type;
-
- if (mips_tls_operand_p (*xloc))
- {
- *xloc = mips_legitimize_tls_address (*xloc);
- return true;
- }
-
- /* See if the address can split into a high part and a LO_SUM. */
- if (mips_symbolic_constant_p (*xloc, &symbol_type)
- && mips_symbolic_address_p (symbol_type, mode)
- && mips_split_p[symbol_type])
- {
- *xloc = mips_split_symbol (0, *xloc);
- return true;
- }
-
- if (GET_CODE (*xloc) == PLUS && GET_CODE (XEXP (*xloc, 1)) == CONST_INT)
- {
- /* Handle REG + CONSTANT using mips_add_offset. */
- rtx reg;
-
- reg = XEXP (*xloc, 0);
- if (!mips_valid_base_register_p (reg, mode, 0))
- reg = copy_to_mode_reg (Pmode, reg);
- *xloc = mips_add_offset (0, reg, INTVAL (XEXP (*xloc, 1)));
- return true;
- }
-
- return false;
-}
-
-
-/* Subroutine of mips_build_integer (with the same interface).
- Assume that the final action in the sequence should be a left shift. */
-
-static unsigned int
-mips_build_shift (struct mips_integer_op *codes, HOST_WIDE_INT value)
-{
- unsigned int i, shift;
-
- /* 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++;
-
- i = mips_build_integer (codes, value);
- codes[i].code = ASHIFT;
- codes[i].value = shift;
- return i + 1;
-}
-
-
-/* As for mips_build_shift, but assume that the final action will be
- an IOR or PLUS operation. */
-
-static unsigned int
-mips_build_lower (struct mips_integer_op *codes, unsigned HOST_WIDE_INT value)
-{
- unsigned HOST_WIDE_INT high;
- unsigned int i;
-
- high = value & ~(unsigned HOST_WIDE_INT) 0xffff;
- if (!LUI_OPERAND (high) && (value & 0x18000) == 0x18000)
- {
- /* The constant is too complex to load with a simple lui/ori pair
- so our goal is to clear 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
- {
- i = mips_build_integer (codes, high);
- codes[i].code = IOR;
- codes[i].value = value & 0xffff;
- }
- return i + 1;
-}
-
-
-/* Fill CODES with a sequence of rtl operations to load VALUE.
- Return the number of operations needed. */
-
-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;
- }
-}
-
-
-/* Load VALUE into DEST, using TEMP as a temporary register if need be. */
-
-void
-mips_move_integer (rtx dest, rtx temp, unsigned HOST_WIDE_INT value)
-{
- struct mips_integer_op codes[MIPS_MAX_INTEGER_OPS];
- enum machine_mode mode;
- unsigned int i, cost;
- rtx x;
-
- mode = GET_MODE (dest);
- cost = mips_build_integer (codes, value);
-
- /* 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 < cost; i++)
- {
- if (no_new_pseudos)
- {
- 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));
-}
-
-
-/* Subroutine of mips_legitimize_move. Move constant SRC into register
- DEST given that SRC satisfies immediate_operand but doesn't satisfy
- move_operand. */
-
-static void
-mips_legitimize_const_move (enum machine_mode mode, rtx dest, rtx src)
-{
- rtx base;
- HOST_WIDE_INT offset;
-
- /* Split moves of big integers into smaller pieces. */
- if (splittable_const_int_operand (src, mode))
- {
- mips_move_integer (dest, dest, INTVAL (src));
- return;
- }
-
- /* Split moves of symbolic constants into high/low pairs. */
- if (splittable_symbolic_operand (src, mode))
- {
- emit_insn (gen_rtx_SET (VOIDmode, dest, mips_split_symbol (dest, src)));
- return;
- }
-
- if (mips_tls_operand_p (src))
- {
- emit_move_insn (dest, mips_legitimize_tls_address (src));
- return;
- }
-
- /* If we have (const (plus symbol offset)), load the symbol first
- and then add in the offset. This is usually better than forcing
- the constant into memory, at least in non-mips16 code. */
- mips_split_const (src, &base, &offset);
- if (!TARGET_MIPS16
- && offset != 0
- && (!no_new_pseudos || SMALL_OPERAND (offset)))
- {
- base = mips_force_temporary (dest, base);
- emit_move_insn (dest, mips_add_offset (0, base, offset));
- return;
- }
-
- src = force_const_mem (mode, src);
-
- /* When using explicit relocs, constant pool references are sometimes
- not legitimate addresses. */
- if (!memory_operand (src, VOIDmode))
- src = replace_equiv_address (src, mips_split_symbol (dest, XEXP (src, 0)));
- emit_move_insn (dest, src);
-}
-
-
-/* If (set DEST SRC) is not a valid instruction, emit an equivalent
- sequence that is valid. */
-
-bool
-mips_legitimize_move (enum machine_mode mode, rtx dest, rtx src)
-{
- if (!register_operand (dest, mode) && !reg_or_0_operand (src, mode))
- {
- emit_move_insn (dest, force_reg (mode, src));
- return true;
- }
-
- /* Check for individual, fully-reloaded mflo and mfhi instructions. */
- if (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
- && REG_P (src) && MD_REG_P (REGNO (src))
- && REG_P (dest) && GP_REG_P (REGNO (dest)))
- {
- int other_regno = REGNO (src) == HI_REGNUM ? LO_REGNUM : HI_REGNUM;
- if (GET_MODE_SIZE (mode) <= 4)
- emit_insn (gen_mfhilo_si (gen_rtx_REG (SImode, REGNO (dest)),
- gen_rtx_REG (SImode, REGNO (src)),
- gen_rtx_REG (SImode, other_regno)));
- else
- emit_insn (gen_mfhilo_di (gen_rtx_REG (DImode, REGNO (dest)),
- gen_rtx_REG (DImode, REGNO (src)),
- gen_rtx_REG (DImode, other_regno)));
- return true;
- }
-
- /* We need to deal with constants that would be legitimate
- immediate_operands but not 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;
-}
-
-/* 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 (rtx op, int low, int high, int mask)
-{
- return (GET_CODE (op) == CONST_INT
- && INTVAL (op) >= low
- && INTVAL (op) <= 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);
-}
-
-static bool
-mips_rtx_costs (rtx x, int code, int outer_code, int *total)
-{
- enum machine_mode mode = GET_MODE (x);
- bool float_mode_p = FLOAT_MODE_P (mode);
-
- switch (code)
- {
- case CONST_INT:
- if (TARGET_MIPS16)
- {
- /* A number between 1 and 8 inclusive is efficient for a shift.
- Otherwise, we will need an extended instruction. */
- if ((outer_code) == ASHIFT || (outer_code) == ASHIFTRT
- || (outer_code) == LSHIFTRT)
- {
- if (INTVAL (x) >= 1 && INTVAL (x) <= 8)
- *total = 0;
- else
- *total = COSTS_N_INSNS (1);
- return true;
- }
-
- /* We can use cmpi for an xor with an unsigned 16 bit value. */
- if ((outer_code) == XOR
- && INTVAL (x) >= 0 && INTVAL (x) < 0x10000)
- {
- *total = 0;
- return true;
- }
-
- /* We may be able to use slt or sltu for a comparison with a
- signed 16 bit value. (The boundary conditions aren't quite
- right, but this is just a heuristic anyhow.) */
- if (((outer_code) == LT || (outer_code) == LE
- || (outer_code) == GE || (outer_code) == GT
- || (outer_code) == LTU || (outer_code) == LEU
- || (outer_code) == GEU || (outer_code) == GTU)
- && INTVAL (x) >= -0x8000 && INTVAL (x) < 0x8000)
- {
- *total = 0;
- return true;
- }
-
- /* Equality comparisons with 0 are cheap. */
- if (((outer_code) == EQ || (outer_code) == NE)
- && INTVAL (x) == 0)
- {
- *total = 0;
- return true;
- }
-
- /* Constants in the range 0...255 can be loaded with an unextended
- instruction. They are therefore as cheap as a register move.
-
- Given the choice between "li R1,0...255" and "move R1,R2"
- (where R2 is a known constant), it is usually better to use "li",
- since we do not want to unnecessarily extend the lifetime
- of R2. */
- if (outer_code == SET
- && INTVAL (x) >= 0
- && INTVAL (x) < 256)
- {
- *total = 0;
- return true;
- }
- }
- else
- {
- /* These can be used anywhere. */
- *total = 0;
- return true;
- }
-
- /* Otherwise fall through to the handling below because
- we'll need to construct the constant. */
-
- case CONST:
- case SYMBOL_REF:
- case LABEL_REF:
- case CONST_DOUBLE:
- if (LEGITIMATE_CONSTANT_P (x))
- {
- *total = COSTS_N_INSNS (1);
- return true;
- }
- else
- {
- /* 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, otherwise use the default handling. */
- int n = mips_address_insns (XEXP (x, 0), GET_MODE (x));
- if (n > 0)
- {
- *total = COSTS_N_INSNS (n + 1);
- return true;
- }
- return false;
- }
-
- case FFS:
- *total = COSTS_N_INSNS (6);
- return true;
-
- case NOT:
- *total = COSTS_N_INSNS ((mode == DImode && !TARGET_64BIT) ? 2 : 1);
- return true;
-
- case AND:
- case IOR:
- case XOR:
- if (mode == DImode && !TARGET_64BIT)
- {
- *total = COSTS_N_INSNS (2);
- return true;
- }
- return false;
-
- case ASHIFT:
- case ASHIFTRT:
- case LSHIFTRT:
- if (mode == DImode && !TARGET_64BIT)
- {
- *total = COSTS_N_INSNS ((GET_CODE (XEXP (x, 1)) == CONST_INT)
- ? 4 : 12);
- return true;
- }
- return false;
-
- case ABS:
- if (float_mode_p)
- *total = COSTS_N_INSNS (1);
- else
- *total = COSTS_N_INSNS (4);
- return true;
-
- case LO_SUM:
- *total = COSTS_N_INSNS (1);
- return true;
-
- case PLUS:
- case MINUS:
- if (float_mode_p)
- {
- *total = mips_cost->fp_add;
- return true;
- }
-
- else if (mode == DImode && !TARGET_64BIT)
- {
- *total = COSTS_N_INSNS (4);
- return true;
- }
- return false;
-
- case NEG:
- if (mode == DImode && !TARGET_64BIT)
- {
- *total = COSTS_N_INSNS (4);
- return true;
- }
- return false;
-
- case MULT:
- if (mode == SFmode)
- *total = mips_cost->fp_mult_sf;
-
- else if (mode == DFmode)
- *total = mips_cost->fp_mult_df;
-
- else if (mode == SImode)
- *total = mips_cost->int_mult_si;
-
- else
- *total = mips_cost->int_mult_di;
-
- return true;
-
- case DIV:
- case MOD:
- if (float_mode_p)
- {
- if (mode == SFmode)
- *total = mips_cost->fp_div_sf;
- else
- *total = mips_cost->fp_div_df;
-
- return true;
- }
- /* Fall through. */
-
- case UDIV:
- case UMOD:
- if (mode == DImode)
- *total = mips_cost->int_div_di;
- else
- *total = mips_cost->int_div_si;
-
- return true;
-
- case SIGN_EXTEND:
- /* A sign extend from SImode to DImode in 64 bit mode is often
- zero instructions, because the result can often be used
- directly by another instruction; we'll call it one. */
- if (TARGET_64BIT && mode == DImode
- && GET_MODE (XEXP (x, 0)) == SImode)
- *total = COSTS_N_INSNS (1);
- else
- *total = COSTS_N_INSNS (2);
- return true;
-
- case ZERO_EXTEND:
- if (TARGET_64BIT && mode == DImode
- && GET_MODE (XEXP (x, 0)) == SImode)
- *total = COSTS_N_INSNS (2);
- else
- *total = COSTS_N_INSNS (1);
- return true;
-
- case FLOAT:
- case UNSIGNED_FLOAT:
- case FIX:
- case FLOAT_EXTEND:
- case FLOAT_TRUNCATE:
- case SQRT:
- *total = mips_cost->fp_add;
- return true;
-
- default:
- return false;
- }
-}
-
-/* Provide the costs of an addressing mode that contains ADDR.
- If ADDR is not a valid address, its cost is irrelevant. */
-
-static int
-mips_address_cost (rtx addr)
-{
- return mips_address_insns (addr, SImode);
-}
-
-/* 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, int high_p)
-{
- unsigned int byte;
- enum machine_mode mode;
-
- mode = GET_MODE (op);
- if (mode == VOIDmode)
- mode = DImode;
-
- if (TARGET_BIG_ENDIAN ? !high_p : high_p)
- byte = UNITS_PER_WORD;
- else
- byte = 0;
-
- if (REG_P (op))
- {
- if (FP_REG_P (REGNO (op)))
- return gen_rtx_REG (word_mode, high_p ? REGNO (op) + 1 : REGNO (op));
- if (ACC_HI_REG_P (REGNO (op)))
- return gen_rtx_REG (word_mode, high_p ? REGNO (op) : REGNO (op) + 1);
- }
-
- 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;
-
- /* FP->FP moves can be done in a single instruction. */
- if (FP_REG_RTX_P (src) && FP_REG_RTX_P (dest))
- return false;
-
- /* Check for floating-point loads and stores. They can be done using
- ldc1 and sdc1 on MIPS II and above. */
- if (mips_isa > 1)
- {
- 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 64-bit move from SRC to DEST assuming that
- mips_split_64bit_move_p holds.
-
- Moves into and out of FPRs cause some difficulty here. Such moves
- will always be DFmode, since paired FPRs are not allowed to store
- DImode values. The most natural representation would be two separate
- 32-bit moves, such as:
-
- (set (reg:SI $f0) (mem:SI ...))
- (set (reg:SI $f1) (mem:SI ...))
-
- However, the second insn is invalid because odd-numbered FPRs are
- not allowed to store independent values. Use the patterns load_df_low,
- load_df_high and store_df_high instead. */
-
-void
-mips_split_64bit_move (rtx dest, rtx src)
-{
- if (FP_REG_RTX_P (dest))
- {
- /* Loading an FPR from memory or from GPRs. */
- emit_insn (gen_load_df_low (copy_rtx (dest), mips_subword (src, 0)));
- emit_insn (gen_load_df_high (dest, mips_subword (src, 1),
- copy_rtx (dest)));
- }
- else if (FP_REG_RTX_P (src))
- {
- /* Storing an FPR into memory or GPRs. */
- emit_move_insn (mips_subword (dest, 0), mips_subword (src, 0));
- emit_insn (gen_store_df_high (mips_subword (dest, 1), src));
- }
- else
- {
- /* The operation can be split into two normal moves. Decide in
- which order to do them. */
- rtx low_dest;
-
- low_dest = mips_subword (dest, 0);
- if (REG_P (low_dest)
- && reg_overlap_mentioned_p (low_dest, src))
- {
- emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1));
- emit_move_insn (low_dest, mips_subword (src, 0));
- }
- else
- {
- emit_move_insn (low_dest, mips_subword (src, 0));
- emit_move_insn (mips_subword (dest, 1), mips_subword (src, 1));
- }
- }
-}
-
-/* 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;
- bool dbl_p;
-
- dest_code = GET_CODE (dest);
- src_code = GET_CODE (src);
- dbl_p = (GET_MODE_SIZE (GET_MODE (dest)) == 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 (GET_MODE (dest))))
- {
- if (dest_code == REG)
- {
- if (GP_REG_P (REGNO (dest)))
- return "move\t%0,%z1";
-
- if (MD_REG_P (REGNO (dest)))
- return "mt%0\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)
- return (dbl_p ? "sd\t%z1,%0" : "sw\t%z1,%0");
- }
- if (dest_code == REG && GP_REG_P (REGNO (dest)))
- {
- if (src_code == REG)
- {
- 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 (ST_REG_P (REGNO (src)) && ISA_HAS_8CC)
- return "lui\t%0,0x3f80\n\tmovf\t%0,%.,%1";
-
- 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 (src_code == MEM)
- return (dbl_p ? "ld\t%0,%1" : "lw\t%0,%1");
-
- if (src_code == CONST_INT)
- {
- /* Don't use the X format, 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 (INTVAL (src) >= 0 && INTVAL (src) <= 0xffff)
- return "li\t%0,%1";
-
- if (INTVAL (src) < 0 && INTVAL (src) >= -0xffff)
- return "#";
- }
-
- if (src_code == HIGH)
- return "lui\t%0,%h1";
-
- if (CONST_GP_P (src))
- return "move\t%0,%1";
-
- if (symbolic_operand (src, VOIDmode))
- 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 ();
-}
-
-/* Restore $gp from its save slot. Valid only when using o32 or
- o64 abicalls. */
-
-void
-mips_restore_gp (void)
-{
- rtx address, slot;
-
- gcc_assert (TARGET_ABICALLS && TARGET_OLDABI);
-
- address = mips_add_offset (pic_offset_table_rtx,
- frame_pointer_needed
- ? hard_frame_pointer_rtx
- : stack_pointer_rtx,
- current_function_outgoing_args_size);
- slot = gen_rtx_MEM (Pmode, address);
-
- emit_move_insn (pic_offset_table_rtx, slot);
- if (!TARGET_EXPLICIT_RELOCS)
- emit_insn (gen_blockage ());
-}
-
-/* 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)
-{
- emit_insn (gen_rtx_SET (VOIDmode, target,
- gen_rtx_fmt_ee (code, GET_MODE (target), op0, op1)));
-}
-
-/* Return true if CMP1 is a suitable second operand for relational
- operator CODE. See also the *sCC patterns in mips.md. */
-
-static bool
-mips_relational_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 ();
- }
-}
-
-/* Canonicalize LE or LEU comparisons into LT comparisons when
- possible to avoid extra instructions or inverting the
- comparison. */
-
-static bool
-mips_canonicalize_comparison (enum rtx_code *code, rtx *cmp1,
- enum machine_mode mode)
-{
- HOST_WIDE_INT original, plus_one;
-
- if (GET_CODE (*cmp1) != CONST_INT)
- return false;
-
- original = INTVAL (*cmp1);
- plus_one = trunc_int_for_mode ((unsigned HOST_WIDE_INT) original + 1, mode);
-
- switch (*code)
- {
- case LE:
- if (original < plus_one)
- {
- *code = LT;
- *cmp1 = force_reg (mode, GEN_INT (plus_one));
- return true;
- }
- break;
-
- case LEU:
- if (plus_one != 0)
- {
- *code = LTU;
- *cmp1 = force_reg (mode, GEN_INT (plus_one));
- return true;
- }
- break;
-
- default:
- return false;
- }
-
- return false;
-
-}
-
-/* Compare CMP0 and CMP1 using relational operator CODE and store the
- result in TARGET. CMP0 and TARGET are register_operands that have
- the same integer mode. 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_relational (enum rtx_code code, bool *invert_ptr,
- rtx target, rtx cmp0, rtx cmp1)
-{
- /* First see if there is a MIPS instruction that can do this operation
- with CMP1 in its current form. If not, try to canonicalize the
- comparison to LT. If that fails, try doing the same for the
- inverse operation. If that also fails, force CMP1 into a register
- and try again. */
- if (mips_relational_operand_ok_p (code, cmp1))
- mips_emit_binary (code, target, cmp0, cmp1);
- else if (mips_canonicalize_comparison (&code, &cmp1, GET_MODE (target)))
- mips_emit_binary (code, target, cmp0, cmp1);
- else
- {
- enum rtx_code inv_code = reverse_condition (code);
- if (!mips_relational_operand_ok_p (inv_code, cmp1))
- {
- cmp1 = force_reg (GET_MODE (cmp0), cmp1);
- mips_emit_int_relational (code, invert_ptr, target, cmp0, cmp1);
- }
- else if (invert_ptr == 0)
- {
- rtx inv_target = gen_reg_rtx (GET_MODE (target));
- mips_emit_binary (inv_code, inv_target, 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_reverse_fp_cond_p (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/NE comparisons against zero are possible,
- otherwise any standard branch condition can be used. The standard branch
- conditions are:
-
- - EQ/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]));
- *op1 = const0_rtx;
- mips_emit_int_relational (*code, &invert, *op0,
- cmp_operands[0], cmp_operands[1]);
- *code = (invert ? EQ : NE);
- }
- }
- 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_reverse_fp_cond_p (&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]);
- }
-}
-
-/* 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 wider than cmp_operands[0]. */
-
-bool
-mips_emit_scc (enum rtx_code code, rtx target)
-{
- if (GET_MODE_CLASS (GET_MODE (cmp_operands[0])) != MODE_INT)
- return false;
-
- target = gen_lowpart (GET_MODE (cmp_operands[0]), target);
- if (code == EQ || code == NE)
- {
- rtx zie = mips_zero_if_equal (cmp_operands[0], cmp_operands[1]);
- mips_emit_binary (code, target, zie, const0_rtx);
- }
- else
- mips_emit_int_relational (code, 0, target,
- cmp_operands[0], cmp_operands[1]);
- return true;
-}
-
-/* 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}. */
-
-void
-gen_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_reverse_fp_cond_p (&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));
-}
-
-/* Emit the common code for conditional moves. OPERANDS is the array
- of operands passed to the conditional move define_expand. */
-
-void
-gen_conditional_move (rtx *operands)
-{
- enum rtx_code code;
- rtx op0, op1;
-
- code = GET_CODE (operands[1]);
- mips_emit_compare (&code, &op0, &op1, true);
- emit_insn (gen_rtx_SET (VOIDmode, operands[0],
- gen_rtx_IF_THEN_ELSE (GET_MODE (operands[0]),
- gen_rtx_fmt_ee (code,
- GET_MODE (op0),
- op0, op1),
- operands[2], operands[3])));
-}
-
-/* Emit a conditional trap. OPERANDS is the array of operands passed to
- the conditional_trap expander. */
-
-void
-mips_gen_conditional_trap (rtx *operands)
-{
- rtx op0, op1;
- enum rtx_code cmp_code = GET_CODE (operands[0]);
- enum machine_mode mode = GET_MODE (cmp_operands[0]);
-
- /* 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)
- {
- 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;
- }
- if (cmp_code == GET_CODE (operands[0]))
- {
- op0 = cmp_operands[0];
- op1 = cmp_operands[1];
- }
- else
- {
- op0 = cmp_operands[1];
- op1 = 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 (cmp_code, mode, op0, op1),
- operands[1]));
-}
-
-/* Load function address ADDR into register DEST. SIBCALL_P is true
- if the address is needed for a sibling call. */
-
-static void
-mips_load_call_address (rtx dest, rtx addr, int sibcall_p)
-{
- /* 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 NewABI sibcalls since the value of $gp on entry
- to the stub would be our caller's gp, not ours. */
- if (TARGET_EXPLICIT_RELOCS
- && !(sibcall_p && TARGET_NEWABI)
- && global_got_operand (addr, VOIDmode))
- {
- rtx high, lo_sum_symbol;
-
- high = mips_unspec_offset_high (dest, pic_offset_table_rtx,
- addr, SYMBOL_GOTOFF_CALL);
- lo_sum_symbol = mips_unspec_address (addr, SYMBOL_GOTOFF_CALL);
- if (Pmode == SImode)
- emit_insn (gen_load_callsi (dest, high, lo_sum_symbol));
- else
- emit_insn (gen_load_calldi (dest, high, lo_sum_symbol));
- }
- else
- emit_move_insn (dest, addr);
-}
-
-
-/* Expand a call or call_value instruction. RESULT is where the
- result will go (null for calls), 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. SIBCALL_P is true
- if we are expanding a sibling call, false if we're expanding
- a normal call. */
-
-void
-mips_expand_call (rtx result, rtx addr, rtx args_size, rtx aux, int sibcall_p)
-{
- rtx orig_addr, pattern, insn;
-
- orig_addr = addr;
- if (!call_insn_operand (addr, VOIDmode))
- {
- addr = gen_reg_rtx (Pmode);
- mips_load_call_address (addr, orig_addr, sibcall_p);
- }
-
- if (TARGET_MIPS16
- && mips16_hard_float
- && build_mips16_call_stub (result, addr, args_size,
- aux == 0 ? 0 : (int) GET_MODE (aux)))
- return;
-
- if (result == 0)
- pattern = (sibcall_p
- ? gen_sibcall_internal (addr, args_size)
- : gen_call_internal (addr, args_size));
- else if (GET_CODE (result) == PARALLEL && XVECLEN (result, 0) == 2)
- {
- rtx reg1, reg2;
-
- reg1 = XEXP (XVECEXP (result, 0, 0), 0);
- reg2 = XEXP (XVECEXP (result, 0, 1), 0);
- pattern =
- (sibcall_p
- ? gen_sibcall_value_multiple_internal (reg1, addr, args_size, reg2)
- : gen_call_value_multiple_internal (reg1, addr, args_size, reg2));
- }
- else
- pattern = (sibcall_p
- ? gen_sibcall_value_internal (result, addr, args_size)
- : gen_call_value_internal (result, addr, args_size));
-
- insn = emit_call_insn (pattern);
-
- /* Lazy-binding stubs require $gp to be valid on entry. */
- if (global_got_operand (orig_addr, VOIDmode))
- use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
-}
-
-
-/* We can handle any sibcall when TARGET_SIBCALLS is true. */
-
-static bool
-mips_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
- tree exp ATTRIBUTE_UNUSED)
-{
- return TARGET_SIBCALLS;
-}
-
-/* Emit code to move general operand SRC into condition-code
- register DEST. SCRATCH is a scratch TFmode float register.
- The sequence is:
-
- FP1 = SRC
- FP2 = 0.0f
- DEST = FP2 < FP1
-
- where FP1 and FP2 are single-precision float registers
- taken from SCRATCH. */
-
-void
-mips_emit_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) + FP_INC);
-
- emit_move_insn (copy_rtx (fp1), src);
- emit_move_insn (copy_rtx (fp2), CONST0_RTX (SFmode));
- emit_insn (gen_slt_sf (dest, fp2, fp1));
-}
-
-/* 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. */
-
-void
-mips_set_return_address (rtx address, rtx scratch)
-{
- rtx slot_address;
-
- compute_frame_size (get_frame_size ());
- gcc_assert ((cfun->machine->frame.mask >> 31) & 1);
- slot_address = mips_add_offset (scratch, stack_pointer_rtx,
- cfun->machine->frame.gp_sp_offset);
-
- emit_move_insn (gen_rtx_MEM (GET_MODE (address), slot_address), address);
-}
-
-/* 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 = alloca (sizeof (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)
- emit_move_insn (regs[i], adjust_address (src, mode, offset));
- else
- {
- rtx part = adjust_address (src, BLKmode, offset);
- if (!mips_expand_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)
- emit_move_insn (adjust_address (dest, mode, offset), regs[i]);
- else
- {
- rtx part = adjust_address (dest, BLKmode, offset);
- if (!mips_expand_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);
- }
-}
-
-#define MAX_MOVE_REGS 4
-#define MAX_MOVE_BYTES (MAX_MOVE_REGS * UNITS_PER_WORD)
-
-
-/* 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 MAX_MOVE_BYTES
- per iteration. LENGTH must be at least MAX_MOVE_BYTES. Assume that the
- memory regions do not overlap. */
-
-static void
-mips_block_move_loop (rtx dest, rtx src, HOST_WIDE_INT length)
-{
- rtx label, src_reg, dest_reg, final_src;
- HOST_WIDE_INT leftover;
-
- leftover = length % MAX_MOVE_BYTES;
- length -= leftover;
-
- /* Create registers and memory references for use within the loop. */
- mips_adjust_block_mem (src, MAX_MOVE_BYTES, &src_reg, &src);
- mips_adjust_block_mem (dest, MAX_MOVE_BYTES, &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, MAX_MOVE_BYTES);
-
- /* Move on to the next block. */
- emit_move_insn (src_reg, plus_constant (src_reg, MAX_MOVE_BYTES));
- emit_move_insn (dest_reg, plus_constant (dest_reg, MAX_MOVE_BYTES));
-
- /* 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. */
-
-bool
-mips_expand_block_move (rtx dest, rtx src, rtx length)
-{
- if (GET_CODE (length) == CONST_INT)
- {
- if (INTVAL (length) <= 2 * MAX_MOVE_BYTES)
- {
- mips_block_move_straight (dest, src, INTVAL (length));
- return true;
- }
- else if (optimize)
- {
- mips_block_move_loop (dest, src, INTVAL (length));
- return true;
- }
- }
- return false;
-}
-
-/* Argument support functions. */
-
-/* Initialize CUMULATIVE_ARGS for a function. */
-
-void
-init_cumulative_args (CUMULATIVE_ARGS *cum, tree fntype,
- rtx libname ATTRIBUTE_UNUSED)
-{
- static CUMULATIVE_ARGS zero_cum;
- tree param, next_param;
-
- *cum = zero_cum;
- cum->prototype = (fntype && TYPE_ARG_TYPES (fntype));
-
- /* 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. */
-
- for (param = fntype ? TYPE_ARG_TYPES (fntype) : 0;
- param != 0; param = next_param)
- {
- next_param = TREE_CHAIN (param);
- if (next_param == 0 && TREE_VALUE (param) != void_type_node)
- cum->gp_reg_found = 1;
- }
-}
-
-
-/* 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_arg_info (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named, struct mips_arg_info *info)
-{
- 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 and complex floating-point types are passed in
- floating-point registers. */
- 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;
-}
-
-
-/* Implement FUNCTION_ARG_ADVANCE. */
-
-void
-function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
- tree type, int named)
-{
- struct mips_arg_info info;
-
- mips_arg_info (cum, mode, type, named, &info);
-
- 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 the O32
- ABI, which passes at most 2 arguments in float registers. */
- if (cum->arg_number < 2 && info.fpr_p)
- cum->fp_code += (mode == SFmode ? 1 : 2) << ((cum->arg_number - 1) * 2);
-
- 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 += FP_INC;
-
- if (info.stack_words > 0)
- cum->stack_words = info.stack_offset + info.stack_words;
-
- cum->arg_number++;
-}
-
-/* Implement FUNCTION_ARG. */
-
-struct rtx_def *
-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
- insn. 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 0;
- }
-
- mips_arg_info (cum, mode, type, named, &info);
-
- /* Return straight away if the whole argument is passed on the stack. */
- if (info.reg_offset == MAX_ARGS_IN_REGISTERS)
- return 0;
-
- if (type != 0
- && TREE_CODE (type) == RECORD_TYPE
- && TARGET_NEWABI
- && TYPE_SIZE_UNIT (type)
- && host_integerp (TYPE_SIZE_UNIT (type), 1)
- && named)
- {
- /* 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;
-
- 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
- && TREE_CODE (TREE_TYPE (field)) == REAL_TYPE
- && !TARGET_SOFT_FLOAT
- && 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;
- int reg;
-
- inner = GET_MODE_INNER (mode);
- reg = 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, reg);
- }
- else
- {
- gcc_assert (info.stack_words == 0);
- real = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (inner, reg),
- const0_rtx);
- imag = gen_rtx_EXPR_LIST (VOIDmode,
- gen_rtx_REG (inner,
- reg + info.reg_words / 2),
- GEN_INT (GET_MODE_SIZE (inner)));
- return gen_rtx_PARALLEL (mode, gen_rtvec (2, real, imag));
- }
- }
-
- if (!info.fpr_p)
- return gen_rtx_REG (mode, GP_ARG_FIRST + info.reg_offset);
- else if (info.reg_offset == 1)
- /* This code handles the special o32 case in which the second word
- of the argument structure is passed in floating-point registers. */
- return gen_rtx_REG (mode, FP_ARG_FIRST + FP_INC);
- else
- return gen_rtx_REG (mode, FP_ARG_FIRST + info.reg_offset);
-}
-
-
-/* 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_arg_info (cum, mode, type, named, &info);
- 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
-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, 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)
- : GET_MODE_CLASS (mode) == MODE_INT)
- 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);
-}
-
-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, 1);
-
- /* 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_rtx_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;
- off &= ~(UNITS_PER_FPVALUE - 1);
- off -= fp_saved * UNITS_PER_FPREG;
-
- mode = TARGET_SINGLE_FLOAT ? SFmode : DFmode;
-
- for (i = local_cum.num_fprs; i < MAX_ARGS_IN_REGISTERS; i += FP_INC)
- {
- rtx ptr, mem;
-
- ptr = plus_constant (virtual_incoming_args_rtx, off);
- mem = gen_rtx_MEM (mode, ptr);
- set_mem_alias_set (mem, get_varargs_alias_set ());
- emit_move_insn (mem, gen_rtx_REG (mode, FP_ARG_FIRST + i));
- 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);
-}
-
-/* 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 !EABI_FLOAT_VARARGS_P, 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. */
-
-static tree
-mips_build_builtin_va_list (void)
-{
- if (EABI_FLOAT_VARARGS_P)
- {
- 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 va_start. */
-
-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 = &current_function_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 (PLUS_EXPR, TREE_TYPE (ovfl), t,
- build_int_cst (NULL_TREE,
- 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 - 1);
- if (fpr_offset)
- t = build2 (PLUS_EXPR, TREE_TYPE (ftop), t,
- build_int_cst (NULL_TREE, -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 (NULL_TREE, 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 (NULL_TREE, 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 va_arg. */
-
-static tree
-mips_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
-{
- HOST_WIDE_INT size, rsize;
- tree addr;
- bool indirect;
-
- indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
-
- if (indirect)
- type = build_pointer_type (type);
-
- size = int_size_in_bytes (type);
- rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
-
- if (mips_abi != ABI_EABI || !EABI_FLOAT_VARARGS_P)
- addr = std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
- else
- {
- /* Not a simple merged stack. */
-
- tree f_ovfl, f_gtop, f_ftop, f_goff, f_foff;
- tree ovfl, top, off, align;
- HOST_WIDE_INT 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);
-
- /* We maintain separate pointers and offsets for floating-point
- and integer arguments, but we need similar code in both cases.
- Let:
-
- TOP be the top of the register save area;
- OFF be the offset from TOP of the next register;
- ADDR_RTX be the address of the argument;
- RSIZE be the number of bytes used to store the argument
- when it's in the register save area;
- OSIZE be the number of bytes used to store it when it's
- in the stack overflow area; and
- PADDING be (BYTES_BIG_ENDIAN ? OSIZE - RSIZE : 0)
-
- The code we want is:
-
- 1: off &= -rsize; // round down
- 2: if (off != 0)
- 3: {
- 4: addr_rtx = top - off;
- 5: off -= rsize;
- 6: }
- 7: else
- 8: {
- 9: ovfl += ((intptr_t) ovfl + osize - 1) & -osize;
- 10: addr_rtx = ovfl + PADDING;
- 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);
-
- 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), valist, f_ftop,
- NULL_TREE);
- off = build3 (COMPONENT_REF, TREE_TYPE (f_foff), valist, f_foff,
- NULL_TREE);
-
- /* When floating-point registers are saved to the stack,
- each one will take up UNITS_PER_HWFPVALUE bytes, regardless
- of the float'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), valist, f_gtop,
- NULL_TREE);
- off = build3 (COMPONENT_REF, TREE_TYPE (f_goff), valist, f_goff,
- NULL_TREE);
- if (rsize > UNITS_PER_WORD)
- {
- /* [1] Emit code for: off &= -rsize. */
- t = build2 (BIT_AND_EXPR, TREE_TYPE (off), off,
- build_int_cst (NULL_TREE, -rsize));
- t = build2 (MODIFY_EXPR, TREE_TYPE (off), off, t);
- gimplify_and_add (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-increment not available to C. Also widen for the
- coming pointer arithmetic. */
- t = fold_convert (TREE_TYPE (off), build_int_cst (NULL_TREE, rsize));
- t = build2 (POSTDECREMENT_EXPR, TREE_TYPE (off), off, t);
- t = fold_convert (sizetype, t);
- t = fold_convert (TREE_TYPE (top), t);
-
- /* [4] Emit code for: addr_rtx = top - off. On big endian machines,
- the argument has RSIZE - SIZE bytes of leading padding. */
- t = build2 (MINUS_EXPR, TREE_TYPE (top), top, t);
- if (BYTES_BIG_ENDIAN && rsize > size)
- {
- u = fold_convert (TREE_TYPE (t), build_int_cst (NULL_TREE,
- rsize - size));
- t = build2 (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 = fold_convert (TREE_TYPE (ovfl),
- build_int_cst (NULL_TREE, osize - 1));
- t = build2 (PLUS_EXPR, TREE_TYPE (ovfl), ovfl, u);
- u = fold_convert (TREE_TYPE (ovfl),
- build_int_cst (NULL_TREE, -osize));
- t = build2 (BIT_AND_EXPR, TREE_TYPE (ovfl), t, u);
- align = build2 (MODIFY_EXPR, TREE_TYPE (ovfl), ovfl, t);
- }
- else
- align = NULL;
-
- /* [10, 11]. Emit code to store ovfl in addr_rtx, then
- post-increment ovfl by osize. On big-endian machines,
- the argument has OSIZE - SIZE bytes of leading padding. */
- 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 = fold_convert (TREE_TYPE (t),
- build_int_cst (NULL_TREE, osize - size));
- t = build2 (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)
- addr = build_va_arg_indirect_ref (addr);
-
- return addr;
-}
-
-/* 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, unsigned int width, 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
- be the upper word and RIGHT the lower word. */
- if (TARGET_BIG_ENDIAN)
- *left = first, *right = last;
- else
- *left = last, *right = first;
-
- return true;
-}
-
-
-/* Try to emit the equivalent of (set DEST (zero_extract SRC WIDTH BITPOS)).
- Return true on success. We only handle cases where zero_extract is
- equivalent to sign_extract. */
-
-bool
-mips_expand_unaligned_load (rtx dest, rtx src, unsigned int width, int bitpos)
-{
- rtx left, right, temp;
-
- /* If TARGET_64BIT, the destination of a 32-bit load 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
- && SUBREG_BYTE (dest) == 0
- && 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 expand (set (zero_extract DEST WIDTH BITPOS) SRC). Return
- true on success. */
-
-bool
-mips_expand_unaligned_store (rtx dest, rtx src, unsigned int width, 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 SIZE POSITION) 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 <= POSITION < GET_MODE_BITSIZE (GET_MODE (op))
- 0 < SIZE <= GET_MODE_BITSIZE (GET_MODE (op))
- 0 < POSITION + SIZE <= 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, rtx size, rtx position)
-{
- HOST_WIDE_INT len, pos;
-
- if (!ISA_HAS_EXT_INS
- || !register_operand (op, VOIDmode)
- || GET_MODE_BITSIZE (GET_MODE (op)) > BITS_PER_WORD)
- return false;
-
- len = INTVAL (size);
- pos = INTVAL (position);
-
- if (len <= 0 || len >= GET_MODE_BITSIZE (GET_MODE (op))
- || pos < 0 || pos + len > GET_MODE_BITSIZE (GET_MODE (op)))
- return false;
-
- return true;
-}
-
-/* Set up globals to generate code for the ISA or processor
- described by INFO. */
-
-static void
-mips_set_architecture (const struct mips_cpu_info *info)
-{
- if (info != 0)
- {
- mips_arch_info = info;
- mips_arch = info->cpu;
- mips_isa = info->isa;
- }
-}
-
-
-/* Likewise for tuning. */
-
-static void
-mips_set_tune (const struct mips_cpu_info *info)
-{
- if (info != 0)
- {
- mips_tune_info = info;
- mips_tune = info->cpu;
- }
-}
-
-/* Implement TARGET_HANDLE_OPTION. */
-
-static bool
-mips_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
-{
- switch (code)
- {
- 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 OPT_march_:
- case OPT_mtune_:
- return mips_parse_cpu (arg) != 0;
-
- case OPT_mips:
- mips_isa_info = mips_parse_cpu (ACONCAT (("mips", arg, NULL)));
- return mips_isa_info != 0;
-
- case OPT_mno_flush_func:
- mips_cache_flush_func = NULL;
- return true;
-
- default:
- return true;
- }
-}
-
-/* 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 (void)
-{
- int i, start, regno;
- enum machine_mode mode;
-
- mips_section_threshold = g_switch_set ? g_switch_value : MIPS_DEFAULT_GVALUE;
-
- /* 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_info != 0)
- {
- if (mips_arch_info == 0)
- mips_set_architecture (mips_isa_info);
- else if (mips_arch_info->isa != mips_isa_info->isa)
- error ("-%s conflicts with the other architecture options, "
- "which specify a %s processor",
- mips_isa_info->name,
- mips_cpu_info_from_isa (mips_arch_info->isa)->name);
- }
-
- if (mips_arch_info == 0)
- {
-#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 (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);
-
- /* Set cost structure for the processor. */
- mips_cost = &mips_rtx_cost_data[mips_tune];
-
- 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
- {
- /* 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 ((target_flags_explicit & MASK_FLOAT64) != 0)
- {
- /* Really, -mfp32 and -mfp64 are ornamental options. There's
- only one right answer here. */
- if (TARGET_64BIT && TARGET_DOUBLE_FLOAT && !TARGET_FLOAT64)
- error ("unsupported combination: %s", "-mgp64 -mfp32 -mdouble-float");
- else if (!TARGET_64BIT && TARGET_FLOAT64)
- error ("unsupported combination: %s", "-mgp32 -mfp64");
- else if (TARGET_SINGLE_FLOAT && TARGET_FLOAT64)
- error ("unsupported combination: %s", "-mfp64 -msingle-float");
- }
- 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
- target_flags &= ~MASK_FLOAT64;
- }
-
- /* End of code shared with GAS. */
-
- 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;
- }
-
- if (MIPS_MARCH_CONTROLS_SOFT_FLOAT
- && (target_flags_explicit & MASK_SOFT_FLOAT) == 0)
- {
- /* For some configurations, it is useful to have -march control
- the default setting of MASK_SOFT_FLOAT. */
- switch ((int) mips_arch)
- {
- case PROCESSOR_R4100:
- case PROCESSOR_R4111:
- case PROCESSOR_R4120:
- case PROCESSOR_R4130:
- target_flags |= MASK_SOFT_FLOAT;
- break;
-
- default:
- target_flags &= ~MASK_SOFT_FLOAT;
- break;
- }
- }
-
- if (!TARGET_OLDABI)
- flag_pcc_struct_return = 0;
-
- if ((target_flags_explicit & MASK_BRANCHLIKELY) == 0)
- {
- /* If neither -mbranch-likely nor -mno-branch-likely was given
- on the command line, set MASK_BRANCHLIKELY based on the target
- architecture.
-
- By default, we enable use of Branch Likely instructions on
- all architectures which support them with the following
- exceptions: when creating MIPS32 or MIPS64 code, and when
- tuning for architectures where their use tends to hurt
- performance.
-
- The MIPS32 and MIPS64 architecture specifications say "Software
- is strongly encouraged to avoid use of Branch Likely
- instructions, as they will be removed from a future revision
- of the [MIPS32 and MIPS64] architecture." Therefore, we do not
- issue those instructions unless instructed to do so by
- -mbranch-likely. */
- if (ISA_HAS_BRANCHLIKELY
- && !(ISA_MIPS32 || ISA_MIPS32R2 || ISA_MIPS64 || ISA_MIPS64R2)
- && !(TUNE_MIPS5500 || TUNE_SB1))
- target_flags |= MASK_BRANCHLIKELY;
- else
- target_flags &= ~MASK_BRANCHLIKELY;
- }
- if (TARGET_BRANCHLIKELY && !ISA_HAS_BRANCHLIKELY)
- warning (0, "generation of Branch Likely instructions enabled, but not supported by architecture");
-
- /* 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;
- }
-
- if (TARGET_ABICALLS)
- {
- /* We need to set flag_pic for executables as well as DSOs
- because we may reference symbols that are not defined in
- the final executable. (MIPS does not use things like
- copy relocs, for example.)
-
- Also, there is a body of code that uses __PIC__ to distinguish
- between -mabicalls and -mno-abicalls code. */
- flag_pic = 1;
- if (mips_section_threshold > 0)
- warning (0, "%<-G%> is incompatible with %<-mabicalls%>");
- }
-
- /* mips_split_addresses is a half-way house between explicit
- relocations and the traditional assembler macros. It can
- split absolute 32-bit symbolic constants into a high/lo_sum
- pair but uses macros for other sorts of access.
-
- Like explicit relocation support for REL targets, it relies
- on GNU extensions in the assembler and the linker.
-
- Although this code should work for -O0, it has traditionally
- been treated as an optimization. */
- if (!TARGET_MIPS16 && TARGET_SPLIT_ADDRESSES
- && optimize && !flag_pic
- && !ABI_HAS_64BIT_SYMBOLS)
- mips_split_addresses = 1;
- else
- mips_split_addresses = 0;
-
- /* -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;
-
- /* When compiling for the mips16, we cannot use floating point. We
- record the original hard float value in mips16_hard_float. */
- if (TARGET_MIPS16)
- {
- if (TARGET_SOFT_FLOAT)
- mips16_hard_float = 0;
- else
- mips16_hard_float = 1;
- target_flags |= MASK_SOFT_FLOAT;
-
- /* Don't run the scheduler before reload, since it tends to
- increase register pressure. */
- flag_schedule_insns = 0;
-
- /* Don't do hot/cold partitioning. The constant layout code expects
- the whole function to be in a single section. */
- flag_reorder_blocks_and_partition = 0;
-
- /* Silently disable -mexplicit-relocs since it doesn't apply
- to mips16 code. Even so, it would overly pedantic to warn
- about "-mips16 -mexplicit-relocs", especially given that
- we use a %gprel() operator. */
- target_flags &= ~MASK_EXPLICIT_RELOCS;
- }
-
- /* When using explicit relocs, we call dbr_schedule from within
- mips_reorg. */
- if (TARGET_EXPLICIT_RELOCS)
- {
- mips_flag_delayed_branch = flag_delayed_branch;
- flag_delayed_branch = 0;
- }
-
-#ifdef MIPS_TFMODE_FORMAT
- REAL_MODE_FORMAT (TFmode) = &MIPS_TFMODE_FORMAT;
-#endif
-
- /* 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 are both true. */
- if (TARGET_PAIRED_SINGLE_FLOAT && !(TARGET_FLOAT64 && TARGET_HARD_FLOAT))
- error ("-mips3d/-mpaired-single must be used with -mfp64 -mhard-float");
-
- /* Make sure that the ISA supports TARGET_PAIRED_SINGLE_FLOAT when it is
- enabled. */
- if (TARGET_PAIRED_SINGLE_FLOAT && !ISA_MIPS64)
- error ("-mips3d/-mpaired-single must be used with -mips64");
-
- if (TARGET_MIPS16 && TARGET_DSP)
- error ("-mips16 and -mdsp cannot be used together");
-
- mips_print_operand_punct['?'] = 1;
- mips_print_operand_punct['#'] = 1;
- mips_print_operand_punct['/'] = 1;
- 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;
-
- /* Set up array to map GCC register number to debug register number.
- Ignore the special purpose register numbers. */
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- mips_dbx_regno[i] = -1;
-
- 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;
-
- mips_dbx_regno[HI_REGNUM] = MD_DBX_FIRST + 0;
- mips_dbx_regno[LO_REGNUM] = MD_DBX_FIRST + 1;
-
- /* Set up array giving whether a given register can hold a given mode. */
-
- for (mode = VOIDmode;
- mode != MAX_MACHINE_MODE;
- mode = (enum machine_mode) ((int)mode + 1))
- {
- register int size = GET_MODE_SIZE (mode);
- register enum mode_class class = GET_MODE_CLASS (mode);
-
- for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
- {
- register int temp;
-
- if (mode == CCV2mode)
- temp = (ISA_HAS_8CC
- && ST_REG_P (regno)
- && (regno - ST_REG_FIRST) % 2 == 0);
-
- else if (mode == CCV4mode)
- temp = (ISA_HAS_8CC
- && ST_REG_P (regno)
- && (regno - ST_REG_FIRST) % 4 == 0);
-
- else 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 = ((regno % FP_INC) == 0)
- && (((class == MODE_FLOAT || class == MODE_COMPLEX_FLOAT
- || class == MODE_VECTOR_FLOAT)
- && size <= UNITS_PER_FPVALUE)
- /* Allow integer modes that fit into a single
- register. We need to put integers into FPRs
- when using instructions like cvt and trunc.
- We can't allow sizes smaller than a word,
- the FPU has no appropriate load/store
- instructions for those. */
- || (class == MODE_INT
- && size >= MIN_UNITS_PER_WORD
- && size <= UNITS_PER_FPREG)
- /* Allow TFmode for CCmode reloads. */
- || (ISA_HAS_8CC && mode == TFmode));
-
- else if (ACC_REG_P (regno))
- temp = (INTEGRAL_MODE_P (mode)
- && (size <= UNITS_PER_WORD
- || (ACC_HI_REG_P (regno)
- && size == 2 * UNITS_PER_WORD)));
-
- else if (ALL_COP_REG_P (regno))
- temp = (class == MODE_INT && size <= UNITS_PER_WORD);
- else
- temp = 0;
-
- mips_hard_regno_mode_ok[(int)mode][regno] = temp;
- }
- }
-
- /* 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;
-
- /* Provide default values for align_* for 64-bit targets. */
- if (TARGET_64BIT && !TARGET_MIPS16)
- {
- if (align_loops == 0)
- align_loops = 8;
- if (align_jumps == 0)
- align_jumps = 8;
- if (align_functions == 0)
- align_functions = 8;
- }
-
- /* Function to allocate machine-dependent function status. */
- init_machine_status = &mips_init_machine_status;
-
- if (ABI_HAS_64BIT_SYMBOLS)
- {
- if (TARGET_EXPLICIT_RELOCS)
- {
- mips_split_p[SYMBOL_64_HIGH] = true;
- mips_hi_relocs[SYMBOL_64_HIGH] = "%highest(";
- mips_lo_relocs[SYMBOL_64_HIGH] = "%higher(";
-
- mips_split_p[SYMBOL_64_MID] = true;
- mips_hi_relocs[SYMBOL_64_MID] = "%higher(";
- mips_lo_relocs[SYMBOL_64_MID] = "%hi(";
-
- mips_split_p[SYMBOL_64_LOW] = true;
- mips_hi_relocs[SYMBOL_64_LOW] = "%hi(";
- mips_lo_relocs[SYMBOL_64_LOW] = "%lo(";
-
- mips_split_p[SYMBOL_GENERAL] = true;
- mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
- }
- }
- else
- {
- if (TARGET_EXPLICIT_RELOCS || mips_split_addresses)
- {
- mips_split_p[SYMBOL_GENERAL] = true;
- mips_hi_relocs[SYMBOL_GENERAL] = "%hi(";
- mips_lo_relocs[SYMBOL_GENERAL] = "%lo(";
- }
- }
-
- if (TARGET_MIPS16)
- {
- /* The high part is provided by a pseudo copy of $gp. */
- mips_split_p[SYMBOL_SMALL_DATA] = true;
- mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gprel(";
- }
-
- if (TARGET_EXPLICIT_RELOCS)
- {
- /* Small data constants are kept whole until after reload,
- then lowered by mips_rewrite_small_data. */
- mips_lo_relocs[SYMBOL_SMALL_DATA] = "%gp_rel(";
-
- mips_split_p[SYMBOL_GOT_LOCAL] = true;
- if (TARGET_NEWABI)
- {
- mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got_page(";
- mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%got_ofst(";
- }
- else
- {
- mips_lo_relocs[SYMBOL_GOTOFF_PAGE] = "%got(";
- mips_lo_relocs[SYMBOL_GOT_LOCAL] = "%lo(";
- }
-
- if (TARGET_XGOT)
- {
- /* The HIGH and LO_SUM are matched by special .md patterns. */
- mips_split_p[SYMBOL_GOT_GLOBAL] = true;
-
- mips_split_p[SYMBOL_GOTOFF_GLOBAL] = true;
- mips_hi_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got_hi(";
- mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%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_GLOBAL] = "%got_disp(";
- else
- mips_lo_relocs[SYMBOL_GOTOFF_GLOBAL] = "%got(";
- mips_lo_relocs[SYMBOL_GOTOFF_CALL] = "%call16(";
- }
- }
-
- if (TARGET_NEWABI)
- {
- mips_split_p[SYMBOL_GOTOFF_LOADGP] = true;
- mips_hi_relocs[SYMBOL_GOTOFF_LOADGP] = "%hi(%neg(%gp_rel(";
- mips_lo_relocs[SYMBOL_GOTOFF_LOADGP] = "%lo(%neg(%gp_rel(";
- }
-
- /* Thread-local relocation operators. */
- mips_lo_relocs[SYMBOL_TLSGD] = "%tlsgd(";
- mips_lo_relocs[SYMBOL_TLSLDM] = "%tlsldm(";
- mips_split_p[SYMBOL_DTPREL] = 1;
- mips_hi_relocs[SYMBOL_DTPREL] = "%dtprel_hi(";
- mips_lo_relocs[SYMBOL_DTPREL] = "%dtprel_lo(";
- mips_lo_relocs[SYMBOL_GOTTPREL] = "%gottprel(";
- mips_split_p[SYMBOL_TPREL] = 1;
- mips_hi_relocs[SYMBOL_TPREL] = "%tprel_hi(";
- mips_lo_relocs[SYMBOL_TPREL] = "%tprel_lo(";
-
- /* We don't have a thread pointer access instruction on MIPS16, or
- appropriate TLS relocations. */
- if (TARGET_MIPS16)
- targetm.have_tls = false;
-
- /* Default to working around R4000 errata only if the processor
- was selected explicitly. */
- if ((target_flags_explicit & MASK_FIX_R4000) == 0
- && 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;
-}
-
-/* Implement CONDITIONAL_REGISTER_USAGE. */
-
-void
-mips_conditional_register_usage (void)
-{
- if (!TARGET_DSP)
- {
- 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;
-
- 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)
- {
- int regno;
-
- /* We only have a single condition code register. We
- implement this by hiding 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 caller 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;
- }
- /* fp20-23 are now caller saved. */
- 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 from fp21 to fp31 are now caller saved. */
- 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;
- }
-}
-
-/* Allocate a chunk of memory for per-function machine-dependent data. */
-static struct machine_function *
-mips_init_machine_status (void)
-{
- return ((struct machine_function *)
- ggc_alloc_cleared (sizeof (struct machine_function)));
-}
-
-/* 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. */
-
-void
-mips_order_regs_for_local_alloc (void)
-{
- register int i;
-
- for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
- reg_alloc_order[i] = i;
-
- 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;
- }
-}
-
-
-/* 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. */
-
-HOST_WIDE_INT
-mips_debugger_offset (rtx addr, HOST_WIDE_INT offset)
-{
- rtx offset2 = const0_rtx;
- rtx reg = eliminate_constant_term (addr, &offset2);
-
- if (offset == 0)
- offset = INTVAL (offset2);
-
- if (reg == stack_pointer_rtx || reg == frame_pointer_rtx
- || reg == hard_frame_pointer_rtx)
- {
- HOST_WIDE_INT frame_size = (!cfun->machine->frame.initialized)
- ? compute_frame_size (get_frame_size ())
- : cfun->machine->frame.total_size;
-
- /* MIPS16 frame is smaller */
- if (frame_pointer_needed && TARGET_MIPS16)
- frame_size -= cfun->machine->frame.args_size;
-
- offset = offset - frame_size;
- }
-
- /* 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;
-}
-
-/* Implement the PRINT_OPERAND macro. The MIPS-specific operand codes are:
-
- 'X' OP is CONST_INT, prints 32 bits in hexadecimal format = "0x%08x",
- 'x' OP is CONST_INT, prints 16 bits in hexadecimal format = "0x%04x",
- 'h' OP is HIGH, prints %hi(X),
- '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.
- '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' for a CONST_INT X, print mips_fp_conditions[X]
- 'Z' print the operand and a comma for ISA_HAS_8CC, otherwise print nothing
- 'R' print the reloc associated with LO_SUM
- 'q' print DSP accumulator registers
-
- The punctuation characters are:
-
- '(' 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.
- '/' Like '#', but does nothing within a delayed branch sequence
- '?' 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 (usually gp or $28).
- '~' Output a branch alignment to LABEL_ALIGN(NULL). */
-
-void
-print_operand (FILE *file, rtx op, int letter)
-{
- register enum rtx_code code;
-
- if (PRINT_OPERAND_PUNCT_VALID_P (letter))
- {
- switch (letter)
- {
- case '?':
- if (mips_branch_likely)
- putc ('l', file);
- break;
-
- case '@':
- fputs (reg_names [GP_REG_FIRST + 1], file);
- break;
-
- case '^':
- fputs (reg_names [PIC_FUNCTION_ADDR_REGNUM], file);
- break;
-
- case '.':
- fputs (reg_names [GP_REG_FIRST + 0], file);
- break;
-
- case '$':
- fputs (reg_names[STACK_POINTER_REGNUM], file);
- break;
-
- case '+':
- fputs (reg_names[PIC_OFFSET_TABLE_REGNUM], file);
- break;
-
- case '&':
- if (final_sequence != 0 && set_noreorder++ == 0)
- fputs (".set\tnoreorder\n\t", file);
- break;
-
- case '*':
- if (final_sequence != 0)
- {
- if (set_noreorder++ == 0)
- fputs (".set\tnoreorder\n\t", file);
-
- if (set_nomacro++ == 0)
- fputs (".set\tnomacro\n\t", file);
- }
- break;
-
- case '!':
- if (final_sequence != 0 && set_nomacro++ == 0)
- fputs ("\n\t.set\tnomacro", file);
- break;
-
- 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;
-
- case '(':
- if (set_noreorder++ == 0)
- fputs (".set\tnoreorder\n\t", file);
- break;
-
- case ')':
- if (set_noreorder == 0)
- error ("internal error: %%) found without a %%( in assembler pattern");
-
- else if (--set_noreorder == 0)
- fputs ("\n\t.set\treorder", file);
-
- break;
-
- case '[':
- if (set_noat++ == 0)
- fputs (".set\tnoat\n\t", file);
- break;
-
- 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);
-
- break;
-
- case '<':
- if (set_nomacro++ == 0)
- fputs (".set\tnomacro\n\t", file);
- break;
-
- 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);
-
- break;
-
- case '{':
- if (set_volatile++ == 0)
- fputs ("#.set\tvolatile\n\t", file);
- break;
-
- case '}':
- if (set_volatile == 0)
- error ("internal error: %%} found without a %%{ in assembler pattern");
- else if (--set_volatile == 0)
- fputs ("\n\t#.set\tnovolatile", file);
-
- break;
-
- case '~':
- {
- if (align_labels_log > 0)
- ASM_OUTPUT_ALIGN (file, align_labels_log);
- }
- break;
-
- default:
- error ("PRINT_OPERAND: unknown punctuation '%c'", letter);
- break;
- }
-
- return;
- }
-
- if (! op)
- {
- error ("PRINT_OPERAND null pointer");
- return;
- }
-
- code = GET_CODE (op);
-
- 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:
- fatal_insn ("PRINT_OPERAND, invalid insn for %%C", op);
- }
-
- 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:
- fatal_insn ("PRINT_OPERAND, invalid insn for %%N", op);
- }
-
- else if (letter == 'F')
- switch (code)
- {
- case EQ: fputs ("c1f", file); break;
- case NE: fputs ("c1t", file); break;
- default:
- fatal_insn ("PRINT_OPERAND, invalid insn for %%F", op);
- }
-
- else if (letter == 'W')
- switch (code)
- {
- case EQ: fputs ("c1t", file); break;
- case NE: fputs ("c1f", file); break;
- default:
- fatal_insn ("PRINT_OPERAND, invalid insn for %%W", op);
- }
-
- else if (letter == 'h')
- {
- if (GET_CODE (op) == HIGH)
- op = XEXP (op, 0);
-
- print_operand_reloc (file, op, mips_hi_relocs);
- }
-
- else if (letter == 'R')
- print_operand_reloc (file, op, mips_lo_relocs);
-
- else if (letter == 'Y')
- {
- if (GET_CODE (op) == CONST_INT
- && ((unsigned HOST_WIDE_INT) INTVAL (op)
- < ARRAY_SIZE (mips_fp_conditions)))
- fputs (mips_fp_conditions[INTVAL (op)], file);
- else
- output_operand_lossage ("invalid %%Y value");
- }
-
- else if (letter == 'Z')
- {
- if (ISA_HAS_8CC)
- {
- print_operand (file, op, 0);
- fputc (',', file);
- }
- }
-
- else if (letter == 'q')
- {
- int regnum;
-
- if (code != REG)
- fatal_insn ("PRINT_OPERAND, invalid insn for %%q", op);
-
- regnum = REGNO (op);
- if (MD_REG_P (regnum))
- fprintf (file, "$ac0");
- else if (DSP_ACC_REG_P (regnum))
- fprintf (file, "$ac%c", reg_names[regnum][3]);
- else
- fatal_insn ("PRINT_OPERAND, invalid insn for %%q", op);
- }
-
- else if (code == REG || code == SUBREG)
- {
- register int regnum;
-
- if (code == REG)
- regnum = REGNO (op);
- else
- regnum = true_regnum (op);
-
- if ((letter == 'M' && ! WORDS_BIG_ENDIAN)
- || (letter == 'L' && WORDS_BIG_ENDIAN)
- || letter == 'D')
- regnum++;
-
- fprintf (file, "%s", reg_names[regnum]);
- }
-
- else if (code == MEM)
- {
- if (letter == 'D')
- output_address (plus_constant (XEXP (op, 0), 4));
- else
- output_address (XEXP (op, 0));
- }
-
- else if (letter == 'x' && GET_CODE (op) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, 0xffff & INTVAL(op));
-
- else if (letter == 'X' && GET_CODE(op) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_HEX, INTVAL (op));
-
- else if (letter == 'd' && GET_CODE(op) == CONST_INT)
- fprintf (file, HOST_WIDE_INT_PRINT_DEC, (INTVAL(op)));
-
- else if (letter == 'z' && op == CONST0_RTX (GET_MODE (op)))
- fputs (reg_names[GP_REG_FIRST], file);
-
- else if (letter == 'd' || letter == 'x' || letter == 'X')
- output_operand_lossage ("invalid use of %%d, %%x, or %%X");
-
- else if (letter == 'T' || letter == 't')
- {
- int truth = (code == NE) == (letter == 'T');
- fputc ("zfnt"[truth * 2 + (GET_MODE (op) == CCmode)], file);
- }
-
- else if (CONST_GP_P (op))
- fputs (reg_names[GLOBAL_POINTER_REGNUM], file);
-
- else
- output_addr_const (file, op);
-}
-
-
-/* Print symbolic operand OP, which is part of a HIGH or LO_SUM.
- RELOCS is the array of relocations to use. */
-
-static void
-print_operand_reloc (FILE *file, rtx op, const char **relocs)
-{
- enum mips_symbol_type symbol_type;
- const char *p;
- rtx base;
- HOST_WIDE_INT offset;
-
- symbol_type = mips_classify_symbolic_expression (op);
- if (relocs[symbol_type] == 0)
- fatal_insn ("PRINT_OPERAND, invalid operand for relocation", op);
-
- /* If OP uses an UNSPEC address, we want to print the inner symbol. */
- mips_split_const (op, &base, &offset);
- if (UNSPEC_ADDRESS_P (base))
- op = plus_constant (UNSPEC_ADDRESS (base), offset);
-
- fputs (relocs[symbol_type], file);
- output_addr_const (file, op);
- for (p = relocs[symbol_type]; *p != 0; p++)
- if (*p == '(')
- fputc (')', file);
-}
-
-/* Output address operand X to FILE. */
-
-void
-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:
- print_operand (file, addr.offset, 0);
- fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
- return;
-
- case ADDRESS_LO_SUM:
- print_operand (file, addr.offset, 'R');
- fprintf (file, "(%s)", reg_names[REGNO (addr.reg)]);
- return;
-
- case ADDRESS_CONST_INT:
- output_addr_const (file, x);
- fprintf (file, "(%s)", reg_names[0]);
- return;
-
- case ADDRESS_SYMBOLIC:
- output_addr_const (file, x);
- return;
- }
- gcc_unreachable ();
-}
-
-/* When using assembler macros, keep track of all of small-data externs
- so that mips_file_end can emit the appropriate declarations for them.
-
- 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. */
-
-int
-mips_output_external (FILE *file ATTRIBUTE_UNUSED, tree decl, const char *name)
-{
- register struct extern_list *p;
-
- if (!TARGET_EXPLICIT_RELOCS && mips_in_small_data_p (decl))
- {
- p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
- p->next = extern_head;
- p->name = name;
- p->size = int_size_in_bytes (TREE_TYPE (decl));
- extern_head = p;
- }
-
- if (TARGET_IRIX && mips_abi == ABI_32 && TREE_CODE (decl) == FUNCTION_DECL)
- {
- p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
- p->next = extern_head;
- p->name = name;
- p->size = -1;
- extern_head = p;
- }
-
- return 0;
-}
-
-#if TARGET_IRIX
-static void
-irix_output_external_libcall (rtx fun)
-{
- register struct extern_list *p;
-
- if (mips_abi == ABI_32)
- {
- p = (struct extern_list *) ggc_alloc (sizeof (struct extern_list));
- p->next = extern_head;
- p->name = XSTR (fun, 0);
- p->size = -1;
- extern_head = p;
- }
-}
-#endif
-
-/* Emit a new filename to a stream. If we are smuggling stabs, try to
- put out a MIPS ECOFF file and a stab. */
-
-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)
- {
- 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);
- }
-
- /* 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)
- {
- 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);
- }
-}
-
-/* Output an ASCII string, in a space-saving way. PREFIX is the string
- that should be written before the opening quote, such as "\t.ascii\t"
- for real string data or "\t# " for a comment. */
-
-void
-mips_output_ascii (FILE *stream, const char *string_param, size_t len,
- const char *prefix)
-{
- size_t i;
- int cur_pos = 17;
- register const unsigned char *string =
- (const unsigned char *)string_param;
-
- fprintf (stream, "%s\"", prefix);
- for (i = 0; i < len; i++)
- {
- register int c = string[i];
-
- if (ISPRINT (c))
- {
- if (c == '\\' || c == '\"')
- {
- putc ('\\', stream);
- cur_pos++;
- }
- putc (c, stream);
- cur_pos++;
- }
- else
- {
- fprintf (stream, "\\%03o", c);
- cur_pos += 4;
- }
-
- if (cur_pos > 72 && i+1 < len)
- {
- cur_pos = 17;
- fprintf (stream, "\"\n%s\"", prefix);
- }
- }
- fprintf (stream, "\"\n");
-}
-
-/* Implement TARGET_ASM_FILE_START. */
-
-static void
-mips_file_start (void)
-{
- default_file_start ();
-
- if (!TARGET_IRIX)
- {
- /* 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. This is unnecessary for the IRIX 5/6 ABIs and
- causes unnecessary IRIX 6 ld warnings. */
- const char * abi_string = NULL;
-
- 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;
- default:
- gcc_unreachable ();
- }
- /* Note - we use fprintf directly rather than calling switch_to_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 (asm_out_file, "\t.section .mdebug.%s\n", abi_string);
-
- /* There is no ELF header flag to distinguish long32 forms of the
- EABI from long64 forms. Emit a special section to help tools
- such as GDB. Do the same for o64, which is sometimes used with
- -mlong64. */
- if (mips_abi == ABI_EABI || mips_abi == ABI_O64)
- fprintf (asm_out_file, "\t.section .gcc_compiled_long%d\n",
- TARGET_LONG64 ? 64 : 32);
-
- /* Restore the default section. */
- fprintf (asm_out_file, "\t.previous\n");
- }
-
- /* Generate the pseudo ops that System V.4 wants. */
- if (TARGET_ABICALLS)
- fprintf (asm_out_file, "\t.abicalls\n");
-
- if (TARGET_MIPS16)
- fprintf (asm_out_file, "\t.set\tmips16\n");
-
- if (flag_verbose_asm)
- fprintf (asm_out_file, "\n%s -G value = %d, Arch = %s, ISA = %d\n",
- ASM_COMMENT_START,
- mips_section_threshold, mips_arch_info->name, mips_isa);
-}
-
-#ifdef BSS_SECTION_ASM_OP
-/* Implement ASM_OUTPUT_ALIGNED_BSS. This differs from the default only
- in the use of sbss. */
-
-void
-mips_output_aligned_bss (FILE *stream, tree decl, const char *name,
- unsigned HOST_WIDE_INT size, int align)
-{
- extern tree last_assemble_variable_decl;
-
- if (mips_in_small_data_p (decl))
- switch_to_section (get_named_section (NULL, ".sbss", 0));
- else
- switch_to_section (bss_section);
- ASM_OUTPUT_ALIGN (stream, floor_log2 (align / BITS_PER_UNIT));
- last_assemble_variable_decl = decl;
- ASM_DECLARE_OBJECT_NAME (stream, name, decl);
- ASM_OUTPUT_SKIP (stream, size != 0 ? size : 1);
-}
-#endif
-
-/* Implement TARGET_ASM_FILE_END. When using assembler macros, emit
- .externs for any small-data variables that turned out to be external. */
-
-static void
-mips_file_end (void)
-{
- tree name_tree;
- struct extern_list *p;
-
- if (extern_head)
- {
- fputs ("\n", asm_out_file);
-
- for (p = extern_head; p != 0; p = p->next)
- {
- name_tree = get_identifier (p->name);
-
- /* Positively ensure only one .extern for any given symbol. */
- if (!TREE_ASM_WRITTEN (name_tree)
- && TREE_SYMBOL_REFERENCED (name_tree))
- {
- TREE_ASM_WRITTEN (name_tree) = 1;
- /* 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. */
- if (TARGET_IRIX && mips_abi == ABI_32 && p->size == -1)
- {
- fputs ("\t.globl ", asm_out_file);
- assemble_name (asm_out_file, p->name);
- fputs (" .text\n", asm_out_file);
- }
- else
- {
- fputs ("\t.extern\t", asm_out_file);
- assemble_name (asm_out_file, p->name);
- fprintf (asm_out_file, ", %d\n", p->size);
- }
- }
- }
- }
-}
-
-/* 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. */
-
-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);
-}
-
-/* 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. */
-
-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);
-}
-
-/* Emit either a label, .comm, or .lcomm directive. When using assembler
- macros, mark the symbol as written so that mips_file_end 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);
-
- if (!TARGET_EXPLICIT_RELOCS)
- {
- tree name_tree = get_identifier (name);
- TREE_ASM_WRITTEN (name_tree) = 1;
- }
-}
-
-#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
-mips_declare_object_name (FILE *stream, const char *name,
- tree decl ATTRIBUTE_UNUSED)
-{
-#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
- ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
-#endif
-
- size_directive_output = 0;
- if (!flag_inhibit_size_directive && DECL_SIZE (decl))
- {
- HOST_WIDE_INT size;
-
- size_directive_output = 1;
- size = int_size_in_bytes (TREE_TYPE (decl));
- ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
- }
-
- mips_declare_object (stream, name, "", ":\n");
-}
-
-/* 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
-
-/* Return true if X 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_type symbol_type;
-
- return (TARGET_EXPLICIT_RELOCS
- && mips_symbolic_constant_p (x, &symbol_type)
- && symbol_type == SYMBOL_SMALL_DATA);
-}
-
-
-/* A for_each_rtx callback for mips_small_data_pattern_p. */
-
-static int
-mips_small_data_pattern_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (*loc) == LO_SUM)
- return -1;
-
- return mips_rewrite_small_data_p (*loc);
-}
-
-/* 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, 0);
-}
-
-/* A for_each_rtx callback, used by mips_rewrite_small_data. */
-
-static int
-mips_rewrite_small_data_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
-{
- if (mips_rewrite_small_data_p (*loc))
- *loc = gen_rtx_LO_SUM (Pmode, pic_offset_table_rtx, *loc);
-
- if (GET_CODE (*loc) == LO_SUM)
- return -1;
-
- return 0;
-}
-
-/* If possible, rewrite OP so that it refers to small data using
- explicit relocations. */
-
-rtx
-mips_rewrite_small_data (rtx op)
-{
- op = copy_insn (op);
- for_each_rtx (&op, mips_rewrite_small_data_1, 0);
- return op;
-}
-
-/* Return true if the current function has an insn that implicitly
- refers to $gp. */
-
-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;
-
- push_topmost_sequence ();
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- if (INSN_P (insn)
- && GET_CODE (PATTERN (insn)) != USE
- && GET_CODE (PATTERN (insn)) != CLOBBER
- && (get_attr_got (insn) != GOT_UNSET
- || small_data_pattern (PATTERN (insn), VOIDmode)))
- break;
- pop_topmost_sequence ();
-
- cfun->machine->has_gp_insn_p = (insn != 0);
- }
- return cfun->machine->has_gp_insn_p;
-}
-
-
-/* Return the register that should be used as the global pointer
- within this function. Return 0 if the function doesn't need
- a global pointer. */
-
-static unsigned int
-mips_global_pointer (void)
-{
- unsigned int regno;
-
- /* $gp is always available in non-abicalls code. */
- if (!TARGET_ABICALLS)
- return GLOBAL_POINTER_REGNUM;
-
- /* We must always provide $gp when it is used implicitly. */
- if (!TARGET_EXPLICIT_RELOCS)
- return GLOBAL_POINTER_REGNUM;
-
- /* FUNCTION_PROFILER includes a jal macro, so we need to give it
- a valid gp. */
- if (current_function_profile)
- return GLOBAL_POINTER_REGNUM;
-
- /* If the function has a nonlocal goto, $gp must hold the correct
- global pointer for the target function. */
- if (current_function_has_nonlocal_goto)
- return GLOBAL_POINTER_REGNUM;
-
- /* If the gp is never referenced, there's no need to initialize it.
- Note that reload can sometimes introduce constant pool references
- into a function that otherwise didn't need them. For example,
- suppose we have an instruction like:
-
- (set (reg:DF R1) (float:DF (reg:SI R2)))
-
- 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.
-
- In cases like these, reload will have added the constant to the pool
- but no instruction will yet refer to it. */
- if (!regs_ever_live[GLOBAL_POINTER_REGNUM]
- && !current_function_uses_const_pool
- && !mips_function_has_gp_insn ())
- return 0;
-
- /* We need a global pointer, but perhaps we can use a call-clobbered
- register instead of $gp. */
- if (TARGET_NEWABI && current_function_is_leaf)
- for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
- if (!regs_ever_live[regno]
- && call_used_regs[regno]
- && !fixed_regs[regno]
- && regno != PIC_FUNCTION_ADDR_REGNUM)
- return regno;
-
- return GLOBAL_POINTER_REGNUM;
-}
-
-
-/* Return true if the current function must save REGNO. */
-
-static bool
-mips_save_reg_p (unsigned int regno)
-{
- /* We only need to save $gp for NewABI PIC. */
- if (regno == GLOBAL_POINTER_REGNUM)
- return (TARGET_ABICALLS && TARGET_NEWABI
- && cfun->machine->global_pointer == regno);
-
- /* Check call-saved registers. */
- if (regs_ever_live[regno] && !call_used_regs[regno])
- return true;
-
- /* We need to save the old frame pointer before setting up a new one. */
- if (regno == HARD_FRAME_POINTER_REGNUM && frame_pointer_needed)
- return true;
-
- /* We need to save the incoming return address if it is ever clobbered
- within the function. */
- if (regno == GP_REG_FIRST + 31 && regs_ever_live[regno])
- return true;
-
- if (TARGET_MIPS16)
- {
- tree return_type;
-
- return_type = DECL_RESULT (current_function_decl);
-
- /* $18 is a special case in mips16 code. It may be used to call
- a function which returns a floating point value, but it is
- marked in call_used_regs. */
- if (regno == GP_REG_FIRST + 18 && regs_ever_live[regno])
- return true;
-
- /* $31 is also a special case. It will be used to copy a return
- value into the floating point registers if the return value is
- floating point. */
- if (regno == GP_REG_FIRST + 31
- && mips16_hard_float
- && !aggregate_value_p (return_type, current_function_decl)
- && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
- && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
- return true;
- }
-
- return false;
-}
-
-
-/* Return the bytes needed to compute the frame pointer from the current
- stack pointer. SIZE is the size (in bytes) of the local variables.
-
- 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 (HOST_WIDE_INT size)
-{
- 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 cprestore_size; /* # bytes that the cprestore slot takes up */
- 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 */
- unsigned int mask; /* mask of saved gp registers */
- unsigned int fmask; /* mask of saved fp registers */
-
- cfun->machine->global_pointer = mips_global_pointer ();
-
- gp_reg_size = 0;
- fp_reg_size = 0;
- mask = 0;
- fmask = 0;
- var_size = MIPS_STACK_ALIGN (size);
- args_size = current_function_outgoing_args_size;
- cprestore_size = MIPS_STACK_ALIGN (STARTING_FRAME_OFFSET) - args_size;
-
- /* The space set aside by STARTING_FRAME_OFFSET isn't needed in leaf
- functions. If the function has local variables, we're committed
- to allocating it anyway. Otherwise reclaim it here. */
- if (var_size == 0 && current_function_is_leaf)
- cprestore_size = args_size = 0;
-
- /* 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 (args_size == 0 && current_function_calls_alloca)
- args_size = 4 * UNITS_PER_WORD;
-
- total_size = var_size + args_size + cprestore_size;
-
- /* Calculate space needed for gp registers. */
- for (regno = GP_REG_FIRST; regno <= GP_REG_LAST; regno++)
- if (mips_save_reg_p (regno))
- {
- gp_reg_size += GET_MODE_SIZE (gpr_mode);
- mask |= 1 << (regno - GP_REG_FIRST);
- }
-
- /* 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 |= 1 << (regno - GP_REG_FIRST);
- }
- }
-
- /* 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 (mips_save_reg_p (regno))
- {
- fp_reg_size += FP_INC * UNITS_PER_FPREG;
- fmask |= ((1 << FP_INC) - 1) << (regno - FP_REG_FIRST);
- }
- }
-
- gp_reg_rounded = MIPS_STACK_ALIGN (gp_reg_size);
- total_size += gp_reg_rounded + MIPS_STACK_ALIGN (fp_reg_size);
-
- /* Add in the space required for saving incoming register arguments. */
- total_size += current_function_pretend_args_size;
- total_size += MIPS_STACK_ALIGN (cfun->machine->varargs_size);
-
- /* Save other computed information. */
- cfun->machine->frame.total_size = total_size;
- cfun->machine->frame.var_size = var_size;
- cfun->machine->frame.args_size = args_size;
- cfun->machine->frame.cprestore_size = cprestore_size;
- cfun->machine->frame.gp_reg_size = gp_reg_size;
- cfun->machine->frame.fp_reg_size = fp_reg_size;
- cfun->machine->frame.mask = mask;
- cfun->machine->frame.fmask = fmask;
- cfun->machine->frame.initialized = reload_completed;
- cfun->machine->frame.num_gp = gp_reg_size / UNITS_PER_WORD;
- cfun->machine->frame.num_fp = fp_reg_size / (FP_INC * UNITS_PER_FPREG);
-
- if (mask)
- {
- HOST_WIDE_INT offset;
-
- offset = (args_size + cprestore_size + var_size
- + gp_reg_size - GET_MODE_SIZE (gpr_mode));
- cfun->machine->frame.gp_sp_offset = offset;
- cfun->machine->frame.gp_save_offset = offset - total_size;
- }
- else
- {
- cfun->machine->frame.gp_sp_offset = 0;
- cfun->machine->frame.gp_save_offset = 0;
- }
-
- if (fmask)
- {
- HOST_WIDE_INT offset;
-
- offset = (args_size + cprestore_size + var_size
- + gp_reg_rounded + fp_reg_size
- - FP_INC * UNITS_PER_FPREG);
- cfun->machine->frame.fp_sp_offset = offset;
- cfun->machine->frame.fp_save_offset = offset - total_size;
- }
- else
- {
- cfun->machine->frame.fp_sp_offset = 0;
- cfun->machine->frame.fp_save_offset = 0;
- }
-
- /* Ok, we're done. */
- return total_size;
-}
-
-/* Implement INITIAL_ELIMINATION_OFFSET. FROM is either the frame
- pointer or argument pointer. TO is either the stack pointer or
- hard frame pointer. */
-
-HOST_WIDE_INT
-mips_initial_elimination_offset (int from, int to)
-{
- HOST_WIDE_INT offset;
-
- compute_frame_size (get_frame_size ());
-
- /* Set OFFSET to the offset from the stack pointer. */
- switch (from)
- {
- case FRAME_POINTER_REGNUM:
- offset = 0;
- break;
-
- case ARG_POINTER_REGNUM:
- offset = (cfun->machine->frame.total_size
- - current_function_pretend_args_size);
- break;
-
- default:
- gcc_unreachable ();
- }
-
- if (TARGET_MIPS16 && to == HARD_FRAME_POINTER_REGNUM)
- offset -= cfun->machine->frame.args_size;
-
- return offset;
-}
-
-/* Implement RETURN_ADDR_RTX. Note, 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);
-}
-
-/* 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. */
-
-static void
-mips_save_restore_reg (enum machine_mode mode, int regno,
- HOST_WIDE_INT offset, mips_save_restore_fn fn)
-{
- rtx mem;
-
- mem = gen_frame_mem (mode, plus_constant (stack_pointer_rtx, offset));
-
- fn (gen_rtx_REG (mode, regno), mem);
-}
-
-
-/* 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. */
-
-static void
-mips_for_each_saved_reg (HOST_WIDE_INT sp_offset, mips_save_restore_fn fn)
-{
-#define BITSET_P(VALUE, BIT) (((VALUE) & (1L << (BIT))) != 0)
-
- enum machine_mode fpr_mode;
- HOST_WIDE_INT offset;
- int regno;
-
- /* 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. */
- 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 (gpr_mode, regno, offset, fn);
- offset -= GET_MODE_SIZE (gpr_mode);
- }
-
- /* This loop must iterate over the same space as its companion in
- compute_frame_size. */
- offset = cfun->machine->frame.fp_sp_offset - sp_offset;
- fpr_mode = (TARGET_SINGLE_FLOAT ? SFmode : DFmode);
- for (regno = (FP_REG_LAST - FP_INC + 1);
- regno >= FP_REG_FIRST;
- regno -= FP_INC)
- 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);
- }
-#undef BITSET_P
-}
-
-/* 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. */
-
-static void
-mips_output_cplocal (void)
-{
- if (!TARGET_EXPLICIT_RELOCS
- && cfun->machine->global_pointer > 0
- && cfun->machine->global_pointer != GLOBAL_POINTER_REGNUM)
- output_asm_insn (".cplocal %+", 0);
-}
-
-/* Return the style of GP load sequence that is being used for the
- current function. */
-
-enum mips_loadgp_style
-mips_current_loadgp_style (void)
-{
- if (!TARGET_ABICALLS || cfun->machine->global_pointer == 0)
- return LOADGP_NONE;
-
- if (TARGET_ABSOLUTE_ABICALLS)
- return LOADGP_ABSOLUTE;
-
- return TARGET_NEWABI ? LOADGP_NEWABI : LOADGP_OLDABI;
-}
-
-/* The __gnu_local_gp symbol. */
-
-static GTY(()) rtx mips_gnu_local_gp;
-
-/* If we're generating n32 or n64 abicalls, emit instructions
- to set up the global pointer. */
-
-static void
-mips_emit_loadgp (void)
-{
- rtx addr, offset, incoming_address;
-
- switch (mips_current_loadgp_style ())
- {
- case LOADGP_ABSOLUTE:
- if (mips_gnu_local_gp == NULL)
- {
- mips_gnu_local_gp = gen_rtx_SYMBOL_REF (Pmode, "__gnu_local_gp");
- SYMBOL_REF_FLAGS (mips_gnu_local_gp) |= SYMBOL_FLAG_LOCAL;
- }
- emit_insn (gen_loadgp_noshared (mips_gnu_local_gp));
- break;
-
- 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 (gen_loadgp (offset, incoming_address));
- if (!TARGET_EXPLICIT_RELOCS)
- emit_insn (gen_loadgp_blockage ());
- break;
-
- default:
- break;
- }
-}
-
-/* Set up the stack and frame (if desired) for the function. */
-
-static void
-mips_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
-{
- const char *fnname;
- HOST_WIDE_INT tsize = cfun->machine->frame.total_size;
-
-#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
-
- /* 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);
-
- if (!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);
-
- if (!flag_inhibit_size_directive)
- {
- fputs ("\t.ent\t", file);
- assemble_name (file, fnname);
- fputs ("\n", file);
- }
-
- assemble_name (file, fnname);
- fputs (":\n", file);
- }
-
- /* 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;
-
- if (!flag_inhibit_size_directive)
- {
- /* .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 && TARGET_MIPS16)
- ? tsize - cfun->machine->frame.args_size
- : tsize),
- reg_names[GP_REG_FIRST + 31],
- cfun->machine->frame.var_size,
- cfun->machine->frame.num_gp,
- cfun->machine->frame.num_fp,
- cfun->machine->frame.args_size,
- cfun->machine->frame.cprestore_size);
-
- /* .mask MASK, GPOFFSET; .fmask FPOFFSET */
- fprintf (file, "\t.mask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
- cfun->machine->frame.mask,
- cfun->machine->frame.gp_save_offset);
- fprintf (file, "\t.fmask\t0x%08x," HOST_WIDE_INT_PRINT_DEC "\n",
- cfun->machine->frame.fmask,
- cfun->machine->frame.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_current_loadgp_style () == LOADGP_OLDABI)
- {
- /* Handle the initialization of $gp for SVR4 PIC. */
- 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);
-
- /* 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 ();
-}
-
-/* Make the last instruction frame related and note that it performs
- the operation described by FRAME_PATTERN. */
-
-static void
-mips_set_frame_expr (rtx frame_pattern)
-{
- rtx insn;
-
- 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 set;
-
- /* 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);
-
- set = gen_rtx_SET (VOIDmode, mem, reg);
- RTX_FRAME_RELATED_P (set) = 1;
-
- return set;
-}
-
-
-/* Save register REG to MEM. Make the instruction frame-related. */
-
-static void
-mips_save_reg (rtx reg, rtx mem)
-{
- if (GET_MODE (reg) == DFmode && !TARGET_FLOAT64)
- {
- rtx x1, x2;
-
- if (mips_split_64bit_move_p (mem, reg))
- mips_split_64bit_move (mem, reg);
- else
- emit_move_insn (mem, reg);
-
- x1 = mips_frame_set (mips_subword (mem, 0), mips_subword (reg, 0));
- x2 = mips_frame_set (mips_subword (mem, 1), mips_subword (reg, 1));
- mips_set_frame_expr (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x1, x2)));
- }
- else
- {
- if (TARGET_MIPS16
- && REGNO (reg) != GP_REG_FIRST + 31
- && !M16_REG_P (REGNO (reg)))
- {
- /* Save a non-mips16 register by moving it through a temporary.
- We don't need to do this for $31 since there's a special
- instruction for it. */
- emit_move_insn (MIPS_PROLOGUE_TEMP (GET_MODE (reg)), reg);
- emit_move_insn (mem, MIPS_PROLOGUE_TEMP (GET_MODE (reg)));
- }
- else
- emit_move_insn (mem, reg);
-
- mips_set_frame_expr (mips_frame_set (mem, reg));
- }
-}
-
-
-/* Expand the prologue into a bunch of separate insns. */
-
-void
-mips_expand_prologue (void)
-{
- HOST_WIDE_INT size;
-
- if (cfun->machine->global_pointer > 0)
- REGNO (pic_offset_table_rtx) = cfun->machine->global_pointer;
-
- size = compute_frame_size (get_frame_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 ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
- {
- HOST_WIDE_INT step1;
-
- step1 = MIN (size, MIPS_MAX_FIRST_STACK_STEP);
- RTX_FRAME_RELATED_P (emit_insn (gen_add3_insn (stack_pointer_rtx,
- stack_pointer_rtx,
- GEN_INT (-step1)))) = 1;
- size -= step1;
- mips_for_each_saved_reg (size, mips_save_reg);
- }
-
- /* 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
- {
- emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), GEN_INT (size));
- if (TARGET_MIPS16)
- {
- /* 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);
- emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
- emit_insn (gen_sub3_insn (hard_frame_pointer_rtx,
- hard_frame_pointer_rtx,
- MIPS_PROLOGUE_TEMP (Pmode)));
- emit_move_insn (stack_pointer_rtx, hard_frame_pointer_rtx);
- }
- else
- emit_insn (gen_sub3_insn (stack_pointer_rtx,
- stack_pointer_rtx,
- MIPS_PROLOGUE_TEMP (Pmode)));
-
- /* 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)));
- }
- }
-
- /* Set up the frame pointer, if we're using one. In mips16 code,
- we point the frame pointer ahead of the outgoing argument area.
- This should allow more variables & incoming arguments to be
- accessed with unextended instructions. */
- if (frame_pointer_needed)
- {
- if (TARGET_MIPS16 && cfun->machine->frame.args_size != 0)
- {
- rtx offset = GEN_INT (cfun->machine->frame.args_size);
- if (SMALL_OPERAND (cfun->machine->frame.args_size))
- RTX_FRAME_RELATED_P
- (emit_insn (gen_add3_insn (hard_frame_pointer_rtx,
- stack_pointer_rtx,
- offset))) = 1;
- else
- {
- emit_move_insn (MIPS_PROLOGUE_TEMP (Pmode), offset);
- emit_move_insn (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,
- cfun->machine->frame.args_size)));
- }
- }
- else
- RTX_FRAME_RELATED_P (emit_move_insn (hard_frame_pointer_rtx,
- stack_pointer_rtx)) = 1;
- }
-
- mips_emit_loadgp ();
-
- /* If generating o32/o64 abicalls, save $gp on the stack. */
- if (TARGET_ABICALLS && !TARGET_NEWABI && !current_function_is_leaf)
- emit_insn (gen_cprestore (GEN_INT (current_function_outgoing_args_size)));
-
- /* If we are profiling, make sure no instructions are scheduled before
- the call to mcount. */
-
- if (current_function_profile)
- emit_insn (gen_blockage ());
-}
-
-/* Do any necessary cleanup after a function to restore stack, frame,
- and regs. */
-
-#define RA_MASK BITMASK_HIGH /* 1 << 31 */
-
-static void
-mips_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
- HOST_WIDE_INT size ATTRIBUTE_UNUSED)
-{
- /* Reinstate the normal $gp. */
- REGNO (pic_offset_table_rtx) = GLOBAL_POINTER_REGNUM;
- mips_output_cplocal ();
-
- 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;
- }
-
- if (!FUNCTION_NAME_ALREADY_DECLARED && !flag_inhibit_size_directive)
- {
- const char *fnname;
-
- /* 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);
- fputs ("\t.end\t", file);
- assemble_name (file, fnname);
- fputs ("\n", file);
- }
-}
-
-/* Emit instructions to restore register REG from slot MEM. */
-
-static void
-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), 7);
-
- if (TARGET_MIPS16 && !M16_REG_P (REGNO (reg)))
- {
- /* Can't restore directly; move through a temporary. */
- emit_move_insn (MIPS_EPILOGUE_TEMP (GET_MODE (reg)), mem);
- emit_move_insn (reg, MIPS_EPILOGUE_TEMP (GET_MODE (reg)));
- }
- else
- emit_move_insn (reg, mem);
-}
-
-
-/* Expand the epilogue into a bunch of separate insns. SIBCALL_P is true
- if this epilogue precedes a sibling call, false if it is for a normal
- "epilogue" pattern. */
-
-void
-mips_expand_epilogue (int sibcall_p)
-{
- HOST_WIDE_INT step1, step2;
- rtx base, target;
-
- if (!sibcall_p && mips_can_use_return_insn ())
- {
- emit_jump_insn (gen_return ());
- return;
- }
-
- /* 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.
-
- Start off by assuming that no registers need to be restored. */
- step1 = cfun->machine->frame.total_size;
- step2 = 0;
-
- /* Work out which register holds the frame address. Account for the
- frame pointer offset used by mips16 code. */
- if (!frame_pointer_needed)
- base = stack_pointer_rtx;
- else
- {
- base = hard_frame_pointer_rtx;
- if (TARGET_MIPS16)
- step1 -= cfun->machine->frame.args_size;
- }
-
- /* If we need to restore registers, deallocate as much stack as
- possible in the second step without going out of range. */
- if ((cfun->machine->frame.mask | cfun->machine->frame.fmask) != 0)
- {
- step2 = MIN (step1, MIPS_MAX_FIRST_STACK_STEP);
- step1 -= step2;
- }
-
- /* Set TARGET to BASE + STEP1. */
- target = base;
- if (step1 > 0)
- {
- rtx adjust;
-
- /* Get an rtx for STEP1 that we can add to BASE. */
- adjust = GEN_INT (step1);
- if (!SMALL_OPERAND (step1))
- {
- emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), adjust);
- adjust = MIPS_EPILOGUE_TEMP (Pmode);
- }
-
- /* Normal mode code can copy the result straight into $sp. */
- if (!TARGET_MIPS16)
- target = stack_pointer_rtx;
-
- emit_insn (gen_add3_insn (target, base, adjust));
- }
-
- /* Copy TARGET into the stack pointer. */
- if (target != stack_pointer_rtx)
- emit_move_insn (stack_pointer_rtx, target);
-
- /* If we're using addressing macros for n32/n64 abicalls, $gp is
- implicitly used by all SYMBOL_REFs. We must emit a blockage
- insn before restoring it. */
- if (TARGET_ABICALLS && TARGET_NEWABI && !TARGET_EXPLICIT_RELOCS)
- emit_insn (gen_blockage ());
-
- /* Restore the registers. */
- mips_for_each_saved_reg (cfun->machine->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)));
-
- /* Add in the __builtin_eh_return stack adjustment. We need to
- use a temporary in mips16 code. */
- if (current_function_calls_eh_return)
- {
- if (TARGET_MIPS16)
- {
- emit_move_insn (MIPS_EPILOGUE_TEMP (Pmode), stack_pointer_rtx);
- emit_insn (gen_add3_insn (MIPS_EPILOGUE_TEMP (Pmode),
- MIPS_EPILOGUE_TEMP (Pmode),
- EH_RETURN_STACKADJ_RTX));
- emit_move_insn (stack_pointer_rtx, MIPS_EPILOGUE_TEMP (Pmode));
- }
- else
- emit_insn (gen_add3_insn (stack_pointer_rtx,
- stack_pointer_rtx,
- EH_RETURN_STACKADJ_RTX));
- }
-
- if (!sibcall_p)
- {
- /* The mips16 loads the return address into $7, not $31. */
- if (TARGET_MIPS16 && (cfun->machine->frame.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)));
- }
-}
-
-/* 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. */
-
-int
-mips_can_use_return_insn (void)
-{
- tree return_type;
-
- if (! reload_completed)
- return 0;
-
- if (regs_ever_live[31] || current_function_profile)
- return 0;
-
- return_type = DECL_RESULT (current_function_decl);
-
- /* 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 (return_type, current_function_decl)
- && GET_MODE_CLASS (DECL_MODE (return_type)) == MODE_FLOAT
- && GET_MODE_SIZE (DECL_MODE (return_type)) <= UNITS_PER_FPVALUE)
- return 0;
-
- if (cfun->machine->frame.initialized)
- return cfun->machine->frame.total_size == 0;
-
- return compute_frame_size (get_frame_size ()) == 0;
-}
-
-/* Implement TARGET_ASM_OUTPUT_MI_THUNK. Generate rtl rather than asm text
- in order to avoid duplicating too much logic from elsewhere. */
-
-static void
-mips_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
- HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
- tree function)
-{
- rtx this, temp1, temp2, insn, fnaddr;
-
- /* Pretend to be a post-reload pass while generating rtl. */
- no_new_pseudos = 1;
- reload_completed = 1;
- reset_block_changes ();
-
- /* Pick a global pointer for -mabicalls. Use $15 rather than $28
- for TARGET_NEWABI since the latter is a call-saved register. */
- if (TARGET_ABICALLS)
- cfun->machine->global_pointer
- = REGNO (pic_offset_table_rtx)
- = TARGET_NEWABI ? 15 : GLOBAL_POINTER_REGNUM;
-
- /* Set up the global pointer for n32 or n64 abicalls. */
- mips_emit_loadgp ();
-
- /* 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 = gen_rtx_REG (Pmode, GP_ARG_FIRST + 1);
- else
- this = gen_rtx_REG (Pmode, GP_ARG_FIRST);
-
- /* Add DELTA to THIS. */
- if (delta != 0)
- {
- rtx offset = GEN_INT (delta);
- if (!SMALL_OPERAND (delta))
- {
- emit_move_insn (temp1, offset);
- offset = temp1;
- }
- emit_insn (gen_add3_insn (this, this, offset));
- }
-
- /* If needed, add *(*THIS + VCALL_OFFSET) to THIS. */
- if (vcall_offset != 0)
- {
- rtx addr;
-
- /* Set TEMP1 to *THIS. */
- emit_move_insn (temp1, gen_rtx_MEM (Pmode, this));
-
- /* Set ADDR to a legitimate address for *THIS + VCALL_OFFSET. */
- addr = mips_add_offset (temp2, temp1, vcall_offset);
-
- /* Load the offset and add it to THIS. */
- emit_move_insn (temp1, gen_rtx_MEM (Pmode, addr));
- emit_insn (gen_add3_insn (this, this, temp1));
- }
-
- /* Jump to the target function. Use a sibcall if direct jumps are
- allowed, otherwise load the address into a register first. */
- fnaddr = XEXP (DECL_RTL (function), 0);
- if (TARGET_MIPS16 || TARGET_ABICALLS || TARGET_LONG_CALLS)
- {
- /* 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 o32 and o64,
- but it's still wrong for n32 and n64; 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. */
- if (TARGET_ABICALLS && !mips_dangerous_for_la25_p (fnaddr))
- temp1 = gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM);
- mips_load_call_address (temp1, fnaddr, true);
-
- if (TARGET_ABICALLS && REGNO (temp1) != PIC_FUNCTION_ADDR_REGNUM)
- emit_move_insn (gen_rtx_REG (Pmode, PIC_FUNCTION_ADDR_REGNUM), temp1);
- emit_jump_insn (gen_indirect_jump (temp1));
- }
- else
- {
- insn = emit_call_insn (gen_sibcall_internal (fnaddr, const0_rtx));
- SIBLING_CALL_P (insn) = 1;
- }
-
- /* Run just enough of rest_of_compilation. This sequence was
- "borrowed" from alpha.c. */
- insn = get_insns ();
- insn_locators_initialize ();
- split_all_insns_noflow ();
- if (TARGET_MIPS16)
- mips16_lay_out_constants ();
- shorten_branches (insn);
- final_start_function (insn, file, 1);
- final (insn, file, 1);
- final_end_function ();
-
- /* Clean up the vars set above. Note that final_end_function resets
- the global pointer for us. */
- reload_completed = 0;
- no_new_pseudos = 0;
-}
-
-/* Returns nonzero if X contains a SYMBOL_REF. */
-
-static int
-symbolic_expression_p (rtx x)
-{
- if (GET_CODE (x) == SYMBOL_REF)
- return 1;
-
- if (GET_CODE (x) == CONST)
- return symbolic_expression_p (XEXP (x, 0));
-
- if (UNARY_P (x))
- return symbolic_expression_p (XEXP (x, 0));
-
- if (ARITHMETIC_P (x))
- return (symbolic_expression_p (XEXP (x, 0))
- || symbolic_expression_p (XEXP (x, 1)));
-
- return 0;
-}
-
-/* Choose the section to use for the constant rtx expression X that has
- mode MODE. */
-
-static section *
-mips_select_rtx_section (enum machine_mode mode, rtx x,
- unsigned HOST_WIDE_INT align)
-{
- if (TARGET_MIPS16)
- {
- /* In mips16 mode, the constant table always goes in the same section
- as the function, so that constants can be loaded using PC relative
- addressing. */
- return function_section (current_function_decl);
- }
- else if (TARGET_EMBEDDED_DATA)
- {
- /* For embedded applications, always put constants in read-only data,
- in order to reduce RAM usage. */
- return mergeable_constant_section (mode, align, 0);
- }
- else
- {
- /* For hosted applications, always put constants in small data if
- possible, as this gives the best performance. */
- /* ??? Consider using mergeable small data sections. */
-
- if (GET_MODE_SIZE (mode) <= (unsigned) mips_section_threshold
- && mips_section_threshold > 0)
- return get_named_section (NULL, ".sdata", 0);
- else if (flag_pic && symbolic_expression_p (x))
- return get_named_section (NULL, ".data.rel.ro", 3);
- else
- return mergeable_constant_section (mode, align, 0);
- }
-}
-
-/* Implement TARGET_ASM_FUNCTION_RODATA_SECTION.
-
- The complication here is that, with the combination TARGET_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. */
-
-static section *
-mips_function_rodata_section (tree decl)
-{
- if (!TARGET_ABICALLS || TARGET_GPWORD)
- return default_function_rodata_section (decl);
-
- 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;
-}
-
-/* Implement TARGET_IN_SMALL_DATA_P. This function controls whether
- locally-defined objects go in a small data section. It also controls
- the setting of the SYMBOL_REF_SMALL_P flag, which in turn helps
- mips_classify_symbol decide when to use %gp_rel(...)($gp) accesses. */
-
-static bool
-mips_in_small_data_p (tree decl)
-{
- HOST_WIDE_INT size;
-
- if (TREE_CODE (decl) == STRING_CST || TREE_CODE (decl) == FUNCTION_DECL)
- return false;
-
- /* We don't yet generate small-data references for -mabicalls. See related
- -G handling in override_options. */
- if (TARGET_ABICALLS)
- return false;
-
- if (TREE_CODE (decl) == VAR_DECL && DECL_SECTION_NAME (decl) != 0)
- {
- const char *name;
-
- /* 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 (TARGET_EXPLICIT_RELOCS || !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;
-
- if (TREE_READONLY (decl)
- && !TREE_SIDE_EFFECTS (decl)
- && (!DECL_INITIAL (decl) || TREE_CONSTANT (DECL_INITIAL (decl))))
- return false;
- }
-
- size = int_size_in_bytes (TREE_TYPE (decl));
- return (size > 0 && size <= mips_section_threshold);
-}
-
-/* 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. */
-
-static bool
-mips_use_anchors_for_symbol_p (rtx symbol)
-{
- switch (mips_classify_symbol (symbol))
- {
- case SYMBOL_CONSTANT_POOL:
- case SYMBOL_SMALL_DATA:
- return false;
-
- default:
- return true;
- }
-}
-
-/* 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 (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 (TREE_CODE (TREE_TYPE (field)) != REAL_TYPE)
- 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 (tree valtype)
-{
- tree fields[2];
-
- return (TARGET_NEWABI
- && TARGET_BIG_ENDIAN
- && AGGREGATE_TYPE_P (valtype)
- && mips_fpr_return_fields (valtype, fields) == 0);
-}
-
-
-/* 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 : FP_INC);
- 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 (tree valtype, tree func ATTRIBUTE_UNUSED,
- enum machine_mode mode)
-{
- if (valtype)
- {
- tree fields[2];
- int unsignedp;
-
- mode = TYPE_MODE (valtype);
- unsignedp = TYPE_UNSIGNED (valtype);
-
- /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns
- true, we must promote the mode just as PROMOTE_MODE does. */
- mode = promote_mode (valtype, mode, &unsignedp, 1);
-
- /* Handle structures whose fields are returned in $f0/$f2. */
- switch (mips_fpr_return_fields (valtype, fields))
- {
- case 1:
- return gen_rtx_REG (mode, FP_RETURN);
-
- 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 ((GET_MODE_CLASS (mode) == MODE_FLOAT
- || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
- && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE)
- return gen_rtx_REG (mode, FP_RETURN);
-
- /* Handle long doubles for n32 & n64. */
- if (mode == TFmode)
- return mips_return_fpr_pair (mode,
- DImode, 0,
- DImode, GET_MODE_SIZE (mode) / 2);
-
- if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT
- && GET_MODE_SIZE (mode) <= UNITS_PER_HWFPVALUE * 2)
- return mips_return_fpr_pair (mode,
- GET_MODE_INNER (mode), 0,
- GET_MODE_INNER (mode),
- GET_MODE_SIZE (mode) / 2);
-
- return gen_rtx_REG (mode, GP_RETURN);
-}
-
-/* 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, tree type,
- bool named ATTRIBUTE_UNUSED)
-{
- if (mips_abi == ABI_EABI)
- {
- int size;
-
- /* ??? How should SCmode be handled? */
- if (mode == DImode || mode == DFmode)
- 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);
- }
-}
-
-static bool
-mips_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED, bool named)
-{
- return mips_abi == ABI_EABI && named;
-}
-
-/* Return true if registers of class CLASS cannot change from mode FROM
- to mode TO. */
-
-bool
-mips_cannot_change_mode_class (enum machine_mode from,
- enum machine_mode to, enum reg_class class)
-{
- if (MIN (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) <= UNITS_PER_WORD
- && MAX (GET_MODE_SIZE (from), GET_MODE_SIZE (to)) > UNITS_PER_WORD)
- {
- if (TARGET_BIG_ENDIAN)
- {
- /* When a multi-word value is stored in paired floating-point
- registers, the first register always holds the low word.
- We therefore can't allow FPRs to change between single-word
- and multi-word modes. */
- if (FP_INC > 1 && reg_classes_intersect_p (FP_REGS, class))
- return true;
- }
- else
- {
- /* LO_REGNO == HI_REGNO + 1, so if a multi-word value is stored
- in LO and HI, the high word always comes first. We therefore
- can't allow values stored in HI to change between single-word
- and multi-word modes.
- This rule applies to both the original HI/LO pair and the new
- DSP accumulators. */
- if (reg_classes_intersect_p (ACC_REGS, class))
- return true;
- }
- }
- /* Loading a 32-bit value into a 64-bit floating-point register
- will not sign-extend the value, despite what LOAD_EXTEND_OP says.
- We can't allow 64-bit float registers to change from SImode to
- to a wider mode. */
- if (TARGET_FLOAT64
- && from == SImode
- && GET_MODE_SIZE (to) >= UNITS_PER_WORD
- && reg_classes_intersect_p (FP_REGS, class))
- return true;
- return false;
-}
-
-/* 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. */
-
-bool
-mips_dangerous_for_la25_p (rtx x)
-{
- HOST_WIDE_INT offset;
-
- if (TARGET_EXPLICIT_RELOCS)
- return false;
-
- mips_split_const (x, &x, &offset);
- return global_got_operand (x, VOIDmode);
-}
-
-/* Implement PREFERRED_RELOAD_CLASS. */
-
-enum reg_class
-mips_preferred_reload_class (rtx x, enum reg_class class)
-{
- if (mips_dangerous_for_la25_p (x) && reg_class_subset_p (LEA_REGS, class))
- return LEA_REGS;
-
- if (TARGET_HARD_FLOAT
- && FLOAT_MODE_P (GET_MODE (x))
- && reg_class_subset_p (FP_REGS, class))
- return FP_REGS;
-
- if (reg_class_subset_p (GR_REGS, class))
- class = GR_REGS;
-
- if (TARGET_MIPS16 && reg_class_subset_p (M16_REGS, class))
- class = M16_REGS;
-
- return class;
-}
-
-/* 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. */
-
-enum reg_class
-mips_secondary_reload_class (enum reg_class class,
- enum machine_mode mode, rtx x, int in_p)
-{
- enum reg_class gr_regs = TARGET_MIPS16 ? M16_REGS : GR_REGS;
- int regno = -1;
- int gp_reg_p;
-
- if (REG_P (x)|| GET_CODE (x) == SUBREG)
- regno = true_regnum (x);
-
- gp_reg_p = TARGET_MIPS16 ? M16_REG_P (regno) : GP_REG_P (regno);
-
- if (mips_dangerous_for_la25_p (x))
- {
- gr_regs = LEA_REGS;
- if (TEST_HARD_REG_BIT (reg_class_contents[(int) class], 25))
- return gr_regs;
- }
-
- /* Copying from HI or LO to anywhere other than a general register
- requires a general register.
- This rule applies to both the original HI/LO pair and the new
- DSP accumulators. */
- if (reg_class_subset_p (class, ACC_REGS))
- {
- if (TARGET_MIPS16 && in_p)
- {
- /* We can't really copy to HI or LO at all in mips16 mode. */
- return M16_REGS;
- }
- return gp_reg_p ? NO_REGS : gr_regs;
- }
- if (ACC_REG_P (regno))
- {
- if (TARGET_MIPS16 && ! in_p)
- {
- /* We can't really copy to HI or LO at all in mips16 mode. */
- return M16_REGS;
- }
- return class == gr_regs ? NO_REGS : gr_regs;
- }
-
- /* We can only copy a value to a condition code register from a
- floating point register, and even then we require a scratch
- floating point register. We can only copy a value out of a
- condition code register into a general register. */
- if (class == ST_REGS)
- {
- if (in_p)
- return FP_REGS;
- return gp_reg_p ? NO_REGS : gr_regs;
- }
- if (ST_REG_P (regno))
- {
- if (! in_p)
- return FP_REGS;
- return class == gr_regs ? NO_REGS : gr_regs;
- }
-
- if (class == FP_REGS)
- {
- if (MEM_P (x))
- {
- /* In this case we can use lwc1, swc1, ldc1 or sdc1. */
- return NO_REGS;
- }
- else if (CONSTANT_P (x) && GET_MODE_CLASS (mode) == MODE_FLOAT)
- {
- /* We can use the l.s and l.d macros to load floating-point
- constants. ??? For l.s, we could probably get better
- code by returning GR_REGS here. */
- return NO_REGS;
- }
- else if (gp_reg_p || x == CONST0_RTX (mode))
- {
- /* In this case we can use mtc1, mfc1, dmtc1 or dmfc1. */
- return NO_REGS;
- }
- else if (FP_REG_P (regno))
- {
- /* In this case we can use mov.s or mov.d. */
- return NO_REGS;
- }
- else
- {
- /* Otherwise, we need to reload through an integer register. */
- return gr_regs;
- }
- }
-
- /* In mips16 mode, going between memory and anything but M16_REGS
- requires an M16_REG. */
- if (TARGET_MIPS16)
- {
- if (class != M16_REGS && class != M16_NA_REGS)
- {
- if (gp_reg_p)
- return NO_REGS;
- return M16_REGS;
- }
- if (! gp_reg_p)
- {
- if (class == M16_REGS || class == M16_NA_REGS)
- return NO_REGS;
- return M16_REGS;
- }
- }
-
- return NO_REGS;
-}
-
-/* Implement CLASS_MAX_NREGS.
-
- Usually all registers are word-sized. The only supported exception
- is -mgp64 -msingle-float, which has 64-bit words but 32-bit float
- registers. A word-based calculation is correct even in that case,
- since -msingle-float disallows multi-FPR values.
-
- The FP status registers are an exception to this rule. They are always
- 4 bytes wide as they only hold condition code modes, and CCmode is always
- considered to be 4 bytes wide. */
-
-int
-mips_class_max_nregs (enum reg_class class ATTRIBUTE_UNUSED,
- enum machine_mode mode)
-{
- if (class == ST_REGS)
- return (GET_MODE_SIZE (mode) + 3) / 4;
- else
- return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-}
-
-static bool
-mips_valid_pointer_mode (enum machine_mode mode)
-{
- return (mode == SImode || (TARGET_64BIT && mode == DImode));
-}
-
-/* Target hook for vector_mode_supported_p. */
-
-static bool
-mips_vector_mode_supported_p (enum machine_mode mode)
-{
- switch (mode)
- {
- case V2SFmode:
- return TARGET_PAIRED_SINGLE_FLOAT;
-
- case V2HImode:
- case V4QImode:
- return TARGET_DSP;
-
- default:
- return false;
- }
-}
-
-/* If we can access small data directly (using gp-relative relocation
- operators) return the small data pointer, otherwise return null.
-
- 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. */
-
-static rtx
-mips16_gp_pseudo_reg (void)
-{
- if (cfun->machine->mips16_gp_pseudo_rtx == NULL_RTX)
- {
- rtx unspec;
- rtx insn, scan;
-
- cfun->machine->mips16_gp_pseudo_rtx = gen_reg_rtx (Pmode);
-
- /* We want to initialize this to a value which gcc will believe
- is constant. */
- start_sequence ();
- unspec = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, const0_rtx), UNSPEC_GP);
- emit_move_insn (cfun->machine->mips16_gp_pseudo_rtx,
- gen_rtx_CONST (Pmode, unspec));
- insn = get_insns ();
- end_sequence ();
-
- 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 (NOTE_P (scan)
- && 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 ();
- }
-
- return cfun->machine->mips16_gp_pseudo_rtx;
-}
-
-/* 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 nonzero if
- we are copying from the floating point registers. */
-
-static void
-mips16_fp_args (FILE *file, int fp_code, int from_fp_p)
-{
- const char *s;
- int gparg, fparg;
- unsigned int f;
-
- /* This code only works for the original 32 bit ABI and the O64 ABI. */
- gcc_assert (TARGET_OLDABI);
-
- 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
- gcc_unreachable ();
-
- ++gparg;
- ++fparg;
- }
-}
-
-/* Build a mips16 function stub. This is used for functions which
- take arguments 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. */
-
-static void
-build_mips16_function_stub (FILE *file)
-{
- 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);
-
- 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)
- {
- fprintf (file, "%s%s",
- need_comma ? ", " : "",
- (f & 3) == 1 ? "float" : "double");
- need_comma = 1;
- }
- fprintf (file, ")\n");
-
- fprintf (file, "\t.set\tnomips16\n");
- switch_to_section (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 cannot emit another .ent. */
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.ent\t", file);
- assemble_name (file, stubname);
- fputs ("\n", file);
- }
-
- 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");
-
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.end\t", file);
- assemble_name (file, stubname);
- fputs ("\n", file);
- }
-
- fprintf (file, "\t.set\tmips16\n");
-
- switch_to_section (function_section (current_function_decl));
-}
-
-/* We keep a list of functions for which we have already built stubs
- in build_mips16_call_stub. */
-
-struct mips16_stub
-{
- struct mips16_stub *next;
- char *name;
- int fpret;
-};
-
-static struct mips16_stub *mips16_stubs;
-
-/* 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.
-
- Similarly, we need a stub if the return value might come back in a
- floating point register.
-
- RETVAL is the location of the return value, or null if this is
- a call rather than a call_value. FN is the address of the
- function and ARG_SIZE is the size of the arguments. FP_CODE
- is the code built by function_arg. This function returns a nonzero
- value if it builds the call instruction itself. */
-
-int
-build_mips16_call_stub (rtx retval, rtx fn, rtx arg_size, int fp_code)
-{
- int fpret;
- const char *fnname;
- char *secname, *stubname;
- struct mips16_stub *l;
- tree stubid, stubdecl;
- int need_comma;
- unsigned int f;
-
- /* 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;
-
- /* Figure out whether the value might come back in a floating point
- register. */
- fpret = (retval != 0
- && GET_MODE_CLASS (GET_MODE (retval)) == MODE_FLOAT
- && GET_MODE_SIZE (GET_MODE (retval)) <= UNITS_PER_FPVALUE);
-
- /* 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;
-
- /* 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;
-
- /* This code will only work for o32 and o64 abis. The other ABI's
- require more sophisticated support. */
- gcc_assert (TARGET_OLDABI);
-
- /* We can only handle SFmode and DFmode floating point return
- values. */
- if (fpret)
- gcc_assert (GET_MODE (retval) == SFmode || GET_MODE (retval) == DFmode);
-
- /* 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. */
-
- if (GET_CODE (fn) != SYMBOL_REF)
- {
- char buf[30];
- tree id;
- rtx stub_fn, insn;
-
- /* ??? If this code is modified to support other ABI's, we need
- to handle PARALLEL return values here. */
-
- 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));
-
- emit_move_insn (gen_rtx_REG (Pmode, 2), fn);
-
- if (retval == NULL_RTX)
- insn = gen_call_internal (stub_fn, arg_size);
- else
- insn = gen_call_value_internal (retval, stub_fn, arg_size);
- insn = emit_call_insn (insn);
-
- /* Put the register usage information on the CALL. */
- 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));
-
- /* Return 1 to tell the caller that we've generated the call
- insn. */
- return 1;
- }
-
- /* We know the function we are going to call. If we have already
- built a stub, we don't need to do anything further. */
-
- fnname = XSTR (fn, 0);
- for (l = mips16_stubs; l != NULL; l = l->next)
- if (strcmp (l->name, fnname) == 0)
- break;
-
- 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
-
- If the function does return a floating point value, the stub
- section is named
- .mips16.call.fp.FNNAME
- */
-
- 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);
-
- 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");
-
- fprintf (asm_out_file, "\t.set\tnomips16\n");
- assemble_start_function (stubdecl, stubname);
-
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.ent\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
-
- assemble_name (asm_out_file, stubname);
- fputs (":\n", asm_out_file);
- }
-
- /* 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. */
-
- /* We don't want the assembler to insert any nops here. */
- fprintf (asm_out_file, "\t.set\tnoreorder\n");
-
- mips16_fp_args (asm_out_file, fp_code, 0);
-
- 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");
- }
-
- fprintf (asm_out_file, "\t.set\treorder\n");
-
-#ifdef ASM_DECLARE_FUNCTION_SIZE
- ASM_DECLARE_FUNCTION_SIZE (asm_out_file, stubname, stubdecl);
-#endif
-
- if (!FUNCTION_NAME_ALREADY_DECLARED)
- {
- fputs ("\t.end\t", asm_out_file);
- assemble_name (asm_out_file, stubname);
- fputs ("\n", asm_out_file);
- }
-
- fprintf (asm_out_file, "\t.set\tmips16\n");
-
- /* Record this stub. */
- l = (struct mips16_stub *) xmalloc (sizeof *l);
- l->name = xstrdup (fnname);
- l->fpret = fpret;
- 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 (fpret && ! l->fpret)
- error ("cannot handle inconsistent calls to %qs", fnname);
-
- /* 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 (l->fpret)
- {
- rtx insn;
-
- if (retval == NULL_RTX)
- insn = gen_call_internal (fn, arg_size);
- else
- insn = gen_call_value_internal (retval, fn, arg_size);
- insn = emit_call_insn (insn);
-
- 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));
-
- /* Return 1 to tell the caller that we've generated the call
- insn. */
- return 1;
- }
-
- /* Return 0 to let the caller generate the call insn. */
- return 0;
-}
-
-/* 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;
-};
-
-/* 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
-add_constant (struct mips16_constant_pool *pool,
- rtx value, enum machine_mode mode)
-{
- struct mips16_constant **p, *c;
- bool first_of_size_p;
-
- /* 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)
- {
- 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 pc-relative lw, addiu and daddiu instructions, the base PC value
- is the address of the instruction with the lowest two bits clear.
- The base PC value for ld has the lowest three bits clear. Assume
- the worst case here. */
- 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 = (struct mips16_constant *) xmalloc (sizeof *c);
- 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. */
-
-static rtx
-dump_constants_1 (enum machine_mode mode, rtx value, rtx insn)
-{
- switch (GET_MODE_CLASS (mode))
- {
- case MODE_INT:
- {
- rtx size = GEN_INT (GET_MODE_SIZE (mode));
- return emit_insn_after (gen_consttable_int (value, size), insn);
- }
-
- case MODE_FLOAT:
- return emit_insn_after (gen_consttable_float (value), insn);
-
- case MODE_VECTOR_FLOAT:
- case MODE_VECTOR_INT:
- {
- int i;
- for (i = 0; i < CONST_VECTOR_NUNITS (value); i++)
- insn = dump_constants_1 (GET_MODE_INNER (mode),
- CONST_VECTOR_ELT (value, i), insn);
- return insn;
- }
-
- default:
- gcc_unreachable ();
- }
-}
-
-
-/* Dump out the constants in CONSTANTS after INSN. */
-
-static void
-dump_constants (struct mips16_constant *constants, rtx insn)
-{
- struct mips16_constant *c, *next;
- int align;
-
- align = 0;
- for (c = constants; c != NULL; c = next)
- {
- /* If necessary, increase the alignment of PC. */
- if (align < GET_MODE_SIZE (c->mode))
- {
- int align_log = floor_log2 (GET_MODE_SIZE (c->mode));
- insn = emit_insn_after (gen_align (GEN_INT (align_log)), insn);
- }
- align = GET_MODE_SIZE (c->mode);
-
- insn = emit_label_after (c->label, insn);
- insn = dump_constants_1 (c->mode, c->value, insn);
-
- next = c->next;
- free (c);
- }
-
- emit_barrier_after (insn);
-}
-
-/* Return the length of instruction INSN. */
-
-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);
-}
-
-/* Rewrite *X so that constant pool references refer to the constant's
- label instead. DATA points to the constant pool structure. */
-
-static int
-mips16_rewrite_pool_refs (rtx *x, void *data)
-{
- struct mips16_constant_pool *pool = data;
- if (GET_CODE (*x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (*x))
- *x = gen_rtx_LABEL_REF (Pmode, add_constant (pool,
- get_pool_constant (*x),
- get_pool_mode (*x)));
- return 0;
-}
-
-/* Build MIPS16 constant pools. */
-
-static void
-mips16_lay_out_constants (void)
-{
- struct mips16_constant_pool pool;
- rtx insn, barrier;
-
- barrier = 0;
- memset (&pool, 0, sizeof (pool));
- for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
- {
- /* Rewrite constant pool references in INSN. */
- if (INSN_P (insn))
- for_each_rtx (&PATTERN (insn), mips16_rewrite_pool_refs, &pool);
-
- pool.insn_address += mips16_insn_length (insn);
-
- if (pool.first != NULL)
- {
- /* 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)
- {
- 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;
- }
-
- /* 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)
- {
- dump_constants (pool.first, barrier);
- pool.first = NULL;
- barrier = 0;
- }
- else if (BARRIER_P (insn))
- barrier = insn;
- }
- }
- dump_constants (pool.first, get_last_insn ());
-}
-
-/* A temporary variable used by for_each_rtx callbacks, etc. */
-static rtx mips_sim_insn;
-
-/* 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;
-
- /* The current simulation time. */
- unsigned int time;
-
- /* How many more instructions can be issued in the current cycle. */
- unsigned int insns_left;
-
- /* 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];
-
- /* The pipeline's current DFA state. */
- state_t dfa_state;
-};
-
-/* Reset STATE to the initial simulation state. */
-
-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);
-}
-
-/* Initialize STATE before its first use. DFA_STATE points to an
- allocated but uninitialized DFA state. */
-
-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);
-}
-
-/* Advance STATE by one clock cycle. */
-
-static void
-mips_sim_next_cycle (struct mips_sim *state)
-{
- state->time++;
- state->insns_left = state->issue_rate;
- state_transition (state->dfa_state, 0);
-}
-
-/* Advance simulation state STATE until instruction INSN can read
- register REG. */
-
-static void
-mips_sim_wait_reg (struct mips_sim *state, rtx insn, rtx reg)
-{
- unsigned int i;
-
- for (i = 0; i < HARD_REGNO_NREGS (REGNO (reg), GET_MODE (reg)); i++)
- if (state->last_set[REGNO (reg) + i].insn != 0)
- {
- unsigned int t;
-
- t = state->last_set[REGNO (reg) + i].time;
- t += insn_latency (state->last_set[REGNO (reg) + i].insn, insn);
- while (state->time < t)
- mips_sim_next_cycle (state);
- }
-}
-
-/* A for_each_rtx callback. If *X is a register, advance simulation state
- DATA until mips_sim_insn can read the register's value. */
-
-static int
-mips_sim_wait_regs_2 (rtx *x, void *data)
-{
- if (REG_P (*x))
- mips_sim_wait_reg (data, mips_sim_insn, *x);
- return 0;
-}
-
-/* Call mips_sim_wait_regs_2 (R, DATA) for each register R mentioned in *X. */
-
-static void
-mips_sim_wait_regs_1 (rtx *x, void *data)
-{
- for_each_rtx (x, mips_sim_wait_regs_2, data);
-}
-
-/* Advance simulation state STATE until all of INSN's register
- dependencies are satisfied. */
-
-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);
-}
-
-/* Advance simulation state STATE until the units required by
- instruction INSN are available. */
-
-static void
-mips_sim_wait_units (struct mips_sim *state, rtx insn)
-{
- state_t tmp_state;
-
- 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);
-}
-
-/* Advance simulation state STATE until INSN is ready to issue. */
-
-static void
-mips_sim_wait_insn (struct mips_sim *state, rtx insn)
-{
- mips_sim_wait_regs (state, insn);
- mips_sim_wait_units (state, insn);
-}
-
-/* mips_sim_insn has just set X. Update the LAST_SET array
- in simulation state DATA. */
-
-static void
-mips_sim_record_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
-{
- struct mips_sim *state;
- unsigned int i;
-
- state = data;
- if (REG_P (x))
- for (i = 0; i < HARD_REGNO_NREGS (REGNO (x), GET_MODE (x)); i++)
- {
- state->last_set[REGNO (x) + i].insn = mips_sim_insn;
- state->last_set[REGNO (x) + i].time = state->time;
- }
-}
-
-/* Issue instruction INSN in scheduler state STATE. Assume that INSN
- can issue immediately (i.e., that mips_sim_wait_insn has already
- been called). */
-
-static void
-mips_sim_issue_insn (struct mips_sim *state, rtx insn)
-{
- state_transition (state->dfa_state, insn);
- state->insns_left--;
-
- mips_sim_insn = insn;
- note_stores (PATTERN (insn), mips_sim_record_set, state);
-}
-
-/* Simulate issuing a NOP in state STATE. */
-
-static void
-mips_sim_issue_nop (struct mips_sim *state)
-{
- 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. */
-
-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);
-
- switch (GET_CODE (SEQ_BEGIN (insn)))
- {
- case CODE_LABEL:
- case CALL_INSN:
- /* We can't predict the processor state after a call or label. */
- mips_sim_reset (state);
- break;
-
- 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;
-
- default:
- break;
- }
-}
-
-/* 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. */
-
-static void
-vr4130_avoid_branch_rt_conflict (rtx insn)
-{
- rtx first, second;
-
- 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)))
- {
- /* 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;
- }
- }
-}
-
-/* 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 instruction is not 8-byte aligned,
- insert a nop to make it aligned. */
-
-static void
-vr4130_align_insns (void)
-{
- struct mips_sim state;
- rtx insn, subinsn, last, last2, next;
- bool aligned_p;
-
- dfa_start ();
-
- /* 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)
- {
- unsigned int length;
-
- next = NEXT_INSN (insn);
-
- /* 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);
-
- 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;
- }
- else if (length & 4)
- aligned_p = !aligned_p;
- last2 = last;
- last = insn;
- }
-
- /* See whether INSN is an aligned label. */
- if (LABEL_P (insn) && label_to_alignment (insn) >= 3)
- aligned_p = true;
- }
- dfa_finish ();
-}
-
-/* Subroutine of mips_reorg. 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
-mips_avoid_hazard (rtx after, rtx insn, int *hilo_delay,
- rtx *delayed_reg, rtx lo_reg)
-{
- rtx pattern, set;
- int nops, ninsns;
-
- if (!INSN_P (insn))
- return;
-
- pattern = PATTERN (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;
-
- /* Ignore zero-length instructions (barriers and the like). */
- ninsns = get_attr_length (insn) / 4;
- if (ninsns == 0)
- return;
-
- /* 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;
-
- case HAZARD_HILO:
- *hilo_delay = 0;
- break;
-
- case HAZARD_DELAY:
- set = single_set (insn);
- gcc_assert (set != 0);
- *delayed_reg = SET_DEST (set);
- break;
- }
-}
-
-
-/* Go through the instruction stream and insert nops where necessary.
- See if the whole function can then be put into .set noreorder &
- .set nomacro. */
-
-static void
-mips_avoid_hazards (void)
-{
- rtx insn, last_insn, lo_reg, delayed_reg;
- int hilo_delay, i;
-
- /* 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;
-
- /* Profiled functions can't be all noreorder because the profiler
- support uses assembler macros. */
- if (current_function_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;
-
- last_insn = 0;
- hilo_delay = 2;
- delayed_reg = 0;
- lo_reg = gen_rtx_REG (SImode, LO_REGNUM);
-
- for (insn = get_insns (); insn != 0; insn = NEXT_INSN (insn))
- if (INSN_P (insn))
- {
- if (GET_CODE (PATTERN (insn)) == SEQUENCE)
- for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
- mips_avoid_hazard (last_insn, XVECEXP (PATTERN (insn), 0, i),
- &hilo_delay, &delayed_reg, lo_reg);
- else
- mips_avoid_hazard (last_insn, insn, &hilo_delay,
- &delayed_reg, lo_reg);
-
- last_insn = insn;
- }
-}
-
-
-/* Implement TARGET_MACHINE_DEPENDENT_REORG. */
-
-static void
-mips_reorg (void)
-{
- if (TARGET_MIPS16)
- mips16_lay_out_constants ();
- else if (TARGET_EXPLICIT_RELOCS)
- {
- if (mips_flag_delayed_branch)
- dbr_schedule (get_insns ());
- mips_avoid_hazards ();
- if (TUNE_MIPS4130 && TARGET_VR4130_ALIGN)
- vr4130_align_insns ();
- }
-}
-
-/* This function does three things:
-
- - Register the special divsi3 and modsi3 functions if -mfix-vr4120.
- - Register the mips16 hardware floating point stubs.
- - Register the gofast functions if selected using --enable-gofast. */
-
-#include "config/gofast.h"
-
-static void
-mips_init_libfuncs (void)
-{
- if (TARGET_FIX_VR4120)
- {
- set_optab_libfunc (sdiv_optab, SImode, "__vr4120_divsi3");
- set_optab_libfunc (smod_optab, SImode, "__vr4120_modsi3");
- }
-
- if (TARGET_MIPS16 && mips16_hard_float)
- {
- 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_conv_libfunc (sfix_optab, SImode, SFmode, "__mips16_fix_truncsfsi");
- set_conv_libfunc (sfloat_optab, SFmode, SImode, "__mips16_floatsisf");
-
- 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_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");
- }
- }
- else
- gofast_maybe_init_libfuncs ();
-}
-
-/* Return a number assessing the cost of moving a register in class
- FROM to class TO. The classes are expressed using the enumeration
- values such as `GENERAL_REGS'. A value of 2 is the default; other
- values are interpreted relative to that.
-
- It is not required that the cost always equal 2 when FROM is the
- same as TO; on some machines it is expensive to move between
- registers if they are not general registers.
-
- If reload sees an insn consisting of a single `set' between two
- hard registers, and if `REGISTER_MOVE_COST' applied to their
- classes returns a value of 2, reload does not check to ensure that
- the constraints of the insn are met. Setting a cost of other than
- 2 will allow reload to verify that the constraints are met. You
- should do this if the `movM' pattern's constraints do not allow
- such copying.
-
- ??? We make the cost of moving from HI/LO into general
- registers the same as for one of moving general registers to
- HI/LO for TARGET_MIPS16 in order to prevent allocating a
- pseudo to HI/LO. This might hurt optimizations though, it
- isn't clear if it is wise. And it might not work in all cases. We
- could solve the DImode LO reg problem by using a multiply, just
- like reload_{in,out}si. We could solve the SImode/HImode HI reg
- problem by using divide instructions. divu puts the remainder in
- the HI reg, so doing a divide by -1 will move the value in the HI
- reg for all values except -1. We could handle that case by using a
- signed divide, e.g. -1 / 2 (or maybe 1 / -2?). We'd have to emit
- a compare/branch to test the input value to see which instruction
- we need to use. This gets pretty messy, but it is feasible. */
-
-int
-mips_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
- enum reg_class to, enum reg_class from)
-{
- if (from == M16_REGS && GR_REG_CLASS_P (to))
- return 2;
- else if (from == M16_NA_REGS && GR_REG_CLASS_P (to))
- return 2;
- else if (GR_REG_CLASS_P (from))
- {
- if (to == M16_REGS)
- return 2;
- else if (to == M16_NA_REGS)
- return 2;
- else if (GR_REG_CLASS_P (to))
- {
- if (TARGET_MIPS16)
- return 4;
- else
- return 2;
- }
- else if (to == FP_REGS)
- return 4;
- else if (reg_class_subset_p (to, ACC_REGS))
- {
- if (TARGET_MIPS16)
- return 12;
- else
- return 6;
- }
- else if (COP_REG_CLASS_P (to))
- {
- return 5;
- }
- }
- else if (from == FP_REGS)
- {
- if (GR_REG_CLASS_P (to))
- return 4;
- else if (to == FP_REGS)
- return 2;
- else if (to == ST_REGS)
- return 8;
- }
- else if (reg_class_subset_p (from, ACC_REGS))
- {
- if (GR_REG_CLASS_P (to))
- {
- if (TARGET_MIPS16)
- return 12;
- else
- return 6;
- }
- }
- else if (from == ST_REGS && GR_REG_CLASS_P (to))
- return 4;
- else if (COP_REG_CLASS_P (from))
- {
- return 5;
- }
-
- /* Fall through.
- ??? What cases are these? Shouldn't we return 2 here? */
-
- return 12;
-}
-
-/* Return the length of INSN. LENGTH is the initial length computed by
- attributes in the machine-description file. */
-
-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;
-
- /* 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 HAZARD_DELAY:
- length += 4;
- break;
-
- case HAZARD_HILO:
- length += 8;
- break;
- }
-
- /* All MIPS16 instructions are a measly two bytes. */
- if (TARGET_MIPS16)
- length /= 2;
-
- return length;
-}
-
-
-/* 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_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
- {
- if (Pmode == DImode)
- return "%[dla\t%@,%0";
- else
- return "%[la\t%@,%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. */
-
-const char *
-mips_output_conditional_branch (rtx insn, rtx *operands,
- const char *branch_if_true,
- const char *branch_if_false)
-{
- unsigned int length;
- rtx taken, not_taken;
-
- 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;
- }
-
- /* 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];
-
- /* 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)
- {
- /* 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");
- }
-
- /* Output the unconditional branch to TAKEN. */
- if (length <= 16)
- output_asm_insn ("j\t%0%/", &taken);
- else
- {
- output_asm_insn (mips_output_load_label (), &taken);
- output_asm_insn ("jr\t%@%]%/", 0);
- }
-
- /* Now deal with its delay slot; see above. */
- if (final_sequence)
- {
- /* 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))
- {
- 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");
- }
-
- /* Output NOT_TAKEN. */
- (*targetm.asm_out.internal_label) (asm_out_file, "L",
- CODE_LABEL_NUMBER (not_taken));
- return "";
-}
-
-/* Return the assembly code for INSN, which branches to OPERANDS[1]
- if some ordered 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)
-{
- 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]))
- {
- /* 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;
-
- 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]);
-}
-
-/* Used to output 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. */
-
-const char *
-mips_output_division (const char *division, rtx *operands)
-{
- const char *s;
-
- s = division;
- if (TARGET_FIX_R4000 || TARGET_FIX_R4400)
- {
- output_asm_insn (s, operands);
- s = "nop";
- }
- if (TARGET_CHECK_ZERO_DIV)
- {
- if (TARGET_MIPS16)
- {
- output_asm_insn (s, operands);
- s = "bnez\t%2,1f\n\tbreak\t7\n1:";
- }
- else if (GENERATE_DIVIDE_TRAPS)
- {
- output_asm_insn (s, operands);
- s = "teq\t%2,%.,7";
- }
- else
- {
- output_asm_insn ("%(bne\t%2,%.,1f", operands);
- output_asm_insn (s, operands);
- s = "break\t7%)\n1:";
- }
- }
- return s;
-}
-
-/* Return true if GIVEN is the same as CANONICAL, or if it is CANONICAL
- with a final "000" replaced by "k". Ignore case.
-
- Note: this function is shared between GCC and GAS. */
-
-static bool
-mips_strict_matching_cpu_name_p (const char *canonical, const char *given)
-{
- while (*given != 0 && TOLOWER (*given) == TOLOWER (*canonical))
- given++, canonical++;
-
- return ((*given == 0 && *canonical == 0)
- || (strcmp (canonical, "000") == 0 && strcasecmp (given, "k") == 0));
-}
-
-
-/* Return true if GIVEN matches CANONICAL, where GIVEN is a user-supplied
- CPU name. We've traditionally allowed a lot of variation here.
-
- Note: this function is shared between GCC and GAS. */
-
-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;
-
- /* 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;
-
- /* 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;
-
- return mips_strict_matching_cpu_name_p (canonical, given);
-}
-
-
-/* Return the mips_cpu_info entry for the processor or ISA given
- by CPU_STRING. Return null if the string isn't recognized.
-
- A similar function exists in GAS. */
-
-static const struct mips_cpu_info *
-mips_parse_cpu (const char *cpu_string)
-{
- const struct mips_cpu_info *p;
- 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))
- {
- warning (0, "the cpu name must be lower case");
- break;
- }
-
- /* '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. Look first at the -mgp options, if given, otherwise base
- the choice on MASK_64BIT in TARGET_DEFAULT. */
- 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));
-
- /* 'default' has traditionally been a no-op. Probably not very useful. */
- if (strcasecmp (cpu_string, "default") == 0)
- return 0;
-
- for (p = mips_cpu_info_table; p->name != 0; p++)
- if (mips_matching_cpu_name_p (p->name, cpu_string))
- return p;
-
- return 0;
-}
-
-
-/* Return the processor associated with the given ISA level, or null
- if the ISA isn't valid. */
-
-static const struct mips_cpu_info *
-mips_cpu_info_from_isa (int isa)
-{
- const struct mips_cpu_info *p;
-
- for (p = mips_cpu_info_table; p->name != 0; p++)
- if (p->isa == isa)
- return p;
-
- return 0;
-}
-
-/* Implement HARD_REGNO_NREGS. The size of FP registers is controlled
- by UNITS_PER_FPREG. The size of FP status registers is always 4, because
- they only hold condition code modes, and CCmode is always considered to
- be 4 bytes wide. All other registers are word sized. */
-
-unsigned int
-mips_hard_regno_nregs (int regno, enum machine_mode mode)
-{
- if (ST_REG_P (regno))
- return ((GET_MODE_SIZE (mode) + 3) / 4);
- else if (! FP_REG_P (regno))
- return ((GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD);
- else
- return ((GET_MODE_SIZE (mode) + UNITS_PER_FPREG - 1) / UNITS_PER_FPREG);
-}
-
-/* Implement TARGET_RETURN_IN_MEMORY. Under the old (i.e., 32 and O64 ABIs)
- all BLKmode objects are returned in memory. Under the new (N32 and
- 64-bit MIPS 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 (tree type, tree fndecl ATTRIBUTE_UNUSED)
-{
- if (TARGET_OLDABI)
- return (TYPE_MODE (type) == BLKmode);
- else
- return ((int_size_in_bytes (type) > (2 * UNITS_PER_WORD))
- || (int_size_in_bytes (type) == -1));
-}
-
-static bool
-mips_strict_argument_naming (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED)
-{
- return !TARGET_OLDABI;
-}
-
-/* Return true if INSN is a multiply-add or multiply-subtract
- instruction and PREV assigns to the accumulator operand. */
-
-bool
-mips_linked_madd_p (rtx prev, rtx insn)
-{
- rtx x;
-
- x = single_set (insn);
- if (x == 0)
- return false;
-
- x = SET_SRC (x);
-
- if (GET_CODE (x) == PLUS
- && GET_CODE (XEXP (x, 0)) == MULT
- && reg_set_p (XEXP (x, 1), prev))
- return true;
-
- if (GET_CODE (x) == MINUS
- && GET_CODE (XEXP (x, 1)) == MULT
- && reg_set_p (XEXP (x, 0), prev))
- return true;
-
- return false;
-}
-
-/* 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
-mips_macc_chains_record (rtx insn)
-{
- if (get_attr_may_clobber_hilo (insn))
- mips_macc_chains_last_hilo = insn;
-}
-
-/* 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. */
-
-static void
-mips_macc_chains_reorder (rtx *ready, int nready)
-{
- int i, j;
-
- 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;
- }
-}
-
-/* The last instruction to be scheduled. */
-
-static rtx vr4130_last_insn;
-
-/* 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. */
-
-static void
-vr4130_true_reg_dependence_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
-{
- rtx *insn_ptr = data;
- if (REG_P (x)
- && *insn_ptr != 0
- && reg_referenced_p (x, PATTERN (*insn_ptr)))
- *insn_ptr = 0;
-}
-
-/* Return true if there is true register dependence between vr4130_last_insn
- and INSN. */
-
-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;
-}
-
-/* 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. */
-
-static bool
-vr4130_swap_insns_p (rtx insn1, rtx insn2)
-{
- rtx dep;
-
- /* Check for the following case:
-
- 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 INSN1 is the last instruction blocking X, it would better to
- choose (INSN1, X) over (INSN2, INSN1). */
- for (dep = INSN_DEPEND (insn1); dep != 0; dep = XEXP (dep, 1))
- if (REG_NOTE_KIND (dep) == REG_DEP_ANTI
- && INSN_PRIORITY (XEXP (dep, 0)) > INSN_PRIORITY (insn2)
- && recog_memoized (XEXP (dep, 0)) >= 0
- && get_attr_vr4130_class (XEXP (dep, 0)) == VR4130_CLASS_ALU)
- return false;
-
- if (vr4130_last_insn != 0
- && recog_memoized (insn1) >= 0
- && recog_memoized (insn2) >= 0)
- {
- /* 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)
- {
- /* If only one of the instructions has a dependence on
- vr4130_last_insn, prefer to schedule the other one first. */
- bool dep1 = vr4130_true_reg_dependence_p (insn1);
- bool dep2 = vr4130_true_reg_dependence_p (insn2);
- if (dep1 != dep2)
- return dep1;
-
- /* 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;
- }
- }
- return false;
-}
-
-/* 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
-vr4130_reorder (rtx *ready, int nready)
-{
- if (vr4130_swap_insns_p (ready[nready - 1], ready[nready - 2]))
- mips_promote_ready (ready, nready - 2, nready - 1);
-}
-
-/* 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. */
-
-static void
-mips_promote_ready (rtx *ready, int lower, int higher)
-{
- rtx new_head;
- int i;
-
- new_head = ready[lower];
- for (i = lower; i < higher; i++)
- ready[i] = ready[i + 1];
- ready[i] = new_head;
-}
-
-/* Implement TARGET_SCHED_REORDER. */
-
-static int
-mips_sched_reorder (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
- rtx *ready, int *nreadyp, int cycle)
-{
- if (!reload_completed && TUNE_MACC_CHAINS)
- {
- if (cycle == 0)
- mips_macc_chains_last_hilo = 0;
- if (*nreadyp > 0)
- mips_macc_chains_reorder (ready, *nreadyp);
- }
- if (reload_completed && TUNE_MIPS4130 && !TARGET_VR4130_ALIGN)
- {
- if (cycle == 0)
- vr4130_last_insn = 0;
- if (*nreadyp > 1)
- vr4130_reorder (ready, *nreadyp);
- }
- return mips_issue_rate ();
-}
-
-/* Implement TARGET_SCHED_VARIABLE_ISSUE. */
-
-static int
-mips_variable_issue (FILE *file ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
- rtx insn, int more)
-{
- switch (GET_CODE (PATTERN (insn)))
- {
- case USE:
- case CLOBBER:
- /* Don't count USEs and CLOBBERs against the issue rate. */
- break;
-
- default:
- more--;
- if (!reload_completed && TUNE_MACC_CHAINS)
- mips_macc_chains_record (insn);
- vr4130_last_insn = insn;
- break;
- }
- return more;
-}
-
-/* Implement TARGET_SCHED_ADJUST_COST. We assume that anti and output
- dependencies have no cost. */
-
-static int
-mips_adjust_cost (rtx insn ATTRIBUTE_UNUSED, rtx link,
- rtx dep ATTRIBUTE_UNUSED, int cost)
-{
- if (REG_NOTE_KIND (link) != 0)
- return 0;
- return cost;
-}
-
-/* Return the number of instructions that can be issued per cycle. */
-
-static int
-mips_issue_rate (void)
-{
- switch (mips_tune)
- {
- case PROCESSOR_R4130:
- case PROCESSOR_R5400:
- case PROCESSOR_R5500:
- case PROCESSOR_R7000:
- case PROCESSOR_R9000:
- case PROCESSOR_OCTEON:
- return 2;
-
- 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;
-
- default:
- return 1;
- }
-}
-
-/* Implements TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD. This should
- be as wide as the scheduling freedom in the DFA. */
-
-static int
-mips_multipass_dfa_lookahead (void)
-{
- /* Can schedule up to 4 of the 6 function units in any one cycle. */
- if (TUNE_SB1)
- return 4;
-
- return 0;
-}
-
-/* Implements a store data bypass check. We need this because the cprestore
- pattern is type store, but defined using an UNSPEC. This UNSPEC causes the
- default routine to abort. We just return false for that case. */
-/* ??? Should try to give a better result here than assuming false. */
-
-int
-mips_store_data_bypass_p (rtx out_insn, rtx in_insn)
-{
- if (GET_CODE (PATTERN (in_insn)) == UNSPEC_VOLATILE)
- return false;
-
- return ! store_data_bypass_p (out_insn, in_insn);
-}
-
-/* Given that we have an rtx of the form (prefetch ... WRITE LOCALITY),
- return the first operand of the associated "pref" or "prefx" insn. */
-
-rtx
-mips_prefetch_cookie (rtx write, rtx locality)
-{
- /* store_streamed / load_streamed. */
- if (INTVAL (locality) <= 0)
- return GEN_INT (INTVAL (write) + 4);
-
- /* store / load. */
- if (INTVAL (locality) <= 2)
- return write;
-
- /* store_retained / load_retained. */
- return GEN_INT (INTVAL (write) + 6);
-}
-
-/* MIPS builtin function support. */
-
-struct 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 builtin function. */
- const char *name;
-
- /* Specifies how the function should be expanded. */
- enum mips_builtin_type builtin_type;
-
- /* The function's prototype. */
- enum mips_function_type function_type;
-
- /* The target flags required for this function. */
- int target_flags;
-};
-
-/* Define a MIPS_BUILTIN_DIRECT function for instruction CODE_FOR_mips_<INSN>.
- FUNCTION_TYPE and TARGET_FLAGS are builtin_description fields. */
-#define DIRECT_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS) \
- { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN, \
- MIPS_BUILTIN_DIRECT, FUNCTION_TYPE, TARGET_FLAGS }
-
-/* Define __builtin_mips_<INSN>_<COND>_{s,d}, both of which require
- TARGET_FLAGS. */
-#define CMP_SCALAR_BUILTINS(INSN, COND, TARGET_FLAGS) \
- { CODE_FOR_mips_ ## INSN ## _cond_s, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_" #INSN "_" #COND "_s", \
- MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_SF_SF, TARGET_FLAGS }, \
- { CODE_FOR_mips_ ## INSN ## _cond_d, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_" #INSN "_" #COND "_d", \
- MIPS_BUILTIN_CMP_SINGLE, MIPS_INT_FTYPE_DF_DF, TARGET_FLAGS }
-
-/* Define __builtin_mips_{any,all,upper,lower}_<INSN>_<COND>_ps.
- The lower and upper forms require TARGET_FLAGS while the any and all
- forms require MASK_MIPS3D. */
-#define CMP_PS_BUILTINS(INSN, COND, TARGET_FLAGS) \
- { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_any_" #INSN "_" #COND "_ps", \
- MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D }, \
- { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_all_" #INSN "_" #COND "_ps", \
- MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF, MASK_MIPS3D }, \
- { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_lower_" #INSN "_" #COND "_ps", \
- MIPS_BUILTIN_CMP_LOWER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS }, \
- { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_upper_" #INSN "_" #COND "_ps", \
- MIPS_BUILTIN_CMP_UPPER, MIPS_INT_FTYPE_V2SF_V2SF, TARGET_FLAGS }
-
-/* Define __builtin_mips_{any,all}_<INSN>_<COND>_4s. The functions
- require MASK_MIPS3D. */
-#define CMP_4S_BUILTINS(INSN, COND) \
- { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_any_" #INSN "_" #COND "_4s", \
- MIPS_BUILTIN_CMP_ANY, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, \
- MASK_MIPS3D }, \
- { CODE_FOR_mips_ ## INSN ## _cond_4s, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_all_" #INSN "_" #COND "_4s", \
- MIPS_BUILTIN_CMP_ALL, MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF, \
- MASK_MIPS3D }
-
-/* Define __builtin_mips_mov{t,f}_<INSN>_<COND>_ps. The comparison
- instruction requires TARGET_FLAGS. */
-#define MOVTF_BUILTINS(INSN, COND, TARGET_FLAGS) \
- { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_movt_" #INSN "_" #COND "_ps", \
- MIPS_BUILTIN_MOVT, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
- TARGET_FLAGS }, \
- { CODE_FOR_mips_ ## INSN ## _cond_ps, MIPS_FP_COND_ ## COND, \
- "__builtin_mips_movf_" #INSN "_" #COND "_ps", \
- MIPS_BUILTIN_MOVF, MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF, \
- TARGET_FLAGS }
-
-/* Define all the builtins related to c.cond.fmt condition COND. */
-#define CMP_BUILTINS(COND) \
- MOVTF_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT), \
- MOVTF_BUILTINS (cabs, COND, MASK_MIPS3D), \
- CMP_SCALAR_BUILTINS (cabs, COND, MASK_MIPS3D), \
- CMP_PS_BUILTINS (c, COND, MASK_PAIRED_SINGLE_FLOAT), \
- CMP_PS_BUILTINS (cabs, COND, MASK_MIPS3D), \
- CMP_4S_BUILTINS (c, COND), \
- CMP_4S_BUILTINS (cabs, COND)
-
-static const struct builtin_description mips_bdesc[] =
-{
- DIRECT_BUILTIN (pll_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (pul_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (plu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (puu_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (cvt_ps_s, MIPS_V2SF_FTYPE_SF_SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (cvt_s_pl, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (cvt_s_pu, MIPS_SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (abs_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT),
-
- DIRECT_BUILTIN (alnv_ps, MIPS_V2SF_FTYPE_V2SF_V2SF_INT,
- MASK_PAIRED_SINGLE_FLOAT),
- DIRECT_BUILTIN (addr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
- DIRECT_BUILTIN (mulr_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
- DIRECT_BUILTIN (cvt_pw_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
- DIRECT_BUILTIN (cvt_ps_pw, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
-
- DIRECT_BUILTIN (recip1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D),
- DIRECT_BUILTIN (recip1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D),
- DIRECT_BUILTIN (recip1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
- DIRECT_BUILTIN (recip2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D),
- DIRECT_BUILTIN (recip2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D),
- DIRECT_BUILTIN (recip2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
-
- DIRECT_BUILTIN (rsqrt1_s, MIPS_SF_FTYPE_SF, MASK_MIPS3D),
- DIRECT_BUILTIN (rsqrt1_d, MIPS_DF_FTYPE_DF, MASK_MIPS3D),
- DIRECT_BUILTIN (rsqrt1_ps, MIPS_V2SF_FTYPE_V2SF, MASK_MIPS3D),
- DIRECT_BUILTIN (rsqrt2_s, MIPS_SF_FTYPE_SF_SF, MASK_MIPS3D),
- DIRECT_BUILTIN (rsqrt2_d, MIPS_DF_FTYPE_DF_DF, MASK_MIPS3D),
- DIRECT_BUILTIN (rsqrt2_ps, MIPS_V2SF_FTYPE_V2SF_V2SF, MASK_MIPS3D),
-
- MIPS_FP_CONDITIONS (CMP_BUILTINS)
-};
-
-/* Builtin functions for the SB-1 processor. */
-
-#define CODE_FOR_mips_sqrt_ps CODE_FOR_sqrtv2sf2
-
-static const struct builtin_description sb1_bdesc[] =
-{
- DIRECT_BUILTIN (sqrt_ps, MIPS_V2SF_FTYPE_V2SF, MASK_PAIRED_SINGLE_FLOAT)
-};
-
-/* Builtin functions for DSP ASE. */
-
-#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 a MIPS_BUILTIN_DIRECT_NO_TARGET function for instruction
- CODE_FOR_mips_<INSN>. FUNCTION_TYPE and TARGET_FLAGS are
- builtin_description fields. */
-#define DIRECT_NO_TARGET_BUILTIN(INSN, FUNCTION_TYPE, TARGET_FLAGS) \
- { CODE_FOR_mips_ ## INSN, 0, "__builtin_mips_" #INSN, \
- MIPS_BUILTIN_DIRECT_NO_TARGET, FUNCTION_TYPE, TARGET_FLAGS }
-
-/* Define __builtin_mips_bposge<VALUE>. <VALUE> is 32 for the MIPS32 DSP
- branch instruction. TARGET_FLAGS is a builtin_description field. */
-#define BPOSGE_BUILTIN(VALUE, TARGET_FLAGS) \
- { CODE_FOR_mips_bposge, 0, "__builtin_mips_bposge" #VALUE, \
- MIPS_BUILTIN_BPOSGE ## VALUE, MIPS_SI_FTYPE_VOID, TARGET_FLAGS }
-
-static const struct builtin_description dsp_bdesc[] =
-{
- DIRECT_BUILTIN (addq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (addq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (addq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (addu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (addu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (subq_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (subq_s_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (subq_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (subu_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (subu_s_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (addsc, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (addwc, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (modsub, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (raddu_w_qb, MIPS_SI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (absq_s_ph, MIPS_V2HI_FTYPE_V2HI, MASK_DSP),
- DIRECT_BUILTIN (absq_s_w, MIPS_SI_FTYPE_SI, MASK_DSP),
- DIRECT_BUILTIN (precrq_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (precrq_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (precrq_rs_ph_w, MIPS_V2HI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (precrqu_s_qb_ph, MIPS_V4QI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (preceq_w_phl, MIPS_SI_FTYPE_V2HI, MASK_DSP),
- DIRECT_BUILTIN (preceq_w_phr, MIPS_SI_FTYPE_V2HI, MASK_DSP),
- DIRECT_BUILTIN (precequ_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (precequ_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (precequ_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (precequ_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (preceu_ph_qbl, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (preceu_ph_qbr, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (preceu_ph_qbla, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (preceu_ph_qbra, MIPS_V2HI_FTYPE_V4QI, MASK_DSP),
- DIRECT_BUILTIN (shll_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP),
- DIRECT_BUILTIN (shll_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
- DIRECT_BUILTIN (shll_s_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
- DIRECT_BUILTIN (shll_s_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (shrl_qb, MIPS_V4QI_FTYPE_V4QI_SI, MASK_DSP),
- DIRECT_BUILTIN (shra_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
- DIRECT_BUILTIN (shra_r_ph, MIPS_V2HI_FTYPE_V2HI_SI, MASK_DSP),
- DIRECT_BUILTIN (shra_r_w, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (muleu_s_ph_qbl, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (muleu_s_ph_qbr, MIPS_V2HI_FTYPE_V4QI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (mulq_rs_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (muleq_s_w_phl, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (muleq_s_w_phr, MIPS_SI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (dpau_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (dpau_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (dpsu_h_qbl, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (dpsu_h_qbr, MIPS_DI_FTYPE_DI_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (dpaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (dpsq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (mulsaq_s_w_ph, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (dpaq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (dpsq_sa_l_w, MIPS_DI_FTYPE_DI_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (maq_s_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (maq_s_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (maq_sa_w_phl, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (maq_sa_w_phr, MIPS_DI_FTYPE_DI_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (bitrev, MIPS_SI_FTYPE_SI, MASK_DSP),
- DIRECT_BUILTIN (insv, MIPS_SI_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (repl_qb, MIPS_V4QI_FTYPE_SI, MASK_DSP),
- DIRECT_BUILTIN (repl_ph, MIPS_V2HI_FTYPE_SI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (cmpu_eq_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (cmpu_lt_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (cmpu_le_qb, MIPS_VOID_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (cmpgu_eq_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (cmpgu_lt_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (cmpgu_le_qb, MIPS_SI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (cmp_eq_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (cmp_lt_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (cmp_le_ph, MIPS_VOID_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (pick_qb, MIPS_V4QI_FTYPE_V4QI_V4QI, MASK_DSP),
- DIRECT_BUILTIN (pick_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (packrl_ph, MIPS_V2HI_FTYPE_V2HI_V2HI, MASK_DSP),
- DIRECT_BUILTIN (extr_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (extr_r_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (extr_rs_w, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (extr_s_h, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (extp, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (extpdp, MIPS_SI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (shilo, MIPS_DI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_BUILTIN (mthlip, MIPS_DI_FTYPE_DI_SI, MASK_DSP),
- DIRECT_NO_TARGET_BUILTIN (wrdsp, MIPS_VOID_FTYPE_SI_SI, MASK_DSP),
- DIRECT_BUILTIN (rddsp, MIPS_SI_FTYPE_SI, MASK_DSP),
- DIRECT_BUILTIN (lbux, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
- DIRECT_BUILTIN (lhx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
- DIRECT_BUILTIN (lwx, MIPS_SI_FTYPE_PTR_SI, MASK_DSP),
- BPOSGE_BUILTIN (32, MASK_DSP)
-};
-
-/* This helps provide a mapping from builtin function codes to bdesc
- arrays. */
-
-struct bdesc_map
-{
- /* The builtin function table that this entry describes. */
- const struct builtin_description *bdesc;
-
- /* The number of entries in the builtin function table. */
- unsigned int size;
-
- /* The target processor that supports these builtin functions.
- PROCESSOR_MAX means we enable them for all processors. */
- enum processor_type proc;
-};
-
-static const struct bdesc_map bdesc_arrays[] =
-{
- { mips_bdesc, ARRAY_SIZE (mips_bdesc), PROCESSOR_MAX },
- { sb1_bdesc, ARRAY_SIZE (sb1_bdesc), PROCESSOR_SB1 },
- { dsp_bdesc, ARRAY_SIZE (dsp_bdesc), PROCESSOR_MAX }
-};
-
-/* Take the head of argument list *ARGLIST and convert it into a form
- suitable for input operand OP of instruction ICODE. Return the value
- and point *ARGLIST at the next element of the list. */
-
-static rtx
-mips_prepare_builtin_arg (enum insn_code icode,
- unsigned int op, tree *arglist)
-{
- rtx value;
- enum machine_mode mode;
-
- value = expand_normal (TREE_VALUE (*arglist));
- mode = insn_data[icode].operand[op].mode;
- if (!insn_data[icode].operand[op].predicate (value, mode))
- {
- value = copy_to_mode_reg (mode, value);
- /* Check the predicate again. */
- if (!insn_data[icode].operand[op].predicate (value, mode))
- {
- error ("invalid argument to builtin function");
- return const0_rtx;
- }
- }
-
- *arglist = TREE_CHAIN (*arglist);
- return value;
-}
-
-/* Return an rtx suitable for output operand OP of instruction ICODE.
- If TARGET is non-null, try to use it where possible. */
-
-static rtx
-mips_prepare_builtin_target (enum insn_code icode, unsigned int op, rtx target)
-{
- enum machine_mode mode;
-
- 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;
-}
-
-/* Expand builtin functions. This is called from TARGET_EXPAND_BUILTIN. */
-
-rtx
-mips_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- int ignore ATTRIBUTE_UNUSED)
-{
- enum insn_code icode;
- enum mips_builtin_type type;
- tree fndecl, arglist;
- unsigned int fcode;
- const struct builtin_description *bdesc;
- const struct bdesc_map *m;
-
- fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
- arglist = TREE_OPERAND (exp, 1);
- fcode = DECL_FUNCTION_CODE (fndecl);
-
- bdesc = NULL;
- for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
- {
- if (fcode < m->size)
- {
- bdesc = m->bdesc;
- icode = bdesc[fcode].icode;
- type = bdesc[fcode].builtin_type;
- break;
- }
- fcode -= m->size;
- }
- if (bdesc == NULL)
- return 0;
-
- switch (type)
- {
- case MIPS_BUILTIN_DIRECT:
- return mips_expand_builtin_direct (icode, target, arglist, true);
-
- case MIPS_BUILTIN_DIRECT_NO_TARGET:
- return mips_expand_builtin_direct (icode, target, arglist, false);
-
- case MIPS_BUILTIN_MOVT:
- case MIPS_BUILTIN_MOVF:
- return mips_expand_builtin_movtf (type, icode, bdesc[fcode].cond,
- target, arglist);
-
- 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 (type, icode, bdesc[fcode].cond,
- target, arglist);
-
- case MIPS_BUILTIN_BPOSGE32:
- return mips_expand_builtin_bposge (type, target);
-
- default:
- return 0;
- }
-}
-
-/* Init builtin functions. This is called from TARGET_INIT_BUILTIN. */
-
-void
-mips_init_builtins (void)
-{
- const struct builtin_description *d;
- const struct bdesc_map *m;
- tree types[(int) MIPS_MAX_FTYPE_MAX];
- tree V2SF_type_node;
- tree V2HI_type_node;
- tree V4QI_type_node;
- unsigned int offset;
-
- /* We have only builtins for -mpaired-single, -mips3d and -mdsp. */
- if (!TARGET_PAIRED_SINGLE_FLOAT && !TARGET_DSP)
- return;
-
- if (TARGET_PAIRED_SINGLE_FLOAT)
- {
- V2SF_type_node = build_vector_type_for_mode (float_type_node, V2SFmode);
-
- types[MIPS_V2SF_FTYPE_V2SF]
- = build_function_type_list (V2SF_type_node, V2SF_type_node, NULL_TREE);
-
- types[MIPS_V2SF_FTYPE_V2SF_V2SF]
- = build_function_type_list (V2SF_type_node,
- V2SF_type_node, V2SF_type_node, NULL_TREE);
-
- types[MIPS_V2SF_FTYPE_V2SF_V2SF_INT]
- = build_function_type_list (V2SF_type_node,
- V2SF_type_node, V2SF_type_node,
- integer_type_node, NULL_TREE);
-
- types[MIPS_V2SF_FTYPE_V2SF_V2SF_V2SF_V2SF]
- = build_function_type_list (V2SF_type_node,
- V2SF_type_node, V2SF_type_node,
- V2SF_type_node, V2SF_type_node, NULL_TREE);
-
- types[MIPS_V2SF_FTYPE_SF_SF]
- = build_function_type_list (V2SF_type_node,
- float_type_node, float_type_node, NULL_TREE);
-
- types[MIPS_INT_FTYPE_V2SF_V2SF]
- = build_function_type_list (integer_type_node,
- V2SF_type_node, V2SF_type_node, NULL_TREE);
-
- types[MIPS_INT_FTYPE_V2SF_V2SF_V2SF_V2SF]
- = build_function_type_list (integer_type_node,
- V2SF_type_node, V2SF_type_node,
- V2SF_type_node, V2SF_type_node, NULL_TREE);
-
- types[MIPS_INT_FTYPE_SF_SF]
- = build_function_type_list (integer_type_node,
- float_type_node, float_type_node, NULL_TREE);
-
- types[MIPS_INT_FTYPE_DF_DF]
- = build_function_type_list (integer_type_node,
- double_type_node, double_type_node, NULL_TREE);
-
- types[MIPS_SF_FTYPE_V2SF]
- = build_function_type_list (float_type_node, V2SF_type_node, NULL_TREE);
-
- types[MIPS_SF_FTYPE_SF]
- = build_function_type_list (float_type_node,
- float_type_node, NULL_TREE);
-
- types[MIPS_SF_FTYPE_SF_SF]
- = build_function_type_list (float_type_node,
- float_type_node, float_type_node, NULL_TREE);
-
- types[MIPS_DF_FTYPE_DF]
- = build_function_type_list (double_type_node,
- double_type_node, NULL_TREE);
-
- types[MIPS_DF_FTYPE_DF_DF]
- = build_function_type_list (double_type_node,
- double_type_node, double_type_node, NULL_TREE);
- }
-
- if (TARGET_DSP)
- {
- V2HI_type_node = build_vector_type_for_mode (intHI_type_node, V2HImode);
- V4QI_type_node = build_vector_type_for_mode (intQI_type_node, V4QImode);
-
- types[MIPS_V2HI_FTYPE_V2HI_V2HI]
- = build_function_type_list (V2HI_type_node,
- V2HI_type_node, V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_SI_SI]
- = build_function_type_list (intSI_type_node,
- intSI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_V4QI_FTYPE_V4QI_V4QI]
- = build_function_type_list (V4QI_type_node,
- V4QI_type_node, V4QI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_V4QI]
- = build_function_type_list (intSI_type_node,
- V4QI_type_node,
- NULL_TREE);
-
- types[MIPS_V2HI_FTYPE_V2HI]
- = build_function_type_list (V2HI_type_node,
- V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_SI]
- = build_function_type_list (intSI_type_node,
- intSI_type_node,
- NULL_TREE);
-
- types[MIPS_V4QI_FTYPE_V2HI_V2HI]
- = build_function_type_list (V4QI_type_node,
- V2HI_type_node, V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_V2HI_FTYPE_SI_SI]
- = build_function_type_list (V2HI_type_node,
- intSI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_V2HI]
- = build_function_type_list (intSI_type_node,
- V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_V2HI_FTYPE_V4QI]
- = build_function_type_list (V2HI_type_node,
- V4QI_type_node,
- NULL_TREE);
-
- types[MIPS_V4QI_FTYPE_V4QI_SI]
- = build_function_type_list (V4QI_type_node,
- V4QI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_V2HI_FTYPE_V2HI_SI]
- = build_function_type_list (V2HI_type_node,
- V2HI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_V2HI_FTYPE_V4QI_V2HI]
- = build_function_type_list (V2HI_type_node,
- V4QI_type_node, V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_V2HI_V2HI]
- = build_function_type_list (intSI_type_node,
- V2HI_type_node, V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_DI_FTYPE_DI_V4QI_V4QI]
- = build_function_type_list (intDI_type_node,
- intDI_type_node, V4QI_type_node, V4QI_type_node,
- NULL_TREE);
-
- types[MIPS_DI_FTYPE_DI_V2HI_V2HI]
- = build_function_type_list (intDI_type_node,
- intDI_type_node, V2HI_type_node, V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_DI_FTYPE_DI_SI_SI]
- = build_function_type_list (intDI_type_node,
- intDI_type_node, intSI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_V4QI_FTYPE_SI]
- = build_function_type_list (V4QI_type_node,
- intSI_type_node,
- NULL_TREE);
-
- types[MIPS_V2HI_FTYPE_SI]
- = build_function_type_list (V2HI_type_node,
- intSI_type_node,
- NULL_TREE);
-
- types[MIPS_VOID_FTYPE_V4QI_V4QI]
- = build_function_type_list (void_type_node,
- V4QI_type_node, V4QI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_V4QI_V4QI]
- = build_function_type_list (intSI_type_node,
- V4QI_type_node, V4QI_type_node,
- NULL_TREE);
-
- types[MIPS_VOID_FTYPE_V2HI_V2HI]
- = build_function_type_list (void_type_node,
- V2HI_type_node, V2HI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_DI_SI]
- = build_function_type_list (intSI_type_node,
- intDI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_DI_FTYPE_DI_SI]
- = build_function_type_list (intDI_type_node,
- intDI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_VOID_FTYPE_SI_SI]
- = build_function_type_list (void_type_node,
- intSI_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_PTR_SI]
- = build_function_type_list (intSI_type_node,
- ptr_type_node, intSI_type_node,
- NULL_TREE);
-
- types[MIPS_SI_FTYPE_VOID]
- = build_function_type (intSI_type_node, void_list_node);
- }
-
- /* Iterate through all of the bdesc arrays, initializing all of the
- builtin functions. */
-
- offset = 0;
- for (m = bdesc_arrays; m < &bdesc_arrays[ARRAY_SIZE (bdesc_arrays)]; m++)
- {
- if (m->proc == PROCESSOR_MAX || (m->proc == mips_arch))
- for (d = m->bdesc; d < &m->bdesc[m->size]; d++)
- if ((d->target_flags & target_flags) == d->target_flags)
- lang_hooks.builtin_function (d->name, types[d->function_type],
- d - m->bdesc + offset,
- BUILT_IN_MD, NULL, NULL);
- offset += m->size;
- }
-}
-
-/* Expand a MIPS_BUILTIN_DIRECT function. ICODE is the code of the
- .md pattern and ARGLIST is the list of function arguments. TARGET,
- if nonnull, suggests a good place to put the result.
- HAS_TARGET indicates the function must return something. */
-
-static rtx
-mips_expand_builtin_direct (enum insn_code icode, rtx target, tree arglist,
- bool has_target)
-{
- rtx ops[MAX_RECOG_OPERANDS];
- int i = 0;
-
- if (has_target)
- {
- /* We save target to ops[0]. */
- ops[0] = mips_prepare_builtin_target (icode, 0, target);
- i = 1;
- }
-
- /* We need to test if arglist is not zero. Some instructions have extra
- clobber registers. */
- for (; i < insn_data[icode].n_operands && arglist != 0; i++)
- ops[i] = mips_prepare_builtin_arg (icode, i, &arglist);
-
- switch (i)
- {
- 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;
-}
-
-/* Expand a __builtin_mips_movt_*_ps() or __builtin_mips_movf_*_ps()
- function (TYPE says which). ARGLIST is the list of arguments to 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. */
-
-static rtx
-mips_expand_builtin_movtf (enum mips_builtin_type type,
- enum insn_code icode, enum mips_fp_condition cond,
- rtx target, tree arglist)
-{
- rtx cmp_result, op0, op1;
-
- cmp_result = mips_prepare_builtin_target (icode, 0, 0);
- op0 = mips_prepare_builtin_arg (icode, 1, &arglist);
- op1 = mips_prepare_builtin_arg (icode, 2, &arglist);
- emit_insn (GEN_FCN (icode) (cmp_result, op0, op1, GEN_INT (cond)));
-
- 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, &arglist);
- op0 = mips_prepare_builtin_arg (icode, 1, &arglist);
- }
- else
- {
- op0 = mips_prepare_builtin_arg (icode, 1, &arglist);
- op1 = mips_prepare_builtin_arg (icode, 2, &arglist);
- }
- emit_insn (gen_mips_cond_move_tf_ps (target, op0, op1, cmp_result));
- return target;
-}
-
-/* Move VALUE_IF_TRUE into TARGET if CONDITION is true; move VALUE_IF_FALSE
- into TARGET otherwise. Return TARGET. */
-
-static rtx
-mips_builtin_branch_and_move (rtx condition, rtx target,
- rtx value_if_true, rtx value_if_false)
-{
- rtx true_label, done_label;
-
- true_label = gen_label_rtx ();
- done_label = gen_label_rtx ();
-
- /* First assume that CONDITION is false. */
- emit_move_insn (target, value_if_false);
-
- /* 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 ();
-
- /* Fix TARGET if CONDITION is true. */
- emit_label (true_label);
- emit_move_insn (target, value_if_true);
-
- emit_label (done_label);
- return target;
-}
-
-/* Expand a comparison builtin of type BUILTIN_TYPE. ICODE is the code
- of the comparison instruction and COND is the condition it should test.
- ARGLIST is the list of function arguments and TARGET, if nonnull,
- suggests a good place to put the boolean result. */
-
-static rtx
-mips_expand_builtin_compare (enum mips_builtin_type builtin_type,
- enum insn_code icode, enum mips_fp_condition cond,
- rtx target, tree arglist)
-{
- rtx offset, condition, cmp_result, ops[MAX_RECOG_OPERANDS];
- int i;
-
- if (target == 0 || GET_MODE (target) != SImode)
- target = gen_reg_rtx (SImode);
-
- /* Prepare the operands to the comparison. */
- cmp_result = mips_prepare_builtin_target (icode, 0, 0);
- for (i = 1; i < insn_data[icode].n_operands - 1; i++)
- ops[i] = mips_prepare_builtin_arg (icode, i, &arglist);
-
- switch (insn_data[icode].n_operands)
- {
- case 4:
- emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2], GEN_INT (cond)));
- break;
-
- case 6:
- emit_insn (GEN_FCN (icode) (cmp_result, ops[1], ops[2],
- ops[3], ops[4], GEN_INT (cond)));
- break;
-
- 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);
-
- 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);
-
- default:
- condition = gen_rtx_NE (VOIDmode, cmp_result, const0_rtx);
- return mips_builtin_branch_and_move (condition, target,
- const1_rtx, const0_rtx);
- }
-}
-
-/* Expand a bposge builtin 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)
-{
- rtx condition, cmp_result;
- int cmp_value;
-
- if (target == 0 || GET_MODE (target) != SImode)
- target = gen_reg_rtx (SImode);
-
- cmp_result = gen_rtx_REG (CCDSPmode, CCDSP_PO_REGNUM);
-
- if (builtin_type == MIPS_BUILTIN_BPOSGE32)
- cmp_value = 32;
- else
- gcc_assert (0);
-
- condition = gen_rtx_GE (VOIDmode, cmp_result, GEN_INT (cmp_value));
- return mips_builtin_branch_and_move (condition, target,
- const1_rtx, const0_rtx);
-}
-
-/* Set SYMBOL_REF_FLAGS for the SYMBOL_REF inside RTL, which belongs to DECL.
- FIRST is true if this is the first time handling this decl. */
-
-static void
-mips_encode_section_info (tree decl, rtx rtl, int first)
-{
- default_encode_section_info (decl, rtl, first);
-
- if (TREE_CODE (decl) == FUNCTION_DECL
- && lookup_attribute ("long_call", TYPE_ATTRIBUTES (TREE_TYPE (decl))))
- {
- rtx symbol = XEXP (rtl, 0);
- SYMBOL_REF_FLAGS (symbol) |= SYMBOL_FLAG_LONG_CALL;
- }
-}
-
-/* Implement TARGET_EXTRA_LIVE_ON_ENTRY. PIC_FUNCTION_ADDR_REGNUM is live
- on entry to a function when generating -mshared abicalls code. */
-
-static void
-mips_extra_live_on_entry (bitmap regs)
-{
- if (TARGET_ABICALLS && !TARGET_ABSOLUTE_ABICALLS)
- bitmap_set_bit (regs, PIC_FUNCTION_ADDR_REGNUM);
-}
-
-/* SImode values are represented as sign-extended to DImode. */
-
-int
-mips_mode_rep_extended (enum machine_mode mode, enum machine_mode mode_rep)
-{
- if (TARGET_64BIT && mode == SImode && mode_rep == DImode)
- return SIGN_EXTEND;
-
- return UNKNOWN;
-}
-
-#include "gt-mips.h"