-/* Copyright (C) 1997, 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005
+ Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
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, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
#include "except.h"
#include "function.h"
#include "optabs.h"
-#include "libfuncs.h"
#include "toplev.h"
#include "basic-block.h"
#include "tm_p.h"
#include <ctype.h>
#include "target.h"
#include "target-def.h"
+#include "targhooks.h"
+#include "integrate.h"
+#include "langhooks.h"
#ifndef FRV_INLINE
#define FRV_INLINE inline
#endif
+/* The maximum number of distinct NOP patterns. There are three:
+ nop, fnop and mnop. */
+#define NUM_NOP_PATTERNS 3
+
+/* Classification of instructions and units: integer, floating-point/media,
+ branch and control. */
+enum frv_insn_group { GROUP_I, GROUP_FM, GROUP_B, GROUP_C, NUM_GROUPS };
+
+/* The DFA names of the units, in packet order. */
+static const char *const frv_unit_names[] =
+{
+ "c",
+ "i0", "f0",
+ "i1", "f1",
+ "i2", "f2",
+ "i3", "f3",
+ "b0", "b1"
+};
+
+/* The classification of each unit in frv_unit_names[]. */
+static const enum frv_insn_group frv_unit_groups[ARRAY_SIZE (frv_unit_names)] =
+{
+ GROUP_C,
+ GROUP_I, GROUP_FM,
+ GROUP_I, GROUP_FM,
+ GROUP_I, GROUP_FM,
+ GROUP_I, GROUP_FM,
+ GROUP_B, GROUP_B
+};
+
+/* Return the DFA unit code associated with the Nth unit of integer
+ or floating-point group GROUP, */
+#define NTH_UNIT(GROUP, N) frv_unit_codes[(GROUP) + (N) * 2 + 1]
+
+/* Return the number of integer or floating-point unit UNIT
+ (1 for I1, 2 for F2, etc.). */
+#define UNIT_NUMBER(UNIT) (((UNIT) - 1) / 2)
+
+/* The DFA unit number for each unit in frv_unit_names[]. */
+static int frv_unit_codes[ARRAY_SIZE (frv_unit_names)];
+
+/* FRV_TYPE_TO_UNIT[T] is the last unit in frv_unit_names[] that can issue
+ an instruction of type T. The value is ARRAY_SIZE (frv_unit_names) if
+ no instruction of type T has been seen. */
+static unsigned int frv_type_to_unit[TYPE_UNKNOWN + 1];
+
+/* An array of dummy nop INSNs, one for each type of nop that the
+ target supports. */
+static GTY(()) rtx frv_nops[NUM_NOP_PATTERNS];
+
+/* The number of nop instructions in frv_nops[]. */
+static unsigned int frv_num_nops;
+
+/* Information about one __builtin_read or __builtin_write access, or
+ the combination of several such accesses. The most general value
+ is all-zeros (an unknown access to an unknown address). */
+struct frv_io {
+ /* The type of access. FRV_IO_UNKNOWN means the access can be either
+ a read or a write. */
+ enum { FRV_IO_UNKNOWN, FRV_IO_READ, FRV_IO_WRITE } type;
+
+ /* The constant address being accessed, or zero if not known. */
+ HOST_WIDE_INT const_address;
+
+ /* The run-time address, as used in operand 0 of the membar pattern. */
+ rtx var_address;
+};
+
+/* Return true if instruction INSN should be packed with the following
+ instruction. */
+#define PACKING_FLAG_P(INSN) (GET_MODE (INSN) == TImode)
+
+/* Set the value of PACKING_FLAG_P(INSN). */
+#define SET_PACKING_FLAG(INSN) PUT_MODE (INSN, TImode)
+#define CLEAR_PACKING_FLAG(INSN) PUT_MODE (INSN, VOIDmode)
+
+/* Loop with REG set to each hard register in rtx X. */
+#define FOR_EACH_REGNO(REG, X) \
+ for (REG = REGNO (X); \
+ REG < REGNO (X) + HARD_REGNO_NREGS (REGNO (X), GET_MODE (X)); \
+ REG++)
+
+/* This structure contains machine specific function data. */
+struct machine_function GTY(())
+{
+ /* True if we have created an rtx that relies on the stack frame. */
+ int frame_needed;
+
+ /* True if this function contains at least one __builtin_{read,write}*. */
+ bool has_membar_p;
+};
+
/* Temporary register allocation support structure. */
typedef struct frv_tmp_reg_struct
{
}
frv_tmp_reg_t;
-/* Register state information for VLIW re-packing phase. These values must fit
- within an unsigned char. */
-#define REGSTATE_DEAD 0x00 /* register is currently dead */
+/* Register state information for VLIW re-packing phase. */
#define REGSTATE_CC_MASK 0x07 /* Mask to isolate CCn for cond exec */
-#define REGSTATE_LIVE 0x08 /* register is live */
-#define REGSTATE_MODIFIED 0x10 /* reg modified in current VLIW insn */
-#define REGSTATE_IF_TRUE 0x20 /* reg modified in cond exec true */
-#define REGSTATE_IF_FALSE 0x40 /* reg modified in cond exec false */
-#define REGSTATE_UNUSED 0x80 /* bit for hire */
-#define REGSTATE_MASK 0xff /* mask for the bits to set */
-
- /* conditional expression used */
+#define REGSTATE_MODIFIED 0x08 /* reg modified in current VLIW insn */
+#define REGSTATE_IF_TRUE 0x10 /* reg modified in cond exec true */
+#define REGSTATE_IF_FALSE 0x20 /* reg modified in cond exec false */
+
#define REGSTATE_IF_EITHER (REGSTATE_IF_TRUE | REGSTATE_IF_FALSE)
-/* the following is not sure in the reg_state bytes, so can have a larger value
- than 0xff. */
-#define REGSTATE_CONDJUMP 0x100 /* conditional jump done in VLIW insn */
+typedef unsigned char regstate_t;
/* Used in frv_frame_accessor_t to indicate the direction of a register-to-
memory move. */
rtx frv_compare_op0;
rtx frv_compare_op1;
-/* Conditional execution support gathered together in one structure */
+/* Conditional execution support gathered together in one structure. */
typedef struct
{
/* Linked list of insns to add if the conditional execution conversion was
/* Current number of temp registers available. */
int cur_scratch_regs;
- /* Number of nested conditional execution blocks */
+ /* Number of nested conditional execution blocks. */
int num_nested_cond_exec;
/* Map of insns that set up constants in scratch registers. */
bitmap scratch_insns_bitmap;
- /* Conditional execution test register (CC0..CC7) */
+ /* Conditional execution test register (CC0..CC7). */
rtx cr_reg;
/* Conditional execution compare register that is paired with cr_reg, so that
nested compares can be done. The csubcc and caddcc instructions don't
have enough bits to specify both a CC register to be set and a CR register
to do the test on, so the same bit number is used for both. Needless to
- say, this is rather inconvient for GCC. */
+ say, this is rather inconvenient for GCC. */
rtx nested_cc_reg;
/* Extra CR registers used for &&, ||. */
rtx extra_fp_cr;
/* Previous CR used in nested if, to make sure we are dealing with the same
- nested if as the previous statement. */
+ nested if as the previous statement. */
rtx last_nested_if_cr;
}
frv_ifcvt_t;
/* Map register number to smallest register class. */
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
-/* Map class letter into register class */
+/* Map class letter into register class. */
enum reg_class reg_class_from_letter[256];
-/* Cached value of frv_stack_info */
+/* Cached value of frv_stack_info. */
static frv_stack_t *frv_stack_cache = (frv_stack_t *)0;
-/* -mbranch-cost= support */
-const char *frv_branch_cost_string;
-int frv_branch_cost_int = DEFAULT_BRANCH_COST;
-
/* -mcpu= support */
-const char *frv_cpu_string; /* -mcpu= option */
frv_cpu_t frv_cpu_type = CPU_TYPE; /* value of -mcpu= */
-/* -mcond-exec-insns= support */
-const char *frv_condexec_insns_str; /* -mcond-exec-insns= option */
-int frv_condexec_insns = DEFAULT_CONDEXEC_INSNS; /* value of -mcond-exec-insns*/
-
-/* -mcond-exec-temps= support */
-const char *frv_condexec_temps_str; /* -mcond-exec-temps= option */
-int frv_condexec_temps = DEFAULT_CONDEXEC_TEMPS; /* value of -mcond-exec-temps*/
-
-/* -msched-lookahead=n */
-const char *frv_sched_lookahead_str; /* -msched-lookahead=n */
-int frv_sched_lookahead = 4; /* -msched-lookahead=n */
-
/* Forward references */
-static int frv_default_flags_for_cpu PARAMS ((void));
-static int frv_string_begins_with PARAMS ((tree, const char *));
-static FRV_INLINE int const_small_data_p PARAMS ((rtx));
-static FRV_INLINE int plus_small_data_p PARAMS ((rtx, rtx));
+
+static bool frv_handle_option (size_t, const char *, int);
+static int frv_default_flags_for_cpu (void);
+static int frv_string_begins_with (tree, const char *);
+static FRV_INLINE bool frv_small_data_reloc_p (rtx, int);
static void frv_print_operand_memory_reference_reg
- PARAMS ((FILE *, rtx));
-static void frv_print_operand_memory_reference PARAMS ((FILE *, rtx, int));
-static int frv_print_operand_jump_hint PARAMS ((rtx));
-static FRV_INLINE int frv_regno_ok_for_base_p PARAMS ((int, int));
-static rtx single_set_pattern PARAMS ((rtx));
-static int frv_function_contains_far_jump PARAMS ((void));
-static rtx frv_alloc_temp_reg PARAMS ((frv_tmp_reg_t *,
- enum reg_class,
- enum machine_mode,
- int, int));
-static rtx frv_frame_offset_rtx PARAMS ((int));
-static rtx frv_frame_mem PARAMS ((enum machine_mode,
- rtx, int));
-static rtx frv_dwarf_store PARAMS ((rtx, int));
-static void frv_frame_insn PARAMS ((rtx, rtx));
-static void frv_frame_access PARAMS ((frv_frame_accessor_t*,
- rtx, int));
-static void frv_frame_access_multi PARAMS ((frv_frame_accessor_t*,
- frv_stack_t *, int));
-static void frv_frame_access_standard_regs PARAMS ((enum frv_stack_op,
- frv_stack_t *));
-static struct machine_function *frv_init_machine_status PARAMS ((void));
-static int frv_legitimate_memory_operand PARAMS ((rtx,
- enum machine_mode,
- int));
-static rtx frv_int_to_acc PARAMS ((enum insn_code,
- int, rtx));
-static enum machine_mode frv_matching_accg_mode PARAMS ((enum machine_mode));
-static rtx frv_read_argument PARAMS ((tree *));
-static int frv_check_constant_argument PARAMS ((enum insn_code,
- int, rtx));
-static rtx frv_legitimize_target PARAMS ((enum insn_code, rtx));
-static rtx frv_legitimize_argument PARAMS ((enum insn_code,
- int, rtx));
-static rtx frv_expand_set_builtin PARAMS ((enum insn_code,
- tree, rtx));
-static rtx frv_expand_unop_builtin PARAMS ((enum insn_code,
- tree, rtx));
-static rtx frv_expand_binop_builtin PARAMS ((enum insn_code,
- tree, rtx));
-static rtx frv_expand_cut_builtin PARAMS ((enum insn_code,
- tree, rtx));
-static rtx frv_expand_binopimm_builtin PARAMS ((enum insn_code,
- tree, rtx));
-static rtx frv_expand_voidbinop_builtin PARAMS ((enum insn_code,
- tree));
-static rtx frv_expand_voidtriop_builtin PARAMS ((enum insn_code,
- tree));
-static rtx frv_expand_voidaccop_builtin PARAMS ((enum insn_code,
- tree));
-static rtx frv_expand_mclracc_builtin PARAMS ((tree));
-static rtx frv_expand_mrdacc_builtin PARAMS ((enum insn_code,
- tree));
-static rtx frv_expand_mwtacc_builtin PARAMS ((enum insn_code,
- tree));
-static rtx frv_expand_noargs_builtin PARAMS ((enum insn_code));
-static rtx frv_emit_comparison PARAMS ((enum rtx_code, rtx,
- rtx));
-static int frv_clear_registers_used PARAMS ((rtx *, void *));
-static void frv_ifcvt_add_insn PARAMS ((rtx, rtx, int));
-static rtx frv_ifcvt_rewrite_mem PARAMS ((rtx,
- enum machine_mode,
- rtx));
-static rtx frv_ifcvt_load_value PARAMS ((rtx, rtx));
-static void frv_registers_update PARAMS ((rtx, unsigned char [],
- int [], int *, int));
-static int frv_registers_used_p PARAMS ((rtx, unsigned char [],
- int));
-static int frv_registers_set_p PARAMS ((rtx, unsigned char [],
- int));
-static int frv_issue_rate PARAMS ((void));
-static int frv_use_dfa_pipeline_interface PARAMS ((void));
-static void frv_pack_insns PARAMS ((void));
-static void frv_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
-static void frv_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
-static bool frv_assemble_integer PARAMS ((rtx, unsigned, int));
-static void frv_init_builtins PARAMS ((void));
-static rtx frv_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
-static void frv_init_libfuncs PARAMS ((void));
-static bool frv_in_small_data_p PARAMS ((tree));
+ (FILE *, rtx);
+static void frv_print_operand_memory_reference (FILE *, rtx, int);
+static int frv_print_operand_jump_hint (rtx);
+static const char *comparison_string (enum rtx_code, rtx);
+static FRV_INLINE int frv_regno_ok_for_base_p (int, int);
+static rtx single_set_pattern (rtx);
+static int frv_function_contains_far_jump (void);
+static rtx frv_alloc_temp_reg (frv_tmp_reg_t *,
+ enum reg_class,
+ enum machine_mode,
+ int, int);
+static rtx frv_frame_offset_rtx (int);
+static rtx frv_frame_mem (enum machine_mode, rtx, int);
+static rtx frv_dwarf_store (rtx, int);
+static void frv_frame_insn (rtx, rtx);
+static void frv_frame_access (frv_frame_accessor_t*,
+ rtx, int);
+static void frv_frame_access_multi (frv_frame_accessor_t*,
+ frv_stack_t *, int);
+static void frv_frame_access_standard_regs (enum frv_stack_op,
+ frv_stack_t *);
+static struct machine_function *frv_init_machine_status (void);
+static rtx frv_int_to_acc (enum insn_code, int, rtx);
+static enum machine_mode frv_matching_accg_mode (enum machine_mode);
+static rtx frv_read_argument (tree, unsigned int);
+static rtx frv_read_iacc_argument (enum machine_mode, tree, unsigned int);
+static int frv_check_constant_argument (enum insn_code, int, rtx);
+static rtx frv_legitimize_target (enum insn_code, rtx);
+static rtx frv_legitimize_argument (enum insn_code, int, rtx);
+static rtx frv_legitimize_tls_address (rtx, enum tls_model);
+static rtx frv_expand_set_builtin (enum insn_code, tree, rtx);
+static rtx frv_expand_unop_builtin (enum insn_code, tree, rtx);
+static rtx frv_expand_binop_builtin (enum insn_code, tree, rtx);
+static rtx frv_expand_cut_builtin (enum insn_code, tree, rtx);
+static rtx frv_expand_binopimm_builtin (enum insn_code, tree, rtx);
+static rtx frv_expand_voidbinop_builtin (enum insn_code, tree);
+static rtx frv_expand_int_void2arg (enum insn_code, tree);
+static rtx frv_expand_prefetches (enum insn_code, tree);
+static rtx frv_expand_voidtriop_builtin (enum insn_code, tree);
+static rtx frv_expand_voidaccop_builtin (enum insn_code, tree);
+static rtx frv_expand_mclracc_builtin (tree);
+static rtx frv_expand_mrdacc_builtin (enum insn_code, tree);
+static rtx frv_expand_mwtacc_builtin (enum insn_code, tree);
+static rtx frv_expand_noargs_builtin (enum insn_code);
+static void frv_split_iacc_move (rtx, rtx);
+static rtx frv_emit_comparison (enum rtx_code, rtx, rtx);
+static int frv_clear_registers_used (rtx *, void *);
+static void frv_ifcvt_add_insn (rtx, rtx, int);
+static rtx frv_ifcvt_rewrite_mem (rtx, enum machine_mode, rtx);
+static rtx frv_ifcvt_load_value (rtx, rtx);
+static int frv_acc_group_1 (rtx *, void *);
+static unsigned int frv_insn_unit (rtx);
+static bool frv_issues_to_branch_unit_p (rtx);
+static int frv_cond_flags (rtx);
+static bool frv_regstate_conflict_p (regstate_t, regstate_t);
+static int frv_registers_conflict_p_1 (rtx *, void *);
+static bool frv_registers_conflict_p (rtx);
+static void frv_registers_update_1 (rtx, rtx, void *);
+static void frv_registers_update (rtx);
+static void frv_start_packet (void);
+static void frv_start_packet_block (void);
+static void frv_finish_packet (void (*) (void));
+static bool frv_pack_insn_p (rtx);
+static void frv_add_insn_to_packet (rtx);
+static void frv_insert_nop_in_packet (rtx);
+static bool frv_for_each_packet (void (*) (void));
+static bool frv_sort_insn_group_1 (enum frv_insn_group,
+ unsigned int, unsigned int,
+ unsigned int, unsigned int,
+ state_t);
+static int frv_compare_insns (const void *, const void *);
+static void frv_sort_insn_group (enum frv_insn_group);
+static void frv_reorder_packet (void);
+static void frv_fill_unused_units (enum frv_insn_group);
+static void frv_align_label (void);
+static void frv_reorg_packet (void);
+static void frv_register_nop (rtx);
+static void frv_reorg (void);
+static void frv_pack_insns (void);
+static void frv_function_prologue (FILE *, HOST_WIDE_INT);
+static void frv_function_epilogue (FILE *, HOST_WIDE_INT);
+static bool frv_assemble_integer (rtx, unsigned, int);
+static void frv_init_builtins (void);
+static rtx frv_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
+static void frv_init_libfuncs (void);
+static bool frv_in_small_data_p (tree);
static void frv_asm_output_mi_thunk
- PARAMS ((FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree));
-static bool frv_rtx_costs PARAMS ((rtx, int, int, int*));
-static void frv_asm_out_constructor PARAMS ((rtx, int));
-static void frv_asm_out_destructor PARAMS ((rtx, int));
+ (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree);
+static void frv_setup_incoming_varargs (CUMULATIVE_ARGS *,
+ enum machine_mode,
+ tree, int *, int);
+static rtx frv_expand_builtin_saveregs (void);
+static bool frv_rtx_costs (rtx, int, int, int*);
+static void frv_asm_out_constructor (rtx, int);
+static void frv_asm_out_destructor (rtx, int);
+static bool frv_function_symbol_referenced_p (rtx);
+static bool frv_cannot_force_const_mem (rtx);
+static const char *unspec_got_name (int);
+static void frv_output_const_unspec (FILE *,
+ const struct frv_unspec *);
+static bool frv_function_ok_for_sibcall (tree, tree);
+static rtx frv_struct_value_rtx (tree, int);
+static bool frv_must_pass_in_stack (enum machine_mode mode, tree type);
+static int frv_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static void frv_output_dwarf_dtprel (FILE *, int, rtx)
+ ATTRIBUTE_UNUSED;
+\f
+/* Allow us to easily change the default for -malloc-cc. */
+#ifndef DEFAULT_NO_ALLOC_CC
+#define MASK_DEFAULT_ALLOC_CC MASK_ALLOC_CC
+#else
+#define MASK_DEFAULT_ALLOC_CC 0
+#endif
\f
/* Initialize the GCC target structure. */
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_EPILOGUE frv_function_epilogue
#undef TARGET_ASM_INTEGER
#define TARGET_ASM_INTEGER frv_assemble_integer
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS \
+ (MASK_DEFAULT_ALLOC_CC \
+ | MASK_COND_MOVE \
+ | MASK_SCC \
+ | MASK_COND_EXEC \
+ | MASK_VLIW_BRANCH \
+ | MASK_MULTI_CE \
+ | MASK_NESTED_CE)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION frv_handle_option
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS frv_init_builtins
#undef TARGET_EXPAND_BUILTIN
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE frv_issue_rate
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE frv_use_dfa_pipeline_interface
+
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL frv_function_ok_for_sibcall
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM frv_cannot_force_const_mem
+
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS HAVE_AS_TLS
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX frv_struct_value_rtx
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK frv_must_pass_in_stack
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE hook_pass_by_reference_must_pass_in_stack
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES frv_arg_partial_bytes
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS frv_expand_builtin_saveregs
+#undef TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS frv_setup_incoming_varargs
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG frv_reorg
+
+#if HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL frv_output_dwarf_dtprel
+#endif
struct gcc_target targetm = TARGET_INITIALIZER;
+
+#define FRV_SYMBOL_REF_TLS_P(RTX) \
+ (GET_CODE (RTX) == SYMBOL_REF && SYMBOL_REF_TLS_MODEL (RTX) != 0)
+
\f
-/* Given a CONST, return true if the symbol_ref points to small data. */
+/* Any function call that satisfies the machine-independent
+ requirements is eligible on FR-V. */
-static FRV_INLINE int
-const_small_data_p (x)
- rtx x;
+static bool
+frv_function_ok_for_sibcall (tree decl ATTRIBUTE_UNUSED,
+ tree exp ATTRIBUTE_UNUSED)
{
- rtx x0, x1;
+ return true;
+}
- if (GET_CODE (XEXP (x, 0)) != PLUS)
- return FALSE;
+/* Return true if SYMBOL is a small data symbol and relocation RELOC
+ can be used to access it directly in a load or store. */
- x0 = XEXP (XEXP (x, 0), 0);
- if (GET_CODE (x0) != SYMBOL_REF || !SYMBOL_REF_SMALL_P (x0))
- return FALSE;
+static FRV_INLINE bool
+frv_small_data_reloc_p (rtx symbol, int reloc)
+{
+ return (GET_CODE (symbol) == SYMBOL_REF
+ && SYMBOL_REF_SMALL_P (symbol)
+ && (!TARGET_FDPIC || flag_pic == 1)
+ && (reloc == R_FRV_GOTOFF12 || reloc == R_FRV_GPREL12));
+}
- x1 = XEXP (XEXP (x, 0), 1);
- if (GET_CODE (x1) != CONST_INT
- || !IN_RANGE_P (INTVAL (x1), -2048, 2047))
- return FALSE;
+/* Return true if X is a valid relocation unspec. If it is, fill in UNSPEC
+ appropriately. */
- return TRUE;
+bool
+frv_const_unspec_p (rtx x, struct frv_unspec *unspec)
+{
+ if (GET_CODE (x) == CONST)
+ {
+ unspec->offset = 0;
+ x = XEXP (x, 0);
+ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+ {
+ unspec->offset += INTVAL (XEXP (x, 1));
+ x = XEXP (x, 0);
+ }
+ if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_GOT)
+ {
+ unspec->symbol = XVECEXP (x, 0, 0);
+ unspec->reloc = INTVAL (XVECEXP (x, 0, 1));
+
+ if (unspec->offset == 0)
+ return true;
+
+ if (frv_small_data_reloc_p (unspec->symbol, unspec->reloc)
+ && unspec->offset > 0
+ && (unsigned HOST_WIDE_INT) unspec->offset < g_switch_value)
+ return true;
+ }
+ }
+ return false;
}
-/* Given a PLUS, return true if this is a small data reference. */
+/* Decide whether we can force certain constants to memory. If we
+ decide we can't, the caller should be able to cope with it in
+ another way.
-static FRV_INLINE int
-plus_small_data_p (op0, op1)
- rtx op0;
- rtx op1;
+ We never allow constants to be forced into memory for TARGET_FDPIC.
+ This is necessary for several reasons:
+
+ 1. Since LEGITIMATE_CONSTANT_P rejects constant pool addresses, the
+ target-independent code will try to force them into the constant
+ pool, thus leading to infinite recursion.
+
+ 2. We can never introduce new constant pool references during reload.
+ Any such reference would require use of the pseudo FDPIC register.
+
+ 3. We can't represent a constant added to a function pointer (which is
+ not the same as a pointer to a function+constant).
+
+ 4. In many cases, it's more efficient to calculate the constant in-line. */
+
+static bool
+frv_cannot_force_const_mem (rtx x ATTRIBUTE_UNUSED)
+{
+ return TARGET_FDPIC;
+}
+\f
+/* Implement TARGET_HANDLE_OPTION. */
+
+static bool
+frv_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
{
- if (GET_MODE (op0) == SImode
- && GET_CODE (op0) == REG
- && REGNO (op0) == SDA_BASE_REG)
+ switch (code)
{
- if (GET_CODE (op1) == SYMBOL_REF)
- return SYMBOL_REF_SMALL_P (op1);
+ case OPT_mcpu_:
+ if (strcmp (arg, "simple") == 0)
+ frv_cpu_type = FRV_CPU_SIMPLE;
+ else if (strcmp (arg, "tomcat") == 0)
+ frv_cpu_type = FRV_CPU_TOMCAT;
+ else if (strcmp (arg, "fr550") == 0)
+ frv_cpu_type = FRV_CPU_FR550;
+ else if (strcmp (arg, "fr500") == 0)
+ frv_cpu_type = FRV_CPU_FR500;
+ else if (strcmp (arg, "fr450") == 0)
+ frv_cpu_type = FRV_CPU_FR450;
+ else if (strcmp (arg, "fr405") == 0)
+ frv_cpu_type = FRV_CPU_FR405;
+ else if (strcmp (arg, "fr400") == 0)
+ frv_cpu_type = FRV_CPU_FR400;
+ else if (strcmp (arg, "fr300") == 0)
+ frv_cpu_type = FRV_CPU_FR300;
+ else if (strcmp (arg, "frv") == 0)
+ frv_cpu_type = FRV_CPU_GENERIC;
+ else
+ return false;
+ return true;
- if (GET_CODE (op1) == CONST)
- return const_small_data_p (op1);
+ default:
+ return true;
}
-
- return FALSE;
}
-\f
static int
-frv_default_flags_for_cpu ()
+frv_default_flags_for_cpu (void)
{
switch (frv_cpu_type)
{
case FRV_CPU_GENERIC:
return MASK_DEFAULT_FRV;
+ case FRV_CPU_FR550:
+ return MASK_DEFAULT_FR550;
+
case FRV_CPU_FR500:
case FRV_CPU_TOMCAT:
return MASK_DEFAULT_FR500;
+ case FRV_CPU_FR450:
+ return MASK_DEFAULT_FR450;
+
+ case FRV_CPU_FR405:
case FRV_CPU_FR400:
return MASK_DEFAULT_FR400;
case FRV_CPU_FR300:
case FRV_CPU_SIMPLE:
return MASK_DEFAULT_SIMPLE;
+
+ default:
+ gcc_unreachable ();
}
- abort ();
}
/* Sometimes certain combinations of command options do not make
`-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
void
-frv_override_options ()
+frv_override_options (void)
{
- int regno, i;
-
- /* Set the cpu type */
- if (frv_cpu_string)
- {
- if (strcmp (frv_cpu_string, "simple") == 0)
- frv_cpu_type = FRV_CPU_SIMPLE;
-
- else if (strcmp (frv_cpu_string, "tomcat") == 0)
- frv_cpu_type = FRV_CPU_TOMCAT;
-
- else if (strncmp (frv_cpu_string, "fr", sizeof ("fr")-1) != 0)
- error ("Unknown cpu: -mcpu=%s", frv_cpu_string);
-
- else
- {
- const char *p = frv_cpu_string + sizeof ("fr") - 1;
- if (strcmp (p, "500") == 0)
- frv_cpu_type = FRV_CPU_FR500;
-
- else if (strcmp (p, "400") == 0)
- frv_cpu_type = FRV_CPU_FR400;
-
- else if (strcmp (p, "300") == 0)
- frv_cpu_type = FRV_CPU_FR300;
-
- else if (strcmp (p, "v") == 0)
- frv_cpu_type = FRV_CPU_GENERIC;
-
- else
- error ("Unknown cpu: -mcpu=%s", frv_cpu_string);
- }
- }
+ int regno;
+ unsigned int i;
target_flags |= (frv_default_flags_for_cpu () & ~target_flags_explicit);
}
}
- /* Both -fpic and -gdwarf want to use .previous and the assembler only keeps
- one level. */
- if (write_symbols == DWARF_DEBUG && flag_pic)
- error ("-fpic and -gdwarf are incompatible (-fpic and -g/-gdwarf-2 are fine)");
-
- /* Change the branch cost value */
- if (frv_branch_cost_string)
- frv_branch_cost_int = atoi (frv_branch_cost_string);
-
- /* Change the # of insns to be converted to conditional execution */
- if (frv_condexec_insns_str)
- frv_condexec_insns = atoi (frv_condexec_insns_str);
-
- /* Change # of temporary registers used to hold integer constants */
- if (frv_condexec_temps_str)
- frv_condexec_temps = atoi (frv_condexec_temps_str);
-
- /* Change scheduling look ahead. */
- if (frv_sched_lookahead_str)
- frv_sched_lookahead = atoi (frv_sched_lookahead_str);
-
/* A C expression whose value is a register class containing hard
register REGNO. In general there is more than one such class;
choose a class which is "minimal", meaning that no smaller class
- also contains the register. */
+ also contains the register. */
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
if (GPR_P (regno))
{
int gpr_reg = regno - GPR_FIRST;
- if ((gpr_reg & 3) == 0)
+
+ if (gpr_reg == GR8_REG)
+ class = GR8_REGS;
+
+ else if (gpr_reg == GR9_REG)
+ class = GR9_REGS;
+
+ else if (gpr_reg == GR14_REG)
+ class = FDPIC_FPTR_REGS;
+
+ else if (gpr_reg == FDPIC_REGNO)
+ class = FDPIC_REGS;
+
+ else if ((gpr_reg & 3) == 0)
class = QUAD_REGS;
else if ((gpr_reg & 1) == 0)
reg_class_from_letter['A'] = QUAD_ACC_REGS;
reg_class_from_letter['B'] = ACCG_REGS;
reg_class_from_letter['C'] = CR_REGS;
+ reg_class_from_letter['W'] = FDPIC_CALL_REGS; /* gp14+15 */
+ reg_class_from_letter['Z'] = FDPIC_REGS; /* gp15 */
/* There is no single unaligned SI op for PIC code. Sometimes we
need to use ".4byte" and sometimes we need to use ".picptr".
See frv_assemble_integer for details. */
- if (flag_pic)
+ if (flag_pic || TARGET_FDPIC)
targetm.asm_out.unaligned_op.si = 0;
+ if ((target_flags_explicit & MASK_LINKED_FP) == 0)
+ target_flags |= MASK_LINKED_FP;
+
+ if ((target_flags_explicit & MASK_OPTIMIZE_MEMBAR) == 0)
+ target_flags |= MASK_OPTIMIZE_MEMBAR;
+
+ for (i = 0; i < ARRAY_SIZE (frv_unit_names); i++)
+ frv_unit_codes[i] = get_cpu_unit_code (frv_unit_names[i]);
+
+ for (i = 0; i < ARRAY_SIZE (frv_type_to_unit); i++)
+ frv_type_to_unit[i] = ARRAY_SIZE (frv_unit_codes);
+
init_machine_status = frv_init_machine_status;
}
You should not use this macro to change options that are not
machine-specific. These should uniformly selected by the same optimization
- level on all supported machines. Use this macro to enable machbine-specific
+ level on all supported machines. Use this macro to enable machine-specific
optimizations.
*Do not examine `write_symbols' in this macro!* The debugging options are
/* On the FRV, possibly disable VLIW packing which is done by the 2nd
scheduling pass at the current time. */
void
-frv_optimization_options (level, size)
- int level;
- int size ATTRIBUTE_UNUSED;
+frv_optimization_options (int level, int size ATTRIBUTE_UNUSED)
{
if (level >= 2)
{
/* Return true if NAME (a STRING_CST node) begins with PREFIX. */
static int
-frv_string_begins_with (name, prefix)
- tree name;
- const char *prefix;
+frv_string_begins_with (tree name, const char *prefix)
{
int prefix_len = strlen (prefix);
target switches are opposed to them.) */
void
-frv_conditional_register_usage ()
+frv_conditional_register_usage (void)
{
int i;
for (i = FPR_FIRST + NUM_FPRS; i <= FPR_LAST; i++)
fixed_regs[i] = call_used_regs[i] = 1;
- for (i = ACC_FIRST + NUM_ACCS; i <= ACC_LAST; i++)
- fixed_regs[i] = call_used_regs[i] = 1;
-
- for (i = ACCG_FIRST + NUM_ACCS; i <= ACCG_LAST; i++)
- fixed_regs[i] = call_used_regs[i] = 1;
-
/* Reserve the registers used for conditional execution. At present, we need
1 ICC and 1 ICR register. */
fixed_regs[ICC_TEMP] = call_used_regs[ICC_TEMP] = 1;
fixed_regs[FCR_FIRST] = call_used_regs[FCR_FIRST] = 1;
}
+ if (TARGET_FDPIC)
+ fixed_regs[GPR_FIRST + 16] = fixed_regs[GPR_FIRST + 17] =
+ call_used_regs[GPR_FIRST + 16] = call_used_regs[GPR_FIRST + 17] = 0;
+
#if 0
/* If -fpic, SDA_BASE_REG is the PIC register. */
if (g_switch_value == 0 && !flag_pic)
*/
frv_stack_t *
-frv_stack_info ()
+frv_stack_info (void)
{
static frv_stack_t info, zero_info;
frv_stack_t *info_ptr = &info;
int alignment;
int offset;
- /* If we've already calculated the values and reload is complete, just return now */
+ /* If we've already calculated the values and reload is complete,
+ just return now. */
if (frv_stack_cache)
return frv_stack_cache;
- /* Zero all fields */
+ /* Zero all fields. */
info = zero_info;
- /* Set up the register range information */
+ /* Set up the register range information. */
info_ptr->regs[STACK_REGS_GPR].name = "gpr";
info_ptr->regs[STACK_REGS_GPR].first = LAST_ARG_REGNUM + 1;
info_ptr->regs[STACK_REGS_GPR].last = GPR_LAST;
info_ptr->regs[STACK_REGS_STDARG].special_p = 1;
info_ptr->regs[STACK_REGS_STRUCT].name = "struct";
- info_ptr->regs[STACK_REGS_STRUCT].first = STRUCT_VALUE_REGNUM;
- info_ptr->regs[STACK_REGS_STRUCT].last = STRUCT_VALUE_REGNUM;
+ info_ptr->regs[STACK_REGS_STRUCT].first = FRV_STRUCT_VALUE_REGNUM;
+ info_ptr->regs[STACK_REGS_STRUCT].last = FRV_STRUCT_VALUE_REGNUM;
info_ptr->regs[STACK_REGS_STRUCT].special_p = 1;
info_ptr->regs[STACK_REGS_FP].name = "fp";
}
}
- /* Iterate over all of the register ranges */
+ /* Iterate over all of the register ranges. */
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]);
int size_2words = 0;
int regno;
- /* Calculate which registers need to be saved & save area size */
+ /* Calculate which registers need to be saved & save area size. */
switch (range)
{
default:
if ((regs_ever_live[regno] && !call_used_regs[regno])
|| (current_function_calls_eh_return
&& (regno >= FIRST_EH_REGNUM && regno <= LAST_EH_REGNUM))
- || (flag_pic && cfun->uses_pic_offset_table && regno == PIC_REGNO))
+ || (!TARGET_FDPIC && flag_pic
+ && cfun->uses_pic_offset_table && regno == PIC_REGNO))
{
info_ptr->save_p[regno] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
case STACK_REGS_LR:
if (regs_ever_live[LR_REGNO]
|| profile_flag
- || frame_pointer_needed
- || (flag_pic && cfun->uses_pic_offset_table))
+ /* This is set for __builtin_return_address, etc. */
+ || cfun->machine->frame_needed
+ || (TARGET_LINKED_FP && frame_pointer_needed)
+ || (!TARGET_FDPIC && flag_pic
+ && cfun->uses_pic_offset_table))
{
info_ptr->save_p[LR_REGNO] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
case STACK_REGS_STDARG:
if (varargs_p)
{
- /* If this is a stdarg function with a non varardic argument split
- between registers and the stack, adjust the saved registers
- downward */
+ /* If this is a stdarg function with a non varardic
+ argument split between registers and the stack,
+ adjust the saved registers downward. */
last -= (ADDR_ALIGN (cfun->pretend_args_size, UNITS_PER_WORD)
/ UNITS_PER_WORD);
case STACK_REGS_STRUCT:
if (cfun->returns_struct)
{
- info_ptr->save_p[STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD;
+ info_ptr->save_p[FRV_STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
}
break;
if (size_1word)
{
- /* If this is a field, it only takes one word */
+ /* If this is a field, it only takes one word. */
if (reg_ptr->field_p)
size_1word = UNITS_PER_WORD;
- /* Determine which register pairs can be saved together */
+ /* Determine which register pairs can be saved together. */
else if (reg_ptr->dword_p && TARGET_DWORD)
{
for (regno = first; regno < last; regno += 2)
/* See if we need to create a frame at all, if so add header area. */
if (info_ptr->total_size > 0
+ || frame_pointer_needed
|| info_ptr->regs[STACK_REGS_LR].size_1word > 0
|| info_ptr->regs[STACK_REGS_STRUCT].size_1word > 0)
{
info_ptr->header_size = 4 * UNITS_PER_WORD;
info_ptr->total_size += 4 * UNITS_PER_WORD;
- /* Calculate the offsets to save normal register pairs */
+ /* Calculate the offsets to save normal register pairs. */
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]);
}
}
- /* Calculate the offsets to save normal single registers */
+ /* Calculate the offsets to save normal single registers. */
for (range = 0; range < STACK_REGS_MAX; range++)
{
frv_stack_regs_t *reg_ptr = &(info_ptr->regs[range]);
if (cfun->returns_struct)
{
- info_ptr->save_p[STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD;
- info_ptr->reg_offset[STRUCT_VALUE_REGNUM] = offset + UNITS_PER_WORD;
+ info_ptr->save_p[FRV_STRUCT_VALUE_REGNUM] = REG_SAVE_1WORD;
+ info_ptr->reg_offset[FRV_STRUCT_VALUE_REGNUM] = offset + UNITS_PER_WORD;
info_ptr->regs[STACK_REGS_STRUCT].size_1word = UNITS_PER_WORD;
}
}
\f
-/* Print the information about the frv stack offsets, etc. when debugging. */
+/* Print the information about the frv stack offsets, etc. when debugging. */
void
-frv_debug_stack (info)
- frv_stack_t *info;
+frv_debug_stack (frv_stack_t *info)
{
int range;
\f
-/* The following variable value is TRUE if the next output insn should
- finish cpu cycle. In order words the insn will have packing bit
- (which means absence of asm code suffix `.p' on assembler. */
+/* Used during final to control the packing of insns. The value is
+ 1 if the current instruction should be packed with the next one,
+ 0 if it shouldn't or -1 if packing is disabled altogether. */
static int frv_insn_packing_flag;
/* True if the current function contains a far jump. */
static int
-frv_function_contains_far_jump ()
+frv_function_contains_far_jump (void)
{
rtx insn = get_insns ();
while (insn != NULL
will return correctly. It also does the VLIW packing. */
static void
-frv_function_prologue (file, size)
- FILE *file;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+frv_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
/* If no frame was created, check whether the function uses a call
instruction to implement a far jump. If so, save the link in gr3 and
rtx insn;
/* Just to check that the above comment is true. */
- if (regs_ever_live[GPR_FIRST + 3])
- abort ();
+ gcc_assert (!regs_ever_live[GPR_FIRST + 3]);
/* Generate the instruction that saves the link register. */
fprintf (file, "\tmovsg lr,gr3\n");
}
frv_pack_insns ();
- frv_insn_packing_flag = TRUE;
+
+ /* Allow the garbage collector to free the nops created by frv_reorg. */
+ memset (frv_nops, 0, sizeof (frv_nops));
}
\f
/* Return the next available temporary register in a given class. */
static rtx
-frv_alloc_temp_reg (info, class, mode, mark_as_used, no_abort)
- frv_tmp_reg_t *info; /* which registers are available */
- enum reg_class class; /* register class desired */
- enum machine_mode mode; /* mode to allocate register with */
- int mark_as_used; /* register not available after allocation */
- int no_abort; /* return NULL instead of aborting */
+frv_alloc_temp_reg (
+ frv_tmp_reg_t *info, /* which registers are available */
+ enum reg_class class, /* register class desired */
+ enum machine_mode mode, /* mode to allocate register with */
+ int mark_as_used, /* register not available after allocation */
+ int no_abort) /* return NULL instead of aborting */
{
int regno = info->next_reg[ (int)class ];
int orig_regno = regno;
regno = 0;
if (regno == orig_regno)
{
- if (no_abort)
- return NULL_RTX;
- else
- abort ();
+ gcc_assert (no_abort);
+ return NULL_RTX;
}
}
The function returns a constant rtx if OFFSET is small enough, otherwise
it loads the constant into register OFFSET_REGNO and returns that. */
static rtx
-frv_frame_offset_rtx (offset)
- int offset;
+frv_frame_offset_rtx (int offset)
{
rtx offset_rtx = GEN_INT (offset);
if (IN_RANGE_P (offset, -2048, 2047))
/* Generate (mem:MODE (plus:Pmode BASE (frv_frame_offset OFFSET)))). The
prologue and epilogue uses such expressions to access the stack. */
static rtx
-frv_frame_mem (mode, base, offset)
- enum machine_mode mode;
- rtx base;
- int offset;
+frv_frame_mem (enum machine_mode mode, rtx base, int offset)
{
return gen_rtx_MEM (mode, gen_rtx_PLUS (Pmode,
base,
or SEQUENCE that has several sets, each set must be individually marked
as frame-related. */
static rtx
-frv_dwarf_store (reg, offset)
- rtx reg;
- int offset;
+frv_dwarf_store (rtx reg, int offset)
{
rtx set = gen_rtx_SET (VOIDmode,
gen_rtx_MEM (GET_MODE (reg),
frame-related and has a REG_FRAME_RELATED_EXPR note containing
DWARF_PATTERN. */
static void
-frv_frame_insn (pattern, dwarf_pattern)
- rtx pattern;
- rtx dwarf_pattern;
+frv_frame_insn (rtx pattern, rtx dwarf_pattern)
{
rtx insn = emit_insn (pattern);
RTX_FRAME_RELATED_P (insn) = 1;
simply be the stack pointer, but if several accesses are being made to a
region far away from the stack pointer, it may be more efficient to set
up a temporary instead.
-
+
Store instructions will be frame-related and will be annotated with the
overall effect of the store. Load instructions will be followed by a
(use) to prevent later optimizations from zapping them.
The function takes care of the moves to and from SPRs, using TEMP_REGNO
as a temporary in such cases. */
static void
-frv_frame_access (accessor, reg, stack_offset)
- frv_frame_accessor_t *accessor;
- rtx reg;
- int stack_offset;
+frv_frame_access (frv_frame_accessor_t *accessor, rtx reg, int stack_offset)
{
enum machine_mode mode = GET_MODE (reg);
rtx mem = frv_frame_mem (mode,
is the stack information generated by frv_stack_info, and REG_SET is the
number of the register set to transfer. */
static void
-frv_frame_access_multi (accessor, info, reg_set)
- frv_frame_accessor_t *accessor;
- frv_stack_t *info;
- int reg_set;
+frv_frame_access_multi (frv_frame_accessor_t *accessor,
+ frv_stack_t *info,
+ int reg_set)
{
frv_stack_regs_t *regs_info;
int regno;
them if OP is FRV_LOAD. INFO is the stack information generated by
frv_stack_info. */
static void
-frv_frame_access_standard_regs (op, info)
- enum frv_stack_op op;
- frv_stack_t *info;
+frv_frame_access_standard_regs (enum frv_stack_op op, frv_stack_t *info)
{
frv_frame_accessor_t accessor;
frv_frame_access_multi (&accessor, info, STACK_REGS_GPR);
frv_frame_access_multi (&accessor, info, STACK_REGS_FPR);
frv_frame_access_multi (&accessor, info, STACK_REGS_LCR);
-}
+}
/* Called after register allocation to add any instructions needed for the
prologue. Using a prologue insn is favored compared to putting all of the
- instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler
- to intermix instructions with the saves of the caller saved registers. In
- some cases, it might be necessary to emit a barrier instruction as the last
- insn to prevent such scheduling.
+ instructions in the TARGET_ASM_FUNCTION_PROLOGUE target hook, since
+ it allows the scheduler to intermix instructions with the saves of
+ the caller saved registers. In some cases, it might be necessary
+ to emit a barrier instruction as the last insn to prevent such
+ scheduling.
Also any insns generated here should have RTX_FRAME_RELATED_P(insn) = 1
so that the debug info generation code can handle them properly. */
void
-frv_expand_prologue ()
+frv_expand_prologue (void)
{
frv_stack_t *info = frv_stack_info ();
rtx sp = stack_pointer_rtx;
if (info->stdarg_size > 0)
emit_insn (gen_blockage ());
- /* Set up pic register/small data register for this function. */
- if (flag_pic && cfun->uses_pic_offset_table)
+ /* Set up pic register/small data register for this function. */
+ if (!TARGET_FDPIC && flag_pic && cfun->uses_pic_offset_table)
emit_insn (gen_pic_prologue (gen_rtx_REG (Pmode, PIC_REGNO),
gen_rtx_REG (Pmode, LR_REGNO),
gen_rtx_REG (SImode, OFFSET_REGNO)));
\f
/* Under frv, all of the work is done via frv_expand_epilogue, but
- this function provides a convient place to do cleanup. */
+ this function provides a convenient place to do cleanup. */
static void
-frv_function_epilogue (file, size)
- FILE *file ATTRIBUTE_UNUSED;
- HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+frv_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT size ATTRIBUTE_UNUSED)
{
frv_stack_cache = (frv_stack_t *)0;
- /* zap last used registers for conditional execution. */
+ /* Zap last used registers for conditional execution. */
memset (&frv_ifcvt.tmp_reg, 0, sizeof (frv_ifcvt.tmp_reg));
- /* release the bitmap of created insns. */
- BITMAP_XFREE (frv_ifcvt.scratch_insns_bitmap);
+ /* Release the bitmap of created insns. */
+ BITMAP_FREE (frv_ifcvt.scratch_insns_bitmap);
}
\f
/* Called after register allocation to add any instructions needed for the
epilogue. Using an epilogue insn is favored compared to putting all of the
- instructions in the FUNCTION_PROLOGUE macro, since it allows the scheduler
- to intermix instructions with the saves of the caller saved registers. In
- some cases, it might be necessary to emit a barrier instruction as the last
- insn to prevent such scheduling.
-
- If SIBCALL_P is true, the final branch back to the calling function is
- omitted, and is used for sibling call (aka tail call) sites. For sibcalls,
- we must not clobber any arguments used for parameter passing or any stack
- slots for arguments passed to the current function. */
+ instructions in the TARGET_ASM_FUNCTION_PROLOGUE target hook, since
+ it allows the scheduler to intermix instructions with the saves of
+ the caller saved registers. In some cases, it might be necessary
+ to emit a barrier instruction as the last insn to prevent such
+ scheduling. */
void
-frv_expand_epilogue (sibcall_p)
- int sibcall_p;
+frv_expand_epilogue (bool emit_return)
{
frv_stack_t *info = frv_stack_info ();
rtx fp = frame_pointer_rtx;
/* Set RETURN_ADDR to the address we should return to. Set it to NULL if
no return instruction should be emitted. */
- if (sibcall_p)
- return_addr = 0;
- else if (info->save_p[LR_REGNO])
+ if (info->save_p[LR_REGNO])
{
int lr_offset;
rtx mem;
if (current_function_calls_eh_return)
emit_insn (gen_stack_adjust (sp, sp, EH_RETURN_STACKADJ_RTX));
- if (return_addr)
+ if (emit_return)
emit_jump_insn (gen_epilogue_return (return_addr));
+ else
+ {
+ rtx lr = return_addr;
+
+ if (REGNO (return_addr) != LR_REGNO)
+ {
+ lr = gen_rtx_REG (Pmode, LR_REGNO);
+ emit_move_insn (lr, return_addr);
+ }
+
+ emit_insn (gen_rtx_USE (VOIDmode, lr));
+ }
}
\f
-/* A C compound statement that outputs the assembler code for a thunk function,
- used to implement C++ virtual function calls with multiple inheritance. The
- thunk acts as a wrapper around a virtual function, adjusting the implicit
- object parameter before handing control off to the real function.
-
- First, emit code to add the integer DELTA to the location that contains the
- incoming first argument. Assume that this argument contains a pointer, and
- is the one used to pass the `this' pointer in C++. This is the incoming
- argument *before* the function prologue, e.g. `%o0' on a sparc. The
- addition must preserve the values of all other incoming arguments.
-
- After the addition, emit code to jump to FUNCTION, which is a
- `FUNCTION_DECL'. This is a direct pure jump, not a call, and does not touch
- the return address. Hence returning from FUNCTION will return to whoever
- called the current `thunk'.
-
- The effect must be as if FUNCTION had been called directly with the adjusted
- first argument. This macro is responsible for emitting all of the code for
- a thunk function; `FUNCTION_PROLOGUE' and `FUNCTION_EPILOGUE' are not
- invoked.
-
- The THUNK_FNDECL is redundant. (DELTA and FUNCTION have already been
- extracted from it.) It might possibly be useful on some targets, but
- probably not.
-
- If you do not define this macro, the target-independent code in the C++
- frontend will generate a less efficient heavyweight thunk that calls
- FUNCTION instead of jumping to it. The generic approach does not support
- varargs. */
+/* Worker function for TARGET_ASM_OUTPUT_MI_THUNK. */
static void
-frv_asm_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
- FILE *file;
- tree thunk_fndecl ATTRIBUTE_UNUSED;
- HOST_WIDE_INT delta;
- HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED;
- tree function;
+frv_asm_output_mi_thunk (FILE *file,
+ tree thunk_fndecl ATTRIBUTE_UNUSED,
+ HOST_WIDE_INT delta,
+ HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
+ tree function)
{
const char *name_func = XSTR (XEXP (DECL_RTL (function), 0), 0);
const char *name_arg0 = reg_names[FIRST_ARG_REGNUM];
const char *name_jmp = reg_names[JUMP_REGNO];
- const char *parallel = ((PACKING_FLAG_USED_P ()) ? ".p" : "");
+ const char *parallel = (frv_issue_rate () > 1 ? ".p" : "");
- /* Do the add using an addi if possible */
+ /* Do the add using an addi if possible. */
if (IN_RANGE_P (delta, -2048, 2047))
fprintf (file, "\taddi %s,#%d,%s\n", name_arg0, (int) delta, name_arg0);
else
fprintf (file, "\tadd %s,%s,%s\n", name_add, name_arg0, name_arg0);
}
- if (!flag_pic)
+ if (TARGET_FDPIC)
+ {
+ const char *name_pic = reg_names[FDPIC_REGNO];
+ name_jmp = reg_names[FDPIC_FPTR_REGNO];
+
+ if (flag_pic != 1)
+ {
+ fprintf (file, "\tsethi%s #gotofffuncdeschi(", parallel);
+ assemble_name (file, name_func);
+ fprintf (file, "),%s\n", name_jmp);
+
+ fprintf (file, "\tsetlo #gotofffuncdesclo(");
+ assemble_name (file, name_func);
+ fprintf (file, "),%s\n", name_jmp);
+
+ fprintf (file, "\tldd @(%s,%s), %s\n", name_jmp, name_pic, name_jmp);
+ }
+ else
+ {
+ fprintf (file, "\tlddo @(%s,#gotofffuncdesc12(", name_pic);
+ assemble_name (file, name_func);
+ fprintf (file, "\t)), %s\n", name_jmp);
+ }
+ }
+ else if (!flag_pic)
{
fprintf (file, "\tsethi%s #hi(", parallel);
assemble_name (file, name_func);
fprintf (file, "\tadd %s,%s,%s\n", name_gppic, name_tmp, name_jmp);
}
- /* Jump to the function address */
+ /* Jump to the function address. */
fprintf (file, "\tjmpl @(%s,%s)\n", name_jmp, reg_names[GPR_FIRST+0]);
}
register can be allocated for ordinary usage, unless you mark it as a fixed
register. See `FIXED_REGISTERS' for more information. */
-/* On frv, create a frame whenever we need to create stack */
+/* On frv, create a frame whenever we need to create stack. */
int
-frv_frame_pointer_required ()
+frv_frame_pointer_required (void)
{
+ /* If we forgoing the usual linkage requirements, we only need
+ a frame pointer if the stack pointer might change. */
+ if (!TARGET_LINKED_FP)
+ return !current_function_sp_is_unchanging;
+
if (! current_function_is_leaf)
return TRUE;
if (!current_function_sp_is_unchanging)
return TRUE;
- if (flag_pic && cfun->uses_pic_offset_table)
+ if (!TARGET_FDPIC && flag_pic && cfun->uses_pic_offset_table)
return TRUE;
if (profile_flag)
/* See frv_stack_info for more details on the frv stack frame. */
int
-frv_initial_elimination_offset (from, to)
- int from;
- int to;
+frv_initial_elimination_offset (int from, int to)
{
frv_stack_t *info = frv_stack_info ();
int ret = 0;
ret = info->total_size - info->pretend_size;
else if (to == STACK_POINTER_REGNUM && from == FRAME_POINTER_REGNUM)
- ret = - info->reg_offset[FRAME_POINTER_REGNUM];
+ ret = info->reg_offset[FRAME_POINTER_REGNUM];
else if (to == FRAME_POINTER_REGNUM && from == ARG_POINTER_REGNUM)
ret = (info->total_size
- info->pretend_size);
else
- abort ();
+ gcc_unreachable ();
if (TARGET_DEBUG_STACK)
fprintf (stderr, "Eliminate %s to %s by adding %d\n",
}
\f
-/* This macro offers an alternative to using `__builtin_saveregs' and defining
- the macro `EXPAND_BUILTIN_SAVEREGS'. Use it to store the anonymous register
- arguments into the stack so that all the arguments appear to have been
- passed consecutively on the stack. Once this is done, you can use the
- standard implementation of varargs that works for machines that pass all
- their arguments on the stack.
-
- The argument ARGS_SO_FAR is the `CUMULATIVE_ARGS' data structure, containing
- the values that obtain after processing of the named arguments. The
- arguments MODE and TYPE describe the last named argument--its machine mode
- and its data type as a tree node.
-
- The macro implementation should do two things: first, push onto the stack
- all the argument registers *not* used for the named arguments, and second,
- store the size of the data thus pushed into the `int'-valued variable whose
- name is supplied as the argument PRETEND_ARGS_SIZE. The value that you
- store here will serve as additional offset for setting up the stack frame.
-
- Because you must generate code to push the anonymous arguments at compile
- time without knowing their data types, `SETUP_INCOMING_VARARGS' is only
- useful on machines that have just a single category of argument register and
- use it uniformly for all data types.
-
- If the argument SECOND_TIME is nonzero, it means that the arguments of the
- function are being analyzed for the second time. This happens for an inline
- function, which is not actually compiled until the end of the source file.
- The macro `SETUP_INCOMING_VARARGS' should not generate any instructions in
- this case. */
+/* Worker function for TARGET_SETUP_INCOMING_VARARGS. */
-void
-frv_setup_incoming_varargs (cum, mode, type, pretend_size, second_time)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type ATTRIBUTE_UNUSED;
- int *pretend_size;
- int second_time;
+static void
+frv_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
+ enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED,
+ int *pretend_size,
+ int second_time)
{
if (TARGET_DEBUG_ARG)
fprintf (stderr,
}
\f
-/* If defined, is a C expression that produces the machine-specific code for a
- call to `__builtin_saveregs'. This code will be moved to the very beginning
- of the function, before any parameter access are made. The return value of
- this function should be an RTX that contains the value to use as the return
- of `__builtin_saveregs'.
-
- If this macro is not defined, the compiler will output an ordinary call to
- the library function `__builtin_saveregs'. */
+/* Worker function for TARGET_EXPAND_BUILTIN_SAVEREGS. */
-rtx
-frv_expand_builtin_saveregs ()
+static rtx
+frv_expand_builtin_saveregs (void)
{
int offset = UNITS_PER_WORD * FRV_NUM_ARG_REGS;
fprintf (stderr, "expand_builtin_saveregs: offset from ap = %d\n",
offset);
- return gen_rtx (PLUS, Pmode, virtual_incoming_args_rtx, GEN_INT (- offset));
+ return gen_rtx_PLUS (Pmode, virtual_incoming_args_rtx, GEN_INT (- offset));
}
\f
/* Expand __builtin_va_start to do the va_start macro. */
void
-frv_expand_builtin_va_start (valist, nextarg)
- tree valist;
- rtx nextarg;
+frv_expand_builtin_va_start (tree valist, rtx nextarg)
{
tree t;
int num = cfun->args_info - FIRST_ARG_REGNUM - FRV_NUM_ARG_REGS;
debug_rtx (nextarg);
}
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
- make_tree (ptr_type_node, nextarg));
+ t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (valist), valist,
+ make_tree (ptr_type_node, nextarg));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
}
\f
-/* Expand __builtin_va_arg to do the va_arg macro. */
-
-rtx
-frv_expand_builtin_va_arg(valist, type)
- tree valist;
- tree type;
-{
- rtx addr;
- rtx mem;
- rtx reg;
-
- if (TARGET_DEBUG_ARG)
- {
- fprintf (stderr, "va_arg:\n");
- debug_tree (type);
- }
-
- if (! AGGREGATE_TYPE_P (type))
- return std_expand_builtin_va_arg (valist, type);
-
- addr = std_expand_builtin_va_arg (valist, ptr_type_node);
- mem = gen_rtx_MEM (Pmode, addr);
- reg = gen_reg_rtx (Pmode);
-
- set_mem_alias_set (mem, get_varargs_alias_set ());
- emit_move_insn (reg, mem);
-
- return reg;
-}
-
-\f
/* Expand a block move operation, and return 1 if successful. Return 0
if we should let the compiler generate normal code.
#endif
int
-frv_expand_block_move (operands)
- rtx operands[];
+frv_expand_block_move (rtx operands[])
{
rtx orig_dest = operands[0];
rtx orig_src = operands[1];
int move_bytes;
enum machine_mode mode;
- /* If this is not a fixed size move, just call memcpy */
+ /* If this is not a fixed size move, just call memcpy. */
if (! constp)
return FALSE;
- /* If this is not a fixed size alignment, abort */
- if (GET_CODE (align_rtx) != CONST_INT)
- abort ();
+ /* This should be a fixed size alignment. */
+ gcc_assert (GET_CODE (align_rtx) == CONST_INT);
align = INTVAL (align_rtx);
num_reg = offset = 0;
for ( ; bytes > 0; (bytes -= move_bytes), (offset += move_bytes))
{
- /* Calculate the correct offset for src/dest */
+ /* Calculate the correct offset for src/dest. */
if (offset == 0)
{
src_addr = src_reg;
operands[0] is the destination
operands[1] is the length
- operands[2] is the alignment */
+ operands[3] is the alignment */
int
-frv_expand_block_clear (operands)
- rtx operands[];
+frv_expand_block_clear (rtx operands[])
{
rtx orig_dest = operands[0];
rtx bytes_rtx = operands[1];
- rtx align_rtx = operands[2];
+ rtx align_rtx = operands[3];
int constp = (GET_CODE (bytes_rtx) == CONST_INT);
int align;
int bytes;
int clear_bytes;
enum machine_mode mode;
- /* If this is not a fixed size move, just call memcpy */
+ /* If this is not a fixed size move, just call memcpy. */
if (! constp)
return FALSE;
- /* If this is not a fixed size alignment, abort */
- if (GET_CODE (align_rtx) != CONST_INT)
- abort ();
+ /* This should be a fixed size alignment. */
+ gcc_assert (GET_CODE (align_rtx) == CONST_INT);
align = INTVAL (align_rtx);
num_reg = offset = 0;
for ( ; bytes > 0; (bytes -= clear_bytes), (offset += clear_bytes))
{
- /* Calculate the correct offset for src/dest */
+ /* Calculate the correct offset for src/dest. */
dest_addr = ((offset == 0)
? dest_reg
: plus_constant (dest_reg, offset));
- /* Generate the appropriate store of gr0 */
+ /* Generate the appropriate store of gr0. */
if (bytes >= 4 && align >= 4)
mode = SImode;
else if (bytes >= 2 && align >= 2)
\f
/* The following variable is used to output modifiers of assembler
- code of the current output insn.. */
+ code of the current output insn. */
static rtx *frv_insn_operands;
/* The following function is used to add assembler insn code suffix .p
- if it is necessary. */
+ if it is necessary. */
const char *
-frv_asm_output_opcode (f, ptr)
- FILE *f;
- const char *ptr;
+frv_asm_output_opcode (FILE *f, const char *ptr)
{
int c;
- if (! PACKING_FLAG_USED_P())
+ if (frv_insn_packing_flag <= 0)
return ptr;
for (; *ptr && *ptr != ' ' && *ptr != '\t';)
fputc (c, f);
}
- if (!frv_insn_packing_flag)
- fprintf (f, ".p");
+ fprintf (f, ".p");
return ptr;
}
-/* The following function sets up the packing bit for the current
- output insn. Remember that the function is not called for asm
- insns. */
+/* Set up the packing bit for the current output insn. Note that this
+ function is not called for asm insns. */
void
-frv_final_prescan_insn (insn, opvec, noperands)
- rtx insn;
- rtx *opvec;
- int noperands ATTRIBUTE_UNUSED;
+frv_final_prescan_insn (rtx insn, rtx *opvec,
+ int noperands ATTRIBUTE_UNUSED)
{
- if (! PACKING_FLAG_USED_P())
- return;
-
- if (!INSN_P (insn))
- return;
-
- frv_insn_operands = opvec;
-
- /* Look for the next printable instruction. frv_pack_insns () has set
- things up so that any printable instruction will have TImode if it
- starts a new packet and VOIDmode if it should be packed with the
- previous instruction.
-
- Printable instructions will be asm_operands or match one of the .md
- patterns. Since asm instructions cannot be packed -- and will
- therefore have TImode -- this loop terminates on any recognizable
- instruction, and on any unrecognizable instruction with TImode. */
- for (insn = NEXT_INSN (insn); insn; insn = NEXT_INSN (insn))
+ if (INSN_P (insn))
{
- if (NOTE_P (insn))
- continue;
- else if (!INSN_P (insn))
- break;
- else if (GET_MODE (insn) == TImode || INSN_CODE (insn) != -1)
- break;
+ if (frv_insn_packing_flag >= 0)
+ {
+ frv_insn_operands = opvec;
+ frv_insn_packing_flag = PACKING_FLAG_P (insn);
+ }
+ else if (recog_memoized (insn) >= 0
+ && get_attr_acc_group (insn) == ACC_GROUP_ODD)
+ /* Packing optimizations have been disabled, but INSN can only
+ be issued in M1. Insert an mnop in M0. */
+ fprintf (asm_out_file, "\tmnop.p\n");
}
-
- /* Set frv_insn_packing_flag to FALSE if the next instruction should
- be packed with this one. Set it to TRUE otherwise. If the next
- instruction is an asm insntruction, this statement will set the
- flag to TRUE, and that value will still hold when the asm operands
- themselves are printed. */
- frv_insn_packing_flag = ! (insn && INSN_P (insn)
- && GET_MODE (insn) != TImode);
}
/* The default is correct, but we need to make sure the frame gets created. */
rtx
-frv_dynamic_chain_address (frame)
- rtx frame;
+frv_dynamic_chain_address (rtx frame)
{
cfun->machine->frame_needed = 1;
return frame;
address of other frames. */
rtx
-frv_return_addr_rtx (count, frame)
- int count ATTRIBUTE_UNUSED;
- rtx frame;
+frv_return_addr_rtx (int count, rtx frame)
{
+ if (count != 0)
+ return const0_rtx;
cfun->machine->frame_needed = 1;
return gen_rtx_MEM (Pmode, plus_constant (frame, 8));
}
GO_IF_LEGITIMATE_ADDRESS forbids register+register addresses, which
this function cannot handle. */
rtx
-frv_index_memory (memref, mode, index)
- rtx memref;
- enum machine_mode mode;
- int index;
+frv_index_memory (rtx memref, enum machine_mode mode, int index)
{
rtx base = XEXP (memref, 0);
if (GET_CODE (base) == PRE_MODIFY)
\f
/* Print a memory address as an operand to reference that memory location. */
void
-frv_print_operand_address (stream, x)
- FILE * stream;
- rtx x;
+frv_print_operand_address (FILE * stream, rtx x)
{
if (GET_CODE (x) == MEM)
x = XEXP (x, 0);
break;
}
- fatal_insn ("Bad insn to frv_print_operand_address:", x);
+ fatal_insn ("bad insn to frv_print_operand_address:", x);
}
\f
static void
-frv_print_operand_memory_reference_reg (stream, x)
- FILE *stream;
- rtx x;
+frv_print_operand_memory_reference_reg (FILE * stream, rtx x)
{
int regno = true_regnum (x);
if (GPR_P (regno))
fputs (reg_names[regno], stream);
else
- fatal_insn ("Bad register to frv_print_operand_memory_reference_reg:", x);
+ fatal_insn ("bad register to frv_print_operand_memory_reference_reg:", x);
}
/* Print a memory reference suitable for the ld/st instructions. */
static void
-frv_print_operand_memory_reference (stream, x, addr_offset)
- FILE *stream;
- rtx x;
- int addr_offset;
+frv_print_operand_memory_reference (FILE * stream, rtx x, int addr_offset)
{
+ struct frv_unspec unspec;
rtx x0 = NULL_RTX;
rtx x1 = NULL_RTX;
break;
default:
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
break;
}
if (!x1)
x1 = const0_rtx;
else if (GET_CODE (x1) != CONST_INT)
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
}
fputs ("@(", stream);
else if (GET_CODE (x0) == REG || GET_CODE (x0) == SUBREG)
frv_print_operand_memory_reference_reg (stream, x0);
else
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
fputs (",", stream);
if (!x1)
fprintf (stream, "%ld", (long) (INTVAL (x1) + addr_offset));
break;
- case SYMBOL_REF:
- if (x0 && GET_CODE (x0) == REG && REGNO (x0) == SDA_BASE_REG
- && SYMBOL_REF_SMALL_P (x1))
- {
- fputs ("#gprel12(", stream);
- assemble_name (stream, XSTR (x1, 0));
- fputs (")", stream);
- }
- else
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
- break;
-
case CONST:
- if (x0 && GET_CODE (x0) == REG && REGNO (x0) == SDA_BASE_REG
- && const_small_data_p (x1))
- {
- fputs ("#gprel12(", stream);
- assemble_name (stream, XSTR (XEXP (XEXP (x1, 0), 0), 0));
- fprintf (stream, "+"HOST_WIDE_INT_PRINT_DEC")",
- INTVAL (XEXP (XEXP (x1, 0), 1)));
- }
- else
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ if (!frv_const_unspec_p (x1, &unspec))
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x1);
+ frv_output_const_unspec (stream, &unspec);
break;
default:
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x);
+ fatal_insn ("bad insn to frv_print_operand_memory_reference:", x);
}
}
#define FRV_JUMP_NOT_LIKELY 0
static int
-frv_print_operand_jump_hint (insn)
- rtx insn;
+frv_print_operand_jump_hint (rtx insn)
{
rtx note;
rtx labelref;
HOST_WIDE_INT prob = -1;
enum { UNKNOWN, BACKWARD, FORWARD } jump_type = UNKNOWN;
- if (GET_CODE (insn) != JUMP_INSN)
- abort ();
+ gcc_assert (GET_CODE (insn) == JUMP_INSN);
/* Assume any non-conditional jump is likely. */
if (! any_condjump_p (insn))
}
\f
+/* Return the comparison operator to use for CODE given that the ICC
+ register is OP0. */
+
+static const char *
+comparison_string (enum rtx_code code, rtx op0)
+{
+ bool is_nz_p = GET_MODE (op0) == CC_NZmode;
+ switch (code)
+ {
+ default: output_operand_lossage ("bad condition code");
+ case EQ: return "eq";
+ case NE: return "ne";
+ case LT: return is_nz_p ? "n" : "lt";
+ case LE: return "le";
+ case GT: return "gt";
+ case GE: return is_nz_p ? "p" : "ge";
+ case LTU: return is_nz_p ? "no" : "c";
+ case LEU: return is_nz_p ? "eq" : "ls";
+ case GTU: return is_nz_p ? "ne" : "hi";
+ case GEU: return is_nz_p ? "ra" : "nc";
+ }
+}
+
/* Print an operand to an assembler instruction.
`%' followed by a letter and a digit says to output an operand in an
are valid with the `PRINT_OPERAND_PUNCT_VALID_P' macro. */
void
-frv_print_operand (file, x, code)
- FILE * file;
- rtx x;
- int code;
+frv_print_operand (FILE * file, rtx x, int code)
{
+ struct frv_unspec unspec;
HOST_WIDE_INT value;
int offset;
value = CONST_DOUBLE_LOW (x);
else
- fatal_insn ("Bad insn in frv_print_operand, bad const_double", x);
+ fatal_insn ("bad insn in frv_print_operand, bad const_double", x);
}
else
{
case '.':
- /* Output r0 */
+ /* Output r0. */
fputs (reg_names[GPR_R0], file);
break;
break;
case '@':
- /* Output small data area base register (gr16). */
+ /* Output small data area base register (gr16). */
fputs (reg_names[SDA_BASE_REG], file);
break;
case '~':
- /* Output pic register (gr17). */
+ /* Output pic register (gr17). */
fputs (reg_names[PIC_REGNO], file);
break;
case '*':
- /* Output the temporary integer CCR register */
+ /* Output the temporary integer CCR register. */
fputs (reg_names[ICR_TEMP], file);
break;
case '&':
- /* Output the temporary integer CC register */
+ /* Output the temporary integer CC register. */
fputs (reg_names[ICC_TEMP], file);
break;
- /* case 'a': print an address */
+ /* case 'a': print an address. */
case 'C':
- /* Print appropriate test for integer branch false operation */
- switch (GET_CODE (x))
- {
- default:
- fatal_insn ("Bad insn to frv_print_operand, 'C' modifier:", x);
-
- case EQ: fputs ("ne", file); break;
- case NE: fputs ("eq", file); break;
- case LT: fputs ("ge", file); break;
- case LE: fputs ("gt", file); break;
- case GT: fputs ("le", file); break;
- case GE: fputs ("lt", file); break;
- case LTU: fputs ("nc", file); break;
- case LEU: fputs ("hi", file); break;
- case GTU: fputs ("ls", file); break;
- case GEU: fputs ("c", file); break;
- }
+ /* Print appropriate test for integer branch false operation. */
+ fputs (comparison_string (reverse_condition (GET_CODE (x)),
+ XEXP (x, 0)), file);
break;
- /* case 'c': print a constant without the constant prefix. If
- CONSTANT_ADDRESS_P(x) is not true, PRINT_OPERAND is called. */
-
case 'c':
- /* Print appropriate test for integer branch true operation */
- switch (GET_CODE (x))
- {
- default:
- fatal_insn ("Bad insn to frv_print_operand, 'c' modifier:", x);
-
- case EQ: fputs ("eq", file); break;
- case NE: fputs ("ne", file); break;
- case LT: fputs ("lt", file); break;
- case LE: fputs ("le", file); break;
- case GT: fputs ("gt", file); break;
- case GE: fputs ("ge", file); break;
- case LTU: fputs ("c", file); break;
- case LEU: fputs ("ls", file); break;
- case GTU: fputs ("hi", file); break;
- case GEU: fputs ("nc", file); break;
- }
+ /* Print appropriate test for integer branch true operation. */
+ fputs (comparison_string (GET_CODE (x), XEXP (x, 0)), file);
break;
case 'e':
fputs ("0", file);
else
- fatal_insn ("Bad insn to frv_print_operand, 'e' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'e' modifier:", x);
break;
case 'F':
- /* Print appropriate test for floating point branch false operation */
+ /* Print appropriate test for floating point branch false operation. */
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'F' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'F' modifier:", x);
case EQ: fputs ("ne", file); break;
case NE: fputs ("eq", file); break;
break;
case 'f':
- /* Print appropriate test for floating point branch true operation */
+ /* Print appropriate test for floating point branch true operation. */
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'f' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'f' modifier:", x);
case EQ: fputs ("eq", file); break;
case NE: fputs ("ne", file); break;
}
break;
+ case 'g':
+ /* Print appropriate GOT function. */
+ if (GET_CODE (x) != CONST_INT)
+ fatal_insn ("bad insn to frv_print_operand, 'g' modifier:", x);
+ fputs (unspec_got_name (INTVAL (x)), file);
+ break;
+
case 'I':
/* Print 'i' if the operand is a constant, or is a memory reference that
- adds a constant */
+ adds a constant. */
if (GET_CODE (x) == MEM)
x = ((GET_CODE (XEXP (x, 0)) == PLUS)
? XEXP (XEXP (x, 0), 1)
: XEXP (x, 0));
+ else if (GET_CODE (x) == PLUS)
+ x = XEXP (x, 1);
switch (GET_CODE (x))
{
case 'i':
/* For jump instructions, print 'i' if the operand is a constant or
- is an expression that adds a constant */
+ is an expression that adds a constant. */
if (GET_CODE (x) == CONST_INT)
fputs ("i", file);
if (GET_CODE (x) == REG)
fputs (reg_names[ REGNO (x)+1 ], file);
else
- fatal_insn ("Bad insn to frv_print_operand, 'L' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'L' modifier:", x);
break;
- /* case 'l': print a LABEL_REF */
+ /* case 'l': print a LABEL_REF. */
case 'M':
case 'N':
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'M/N' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'M/N' modifier:", x);
case MEM:
frv_print_operand_memory_reference (file, XEXP (x, 0), offset);
switch (GET_CODE (x))
{
default:
- fatal_insn ("Bad insn to frv_print_operand, 'O' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'O' modifier:", x);
case PLUS: fputs ("add", file); break;
case MINUS: fputs ("sub", file); break;
}
break;
- /* case 'n': negate and print a constant int */
+ /* case 'n': negate and print a constant int. */
case 'P':
/* Print PIC label using operand as the number. */
if (GET_CODE (x) != CONST_INT)
- fatal_insn ("Bad insn to frv_print_operand, P modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, P modifier:", x);
fprintf (file, ".LCF%ld", (long)INTVAL (x));
break;
case 'U':
- /* Print 'u' if the operand is a update load/store */
+ /* Print 'u' if the operand is a update load/store. */
if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PRE_MODIFY)
fputs ("u", file);
break;
case 'z':
- /* If value is 0, print gr0, otherwise it must be a register */
+ /* If value is 0, print gr0, otherwise it must be a register. */
if (GET_CODE (x) == CONST_INT && INTVAL (x) == 0)
fputs (reg_names[GPR_R0], file);
fputs (reg_names [REGNO (x)], file);
else
- fatal_insn ("Bad insn in frv_print_operand, z case", x);
+ fatal_insn ("bad insn in frv_print_operand, z case", x);
break;
case 'x':
- /* Print constant in hex */
+ /* Print constant in hex. */
if (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE)
{
fprintf (file, "%s0x%.4lx", IMMEDIATE_PREFIX, (long) value);
break;
}
- /* fall through */
+ /* Fall through. */
case '\0':
if (GET_CODE (x) == REG)
|| GET_CODE (x) == CONST_DOUBLE)
fprintf (file, "%s%ld", IMMEDIATE_PREFIX, (long) value);
+ else if (frv_const_unspec_p (x, &unspec))
+ frv_output_const_unspec (file, &unspec);
+
else if (GET_CODE (x) == MEM)
frv_print_operand_address (file, XEXP (x, 0));
frv_print_operand_address (file, x);
else
- fatal_insn ("Bad insn in frv_print_operand, 0 case", x);
+ fatal_insn ("bad insn in frv_print_operand, 0 case", x);
break;
FNTYPE is nonzero, but never both of them at once. */
void
-frv_init_cumulative_args (cum, fntype, libname, fndecl, incoming)
- CUMULATIVE_ARGS *cum;
- tree fntype;
- rtx libname;
- tree fndecl;
- int incoming;
+frv_init_cumulative_args (CUMULATIVE_ARGS *cum,
+ tree fntype,
+ rtx libname,
+ tree fndecl,
+ int incoming)
{
*cum = FIRST_ARG_REGNUM;
}
\f
+/* Return true if we should pass an argument on the stack rather than
+ in registers. */
+
+static bool
+frv_must_pass_in_stack (enum machine_mode mode, tree type)
+{
+ if (mode == BLKmode)
+ return true;
+ if (type == NULL)
+ return false;
+ return AGGREGATE_TYPE_P (type);
+}
+
/* If defined, a C expression that gives the alignment boundary, in bits, of an
argument with the specified mode and type. If it is not defined,
`PARM_BOUNDARY' is used for all arguments. */
int
-frv_function_arg_boundary (mode, type)
- enum machine_mode mode ATTRIBUTE_UNUSED;
- tree type ATTRIBUTE_UNUSED;
+frv_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
+ tree type ATTRIBUTE_UNUSED)
{
return BITS_PER_WORD;
}
-\f
-/* A C expression that controls whether a function argument is passed in a
- register, and which register.
-
- The arguments are CUM, of type CUMULATIVE_ARGS, which summarizes (in a way
- defined by INIT_CUMULATIVE_ARGS and FUNCTION_ARG_ADVANCE) all of the previous
- arguments so far passed in registers; MODE, the machine mode of the argument;
- TYPE, the data type of the argument as a tree node or 0 if that is not known
- (which happens for C support library functions); and NAMED, which is 1 for an
- ordinary argument and 0 for nameless arguments that correspond to `...' in the
- called function's prototype.
-
- The value of the expression should either be a `reg' RTX for the hard
- register in which to pass the argument, or zero to pass the argument on the
- stack.
-
- For machines like the VAX and 68000, where normally all arguments are
- pushed, zero suffices as a definition.
-
- The usual way to make the ANSI library `stdarg.h' work on a machine where
- some arguments are usually passed in registers, is to cause nameless
- arguments to be passed on the stack instead. This is done by making
- `FUNCTION_ARG' return 0 whenever NAMED is 0.
-
- You may use the macro `MUST_PASS_IN_STACK (MODE, TYPE)' in the definition of
- this macro to determine if this argument is of a type that must be passed in
- the stack. If `REG_PARM_STACK_SPACE' is not defined and `FUNCTION_ARG'
- returns nonzero for such an argument, the compiler will abort. If
- `REG_PARM_STACK_SPACE' is defined, the argument will be computed in the
- stack and then loaded into a register. */
-
rtx
-frv_function_arg (cum, mode, type, named, incoming)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type ATTRIBUTE_UNUSED;
- int named;
- int incoming ATTRIBUTE_UNUSED;
+frv_function_arg (CUMULATIVE_ARGS *cum,
+ enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED,
+ int named,
+ int incoming ATTRIBUTE_UNUSED)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int arg_num = *cum;
else if (arg_num <= LAST_ARG_REGNUM)
{
- ret = gen_rtx (REG, xmode, arg_num);
+ ret = gen_rtx_REG (xmode, arg_num);
debstr = reg_names[arg_num];
}
for arguments without any special help. */
void
-frv_function_arg_advance (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type ATTRIBUTE_UNUSED;
- int named;
+frv_function_arg_advance (CUMULATIVE_ARGS *cum,
+ enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED,
+ int named)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int bytes = GET_MODE_SIZE (xmode);
used by the caller for this argument; likewise `FUNCTION_INCOMING_ARG', for
the called function. */
-int
-frv_function_arg_partial_nregs (cum, mode, type, named)
- CUMULATIVE_ARGS *cum;
- enum machine_mode mode;
- tree type ATTRIBUTE_UNUSED;
- int named ATTRIBUTE_UNUSED;
+static int
+frv_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ tree type ATTRIBUTE_UNUSED, bool named ATTRIBUTE_UNUSED)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int bytes = GET_MODE_SIZE (xmode);
ret = ((arg_num <= LAST_ARG_REGNUM && arg_num + words > LAST_ARG_REGNUM+1)
? LAST_ARG_REGNUM - arg_num + 1
: 0);
+ ret *= UNITS_PER_WORD;
if (TARGET_DEBUG_ARG && ret)
- fprintf (stderr, "function_arg_partial_nregs: %d\n", ret);
+ fprintf (stderr, "frv_arg_partial_bytes: %d\n", ret);
return ret;
-
-}
-
-\f
-
-/* A C expression that indicates when an argument must be passed by reference.
- If nonzero for an argument, a copy of that argument is made in memory and a
- pointer to the argument is passed instead of the argument itself. The
- pointer is passed in whatever way is appropriate for passing a pointer to
- that type.
-
- On machines where `REG_PARM_STACK_SPACE' is not defined, a suitable
- definition of this macro might be
- #define FUNCTION_ARG_PASS_BY_REFERENCE(CUM, MODE, TYPE, NAMED) \
- MUST_PASS_IN_STACK (MODE, TYPE) */
-
-int
-frv_function_arg_pass_by_reference (cum, mode, type, named)
- CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
- enum machine_mode mode;
- tree type;
- int named ATTRIBUTE_UNUSED;
-{
- return MUST_PASS_IN_STACK (mode, type);
-}
-
-/* If defined, a C expression that indicates when it is the called function's
- responsibility to make a copy of arguments passed by invisible reference.
- Normally, the caller makes a copy and passes the address of the copy to the
- routine being called. When FUNCTION_ARG_CALLEE_COPIES is defined and is
- nonzero, the caller does not make a copy. Instead, it passes a pointer to
- the "live" value. The called function must not modify this value. If it
- can be determined that the value won't be modified, it need not make a copy;
- otherwise a copy must be made. */
-
-int
-frv_function_arg_callee_copies (cum, mode, type, named)
- CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- tree type ATTRIBUTE_UNUSED;
- int named ATTRIBUTE_UNUSED;
-{
- return 0;
-}
-
-/* If defined, a C expression that indicates when it is more desirable to keep
- an argument passed by invisible reference as a reference, rather than
- copying it to a pseudo register. */
-
-int
-frv_function_arg_keep_as_reference (cum, mode, type, named)
- CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- tree type ATTRIBUTE_UNUSED;
- int named ATTRIBUTE_UNUSED;
-{
- return 0;
}
\f
/* Return true if a register is ok to use as a base or index register. */
static FRV_INLINE int
-frv_regno_ok_for_base_p (regno, strict_p)
- int regno;
- int strict_p;
+frv_regno_ok_for_base_p (int regno, int strict_p)
{
if (GPR_P (regno))
return TRUE;
`PRINT_OPERAND_ADDRESS'. */
int
-frv_legitimate_address_p (mode, x, strict_p, condexec_p)
- enum machine_mode mode;
- rtx x;
- int strict_p;
- int condexec_p;
+frv_legitimate_address_p (enum machine_mode mode,
+ rtx x,
+ int strict_p,
+ int condexec_p,
+ int allow_double_reg_p)
{
rtx x0, x1;
int ret = 0;
HOST_WIDE_INT value;
unsigned regno0;
+ if (FRV_SYMBOL_REF_TLS_P (x))
+ return 0;
+
switch (GET_CODE (x))
{
default:
if (GET_CODE (x) != REG)
break;
- /* fall through */
+ /* Fall through. */
case REG:
ret = frv_regno_ok_for_base_p (REGNO (x), strict_p);
break;
case CONST_INT:
- /* 12 bit immediate */
+ /* 12-bit immediate */
if (condexec_p)
ret = FALSE;
else
if (GET_CODE (x1) != REG)
break;
- /* fall through */
+ /* Fall through. */
case REG:
- /* Do not allow reg+reg addressing for modes > 1 word if we can't depend
- on having move double instructions */
- if (GET_MODE_SIZE (mode) > UNITS_PER_WORD)
+ /* Do not allow reg+reg addressing for modes > 1 word if we
+ can't depend on having move double instructions. */
+ if (!allow_double_reg_p && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
ret = FALSE;
else
ret = frv_regno_ok_for_base_p (REGNO (x1), strict_p);
break;
case CONST_INT:
- /* 12 bit immediate */
+ /* 12-bit immediate */
if (condexec_p)
ret = FALSE;
else
}
break;
- case SYMBOL_REF:
- if (!condexec_p
- && regno0 == SDA_BASE_REG
- && SYMBOL_REF_SMALL_P (x1))
- ret = TRUE;
- break;
-
case CONST:
- if (!condexec_p && regno0 == SDA_BASE_REG && const_small_data_p (x1))
+ if (!condexec_p && got12_operand (x1, VOIDmode))
ret = TRUE;
break;
return ret;
}
-\f
-/* A C compound statement that attempts to replace X with a valid memory
- address for an operand of mode MODE. WIN will be a C statement label
- elsewhere in the code; the macro definition may use
-
- GO_IF_LEGITIMATE_ADDRESS (MODE, X, WIN);
-
- to avoid further processing if the address has become legitimate.
+/* Given an ADDR, generate code to inline the PLT. */
+static rtx
+gen_inlined_tls_plt (rtx addr)
+{
+ rtx retval, dest;
+ rtx picreg = get_hard_reg_initial_val (Pmode, FDPIC_REG);
- X will always be the result of a call to `break_out_memory_refs', and OLDX
- will be the operand that was given to that function to produce X.
- The code generated by this macro should not alter the substructure of X. If
- it transforms X into a more legitimate form, it should assign X (which will
- always be a C variable) a new value.
+ dest = gen_reg_rtx (DImode);
- It is not necessary for this macro to come up with a legitimate address.
- The compiler has standard ways of doing so in all cases. In fact, it is
- safe for this macro to do nothing. But often a machine-dependent strategy
- can generate better code. */
+ if (flag_pic == 1)
+ {
+ /*
+ -fpic version:
-rtx
-frv_legitimize_address (x, oldx, mode)
- rtx x;
- rtx oldx ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- rtx ret = NULL_RTX;
-
- /* Don't try to legitimize addresses if we are not optimizing, since the
- address we generate is not a general operand, and will horribly mess
- things up when force_reg is called to try and put it in a register because
- we aren't optimizing. */
- if (optimize
- && ((GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_SMALL_P (x))
- || (GET_CODE (x) == CONST && const_small_data_p (x))))
- {
- ret = gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode, SDA_BASE_REG), x);
- if (flag_pic)
- cfun->uses_pic_offset_table = TRUE;
+ lddi.p @(gr15, #gottlsdesc12(ADDR)), gr8
+ calll #gettlsoff(ADDR)@(gr8, gr0)
+ */
+ emit_insn (gen_tls_lddi (dest, addr, picreg));
}
-
- if (TARGET_DEBUG_ADDR && ret != NULL_RTX)
+ else
{
- fprintf (stderr, "\n========== LEGITIMIZE_ADDRESS, mode = %s, modified address\n",
- GET_MODE_NAME (mode));
- debug_rtx (ret);
- }
-
- return ret;
-}
+ /*
+ -fPIC version:
-/* Return 1 if operand is a valid FRV address. CONDEXEC_P is true if
- the operand is used by a predicated instruction. */
+ sethi.p #gottlsdeschi(ADDR), gr8
+ setlo #gottlsdesclo(ADDR), gr8
+ ldd #tlsdesc(ADDR)@(gr15, gr8), gr8
+ calll #gettlsoff(ADDR)@(gr8, gr0)
+ */
+ rtx reguse = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (reguse, addr, GEN_INT (R_FRV_GOTTLSDESCHI)));
+ emit_insn (gen_tls_tlsdesc_ldd (dest, picreg, reguse, addr));
+ }
-static int
-frv_legitimate_memory_operand (op, mode, condexec_p)
- rtx op;
- enum machine_mode mode;
- int condexec_p;
-{
- return ((GET_MODE (op) == mode || mode == VOIDmode)
- && GET_CODE (op) == MEM
- && frv_legitimate_address_p (mode, XEXP (op, 0),
- reload_completed, condexec_p));
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_tls_indirect_call (retval, addr, dest, picreg));
+ return retval;
}
-\f
-/* Return 1 is OP is a memory operand, or will be turned into one by
- reload. */
-
-int frv_load_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+/* Emit a TLSMOFF or TLSMOFF12 offset, depending on -mTLS. Returns
+ the destination address. */
+static rtx
+gen_tlsmoff (rtx addr, rtx reg)
{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ rtx dest = gen_reg_rtx (Pmode);
- if (reload_in_progress)
+ if (TARGET_BIG_TLS)
{
- rtx tmp = op;
- if (GET_CODE (tmp) == SUBREG)
- tmp = SUBREG_REG (tmp);
- if (GET_CODE (tmp) == REG
- && REGNO (tmp) >= FIRST_PSEUDO_REGISTER)
- op = reg_equiv_memory_loc[REGNO (tmp)];
+ /* sethi.p #tlsmoffhi(x), grA
+ setlo #tlsmofflo(x), grA
+ */
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (dest, addr,
+ GEN_INT (R_FRV_TLSMOFFHI)));
+ dest = gen_rtx_PLUS (Pmode, dest, reg);
}
-
- return op && memory_operand (op, mode);
-}
-
-
-/* Return 1 if operand is a GPR register or a FPR register. */
-
-int gpr_or_fpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
+ else
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
+ /* addi grB, #tlsmoff12(x), grC
+ -or-
+ ld/st @(grB, #tlsmoff12(x)), grC
+ */
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_symGOTOFF2reg_i (dest, addr, reg,
+ GEN_INT (R_FRV_TLSMOFF12)));
}
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- return FALSE;
+ return dest;
}
-/* Return 1 if operand is a GPR register or 12 bit signed immediate. */
-
-int gpr_or_int12_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+/* Generate code for a TLS address. */
+static rtx
+frv_legitimize_tls_address (rtx addr, enum tls_model model)
{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
+ rtx dest, tp = gen_rtx_REG (Pmode, 29);
+ rtx picreg = get_hard_reg_initial_val (Pmode, 15);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
+ switch (model)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a GPR register, or a FPR register, or a 12 bit
- signed immediate. */
+ case TLS_MODEL_INITIAL_EXEC:
+ if (flag_pic == 1)
+ {
+ /* -fpic version.
+ ldi @(gr15, #gottlsoff12(x)), gr5
+ */
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tls_load_gottlsoff12 (dest, addr, picreg));
+ dest = gen_rtx_PLUS (Pmode, tp, dest);
+ }
+ else
+ {
+ /* -fPIC or anything else.
+
+ sethi.p #gottlsoffhi(x), gr14
+ setlo #gottlsofflo(x), gr14
+ ld #tlsoff(x)@(gr15, gr14), gr9
+ */
+ rtx tmp = gen_reg_rtx (Pmode);
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (tmp, addr,
+ GEN_INT (R_FRV_GOTTLSOFF_HI)));
+
+ emit_insn (gen_tls_tlsoff_ld (dest, picreg, tmp, addr));
+ dest = gen_rtx_PLUS (Pmode, tp, dest);
+ }
+ break;
+ case TLS_MODEL_LOCAL_DYNAMIC:
+ {
+ rtx reg, retval;
-int gpr_fpr_or_int12_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
+ if (TARGET_INLINE_PLT)
+ retval = gen_inlined_tls_plt (GEN_INT (0));
+ else
+ {
+ /* call #gettlsoff(0) */
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_call_gettlsoff (retval, GEN_INT (0), picreg));
+ }
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
+ reg = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, reg,
+ gen_rtx_PLUS (Pmode,
+ retval, tp)));
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ dest = gen_tlsmoff (addr, reg);
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ /*
+ dest = gen_reg_rtx (Pmode);
+ emit_insn (gen_tlsoff_hilo (dest, addr,
+ GEN_INT (R_FRV_TLSMOFFHI)));
+ dest = gen_rtx_PLUS (Pmode, dest, reg);
+ */
+ break;
+ }
+ case TLS_MODEL_LOCAL_EXEC:
+ dest = gen_tlsmoff (addr, gen_rtx_REG (Pmode, 29));
+ break;
+ case TLS_MODEL_GLOBAL_DYNAMIC:
+ {
+ rtx retval;
- op = SUBREG_REG (op);
+ if (TARGET_INLINE_PLT)
+ retval = gen_inlined_tls_plt (addr);
+ else
+ {
+ /* call #gettlsoff(x) */
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_call_gettlsoff (retval, addr, picreg));
+ }
+ dest = gen_rtx_PLUS (Pmode, retval, tp);
+ break;
+ }
+ default:
+ gcc_unreachable ();
}
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- return FALSE;
+ return dest;
}
-/* Return 1 if operand is a register or 6 bit signed immediate. */
-
-int fpr_or_int6_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+rtx
+frv_legitimize_address (rtx x,
+ rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -32, 31);
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
+ if (GET_CODE (x) == SYMBOL_REF)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (x);
+ if (model != 0)
+ return frv_legitimize_tls_address (x, model);
}
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return FPR_OR_PSEUDO_P (REGNO (op));
+ return NULL_RTX;
}
+\f
+/* Test whether a local function descriptor is canonical, i.e.,
+ whether we can use FUNCDESC_GOTOFF to compute the address of the
+ function. */
-/* Return 1 if operand is a register or 10 bit signed immediate. */
-
-int gpr_or_int10_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+static bool
+frv_local_funcdesc_p (rtx fnx)
{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -512, 511);
+ tree fn;
+ enum symbol_visibility vis;
+ bool ret;
- if (GET_MODE (op) != mode && mode != VOIDmode)
+ if (! SYMBOL_REF_LOCAL_P (fnx))
return FALSE;
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
+ fn = SYMBOL_REF_DECL (fnx);
- if (GET_CODE (op) != REG)
+ if (! fn)
return FALSE;
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a register or an integer immediate. */
+ vis = DECL_VISIBILITY (fn);
-int gpr_or_int_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) == CONST_INT)
+ if (vis == VISIBILITY_PROTECTED)
+ /* Private function descriptors for protected functions are not
+ canonical. Temporarily change the visibility to global. */
+ vis = VISIBILITY_DEFAULT;
+ else if (flag_shlib)
+ /* If we're already compiling for a shared library (that, unlike
+ executables, can't assume that the existence of a definition
+ implies local binding), we can skip the re-testing. */
return TRUE;
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
+ ret = default_binds_local_p_1 (fn, flag_pic);
- if (GET_CODE (op) != REG)
- return FALSE;
+ DECL_VISIBILITY (fn) = vis;
- return GPR_OR_PSEUDO_P (REGNO (op));
+ return ret;
}
-/* Return 1 if operand is a 12 bit signed immediate. */
+/* Load the _gp symbol into DEST. SRC is supposed to be the FDPIC
+ register. */
-int int12_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
+rtx
+frv_gen_GPsym2reg (rtx dest, rtx src)
+{
+ tree gp = get_identifier ("_gp");
+ rtx gp_sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (gp));
+
+ return gen_symGOT2reg (dest, gp_sym, src, GEN_INT (R_FRV_GOT12));
+}
+
+static const char *
+unspec_got_name (int i)
+{
+ switch (i)
+ {
+ case R_FRV_GOT12: return "got12";
+ case R_FRV_GOTHI: return "gothi";
+ case R_FRV_GOTLO: return "gotlo";
+ case R_FRV_FUNCDESC: return "funcdesc";
+ case R_FRV_FUNCDESC_GOT12: return "gotfuncdesc12";
+ case R_FRV_FUNCDESC_GOTHI: return "gotfuncdeschi";
+ case R_FRV_FUNCDESC_GOTLO: return "gotfuncdesclo";
+ case R_FRV_FUNCDESC_VALUE: return "funcdescvalue";
+ case R_FRV_FUNCDESC_GOTOFF12: return "gotofffuncdesc12";
+ case R_FRV_FUNCDESC_GOTOFFHI: return "gotofffuncdeschi";
+ case R_FRV_FUNCDESC_GOTOFFLO: return "gotofffuncdesclo";
+ case R_FRV_GOTOFF12: return "gotoff12";
+ case R_FRV_GOTOFFHI: return "gotoffhi";
+ case R_FRV_GOTOFFLO: return "gotofflo";
+ case R_FRV_GPREL12: return "gprel12";
+ case R_FRV_GPRELHI: return "gprelhi";
+ case R_FRV_GPRELLO: return "gprello";
+ case R_FRV_GOTTLSOFF_HI: return "gottlsoffhi";
+ case R_FRV_GOTTLSOFF_LO: return "gottlsofflo";
+ case R_FRV_TLSMOFFHI: return "tlsmoffhi";
+ case R_FRV_TLSMOFFLO: return "tlsmofflo";
+ case R_FRV_TLSMOFF12: return "tlsmoff12";
+ case R_FRV_TLSDESCHI: return "tlsdeschi";
+ case R_FRV_TLSDESCLO: return "tlsdesclo";
+ case R_FRV_GOTTLSDESCHI: return "gottlsdeschi";
+ case R_FRV_GOTTLSDESCLO: return "gottlsdesclo";
+ default: gcc_unreachable ();
+ }
+}
+
+/* Write the assembler syntax for UNSPEC to STREAM. Note that any offset
+ is added inside the relocation operator. */
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
+static void
+frv_output_const_unspec (FILE *stream, const struct frv_unspec *unspec)
+{
+ fprintf (stream, "#%s(", unspec_got_name (unspec->reloc));
+ output_addr_const (stream, plus_constant (unspec->symbol, unspec->offset));
+ fputs (")", stream);
}
-/* Return 1 if operand is a 6 bit signed immediate. */
+/* Implement FIND_BASE_TERM. See whether ORIG_X represents #gprel12(foo)
+ or #gotoff12(foo) for some small data symbol foo. If so, return foo,
+ otherwise return ORIG_X. */
-int int6_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+rtx
+frv_find_base_term (rtx x)
{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
+ struct frv_unspec unspec;
- return IN_RANGE_P (INTVAL (op), -32, 31);
-}
-
-/* Return 1 if operand is a 5 bit signed immediate. */
+ if (frv_const_unspec_p (x, &unspec)
+ && frv_small_data_reloc_p (unspec.symbol, unspec.reloc))
+ return plus_constant (unspec.symbol, unspec.offset);
-int int5_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), -16, 15);
+ return x;
}
-/* Return 1 if operand is a 5 bit unsigned immediate. */
+/* Return 1 if operand is a valid FRV address. CONDEXEC_P is true if
+ the operand is used by a predicated instruction. */
-int uint5_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+int
+frv_legitimate_memory_operand (rtx op, enum machine_mode mode, int condexec_p)
{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 31);
+ return ((GET_MODE (op) == mode || mode == VOIDmode)
+ && GET_CODE (op) == MEM
+ && frv_legitimate_address_p (mode, XEXP (op, 0),
+ reload_completed, condexec_p, FALSE));
}
-/* Return 1 if operand is a 4 bit unsigned immediate. */
-
-int uint4_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+void
+frv_expand_fdpic_call (rtx *operands, bool ret_value, bool sibcall)
{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 15);
-}
+ rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
+ rtx picreg = get_hard_reg_initial_val (SImode, FDPIC_REG);
+ rtx c, rvrtx=0;
+ rtx addr;
-/* Return 1 if operand is a 1 bit unsigned immediate (0 or 1). */
+ if (ret_value)
+ {
+ rvrtx = operands[0];
+ operands ++;
+ }
+
+ addr = XEXP (operands[0], 0);
+
+ /* Inline PLTs if we're optimizing for speed. We'd like to inline
+ any calls that would involve a PLT, but can't tell, since we
+ don't know whether an extern function is going to be provided by
+ a separate translation unit or imported from a separate module.
+ When compiling for shared libraries, if the function has default
+ visibility, we assume it's overridable, so we inline the PLT, but
+ for executables, we don't really have a way to make a good
+ decision: a function is as likely to be imported from a shared
+ library as it is to be defined in the executable itself. We
+ assume executables will get global functions defined locally,
+ whereas shared libraries will have them potentially overridden,
+ so we only inline PLTs when compiling for shared libraries.
+
+ In order to mark a function as local to a shared library, any
+ non-default visibility attribute suffices. Unfortunately,
+ there's no simple way to tag a function declaration as ``in a
+ different module'', which we could then use to trigger PLT
+ inlining on executables. There's -minline-plt, but it affects
+ all external functions, so one would have to also mark function
+ declarations available in the same module with non-default
+ visibility, which is advantageous in itself. */
+ if (GET_CODE (addr) == SYMBOL_REF
+ && ((!SYMBOL_REF_LOCAL_P (addr) && TARGET_INLINE_PLT)
+ || sibcall))
+ {
+ rtx x, dest;
+ dest = gen_reg_rtx (SImode);
+ if (flag_pic != 1)
+ x = gen_symGOTOFF2reg_hilo (dest, addr, OUR_FDPIC_REG,
+ GEN_INT (R_FRV_FUNCDESC_GOTOFF12));
+ else
+ x = gen_symGOTOFF2reg (dest, addr, OUR_FDPIC_REG,
+ GEN_INT (R_FRV_FUNCDESC_GOTOFF12));
+ emit_insn (x);
+ cfun->uses_pic_offset_table = TRUE;
+ addr = dest;
+ }
+ else if (GET_CODE (addr) == SYMBOL_REF)
+ {
+ /* These are always either local, or handled through a local
+ PLT. */
+ if (ret_value)
+ c = gen_call_value_fdpicsi (rvrtx, addr, operands[1],
+ operands[2], picreg, lr);
+ else
+ c = gen_call_fdpicsi (addr, operands[1], operands[2], picreg, lr);
+ emit_call_insn (c);
+ return;
+ }
+ else if (! ldd_address_operand (addr, Pmode))
+ addr = force_reg (Pmode, addr);
-int uint1_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 1);
-}
+ picreg = gen_reg_rtx (DImode);
+ emit_insn (gen_movdi_ldd (picreg, addr));
-/* Return 1 if operand is an integer constant that takes 2 instructions
- to load up and can be split into sethi/setlo instructions.. */
+ if (sibcall && ret_value)
+ c = gen_sibcall_value_fdpicdi (rvrtx, picreg, const0_rtx);
+ else if (sibcall)
+ c = gen_sibcall_fdpicdi (picreg, const0_rtx);
+ else if (ret_value)
+ c = gen_call_value_fdpicdi (rvrtx, picreg, const0_rtx, lr);
+ else
+ c = gen_call_fdpicdi (picreg, const0_rtx, lr);
+ emit_call_insn (c);
+}
+\f
+/* Look for a SYMBOL_REF of a function in an rtx. We always want to
+ process these separately from any offsets, such that we add any
+ offsets to the function descriptor (the actual pointer), not to the
+ function address. */
-int int_2word_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+static bool
+frv_function_symbol_referenced_p (rtx x)
{
- HOST_WIDE_INT value;
- REAL_VALUE_TYPE rv;
- long l;
-
- switch (GET_CODE (op))
- {
- default:
- break;
+ const char *format;
+ int length;
+ int j;
- case LABEL_REF:
- return (flag_pic == 0);
+ if (GET_CODE (x) == SYMBOL_REF)
+ return SYMBOL_REF_FUNCTION_P (x);
- case CONST:
- /* small data references are already 1 word */
- return (flag_pic == 0) && (! const_small_data_p (op));
+ length = GET_RTX_LENGTH (GET_CODE (x));
+ format = GET_RTX_FORMAT (GET_CODE (x));
- case SYMBOL_REF:
- /* small data references are already 1 word */
- return (flag_pic == 0) && (! SYMBOL_REF_SMALL_P (op));
+ for (j = 0; j < length; ++j)
+ {
+ switch (format[j])
+ {
+ case 'e':
+ if (frv_function_symbol_referenced_p (XEXP (x, j)))
+ return TRUE;
+ break;
- case CONST_INT:
- return ! IN_RANGE_P (INTVAL (op), -32768, 32767);
+ case 'V':
+ case 'E':
+ if (XVEC (x, j) != 0)
+ {
+ int k;
+ for (k = 0; k < XVECLEN (x, j); ++k)
+ if (frv_function_symbol_referenced_p (XVECEXP (x, j, k)))
+ return TRUE;
+ }
+ break;
- case CONST_DOUBLE:
- if (GET_MODE (op) == SFmode)
- {
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
- value = l;
- return ! IN_RANGE_P (value, -32768, 32767);
- }
- else if (GET_MODE (op) == VOIDmode)
- {
- value = CONST_DOUBLE_LOW (op);
- return ! IN_RANGE_P (value, -32768, 32767);
+ default:
+ /* Nothing to do. */
+ break;
}
- break;
}
return FALSE;
}
-/* Return 1 if operand is the pic address register. */
+/* Return true if the memory operand is one that can be conditionally
+ executed. */
+
int
-pic_register_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+condexec_memory_operand (rtx op, enum machine_mode mode)
{
- if (! flag_pic)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- if (REGNO (op) != PIC_REGNO)
- return FALSE;
-
- return TRUE;
-}
-
-/* Return 1 if operand is a symbolic reference when a PIC option is specified
- that takes 3 seperate instructions to form. */
+ enum machine_mode op_mode = GET_MODE (op);
+ rtx addr;
-int pic_symbolic_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (! flag_pic)
+ if (mode != VOIDmode && op_mode != mode)
return FALSE;
- switch (GET_CODE (op))
+ switch (op_mode)
{
default:
- break;
-
- case LABEL_REF:
- return TRUE;
-
- case SYMBOL_REF:
- /* small data references are already 1 word */
- return ! SYMBOL_REF_SMALL_P (op);
+ return FALSE;
- case CONST:
- /* small data references are already 1 word */
- return ! const_small_data_p (op);
+ case QImode:
+ case HImode:
+ case SImode:
+ case SFmode:
+ break;
}
- return FALSE;
+ if (GET_CODE (op) != MEM)
+ return FALSE;
+
+ addr = XEXP (op, 0);
+ return frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE);
}
+\f
+/* Return true if the bare return instruction can be used outside of the
+ epilog code. For frv, we only do it if there was no stack allocation. */
-/* Return 1 if operand is the small data register. */
int
-small_data_register_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+direct_return_p (void)
{
- if (GET_CODE (op) != REG)
- return FALSE;
+ frv_stack_t *info;
- if (REGNO (op) != SDA_BASE_REG)
+ if (!reload_completed)
return FALSE;
- return TRUE;
+ info = frv_stack_info ();
+ return (info->total_size == 0);
}
-/* Return 1 if operand is a symbolic reference to a small data area static or
- global object. */
-
-int small_data_symbolic_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
+\f
+void
+frv_emit_move (enum machine_mode mode, rtx dest, rtx src)
{
- switch (GET_CODE (op))
+ if (GET_CODE (src) == SYMBOL_REF)
{
- default:
- break;
-
- case CONST:
- return const_small_data_p (op);
-
- case SYMBOL_REF:
- return SYMBOL_REF_SMALL_P (op);
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (src);
+ if (model != 0)
+ src = frv_legitimize_tls_address (src, model);
}
- return FALSE;
-}
-
-/* Return 1 if operand is a 16 bit unsigned immediate */
-
-int uint16_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return IN_RANGE_P (INTVAL (op), 0, 0xffff);
-}
+ switch (mode)
+ {
+ case SImode:
+ if (frv_emit_movsi (dest, src))
+ return;
+ break;
-/* Return 1 if operand is an integer constant with the bottom 16 bits clear */
+ case QImode:
+ case HImode:
+ case DImode:
+ case SFmode:
+ case DFmode:
+ if (!reload_in_progress
+ && !reload_completed
+ && !register_operand (dest, mode)
+ && !reg_or_0_operand (src, mode))
+ src = copy_to_mode_reg (mode, src);
+ break;
-int upper_int16_operand (op, mode)
- rtx op;
- enum machine_mode mode ATTRIBUTE_UNUSED;
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
+ default:
+ gcc_unreachable ();
+ }
- return ((INTVAL (op) & 0xffff) == 0);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, src));
}
-/* Return true if operand is a GPR register. */
+/* Emit code to handle a MOVSI, adding in the small data register or pic
+ register if needed to load up addresses. Return TRUE if the appropriate
+ instructions are emitted. */
int
-integer_register_operand (op, mode)
- rtx op;
- enum machine_mode mode;
+frv_emit_movsi (rtx dest, rtx src)
{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return true if operand is a GPR register. Do not allow SUBREG's
- here, in order to prevent a combine bug. */
-
-int
-gpr_no_subreg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return true if operand is a FPR register. */
-
-int
-fpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return FPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return true if operand is an even GPR or FPR register. */
-
-int
-even_reg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (GPR_P (regno))
- return (((regno - GPR_FIRST) & 1) == 0);
-
- if (FPR_P (regno))
- return (((regno - FPR_FIRST) & 1) == 0);
-
- return FALSE;
-}
-
-/* Return true if operand is an odd GPR register. */
-
-int
-odd_reg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- /* assume that reload will give us an even register */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- if (GPR_P (regno))
- return (((regno - GPR_FIRST) & 1) != 0);
-
- if (FPR_P (regno))
- return (((regno - FPR_FIRST) & 1) != 0);
-
- return FALSE;
-}
-
-/* Return true if operand is an even GPR register. */
-
-int
-even_gpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (! GPR_P (regno))
- return FALSE;
-
- return (((regno - GPR_FIRST) & 1) == 0);
-}
-
-/* Return true if operand is an odd GPR register. */
-
-int
-odd_gpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- /* assume that reload will give us an even register */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- if (! GPR_P (regno))
- return FALSE;
-
- return (((regno - GPR_FIRST) & 1) != 0);
-}
-
-/* Return true if operand is a quad aligned FPR register. */
-
-int
-quad_fpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (! FPR_P (regno))
- return FALSE;
-
- return (((regno - FPR_FIRST) & 3) == 0);
-}
-
-/* Return true if operand is an even FPR register. */
-
-int
-even_fpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- if (! FPR_P (regno))
- return FALSE;
-
- return (((regno - FPR_FIRST) & 1) == 0);
-}
-
-/* Return true if operand is an odd FPR register. */
-
-int
-odd_fpr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- /* assume that reload will give us an even register */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- if (! FPR_P (regno))
- return FALSE;
-
- return (((regno - FPR_FIRST) & 1) != 0);
-}
-
-/* Return true if operand is a 2 word memory address that can be loaded in one
- instruction to load or store. We assume the stack and frame pointers are
- suitably aligned, and variables in the small data area. FIXME -- at some we
- should recognize other globals and statics. We can't assume that any old
- pointer is aligned, given that arguments could be passed on an odd word on
- the stack and the address taken and passed through to another function. */
-
-int
-dbl_memory_one_insn_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx addr;
- rtx addr_reg;
-
- if (! TARGET_DWORD)
- return FALSE;
-
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
- return FALSE;
-
- addr = XEXP (op, 0);
- if (GET_CODE (addr) == REG)
- addr_reg = addr;
-
- else if (GET_CODE (addr) == PLUS)
- {
- rtx addr0 = XEXP (addr, 0);
- rtx addr1 = XEXP (addr, 1);
-
- if (GET_CODE (addr0) != REG)
- return FALSE;
-
- if (plus_small_data_p (addr0, addr1))
- return TRUE;
-
- if (GET_CODE (addr1) != CONST_INT)
- return FALSE;
-
- if ((INTVAL (addr1) & 7) != 0)
- return FALSE;
-
- addr_reg = addr0;
- }
-
- else
- return FALSE;
-
- if (addr_reg == frame_pointer_rtx || addr_reg == stack_pointer_rtx)
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operand is a 2 word memory address that needs to
- use two instructions to load or store. */
-
-int
-dbl_memory_two_insn_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
- return FALSE;
-
- if (! TARGET_DWORD)
- return TRUE;
-
- return ! dbl_memory_one_insn_operand (op, mode);
-}
-
-/* Return true if operand is something that can be an output for a move
- operation. */
-
-int
-move_destination_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, FALSE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
- return TRUE;
-
- return frv_legitimate_memory_operand (op, mode, FALSE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is something that can be an input for a move
- operation. */
-
-int
-move_source_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case CONST_INT:
- case CONST_DOUBLE:
- case SYMBOL_REF:
- case LABEL_REF:
- case CONST:
- return immediate_operand (op, mode);
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, FALSE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
- return TRUE;
-
- return frv_legitimate_memory_operand (op, mode, FALSE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is something that can be an output for a conditional
- move operation. */
-
-int
-condexec_dest_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, TRUE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
- return TRUE;
-
- return frv_legitimate_memory_operand (op, mode, TRUE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is something that can be an input for a conditional
- move operation. */
-
-int
-condexec_source_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case CONST_INT:
- case CONST_DOUBLE:
- return ZERO_P (op);
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, TRUE);
-
- return (code == REG);
-
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return TRUE;
-
- case MEM:
- if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
- return TRUE;
-
- return frv_legitimate_memory_operand (op, mode, TRUE);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is a register of any flavor or a 0 of the
- appropriate type. */
-
-int
-reg_or_0_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- switch (GET_CODE (op))
- {
- default:
- break;
-
- case REG:
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- return register_operand (op, mode);
-
- case CONST_INT:
- case CONST_DOUBLE:
- return ZERO_P (op);
- }
-
- return FALSE;
-}
-
-/* Return true if operand is the link register */
-
-int
-lr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_CODE (op) != REG)
- return FALSE;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (REGNO (op) != LR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
-
- return TRUE;
-}
-
-/* Return true if operand is a gpr register or a valid memory operation. */
-
-int
-gpr_or_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- return (integer_register_operand (op, mode)
- || frv_legitimate_memory_operand (op, mode, FALSE));
-}
-
-/* Return true if operand is a fpr register or a valid memory operation. */
-
-int
-fpr_or_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- return (fpr_operand (op, mode)
- || frv_legitimate_memory_operand (op, mode, FALSE));
-}
-
-/* Return true if operand is an icc register */
-
-int
-icc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return ICC_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is an fcc register */
-
-int
-fcc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return FCC_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is either an fcc or icc register */
-
-int
-cc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (CC_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operand is an integer CCR register */
-
-int
-icr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return ICR_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is an fcc register */
-
-int
-fcr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return FCR_OR_PSEUDO_P (regno);
-}
-
-/* Return true if operand is either an fcc or icc register */
-
-int
-cr_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- if (CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operand is a memory reference suitable for a call. */
-
-int
-call_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
- return FALSE;
-
- if (GET_CODE (op) == SYMBOL_REF)
- return TRUE;
-
- /* Note this doesn't allow reg+reg or reg+imm12 addressing (which should
- never occur anyway), but prevents reload from not handling the case
- properly of a call through a pointer on a function that calls
- vfork/setjmp, etc. due to the need to flush all of the registers to stack. */
- return gpr_or_int12_operand (op, mode);
-}
-
-/* Return true if operator is a kind of relational operator. */
-
-int
-relational_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ:
- case NE:
- case LE:
- case LT:
- case GE:
- case GT:
- case LEU:
- case LTU:
- case GEU:
- case GTU:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- switch (GET_MODE (op0))
- {
- default:
- break;
-
- case CCmode:
- case CC_UNSmode:
- return ICC_OR_PSEUDO_P (regno);
-
- case CC_FPmode:
- return FCC_OR_PSEUDO_P (regno);
-
- case CC_CCRmode:
- return CR_OR_PSEUDO_P (regno);
- }
-
- return FALSE;
-}
-
-/* Return true if operator is a signed integer relational operator */
-
-int
-signed_relational_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ:
- case NE:
- case LE:
- case LT:
- case GE:
- case GT:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (GET_MODE (op0) == CCmode && ICC_OR_PSEUDO_P (regno))
- return TRUE;
-
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is a signed integer relational operator */
-
-int
-unsigned_relational_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case LEU:
- case LTU:
- case GEU:
- case GTU:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (GET_MODE (op0) == CC_UNSmode && ICC_OR_PSEUDO_P (regno))
- return TRUE;
-
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is a floating point relational operator */
-
-int
-float_relational_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ: case NE:
- case LE: case LT:
- case GE: case GT:
-#if 0
- case UEQ: case UNE:
- case ULE: case ULT:
- case UGE: case UGT:
- case ORDERED:
- case UNORDERED:
-#endif
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (GET_MODE (op0) == CC_FPmode && FCC_OR_PSEUDO_P (regno))
- return TRUE;
-
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is EQ/NE of a conditional execution register. */
-
-int
-ccr_eqne_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case EQ:
- case NE:
- break;
- }
-
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
-
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
-
- regno = REGNO (op0);
- if (op_mode == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
-
- return FALSE;
-}
-
-/* Return true if operator is a minimum or maximum operator (both signed and
- unsigned). */
-
-int
-minmax_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case SMIN:
- case SMAX:
- case UMIN:
- case UMAX:
- break;
- }
-
- if (! integer_register_operand (XEXP (op, 0), mode))
- return FALSE;
-
- if (! gpr_or_int10_operand (XEXP (op, 1), mode))
- return FALSE;
-
- return TRUE;
-}
-
-/* Return true if operator is an integer binary operator that can executed
- conditionally and takes 1 cycle. */
-
-int
-condexec_si_binary_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case PLUS:
- case MINUS:
- case AND:
- case IOR:
- case XOR:
- case ASHIFT:
- case ASHIFTRT:
- case LSHIFTRT:
- return TRUE;
- }
-}
-
-/* Return true if operator is an integer binary operator that can be
- executed conditionally by a media instruction. */
-
-int
-condexec_si_media_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case AND:
- case IOR:
- case XOR:
- return TRUE;
- }
-}
-
-/* Return true if operator is an integer division operator that can executed
- conditionally. */
-
-int
-condexec_si_divide_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case DIV:
- case UDIV:
- return TRUE;
- }
-}
-
-/* Return true if operator is an integer unary operator that can executed
- conditionally. */
-
-int
-condexec_si_unary_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case NEG:
- case NOT:
- return TRUE;
- }
-}
-
-/* Return true if operator is a conversion-type expression that can be
- evaluated conditionally by floating-point instructions. */
-
-int
-condexec_sf_conv_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case NEG:
- case ABS:
- return TRUE;
- }
-}
-
-/* Return true if operator is an addition or subtraction expression.
- Such expressions can be evaluated conditionally by floating-point
- instructions. */
-
-int
-condexec_sf_add_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case PLUS:
- case MINUS:
- return TRUE;
- }
-}
-
-/* Return true if the memory operand is one that can be conditionally
- executed. */
-
-int
-condexec_memory_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
- rtx addr;
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (op_mode)
- {
- default:
- return FALSE;
-
- case QImode:
- case HImode:
- case SImode:
- case SFmode:
- break;
- }
-
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- addr = XEXP (op, 0);
- if (GET_CODE (addr) == ADDRESSOF)
- return TRUE;
-
- return frv_legitimate_address_p (mode, addr, reload_completed, TRUE);
-}
-
-/* Return true if operator is an integer binary operator that can be combined
- with a setcc operation. Do not allow the arithmetic operations that could
- potentially overflow since the FR-V sets the condition code based on the
- "true" value of the result, not the result after truncating to a 32-bit
- register. */
-
-int
-intop_compare_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case AND:
- case IOR:
- case XOR:
- case ASHIFTRT:
- case LSHIFTRT:
- break;
- }
-
- if (! integer_register_operand (XEXP (op, 0), SImode))
- return FALSE;
-
- if (! gpr_or_int10_operand (XEXP (op, 1), SImode))
- return FALSE;
-
- return TRUE;
-}
-
-/* Return true if operator is an integer binary operator that can be combined
- with a setcc operation inside of a conditional execution. */
-
-int
-condexec_intop_cmp_operator (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- enum machine_mode op_mode = GET_MODE (op);
-
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
-
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
-
- case AND:
- case IOR:
- case XOR:
- case ASHIFTRT:
- case LSHIFTRT:
- break;
- }
-
- if (! integer_register_operand (XEXP (op, 0), SImode))
- return FALSE;
-
- if (! integer_register_operand (XEXP (op, 1), SImode))
- return FALSE;
-
- return TRUE;
-}
-
-/* Return 1 if operand is a valid ACC register number */
-
-int
-acc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return ACC_OR_PSEUDO_P (regno);
-}
-
-/* Return 1 if operand is a valid even ACC register number */
-
-int
-even_acc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return (ACC_OR_PSEUDO_P (regno) && ((regno - ACC_FIRST) & 1) == 0);
-}
-
-/* Return 1 if operand is zero or four */
-
-int
-quad_acc_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- regno = REGNO (op);
- return (ACC_OR_PSEUDO_P (regno) && ((regno - ACC_FIRST) & 3) == 0);
-}
-
-/* Return 1 if operand is a valid ACCG register number */
-
-int
-accg_operand (op, mode)
- rtx op;
- enum machine_mode mode;
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return ACCG_OR_PSEUDO_P (REGNO (op));
-}
-
-\f
-/* Return true if the bare return instruction can be used outside of the
- epilog code. For frv, we only do it if there was no stack allocation. */
-
-int
-direct_return_p ()
-{
- frv_stack_t *info;
-
- if (!reload_completed)
- return FALSE;
-
- info = frv_stack_info ();
- return (info->total_size == 0);
-}
-
-\f
-/* Emit code to handle a MOVSI, adding in the small data register or pic
- register if needed to load up addresses. Return TRUE if the appropriate
- instructions are emitted. */
-
-int
-frv_emit_movsi (dest, src)
- rtx dest;
- rtx src;
-{
- int base_regno = -1;
+ int base_regno = -1;
+ int unspec = 0;
+ rtx sym = src;
+ struct frv_unspec old_unspec;
if (!reload_in_progress
&& !reload_completed
&& (!reg_or_0_operand (src, SImode)
/* Virtual registers will almost always be replaced by an
add instruction, so expose this to CSE by copying to
- an intermediate register */
+ an intermediate register. */
|| (GET_CODE (src) == REG
&& IN_RANGE_P (REGNO (src),
FIRST_VIRTUAL_REGISTER,
break;
case LABEL_REF:
- if (flag_pic)
+ handle_label:
+ if (TARGET_FDPIC)
+ {
+ /* Using GPREL12, we use a single GOT entry for all symbols
+ in read-only sections, but trade sequences such as:
+
+ sethi #gothi(label), gr#
+ setlo #gotlo(label), gr#
+ ld @(gr15,gr#), gr#
+
+ for
+
+ ld @(gr15,#got12(_gp)), gr#
+ sethi #gprelhi(label), gr##
+ setlo #gprello(label), gr##
+ add gr#, gr##, gr##
+
+ We may often be able to share gr# for multiple
+ computations of GPREL addresses, and we may often fold
+ the final add into the pair of registers of a load or
+ store instruction, so it's often profitable. Even when
+ optimizing for size, we're trading a GOT entry for an
+ additional instruction, which trades GOT space
+ (read-write) for code size (read-only, shareable), as
+ long as the symbol is not used in more than two different
+ locations.
+
+ With -fpie/-fpic, we'd be trading a single load for a
+ sequence of 4 instructions, because the offset of the
+ label can't be assumed to be addressable with 12 bits, so
+ we don't do this. */
+ if (TARGET_GPREL_RO)
+ unspec = R_FRV_GPREL12;
+ else
+ unspec = R_FRV_GOT12;
+ }
+ else if (flag_pic)
base_regno = PIC_REGNO;
break;
case CONST:
- if (const_small_data_p (src))
- base_regno = SDA_BASE_REG;
-
- else if (flag_pic)
- base_regno = PIC_REGNO;
+ if (frv_const_unspec_p (src, &old_unspec))
+ break;
+ if (TARGET_FDPIC && frv_function_symbol_referenced_p (XEXP (src, 0)))
+ {
+ handle_whatever:
+ src = force_reg (GET_MODE (XEXP (src, 0)), XEXP (src, 0));
+ emit_move_insn (dest, src);
+ return TRUE;
+ }
+ else
+ {
+ sym = XEXP (sym, 0);
+ if (GET_CODE (sym) == PLUS
+ && GET_CODE (XEXP (sym, 0)) == SYMBOL_REF
+ && GET_CODE (XEXP (sym, 1)) == CONST_INT)
+ sym = XEXP (sym, 0);
+ if (GET_CODE (sym) == SYMBOL_REF)
+ goto handle_sym;
+ else if (GET_CODE (sym) == LABEL_REF)
+ goto handle_label;
+ else
+ goto handle_whatever;
+ }
break;
- case SYMBOL_REF:
- if (SYMBOL_REF_SMALL_P (src))
+ case SYMBOL_REF:
+ handle_sym:
+ if (TARGET_FDPIC)
+ {
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (sym);
+
+ if (model != 0)
+ {
+ src = frv_legitimize_tls_address (src, model);
+ emit_move_insn (dest, src);
+ return TRUE;
+ }
+
+ if (SYMBOL_REF_FUNCTION_P (sym))
+ {
+ if (frv_local_funcdesc_p (sym))
+ unspec = R_FRV_FUNCDESC_GOTOFF12;
+ else
+ unspec = R_FRV_FUNCDESC_GOT12;
+ }
+ else
+ {
+ if (CONSTANT_POOL_ADDRESS_P (sym))
+ switch (GET_CODE (get_pool_constant (sym)))
+ {
+ case CONST:
+ case SYMBOL_REF:
+ case LABEL_REF:
+ if (flag_pic)
+ {
+ unspec = R_FRV_GOTOFF12;
+ break;
+ }
+ /* Fall through. */
+ default:
+ if (TARGET_GPREL_RO)
+ unspec = R_FRV_GPREL12;
+ else
+ unspec = R_FRV_GOT12;
+ break;
+ }
+ else if (SYMBOL_REF_LOCAL_P (sym)
+ && !SYMBOL_REF_EXTERNAL_P (sym)
+ && SYMBOL_REF_DECL (sym)
+ && (!DECL_P (SYMBOL_REF_DECL (sym))
+ || !DECL_COMMON (SYMBOL_REF_DECL (sym))))
+ {
+ tree decl = SYMBOL_REF_DECL (sym);
+ tree init = TREE_CODE (decl) == VAR_DECL
+ ? DECL_INITIAL (decl)
+ : TREE_CODE (decl) == CONSTRUCTOR
+ ? decl : 0;
+ int reloc = 0;
+ bool named_section, readonly;
+
+ if (init && init != error_mark_node)
+ reloc = compute_reloc_for_constant (init);
+
+ named_section = TREE_CODE (decl) == VAR_DECL
+ && lookup_attribute ("section", DECL_ATTRIBUTES (decl));
+ readonly = decl_readonly_section (decl, reloc);
+
+ if (named_section)
+ unspec = R_FRV_GOT12;
+ else if (!readonly)
+ unspec = R_FRV_GOTOFF12;
+ else if (readonly && TARGET_GPREL_RO)
+ unspec = R_FRV_GPREL12;
+ else
+ unspec = R_FRV_GOT12;
+ }
+ else
+ unspec = R_FRV_GOT12;
+ }
+ }
+
+ else if (SYMBOL_REF_SMALL_P (sym))
base_regno = SDA_BASE_REG;
else if (flag_pic)
if (base_regno >= 0)
{
- emit_insn (gen_rtx_SET (VOIDmode, dest,
- gen_rtx_PLUS (Pmode,
- gen_rtx_REG (Pmode, base_regno),
- src)));
-
+ if (GET_CODE (sym) == SYMBOL_REF && SYMBOL_REF_SMALL_P (sym))
+ emit_insn (gen_symGOTOFF2reg (dest, src,
+ gen_rtx_REG (Pmode, base_regno),
+ GEN_INT (R_FRV_GPREL12)));
+ else
+ emit_insn (gen_symGOTOFF2reg_hilo (dest, src,
+ gen_rtx_REG (Pmode, base_regno),
+ GEN_INT (R_FRV_GPREL12)));
if (base_regno == PIC_REGNO)
cfun->uses_pic_offset_table = TRUE;
+ return TRUE;
+ }
+ if (unspec)
+ {
+ rtx x;
+
+ /* Since OUR_FDPIC_REG is a pseudo register, we can't safely introduce
+ new uses of it once reload has begun. */
+ gcc_assert (!reload_in_progress && !reload_completed);
+
+ switch (unspec)
+ {
+ case R_FRV_GOTOFF12:
+ if (!frv_small_data_reloc_p (sym, unspec))
+ x = gen_symGOTOFF2reg_hilo (dest, src, OUR_FDPIC_REG,
+ GEN_INT (unspec));
+ else
+ x = gen_symGOTOFF2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
+ break;
+ case R_FRV_GPREL12:
+ if (!frv_small_data_reloc_p (sym, unspec))
+ x = gen_symGPREL2reg_hilo (dest, src, OUR_FDPIC_REG,
+ GEN_INT (unspec));
+ else
+ x = gen_symGPREL2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
+ break;
+ case R_FRV_FUNCDESC_GOTOFF12:
+ if (flag_pic != 1)
+ x = gen_symGOTOFF2reg_hilo (dest, src, OUR_FDPIC_REG,
+ GEN_INT (unspec));
+ else
+ x = gen_symGOTOFF2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
+ break;
+ default:
+ if (flag_pic != 1)
+ x = gen_symGOT2reg_hilo (dest, src, OUR_FDPIC_REG,
+ GEN_INT (unspec));
+ else
+ x = gen_symGOT2reg (dest, src, OUR_FDPIC_REG, GEN_INT (unspec));
+ break;
+ }
+ emit_insn (x);
+ cfun->uses_pic_offset_table = TRUE;
return TRUE;
}
+
return FALSE;
}
/* Return a string to output a single word move. */
const char *
-output_move_single (operands, insn)
- rtx operands[];
- rtx insn;
+output_move_single (rtx operands[], rtx insn)
{
rtx dest = operands[0];
rtx src = operands[1];
|| GET_CODE (src) == LABEL_REF
|| GET_CODE (src) == CONST)
{
- /* Silently fix up instances where the small data pointer is not
- used in the address. */
- if (small_data_symbolic_operand (src, GET_MODE (src)))
- return "addi %@, #gprel12(%1), %0";
-
return "#";
}
}
if (GPR_P (src_regno))
return "movgs %1, %0";
}
+ else if (ZERO_P (src))
+ return "movgs %., %0";
}
}
}
}
- fatal_insn ("Bad output_move_single operand", insn);
+ fatal_insn ("bad output_move_single operand", insn);
return "";
}
/* Return a string to output a double word move. */
const char *
-output_move_double (operands, insn)
- rtx operands[];
- rtx insn;
+output_move_double (rtx operands[], rtx insn)
{
rtx dest = operands[0];
rtx src = operands[1];
}
}
- fatal_insn ("Bad output_move_double operand", insn);
+ fatal_insn ("bad output_move_double operand", insn);
return "";
}
Operand3 -- source */
const char *
-output_condmove_single (operands, insn)
- rtx operands[];
- rtx insn;
+output_condmove_single (rtx operands[], rtx insn)
{
rtx dest = operands[2];
rtx src = operands[3];
}
}
- fatal_insn ("Bad output_condmove_single operand", insn);
+ fatal_insn ("bad output_condmove_single operand", insn);
return "";
}
comparison was done it. */
static rtx
-frv_emit_comparison (test, op0, op1)
- enum rtx_code test;
- rtx op0;
- rtx op1;
+frv_emit_comparison (enum rtx_code test, rtx op0, rtx op1)
{
enum machine_mode cc_mode;
rtx cc_reg;
- /* Floating point doesn't have comparison against a constant */
+ /* Floating point doesn't have comparison against a constant. */
if (GET_MODE (op0) == CC_FPmode && GET_CODE (op1) != REG)
op1 = force_reg (GET_MODE (op0), op1);
conditional execution, but that confuses the rest of the compiler. */
int
-frv_emit_cond_branch (test, label)
- enum rtx_code test;
- rtx label;
+frv_emit_cond_branch (enum rtx_code test, rtx label)
{
rtx test_rtx;
rtx label_ref;
(label_ref <branch_label>)
(pc))) */
label_ref = gen_rtx_LABEL_REF (VOIDmode, label);
- test_rtx = gen_rtx (test, cc_mode, cc_reg, const0_rtx);
+ test_rtx = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx);
if_else = gen_rtx_IF_THEN_ELSE (cc_mode, test_rtx, label_ref, pc_rtx);
emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx, if_else));
return TRUE;
operands were previously stored in frv_compare_op0 and frv_compare_op1. */
int
-frv_emit_scc (test, target)
- enum rtx_code test;
- rtx target;
+frv_emit_scc (enum rtx_code test, rtx target)
{
rtx set;
rtx test_rtx;
\f
/* Split a SCC instruction into component parts, returning a SEQUENCE to hold
- the seperate insns. */
+ the separate insns. */
rtx
-frv_split_scc (dest, test, cc_reg, cr_reg, value)
- rtx dest;
- rtx test;
- rtx cc_reg;
- rtx cr_reg;
- HOST_WIDE_INT value;
+frv_split_scc (rtx dest, rtx test, rtx cc_reg, rtx cr_reg, HOST_WIDE_INT value)
{
rtx ret;
move. */
int
-frv_emit_cond_move (dest, test_rtx, src1, src2)
- rtx dest;
- rtx test_rtx;
- rtx src1;
- rtx src2;
+frv_emit_cond_move (rtx dest, rtx test_rtx, rtx src1, rtx src2)
{
rtx set;
rtx clobber_cc;
HOST_WIDE_INT value1 = INTVAL (src1);
HOST_WIDE_INT value2 = INTVAL (src2);
- /* having 0 as one of the constants can be done by loading the other
+ /* Having 0 as one of the constants can be done by loading the other
constant, and optionally moving in gr0. */
if (value1 == 0 || value2 == 0)
;
}
\f
-/* Split a conditonal move into constituent parts, returning a SEQUENCE
+/* Split a conditional move into constituent parts, returning a SEQUENCE
containing all of the insns. */
rtx
-frv_split_cond_move (operands)
- rtx operands[];
+frv_split_cond_move (rtx operands[])
{
rtx dest = operands[0];
rtx test = operands[1];
HOST_WIDE_INT value1 = INTVAL (src1);
HOST_WIDE_INT value2 = INTVAL (src2);
- /* having 0 as one of the constants can be done by loading the other
+ /* Having 0 as one of the constants can be done by loading the other
constant, and optionally moving in gr0. */
if (value1 == 0)
{
}
else
- abort ();
+ gcc_unreachable ();
}
else
{
/* Split (set DEST SOURCE), where DEST is a double register and SOURCE is a
memory location that is not known to be dword-aligned. */
void
-frv_split_double_load (dest, source)
- rtx dest;
- rtx source;
+frv_split_double_load (rtx dest, rtx source)
{
int regno = REGNO (dest);
rtx dest1 = gen_highpart (SImode, dest);
/* Split (set DEST SOURCE), where DEST refers to a dword memory location
and SOURCE is either a double register or the constant zero. */
void
-frv_split_double_store (dest, source)
- rtx dest;
- rtx source;
+frv_split_double_store (rtx dest, rtx source)
{
rtx dest1 = change_address (dest, SImode, NULL);
rtx dest2 = frv_index_memory (dest, SImode, 1);
insns. */
rtx
-frv_split_minmax (operands)
- rtx operands[];
+frv_split_minmax (rtx operands[])
{
rtx dest = operands[0];
rtx minmax = operands[1];
start_sequence ();
- /* Figure out which test to use */
+ /* Figure out which test to use. */
switch (GET_CODE (minmax))
{
default:
- abort ();
+ gcc_unreachable ();
case SMIN: test_code = LT; break;
case SMAX: test_code = GT; break;
then do a conditional move of the other value. */
if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
{
- if (rtx_equal_p (dest, src1))
- abort ();
+ gcc_assert (!rtx_equal_p (dest, src1));
emit_move_insn (dest, src2);
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
insns. */
rtx
-frv_split_abs (operands)
- rtx operands[];
+frv_split_abs (rtx operands[])
{
rtx dest = operands[0];
rtx src = operands[1];
cr_reg,
gen_rtx_fmt_ee (LT, CC_CCRmode, cc_reg, const0_rtx)));
- /* Emit the conditional negate if the value is negative */
+ /* Emit the conditional negate if the value is negative. */
emit_insn (gen_rtx_COND_EXEC (VOIDmode,
gen_rtx_NE (CC_CCRmode, cr_reg, const0_rtx),
gen_negsi2 (dest, src)));
register used in an insn. */
static int
-frv_clear_registers_used (ptr, data)
- rtx *ptr;
- void *data;
+frv_clear_registers_used (rtx *ptr, void *data)
{
if (GET_CODE (*ptr) == REG)
{
/* On the FR-V, we don't have any extra fields per se, but it is useful hook to
initialize the static storage. */
void
-frv_ifcvt_init_extra_fields (ce_info)
- ce_if_block_t *ce_info ATTRIBUTE_UNUSED;
+frv_ifcvt_init_extra_fields (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
frv_ifcvt.added_insns_list = NULL_RTX;
frv_ifcvt.cur_scratch_regs = 0;
}
\f
-/* Internal function to add a potenial insn to the list of insns to be inserted
+/* Internal function to add a potential insn to the list of insns to be inserted
if the conditional execution conversion is successful. */
static void
-frv_ifcvt_add_insn (pattern, insn, before_p)
- rtx pattern;
- rtx insn;
- int before_p;
+frv_ifcvt_add_insn (rtx pattern, rtx insn, int before_p)
{
rtx link = alloc_EXPR_LIST (VOIDmode, pattern, insn);
- link->jump = before_p; /* mark to add this before or after insn */
+ link->jump = before_p; /* Mark to add this before or after insn. */
frv_ifcvt.added_insns_list = alloc_EXPR_LIST (VOIDmode, link,
frv_ifcvt.added_insns_list);
tests cannot be converted. */
void
-frv_ifcvt_modify_tests (ce_info, p_true, p_false)
- ce_if_block_t *ce_info;
- rtx *p_true;
- rtx *p_false;
+frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false)
{
basic_block test_bb = ce_info->test_bb; /* test basic block */
basic_block then_bb = ce_info->then_bb; /* THEN */
enum reg_class cr_class;
int cc_first;
int cc_last;
+ reg_set_iterator rsi;
/* Make sure we are only dealing with hard registers. Also honor the
-mno-cond-exec switch, and -mno-nested-cond-exec switches if
applicable. */
- if (!reload_completed || TARGET_NO_COND_EXEC
- || (TARGET_NO_NESTED_CE && ce_info->pass > 1))
+ if (!reload_completed || !TARGET_COND_EXEC
+ || (!TARGET_NESTED_CE && ce_info->pass > 1))
goto fail;
/* Figure out which registers we can allocate for our own purposes. Only
consider registers that are not preserved across function calls and are
not fixed. However, allow the ICC/ICR temporary registers to be allocated
- if we did not need to use them in reloading other registers. */
+ if we did not need to use them in reloading other registers. */
memset (&tmp_reg->regs, 0, sizeof (tmp_reg->regs));
COPY_HARD_REG_SET (tmp_reg->regs, call_used_reg_set);
AND_COMPL_HARD_REG_SET (tmp_reg->regs, fixed_reg_set);
for (j = CC_FIRST; j <= CC_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
{
- if (REGNO_REG_SET_P (then_bb->global_live_at_start, j))
+ if (REGNO_REG_SET_P (then_bb->il.rtl->global_live_at_start, j))
continue;
- if (else_bb && REGNO_REG_SET_P (else_bb->global_live_at_start, j))
+ if (else_bb
+ && REGNO_REG_SET_P (else_bb->il.rtl->global_live_at_start, j))
continue;
- if (join_bb && REGNO_REG_SET_P (join_bb->global_live_at_start, j))
+ if (join_bb
+ && REGNO_REG_SET_P (join_bb->il.rtl->global_live_at_start, j))
continue;
SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j);
if (join_bb)
{
- int regno;
+ unsigned int regno;
/* Remove anything live at the beginning of the join block from being
available for allocation. */
- EXECUTE_IF_SET_IN_REG_SET (join_bb->global_live_at_start, 0, regno,
- {
- if (regno < FIRST_PSEUDO_REGISTER)
- CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
- });
+ EXECUTE_IF_SET_IN_REG_SET (join_bb->il.rtl->global_live_at_start, 0, regno, rsi)
+ {
+ if (regno < FIRST_PSEUDO_REGISTER)
+ CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
+ }
}
/* Add in all of the blocks in multiple &&/|| blocks to be scanned. */
while (multiple_test_bb != test_bb)
{
bb[num_bb++] = multiple_test_bb;
- multiple_test_bb = multiple_test_bb->pred->src;
+ multiple_test_bb = EDGE_PRED (multiple_test_bb, 0)->src;
}
}
/* Scan all of the blocks for registers that must not be allocated. */
for (j = 0; j < num_bb; j++)
{
- rtx last_insn = bb[j]->end;
- rtx insn = bb[j]->head;
- int regno;
+ rtx last_insn = BB_END (bb[j]);
+ rtx insn = BB_HEAD (bb[j]);
+ unsigned int regno;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Scanning %s block %d, start %d, end %d\n",
+ if (dump_file)
+ fprintf (dump_file, "Scanning %s block %d, start %d, end %d\n",
(bb[j] == else_bb) ? "else" : ((bb[j] == then_bb) ? "then" : "test"),
(int) bb[j]->index,
- (int) INSN_UID (bb[j]->head),
- (int) INSN_UID (bb[j]->end));
+ (int) INSN_UID (BB_HEAD (bb[j])),
+ (int) INSN_UID (BB_END (bb[j])));
/* Anything live at the beginning of the block is obviously unavailable
for allocation. */
- EXECUTE_IF_SET_IN_REG_SET (bb[j]->global_live_at_start, 0, regno,
- {
- if (regno < FIRST_PSEUDO_REGISTER)
- CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
- });
+ EXECUTE_IF_SET_IN_REG_SET (bb[j]->il.rtl->global_live_at_start, 0, regno, rsi)
+ {
+ if (regno < FIRST_PSEUDO_REGISTER)
+ CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
+ }
- /* loop through the insns in the block. */
+ /* Loop through the insns in the block. */
for (;;)
{
/* Mark any new registers that are created as being unavailable for
else if (CR_P (regno)
&& (src_code == IF_THEN_ELSE
- || GET_RTX_CLASS (src_code) == '<'))
+ || COMPARISON_P (src)))
skip_nested_if = TRUE;
}
}
CLEAR_HARD_REG_BIT (tmp_reg->regs, j);
}
- if (rtl_dump_file)
+ if (dump_file)
{
int num_gprs = 0;
- fprintf (rtl_dump_file, "Available GPRs: ");
+ fprintf (dump_file, "Available GPRs: ");
for (j = GPR_FIRST; j <= GPR_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
{
- fprintf (rtl_dump_file, " %d [%s]", j, reg_names[j]);
+ fprintf (dump_file, " %d [%s]", j, reg_names[j]);
if (++num_gprs > GPR_TEMP_NUM+2)
break;
}
- fprintf (rtl_dump_file, "%s\nAvailable CRs: ",
+ fprintf (dump_file, "%s\nAvailable CRs: ",
(num_gprs > GPR_TEMP_NUM+2) ? " ..." : "");
for (j = CR_FIRST; j <= CR_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
- fprintf (rtl_dump_file, " %d [%s]", j, reg_names[j]);
+ fprintf (dump_file, " %d [%s]", j, reg_names[j]);
- fputs ("\n", rtl_dump_file);
+ fputs ("\n", dump_file);
if (ce_info->pass > 1)
{
- fprintf (rtl_dump_file, "Modifiable CCs: ");
+ fprintf (dump_file, "Modifiable CCs: ");
for (j = CC_FIRST; j <= CC_LAST; j++)
if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
- fprintf (rtl_dump_file, " %d [%s]", j, reg_names[j]);
+ fprintf (dump_file, " %d [%s]", j, reg_names[j]);
- fprintf (rtl_dump_file, "\n%d nested COND_EXEC statements\n",
+ fprintf (dump_file, "\n%d nested COND_EXEC statements\n",
frv_ifcvt.num_nested_cond_exec);
}
}
/* Allocate the appropriate temporary condition code register. Try to
allocate the ICR/FCR register that corresponds to the ICC/FCC register so
that conditional cmp's can be done. */
- if (mode == CCmode || mode == CC_UNSmode)
+ if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
{
cr_class = ICR_REGS;
cc_class = ICC_REGS;
if (! cr)
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Could not allocate a CR temporary register\n");
+ if (dump_file)
+ fprintf (dump_file, "Could not allocate a CR temporary register\n");
goto fail;
}
- if (rtl_dump_file)
- fprintf (rtl_dump_file,
+ if (dump_file)
+ fprintf (dump_file,
"Will use %s for conditional execution, %s for nested comparisons\n",
reg_names[ REGNO (cr)],
(nested_cc) ? reg_names[ REGNO (nested_cc) ] : "<none>");
gen_rtx_fmt_ee (code, CC_CCRmode, cc, const0_rtx));
/* Record the check insn to be inserted later. */
- frv_ifcvt_add_insn (check_insn, test_bb->end, TRUE);
+ frv_ifcvt_add_insn (check_insn, BB_END (test_bb), TRUE);
/* Update the tests. */
frv_ifcvt.cr_reg = cr;
fail:
*p_true = NULL_RTX;
*p_false = NULL_RTX;
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Disabling this conditional execution.\n");
+ if (dump_file)
+ fprintf (dump_file, "Disabling this conditional execution.\n");
return;
}
(const_int 0))) */
void
-frv_ifcvt_modify_multiple_tests (ce_info, bb, p_true, p_false)
- ce_if_block_t *ce_info;
- basic_block bb;
- rtx *p_true;
- rtx *p_false;
+frv_ifcvt_modify_multiple_tests (ce_if_block_t *ce_info,
+ basic_block bb,
+ rtx *p_true,
+ rtx *p_false)
{
rtx old_true = XEXP (*p_true, 0);
rtx old_false = XEXP (*p_false, 0);
debug_rtx (*p_false);
}
- if (TARGET_NO_MULTI_CE)
+ if (!TARGET_MULTI_CE)
goto fail;
if (GET_CODE (cr) != REG)
goto fail;
-
- if (mode == CCmode || mode == CC_UNSmode)
+
+ if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
{
cr_class = ICR_REGS;
p_new_cr = &frv_ifcvt.extra_int_cr;
/* First add the andcr/andncr/orcr/orncr, which will be added after the
conditional check instruction, due to frv_ifcvt_add_insn being a LIFO
stack. */
- frv_ifcvt_add_insn ((*logical_func) (cr, cr, new_cr), bb->end, TRUE);
+ frv_ifcvt_add_insn ((*logical_func) (cr, cr, new_cr), BB_END (bb), TRUE);
/* Now add the conditional check insn. */
cc = XEXP (test_expr, 0);
check_insn = gen_rtx_SET (VOIDmode, new_cr, if_else);
- /* add the new check insn to the list of check insns that need to be
+ /* Add the new check insn to the list of check insns that need to be
inserted. */
- frv_ifcvt_add_insn (check_insn, bb->end, TRUE);
+ frv_ifcvt_add_insn (check_insn, BB_END (bb), TRUE);
if (TARGET_DEBUG_COND_EXEC)
{
fail:
*p_true = *p_false = NULL_RTX;
- /* If we allocated a CR register, release it. */
+ /* If we allocated a CR register, release it. */
if (new_cr)
{
CLEAR_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, REGNO (new_cr));
that use constants to ones that just use registers. */
static rtx
-frv_ifcvt_load_value (value, insn)
- rtx value;
- rtx insn ATTRIBUTE_UNUSED;
+frv_ifcvt_load_value (rtx value, rtx insn ATTRIBUTE_UNUSED)
{
int num_alloc = frv_ifcvt.cur_scratch_regs;
int i;
}
}
- /* Have we exhausted the number of registers available? */
+ /* Have we exhausted the number of registers available? */
if (num_alloc >= GPR_TEMP_NUM)
{
- if (rtl_dump_file)
- fprintf (rtl_dump_file, "Too many temporary registers allocated\n");
+ if (dump_file)
+ fprintf (dump_file, "Too many temporary registers allocated\n");
return NULL_RTX;
}
reg = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, GPR_REGS, SImode, TRUE, TRUE);
if (! reg)
{
- if (rtl_dump_file)
- fputs ("Could not find a scratch register\n", rtl_dump_file);
+ if (dump_file)
+ fputs ("Could not find a scratch register\n", dump_file);
return NULL_RTX;
}
frv_ifcvt.cur_scratch_regs++;
frv_ifcvt.scratch_regs[num_alloc] = gen_rtx_SET (VOIDmode, reg, value);
- if (rtl_dump_file)
+ if (dump_file)
{
if (GET_CODE (value) == CONST_INT)
- fprintf (rtl_dump_file, "Register %s will hold %ld\n",
+ fprintf (dump_file, "Register %s will hold %ld\n",
reg_names[ REGNO (reg)], (long)INTVAL (value));
else if (GET_CODE (value) == REG && REGNO (value) == LR_REGNO)
- fprintf (rtl_dump_file, "Register %s will hold LR\n",
+ fprintf (dump_file, "Register %s will hold LR\n",
reg_names[ REGNO (reg)]);
else
- fprintf (rtl_dump_file, "Register %s will hold a saved value\n",
+ fprintf (dump_file, "Register %s will hold a saved value\n",
reg_names[ REGNO (reg)]);
}
into a temporary register, or the new MEM if we were successful. */
static rtx
-frv_ifcvt_rewrite_mem (mem, mode, insn)
- rtx mem;
- enum machine_mode mode;
- rtx insn;
+frv_ifcvt_rewrite_mem (rtx mem, enum machine_mode mode, rtx insn)
{
rtx addr = XEXP (mem, 0);
- if (!frv_legitimate_address_p (mode, addr, reload_completed, TRUE))
+ if (!frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE))
{
if (GET_CODE (addr) == PLUS)
{
rtx addr_op0 = XEXP (addr, 0);
rtx addr_op1 = XEXP (addr, 1);
- if (plus_small_data_p (addr_op0, addr_op1))
- addr = frv_ifcvt_load_value (addr, insn);
-
- else if (GET_CODE (addr_op0) == REG && CONSTANT_P (addr_op1))
+ if (GET_CODE (addr_op0) == REG && CONSTANT_P (addr_op1))
{
rtx reg = frv_ifcvt_load_value (addr_op1, insn);
if (!reg)
SET, possibly conditionally executed. It may also have CLOBBERs, USEs. */
static rtx
-single_set_pattern (pattern)
- rtx pattern;
+single_set_pattern (rtx pattern)
{
rtx set;
int i;
insn cannot be converted to be executed conditionally. */
rtx
-frv_ifcvt_modify_insn (ce_info, pattern, insn)
- ce_if_block_t *ce_info ATTRIBUTE_UNUSED;
- rtx pattern;
- rtx insn;
+frv_ifcvt_modify_insn (ce_if_block_t *ce_info,
+ rtx pattern,
+ rtx insn)
{
rtx orig_ce_pattern = pattern;
rtx set;
rtx op1;
rtx test;
- if (GET_CODE (pattern) != COND_EXEC)
- abort ();
+ gcc_assert (GET_CODE (pattern) == COND_EXEC);
test = COND_EXEC_TEST (pattern);
if (GET_CODE (test) == AND)
rtx src = SET_SRC (set);
enum machine_mode mode = GET_MODE (dest);
- /* Check for normal binary operators */
- if (mode == SImode
- && (GET_RTX_CLASS (GET_CODE (src)) == '2'
- || GET_RTX_CLASS (GET_CODE (src)) == 'c'))
+ /* Check for normal binary operators. */
+ if (mode == SImode && ARITHMETIC_P (src))
{
op0 = XEXP (src, 0);
op1 = XEXP (src, 1);
- /* Special case load of small data address which looks like:
- r16+symbol_ref */
- if (GET_CODE (src) == PLUS && plus_small_data_p (op0, op1))
- {
- src = frv_ifcvt_load_value (src, insn);
- if (src)
- COND_EXEC_CODE (pattern) = gen_rtx_SET (VOIDmode, dest, src);
- else
- goto fail;
- }
-
- else if (integer_register_operand (op0, SImode) && CONSTANT_P (op1))
+ if (integer_register_operand (op0, SImode) && CONSTANT_P (op1))
{
op1 = frv_ifcvt_load_value (op1, insn);
if (op1)
other registers. */
else if (frv_ifcvt.scratch_insns_bitmap
&& bitmap_bit_p (frv_ifcvt.scratch_insns_bitmap,
- INSN_UID (insn)))
+ INSN_UID (insn))
+ && REG_P (SET_DEST (set))
+ /* We must not unconditionally set a scratch reg chosen
+ for a nested if-converted block if its incoming
+ value from the TEST block (or the result of the THEN
+ branch) could/should propagate to the JOIN block.
+ It suffices to test whether the register is live at
+ the JOIN point: if it's live there, we can infer
+ that we set it in the former JOIN block of the
+ nested if-converted block (otherwise it wouldn't
+ have been available as a scratch register), and it
+ is either propagated through or set in the other
+ conditional block. It's probably not worth trying
+ to catch the latter case, and it could actually
+ limit scheduling of the combined block quite
+ severely. */
+ && ce_info->join_bb
+ && ! (REGNO_REG_SET_P
+ (ce_info->join_bb->il.rtl->global_live_at_start,
+ REGNO (SET_DEST (set))))
+ /* Similarly, we must not unconditionally set a reg
+ used as scratch in the THEN branch if the same reg
+ is live in the ELSE branch. */
+ && (! ce_info->else_bb
+ || BLOCK_FOR_INSN (insn) == ce_info->else_bb
+ || ! (REGNO_REG_SET_P
+ (ce_info->else_bb->il.rtl->global_live_at_start,
+ REGNO (SET_DEST (set))))))
pattern = set;
else if (mode == QImode || mode == HImode || mode == SImode
/* Rewrite a nested set cccr in terms of IF_THEN_ELSE. Also deal with
rewriting the CC register to be the same as the paired CC/CR register
for nested ifs. */
- else if (mode == CC_CCRmode && GET_RTX_CLASS (GET_CODE (src)) == '<')
+ else if (mode == CC_CCRmode && COMPARISON_P (src))
{
int regno = REGNO (XEXP (src, 0));
rtx if_else;
conditional if information CE_INFO. */
void
-frv_ifcvt_modify_final (ce_info)
- ce_if_block_t *ce_info ATTRIBUTE_UNUSED;
+frv_ifcvt_modify_final (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
rtx existing_insn;
rtx check_insn;
/* Loop inserting the check insns. The last check insn is the first test,
and is the appropriate place to insert constants. */
- if (! p)
- abort ();
+ gcc_assert (p);
do
{
{
rtx insn = emit_insn_before (frv_ifcvt.scratch_regs[i], existing_insn);
if (! frv_ifcvt.scratch_insns_bitmap)
- frv_ifcvt.scratch_insns_bitmap = BITMAP_XMALLOC ();
+ frv_ifcvt.scratch_insns_bitmap = BITMAP_ALLOC (NULL);
bitmap_set_bit (frv_ifcvt.scratch_insns_bitmap, INSN_UID (insn));
frv_ifcvt.scratch_regs[i] = NULL_RTX;
}
information CE_INFO. */
void
-frv_ifcvt_modify_cancel (ce_info)
- ce_if_block_t *ce_info ATTRIBUTE_UNUSED;
+frv_ifcvt_modify_cancel (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
int i;
rtx p = frv_ifcvt.added_insns_list;
jmpl @(gr0,<jmp_reg>) */
int
-frv_trampoline_size ()
+frv_trampoline_size (void)
{
- return 5 /* instructions */ * 4 /* instruction size */;
+ if (TARGET_FDPIC)
+ /* Allocate room for the function descriptor and the lddi
+ instruction. */
+ return 8 + 6 * 4;
+ return 5 /* instructions */ * 4 /* instruction size. */;
}
\f
jmpl @(gr0,<jmp_reg>) */
void
-frv_initialize_trampoline (addr, fnaddr, static_chain)
- rtx addr;
- rtx fnaddr;
- rtx static_chain;
+frv_initialize_trampoline (rtx addr, rtx fnaddr, rtx static_chain)
{
rtx sc_reg = force_reg (Pmode, static_chain);
This case often occurs between floating-point and general registers. */
enum reg_class
-frv_secondary_reload_class (class, mode, x, in_p)
- enum reg_class class;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- rtx x;
- int in_p ATTRIBUTE_UNUSED;
+frv_secondary_reload_class (enum reg_class class,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x,
+ int in_p ATTRIBUTE_UNUSED)
{
enum reg_class ret;
register allocation. */
int
-frv_class_likely_spilled_p (class)
- enum reg_class class;
+frv_class_likely_spilled_p (enum reg_class class)
{
switch (class)
{
default:
break;
+ case GR8_REGS:
+ case GR9_REGS:
+ case GR89_REGS:
+ case FDPIC_FPTR_REGS:
+ case FDPIC_REGS:
case ICC_REGS:
case FCC_REGS:
case CC_REGS:
*/
int
-frv_adjust_field_align (field, computed)
- tree field;
- int computed;
+frv_adjust_field_align (tree field, int computed)
{
- /* C++ provides a null DECL_CONTEXT if the bit field is wider than its
- type. */
- if (DECL_BIT_FIELD (field) && DECL_CONTEXT (field))
+ /* Make sure that the bitfield is not wider than the type. */
+ if (DECL_BIT_FIELD (field)
+ && !DECL_ARTIFICIAL (field))
{
tree parent = DECL_CONTEXT (field);
tree prev = NULL_TREE;
tree cur;
- /* Loop finding the previous field to the current one */
for (cur = TYPE_FIELDS (parent); cur && cur != field; cur = TREE_CHAIN (cur))
{
if (TREE_CODE (cur) != FIELD_DECL)
prev = cur;
}
- if (!cur)
- abort ();
+ gcc_assert (cur);
/* If this isn't a :0 field and if the previous element is a bitfield
also, see if the type is different, if so, we will need to align the
- bit-field to the next boundary */
+ bit-field to the next boundary. */
if (prev
&& ! DECL_PACKED (field)
&& ! integer_zerop (DECL_SIZE (field))
pattern's constraint asks for one. */
int
-frv_hard_regno_mode_ok (regno, mode)
- int regno;
- enum machine_mode mode;
+frv_hard_regno_mode_ok (int regno, enum machine_mode mode)
{
int base;
int mask;
{
case CCmode:
case CC_UNSmode:
+ case CC_NZmode:
return ICC_P (regno) || GPR_P (regno);
case CC_CCRmode:
}
else
{
- /* The other registers store one word. */
- if (GPR_P (regno))
+ /* The other registers store one word. */
+ if (GPR_P (regno) || regno == AP_FIRST)
base = GPR_FIRST;
else if (FPR_P (regno))
else if (ACC_P (regno))
base = ACC_FIRST;
+ else if (SPR_P (regno))
+ return mode == SImode;
+
+ /* Fill in the table. */
else
return 0;
for each byte. */
int
-frv_hard_regno_nregs (regno, mode)
- int regno;
- enum machine_mode mode;
+frv_hard_regno_nregs (int regno, enum machine_mode mode)
{
if (ACCG_P (regno))
return GET_MODE_SIZE (mode);
This declaration is required. */
int
-frv_class_max_nregs (class, mode)
- enum reg_class class;
- enum machine_mode mode;
+frv_class_max_nregs (enum reg_class class, enum machine_mode mode)
{
if (class == ACCG_REGS)
/* An N-byte value requires N accumulator guards. */
definition for this macro on machines where anything `CONSTANT_P' is valid. */
int
-frv_legitimate_constant_p (x)
- rtx x;
+frv_legitimate_constant_p (rtx x)
{
enum machine_mode mode = GET_MODE (x);
- /* All of the integer constants are ok */
+ /* frv_cannot_force_const_mem always returns true for FDPIC. This
+ means that the move expanders will be expected to deal with most
+ kinds of constant, regardless of what we return here.
+
+ However, among its other duties, LEGITIMATE_CONSTANT_P decides whether
+ a constant can be entered into reg_equiv_constant[]. If we return true,
+ reload can create new instances of the constant whenever it likes.
+
+ The idea is therefore to accept as many constants as possible (to give
+ reload more freedom) while rejecting constants that can only be created
+ at certain times. In particular, anything with a symbolic component will
+ require use of the pseudo FDPIC register, which is only available before
+ reload. */
+ if (TARGET_FDPIC)
+ return LEGITIMATE_PIC_OPERAND_P (x);
+
+ /* All of the integer constants are ok. */
if (GET_CODE (x) != CONST_DOUBLE)
return TRUE;
- /* double integer constants are ok */
+ /* double integer constants are ok. */
if (mode == VOIDmode || mode == DImode)
return TRUE;
- /* 0 is always ok */
+ /* 0 is always ok. */
if (x == CONST0_RTX (mode))
return TRUE;
/* If floating point is just emulated, allow any constant, since it will be
- constructed in the GPRs */
+ constructed in the GPRs. */
if (!TARGET_HAS_FPRS)
return TRUE;
/* Otherwise store the constant away and do a load. */
return FALSE;
}
+
+/* Implement SELECT_CC_MODE. Choose CC_FP for floating-point comparisons,
+ CC_NZ for comparisons against zero in which a single Z or N flag test
+ is enough, CC_UNS for other unsigned comparisons, and CC for other
+ signed comparisons. */
+
+enum machine_mode
+frv_select_cc_mode (enum rtx_code code, rtx x, rtx y)
+{
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ return CC_FPmode;
+
+ switch (code)
+ {
+ case EQ:
+ case NE:
+ case LT:
+ case GE:
+ return y == const0_rtx ? CC_NZmode : CCmode;
+
+ case GTU:
+ case GEU:
+ case LTU:
+ case LEU:
+ return y == const0_rtx ? CC_NZmode : CC_UNSmode;
+
+ default:
+ return CCmode;
+ }
+}
\f
/* A C expression for the cost of moving data from a register in class FROM to
one in class TO. The classes are expressed using the enumeration values
#define LOW_COST 1
int
-frv_register_move_cost (from, to)
- enum reg_class from;
- enum reg_class to;
+frv_register_move_cost (enum reg_class from, enum reg_class to)
{
switch (from)
{
need a fixup entry for aligned (non-debugging) code. */
static bool
-frv_assemble_integer (value, size, aligned_p)
- rtx value;
- unsigned int size;
- int aligned_p;
+frv_assemble_integer (rtx value, unsigned int size, int aligned_p)
{
- if (flag_pic && size == UNITS_PER_WORD)
+ if ((flag_pic || TARGET_FDPIC) && size == UNITS_PER_WORD)
{
if (GET_CODE (value) == CONST
|| GET_CODE (value) == SYMBOL_REF
|| GET_CODE (value) == LABEL_REF)
{
- if (aligned_p)
+ if (TARGET_FDPIC && GET_CODE (value) == SYMBOL_REF
+ && SYMBOL_REF_FUNCTION_P (value))
+ {
+ fputs ("\t.picptr\tfuncdesc(", asm_out_file);
+ output_addr_const (asm_out_file, value);
+ fputs (")\n", asm_out_file);
+ return true;
+ }
+ else if (TARGET_FDPIC && GET_CODE (value) == CONST
+ && frv_function_symbol_referenced_p (value))
+ return false;
+ if (aligned_p && !TARGET_FDPIC)
{
static int label_num = 0;
char buf[256];
return default_assemble_integer (value, size, aligned_p);
}
-/* Function to set up the backend function structure. */
+/* Function to set up the backend function structure. */
+
+static struct machine_function *
+frv_init_machine_status (void)
+{
+ return ggc_alloc_cleared (sizeof (struct machine_function));
+}
+\f
+/* Implement TARGET_SCHED_ISSUE_RATE. */
+
+int
+frv_issue_rate (void)
+{
+ if (!TARGET_PACK)
+ return 1;
+
+ switch (frv_cpu_type)
+ {
+ default:
+ case FRV_CPU_FR300:
+ case FRV_CPU_SIMPLE:
+ return 1;
+
+ case FRV_CPU_FR400:
+ case FRV_CPU_FR405:
+ case FRV_CPU_FR450:
+ return 2;
+
+ case FRV_CPU_GENERIC:
+ case FRV_CPU_FR500:
+ case FRV_CPU_TOMCAT:
+ return 4;
+
+ case FRV_CPU_FR550:
+ return 8;
+ }
+}
+\f
+/* A for_each_rtx callback. If X refers to an accumulator, return
+ ACC_GROUP_ODD if the bit 2 of the register number is set and
+ ACC_GROUP_EVEN if it is clear. Return 0 (ACC_GROUP_NONE)
+ otherwise. */
+
+static int
+frv_acc_group_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+ if (REG_P (*x))
+ {
+ if (ACC_P (REGNO (*x)))
+ return (REGNO (*x) - ACC_FIRST) & 4 ? ACC_GROUP_ODD : ACC_GROUP_EVEN;
+ if (ACCG_P (REGNO (*x)))
+ return (REGNO (*x) - ACCG_FIRST) & 4 ? ACC_GROUP_ODD : ACC_GROUP_EVEN;
+ }
+ return 0;
+}
+
+/* Return the value of INSN's acc_group attribute. */
+
+int
+frv_acc_group (rtx insn)
+{
+ /* This distinction only applies to the FR550 packing constraints. */
+ if (frv_cpu_type != FRV_CPU_FR550)
+ return ACC_GROUP_NONE;
+ return for_each_rtx (&PATTERN (insn), frv_acc_group_1, 0);
+}
+
+/* Return the index of the DFA unit in FRV_UNIT_NAMES[] that instruction
+ INSN will try to claim first. Since this value depends only on the
+ type attribute, we can cache the results in FRV_TYPE_TO_UNIT[]. */
+
+static unsigned int
+frv_insn_unit (rtx insn)
+{
+ enum attr_type type;
+
+ type = get_attr_type (insn);
+ if (frv_type_to_unit[type] == ARRAY_SIZE (frv_unit_codes))
+ {
+ /* We haven't seen this type of instruction before. */
+ state_t state;
+ unsigned int unit;
+
+ /* Issue the instruction on its own to see which unit it prefers. */
+ state = alloca (state_size ());
+ state_reset (state);
+ state_transition (state, insn);
+
+ /* Find out which unit was taken. */
+ for (unit = 0; unit < ARRAY_SIZE (frv_unit_codes); unit++)
+ if (cpu_unit_reservation_p (state, frv_unit_codes[unit]))
+ break;
+
+ gcc_assert (unit != ARRAY_SIZE (frv_unit_codes));
+
+ frv_type_to_unit[type] = unit;
+ }
+ return frv_type_to_unit[type];
+}
+
+/* Return true if INSN issues to a branch unit. */
+
+static bool
+frv_issues_to_branch_unit_p (rtx insn)
+{
+ return frv_unit_groups[frv_insn_unit (insn)] == GROUP_B;
+}
+\f
+/* The current state of the packing pass, implemented by frv_pack_insns. */
+static struct {
+ /* The state of the pipeline DFA. */
+ state_t dfa_state;
+
+ /* Which hardware registers are set within the current packet,
+ and the conditions under which they are set. */
+ regstate_t regstate[FIRST_PSEUDO_REGISTER];
+
+ /* The memory locations that have been modified so far in this
+ packet. MEM is the memref and COND is the regstate_t condition
+ under which it is set. */
+ struct {
+ rtx mem;
+ regstate_t cond;
+ } mems[2];
+
+ /* The number of valid entries in MEMS. The value is larger than
+ ARRAY_SIZE (mems) if there were too many mems to record. */
+ unsigned int num_mems;
+
+ /* The maximum number of instructions that can be packed together. */
+ unsigned int issue_rate;
+
+ /* The instructions in the packet, partitioned into groups. */
+ struct frv_packet_group {
+ /* How many instructions in the packet belong to this group. */
+ unsigned int num_insns;
+
+ /* A list of the instructions that belong to this group, in the order
+ they appear in the rtl stream. */
+ rtx insns[ARRAY_SIZE (frv_unit_codes)];
+
+ /* The contents of INSNS after they have been sorted into the correct
+ assembly-language order. Element X issues to unit X. The list may
+ contain extra nops. */
+ rtx sorted[ARRAY_SIZE (frv_unit_codes)];
+
+ /* The member of frv_nops[] to use in sorted[]. */
+ rtx nop;
+ } groups[NUM_GROUPS];
+
+ /* The instructions that make up the current packet. */
+ rtx insns[ARRAY_SIZE (frv_unit_codes)];
+ unsigned int num_insns;
+} frv_packet;
+
+/* Return the regstate_t flags for the given COND_EXEC condition.
+ Abort if the condition isn't in the right form. */
+
+static int
+frv_cond_flags (rtx cond)
+{
+ gcc_assert ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
+ && GET_CODE (XEXP (cond, 0)) == REG
+ && CR_P (REGNO (XEXP (cond, 0)))
+ && XEXP (cond, 1) == const0_rtx);
+ return ((REGNO (XEXP (cond, 0)) - CR_FIRST)
+ | (GET_CODE (cond) == NE
+ ? REGSTATE_IF_TRUE
+ : REGSTATE_IF_FALSE));
+}
+
+
+/* Return true if something accessed under condition COND2 can
+ conflict with something written under condition COND1. */
+
+static bool
+frv_regstate_conflict_p (regstate_t cond1, regstate_t cond2)
+{
+ /* If either reference was unconditional, we have a conflict. */
+ if ((cond1 & REGSTATE_IF_EITHER) == 0
+ || (cond2 & REGSTATE_IF_EITHER) == 0)
+ return true;
+
+ /* The references might conflict if they were controlled by
+ different CRs. */
+ if ((cond1 & REGSTATE_CC_MASK) != (cond2 & REGSTATE_CC_MASK))
+ return true;
+
+ /* They definitely conflict if they are controlled by the
+ same condition. */
+ if ((cond1 & cond2 & REGSTATE_IF_EITHER) != 0)
+ return true;
+
+ return false;
+}
+
+
+/* A for_each_rtx callback. Return 1 if *X depends on an instruction in
+ the current packet. DATA points to a regstate_t that describes the
+ condition under which *X might be set or used. */
+
+static int
+frv_registers_conflict_p_1 (rtx *x, void *data)
+{
+ unsigned int regno, i;
+ regstate_t cond;
+
+ cond = *(regstate_t *) data;
+
+ if (GET_CODE (*x) == REG)
+ FOR_EACH_REGNO (regno, *x)
+ if ((frv_packet.regstate[regno] & REGSTATE_MODIFIED) != 0)
+ if (frv_regstate_conflict_p (frv_packet.regstate[regno], cond))
+ return 1;
+
+ if (GET_CODE (*x) == MEM)
+ {
+ /* If we ran out of memory slots, assume a conflict. */
+ if (frv_packet.num_mems > ARRAY_SIZE (frv_packet.mems))
+ return 1;
+
+ /* Check for output or true dependencies with earlier MEMs. */
+ for (i = 0; i < frv_packet.num_mems; i++)
+ if (frv_regstate_conflict_p (frv_packet.mems[i].cond, cond))
+ {
+ if (true_dependence (frv_packet.mems[i].mem, VOIDmode,
+ *x, rtx_varies_p))
+ return 1;
+
+ if (output_dependence (frv_packet.mems[i].mem, *x))
+ return 1;
+ }
+ }
+
+ /* The return values of calls aren't significant: they describe
+ the effect of the call as a whole, not of the insn itself. */
+ if (GET_CODE (*x) == SET && GET_CODE (SET_SRC (*x)) == CALL)
+ {
+ if (for_each_rtx (&SET_SRC (*x), frv_registers_conflict_p_1, data))
+ return 1;
+ return -1;
+ }
+
+ /* Check subexpressions. */
+ return 0;
+}
+
+
+/* Return true if something in X might depend on an instruction
+ in the current packet. */
+
+static bool
+frv_registers_conflict_p (rtx x)
+{
+ regstate_t flags;
+
+ flags = 0;
+ if (GET_CODE (x) == COND_EXEC)
+ {
+ if (for_each_rtx (&XEXP (x, 0), frv_registers_conflict_p_1, &flags))
+ return true;
+
+ flags |= frv_cond_flags (XEXP (x, 0));
+ x = XEXP (x, 1);
+ }
+ return for_each_rtx (&x, frv_registers_conflict_p_1, &flags);
+}
+
+
+/* A note_stores callback. DATA points to the regstate_t condition
+ under which X is modified. Update FRV_PACKET accordingly. */
+
+static void
+frv_registers_update_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ unsigned int regno;
+
+ if (GET_CODE (x) == REG)
+ FOR_EACH_REGNO (regno, x)
+ frv_packet.regstate[regno] |= *(regstate_t *) data;
+
+ if (GET_CODE (x) == MEM)
+ {
+ if (frv_packet.num_mems < ARRAY_SIZE (frv_packet.mems))
+ {
+ frv_packet.mems[frv_packet.num_mems].mem = x;
+ frv_packet.mems[frv_packet.num_mems].cond = *(regstate_t *) data;
+ }
+ frv_packet.num_mems++;
+ }
+}
+
+
+/* Update the register state information for an instruction whose
+ body is X. */
+
+static void
+frv_registers_update (rtx x)
+{
+ regstate_t flags;
+
+ flags = REGSTATE_MODIFIED;
+ if (GET_CODE (x) == COND_EXEC)
+ {
+ flags |= frv_cond_flags (XEXP (x, 0));
+ x = XEXP (x, 1);
+ }
+ note_stores (x, frv_registers_update_1, &flags);
+}
+
+
+/* Initialize frv_packet for the start of a new packet. */
+
+static void
+frv_start_packet (void)
+{
+ enum frv_insn_group group;
+
+ memset (frv_packet.regstate, 0, sizeof (frv_packet.regstate));
+ frv_packet.num_mems = 0;
+ frv_packet.num_insns = 0;
+ for (group = 0; group < NUM_GROUPS; group++)
+ frv_packet.groups[group].num_insns = 0;
+}
+
+
+/* Likewise for the start of a new basic block. */
+
+static void
+frv_start_packet_block (void)
+{
+ state_reset (frv_packet.dfa_state);
+ frv_start_packet ();
+}
+
+
+/* Finish the current packet, if any, and start a new one. Call
+ HANDLE_PACKET with FRV_PACKET describing the completed packet. */
+
+static void
+frv_finish_packet (void (*handle_packet) (void))
+{
+ if (frv_packet.num_insns > 0)
+ {
+ handle_packet ();
+ state_transition (frv_packet.dfa_state, 0);
+ frv_start_packet ();
+ }
+}
+
+
+/* Return true if INSN can be added to the current packet. Update
+ the DFA state on success. */
+
+static bool
+frv_pack_insn_p (rtx insn)
+{
+ /* See if the packet is already as long as it can be. */
+ if (frv_packet.num_insns == frv_packet.issue_rate)
+ return false;
+
+ /* If the scheduler thought that an instruction should start a packet,
+ it's usually a good idea to believe it. It knows much more about
+ the latencies than we do.
+
+ There are some exceptions though:
+
+ - Conditional instructions are scheduled on the assumption that
+ they will be executed. This is usually a good thing, since it
+ tends to avoid unnecessary stalls in the conditional code.
+ But we want to pack conditional instructions as tightly as
+ possible, in order to optimize the case where they aren't
+ executed.
+
+ - The scheduler will always put branches on their own, even
+ if there's no real dependency.
+
+ - There's no point putting a call in its own packet unless
+ we have to. */
+ if (frv_packet.num_insns > 0
+ && GET_CODE (insn) == INSN
+ && GET_MODE (insn) == TImode
+ && GET_CODE (PATTERN (insn)) != COND_EXEC)
+ return false;
+
+ /* Check for register conflicts. Don't do this for setlo since any
+ conflict will be with the partnering sethi, with which it can
+ be packed. */
+ if (get_attr_type (insn) != TYPE_SETLO)
+ if (frv_registers_conflict_p (PATTERN (insn)))
+ return false;
+
+ return state_transition (frv_packet.dfa_state, insn) < 0;
+}
+
+
+/* Add instruction INSN to the current packet. */
+
+static void
+frv_add_insn_to_packet (rtx insn)
+{
+ struct frv_packet_group *packet_group;
+
+ packet_group = &frv_packet.groups[frv_unit_groups[frv_insn_unit (insn)]];
+ packet_group->insns[packet_group->num_insns++] = insn;
+ frv_packet.insns[frv_packet.num_insns++] = insn;
+
+ frv_registers_update (PATTERN (insn));
+}
+
+
+/* Insert INSN (a member of frv_nops[]) into the current packet. If the
+ packet ends in a branch or call, insert the nop before it, otherwise
+ add to the end. */
+
+static void
+frv_insert_nop_in_packet (rtx insn)
+{
+ struct frv_packet_group *packet_group;
+ rtx last;
+
+ packet_group = &frv_packet.groups[frv_unit_groups[frv_insn_unit (insn)]];
+ last = frv_packet.insns[frv_packet.num_insns - 1];
+ if (GET_CODE (last) != INSN)
+ {
+ insn = emit_insn_before (PATTERN (insn), last);
+ frv_packet.insns[frv_packet.num_insns - 1] = insn;
+ frv_packet.insns[frv_packet.num_insns++] = last;
+ }
+ else
+ {
+ insn = emit_insn_after (PATTERN (insn), last);
+ frv_packet.insns[frv_packet.num_insns++] = insn;
+ }
+ packet_group->insns[packet_group->num_insns++] = insn;
+}
+
+
+/* If packing is enabled, divide the instructions into packets and
+ return true. Call HANDLE_PACKET for each complete packet. */
-static struct machine_function *
-frv_init_machine_status ()
+static bool
+frv_for_each_packet (void (*handle_packet) (void))
{
- return ggc_alloc_cleared (sizeof (struct machine_function));
-}
-\f
-/* Implement TARGET_SCHED_ISSUE_RATE. */
+ rtx insn, next_insn;
-static int
-frv_issue_rate (void)
-{
- if (!TARGET_PACK)
- return 1;
+ frv_packet.issue_rate = frv_issue_rate ();
- switch (frv_cpu_type)
+ /* Early exit if we don't want to pack insns. */
+ if (!optimize
+ || !flag_schedule_insns_after_reload
+ || !TARGET_VLIW_BRANCH
+ || frv_packet.issue_rate == 1)
+ return false;
+
+ /* Set up the initial packing state. */
+ dfa_start ();
+ frv_packet.dfa_state = alloca (state_size ());
+
+ frv_start_packet_block ();
+ for (insn = get_insns (); insn != 0; insn = next_insn)
{
- default:
- case FRV_CPU_FR300:
- case FRV_CPU_SIMPLE:
- return 1;
+ enum rtx_code code;
+ bool eh_insn_p;
- case FRV_CPU_FR400:
- return 2;
+ code = GET_CODE (insn);
+ next_insn = NEXT_INSN (insn);
- case FRV_CPU_GENERIC:
- case FRV_CPU_FR500:
- case FRV_CPU_TOMCAT:
- return 4;
+ if (code == CODE_LABEL)
+ {
+ frv_finish_packet (handle_packet);
+ frv_start_packet_block ();
+ }
+
+ if (INSN_P (insn))
+ switch (GET_CODE (PATTERN (insn)))
+ {
+ case USE:
+ case CLOBBER:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ break;
+
+ default:
+ /* Calls mustn't be packed on a TOMCAT. */
+ if (GET_CODE (insn) == CALL_INSN && frv_cpu_type == FRV_CPU_TOMCAT)
+ frv_finish_packet (handle_packet);
+
+ /* Since the last instruction in a packet determines the EH
+ region, any exception-throwing instruction must come at
+ the end of reordered packet. Insns that issue to a
+ branch unit are bound to come last; for others it's
+ too hard to predict. */
+ eh_insn_p = (find_reg_note (insn, REG_EH_REGION, NULL) != NULL);
+ if (eh_insn_p && !frv_issues_to_branch_unit_p (insn))
+ frv_finish_packet (handle_packet);
+
+ /* Finish the current packet if we can't add INSN to it.
+ Simulate cycles until INSN is ready to issue. */
+ if (!frv_pack_insn_p (insn))
+ {
+ frv_finish_packet (handle_packet);
+ while (!frv_pack_insn_p (insn))
+ state_transition (frv_packet.dfa_state, 0);
+ }
+
+ /* Add the instruction to the packet. */
+ frv_add_insn_to_packet (insn);
+
+ /* Calls and jumps end a packet, as do insns that throw
+ an exception. */
+ if (code == CALL_INSN || code == JUMP_INSN || eh_insn_p)
+ frv_finish_packet (handle_packet);
+ break;
+ }
}
+ frv_finish_packet (handle_packet);
+ dfa_finish ();
+ return true;
}
+\f
+/* Subroutine of frv_sort_insn_group. We are trying to sort
+ frv_packet.groups[GROUP].sorted[0...NUM_INSNS-1] into assembly
+ language order. We have already picked a new position for
+ frv_packet.groups[GROUP].sorted[X] if bit X of ISSUED is set.
+ These instructions will occupy elements [0, LOWER_SLOT) and
+ [UPPER_SLOT, NUM_INSNS) of the final (sorted) array. STATE is
+ the DFA state after issuing these instructions.
+
+ Try filling elements [LOWER_SLOT, UPPER_SLOT) with every permutation
+ of the unused instructions. Return true if one such permutation gives
+ a valid ordering, leaving the successful permutation in sorted[].
+ Do not modify sorted[] until a valid permutation is found. */
+
+static bool
+frv_sort_insn_group_1 (enum frv_insn_group group,
+ unsigned int lower_slot, unsigned int upper_slot,
+ unsigned int issued, unsigned int num_insns,
+ state_t state)
+{
+ struct frv_packet_group *packet_group;
+ unsigned int i;
+ state_t test_state;
+ size_t dfa_size;
+ rtx insn;
+
+ /* Early success if we've filled all the slots. */
+ if (lower_slot == upper_slot)
+ return true;
+
+ packet_group = &frv_packet.groups[group];
+ dfa_size = state_size ();
+ test_state = alloca (dfa_size);
+
+ /* Try issuing each unused instruction. */
+ for (i = num_insns - 1; i + 1 != 0; i--)
+ if (~issued & (1 << i))
+ {
+ insn = packet_group->sorted[i];
+ memcpy (test_state, state, dfa_size);
+ if (state_transition (test_state, insn) < 0
+ && cpu_unit_reservation_p (test_state,
+ NTH_UNIT (group, upper_slot - 1))
+ && frv_sort_insn_group_1 (group, lower_slot, upper_slot - 1,
+ issued | (1 << i), num_insns,
+ test_state))
+ {
+ packet_group->sorted[upper_slot - 1] = insn;
+ return true;
+ }
+ }
+ return false;
+}
-/* Implement TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE. */
+/* Compare two instructions by their frv_insn_unit. */
static int
-frv_use_dfa_pipeline_interface (void)
+frv_compare_insns (const void *first, const void *second)
{
- return true;
+ const rtx *insn1 = first, *insn2 = second;
+ return frv_insn_unit (*insn1) - frv_insn_unit (*insn2);
}
-\f
-/* Update the register state information, to know about which registers are set
- or clobbered. */
-static void
-frv_registers_update (x, reg_state, modified, p_num_mod, flag)
- rtx x;
- unsigned char reg_state[];
- int modified[];
- int *p_num_mod;
- int flag;
-{
- int regno, reg_max;
- rtx reg;
- rtx cond;
- const char *format;
- int length;
- int j;
+/* Copy frv_packet.groups[GROUP].insns[] to frv_packet.groups[GROUP].sorted[]
+ and sort it into assembly language order. See frv.md for a description of
+ the algorithm. */
- switch (GET_CODE (x))
- {
- default:
- break;
+static void
+frv_sort_insn_group (enum frv_insn_group group)
+{
+ struct frv_packet_group *packet_group;
+ unsigned int first, i, nop, max_unit, num_slots;
+ state_t state, test_state;
+ size_t dfa_size;
- /* Clobber just modifies a register, it doesn't make it live. */
- case CLOBBER:
- frv_registers_update (XEXP (x, 0), reg_state, modified, p_num_mod,
- flag | REGSTATE_MODIFIED);
- return;
+ packet_group = &frv_packet.groups[group];
- /* Pre modify updates the first argument, just references the second. */
- case PRE_MODIFY:
- case SET:
- frv_registers_update (XEXP (x, 0), reg_state, modified, p_num_mod,
- flag | REGSTATE_MODIFIED | REGSTATE_LIVE);
- frv_registers_update (XEXP (x, 1), reg_state, modified, p_num_mod, flag);
- return;
+ /* Assume no nop is needed. */
+ packet_group->nop = 0;
- /* For COND_EXEC, pass the appropriate flag to evaluate the conditional
- statement, but just to be sure, make sure it is the type of cond_exec
- we expect. */
- case COND_EXEC:
- cond = XEXP (x, 0);
- if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
- && GET_CODE (XEXP (cond, 0)) == REG
- && CR_P (REGNO (XEXP (cond, 0)))
- && GET_CODE (XEXP (cond, 1)) == CONST_INT
- && INTVAL (XEXP (cond, 1)) == 0
- && (flag & (REGSTATE_MODIFIED | REGSTATE_IF_EITHER)) == 0)
- {
- frv_registers_update (cond, reg_state, modified, p_num_mod, flag);
- flag |= ((REGNO (XEXP (cond, 0)) - CR_FIRST)
- | ((GET_CODE (cond) == NE)
- ? REGSTATE_IF_TRUE
- : REGSTATE_IF_FALSE));
-
- frv_registers_update (XEXP (x, 1), reg_state, modified, p_num_mod,
- flag);
- return;
- }
- else
- fatal_insn ("frv_registers_update", x);
+ if (packet_group->num_insns == 0)
+ return;
- /* MEM resets the modification bits. */
- case MEM:
- flag &= ~REGSTATE_MODIFIED;
- break;
+ /* Copy insns[] to sorted[]. */
+ memcpy (packet_group->sorted, packet_group->insns,
+ sizeof (rtx) * packet_group->num_insns);
- /* See if we need to set the modified flag. */
- case SUBREG:
- reg = SUBREG_REG (x);
- if (GET_CODE (reg) == REG)
- {
- regno = subreg_regno (x);
- reg_max = REGNO (reg) + HARD_REGNO_NREGS (regno, GET_MODE (reg));
- goto reg_common;
- }
- break;
+ /* Sort sorted[] by the unit that each insn tries to take first. */
+ if (packet_group->num_insns > 1)
+ qsort (packet_group->sorted, packet_group->num_insns,
+ sizeof (rtx), frv_compare_insns);
- case REG:
- regno = REGNO (x);
- reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
- /* fall through */
+ /* That's always enough for branch and control insns. */
+ if (group == GROUP_B || group == GROUP_C)
+ return;
- reg_common:
- if (flag & REGSTATE_MODIFIED)
- {
- flag &= REGSTATE_MASK;
- while (regno < reg_max)
- {
- int rs = reg_state[regno];
+ dfa_size = state_size ();
+ state = alloca (dfa_size);
+ test_state = alloca (dfa_size);
- if (flag != rs)
- {
- if ((rs & REGSTATE_MODIFIED) == 0)
- {
- modified[ *p_num_mod ] = regno;
- (*p_num_mod)++;
- }
+ /* Find the highest FIRST such that sorted[0...FIRST-1] can issue
+ consecutively and such that the DFA takes unit X when sorted[X]
+ is added. Set STATE to the new DFA state. */
+ state_reset (test_state);
+ for (first = 0; first < packet_group->num_insns; first++)
+ {
+ memcpy (state, test_state, dfa_size);
+ if (state_transition (test_state, packet_group->sorted[first]) >= 0
+ || !cpu_unit_reservation_p (test_state, NTH_UNIT (group, first)))
+ break;
+ }
- /* If the previous register state had the register as
- modified, possibly in some conditional execution context,
- and the current insn modifies in some other context, or
- outside of conditional execution, just mark the variable
- as modified. */
- else
- flag &= ~(REGSTATE_IF_EITHER | REGSTATE_CC_MASK);
+ /* If all the instructions issued in ascending order, we're done. */
+ if (first == packet_group->num_insns)
+ return;
- reg_state[regno] = (rs | flag);
- }
- regno++;
- }
+ /* Add nops to the end of sorted[] and try each permutation until
+ we find one that works. */
+ for (nop = 0; nop < frv_num_nops; nop++)
+ {
+ max_unit = frv_insn_unit (frv_nops[nop]);
+ if (frv_unit_groups[max_unit] == group)
+ {
+ packet_group->nop = frv_nops[nop];
+ num_slots = UNIT_NUMBER (max_unit) + 1;
+ for (i = packet_group->num_insns; i < num_slots; i++)
+ packet_group->sorted[i] = frv_nops[nop];
+ if (frv_sort_insn_group_1 (group, first, num_slots,
+ (1 << first) - 1, num_slots, state))
+ return;
}
- return;
}
+ gcc_unreachable ();
+}
+\f
+/* Sort the current packet into assembly-language order. Set packing
+ flags as appropriate. */
+static void
+frv_reorder_packet (void)
+{
+ unsigned int cursor[NUM_GROUPS];
+ rtx insns[ARRAY_SIZE (frv_unit_groups)];
+ unsigned int unit, to, from;
+ enum frv_insn_group group;
+ struct frv_packet_group *packet_group;
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
+ /* First sort each group individually. */
+ for (group = 0; group < NUM_GROUPS; group++)
+ {
+ cursor[group] = 0;
+ frv_sort_insn_group (group);
+ }
- for (j = 0; j < length; ++j)
+ /* Go through the unit template and try add an instruction from
+ that unit's group. */
+ to = 0;
+ for (unit = 0; unit < ARRAY_SIZE (frv_unit_groups); unit++)
{
- switch (format[j])
+ group = frv_unit_groups[unit];
+ packet_group = &frv_packet.groups[group];
+ if (cursor[group] < packet_group->num_insns)
{
- case 'e':
- frv_registers_update (XEXP (x, j), reg_state, modified, p_num_mod,
- flag);
- break;
-
- case 'V':
- case 'E':
- if (XVEC (x, j) != 0)
- {
- int k;
- for (k = 0; k < XVECLEN (x, j); ++k)
- frv_registers_update (XVECEXP (x, j, k), reg_state, modified,
- p_num_mod, flag);
- }
- break;
-
- default:
- /* Nothing to do. */
- break;
+ /* frv_reorg should have added nops for us. */
+ gcc_assert (packet_group->sorted[cursor[group]]
+ != packet_group->nop);
+ insns[to++] = packet_group->sorted[cursor[group]++];
}
}
- return;
+ gcc_assert (to == frv_packet.num_insns);
+
+ /* Clear the last instruction's packing flag, thus marking the end of
+ a packet. Reorder the other instructions relative to it. */
+ CLEAR_PACKING_FLAG (insns[to - 1]);
+ for (from = 0; from < to - 1; from++)
+ {
+ remove_insn (insns[from]);
+ add_insn_before (insns[from], insns[to - 1]);
+ SET_PACKING_FLAG (insns[from]);
+ }
}
+
+/* Divide instructions into packets. Reorder the contents of each
+ packet so that they are in the correct assembly-language order.
+
+ Since this pass can change the raw meaning of the rtl stream, it must
+ only be called at the last minute, just before the instructions are
+ written out. */
+
+static void
+frv_pack_insns (void)
+{
+ if (frv_for_each_packet (frv_reorder_packet))
+ frv_insn_packing_flag = 0;
+ else
+ frv_insn_packing_flag = -1;
+}
\f
-/* Return if any registers in a hard register set were used an insn. */
+/* See whether we need to add nops to group GROUP in order to
+ make a valid packet. */
-static int
-frv_registers_used_p (x, reg_state, flag)
- rtx x;
- unsigned char reg_state[];
- int flag;
+static void
+frv_fill_unused_units (enum frv_insn_group group)
{
- int regno, reg_max;
- rtx reg;
- rtx cond;
- rtx dest;
- const char *format;
- int result;
- int length;
- int j;
+ unsigned int non_nops, nops, i;
+ struct frv_packet_group *packet_group;
- switch (GET_CODE (x))
- {
- default:
- break;
+ packet_group = &frv_packet.groups[group];
- /* Skip clobber, that doesn't use the previous value */
- case CLOBBER:
- return FALSE;
+ /* Sort the instructions into assembly-language order.
+ Use nops to fill slots that are otherwise unused. */
+ frv_sort_insn_group (group);
- /* For SET, if a conditional jump has occurred in the same insn, only
- allow a set of a CR register if that register is not currently live.
- This is because on the FR-V, B0/B1 instructions are always last.
- Otherwise, don't look at the result, except within a MEM, but do look
- at the source. */
- case SET:
- dest = SET_DEST (x);
- if (flag & REGSTATE_CONDJUMP
- && GET_CODE (dest) == REG && CR_P (REGNO (dest))
- && (reg_state[ REGNO (dest) ] & REGSTATE_LIVE) != 0)
- return TRUE;
-
- if (GET_CODE (dest) == MEM)
- {
- result = frv_registers_used_p (XEXP (dest, 0), reg_state, flag);
- if (result)
- return result;
- }
+ /* See how many nops are needed before the final useful instruction. */
+ i = nops = 0;
+ for (non_nops = 0; non_nops < packet_group->num_insns; non_nops++)
+ while (packet_group->sorted[i++] == packet_group->nop)
+ nops++;
- return frv_registers_used_p (SET_SRC (x), reg_state, flag);
-
- /* For COND_EXEC, pass the appropriate flag to evaluate the conditional
- statement, but just to be sure, make sure it is the type of cond_exec
- we expect. */
- case COND_EXEC:
- cond = XEXP (x, 0);
- if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
- && GET_CODE (XEXP (cond, 0)) == REG
- && CR_P (REGNO (XEXP (cond, 0)))
- && GET_CODE (XEXP (cond, 1)) == CONST_INT
- && INTVAL (XEXP (cond, 1)) == 0
- && (flag & (REGSTATE_MODIFIED | REGSTATE_IF_EITHER)) == 0)
- {
- result = frv_registers_used_p (cond, reg_state, flag);
- if (result)
- return result;
+ /* Insert that many nops into the instruction stream. */
+ while (nops-- > 0)
+ frv_insert_nop_in_packet (packet_group->nop);
+}
- flag |= ((REGNO (XEXP (cond, 0)) - CR_FIRST)
- | ((GET_CODE (cond) == NE)
- ? REGSTATE_IF_TRUE
- : REGSTATE_IF_FALSE));
+/* Return true if accesses IO1 and IO2 refer to the same doubleword. */
- return frv_registers_used_p (XEXP (x, 1), reg_state, flag);
- }
- else
- fatal_insn ("frv_registers_used_p", x);
+static bool
+frv_same_doubleword_p (const struct frv_io *io1, const struct frv_io *io2)
+{
+ if (io1->const_address != 0 && io2->const_address != 0)
+ return io1->const_address == io2->const_address;
- /* See if a register or subreg was modified in the same VLIW insn. */
- case SUBREG:
- reg = SUBREG_REG (x);
- if (GET_CODE (reg) == REG)
- {
- regno = subreg_regno (x);
- reg_max = REGNO (reg) + HARD_REGNO_NREGS (regno, GET_MODE (reg));
- goto reg_common;
- }
- break;
+ if (io1->var_address != 0 && io2->var_address != 0)
+ return rtx_equal_p (io1->var_address, io2->var_address);
- case REG:
- regno = REGNO (x);
- reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
- /* fall through */
+ return false;
+}
- reg_common:
- while (regno < reg_max)
- {
- int rs = reg_state[regno];
+/* Return true if operations IO1 and IO2 are guaranteed to complete
+ in order. */
- if (rs & REGSTATE_MODIFIED)
- {
- int rs_if = rs & REGSTATE_IF_EITHER;
- int flag_if = flag & REGSTATE_IF_EITHER;
-
- /* Simple modification, no conditional execution */
- if ((rs & REGSTATE_IF_EITHER) == 0)
- return TRUE;
-
- /* See if the variable is only modified in a conditional
- execution expression opposite to the conditional execution
- expression that governs this expression (ie, true vs. false
- for the same CC register). If this isn't two halves of the
- same conditional expression, consider the register
- modified. */
- if (((rs_if == REGSTATE_IF_TRUE && flag_if == REGSTATE_IF_FALSE)
- || (rs_if == REGSTATE_IF_FALSE && flag_if == REGSTATE_IF_TRUE))
- && ((rs & REGSTATE_CC_MASK) == (flag & REGSTATE_CC_MASK)))
- ;
- else
- return TRUE;
- }
+static bool
+frv_io_fixed_order_p (const struct frv_io *io1, const struct frv_io *io2)
+{
+ /* The order of writes is always preserved. */
+ if (io1->type == FRV_IO_WRITE && io2->type == FRV_IO_WRITE)
+ return true;
- regno++;
- }
- return FALSE;
- }
+ /* The order of reads isn't preserved. */
+ if (io1->type != FRV_IO_WRITE && io2->type != FRV_IO_WRITE)
+ return false;
+ /* One operation is a write and the other is (or could be) a read.
+ The order is only guaranteed if the accesses are to the same
+ doubleword. */
+ return frv_same_doubleword_p (io1, io2);
+}
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
+/* Generalize I/O operation X so that it covers both X and Y. */
- for (j = 0; j < length; ++j)
+static void
+frv_io_union (struct frv_io *x, const struct frv_io *y)
+{
+ if (x->type != y->type)
+ x->type = FRV_IO_UNKNOWN;
+ if (!frv_same_doubleword_p (x, y))
{
- switch (format[j])
- {
- case 'e':
- result = frv_registers_used_p (XEXP (x, j), reg_state, flag);
- if (result != 0)
- return result;
- break;
+ x->const_address = 0;
+ x->var_address = 0;
+ }
+}
- case 'V':
- case 'E':
- if (XVEC (x, j) != 0)
- {
- int k;
- for (k = 0; k < XVECLEN (x, j); ++k)
- {
- result = frv_registers_used_p (XVECEXP (x, j, k), reg_state,
- flag);
- if (result != 0)
- return result;
- }
- }
- break;
+/* Fill IO with information about the load or store associated with
+ membar instruction INSN. */
- default:
- /* Nothing to do. */
- break;
- }
- }
+static void
+frv_extract_membar (struct frv_io *io, rtx insn)
+{
+ extract_insn (insn);
+ io->type = INTVAL (recog_data.operand[2]);
+ io->const_address = INTVAL (recog_data.operand[1]);
+ io->var_address = XEXP (recog_data.operand[0], 0);
+}
- return 0;
+/* A note_stores callback for which DATA points to an rtx. Nullify *DATA
+ if X is a register and *DATA depends on X. */
+
+static void
+frv_io_check_address (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx *other = data;
+
+ if (REG_P (x) && *other != 0 && reg_overlap_mentioned_p (x, *other))
+ *other = 0;
}
-/* Return if any registers in a hard register set were set in an insn. */
+/* A note_stores callback for which DATA points to a HARD_REG_SET.
+ Remove every modified register from the set. */
-static int
-frv_registers_set_p (x, reg_state, modify_p)
- rtx x;
- unsigned char reg_state[];
- int modify_p;
+static void
+frv_io_handle_set (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
{
- int regno, reg_max;
- rtx reg;
- rtx cond;
- const char *format;
- int length;
- int j;
+ HARD_REG_SET *set = data;
+ unsigned int regno;
- switch (GET_CODE (x))
- {
- default:
- break;
+ if (REG_P (x))
+ FOR_EACH_REGNO (regno, x)
+ CLEAR_HARD_REG_BIT (*set, regno);
+}
- case CLOBBER:
- return frv_registers_set_p (XEXP (x, 0), reg_state, TRUE);
+/* A for_each_rtx callback for which DATA points to a HARD_REG_SET.
+ Add every register in *X to the set. */
- case PRE_MODIFY:
- case SET:
- return (frv_registers_set_p (XEXP (x, 0), reg_state, TRUE)
- || frv_registers_set_p (XEXP (x, 1), reg_state, FALSE));
-
- case COND_EXEC:
- cond = XEXP (x, 0);
- /* just to be sure, make sure it is the type of cond_exec we
- expect. */
- if ((GET_CODE (cond) == EQ || GET_CODE (cond) == NE)
- && GET_CODE (XEXP (cond, 0)) == REG
- && CR_P (REGNO (XEXP (cond, 0)))
- && GET_CODE (XEXP (cond, 1)) == CONST_INT
- && INTVAL (XEXP (cond, 1)) == 0
- && !modify_p)
- return frv_registers_set_p (XEXP (x, 1), reg_state, modify_p);
- else
- fatal_insn ("frv_registers_set_p", x);
+static int
+frv_io_handle_use_1 (rtx *x, void *data)
+{
+ HARD_REG_SET *set = data;
+ unsigned int regno;
- /* MEM resets the modification bits. */
- case MEM:
- modify_p = FALSE;
- break;
+ if (REG_P (*x))
+ FOR_EACH_REGNO (regno, *x)
+ SET_HARD_REG_BIT (*set, regno);
- /* See if we need to set the modified modify_p. */
- case SUBREG:
- reg = SUBREG_REG (x);
- if (GET_CODE (reg) == REG)
- {
- regno = subreg_regno (x);
- reg_max = REGNO (reg) + HARD_REGNO_NREGS (regno, GET_MODE (reg));
- goto reg_common;
- }
- break;
+ return 0;
+}
- case REG:
- regno = REGNO (x);
- reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
- /* fall through */
+/* A note_stores callback that applies frv_io_handle_use_1 to an
+ entire rhs value. */
- reg_common:
- if (modify_p)
- while (regno < reg_max)
- {
- int rs = reg_state[regno];
+static void
+frv_io_handle_use (rtx *x, void *data)
+{
+ for_each_rtx (x, frv_io_handle_use_1, data);
+}
- if (rs & REGSTATE_MODIFIED)
- return TRUE;
- regno++;
- }
- return FALSE;
- }
+/* Go through block BB looking for membars to remove. There are two
+ cases where intra-block analysis is enough:
+ - a membar is redundant if it occurs between two consecutive I/O
+ operations and if those operations are guaranteed to complete
+ in order.
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
+ - a membar for a __builtin_read is redundant if the result is
+ used before the next I/O operation is issued.
- for (j = 0; j < length; ++j)
- {
- switch (format[j])
- {
- case 'e':
- if (frv_registers_set_p (XEXP (x, j), reg_state, modify_p))
- return TRUE;
- break;
+ If the last membar in the block could not be removed, and there
+ are guaranteed to be no I/O operations between that membar and
+ the end of the block, store the membar in *LAST_MEMBAR, otherwise
+ store null.
- case 'V':
- case 'E':
- if (XVEC (x, j) != 0)
+ Describe the block's first I/O operation in *NEXT_IO. Describe
+ an unknown operation if the block doesn't do any I/O. */
+
+static void
+frv_optimize_membar_local (basic_block bb, struct frv_io *next_io,
+ rtx *last_membar)
+{
+ HARD_REG_SET used_regs;
+ rtx next_membar, set, insn;
+ bool next_is_end_p;
+
+ /* NEXT_IO is the next I/O operation to be performed after the current
+ instruction. It starts off as being an unknown operation. */
+ memset (next_io, 0, sizeof (*next_io));
+
+ /* NEXT_IS_END_P is true if NEXT_IO describes the end of the block. */
+ next_is_end_p = true;
+
+ /* If the current instruction is a __builtin_read or __builtin_write,
+ NEXT_MEMBAR is the membar instruction associated with it. NEXT_MEMBAR
+ is null if the membar has already been deleted.
+
+ Note that the initialization here should only be needed to
+ suppress warnings. */
+ next_membar = 0;
+
+ /* USED_REGS is the set of registers that are used before the
+ next I/O instruction. */
+ CLEAR_HARD_REG_SET (used_regs);
+
+ for (insn = BB_END (bb); insn != BB_HEAD (bb); insn = PREV_INSN (insn))
+ if (GET_CODE (insn) == CALL_INSN)
+ {
+ /* We can't predict what a call will do to volatile memory. */
+ memset (next_io, 0, sizeof (struct frv_io));
+ next_is_end_p = false;
+ CLEAR_HARD_REG_SET (used_regs);
+ }
+ else if (INSN_P (insn))
+ switch (recog_memoized (insn))
+ {
+ case CODE_FOR_optional_membar_qi:
+ case CODE_FOR_optional_membar_hi:
+ case CODE_FOR_optional_membar_si:
+ case CODE_FOR_optional_membar_di:
+ next_membar = insn;
+ if (next_is_end_p)
+ {
+ /* Local information isn't enough to decide whether this
+ membar is needed. Stash it away for later. */
+ *last_membar = insn;
+ frv_extract_membar (next_io, insn);
+ next_is_end_p = false;
+ }
+ else
{
- int k;
- for (k = 0; k < XVECLEN (x, j); ++k)
- if (frv_registers_set_p (XVECEXP (x, j, k), reg_state,
- modify_p))
- return TRUE;
+ /* Check whether the I/O operation before INSN could be
+ reordered with one described by NEXT_IO. If it can't,
+ INSN will not be needed. */
+ struct frv_io prev_io;
+
+ frv_extract_membar (&prev_io, insn);
+ if (frv_io_fixed_order_p (&prev_io, next_io))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Local] Removing membar %d since order"
+ " of accesses is guaranteed\n",
+ INSN_UID (next_membar));
+
+ insn = NEXT_INSN (insn);
+ delete_insn (next_membar);
+ next_membar = 0;
+ }
+ *next_io = prev_io;
}
break;
default:
- /* Nothing to do. */
+ /* Invalidate NEXT_IO's address if it depends on something that
+ is clobbered by INSN. */
+ if (next_io->var_address)
+ note_stores (PATTERN (insn), frv_io_check_address,
+ &next_io->var_address);
+
+ /* If the next membar is associated with a __builtin_read,
+ see if INSN reads from that address. If it does, and if
+ the destination register is used before the next I/O access,
+ there is no need for the membar. */
+ set = PATTERN (insn);
+ if (next_io->type == FRV_IO_READ
+ && next_io->var_address != 0
+ && next_membar != 0
+ && GET_CODE (set) == SET
+ && GET_CODE (SET_DEST (set)) == REG
+ && TEST_HARD_REG_BIT (used_regs, REGNO (SET_DEST (set))))
+ {
+ rtx src;
+
+ src = SET_SRC (set);
+ if (GET_CODE (src) == ZERO_EXTEND)
+ src = XEXP (src, 0);
+
+ if (GET_CODE (src) == MEM
+ && rtx_equal_p (XEXP (src, 0), next_io->var_address))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Local] Removing membar %d since the target"
+ " of %d is used before the I/O operation\n",
+ INSN_UID (next_membar), INSN_UID (insn));
+
+ if (next_membar == *last_membar)
+ *last_membar = 0;
+
+ delete_insn (next_membar);
+ next_membar = 0;
+ }
+ }
+
+ /* If INSN has volatile references, forget about any registers
+ that are used after it. Otherwise forget about uses that
+ are (or might be) defined by INSN. */
+ if (volatile_refs_p (PATTERN (insn)))
+ CLEAR_HARD_REG_SET (used_regs);
+ else
+ note_stores (PATTERN (insn), frv_io_handle_set, &used_regs);
+
+ note_uses (&PATTERN (insn), frv_io_handle_use, &used_regs);
break;
}
- }
-
- return FALSE;
}
-\f
-/* On the FR-V, this pass is used to rescan the insn chain, and pack
- conditional branches/calls/jumps, etc. with previous insns where it can. It
- does not reorder the instructions. We assume the scheduler left the flow
- information in a reasonable state. */
+/* See if MEMBAR, the last membar instruction in BB, can be removed.
+ FIRST_IO[X] describes the first operation performed by basic block X. */
static void
-frv_pack_insns ()
+frv_optimize_membar_global (basic_block bb, struct frv_io *first_io,
+ rtx membar)
{
- state_t frv_state; /* frv state machine */
- int cur_start_vliw_p; /* current insn starts a VLIW insn */
- int next_start_vliw_p; /* next insn starts a VLIW insn */
- int cur_condjump_p; /* flag if current insn is a cond jump*/
- int next_condjump_p; /* flag if next insn is a cond jump */
- rtx insn;
- rtx link;
- int j;
- int num_mod = 0; /* # of modified registers */
- int modified[FIRST_PSEUDO_REGISTER]; /* registers modified in current VLIW */
- /* register state information */
- unsigned char reg_state[FIRST_PSEUDO_REGISTER];
-
- /* If we weren't going to pack the insns, don't bother with this pass. */
- if (!optimize
- || !flag_schedule_insns_after_reload
- || TARGET_NO_VLIW_BRANCH
- || frv_issue_rate () == 1)
- return;
+ struct frv_io this_io, next_io;
+ edge succ;
+ edge_iterator ei;
- /* Set up the instruction and register states. */
- dfa_start ();
- frv_state = (state_t) xmalloc (state_size ());
- memset (reg_state, REGSTATE_DEAD, sizeof (reg_state));
+ /* We need to keep the membar if there is an edge to the exit block. */
+ FOR_EACH_EDGE (succ, ei, bb->succs)
+ /* for (succ = bb->succ; succ != 0; succ = succ->succ_next) */
+ if (succ->dest == EXIT_BLOCK_PTR)
+ return;
- /* Go through the insns, and repack the insns. */
- state_reset (frv_state);
- cur_start_vliw_p = FALSE;
- next_start_vliw_p = TRUE;
- cur_condjump_p = 0;
- next_condjump_p = 0;
+ /* Work out the union of all successor blocks. */
+ ei = ei_start (bb->succs);
+ ei_cond (ei, &succ);
+ /* next_io = first_io[bb->succ->dest->index]; */
+ next_io = first_io[succ->dest->index];
+ ei = ei_start (bb->succs);
+ if (ei_cond (ei, &succ))
+ {
+ for (ei_next (&ei); ei_cond (ei, &succ); ei_next (&ei))
+ /*for (succ = bb->succ->succ_next; succ != 0; succ = succ->succ_next)*/
+ frv_io_union (&next_io, &first_io[succ->dest->index]);
+ }
+ else
+ gcc_unreachable ();
- for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
+ frv_extract_membar (&this_io, membar);
+ if (frv_io_fixed_order_p (&this_io, &next_io))
{
- enum rtx_code code = GET_CODE (insn);
- enum rtx_code pattern_code;
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Global] Removing membar %d since order of accesses"
+ " is guaranteed\n", INSN_UID (membar));
- /* For basic block begin notes redo the live information, and skip other
- notes. */
- if (code == NOTE)
- {
- if (NOTE_LINE_NUMBER (insn) == (int)NOTE_INSN_BASIC_BLOCK)
- {
- regset live;
+ delete_insn (membar);
+ }
+}
- for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
- reg_state[j] &= ~ REGSTATE_LIVE;
+/* Remove redundant membars from the current function. */
- live = NOTE_BASIC_BLOCK (insn)->global_live_at_start;
- EXECUTE_IF_SET_IN_REG_SET(live, 0, j,
- {
- reg_state[j] |= REGSTATE_LIVE;
- });
- }
+static void
+frv_optimize_membar (void)
+{
+ basic_block bb;
+ struct frv_io *first_io;
+ rtx *last_membar;
- continue;
- }
+ compute_bb_for_insn ();
+ first_io = xcalloc (last_basic_block, sizeof (struct frv_io));
+ last_membar = xcalloc (last_basic_block, sizeof (rtx));
- /* things like labels reset everything. */
- if (GET_RTX_CLASS (code) != 'i')
- {
- next_start_vliw_p = TRUE;
- continue;
- }
+ FOR_EACH_BB (bb)
+ frv_optimize_membar_local (bb, &first_io[bb->index],
+ &last_membar[bb->index]);
- /* Clear the VLIW start flag on random USE and CLOBBER insns, which is
- set on the USE insn that preceeds the return, and potentially on
- CLOBBERs for setting multiword variables. Also skip the ADDR_VEC
- holding the case table labels. */
- pattern_code = GET_CODE (PATTERN (insn));
- if (pattern_code == USE || pattern_code == CLOBBER
- || pattern_code == ADDR_VEC || pattern_code == ADDR_DIFF_VEC)
- {
- CLEAR_VLIW_START (insn);
- continue;
- }
+ FOR_EACH_BB (bb)
+ if (last_membar[bb->index] != 0)
+ frv_optimize_membar_global (bb, first_io, last_membar[bb->index]);
- cur_start_vliw_p = next_start_vliw_p;
- next_start_vliw_p = FALSE;
+ free (first_io);
+ free (last_membar);
+}
+\f
+/* Used by frv_reorg to keep track of the current packet's address. */
+static unsigned int frv_packet_address;
- cur_condjump_p |= next_condjump_p;
- next_condjump_p = 0;
+/* If the current packet falls through to a label, try to pad the packet
+ with nops in order to fit the label's alignment requirements. */
- /* Unconditional branches and calls end the current VLIW insn. */
- if (code == CALL_INSN)
- {
- next_start_vliw_p = TRUE;
+static void
+frv_align_label (void)
+{
+ unsigned int alignment, target, nop;
+ rtx x, last, barrier, label;
- /* On a TOMCAT, calls must be alone in the VLIW insns. */
- if (frv_cpu_type == FRV_CPU_TOMCAT)
- cur_start_vliw_p = TRUE;
- }
- else if (code == JUMP_INSN)
+ /* Walk forward to the start of the next packet. Set ALIGNMENT to the
+ maximum alignment of that packet, LABEL to the last label between
+ the packets, and BARRIER to the last barrier. */
+ last = frv_packet.insns[frv_packet.num_insns - 1];
+ label = barrier = 0;
+ alignment = 4;
+ for (x = NEXT_INSN (last); x != 0 && !INSN_P (x); x = NEXT_INSN (x))
+ {
+ if (LABEL_P (x))
{
- if (any_condjump_p (insn))
- next_condjump_p = REGSTATE_CONDJUMP;
- else
- next_start_vliw_p = TRUE;
+ unsigned int subalign = 1 << label_to_alignment (x);
+ alignment = MAX (alignment, subalign);
+ label = x;
}
+ if (BARRIER_P (x))
+ barrier = x;
+ }
- /* Only allow setting a CCR register after a conditional branch. */
- else if (((cur_condjump_p & REGSTATE_CONDJUMP) != 0)
- && get_attr_type (insn) != TYPE_CCR)
- cur_start_vliw_p = TRUE;
-
- /* Determine if we need to start a new VLIW instruction. */
- if (cur_start_vliw_p
- /* Do not check for register conflicts in a setlo instruction
- because any output or true dependencies will be with the
- partnering sethi instruction, with which it can be packed.
-
- Although output dependencies are rare they are still
- possible. So check output dependencies in VLIW insn. */
- || (get_attr_type (insn) != TYPE_SETLO
- && (frv_registers_used_p (PATTERN (insn),
- reg_state,
- cur_condjump_p)
- || frv_registers_set_p (PATTERN (insn), reg_state, FALSE)))
- || state_transition (frv_state, insn) >= 0)
- {
- SET_VLIW_START (insn);
- state_reset (frv_state);
- state_transition (frv_state, insn);
- cur_condjump_p = 0;
-
- /* Update the modified registers. */
- for (j = 0; j < num_mod; j++)
- reg_state[ modified[j] ] &= ~(REGSTATE_CC_MASK
- | REGSTATE_IF_EITHER
- | REGSTATE_MODIFIED);
-
- num_mod = 0;
- }
- else
- CLEAR_VLIW_START (insn);
+ /* If -malign-labels, and the packet falls through to an unaligned
+ label, try introducing a nop to align that label to 8 bytes. */
+ if (TARGET_ALIGN_LABELS
+ && label != 0
+ && barrier == 0
+ && frv_packet.num_insns < frv_packet.issue_rate)
+ alignment = MAX (alignment, 8);
- /* Record which registers are modified. */
- frv_registers_update (PATTERN (insn), reg_state, modified, &num_mod, 0);
+ /* Advance the address to the end of the current packet. */
+ frv_packet_address += frv_packet.num_insns * 4;
- /* Process the death notices */
- for (link = REG_NOTES (insn);
- link != NULL_RTX;
- link = XEXP (link, 1))
- {
- rtx reg = XEXP (link, 0);
+ /* Work out the target address, after alignment. */
+ target = (frv_packet_address + alignment - 1) & -alignment;
- if (REG_NOTE_KIND (link) == REG_DEAD && GET_CODE (reg) == REG)
- {
- int regno = REGNO (reg);
- int n = regno + HARD_REGNO_NREGS (regno, GET_MODE (reg));
- for (; regno < n; regno++)
- reg_state[regno] &= ~REGSTATE_LIVE;
- }
+ /* If the packet falls through to the label, try to find an efficient
+ padding sequence. */
+ if (barrier == 0)
+ {
+ /* First try adding nops to the current packet. */
+ for (nop = 0; nop < frv_num_nops; nop++)
+ while (frv_packet_address < target && frv_pack_insn_p (frv_nops[nop]))
+ {
+ frv_insert_nop_in_packet (frv_nops[nop]);
+ frv_packet_address += 4;
+ }
+
+ /* If we still haven't reached the target, add some new packets that
+ contain only nops. If there are two types of nop, insert an
+ alternating sequence of frv_nops[0] and frv_nops[1], which will
+ lead to packets like:
+
+ nop.p
+ mnop.p/fnop.p
+ nop.p
+ mnop/fnop
+
+ etc. Just emit frv_nops[0] if that's the only nop we have. */
+ last = frv_packet.insns[frv_packet.num_insns - 1];
+ nop = 0;
+ while (frv_packet_address < target)
+ {
+ last = emit_insn_after (PATTERN (frv_nops[nop]), last);
+ frv_packet_address += 4;
+ if (frv_num_nops > 1)
+ nop ^= 1;
}
}
- free (frv_state);
- dfa_finish ();
- return;
+ frv_packet_address = target;
+}
+
+/* Subroutine of frv_reorg, called after each packet has been constructed
+ in frv_packet. */
+
+static void
+frv_reorg_packet (void)
+{
+ frv_fill_unused_units (GROUP_I);
+ frv_fill_unused_units (GROUP_FM);
+ frv_align_label ();
+}
+
+/* Add an instruction with pattern NOP to frv_nops[]. */
+
+static void
+frv_register_nop (rtx nop)
+{
+ nop = make_insn_raw (nop);
+ NEXT_INSN (nop) = 0;
+ PREV_INSN (nop) = 0;
+ frv_nops[frv_num_nops++] = nop;
}
+/* Implement TARGET_MACHINE_DEPENDENT_REORG. Divide the instructions
+ into packets and check whether we need to insert nops in order to
+ fulfill the processor's issue requirements. Also, if the user has
+ requested a certain alignment for a label, try to meet that alignment
+ by inserting nops in the previous packet. */
+
+static void
+frv_reorg (void)
+{
+ if (optimize > 0 && TARGET_OPTIMIZE_MEMBAR && cfun->machine->has_membar_p)
+ frv_optimize_membar ();
+
+ frv_num_nops = 0;
+ frv_register_nop (gen_nop ());
+ if (TARGET_MEDIA)
+ frv_register_nop (gen_mnop ());
+ if (TARGET_HARD_FLOAT)
+ frv_register_nop (gen_fnop ());
+
+ /* Estimate the length of each branch. Although this may change after
+ we've inserted nops, it will only do so in big functions. */
+ shorten_branches (get_insns ());
+
+ frv_packet_address = 0;
+ frv_for_each_packet (frv_reorg_packet);
+}
\f
#define def_builtin(name, type, code) \
- builtin_function ((name), (type), (code), BUILT_IN_MD, NULL, NULL)
+ add_builtin_function ((name), (type), (code), BUILT_IN_MD, NULL, NULL)
struct builtin_description
{
{ CODE_FOR_mhdsets, "__MHDSETS", FRV_BUILTIN_MHDSETS, 0, 0 }
};
-/* Media intrinsics that take just one argument. */
+/* Media intrinsics that take just one argument. */
static struct builtin_description bdesc_1arg[] =
{
{ CODE_FOR_munpackh, "__MUNPACKH", FRV_BUILTIN_MUNPACKH, 0, 0 },
{ CODE_FOR_mbtoh, "__MBTOH", FRV_BUILTIN_MBTOH, 0, 0 },
{ CODE_FOR_mhtob, "__MHTOB", FRV_BUILTIN_MHTOB, 0, 0 },
- { CODE_FOR_mabshs, "__MABSHS", FRV_BUILTIN_MABSHS, 0, 0 }
+ { CODE_FOR_mabshs, "__MABSHS", FRV_BUILTIN_MABSHS, 0, 0 },
+ { CODE_FOR_scutss, "__SCUTSS", FRV_BUILTIN_SCUTSS, 0, 0 }
};
-/* Media intrinsics that take two arguments. */
+/* Media intrinsics that take two arguments. */
static struct builtin_description bdesc_2arg[] =
{
{ CODE_FOR_mqsubhss, "__MQSUBHSS", FRV_BUILTIN_MQSUBHSS, 0, 0 },
{ CODE_FOR_mqsubhus, "__MQSUBHUS", FRV_BUILTIN_MQSUBHUS, 0, 0 },
{ CODE_FOR_mpackh, "__MPACKH", FRV_BUILTIN_MPACKH, 0, 0 },
- { CODE_FOR_mdpackh, "__MDPACKH", FRV_BUILTIN_MDPACKH, 0, 0 },
{ CODE_FOR_mcop1, "__Mcop1", FRV_BUILTIN_MCOP1, 0, 0 },
{ CODE_FOR_mcop2, "__Mcop2", FRV_BUILTIN_MCOP2, 0, 0 },
{ CODE_FOR_mwcut, "__MWCUT", FRV_BUILTIN_MWCUT, 0, 0 },
- { CODE_FOR_mqsaths, "__MQSATHS", FRV_BUILTIN_MQSATHS, 0, 0 }
+ { CODE_FOR_mqsaths, "__MQSATHS", FRV_BUILTIN_MQSATHS, 0, 0 },
+ { CODE_FOR_mqlclrhs, "__MQLCLRHS", FRV_BUILTIN_MQLCLRHS, 0, 0 },
+ { CODE_FOR_mqlmths, "__MQLMTHS", FRV_BUILTIN_MQLMTHS, 0, 0 },
+ { CODE_FOR_smul, "__SMUL", FRV_BUILTIN_SMUL, 0, 0 },
+ { CODE_FOR_umul, "__UMUL", FRV_BUILTIN_UMUL, 0, 0 },
+ { CODE_FOR_addss, "__ADDSS", FRV_BUILTIN_ADDSS, 0, 0 },
+ { CODE_FOR_subss, "__SUBSS", FRV_BUILTIN_SUBSS, 0, 0 },
+ { CODE_FOR_slass, "__SLASS", FRV_BUILTIN_SLASS, 0, 0 },
+ { CODE_FOR_scan, "__SCAN", FRV_BUILTIN_SCAN, 0, 0 }
+};
+
+/* Integer intrinsics that take two arguments and have no return value. */
+
+static struct builtin_description bdesc_int_void2arg[] =
+{
+ { CODE_FOR_smass, "__SMASS", FRV_BUILTIN_SMASS, 0, 0 },
+ { CODE_FOR_smsss, "__SMSSS", FRV_BUILTIN_SMSSS, 0, 0 },
+ { CODE_FOR_smu, "__SMU", FRV_BUILTIN_SMU, 0, 0 }
+};
+
+static struct builtin_description bdesc_prefetches[] =
+{
+ { CODE_FOR_frv_prefetch0, "__data_prefetch0", FRV_BUILTIN_PREFETCH0, 0, 0 },
+ { CODE_FOR_frv_prefetch, "__data_prefetch", FRV_BUILTIN_PREFETCH, 0, 0 }
};
/* Media intrinsics that take two arguments, the first being an ACC number. */
{ CODE_FOR_mdcutssi, "__MDCUTSSI", FRV_BUILTIN_MDCUTSSI, 0, 0 }
};
-/* Two-argument media intrinsics with an immediate second argument. */
+/* Two-argument media intrinsics with an immediate second argument. */
static struct builtin_description bdesc_2argimm[] =
{
{ CODE_FOR_mhsetloh, "__MHSETLOH", FRV_BUILTIN_MHSETLOH, 0, 0 },
{ CODE_FOR_mhsethis, "__MHSETHIS", FRV_BUILTIN_MHSETHIS, 0, 0 },
{ CODE_FOR_mhsethih, "__MHSETHIH", FRV_BUILTIN_MHSETHIH, 0, 0 },
- { CODE_FOR_mhdseth, "__MHDSETH", FRV_BUILTIN_MHDSETH, 0, 0 }
+ { CODE_FOR_mhdseth, "__MHDSETH", FRV_BUILTIN_MHDSETH, 0, 0 },
+ { CODE_FOR_mqsllhi, "__MQSLLHI", FRV_BUILTIN_MQSLLHI, 0, 0 },
+ { CODE_FOR_mqsrahi, "__MQSRAHI", FRV_BUILTIN_MQSRAHI, 0, 0 }
};
/* Media intrinsics that take two arguments and return void, the first argument
- being a pointer to 4 words in memory. */
+ being a pointer to 4 words in memory. */
static struct builtin_description bdesc_void2arg[] =
{
};
/* Media intrinsics that take three arguments, the first being a const_int that
- denotes an accumulator, and that return void. */
+ denotes an accumulator, and that return void. */
static struct builtin_description bdesc_void3arg[] =
{
{ CODE_FOR_mdasaccs, "__MDASACCS", FRV_BUILTIN_MDASACCS, 0, 0 }
};
-/* Initialize media builtins. */
+/* Intrinsics that load a value and then issue a MEMBAR. The load is
+ a normal move and the ICODE is for the membar. */
+
+static struct builtin_description bdesc_loads[] =
+{
+ { CODE_FOR_optional_membar_qi, "__builtin_read8",
+ FRV_BUILTIN_READ8, 0, 0 },
+ { CODE_FOR_optional_membar_hi, "__builtin_read16",
+ FRV_BUILTIN_READ16, 0, 0 },
+ { CODE_FOR_optional_membar_si, "__builtin_read32",
+ FRV_BUILTIN_READ32, 0, 0 },
+ { CODE_FOR_optional_membar_di, "__builtin_read64",
+ FRV_BUILTIN_READ64, 0, 0 }
+};
+
+/* Likewise stores. */
+
+static struct builtin_description bdesc_stores[] =
+{
+ { CODE_FOR_optional_membar_qi, "__builtin_write8",
+ FRV_BUILTIN_WRITE8, 0, 0 },
+ { CODE_FOR_optional_membar_hi, "__builtin_write16",
+ FRV_BUILTIN_WRITE16, 0, 0 },
+ { CODE_FOR_optional_membar_si, "__builtin_write32",
+ FRV_BUILTIN_WRITE32, 0, 0 },
+ { CODE_FOR_optional_membar_di, "__builtin_write64",
+ FRV_BUILTIN_WRITE64, 0, 0 },
+};
+
+/* Initialize media builtins. */
static void
-frv_init_builtins ()
+frv_init_builtins (void)
{
tree endlink = void_list_node;
tree accumulator = integer_type_node;
tree sword2 = long_long_integer_type_node;
tree uword2 = long_long_unsigned_type_node;
tree uword4 = build_pointer_type (uword1);
+ tree vptr = build_pointer_type (build_type_variant (void_type_node, 0, 1));
+ tree ubyte = unsigned_char_type_node;
+ tree iacc = integer_type_node;
#define UNARY(RET, T1) \
build_function_type (RET, tree_cons (NULL_TREE, T1, endlink))
tree_cons (NULL_TREE, T2, \
tree_cons (NULL_TREE, T3, endlink))))
+#define QUAD(RET, T1, T2, T3, T4) \
+ build_function_type (RET, tree_cons (NULL_TREE, T1, \
+ tree_cons (NULL_TREE, T2, \
+ tree_cons (NULL_TREE, T3, \
+ tree_cons (NULL_TREE, T4, endlink)))))
+
tree void_ftype_void = build_function_type (voidt, endlink);
tree void_ftype_acc = UNARY (voidt, accumulator);
tree uw2_ftype_uw2_uw2 = BINARY (uword2, uword2, uword2);
tree uw2_ftype_uw2_int = BINARY (uword2, uword2, integer);
tree uw2_ftype_acc_int = BINARY (uword2, accumulator, integer);
+ tree uw2_ftype_uh_uh_uh_uh = QUAD (uword2, uhalf, uhalf, uhalf, uhalf);
tree sw2_ftype_sw2_sw2 = BINARY (sword2, sword2, sword2);
+ tree sw2_ftype_sw2_int = BINARY (sword2, sword2, integer);
+ tree uw2_ftype_uw1_uw1 = BINARY (uword2, uword1, uword1);
+ tree sw2_ftype_sw1_sw1 = BINARY (sword2, sword1, sword1);
+ tree void_ftype_sw1_sw1 = BINARY (voidt, sword1, sword1);
+ tree void_ftype_iacc_sw2 = BINARY (voidt, iacc, sword2);
+ tree void_ftype_iacc_sw1 = BINARY (voidt, iacc, sword1);
+ tree sw1_ftype_sw1 = UNARY (sword1, sword1);
+ tree sw2_ftype_iacc = UNARY (sword2, iacc);
+ tree sw1_ftype_iacc = UNARY (sword1, iacc);
+ tree void_ftype_ptr = UNARY (voidt, const_ptr_type_node);
+ tree uw1_ftype_vptr = UNARY (uword1, vptr);
+ tree uw2_ftype_vptr = UNARY (uword2, vptr);
+ tree void_ftype_vptr_ub = BINARY (voidt, vptr, ubyte);
+ tree void_ftype_vptr_uh = BINARY (voidt, vptr, uhalf);
+ tree void_ftype_vptr_uw1 = BINARY (voidt, vptr, uword1);
+ tree void_ftype_vptr_uw2 = BINARY (voidt, vptr, uword2);
def_builtin ("__MAND", uw1_ftype_uw1_uw1, FRV_BUILTIN_MAND);
def_builtin ("__MOR", uw1_ftype_uw1_uw1, FRV_BUILTIN_MOR);
def_builtin ("__MEXPDHD", uw2_ftype_uw1_int, FRV_BUILTIN_MEXPDHD);
def_builtin ("__MPACKH", uw1_ftype_uh_uh, FRV_BUILTIN_MPACKH);
def_builtin ("__MUNPACKH", uw2_ftype_uw1, FRV_BUILTIN_MUNPACKH);
- def_builtin ("__MDPACKH", uw2_ftype_uw2_uw2, FRV_BUILTIN_MDPACKH);
- def_builtin ("__MDUNPACKH", void_ftype_uw4_uw2, FRV_BUILTIN_MDUNPACKH);
+ def_builtin ("__MDPACKH", uw2_ftype_uh_uh_uh_uh, FRV_BUILTIN_MDPACKH);
+ def_builtin ("__MDUNPACKH", void_ftype_uw4_uw2, FRV_BUILTIN_MDUNPACKH);
def_builtin ("__MBTOH", uw2_ftype_uw1, FRV_BUILTIN_MBTOH);
def_builtin ("__MHTOB", uw1_ftype_uw2, FRV_BUILTIN_MHTOB);
def_builtin ("__MBTOHE", void_ftype_uw4_uw1, FRV_BUILTIN_MBTOHE);
def_builtin ("__MHSETLOH", uw1_ftype_uw1_int, FRV_BUILTIN_MHSETLOH);
def_builtin ("__MHSETHIH", uw1_ftype_uw1_int, FRV_BUILTIN_MHSETHIH);
def_builtin ("__MHDSETH", uw1_ftype_uw1_int, FRV_BUILTIN_MHDSETH);
+ def_builtin ("__MQLCLRHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQLCLRHS);
+ def_builtin ("__MQLMTHS", sw2_ftype_sw2_sw2, FRV_BUILTIN_MQLMTHS);
+ def_builtin ("__MQSLLHI", uw2_ftype_uw2_int, FRV_BUILTIN_MQSLLHI);
+ def_builtin ("__MQSRAHI", sw2_ftype_sw2_int, FRV_BUILTIN_MQSRAHI);
+ def_builtin ("__SMUL", sw2_ftype_sw1_sw1, FRV_BUILTIN_SMUL);
+ def_builtin ("__UMUL", uw2_ftype_uw1_uw1, FRV_BUILTIN_UMUL);
+ def_builtin ("__SMASS", void_ftype_sw1_sw1, FRV_BUILTIN_SMASS);
+ def_builtin ("__SMSSS", void_ftype_sw1_sw1, FRV_BUILTIN_SMSSS);
+ def_builtin ("__SMU", void_ftype_sw1_sw1, FRV_BUILTIN_SMU);
+ def_builtin ("__ADDSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_ADDSS);
+ def_builtin ("__SUBSS", sw1_ftype_sw1_sw1, FRV_BUILTIN_SUBSS);
+ def_builtin ("__SLASS", sw1_ftype_sw1_sw1, FRV_BUILTIN_SLASS);
+ def_builtin ("__SCAN", sw1_ftype_sw1_sw1, FRV_BUILTIN_SCAN);
+ def_builtin ("__SCUTSS", sw1_ftype_sw1, FRV_BUILTIN_SCUTSS);
+ def_builtin ("__IACCreadll", sw2_ftype_iacc, FRV_BUILTIN_IACCreadll);
+ def_builtin ("__IACCreadl", sw1_ftype_iacc, FRV_BUILTIN_IACCreadl);
+ def_builtin ("__IACCsetll", void_ftype_iacc_sw2, FRV_BUILTIN_IACCsetll);
+ def_builtin ("__IACCsetl", void_ftype_iacc_sw1, FRV_BUILTIN_IACCsetl);
+ def_builtin ("__data_prefetch0", void_ftype_ptr, FRV_BUILTIN_PREFETCH0);
+ def_builtin ("__data_prefetch", void_ftype_ptr, FRV_BUILTIN_PREFETCH);
+ def_builtin ("__builtin_read8", uw1_ftype_vptr, FRV_BUILTIN_READ8);
+ def_builtin ("__builtin_read16", uw1_ftype_vptr, FRV_BUILTIN_READ16);
+ def_builtin ("__builtin_read32", uw1_ftype_vptr, FRV_BUILTIN_READ32);
+ def_builtin ("__builtin_read64", uw2_ftype_vptr, FRV_BUILTIN_READ64);
+
+ def_builtin ("__builtin_write8", void_ftype_vptr_ub, FRV_BUILTIN_WRITE8);
+ def_builtin ("__builtin_write16", void_ftype_vptr_uh, FRV_BUILTIN_WRITE16);
+ def_builtin ("__builtin_write32", void_ftype_vptr_uw1, FRV_BUILTIN_WRITE32);
+ def_builtin ("__builtin_write64", void_ftype_vptr_uw2, FRV_BUILTIN_WRITE64);
#undef UNARY
#undef BINARY
#undef TRINARY
+#undef QUAD
}
/* Set the names for various arithmetic operations according to the
set_optab_libfunc (smul_optab, DFmode, "__muld");
set_optab_libfunc (sdiv_optab, DFmode, "__divd");
- fixsfsi_libfunc = init_one_libfunc ("__ftoi");
- fixunssfsi_libfunc = init_one_libfunc ("__ftoui");
- fixsfdi_libfunc = init_one_libfunc ("__ftoll");
- fixunssfdi_libfunc = init_one_libfunc ("__ftoull");
- fixdfsi_libfunc = init_one_libfunc ("__dtoi");
- fixunsdfsi_libfunc = init_one_libfunc ("__dtoui");
- fixdfdi_libfunc = init_one_libfunc ("__dtoll");
- fixunsdfdi_libfunc = init_one_libfunc ("__dtoull");
- floatsisf_libfunc = init_one_libfunc ("__itof");
- floatdisf_libfunc = init_one_libfunc ("__lltof");
- floatsidf_libfunc = init_one_libfunc ("__itod");
- floatdidf_libfunc = init_one_libfunc ("__lltod");
- extendsfdf2_libfunc = init_one_libfunc ("__ftod");
- truncdfsf2_libfunc = init_one_libfunc ("__dtof");
+ set_conv_libfunc (sext_optab, DFmode, SFmode, "__ftod");
+ set_conv_libfunc (trunc_optab, SFmode, DFmode, "__dtof");
+
+ set_conv_libfunc (sfix_optab, SImode, SFmode, "__ftoi");
+ set_conv_libfunc (sfix_optab, DImode, SFmode, "__ftoll");
+ set_conv_libfunc (sfix_optab, SImode, DFmode, "__dtoi");
+ set_conv_libfunc (sfix_optab, DImode, DFmode, "__dtoll");
+
+ set_conv_libfunc (ufix_optab, SImode, SFmode, "__ftoui");
+ set_conv_libfunc (ufix_optab, DImode, SFmode, "__ftoull");
+ set_conv_libfunc (ufix_optab, SImode, DFmode, "__dtoui");
+ set_conv_libfunc (ufix_optab, DImode, DFmode, "__dtoull");
+
+ set_conv_libfunc (sfloat_optab, SFmode, SImode, "__itof");
+ set_conv_libfunc (sfloat_optab, SFmode, DImode, "__lltof");
+ set_conv_libfunc (sfloat_optab, DFmode, SImode, "__itod");
+ set_conv_libfunc (sfloat_optab, DFmode, DImode, "__lltod");
}
/* Convert an integer constant to an accumulator register. ICODE is the
instruction. */
static rtx
-frv_int_to_acc (icode, opnum, opval)
- enum insn_code icode;
- int opnum;
- rtx opval;
+frv_int_to_acc (enum insn_code icode, int opnum, rtx opval)
{
rtx reg;
+ int i;
+
+ /* ACCs and ACCGs are implicit global registers if media intrinsics
+ are being used. We set up this lazily to avoid creating lots of
+ unnecessary call_insn rtl in non-media code. */
+ for (i = 0; i <= ACC_MASK; i++)
+ if ((i & ACC_MASK) == i)
+ global_regs[i + ACC_FIRST] = global_regs[i + ACCG_FIRST] = 1;
if (GET_CODE (opval) != CONST_INT)
{
error ("accumulator is not a constant integer");
return NULL_RTX;
}
- if (! IN_RANGE_P (INTVAL (opval), 0, NUM_ACCS - 1))
+ if ((INTVAL (opval) & ~ACC_MASK) != 0)
{
error ("accumulator number is out of bounds");
return NULL_RTX;
if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode))
{
- error ("inappropriate accumulator for `%s'", insn_data[icode].name);
+ error ("inappropriate accumulator for %qs", insn_data[icode].name);
return NULL_RTX;
}
return reg;
should have. */
static enum machine_mode
-frv_matching_accg_mode (mode)
- enum machine_mode mode;
+frv_matching_accg_mode (enum machine_mode mode)
{
switch (mode)
{
return QImode;
default:
- abort ();
+ gcc_unreachable ();
}
}
+/* Given that a __builtin_read or __builtin_write function is accessing
+ address ADDRESS, return the value that should be used as operand 1
+ of the membar. */
+
+static rtx
+frv_io_address_cookie (rtx address)
+{
+ return (GET_CODE (address) == CONST_INT
+ ? GEN_INT (INTVAL (address) / 8 * 8)
+ : const0_rtx);
+}
+
/* Return the accumulator guard that should be paired with accumulator
register ACC. The mode of the returned register is in the same
class as ACC, but is four times smaller. */
rtx
-frv_matching_accg_for_acc (acc)
- rtx acc;
+frv_matching_accg_for_acc (rtx acc)
{
return gen_rtx_REG (frv_matching_accg_mode (GET_MODE (acc)),
REGNO (acc) - ACC_FIRST + ACCG_FIRST);
}
-/* Read a value from the head of the tree list pointed to by ARGLISTPTR.
- Return the value as an rtx and replace *ARGLISTPTR with the tail of the
- list. */
+/* Read the requested argument from the call EXP given by INDEX.
+ Return the value as an rtx. */
+
+static rtx
+frv_read_argument (tree exp, unsigned int index)
+{
+ return expand_expr (CALL_EXPR_ARG (exp, index),
+ NULL_RTX, VOIDmode, 0);
+}
+
+/* Like frv_read_argument, but interpret the argument as the number
+ of an IACC register and return a (reg:MODE ...) rtx for it. */
static rtx
-frv_read_argument (arglistptr)
- tree *arglistptr;
+frv_read_iacc_argument (enum machine_mode mode, tree call,
+ unsigned int index)
{
- tree next = TREE_VALUE (*arglistptr);
- *arglistptr = TREE_CHAIN (*arglistptr);
- return expand_expr (next, NULL_RTX, VOIDmode, 0);
+ int i, regno;
+ rtx op;
+
+ op = frv_read_argument (call, index);
+ if (GET_CODE (op) != CONST_INT
+ || INTVAL (op) < 0
+ || INTVAL (op) > IACC_LAST - IACC_FIRST
+ || ((INTVAL (op) * 4) & (GET_MODE_SIZE (mode) - 1)) != 0)
+ {
+ error ("invalid IACC argument");
+ op = const0_rtx;
+ }
+
+ /* IACCs are implicit global registers. We set up this lazily to
+ avoid creating lots of unnecessary call_insn rtl when IACCs aren't
+ being used. */
+ regno = INTVAL (op) + IACC_FIRST;
+ for (i = 0; i < HARD_REGNO_NREGS (regno, mode); i++)
+ global_regs[regno + i] = 1;
+
+ return gen_rtx_REG (mode, regno);
}
/* Return true if OPVAL can be used for operand OPNUM of instruction ICODE.
function prints an error if OPVAL is not valid. */
static int
-frv_check_constant_argument (icode, opnum, opval)
- enum insn_code icode;
- int opnum;
- rtx opval;
+frv_check_constant_argument (enum insn_code icode, int opnum, rtx opval)
{
if (GET_CODE (opval) != CONST_INT)
{
- error ("`%s' expects a constant argument", insn_data[icode].name);
+ error ("%qs expects a constant argument", insn_data[icode].name);
return FALSE;
}
if (! (*insn_data[icode].operand[opnum].predicate) (opval, VOIDmode))
{
- error ("constant argument out of range for `%s'", insn_data[icode].name);
+ error ("constant argument out of range for %qs", insn_data[icode].name);
return FALSE;
}
return TRUE;
predicate. */
static rtx
-frv_legitimize_target (icode, target)
- enum insn_code icode;
- rtx target;
+frv_legitimize_target (enum insn_code icode, rtx target)
{
enum machine_mode mode = insn_data[icode].operand[0].mode;
}
/* Given that ARG is being passed as operand OPNUM to instruction ICODE,
- check whether ARG satisfies the operand's contraints. If it doesn't,
+ check whether ARG satisfies the operand's constraints. If it doesn't,
copy ARG to a temporary register and return that. Otherwise return ARG
itself. */
static rtx
-frv_legitimize_argument (icode, opnum, arg)
- enum insn_code icode;
- int opnum;
- rtx arg;
+frv_legitimize_argument (enum insn_code icode, int opnum, rtx arg)
{
enum machine_mode mode = insn_data[icode].operand[opnum].mode;
return copy_to_mode_reg (mode, arg);
}
+/* Return a volatile memory reference of mode MODE whose address is ARG. */
+
+static rtx
+frv_volatile_memref (enum machine_mode mode, rtx arg)
+{
+ rtx mem;
+
+ mem = gen_rtx_MEM (mode, memory_address (mode, arg));
+ MEM_VOLATILE_P (mem) = 1;
+ return mem;
+}
+
/* Expand builtins that take a single, constant argument. At the moment,
only MHDSETS falls into this category. */
static rtx
-frv_expand_set_builtin (icode, arglist, target)
- enum insn_code icode;
- tree arglist;
- rtx target;
+frv_expand_set_builtin (enum insn_code icode, tree call, rtx target)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
if (! frv_check_constant_argument (icode, 1, op0))
return NULL_RTX;
return target;
}
-/* Expand builtins that take one operand. */
+/* Expand builtins that take one operand. */
static rtx
-frv_expand_unop_builtin (icode, arglist, target)
- enum insn_code icode;
- tree arglist;
- rtx target;
+frv_expand_unop_builtin (enum insn_code icode, tree call, rtx target)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
target = frv_legitimize_target (icode, target);
op0 = frv_legitimize_argument (icode, 1, op0);
return target;
}
-/* Expand builtins that take two operands. */
+/* Expand builtins that take two operands. */
static rtx
-frv_expand_binop_builtin (icode, arglist, target)
- enum insn_code icode;
- tree arglist;
- rtx target;
+frv_expand_binop_builtin (enum insn_code icode, tree call, rtx target)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
target = frv_legitimize_target (icode, target);
op0 = frv_legitimize_argument (icode, 1, op0);
}
/* Expand cut-style builtins, which take two operands and an implicit ACCG
- one. */
+ one. */
static rtx
-frv_expand_cut_builtin (icode, arglist, target)
- enum insn_code icode;
- tree arglist;
- rtx target;
+frv_expand_cut_builtin (enum insn_code icode, tree call, rtx target)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
rtx op2;
target = frv_legitimize_target (icode, target);
return target;
}
-/* Expand builtins that take two operands and the second is immediate. */
+/* Expand builtins that take two operands and the second is immediate. */
static rtx
-frv_expand_binopimm_builtin (icode, arglist, target)
- enum insn_code icode;
- tree arglist;
- rtx target;
+frv_expand_binopimm_builtin (enum insn_code icode, tree call, rtx target)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
if (! frv_check_constant_argument (icode, 2, op1))
return NULL_RTX;
}
/* Expand builtins that take two operands, the first operand being a pointer to
- ints and return void. */
+ ints and return void. */
static rtx
-frv_expand_voidbinop_builtin (icode, arglist)
- enum insn_code icode;
- tree arglist;
+frv_expand_voidbinop_builtin (enum insn_code icode, tree call)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
enum machine_mode mode0 = insn_data[icode].operand[0].mode;
rtx addr;
return 0;
}
+/* Expand builtins that take two long operands and return void. */
+
+static rtx
+frv_expand_int_void2arg (enum insn_code icode, tree call)
+{
+ rtx pat;
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
+
+ op0 = frv_legitimize_argument (icode, 1, op0);
+ op1 = frv_legitimize_argument (icode, 1, op1);
+ pat = GEN_FCN (icode) (op0, op1);
+ if (! pat)
+ return NULL_RTX;
+
+ emit_insn (pat);
+ return NULL_RTX;
+}
+
+/* Expand prefetch builtins. These take a single address as argument. */
+
+static rtx
+frv_expand_prefetches (enum insn_code icode, tree call)
+{
+ rtx pat;
+ rtx op0 = frv_read_argument (call, 0);
+
+ pat = GEN_FCN (icode) (force_reg (Pmode, op0));
+ if (! pat)
+ return 0;
+
+ emit_insn (pat);
+ return 0;
+}
+
/* Expand builtins that take three operands and return void. The first
argument must be a constant that describes a pair or quad accumulators. A
fourth argument is created that is the accumulator guard register that
corresponds to the accumulator. */
static rtx
-frv_expand_voidtriop_builtin (icode, arglist)
- enum insn_code icode;
- tree arglist;
+frv_expand_voidtriop_builtin (enum insn_code icode, tree call)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
- rtx op2 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
+ rtx op2 = frv_read_argument (call, 2);
rtx op3;
op0 = frv_int_to_acc (icode, 0, op0);
void. */
static rtx
-frv_expand_voidaccop_builtin (icode, arglist)
- enum insn_code icode;
- tree arglist;
+frv_expand_voidaccop_builtin (enum insn_code icode, tree call)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
rtx op2;
rtx op3;
return NULL_RTX;
}
+/* Expand a __builtin_read* function. ICODE is the instruction code for the
+ membar and TARGET_MODE is the mode that the loaded value should have. */
+
+static rtx
+frv_expand_load_builtin (enum insn_code icode, enum machine_mode target_mode,
+ tree call, rtx target)
+{
+ rtx op0 = frv_read_argument (call, 0);
+ rtx cookie = frv_io_address_cookie (op0);
+
+ if (target == 0 || !REG_P (target))
+ target = gen_reg_rtx (target_mode);
+ op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0);
+ convert_move (target, op0, 1);
+ emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_READ)));
+ cfun->machine->has_membar_p = 1;
+ return target;
+}
+
+/* Likewise __builtin_write* functions. */
+
+static rtx
+frv_expand_store_builtin (enum insn_code icode, tree call)
+{
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
+ rtx cookie = frv_io_address_cookie (op0);
+
+ op0 = frv_volatile_memref (insn_data[icode].operand[0].mode, op0);
+ convert_move (op0, force_reg (insn_data[icode].operand[0].mode, op1), 1);
+ emit_insn (GEN_FCN (icode) (copy_rtx (op0), cookie, GEN_INT (FRV_IO_WRITE)));
+ cfun->machine->has_membar_p = 1;
+ return NULL_RTX;
+}
+
+/* Expand the MDPACKH builtin. It takes four unsigned short arguments and
+ each argument forms one word of the two double-word input registers.
+ CALL is the tree for the call and TARGET, if nonnull, suggests a good place
+ to put the return value. */
+
+static rtx
+frv_expand_mdpackh_builtin (tree call, rtx target)
+{
+ enum insn_code icode = CODE_FOR_mdpackh;
+ rtx pat, op0, op1;
+ rtx arg1 = frv_read_argument (call, 0);
+ rtx arg2 = frv_read_argument (call, 1);
+ rtx arg3 = frv_read_argument (call, 2);
+ rtx arg4 = frv_read_argument (call, 3);
+
+ target = frv_legitimize_target (icode, target);
+ op0 = gen_reg_rtx (DImode);
+ op1 = gen_reg_rtx (DImode);
+
+ /* The high half of each word is not explicitly initialized, so indicate
+ that the input operands are not live before this point. */
+ emit_insn (gen_rtx_CLOBBER (DImode, op0));
+ emit_insn (gen_rtx_CLOBBER (DImode, op1));
+
+ /* Move each argument into the low half of its associated input word. */
+ emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 2), arg1);
+ emit_move_insn (simplify_gen_subreg (HImode, op0, DImode, 6), arg2);
+ emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 2), arg3);
+ emit_move_insn (simplify_gen_subreg (HImode, op1, DImode, 6), arg4);
+
+ pat = GEN_FCN (icode) (target, op0, op1);
+ if (! pat)
+ return NULL_RTX;
+
+ emit_insn (pat);
+ return target;
+}
+
/* Expand the MCLRACC builtin. This builtin takes a single accumulator
number as argument. */
static rtx
-frv_expand_mclracc_builtin (arglist)
- tree arglist;
+frv_expand_mclracc_builtin (tree call)
{
enum insn_code icode = CODE_FOR_mclracc;
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
op0 = frv_int_to_acc (icode, 0, op0);
if (! op0)
/* Expand builtins that take no arguments. */
static rtx
-frv_expand_noargs_builtin (icode)
- enum insn_code icode;
+frv_expand_noargs_builtin (enum insn_code icode)
{
- rtx pat = GEN_FCN (icode) (GEN_INT (0));
+ rtx pat = GEN_FCN (icode) (const0_rtx);
if (pat)
emit_insn (pat);
number or accumulator guard number as argument and return an SI integer. */
static rtx
-frv_expand_mrdacc_builtin (icode, arglist)
- enum insn_code icode;
- tree arglist;
+frv_expand_mrdacc_builtin (enum insn_code icode, tree call)
{
rtx pat;
rtx target = gen_reg_rtx (SImode);
- rtx op0 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
op0 = frv_int_to_acc (icode, 1, op0);
if (! op0)
second. */
static rtx
-frv_expand_mwtacc_builtin (icode, arglist)
- enum insn_code icode;
- tree arglist;
+frv_expand_mwtacc_builtin (enum insn_code icode, tree call)
{
rtx pat;
- rtx op0 = frv_read_argument (&arglist);
- rtx op1 = frv_read_argument (&arglist);
+ rtx op0 = frv_read_argument (call, 0);
+ rtx op1 = frv_read_argument (call, 1);
op0 = frv_int_to_acc (icode, 0, op0);
if (! op0)
return NULL_RTX;
}
-/* Expand builtins. */
+/* Emit a move from SRC to DEST in SImode chunks. This can be used
+ to move DImode values into and out of IACC0. */
+
+static void
+frv_split_iacc_move (rtx dest, rtx src)
+{
+ enum machine_mode inner;
+ int i;
+
+ inner = GET_MODE (dest);
+ for (i = 0; i < GET_MODE_SIZE (inner); i += GET_MODE_SIZE (SImode))
+ emit_move_insn (simplify_gen_subreg (SImode, dest, inner, i),
+ simplify_gen_subreg (SImode, src, inner, i));
+}
+
+/* Expand builtins. */
static rtx
-frv_expand_builtin (exp, target, subtarget, mode, ignore)
- tree exp;
- rtx target;
- rtx subtarget ATTRIBUTE_UNUSED;
- enum machine_mode mode ATTRIBUTE_UNUSED;
- int ignore ATTRIBUTE_UNUSED;
-{
- tree arglist = TREE_OPERAND (exp, 1);
- tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+frv_expand_builtin (tree exp,
+ rtx target,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore ATTRIBUTE_UNUSED)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
unsigned fcode = (unsigned)DECL_FUNCTION_CODE (fndecl);
unsigned i;
struct builtin_description *d;
- if (! TARGET_MEDIA)
+ if (fcode < FRV_BUILTIN_FIRST_NONMEDIA && !TARGET_MEDIA)
{
error ("media functions are not available unless -mmedia is used");
return NULL_RTX;
case FRV_BUILTIN_MHDSETH:
if (! TARGET_MEDIA_REV2)
{
- error ("this media function is only available on the fr400");
+ error ("this media function is only available on the fr400"
+ " and fr550");
+ return NULL_RTX;
+ }
+ break;
+
+ case FRV_BUILTIN_SMASS:
+ case FRV_BUILTIN_SMSSS:
+ case FRV_BUILTIN_SMU:
+ case FRV_BUILTIN_ADDSS:
+ case FRV_BUILTIN_SUBSS:
+ case FRV_BUILTIN_SLASS:
+ case FRV_BUILTIN_SCUTSS:
+ case FRV_BUILTIN_IACCreadll:
+ case FRV_BUILTIN_IACCreadl:
+ case FRV_BUILTIN_IACCsetll:
+ case FRV_BUILTIN_IACCsetl:
+ if (!TARGET_FR405_BUILTINS)
+ {
+ error ("this builtin function is only available"
+ " on the fr405 and fr450");
+ return NULL_RTX;
+ }
+ break;
+
+ case FRV_BUILTIN_PREFETCH:
+ if (!TARGET_FR500_FR550_BUILTINS)
+ {
+ error ("this builtin function is only available on the fr500"
+ " and fr550");
+ return NULL_RTX;
+ }
+ break;
+
+ case FRV_BUILTIN_MQLCLRHS:
+ case FRV_BUILTIN_MQLMTHS:
+ case FRV_BUILTIN_MQSLLHI:
+ case FRV_BUILTIN_MQSRAHI:
+ if (!TARGET_MEDIA_FR450)
+ {
+ error ("this builtin function is only available on the fr450");
return NULL_RTX;
}
break;
break;
}
- /* Expand unique builtins. */
+ /* Expand unique builtins. */
switch (fcode)
{
return frv_expand_noargs_builtin (CODE_FOR_mtrap);
case FRV_BUILTIN_MCLRACC:
- return frv_expand_mclracc_builtin (arglist);
+ return frv_expand_mclracc_builtin (exp);
case FRV_BUILTIN_MCLRACCA:
if (TARGET_ACC_8)
return frv_expand_noargs_builtin (CODE_FOR_mclracca4);
case FRV_BUILTIN_MRDACC:
- return frv_expand_mrdacc_builtin (CODE_FOR_mrdacc, arglist);
+ return frv_expand_mrdacc_builtin (CODE_FOR_mrdacc, exp);
case FRV_BUILTIN_MRDACCG:
- return frv_expand_mrdacc_builtin (CODE_FOR_mrdaccg, arglist);
+ return frv_expand_mrdacc_builtin (CODE_FOR_mrdaccg, exp);
case FRV_BUILTIN_MWTACC:
- return frv_expand_mwtacc_builtin (CODE_FOR_mwtacc, arglist);
+ return frv_expand_mwtacc_builtin (CODE_FOR_mwtacc, exp);
case FRV_BUILTIN_MWTACCG:
- return frv_expand_mwtacc_builtin (CODE_FOR_mwtaccg, arglist);
+ return frv_expand_mwtacc_builtin (CODE_FOR_mwtaccg, exp);
+
+ case FRV_BUILTIN_MDPACKH:
+ return frv_expand_mdpackh_builtin (exp, target);
+
+ case FRV_BUILTIN_IACCreadll:
+ {
+ rtx src = frv_read_iacc_argument (DImode, exp, 0);
+ if (target == 0 || !REG_P (target))
+ target = gen_reg_rtx (DImode);
+ frv_split_iacc_move (target, src);
+ return target;
+ }
+
+ case FRV_BUILTIN_IACCreadl:
+ return frv_read_iacc_argument (SImode, exp, 0);
+
+ case FRV_BUILTIN_IACCsetll:
+ {
+ rtx dest = frv_read_iacc_argument (DImode, exp, 0);
+ rtx src = frv_read_argument (exp, 1);
+ frv_split_iacc_move (dest, force_reg (DImode, src));
+ return 0;
+ }
+
+ case FRV_BUILTIN_IACCsetl:
+ {
+ rtx dest = frv_read_iacc_argument (SImode, exp, 0);
+ rtx src = frv_read_argument (exp, 1);
+ emit_move_insn (dest, force_reg (SImode, src));
+ return 0;
+ }
default:
break;
}
- /* Expand groups of builtins. */
+ /* Expand groups of builtins. */
for (i = 0, d = bdesc_set; i < ARRAY_SIZE (bdesc_set); i++, d++)
if (d->code == fcode)
- return frv_expand_set_builtin (d->icode, arglist, target);
+ return frv_expand_set_builtin (d->icode, exp, target);
for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
if (d->code == fcode)
- return frv_expand_unop_builtin (d->icode, arglist, target);
+ return frv_expand_unop_builtin (d->icode, exp, target);
for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
if (d->code == fcode)
- return frv_expand_binop_builtin (d->icode, arglist, target);
+ return frv_expand_binop_builtin (d->icode, exp, target);
for (i = 0, d = bdesc_cut; i < ARRAY_SIZE (bdesc_cut); i++, d++)
if (d->code == fcode)
- return frv_expand_cut_builtin (d->icode, arglist, target);
+ return frv_expand_cut_builtin (d->icode, exp, target);
for (i = 0, d = bdesc_2argimm; i < ARRAY_SIZE (bdesc_2argimm); i++, d++)
if (d->code == fcode)
- return frv_expand_binopimm_builtin (d->icode, arglist, target);
+ return frv_expand_binopimm_builtin (d->icode, exp, target);
for (i = 0, d = bdesc_void2arg; i < ARRAY_SIZE (bdesc_void2arg); i++, d++)
if (d->code == fcode)
- return frv_expand_voidbinop_builtin (d->icode, arglist);
+ return frv_expand_voidbinop_builtin (d->icode, exp);
for (i = 0, d = bdesc_void3arg; i < ARRAY_SIZE (bdesc_void3arg); i++, d++)
if (d->code == fcode)
- return frv_expand_voidtriop_builtin (d->icode, arglist);
+ return frv_expand_voidtriop_builtin (d->icode, exp);
for (i = 0, d = bdesc_voidacc; i < ARRAY_SIZE (bdesc_voidacc); i++, d++)
if (d->code == fcode)
- return frv_expand_voidaccop_builtin (d->icode, arglist);
+ return frv_expand_voidaccop_builtin (d->icode, exp);
+
+ for (i = 0, d = bdesc_int_void2arg;
+ i < ARRAY_SIZE (bdesc_int_void2arg); i++, d++)
+ if (d->code == fcode)
+ return frv_expand_int_void2arg (d->icode, exp);
+
+ for (i = 0, d = bdesc_prefetches;
+ i < ARRAY_SIZE (bdesc_prefetches); i++, d++)
+ if (d->code == fcode)
+ return frv_expand_prefetches (d->icode, exp);
+
+ for (i = 0, d = bdesc_loads; i < ARRAY_SIZE (bdesc_loads); i++, d++)
+ if (d->code == fcode)
+ return frv_expand_load_builtin (d->icode, TYPE_MODE (TREE_TYPE (exp)),
+ exp, target);
+
+ for (i = 0, d = bdesc_stores; i < ARRAY_SIZE (bdesc_stores); i++, d++)
+ if (d->code == fcode)
+ return frv_expand_store_builtin (d->icode, exp);
return 0;
}
static bool
-frv_in_small_data_p (decl)
- tree decl;
+frv_in_small_data_p (tree decl)
{
HOST_WIDE_INT size;
tree section_name;
if (TREE_CODE (decl) != VAR_DECL || DECL_ARTIFICIAL (decl))
return false;
- size = int_size_in_bytes (TREE_TYPE (decl));
- if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
- return true;
-
/* If we already know which section the decl should be in, see if
it's a small data section. */
section_name = DECL_SECTION_NAME (decl);
if (section_name)
{
- if (TREE_CODE (section_name) != STRING_CST)
- abort ();
+ gcc_assert (TREE_CODE (section_name) == STRING_CST);
if (frv_string_begins_with (section_name, ".sdata"))
return true;
if (frv_string_begins_with (section_name, ".sbss"))
return true;
+ return false;
}
+ size = int_size_in_bytes (TREE_TYPE (decl));
+ if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
+ return true;
+
return false;
}
\f
static bool
-frv_rtx_costs (x, code, outer_code, total)
- rtx x;
- int code, outer_code ATTRIBUTE_UNUSED;
- int *total;
+frv_rtx_costs (rtx x,
+ int code ATTRIBUTE_UNUSED,
+ int outer_code ATTRIBUTE_UNUSED,
+ int *total)
{
+ if (outer_code == MEM)
+ {
+ /* Don't differentiate between memory addresses. All the ones
+ we accept have equal cost. */
+ *total = COSTS_N_INSNS (0);
+ return true;
+ }
+
switch (code)
{
case CONST_INT:
- /* Make 12 bit integers really cheap. */
+ /* Make 12-bit integers really cheap. */
if (IN_RANGE_P (INTVAL (x), -2048, 2047))
{
*total = 0;
return true;
}
- /* FALLTHRU */
+ /* Fall through. */
case CONST:
case LABEL_REF:
*total = COSTS_N_INSNS (18);
return true;
+ case MEM:
+ *total = COSTS_N_INSNS (3);
+ return true;
+
default:
return false;
}
}
\f
static void
-frv_asm_out_constructor (symbol, priority)
- rtx symbol;
- int priority ATTRIBUTE_UNUSED;
+frv_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
{
- ctors_section ();
+ switch_to_section (ctors_section);
assemble_align (POINTER_SIZE);
+ if (TARGET_FDPIC)
+ {
+ int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ gcc_assert (ok);
+ return;
+ }
assemble_integer_with_op ("\t.picptr\t", symbol);
}
static void
-frv_asm_out_destructor (symbol, priority)
- rtx symbol;
- int priority ATTRIBUTE_UNUSED;
+frv_asm_out_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
{
- dtors_section ();
+ switch_to_section (dtors_section);
assemble_align (POINTER_SIZE);
+ if (TARGET_FDPIC)
+ {
+ int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ gcc_assert (ok);
+ return;
+ }
assemble_integer_with_op ("\t.picptr\t", symbol);
}
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX. */
+
+static rtx
+frv_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+ int incoming ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (Pmode, FRV_STRUCT_VALUE_REGNUM);
+}
+
+#define TLS_BIAS (2048 - 16)
+
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
+ We need to emit DTP-relative relocations. */
+
+static void
+frv_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+ gcc_assert (size == 4);
+ fputs ("\t.picptr\ttlsmoff(", file);
+ /* We want the unbiased TLS offset, so add the bias to the
+ expression, such that the implicit biasing cancels out. */
+ output_addr_const (file, plus_constant (x, TLS_BIAS));
+ fputs (")", file);
+}
+
+#include "gt-frv.h"