-/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004
- Free Software Foundation, Inc.
+/* Copyright (C) 1997, 1998, 1999, 2000, 2001, 2003, 2004, 2005, 2006, 2007,
+ 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
Contributed by Red Hat, Inc.
This file is part of GCC.
GCC is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
any later version.
GCC is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING. If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA. */
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
#include "config.h"
#include "system.h"
#include "tree.h"
#include "regs.h"
#include "hard-reg-set.h"
-#include "real.h"
#include "insn-config.h"
#include "conditions.h"
#include "insn-flags.h"
#include "except.h"
#include "function.h"
#include "optabs.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
#include "basic-block.h"
#include "tm_p.h"
#include "ggc.h"
-#include <ctype.h>
#include "target.h"
#include "target-def.h"
#include "targhooks.h"
#include "integrate.h"
#include "langhooks.h"
+#include "df.h"
#ifndef FRV_INLINE
#define FRV_INLINE inline
#endif
-/* Information about a relocation unspec. SYMBOL is the relocation symbol
- (a SYMBOL_REF or LABEL_REF), RELOC is the type of relocation and OFFSET
- is the constant addend. */
-struct frv_unspec {
- rtx symbol;
- int reloc;
- HOST_WIDE_INT offset;
+/* 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;
+
+ /* The type of access. FRV_IO_UNKNOWN means the access can be either
+ a read or a write. */
+enum frv_io_type { FRV_IO_UNKNOWN, FRV_IO_READ, FRV_IO_WRITE };
+
+/* 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 {
+ enum frv_io_type 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 GTY(()) machine_function
+{
+ /* 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. */
}
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. */
int base_offset;
} frv_frame_accessor_t;
-/* Define the information needed to generate branch and scc insns. This is
- stored from the compare operation. */
-rtx frv_compare_op0;
-rtx frv_compare_op1;
-
/* Conditional execution support gathered together in one structure. */
typedef struct
{
/* Map register number to smallest register class. */
enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER];
-/* Map class letter into register class. */
-enum reg_class reg_class_from_letter[256];
-
/* 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 void frv_option_override (void);
+static bool frv_legitimate_address_p (enum machine_mode, rtx, bool);
static int frv_default_flags_for_cpu (void);
-static int frv_string_begins_with (tree, const char *);
+static int frv_string_begins_with (const_tree, const char *);
static FRV_INLINE bool frv_small_data_reloc_p (rtx, int);
-static FRV_INLINE bool frv_const_unspec_p (rtx, struct frv_unspec *);
+static void frv_print_operand (FILE *, rtx, int);
+static void frv_print_operand_address (FILE *, rtx);
+static bool frv_print_operand_punct_valid_p (unsigned char code);
static void frv_print_operand_memory_reference_reg
(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 rtx frv_function_value (const_tree, const_tree,
+ bool);
+static rtx frv_libcall_value (enum machine_mode,
+ const_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 void frv_frame_access_standard_regs (enum frv_stack_op,
frv_stack_t *);
static struct machine_function *frv_init_machine_status (void);
-static int frv_legitimate_memory_operand (rtx, enum machine_mode, int);
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 *);
+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_legitimize_address (rtx, rtx, enum machine_mode);
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 void frv_registers_update (rtx, unsigned char [],
- int [], int *, int);
-static int frv_registers_used_p (rtx, unsigned char [], int);
-static int frv_registers_set_p (rtx, unsigned char [], int);
-static int frv_issue_rate (void);
+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, const_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 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 bool frv_in_small_data_p (const_tree);
static void frv_asm_output_mi_thunk
(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_expand_builtin_va_start (tree, rtx);
+static bool frv_rtx_costs (rtx, int, int, int*, bool);
+static int frv_register_move_cost (enum machine_mode,
+ reg_class_t, reg_class_t);
+static int frv_memory_move_cost (enum machine_mode,
+ reg_class_t, bool);
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 bool frv_cannot_force_const_mem (enum machine_mode, 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 bool frv_must_pass_in_stack (enum machine_mode mode, const_tree type);
+static int frv_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+ tree, bool);
+static rtx frv_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static rtx frv_function_incoming_arg (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static void frv_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+ const_tree, bool);
+static unsigned int frv_function_arg_boundary (enum machine_mode,
+ const_tree);
+static void frv_output_dwarf_dtprel (FILE *, int, rtx)
+ ATTRIBUTE_UNUSED;
+static reg_class_t frv_secondary_reload (bool, rtx, reg_class_t,
+ enum machine_mode,
+ secondary_reload_info *);
+static bool frv_frame_pointer_required (void);
+static bool frv_can_eliminate (const int, const int);
+static void frv_conditional_register_usage (void);
+static void frv_trampoline_init (rtx, tree, rtx);
+static bool frv_class_likely_spilled_p (reg_class_t);
+
+/* Implement TARGET_OPTION_OPTIMIZATION_TABLE. */
+static const struct default_options frv_option_optimization_table[] =
+ {
+ { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
+ { OPT_LEVELS_NONE, 0, NULL, 0 }
+ };
+\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_PRINT_OPERAND
+#define TARGET_PRINT_OPERAND frv_print_operand
+#undef TARGET_PRINT_OPERAND_ADDRESS
+#define TARGET_PRINT_OPERAND_ADDRESS frv_print_operand_address
+#undef TARGET_PRINT_OPERAND_PUNCT_VALID_P
+#define TARGET_PRINT_OPERAND_PUNCT_VALID_P frv_print_operand_punct_valid_p
#undef TARGET_ASM_FUNCTION_PROLOGUE
#define TARGET_ASM_FUNCTION_PROLOGUE frv_function_prologue
#undef TARGET_ASM_FUNCTION_EPILOGUE
#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_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE frv_option_override
+#undef TARGET_OPTION_OPTIMIZATION_TABLE
+#define TARGET_OPTION_OPTIMIZATION_TABLE frv_option_optimization_table
#undef TARGET_INIT_BUILTINS
#define TARGET_INIT_BUILTINS frv_init_builtins
#undef TARGET_EXPAND_BUILTIN
#define TARGET_INIT_LIBFUNCS frv_init_libfuncs
#undef TARGET_IN_SMALL_DATA_P
#define TARGET_IN_SMALL_DATA_P frv_in_small_data_p
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST frv_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST frv_memory_move_cost
#undef TARGET_RTX_COSTS
#define TARGET_RTX_COSTS frv_rtx_costs
#undef TARGET_ASM_CONSTRUCTOR
#undef TARGET_SCHED_ISSUE_RATE
#define TARGET_SCHED_ISSUE_RATE frv_issue_rate
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS frv_legitimize_address
+
#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_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG frv_function_arg
+#undef TARGET_FUNCTION_INCOMING_ARG
+#define TARGET_FUNCTION_INCOMING_ARG frv_function_incoming_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE frv_function_arg_advance
+#undef TARGET_FUNCTION_ARG_BOUNDARY
+#define TARGET_FUNCTION_ARG_BOUNDARY frv_function_arg_boundary
#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
+
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START frv_expand_builtin_va_start
+
+#if HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL frv_output_dwarf_dtprel
+#endif
+
+#undef TARGET_CLASS_LIKELY_SPILLED_P
+#define TARGET_CLASS_LIKELY_SPILLED_P frv_class_likely_spilled_p
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD frv_secondary_reload
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P frv_legitimate_address_p
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED frv_frame_pointer_required
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE frv_can_eliminate
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE frv_conditional_register_usage
+
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT frv_trampoline_init
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE frv_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE frv_libcall_value
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
/* Any function call that satisfies the machine-independent
requirements is eligible on FR-V. */
/* Return true if X is a valid relocation unspec. If it is, fill in UNSPEC
appropriately. */
-static FRV_INLINE bool
+bool
frv_const_unspec_p (rtx x, struct frv_unspec *unspec)
{
if (GET_CODE (x) == CONST)
if (frv_small_data_reloc_p (unspec->symbol, unspec->reloc)
&& unspec->offset > 0
- && (unsigned HOST_WIDE_INT) unspec->offset < g_switch_value)
+ && unspec->offset < g_switch_value)
return true;
}
}
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)
+frv_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x ATTRIBUTE_UNUSED)
{
return TARGET_FDPIC;
}
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
- sense on a particular target machine. You can define a macro
- `OVERRIDE_OPTIONS' to take account of this. This macro, if
- defined, is executed once just after all the command options have
- been parsed.
+/* Implement TARGET_OPTION_OVERRIDE. */
- Don't use this macro to turn on various extra optimizations for
- `-O'. That is what `OPTIMIZATION_OPTIONS' is for. */
-
-void
-frv_override_options (void)
+static void
+frv_option_override (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);
if (!flag_pic) /* -fPIC */
flag_pic = 2;
- if (! g_switch_set) /* -G0 */
+ if (!global_options_set.x_g_switch_value) /* -G0 */
{
- g_switch_set = 1;
g_switch_value = 0;
}
}
- /* 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
for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
{
- enum reg_class class;
+ enum reg_class rclass;
if (GPR_P (regno))
{
int gpr_reg = regno - GPR_FIRST;
- if ((gpr_reg & 3) == 0)
- class = QUAD_REGS;
+
+ if (gpr_reg == GR8_REG)
+ rclass = GR8_REGS;
+
+ else if (gpr_reg == GR9_REG)
+ rclass = GR9_REGS;
+
+ else if (gpr_reg == GR14_REG)
+ rclass = FDPIC_FPTR_REGS;
+
+ else if (gpr_reg == FDPIC_REGNO)
+ rclass = FDPIC_REGS;
+
+ else if ((gpr_reg & 3) == 0)
+ rclass = QUAD_REGS;
else if ((gpr_reg & 1) == 0)
- class = EVEN_REGS;
+ rclass = EVEN_REGS;
else
- class = GPR_REGS;
+ rclass = GPR_REGS;
}
else if (FPR_P (regno))
{
int fpr_reg = regno - GPR_FIRST;
if ((fpr_reg & 3) == 0)
- class = QUAD_FPR_REGS;
+ rclass = QUAD_FPR_REGS;
else if ((fpr_reg & 1) == 0)
- class = FEVEN_REGS;
+ rclass = FEVEN_REGS;
else
- class = FPR_REGS;
+ rclass = FPR_REGS;
}
else if (regno == LR_REGNO)
- class = LR_REG;
+ rclass = LR_REG;
else if (regno == LCR_REGNO)
- class = LCR_REG;
+ rclass = LCR_REG;
else if (ICC_P (regno))
- class = ICC_REGS;
+ rclass = ICC_REGS;
else if (FCC_P (regno))
- class = FCC_REGS;
+ rclass = FCC_REGS;
else if (ICR_P (regno))
- class = ICR_REGS;
+ rclass = ICR_REGS;
else if (FCR_P (regno))
- class = FCR_REGS;
+ rclass = FCR_REGS;
else if (ACC_P (regno))
{
int r = regno - ACC_FIRST;
if ((r & 3) == 0)
- class = QUAD_ACC_REGS;
+ rclass = QUAD_ACC_REGS;
else if ((r & 1) == 0)
- class = EVEN_ACC_REGS;
+ rclass = EVEN_ACC_REGS;
else
- class = ACC_REGS;
+ rclass = ACC_REGS;
}
else if (ACCG_P (regno))
- class = ACCG_REGS;
+ rclass = ACCG_REGS;
else
- class = NO_REGS;
+ rclass = NO_REGS;
- regno_reg_class[regno] = class;
+ regno_reg_class[regno] = rclass;
}
/* Check for small data option */
- if (!g_switch_set)
+ if (!global_options_set.x_g_switch_value && !TARGET_LIBPIC)
g_switch_value = SDATA_DEFAULT_SIZE;
- /* A C expression which defines the machine-dependent operand
- constraint letters for register classes. If CHAR is such a
- letter, the value should be the register class corresponding to
- it. Otherwise, the value should be `NO_REGS'. The register
- letter `r', corresponding to class `GENERAL_REGS', will not be
- passed to this macro; you do not need to handle it.
-
- The following letters are unavailable, due to being used as
- constraints:
- '0'..'9'
- '<', '>'
- 'E', 'F', 'G', 'H'
- 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P'
- 'Q', 'R', 'S', 'T', 'U'
- 'V', 'X'
- 'g', 'i', 'm', 'n', 'o', 'p', 'r', 's' */
-
- for (i = 0; i < 256; i++)
- reg_class_from_letter[i] = NO_REGS;
-
- reg_class_from_letter['a'] = ACC_REGS;
- reg_class_from_letter['b'] = EVEN_ACC_REGS;
- reg_class_from_letter['c'] = CC_REGS;
- reg_class_from_letter['d'] = GPR_REGS;
- reg_class_from_letter['e'] = EVEN_REGS;
- reg_class_from_letter['f'] = FPR_REGS;
- reg_class_from_letter['h'] = FEVEN_REGS;
- reg_class_from_letter['l'] = LR_REG;
- reg_class_from_letter['q'] = QUAD_REGS;
- reg_class_from_letter['t'] = ICC_REGS;
- reg_class_from_letter['u'] = FCC_REGS;
- reg_class_from_letter['v'] = ICR_REGS;
- reg_class_from_letter['w'] = FCR_REGS;
- reg_class_from_letter['x'] = QUAD_FPR_REGS;
- reg_class_from_letter['y'] = LCR_REG;
- reg_class_from_letter['z'] = SPR_REGS;
- 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 ((target_flags_explicit & MASK_LINKED_FP) == 0)
target_flags |= MASK_LINKED_FP;
- init_machine_status = frv_init_machine_status;
-}
-
-\f
-/* Some machines may desire to change what optimizations are performed for
- various optimization levels. This macro, if defined, is executed once just
- after the optimization level is determined and before the remainder of the
- command options have been parsed. Values set in this macro are used as the
- default values for the other command line options.
-
- LEVEL is the optimization level specified; 2 if `-O2' is specified, 1 if
- `-O' is specified, and 0 if neither is specified.
+ if ((target_flags_explicit & MASK_OPTIMIZE_MEMBAR) == 0)
+ target_flags |= MASK_OPTIMIZE_MEMBAR;
- SIZE is nonzero if `-Os' is specified, 0 otherwise.
+ for (i = 0; i < ARRAY_SIZE (frv_unit_names); i++)
+ frv_unit_codes[i] = get_cpu_unit_code (frv_unit_names[i]);
- 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
- optimizations.
+ for (i = 0; i < ARRAY_SIZE (frv_type_to_unit); i++)
+ frv_type_to_unit[i] = ARRAY_SIZE (frv_unit_codes);
- *Do not examine `write_symbols' in this macro!* The debugging options are
- *not supposed to alter the generated code. */
-
-/* On the FRV, possibly disable VLIW packing which is done by the 2nd
- scheduling pass at the current time. */
-void
-frv_optimization_options (int level, int size ATTRIBUTE_UNUSED)
-{
- if (level >= 2)
- {
-#ifdef DISABLE_SCHED2
- flag_schedule_insns_after_reload = 0;
-#endif
-#ifdef ENABLE_RCSP
- flag_rcsp = 1;
-#endif
- }
+ init_machine_status = frv_init_machine_status;
}
\f
/* Return true if NAME (a STRING_CST node) begins with PREFIX. */
static int
-frv_string_begins_with (tree name, const char *prefix)
+frv_string_begins_with (const_tree name, const char *prefix)
{
- int prefix_len = strlen (prefix);
+ const int prefix_len = strlen (prefix);
/* Remember: NAME's length includes the null terminator. */
return (TREE_STRING_LENGTH (name) > prefix_len
switches, then GCC will automatically avoid using these registers when the
target switches are opposed to them.) */
-void
+static void
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;
/* Find the last argument, and see if it is __builtin_va_alist. */
for (cur_arg = DECL_ARGUMENTS (fndecl); cur_arg != (tree)0; cur_arg = next_arg)
{
- next_arg = TREE_CHAIN (cur_arg);
+ next_arg = DECL_CHAIN (cur_arg);
if (next_arg == (tree)0)
{
if (DECL_NAME (cur_arg)
default:
for (regno = first; regno <= last; regno++)
{
- if ((regs_ever_live[regno] && !call_used_regs[regno])
- || (current_function_calls_eh_return
+ if ((df_regs_ever_live_p (regno) && !call_used_regs[regno])
+ || (crtl->calls_eh_return
&& (regno >= FIRST_EH_REGNUM && regno <= LAST_EH_REGNUM))
|| (!TARGET_FDPIC && flag_pic
- && cfun->uses_pic_offset_table && regno == PIC_REGNO))
+ && crtl->uses_pic_offset_table && regno == PIC_REGNO))
{
info_ptr->save_p[regno] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
break;
case STACK_REGS_LR:
- if (regs_ever_live[LR_REGNO]
+ if (df_regs_ever_live_p (LR_REGNO)
|| profile_flag
/* 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))
+ && crtl->uses_pic_offset_table))
{
info_ptr->save_p[LR_REGNO] = REG_SAVE_1WORD;
size_1word += UNITS_PER_WORD;
/* 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)
+ last -= (ADDR_ALIGN (crtl->args.pretend_args_size, UNITS_PER_WORD)
/ UNITS_PER_WORD);
for (regno = first; regno <= last; regno++)
be used, or the size of a word otherwise. */
alignment = (TARGET_DWORD? 2 * UNITS_PER_WORD : UNITS_PER_WORD);
- info_ptr->parameter_size = ADDR_ALIGN (cfun->outgoing_args_size, alignment);
+ info_ptr->parameter_size = ADDR_ALIGN (crtl->outgoing_args_size, alignment);
info_ptr->regs_size = ADDR_ALIGN (info_ptr->regs_size_2words
+ info_ptr->regs_size_1word,
alignment);
info_ptr->vars_size = ADDR_ALIGN (get_frame_size (), alignment);
- info_ptr->pretend_size = cfun->pretend_args_size;
+ info_ptr->pretend_size = crtl->args.pretend_args_size;
/* Work out the size of the frame, excluding the header. Both the frame
body and register parameter area will be dword-aligned. */
\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;
rtx insn;
/* Just to check that the above comment is true. */
- if (regs_ever_live[GPR_FIRST + 3])
- abort ();
+ gcc_assert (!df_regs_ever_live_p (GPR_FIRST + 3));
/* Generate the instruction that saves the link register. */
fprintf (file, "\tmovsg lr,gr3\n");
{
rtx address = XEXP (XVECEXP (pattern, 0, 1), 0);
if (GET_CODE (address) == REG && REGNO (address) == LR_REGNO)
- REGNO (address) = GPR_FIRST + 3;
+ SET_REGNO (address, GPR_FIRST + 3);
}
}
}
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
static rtx
frv_alloc_temp_reg (
frv_tmp_reg_t *info, /* which registers are available */
- enum reg_class class, /* register class desired */
+ enum reg_class rclass, /* 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 regno = info->next_reg[ (int)rclass ];
int orig_regno = regno;
- HARD_REG_SET *reg_in_class = ®_class_contents[ (int)class ];
+ HARD_REG_SET *reg_in_class = ®_class_contents[ (int)rclass ];
int i, nr;
for (;;)
regno = 0;
if (regno == orig_regno)
{
- if (no_abort)
- return NULL_RTX;
- else
- abort ();
+ gcc_assert (no_abort);
+ return NULL_RTX;
}
}
nr = HARD_REGNO_NREGS (regno, mode);
- info->next_reg[ (int)class ] = regno + nr;
+ info->next_reg[ (int)rclass ] = regno + nr;
if (mark_as_used)
for (i = 0; i < nr; i++)
frv_frame_offset_rtx (int offset)
{
rtx offset_rtx = GEN_INT (offset);
- if (IN_RANGE_P (offset, -2048, 2047))
+ if (IN_RANGE (offset, -2048, 2047))
return offset_rtx;
else
{
rtx reg_rtx = gen_rtx_REG (SImode, OFFSET_REGNO);
- if (IN_RANGE_P (offset, -32768, 32767))
+ if (IN_RANGE (offset, -32768, 32767))
emit_insn (gen_movsi (reg_rtx, offset_rtx));
else
{
emit_insn (gen_rtx_SET (VOIDmode, reg, temp));
}
else
- emit_insn (gen_rtx_SET (VOIDmode, reg, mem));
- emit_insn (gen_rtx_USE (VOIDmode, reg));
+ {
+ /* We cannot use reg+reg addressing for DImode access. */
+ if (mode == DImode
+ && GET_CODE (XEXP (mem, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (mem, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (mem, 0), 1)) == REG)
+ {
+ rtx temp = gen_rtx_REG (SImode, TEMP_REGNO);
+
+ emit_move_insn (temp,
+ gen_rtx_PLUS (SImode, XEXP (XEXP (mem, 0), 0),
+ XEXP (XEXP (mem, 0), 1)));
+ mem = gen_rtx_MEM (DImode, temp);
+ }
+ emit_insn (gen_rtx_SET (VOIDmode, reg, mem));
+ }
+ emit_use (reg);
}
else
{
frv_frame_insn (gen_rtx_SET (Pmode, mem, temp),
frv_dwarf_store (reg, stack_offset));
}
- else if (GET_MODE (reg) == DImode)
+ else if (mode == DImode)
{
/* For DImode saves, the dwarf2 version needs to be a SEQUENCE
with a separate save for each register. */
rtx reg2 = gen_rtx_REG (SImode, REGNO (reg) + 1);
rtx set1 = frv_dwarf_store (reg1, stack_offset);
rtx set2 = frv_dwarf_store (reg2, stack_offset + 4);
+
+ /* Also we cannot use reg+reg addressing. */
+ if (GET_CODE (XEXP (mem, 0)) == PLUS
+ && GET_CODE (XEXP (XEXP (mem, 0), 0)) == REG
+ && GET_CODE (XEXP (XEXP (mem, 0), 1)) == REG)
+ {
+ rtx temp = gen_rtx_REG (SImode, TEMP_REGNO);
+ emit_move_insn (temp,
+ gen_rtx_PLUS (SImode, XEXP (XEXP (mem, 0), 0),
+ XEXP (XEXP (mem, 0), 1)));
+ mem = gen_rtx_MEM (DImode, temp);
+ }
+
frv_frame_insn (gen_rtx_SET (Pmode, mem, reg),
gen_rtx_PARALLEL (VOIDmode,
gen_rtvec (2, set1, set2)));
accessor.op = FRV_STORE;
if (frame_pointer_needed && info->total_size > 2048)
{
- rtx insn;
-
accessor.base = gen_rtx_REG (Pmode, OLD_SP_REGNO);
accessor.base_offset = info->total_size;
- insn = emit_insn (gen_movsi (accessor.base, sp));
+ emit_insn (gen_movsi (accessor.base, sp));
}
else
{
emit_insn (gen_blockage ());
/* Set up pic register/small data register for this function. */
- if (!TARGET_FDPIC && flag_pic && cfun->uses_pic_offset_table)
+ if (!TARGET_FDPIC && flag_pic && crtl->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)));
memset (&frv_ifcvt.tmp_reg, 0, sizeof (frv_ifcvt.tmp_reg));
/* Release the bitmap of created insns. */
- BITMAP_XFREE (frv_ifcvt.scratch_insns_bitmap);
+ BITMAP_FREE (frv_ifcvt.scratch_insns_bitmap);
}
\f
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. */
+ scheduling. */
void
frv_expand_epilogue (bool emit_return)
if (frame_pointer_needed)
{
emit_insn (gen_rtx_SET (VOIDmode, fp, gen_rtx_MEM (Pmode, fp)));
- emit_insn (gen_rtx_USE (VOIDmode, fp));
+ emit_use (fp);
}
/* Deallocate the stack frame. */
}
/* If this function uses eh_return, add the final stack adjustment now. */
- if (current_function_calls_eh_return)
+ if (crtl->calls_eh_return)
emit_insn (gen_stack_adjust (sp, sp, EH_RETURN_STACKADJ_RTX));
if (emit_return)
emit_move_insn (lr, return_addr);
}
- emit_insn (gen_rtx_USE (VOIDmode, lr));
+ emit_use (lr);
}
}
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. */
- if (IN_RANGE_P (delta, -2048, 2047))
+ if (IN_RANGE (delta, -2048, 2047))
fprintf (file, "\taddi %s,#%d,%s\n", name_arg0, (int) delta, name_arg0);
else
{
}
\f
-/* A C expression which is nonzero if a function must have and use a frame
- pointer. This expression is evaluated in the reload pass. If its value is
- nonzero the function will have a frame pointer.
-
- The expression can in principle examine the current function and decide
- according to the facts, but on most machines the constant 0 or the constant
- 1 suffices. Use 0 when the machine allows code to be generated with no
- frame pointer, and doing so saves some time or space. Use 1 when there is
- no possible advantage to avoiding a frame pointer.
-
- In certain cases, the compiler does not know how to produce valid code
- without a frame pointer. The compiler recognizes those cases and
- automatically gives the function a frame pointer regardless of what
- `FRAME_POINTER_REQUIRED' says. You don't need to worry about them.
-
- In a function that does not require a frame pointer, the frame pointer
- 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. */
-int
+static bool
frv_frame_pointer_required (void)
{
/* If we forgoing the usual linkage requirements, we only need
return !current_function_sp_is_unchanging;
if (! current_function_is_leaf)
- return TRUE;
+ return true;
if (get_frame_size () != 0)
- return TRUE;
+ return true;
if (cfun->stdarg)
- return TRUE;
+ return true;
if (!current_function_sp_is_unchanging)
- return TRUE;
+ return true;
- if (!TARGET_FDPIC && flag_pic && cfun->uses_pic_offset_table)
- return TRUE;
+ if (!TARGET_FDPIC && flag_pic && crtl->uses_pic_offset_table)
+ return true;
if (profile_flag)
- return TRUE;
+ return true;
if (cfun->machine->frame_needed)
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
\f
+/* Worker function for TARGET_CAN_ELIMINATE. */
+
+bool
+frv_can_eliminate (const int from, const int to)
+{
+ return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM
+ ? ! frame_pointer_needed
+ : true);
+}
+
/* This macro is similar to `INITIAL_FRAME_POINTER_OFFSET'. It specifies the
initial difference between the specified pair of registers. This macro must
be defined if `ELIMINABLE_REGS' is defined. */
- info->pretend_size);
else
- abort ();
+ gcc_unreachable ();
if (TARGET_DEBUG_STACK)
fprintf (stderr, "Eliminate %s to %s by adding %d\n",
\f
/* Expand __builtin_va_start to do the va_start macro. */
-void
+static void
frv_expand_builtin_va_start (tree valist, rtx nextarg)
{
tree t;
- int num = cfun->args_info - FIRST_ARG_REGNUM - FRV_NUM_ARG_REGS;
+ int num = crtl->args.info - FIRST_ARG_REGNUM - FRV_NUM_ARG_REGS;
nextarg = gen_rtx_PLUS (Pmode, virtual_incoming_args_rtx,
GEN_INT (UNITS_PER_WORD * num));
if (TARGET_DEBUG_ARG)
{
fprintf (stderr, "va_start: args_info = %d, num = %d\n",
- cfun->args_info, num);
+ crtl->args.info, num);
debug_rtx (nextarg);
}
- t = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
- make_tree (ptr_type_node, nextarg));
+ t = build2 (MODIFY_EXPR, TREE_TYPE (valist), valist,
+ fold_convert (TREE_TYPE (valist),
+ make_tree (sizetype, nextarg)));
TREE_SIDE_EFFECTS (t) = 1;
expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
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);
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 (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 offset;
- int num_reg;
rtx dest_reg;
rtx dest_addr;
rtx dest_mem;
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);
/* Move the address into a scratch register. */
dest_reg = copy_addr_to_reg (XEXP (orig_dest, 0));
- num_reg = offset = 0;
+ offset = 0;
for ( ; bytes > 0; (bytes -= clear_bytes), (offset += clear_bytes))
{
/* Calculate the correct offset for src/dest. */
{
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 (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 instruction, 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);
}
MEMREF has already happened.
MEMREF must be a legitimate operand for modes larger than SImode.
- GO_IF_LEGITIMATE_ADDRESS forbids register+register addresses, which
+ frv_legitimate_address_p forbids register+register addresses, which
this function cannot handle. */
rtx
frv_index_memory (rtx memref, enum machine_mode mode, int index)
\f
/* Print a memory address as an operand to reference that memory location. */
-void
+static void
frv_print_operand_address (FILE * stream, rtx x)
{
if (GET_CODE (x) == MEM)
output_addr_const (stream, x);
return;
+ case PLUS:
+ /* Poorly constructed asm statements can trigger this alternative.
+ See gcc/testsuite/gcc.dg/asm-4.c for an example. */
+ frv_print_operand_memory_reference (stream, x, 0);
+ return;
+
default:
break;
}
- fatal_insn ("Bad insn to frv_print_operand_address:", x);
+ fatal_insn ("bad insn to frv_print_operand_address:", x);
}
\f
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. */
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)
case CONST:
if (!frv_const_unspec_p (x1, &unspec))
- fatal_insn ("Bad insn to frv_print_operand_memory_reference:", x1);
+ 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);
}
}
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
- alternate fashion. Four letters have standard, built-in meanings described
- below. The machine description macro `PRINT_OPERAND' can define additional
- letters with nonstandard meanings.
+ alternate fashion. Four letters have standard, built-in meanings
+ described below. The hook `TARGET_PRINT_OPERAND' can define
+ additional letters with nonstandard meanings.
`%cDIGIT' can be used to substitute an operand that is a constant value
without the syntax that normally indicates an immediate operand.
than once in a single template that generates multiple assembler
instructions.
- `%' followed by a punctuation character specifies a substitution that does
- not use an operand. Only one case is standard: `%%' outputs a `%' into the
- assembler code. Other nonstandard cases can be defined in the
- `PRINT_OPERAND' macro. You must also define which punctuation characters
- are valid with the `PRINT_OPERAND_PUNCT_VALID_P' macro. */
+ `%' followed by a punctuation character specifies a substitution that
+ does not use an operand. Only one case is standard: `%%' outputs a
+ `%' into the assembler code. Other nonstandard cases can be defined
+ in the `TARGET_PRINT_OPERAND' hook. You must also define which
+ punctuation characters are valid with the
+ `TARGET_PRINT_OPERAND_PUNCT_VALID_P' hook. */
-void
+static void
frv_print_operand (FILE * file, rtx x, int code)
{
struct frv_unspec unspec;
HOST_WIDE_INT value;
int offset;
- if (code != 0 && !isalpha (code))
+ if (code != 0 && !ISALPHA (code))
value = 0;
else if (GET_CODE (x) == CONST_INT)
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 '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;
- }
+ 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;
- }
+ 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':
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;
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;
case 'g':
/* Print appropriate GOT function. */
if (GET_CODE (x) != CONST_INT)
- fatal_insn ("Bad insn to frv_print_operand, 'g' modifier:", x);
+ fatal_insn ("bad insn to frv_print_operand, 'g' modifier:", x);
fputs (unspec_got_name (INTVAL (x)), file);
break;
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. */
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;
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;
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':
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;
return;
}
+static bool
+frv_print_operand_punct_valid_p (unsigned char code)
+{
+ return (code == '.' || code == '#' || code == '@' || code == '~'
+ || code == '*' || code == '&');
+}
+
\f
/* A C statement (sans semicolon) for initializing the variable CUM for the
state at the beginning of the argument list. The variable has type
in registers. */
static bool
-frv_must_pass_in_stack (enum machine_mode mode, tree type)
+frv_must_pass_in_stack (enum machine_mode mode, const_tree type)
{
if (mode == BLKmode)
return true;
argument with the specified mode and type. If it is not defined,
`PARM_BOUNDARY' is used for all arguments. */
-int
+static unsigned int
frv_function_arg_boundary (enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED)
+ const_tree type ATTRIBUTE_UNUSED)
{
return BITS_PER_WORD;
}
-rtx
-frv_function_arg (CUMULATIVE_ARGS *cum,
- enum machine_mode mode,
- tree type ATTRIBUTE_UNUSED,
- int named,
- int incoming ATTRIBUTE_UNUSED)
+static rtx
+frv_function_arg_1 (const CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type ATTRIBUTE_UNUSED, bool named,
+ bool incoming ATTRIBUTE_UNUSED)
{
enum machine_mode xmode = (mode == BLKmode) ? SImode : mode;
int arg_num = *cum;
return ret;
}
+static rtx
+frv_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ return frv_function_arg_1 (cum, mode, type, named, false);
+}
+
+static rtx
+frv_function_incoming_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+ const_tree type, bool named)
+{
+ return frv_function_arg_1 (cum, mode, type, named, true);
+}
+
\f
/* A C statement (sans semicolon) to update the summarizer variable CUM to
advance past an argument in the argument list. The values MODE, TYPE and
the stack. The compiler knows how to track the amount of stack space used
for arguments without any special help. */
-void
+static void
frv_function_arg_advance (CUMULATIVE_ARGS *cum,
enum machine_mode mode,
- tree type ATTRIBUTE_UNUSED,
- int named)
+ const_tree type ATTRIBUTE_UNUSED,
+ bool 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 (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
+/* Implements TARGET_FUNCTION_VALUE. */
+static rtx
+frv_function_value (const_tree valtype,
+ const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
+ bool outgoing ATTRIBUTE_UNUSED)
+{
+ return gen_rtx_REG (TYPE_MODE (valtype), RETURN_VALUE_REGNUM);
}
\f
-/* 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. */
+/* Implements TARGET_LIBCALL_VALUE. */
-int
-frv_function_arg_callee_copies (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- tree type ATTRIBUTE_UNUSED,
- int named ATTRIBUTE_UNUSED)
+static rtx
+frv_libcall_value (enum machine_mode mode,
+ const_rtx fun ATTRIBUTE_UNUSED)
{
- return 0;
+ return gen_rtx_REG (mode, RETURN_VALUE_REGNUM);
}
\f
+/* Implements FUNCTION_VALUE_REGNO_P. */
+
+bool
+frv_function_value_regno_p (const unsigned int regno)
+{
+ return (regno == RETURN_VALUE_REGNUM);
+}
+\f
/* Return true if a register is ok to use as a base or index register. */
static FRV_INLINE int
conditional to define the strict variant in that case and the non-strict
variant otherwise.
- Subroutines to check for acceptable registers for various purposes (one for
- base registers, one for index registers, and so on) are typically among the
- subroutines used to define `GO_IF_LEGITIMATE_ADDRESS'. Then only these
- subroutine macros need have two variants; the higher levels of macros may be
- the same whether strict or not.
-
Normally, constant addresses which are the sum of a `symbol_ref' and an
integer are stored inside a `const' RTX to mark them as constant.
Therefore, there is no need to recognize such sums specifically as
legitimate addresses. Normally you would simply recognize any `const' as
legitimate.
- Usually `PRINT_OPERAND_ADDRESS' is not prepared to handle constant sums that
- are not marked with `const'. It assumes that a naked `plus' indicates
- indexing. If so, then you *must* reject such naked constant sums as
- illegitimate addresses, so that none of them will be given to
- `PRINT_OPERAND_ADDRESS'.
-
- On some machines, whether a symbolic address is legitimate depends on the
- section that the address refers to. On these machines, define the macro
- `ENCODE_SECTION_INFO' to store the information into the `symbol_ref', and
- then check for it here. When you see a `const', you will have to look
- inside it to find the `symbol_ref' in order to determine the section.
-
- The best way to modify the name string is by adding text to the beginning,
- with suitable punctuation to prevent any ambiguity. Allocate the new name
- in `saveable_obstack'. You will have to modify `ASM_OUTPUT_LABELREF' to
- remove and decode the added text and output the name accordingly, and define
- `(* targetm.strip_name_encoding)' to access the original name string.
-
- You can check the information stored here into the `symbol_ref' in the
- definitions of the macros `GO_IF_LEGITIMATE_ADDRESS' and
- `PRINT_OPERAND_ADDRESS'. */
+ Usually `TARGET_PRINT_OPERAND_ADDRESS' is not prepared to handle
+ constant sums that are not marked with `const'. It assumes that a
+ naked `plus' indicates indexing. If so, then you *must* reject such
+ naked constant sums as illegitimate addresses, so that none of them
+ will be given to `TARGET_PRINT_OPERAND_ADDRESS'. */
int
-frv_legitimate_address_p (enum machine_mode mode,
- rtx x,
- int strict_p,
- int condexec_p,
- int allow_double_reg_p)
+frv_legitimate_address_p_1 (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:
break;
case CONST_INT:
- /* 12 bit immediate */
+ /* 12-bit immediate */
if (condexec_p)
ret = FALSE;
else
{
- ret = IN_RANGE_P (INTVAL (x), -2048, 2047);
+ ret = IN_RANGE (INTVAL (x), -2048, 2047);
/* If we can't use load/store double operations, make sure we can
address the second word. */
if (ret && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
- ret = IN_RANGE_P (INTVAL (x) + GET_MODE_SIZE (mode) - 1,
- -2048, 2047);
+ ret = IN_RANGE (INTVAL (x) + GET_MODE_SIZE (mode) - 1,
+ -2048, 2047);
}
break;
break;
case CONST_INT:
- /* 12 bit immediate */
+ /* 12-bit immediate */
if (condexec_p)
ret = FALSE;
else
{
value = INTVAL (x1);
- ret = IN_RANGE_P (value, -2048, 2047);
+ ret = IN_RANGE (value, -2048, 2047);
/* If we can't use load/store double operations, make sure we can
address the second word. */
if (ret && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
- ret = IN_RANGE_P (value + GET_MODE_SIZE (mode) - 1, -2048, 2047);
+ ret = IN_RANGE (value + GET_MODE_SIZE (mode) - 1, -2048, 2047);
}
break;
if (TARGET_DEBUG_ADDR)
{
- fprintf (stderr, "\n========== GO_IF_LEGITIMATE_ADDRESS, mode = %s, result = %d, addresses are %sstrict%s\n",
+ fprintf (stderr, "\n========== legitimate_address_p, mode = %s, result = %d, addresses are %sstrict%s\n",
GET_MODE_NAME (mode), ret, (strict_p) ? "" : "not ",
(condexec_p) ? ", inside conditional code" : "");
debug_rtx (x);
return ret;
}
-\f
-/* Test whether a local function descriptor is canonical, i.e.,
- whether we can use FUNCDESC_GOTOFF to compute the address of the
- function. */
+bool
+frv_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p)
+{
+ return frv_legitimate_address_p_1 (mode, x, strict_p, FALSE, FALSE);
+}
-static bool
-frv_local_funcdesc_p (rtx fnx)
+/* Given an ADDR, generate code to inline the PLT. */
+static rtx
+gen_inlined_tls_plt (rtx addr)
{
- tree fn;
- enum symbol_visibility vis;
- bool ret;
+ rtx retval, dest;
+ rtx picreg = get_hard_reg_initial_val (Pmode, FDPIC_REG);
- if (! SYMBOL_REF_LOCAL_P (fnx))
- return FALSE;
- fn = SYMBOL_REF_DECL (fnx);
+ dest = gen_reg_rtx (DImode);
- if (! fn)
- return FALSE;
+ if (flag_pic == 1)
+ {
+ /*
+ -fpic version:
- vis = DECL_VISIBILITY (fn);
+ lddi.p @(gr15, #gottlsdesc12(ADDR)), gr8
+ calll #gettlsoff(ADDR)@(gr8, gr0)
+ */
+ emit_insn (gen_tls_lddi (dest, addr, picreg));
+ }
+ else
+ {
+ /*
+ -fPIC version:
- 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
+ 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));
+ }
+
+ retval = gen_reg_rtx (Pmode);
+ emit_insn (gen_tls_indirect_call (retval, addr, dest, picreg));
+ return retval;
+}
+
+/* Emit a TLSMOFF or TLSMOFF12 offset, depending on -mTLS. Returns
+ the destination address. */
+static rtx
+gen_tlsmoff (rtx addr, rtx reg)
+{
+ rtx dest = gen_reg_rtx (Pmode);
+
+ if (TARGET_BIG_TLS)
+ {
+ /* 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);
+ }
+ else
+ {
+ /* 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)));
+ }
+ return dest;
+}
+
+/* Generate code for a TLS address. */
+static rtx
+frv_legitimize_tls_address (rtx addr, enum tls_model model)
+{
+ rtx dest, tp = gen_rtx_REG (Pmode, 29);
+ rtx picreg = get_hard_reg_initial_val (Pmode, 15);
+
+ switch (model)
+ {
+ 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;
+
+ 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));
+ }
+
+ reg = gen_reg_rtx (Pmode);
+ emit_insn (gen_rtx_SET (VOIDmode, reg,
+ gen_rtx_PLUS (Pmode,
+ retval, tp)));
+
+ dest = gen_tlsmoff (addr, reg);
+
+ /*
+ 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;
+
+ 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 ();
+ }
+
+ return dest;
+}
+
+rtx
+frv_legitimize_address (rtx x,
+ rtx oldx ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+ if (GET_CODE (x) == SYMBOL_REF)
+ {
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (x);
+ if (model != 0)
+ return frv_legitimize_tls_address (x, model);
+ }
+
+ return x;
+}
+\f
+/* Test whether a local function descriptor is canonical, i.e.,
+ whether we can use FUNCDESC_GOTOFF to compute the address of the
+ function. */
+
+static bool
+frv_local_funcdesc_p (rtx fnx)
+{
+ tree fn;
+ enum symbol_visibility vis;
+ bool ret;
+
+ if (! SYMBOL_REF_LOCAL_P (fnx))
+ return FALSE;
+
+ fn = SYMBOL_REF_DECL (fnx);
+
+ if (! fn)
+ return FALSE;
+
+ vis = DECL_VISIBILITY (fn);
+
+ 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;
case R_FRV_GPREL12: return "gprel12";
case R_FRV_GPRELHI: return "gprelhi";
case R_FRV_GPRELLO: return "gprello";
- default: abort ();
+ 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 ();
}
}
/* Return 1 if operand is a valid FRV address. CONDEXEC_P is true if
the operand is used by a predicated instruction. */
-static int
+int
frv_legitimate_memory_operand (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, FALSE));
+ && frv_legitimate_address_p_1 (mode, XEXP (op, 0),
+ reload_completed, condexec_p, FALSE));
}
void
x = gen_symGOTOFF2reg (dest, addr, OUR_FDPIC_REG,
GEN_INT (R_FRV_FUNCDESC_GOTOFF12));
emit_insn (x);
- cfun->uses_pic_offset_table = TRUE;
+ crtl->uses_pic_offset_table = TRUE;
addr = dest;
- }
+ }
else if (GET_CODE (addr) == SYMBOL_REF)
{
/* These are always either local, or handled through a local
c = gen_call_fdpicdi (picreg, const0_rtx, lr);
emit_call_insn (c);
}
-
-/* An address operand that may use a pair of registers, an addressing
- mode that we reject in general. */
-
-int
-ldd_address_operand (rtx x, enum machine_mode mode)
-{
- if (GET_MODE (x) != mode && GET_MODE (x) != VOIDmode)
- return FALSE;
-
- return frv_legitimate_address_p (DImode, x, reload_completed, FALSE, TRUE);
-}
-
-int
-fdpic_fptr_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
- if (GET_CODE (op) != REG)
- return FALSE;
- if (REGNO (op) != FDPIC_FPTR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
- return TRUE;
-}
\f
-/* Return 1 is OP is a memory operand, or will be turned into one by
- reload. */
+/* 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
-frv_load_operand (rtx op, enum machine_mode mode)
+static bool
+frv_function_symbol_referenced_p (rtx x)
{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (reload_in_progress)
- {
- 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)];
- }
-
- return op && memory_operand (op, mode);
-}
-
-
-/* Return 1 if operand is a GPR register or a FPR register. */
+ const char *format;
+ int length;
+ int j;
-int
-gpr_or_fpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ if (GET_CODE (x) == SYMBOL_REF)
+ return SYMBOL_REF_FUNCTION_P (x);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ length = GET_RTX_LENGTH (GET_CODE (x));
+ format = GET_RTX_FORMAT (GET_CODE (x));
- if (GET_CODE (op) == SUBREG)
+ for (j = 0; j < length; ++j)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
+ switch (format[j])
+ {
+ case 'e':
+ if (frv_function_symbol_referenced_p (XEXP (x, j)))
+ return TRUE;
+ break;
- if (GET_CODE (op) != REG)
- return FALSE;
+ 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;
- regno = REGNO (op);
- if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
+ default:
+ /* Nothing to do. */
+ break;
+ }
+ }
return FALSE;
}
-/* Return 1 if operand is a GPR register or 12 bit signed immediate. */
+/* Return true if the memory operand is one that can be conditionally
+ executed. */
int
-gpr_or_int12_operand (rtx op, enum machine_mode mode)
+condexec_memory_operand (rtx op, enum machine_mode mode)
{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
-
- if (got12_operand (op, mode))
- return true;
+ enum machine_mode op_mode = GET_MODE (op);
+ rtx addr;
- if (GET_MODE (op) != mode && mode != VOIDmode)
+ if (mode != VOIDmode && op_mode != mode)
return FALSE;
- if (GET_CODE (op) == SUBREG)
+ switch (op_mode)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ default:
+ return FALSE;
- op = SUBREG_REG (op);
+ case QImode:
+ case HImode:
+ case SImode:
+ case SFmode:
+ break;
}
- if (GET_CODE (op) != REG)
+ if (GET_CODE (op) != MEM)
return FALSE;
- return GPR_OR_PSEUDO_P (REGNO (op));
+ addr = XEXP (op, 0);
+ return frv_legitimate_address_p_1 (mode, addr, reload_completed, TRUE, FALSE);
}
-
-/* Return 1 if operand is a GPR register, or a FPR register, or a 12 bit
- signed immediate. */
+\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
-gpr_fpr_or_int12_operand (rtx op, enum machine_mode mode)
+direct_return_p (void)
{
- int regno;
-
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
-
- 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);
- }
+ frv_stack_t *info;
- if (GET_CODE (op) != REG)
+ if (!reload_completed)
return FALSE;
- regno = REGNO (op);
- if (GPR_P (regno) || FPR_P (regno) || regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
-
- return FALSE;
+ info = frv_stack_info ();
+ return (info->total_size == 0);
}
-/* Return 1 if operand is a register or 6 bit signed immediate. */
-
-int
-fpr_or_int6_operand (rtx op, enum machine_mode mode)
+\f
+void
+frv_emit_move (enum machine_mode mode, rtx dest, rtx src)
{
- 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 (src) == 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 (src);
+ if (model != 0)
+ src = frv_legitimize_tls_address (src, model);
}
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return FPR_OR_PSEUDO_P (REGNO (op));
-}
-
-/* Return 1 if operand is a register or 10 bit signed immediate. */
-
-int
-gpr_or_int10_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) == CONST_INT)
- return IN_RANGE_P (INTVAL (op), -512, 511);
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
-
- if (GET_CODE (op) == SUBREG)
+ switch (mode)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ case SImode:
+ if (frv_emit_movsi (dest, src))
+ return;
+ break;
- op = SUBREG_REG (op);
- }
+ 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;
- if (GET_CODE (op) != REG)
- return FALSE;
+ default:
+ gcc_unreachable ();
+ }
- return GPR_OR_PSEUDO_P (REGNO (op));
+ emit_insn (gen_rtx_SET (VOIDmode, dest, src));
}
-/* Return 1 if operand is a register or an integer immediate. */
+/* 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
-gpr_or_int_operand (rtx op, enum machine_mode mode)
+frv_emit_movsi (rtx dest, rtx src)
{
- if (GET_CODE (op) == CONST_INT)
- return TRUE;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ int base_regno = -1;
+ int unspec = 0;
+ rtx sym = src;
+ struct frv_unspec old_unspec;
- if (GET_CODE (op) == SUBREG)
+ if (!reload_in_progress
+ && !reload_completed
+ && !register_operand (dest, SImode)
+ && (!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. */
+ || (GET_CODE (src) == REG
+ && IN_RANGE (REGNO (src),
+ FIRST_VIRTUAL_REGISTER,
+ LAST_VIRTUAL_POINTER_REGISTER))))
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
+ emit_insn (gen_rtx_SET (VOIDmode, dest, copy_to_mode_reg (SImode, src)));
+ return TRUE;
}
- if (GET_CODE (op) != REG)
- return FALSE;
+ /* Explicitly add in the PIC or small data register if needed. */
+ switch (GET_CODE (src))
+ {
+ default:
+ break;
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
+ case LABEL_REF:
+ 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:
-/* Return 1 if operand is a 12 bit signed immediate. */
-
-int
-int12_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return IN_RANGE_P (INTVAL (op), -2048, 2047);
-}
-
-/* Return 1 if operand is a 6 bit signed immediate. */
-
-int
-int6_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
-
- return IN_RANGE_P (INTVAL (op), -32, 31);
-}
-
-/* Return 1 if operand is a 5 bit signed immediate. */
-
-int
-int5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), -16, 15);
-}
-
-/* Return 1 if operand is a 5 bit unsigned immediate. */
-
-int
-uint5_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 31);
-}
-
-/* Return 1 if operand is a 4 bit unsigned immediate. */
-
-int
-uint4_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 15);
-}
+ sethi #gothi(label), gr#
+ setlo #gotlo(label), gr#
+ ld @(gr15,gr#), gr#
-/* Return 1 if operand is a 1 bit unsigned immediate (0 or 1). */
+ for
-int
-uint1_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- return GET_CODE (op) == CONST_INT && IN_RANGE_P (INTVAL (op), 0, 1);
-}
+ ld @(gr15,#got12(_gp)), gr#
+ sethi #gprelhi(label), gr##
+ setlo #gprello(label), gr##
+ add gr#, gr##, gr##
-/* Return 1 if operand is an integer constant that takes 2 instructions
- to load up and can be split into sethi/setlo instructions.. */
+ 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.
-int
-int_2word_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- HOST_WIDE_INT value;
- REAL_VALUE_TYPE rv;
- long l;
+ 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;
- switch (GET_CODE (op))
- {
- default:
break;
- case LABEL_REF:
- if (TARGET_FDPIC)
- return FALSE;
-
- return (flag_pic == 0);
-
case CONST:
- if (flag_pic || TARGET_FDPIC)
- return FALSE;
-
- op = XEXP (op, 0);
- if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
- op = XEXP (op, 0);
- return GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF;
-
- case SYMBOL_REF:
- if (TARGET_FDPIC)
- return FALSE;
-
- /* small data references are already 1 word */
- return (flag_pic == 0) && (! SYMBOL_REF_SMALL_P (op));
-
- case CONST_INT:
- return ! IN_RANGE_P (INTVAL (op), -32768, 32767);
+ if (frv_const_unspec_p (src, &old_unspec))
+ break;
- case CONST_DOUBLE:
- if (GET_MODE (op) == SFmode)
+ if (TARGET_FDPIC && frv_function_symbol_referenced_p (XEXP (src, 0)))
{
- REAL_VALUE_FROM_CONST_DOUBLE (rv, op);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
- value = l;
- return ! IN_RANGE_P (value, -32768, 32767);
+ handle_whatever:
+ src = force_reg (GET_MODE (XEXP (src, 0)), XEXP (src, 0));
+ emit_move_insn (dest, src);
+ return TRUE;
}
- else if (GET_MODE (op) == VOIDmode)
+ else
{
- value = CONST_DOUBLE_LOW (op);
- return ! IN_RANGE_P (value, -32768, 32767);
+ 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;
- }
- return FALSE;
-}
+ case SYMBOL_REF:
+ handle_sym:
+ if (TARGET_FDPIC)
+ {
+ enum tls_model model = SYMBOL_REF_TLS_MODEL (sym);
-/* Return 1 if operand is a 16 bit unsigned immediate. */
+ if (model != 0)
+ {
+ src = frv_legitimize_tls_address (src, model);
+ emit_move_insn (dest, src);
+ return TRUE;
+ }
-int
-uint16_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
+ 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;
- return IN_RANGE_P (INTVAL (op), 0, 0xffff);
-}
+ if (init && init != error_mark_node)
+ reloc = compute_reloc_for_constant (init);
-/* Return 1 if operand is an integer constant with the bottom 16 bits
- clear. */
+ named_section = TREE_CODE (decl) == VAR_DECL
+ && lookup_attribute ("section", DECL_ATTRIBUTES (decl));
+ readonly = decl_readonly_section (decl, reloc);
-int
-upper_int16_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- if (GET_CODE (op) != CONST_INT)
- return FALSE;
+ 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;
+ }
+ }
- return ((INTVAL (op) & 0xffff) == 0);
-}
+ else if (SYMBOL_REF_SMALL_P (sym))
+ base_regno = SDA_BASE_REG;
-/* Return true if operand is a GPR register. */
+ else if (flag_pic)
+ base_regno = PIC_REGNO;
-int
-integer_register_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ break;
+ }
- if (GET_CODE (op) == SUBREG)
+ if (base_regno >= 0)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
+ 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)
+ crtl->uses_pic_offset_table = TRUE;
+ return TRUE;
}
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return GPR_OR_PSEUDO_P (REGNO (op));
-}
+ if (unspec)
+ {
+ rtx x;
-/* Return true if operand is a GPR register. Do not allow SUBREG's
- here, in order to prevent a combine bug. */
+ /* 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);
-int
-gpr_no_subreg_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ 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);
+ crtl->uses_pic_offset_table = TRUE;
+ return TRUE;
+ }
- if (GET_CODE (op) != REG)
- return FALSE;
- return GPR_OR_PSEUDO_P (REGNO (op));
+ return FALSE;
}
-/* Return true if operand is a FPR register. */
+\f
+/* Return a string to output a single word move. */
-int
-fpr_operand (rtx op, enum machine_mode mode)
+const char *
+output_move_single (rtx operands[], rtx insn)
{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ rtx dest = operands[0];
+ rtx src = operands[1];
- if (GET_CODE (op) == SUBREG)
+ if (GET_CODE (dest) == REG)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
+ int dest_regno = REGNO (dest);
+ enum machine_mode mode = GET_MODE (dest);
- if (GET_CODE (op) != REG)
- return FALSE;
-
- return FPR_OR_PSEUDO_P (REGNO (op));
-}
+ if (GPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* gpr <- some sort of register */
+ int src_regno = REGNO (src);
-/* Return true if operand is an even GPR or FPR register. */
+ if (GPR_P (src_regno))
+ return "mov %1, %0";
-int
-even_reg_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ else if (FPR_P (src_regno))
+ return "movfg %1, %0";
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ else if (SPR_P (src_regno))
+ return "movsg %1, %0";
+ }
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ else if (GET_CODE (src) == MEM)
+ {
+ /* gpr <- memory */
+ switch (mode)
+ {
+ default:
+ break;
- op = SUBREG_REG (op);
- }
+ case QImode:
+ return "ldsb%I1%U1 %M1,%0";
- if (GET_CODE (op) != REG)
- return FALSE;
+ case HImode:
+ return "ldsh%I1%U1 %M1,%0";
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
+ case SImode:
+ case SFmode:
+ return "ld%I1%U1 %M1, %0";
+ }
+ }
- if (GPR_P (regno))
- return (((regno - GPR_FIRST) & 1) == 0);
+ else if (GET_CODE (src) == CONST_INT
+ || GET_CODE (src) == CONST_DOUBLE)
+ {
+ /* gpr <- integer/floating constant */
+ HOST_WIDE_INT value;
- if (FPR_P (regno))
- return (((regno - FPR_FIRST) & 1) == 0);
+ if (GET_CODE (src) == CONST_INT)
+ value = INTVAL (src);
- return FALSE;
-}
+ else if (mode == SFmode)
+ {
+ REAL_VALUE_TYPE rv;
+ long l;
-/* Return true if operand is an odd GPR register. */
+ REAL_VALUE_FROM_CONST_DOUBLE (rv, src);
+ REAL_VALUE_TO_TARGET_SINGLE (rv, l);
+ value = l;
+ }
-int
-odd_reg_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ else
+ value = CONST_DOUBLE_LOW (src);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ if (IN_RANGE (value, -32768, 32767))
+ return "setlos %1, %0";
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ return "#";
+ }
- op = SUBREG_REG (op);
- }
+ else if (GET_CODE (src) == SYMBOL_REF
+ || GET_CODE (src) == LABEL_REF
+ || GET_CODE (src) == CONST)
+ {
+ return "#";
+ }
+ }
- if (GET_CODE (op) != REG)
- return FALSE;
+ else if (FPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* fpr <- some sort of register */
+ int src_regno = REGNO (src);
- regno = REGNO (op);
- /* Assume that reload will give us an even register. */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
+ if (GPR_P (src_regno))
+ return "movgf %1, %0";
- if (GPR_P (regno))
- return (((regno - GPR_FIRST) & 1) != 0);
+ else if (FPR_P (src_regno))
+ {
+ if (TARGET_HARD_FLOAT)
+ return "fmovs %1, %0";
+ else
+ return "mor %1, %1, %0";
+ }
+ }
- if (FPR_P (regno))
- return (((regno - FPR_FIRST) & 1) != 0);
+ else if (GET_CODE (src) == MEM)
+ {
+ /* fpr <- memory */
+ switch (mode)
+ {
+ default:
+ break;
- return FALSE;
-}
+ case QImode:
+ return "ldbf%I1%U1 %M1,%0";
-/* Return true if operand is an even GPR register. */
+ case HImode:
+ return "ldhf%I1%U1 %M1,%0";
-int
-even_gpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ case SImode:
+ case SFmode:
+ return "ldf%I1%U1 %M1, %0";
+ }
+ }
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ else if (ZERO_P (src))
+ return "movgf %., %0";
+ }
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ else if (SPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* spr <- some sort of register */
+ int src_regno = REGNO (src);
- op = SUBREG_REG (op);
+ if (GPR_P (src_regno))
+ return "movgs %1, %0";
+ }
+ else if (ZERO_P (src))
+ return "movgs %., %0";
+ }
}
- 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 (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 (dest) == MEM)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
+ if (GET_CODE (src) == REG)
+ {
+ int src_regno = REGNO (src);
+ enum machine_mode mode = GET_MODE (dest);
- if (GET_CODE (op) != REG)
- return FALSE;
+ if (GPR_P (src_regno))
+ {
+ switch (mode)
+ {
+ default:
+ break;
- regno = REGNO (op);
- /* Assume that reload will give us an even register. */
- if (regno >= FIRST_PSEUDO_REGISTER)
- return FALSE;
+ case QImode:
+ return "stb%I0%U0 %1, %M0";
- if (! GPR_P (regno))
- return FALSE;
+ case HImode:
+ return "sth%I0%U0 %1, %M0";
- return (((regno - GPR_FIRST) & 1) != 0);
-}
+ case SImode:
+ case SFmode:
+ return "st%I0%U0 %1, %M0";
+ }
+ }
-/* Return true if operand is a quad aligned FPR register. */
+ else if (FPR_P (src_regno))
+ {
+ switch (mode)
+ {
+ default:
+ break;
-int
-quad_fpr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ case QImode:
+ return "stbf%I0%U0 %1, %M0";
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ case HImode:
+ return "sthf%I0%U0 %1, %M0";
- if (GET_CODE (op) == SUBREG)
- {
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ case SImode:
+ case SFmode:
+ return "stf%I0%U0 %1, %M0";
+ }
+ }
+ }
- op = SUBREG_REG (op);
- }
+ else if (ZERO_P (src))
+ {
+ switch (GET_MODE (dest))
+ {
+ default:
+ break;
- if (GET_CODE (op) != REG)
- return FALSE;
+ case QImode:
+ return "stb%I0%U0 %., %M0";
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
+ case HImode:
+ return "sth%I0%U0 %., %M0";
- if (! FPR_P (regno))
- return FALSE;
+ case SImode:
+ case SFmode:
+ return "st%I0%U0 %., %M0";
+ }
+ }
+ }
- return (((regno - FPR_FIRST) & 3) == 0);
+ fatal_insn ("bad output_move_single operand", insn);
+ return "";
}
-/* Return true if operand is an even FPR register. */
+\f
+/* Return a string to output a double word move. */
-int
-even_fpr_operand (rtx op, enum machine_mode mode)
+const char *
+output_move_double (rtx operands[], rtx insn)
{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ rtx dest = operands[0];
+ rtx src = operands[1];
+ enum machine_mode mode = GET_MODE (dest);
- if (GET_CODE (op) == SUBREG)
+ if (GET_CODE (dest) == REG)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ int dest_regno = REGNO (dest);
- op = SUBREG_REG (op);
- }
+ if (GPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* gpr <- some sort of register */
+ int src_regno = REGNO (src);
- if (GET_CODE (op) != REG)
- return FALSE;
+ if (GPR_P (src_regno))
+ return "#";
- regno = REGNO (op);
- if (regno >= FIRST_PSEUDO_REGISTER)
- return TRUE;
+ else if (FPR_P (src_regno))
+ {
+ if (((dest_regno - GPR_FIRST) & 1) == 0
+ && ((src_regno - FPR_FIRST) & 1) == 0)
+ return "movfgd %1, %0";
- if (! FPR_P (regno))
- return FALSE;
+ return "#";
+ }
+ }
- return (((regno - FPR_FIRST) & 1) == 0);
-}
+ else if (GET_CODE (src) == MEM)
+ {
+ /* gpr <- memory */
+ if (dbl_memory_one_insn_operand (src, mode))
+ return "ldd%I1%U1 %M1, %0";
-/* Return true if operand is an odd FPR register. */
+ return "#";
+ }
-int
-odd_fpr_operand (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 (rtx op, enum machine_mode mode)
-{
- rtx addr;
- rtx addr_reg;
-
- if (! TARGET_DWORD)
- return FALSE;
-
- if (GET_CODE (op) != MEM)
- return FALSE;
+ else if (GET_CODE (src) == CONST_INT
+ || GET_CODE (src) == CONST_DOUBLE)
+ return "#";
+ }
- if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
- return FALSE;
+ else if (FPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* fpr <- some sort of register */
+ int src_regno = REGNO (src);
- addr = XEXP (op, 0);
- if (GET_CODE (addr) == REG)
- addr_reg = addr;
+ if (GPR_P (src_regno))
+ {
+ if (((dest_regno - FPR_FIRST) & 1) == 0
+ && ((src_regno - GPR_FIRST) & 1) == 0)
+ return "movgfd %1, %0";
- else if (GET_CODE (addr) == PLUS)
- {
- rtx addr0 = XEXP (addr, 0);
- rtx addr1 = XEXP (addr, 1);
+ return "#";
+ }
- if (GET_CODE (addr0) != REG)
- return FALSE;
+ else if (FPR_P (src_regno))
+ {
+ if (TARGET_DOUBLE
+ && ((dest_regno - FPR_FIRST) & 1) == 0
+ && ((src_regno - FPR_FIRST) & 1) == 0)
+ return "fmovd %1, %0";
- if (got12_operand (addr1, VOIDmode))
- return TRUE;
+ return "#";
+ }
+ }
- if (GET_CODE (addr1) != CONST_INT)
- return FALSE;
+ else if (GET_CODE (src) == MEM)
+ {
+ /* fpr <- memory */
+ if (dbl_memory_one_insn_operand (src, mode))
+ return "lddf%I1%U1 %M1, %0";
- if ((INTVAL (addr1) & 7) != 0)
- return FALSE;
+ return "#";
+ }
- addr_reg = addr0;
+ else if (ZERO_P (src))
+ return "#";
+ }
}
- else
- return FALSE;
+ else if (GET_CODE (dest) == MEM)
+ {
+ if (GET_CODE (src) == REG)
+ {
+ int src_regno = REGNO (src);
- if (addr_reg == frame_pointer_rtx || addr_reg == stack_pointer_rtx)
- return TRUE;
+ if (GPR_P (src_regno))
+ {
+ if (((src_regno - GPR_FIRST) & 1) == 0
+ && dbl_memory_one_insn_operand (dest, mode))
+ return "std%I0%U0 %1, %M0";
- return FALSE;
-}
+ return "#";
+ }
-/* Return true if operand is a 2 word memory address that needs to
- use two instructions to load or store. */
+ if (FPR_P (src_regno))
+ {
+ if (((src_regno - FPR_FIRST) & 1) == 0
+ && dbl_memory_one_insn_operand (dest, mode))
+ return "stdf%I0%U0 %1, %M0";
-int
-dbl_memory_two_insn_operand (rtx op, enum machine_mode mode)
-{
- if (GET_CODE (op) != MEM)
- return FALSE;
+ return "#";
+ }
+ }
- if (mode != VOIDmode && GET_MODE_SIZE (mode) != 2*UNITS_PER_WORD)
- return FALSE;
+ else if (ZERO_P (src))
+ {
+ if (dbl_memory_one_insn_operand (dest, mode))
+ return "std%I0%U0 %., %M0";
- if (! TARGET_DWORD)
- return TRUE;
+ return "#";
+ }
+ }
- return ! dbl_memory_one_insn_operand (op, mode);
+ fatal_insn ("bad output_move_double operand", insn);
+ return "";
}
-/* Return true if operand is something that can be an output for a move
- operation. */
+\f
+/* Return a string to output a single word conditional move.
+ Operand0 -- EQ/NE of ccr register and 0
+ Operand1 -- CCR register
+ Operand2 -- destination
+ Operand3 -- source */
-int
-move_destination_operand (rtx op, enum machine_mode mode)
+const char *
+output_condmove_single (rtx operands[], rtx insn)
{
- rtx subreg;
- enum rtx_code code;
+ rtx dest = operands[2];
+ rtx src = operands[3];
- switch (GET_CODE (op))
+ if (GET_CODE (dest) == REG)
{
- default:
- break;
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ int dest_regno = REGNO (dest);
+ enum machine_mode mode = GET_MODE (dest);
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, FALSE, FALSE);
+ if (GPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* gpr <- some sort of register */
+ int src_regno = REGNO (src);
- return (code == REG);
+ if (GPR_P (src_regno))
+ return "cmov %z3, %2, %1, %e0";
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ else if (FPR_P (src_regno))
+ return "cmovfg %3, %2, %1, %e0";
+ }
- return TRUE;
+ else if (GET_CODE (src) == MEM)
+ {
+ /* gpr <- memory */
+ switch (mode)
+ {
+ default:
+ break;
- case MEM:
- return frv_legitimate_memory_operand (op, mode, FALSE);
- }
+ case QImode:
+ return "cldsb%I3%U3 %M3, %2, %1, %e0";
- return FALSE;
-}
+ case HImode:
+ return "cldsh%I3%U3 %M3, %2, %1, %e0";
-/* 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. */
+ case SImode:
+ case SFmode:
+ return "cld%I3%U3 %M3, %2, %1, %e0";
+ }
+ }
-static bool
-frv_function_symbol_referenced_p (rtx x)
-{
- const char *format;
- int length;
- int j;
+ else if (ZERO_P (src))
+ return "cmov %., %2, %1, %e0";
+ }
- if (GET_CODE (x) == SYMBOL_REF)
- return SYMBOL_REF_FUNCTION_P (x);
+ else if (FPR_P (dest_regno))
+ {
+ if (GET_CODE (src) == REG)
+ {
+ /* fpr <- some sort of register */
+ int src_regno = REGNO (src);
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
+ if (GPR_P (src_regno))
+ return "cmovgf %3, %2, %1, %e0";
- for (j = 0; j < length; ++j)
- {
- switch (format[j])
- {
- case 'e':
- if (frv_function_symbol_referenced_p (XEXP (x, j)))
- return TRUE;
- break;
+ else if (FPR_P (src_regno))
+ {
+ if (TARGET_HARD_FLOAT)
+ return "cfmovs %3,%2,%1,%e0";
+ else
+ return "cmor %3, %3, %2, %1, %e0";
+ }
+ }
- case 'V':
- case 'E':
- if (XVEC (x, j) != 0)
+ else if (GET_CODE (src) == MEM)
{
- int k;
- for (k = 0; k < XVECLEN (x, j); ++k)
- if (frv_function_symbol_referenced_p (XVECEXP (x, j, k)))
- return TRUE;
+ /* fpr <- memory */
+ if (mode == SImode || mode == SFmode)
+ return "cldf%I3%U3 %M3, %2, %1, %e0";
}
- break;
- default:
- /* Nothing to do. */
- break;
+ else if (ZERO_P (src))
+ return "cmovgf %., %2, %1, %e0";
}
}
- return FALSE;
-}
-
-/* Return true if operand is something that can be an input for a move
- operation. */
-
-int
-move_source_operand (rtx op, enum machine_mode mode)
-{
- rtx subreg;
- enum rtx_code code;
-
- switch (GET_CODE (op))
+ else if (GET_CODE (dest) == MEM)
{
- default:
- break;
-
- case CONST_INT:
- case CONST_DOUBLE:
- return immediate_operand (op, mode);
-
- case SUBREG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ if (GET_CODE (src) == REG)
+ {
+ int src_regno = REGNO (src);
+ enum machine_mode mode = GET_MODE (dest);
- subreg = SUBREG_REG (op);
- code = GET_CODE (subreg);
- if (code == MEM)
- return frv_legitimate_address_p (mode, XEXP (subreg, 0),
- reload_completed, FALSE, FALSE);
+ if (GPR_P (src_regno))
+ {
+ switch (mode)
+ {
+ default:
+ break;
- return (code == REG);
+ case QImode:
+ return "cstb%I2%U2 %3, %M2, %1, %e0";
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ case HImode:
+ return "csth%I2%U2 %3, %M2, %1, %e0";
- return TRUE;
+ case SImode:
+ case SFmode:
+ return "cst%I2%U2 %3, %M2, %1, %e0";
+ }
+ }
- case MEM:
- return frv_legitimate_memory_operand (op, mode, FALSE);
- }
+ else if (FPR_P (src_regno) && (mode == SImode || mode == SFmode))
+ return "cstf%I2%U2 %3, %M2, %1, %e0";
+ }
- return FALSE;
-}
-
-/* Return true if operand is something that can be an output for a conditional
- move operation. */
-
-int
-condexec_dest_operand (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, FALSE);
-
- return (code == REG);
+ else if (ZERO_P (src))
+ {
+ enum machine_mode mode = GET_MODE (dest);
+ switch (mode)
+ {
+ default:
+ break;
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ case QImode:
+ return "cstb%I2%U2 %., %M2, %1, %e0";
- return TRUE;
+ case HImode:
+ return "csth%I2%U2 %., %M2, %1, %e0";
- case MEM:
- return frv_legitimate_memory_operand (op, mode, TRUE);
+ case SImode:
+ case SFmode:
+ return "cst%I2%U2 %., %M2, %1, %e0";
+ }
+ }
}
- return FALSE;
+ fatal_insn ("bad output_condmove_single operand", insn);
+ return "";
}
-/* Return true if operand is something that can be an input for a conditional
- move operation. */
+\f
+/* Emit the appropriate code to do a comparison, returning the register the
+ comparison was done it. */
-int
-condexec_source_operand (rtx op, enum machine_mode mode)
+static rtx
+frv_emit_comparison (enum rtx_code test, rtx op0, rtx op1)
{
- 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, FALSE);
-
- return (code == REG);
+ enum machine_mode cc_mode;
+ rtx cc_reg;
- case REG:
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ /* 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);
- return TRUE;
+ /* Possibly disable using anything but a fixed register in order to work
+ around cse moving comparisons past function calls. */
+ cc_mode = SELECT_CC_MODE (test, op0, op1);
+ cc_reg = ((TARGET_ALLOC_CC)
+ ? gen_reg_rtx (cc_mode)
+ : gen_rtx_REG (cc_mode,
+ (cc_mode == CC_FPmode) ? FCC_FIRST : ICC_FIRST));
- case MEM:
- return frv_legitimate_memory_operand (op, mode, TRUE);
- }
+ emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
+ gen_rtx_COMPARE (cc_mode, op0, op1)));
- return FALSE;
+ return cc_reg;
}
-/* Return true if operand is a register of any flavor or a 0 of the
- appropriate type. */
+\f
+/* Emit code for a conditional branch.
+ XXX: I originally wanted to add a clobber of a CCR register to use in
+ conditional execution, but that confuses the rest of the compiler. */
int
-reg_or_0_operand (rtx op, enum machine_mode mode)
+frv_emit_cond_branch (rtx operands[])
{
- 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);
- }
+ rtx test_rtx;
+ rtx label_ref;
+ rtx if_else;
+ enum rtx_code test = GET_CODE (operands[0]);
+ rtx cc_reg = frv_emit_comparison (test, operands[1], operands[2]);
+ enum machine_mode cc_mode = GET_MODE (cc_reg);
- return FALSE;
+ /* Branches generate:
+ (set (pc)
+ (if_then_else (<test>, <cc_reg>, (const_int 0))
+ (label_ref <branch_label>)
+ (pc))) */
+ label_ref = gen_rtx_LABEL_REF (VOIDmode, operands[3]);
+ 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;
}
-/* Return true if operand is the link register. */
+\f
+/* Emit code to set a gpr to 1/0 based on a comparison. */
int
-lr_operand (rtx op, enum machine_mode mode)
+frv_emit_scc (rtx operands[])
{
- if (GET_CODE (op) != REG)
- return FALSE;
+ rtx set;
+ rtx test_rtx;
+ rtx clobber;
+ rtx cr_reg;
+ enum rtx_code test = GET_CODE (operands[1]);
+ rtx cc_reg = frv_emit_comparison (test, operands[2], operands[3]);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ /* SCC instructions generate:
+ (parallel [(set <target> (<test>, <cc_reg>, (const_int 0))
+ (clobber (<ccr_reg>))]) */
+ test_rtx = gen_rtx_fmt_ee (test, SImode, cc_reg, const0_rtx);
+ set = gen_rtx_SET (VOIDmode, operands[0], test_rtx);
- if (REGNO (op) != LR_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
+ cr_reg = ((TARGET_ALLOC_CC)
+ ? gen_reg_rtx (CC_CCRmode)
+ : gen_rtx_REG (CC_CCRmode,
+ ((GET_MODE (cc_reg) == CC_FPmode)
+ ? FCR_FIRST
+ : ICR_FIRST)));
+ clobber = gen_rtx_CLOBBER (VOIDmode, cr_reg);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
return TRUE;
}
-/* Return true if operand is the uClinux PIC register. */
+\f
+/* Split a SCC instruction into component parts, returning a SEQUENCE to hold
+ the separate insns. */
-int
-fdpic_operand (rtx op, enum machine_mode mode)
+rtx
+frv_split_scc (rtx dest, rtx test, rtx cc_reg, rtx cr_reg, HOST_WIDE_INT value)
{
- if (!TARGET_FDPIC)
- return FALSE;
-
- if (GET_CODE (op) != REG)
- return FALSE;
+ rtx ret;
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ start_sequence ();
- if (REGNO (op) != FDPIC_REGNO && REGNO (op) < FIRST_PSEUDO_REGISTER)
- return FALSE;
+ /* Set the appropriate CCR bit. */
+ emit_insn (gen_rtx_SET (VOIDmode,
+ cr_reg,
+ gen_rtx_fmt_ee (GET_CODE (test),
+ GET_MODE (cr_reg),
+ cc_reg,
+ const0_rtx)));
- return TRUE;
-}
+ /* Move the value into the destination. */
+ emit_move_insn (dest, GEN_INT (value));
-int
-got12_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- struct frv_unspec unspec;
+ /* Move 0 into the destination if the test failed */
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_EQ (GET_MODE (cr_reg),
+ cr_reg,
+ const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, const0_rtx)));
- if (frv_const_unspec_p (op, &unspec))
- switch (unspec.reloc)
- {
- case R_FRV_GOT12:
- case R_FRV_GOTOFF12:
- case R_FRV_FUNCDESC_GOT12:
- case R_FRV_FUNCDESC_GOTOFF12:
- case R_FRV_GPREL12:
- return true;
- }
- return false;
+ /* Finish up, return sequence. */
+ ret = get_insns ();
+ end_sequence ();
+ return ret;
}
-/* Return true if OP is a valid const-unspec expression. */
-
-int
-const_unspec_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
- struct frv_unspec unspec;
-
- return frv_const_unspec_p (op, &unspec);
-}
-/* Return true if operand is a gpr register or a valid memory operation. */
+\f
+/* Emit the code for a conditional move, return TRUE if we could do the
+ move. */
int
-gpr_or_memory_operand (rtx op, enum machine_mode mode)
+frv_emit_cond_move (rtx dest, rtx test_rtx, rtx src1, rtx src2)
{
- 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. */
+ rtx set;
+ rtx clobber_cc;
+ rtx test2;
+ rtx cr_reg;
+ rtx if_rtx;
+ enum rtx_code test = GET_CODE (test_rtx);
+ rtx cc_reg = frv_emit_comparison (test,
+ XEXP (test_rtx, 0), XEXP (test_rtx, 1));
+ enum machine_mode cc_mode = GET_MODE (cc_reg);
-int
-fpr_or_memory_operand (rtx op, enum machine_mode mode)
-{
- return (fpr_operand (op, mode)
- || frv_legitimate_memory_operand (op, mode, FALSE));
-}
+ /* Conditional move instructions generate:
+ (parallel [(set <target>
+ (if_then_else (<test> <cc_reg> (const_int 0))
+ <src1>
+ <src2>))
+ (clobber (<ccr_reg>))]) */
-/* Return true if operand is an icc register. */
+ /* Handle various cases of conditional move involving two constants. */
+ if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT)
+ {
+ HOST_WIDE_INT value1 = INTVAL (src1);
+ HOST_WIDE_INT value2 = INTVAL (src2);
-int
-icc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ /* 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)
+ ;
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ /* If the first value is within an addi range and also the difference
+ between the two fits in an addi's range, load up the difference, then
+ conditionally move in 0, and then unconditionally add the first
+ value. */
+ else if (IN_RANGE (value1, -2048, 2047)
+ && IN_RANGE (value2 - value1, -2048, 2047))
+ ;
- if (GET_CODE (op) != REG)
- return FALSE;
+ /* If neither condition holds, just force the constant into a
+ register. */
+ else
+ {
+ src1 = force_reg (GET_MODE (dest), src1);
+ src2 = force_reg (GET_MODE (dest), src2);
+ }
+ }
- regno = REGNO (op);
- return ICC_OR_PSEUDO_P (regno);
-}
+ /* If one value is a register, insure the other value is either 0 or a
+ register. */
+ else
+ {
+ if (GET_CODE (src1) == CONST_INT && INTVAL (src1) != 0)
+ src1 = force_reg (GET_MODE (dest), src1);
-/* Return true if operand is an fcc register. */
+ if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
+ src2 = force_reg (GET_MODE (dest), src2);
+ }
-int
-fcc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ test2 = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx);
+ if_rtx = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), test2, src1, src2);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ set = gen_rtx_SET (VOIDmode, dest, if_rtx);
- if (GET_CODE (op) != REG)
- return FALSE;
+ cr_reg = ((TARGET_ALLOC_CC)
+ ? gen_reg_rtx (CC_CCRmode)
+ : gen_rtx_REG (CC_CCRmode,
+ (cc_mode == CC_FPmode) ? FCR_FIRST : ICR_FIRST));
- regno = REGNO (op);
- return FCC_OR_PSEUDO_P (regno);
+ clobber_cc = gen_rtx_CLOBBER (VOIDmode, cr_reg);
+ emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber_cc)));
+ return TRUE;
}
-/* Return true if operand is either an fcc or icc register. */
+\f
+/* Split a conditional move into constituent parts, returning a SEQUENCE
+ containing all of the insns. */
-int
-cc_operand (rtx op, enum machine_mode mode)
+rtx
+frv_split_cond_move (rtx operands[])
{
- 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 (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. */
+ rtx dest = operands[0];
+ rtx test = operands[1];
+ rtx cc_reg = operands[2];
+ rtx src1 = operands[3];
+ rtx src2 = operands[4];
+ rtx cr_reg = operands[5];
+ rtx ret;
+ enum machine_mode cr_mode = GET_MODE (cr_reg);
-int
-fcr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ start_sequence ();
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ /* Set the appropriate CCR bit. */
+ emit_insn (gen_rtx_SET (VOIDmode,
+ cr_reg,
+ gen_rtx_fmt_ee (GET_CODE (test),
+ GET_MODE (cr_reg),
+ cc_reg,
+ const0_rtx)));
- if (GET_CODE (op) != REG)
- return FALSE;
+ /* Handle various cases of conditional move involving two constants. */
+ if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT)
+ {
+ HOST_WIDE_INT value1 = INTVAL (src1);
+ HOST_WIDE_INT value2 = INTVAL (src2);
- regno = REGNO (op);
- return FCR_OR_PSEUDO_P (regno);
-}
+ /* Having 0 as one of the constants can be done by loading the other
+ constant, and optionally moving in gr0. */
+ if (value1 == 0)
+ {
+ emit_move_insn (dest, src2);
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_NE (cr_mode, cr_reg,
+ const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src1)));
+ }
-/* Return true if operand is either an fcc or icc register. */
+ else if (value2 == 0)
+ {
+ emit_move_insn (dest, src1);
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_EQ (cr_mode, cr_reg,
+ const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src2)));
+ }
-int
-cr_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ /* If the first value is within an addi range and also the difference
+ between the two fits in an addi's range, load up the difference, then
+ conditionally move in 0, and then unconditionally add the first
+ value. */
+ else if (IN_RANGE (value1, -2048, 2047)
+ && IN_RANGE (value2 - value1, -2048, 2047))
+ {
+ rtx dest_si = ((GET_MODE (dest) == SImode)
+ ? dest
+ : gen_rtx_SUBREG (SImode, dest, 0));
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ emit_move_insn (dest_si, GEN_INT (value2 - value1));
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_NE (cr_mode, cr_reg,
+ const0_rtx),
+ gen_rtx_SET (VOIDmode, dest_si,
+ const0_rtx)));
+ emit_insn (gen_addsi3 (dest_si, dest_si, src1));
+ }
- if (GET_CODE (op) != REG)
- return FALSE;
+ else
+ gcc_unreachable ();
+ }
+ else
+ {
+ /* Emit the conditional move for the test being true if needed. */
+ if (! rtx_equal_p (dest, src1))
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src1)));
- regno = REGNO (op);
- if (CR_OR_PSEUDO_P (regno))
- return TRUE;
+ /* Emit the conditional move for the test being false if needed. */
+ if (! rtx_equal_p (dest, src2))
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_EQ (cr_mode, cr_reg, const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src2)));
+ }
- return FALSE;
+ /* Finish up, return sequence. */
+ ret = get_insns ();
+ end_sequence ();
+ return ret;
}
-/* Return true if operand is a memory reference suitable for a call. */
-
-int
-call_operand (rtx op, enum machine_mode mode)
+\f
+/* 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 (rtx dest, rtx source)
{
- if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
- return FALSE;
+ int regno = REGNO (dest);
+ rtx dest1 = gen_highpart (SImode, dest);
+ rtx dest2 = gen_lowpart (SImode, dest);
+ rtx address = XEXP (source, 0);
- if (GET_CODE (op) == SYMBOL_REF)
- return TRUE;
+ /* If the address is pre-modified, load the lower-numbered register
+ first, then load the other register using an integer offset from
+ the modified base register. This order should always be safe,
+ since the pre-modification cannot affect the same registers as the
+ load does.
- /* 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);
+ The situation for other loads is more complicated. Loading one
+ of the registers could affect the value of ADDRESS, so we must
+ be careful which order we do them in. */
+ if (GET_CODE (address) == PRE_MODIFY
+ || ! refers_to_regno_p (regno, regno + 1, address, NULL))
+ {
+ /* It is safe to load the lower-numbered register first. */
+ emit_move_insn (dest1, change_address (source, SImode, NULL));
+ emit_move_insn (dest2, frv_index_memory (source, SImode, 1));
+ }
+ else
+ {
+ /* ADDRESS is not pre-modified and the address depends on the
+ lower-numbered register. Load the higher-numbered register
+ first. */
+ emit_move_insn (dest2, frv_index_memory (source, SImode, 1));
+ emit_move_insn (dest1, change_address (source, SImode, NULL));
+ }
}
-/* Return true if operand is a memory reference suitable for a sibcall. */
-
-int
-sibcall_operand (rtx op, enum machine_mode mode)
+/* 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 (rtx dest, rtx source)
{
- if (GET_MODE (op) != mode && mode != VOIDmode && GET_CODE (op) != CONST_INT)
- return FALSE;
-
- /* 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);
+ rtx dest1 = change_address (dest, SImode, NULL);
+ rtx dest2 = frv_index_memory (dest, SImode, 1);
+ if (ZERO_P (source))
+ {
+ emit_move_insn (dest1, CONST0_RTX (SImode));
+ emit_move_insn (dest2, CONST0_RTX (SImode));
+ }
+ else
+ {
+ emit_move_insn (dest1, gen_highpart (SImode, source));
+ emit_move_insn (dest2, gen_lowpart (SImode, source));
+ }
}
-/* Return true if operator is a kind of relational operator. */
+\f
+/* Split a min/max operation returning a SEQUENCE containing all of the
+ insns. */
-int
-relational_operator (rtx op, enum machine_mode mode)
+rtx
+frv_split_minmax (rtx operands[])
{
- rtx op0;
- rtx op1;
- int regno;
+ rtx dest = operands[0];
+ rtx minmax = operands[1];
+ rtx src1 = operands[2];
+ rtx src2 = operands[3];
+ rtx cc_reg = operands[4];
+ rtx cr_reg = operands[5];
+ rtx ret;
+ enum rtx_code test_code;
+ enum machine_mode cr_mode = GET_MODE (cr_reg);
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
+ start_sequence ();
- switch (GET_CODE (op))
+ /* Figure out which test to use. */
+ switch (GET_CODE (minmax))
{
default:
- return FALSE;
+ gcc_unreachable ();
- case EQ:
- case NE:
- case LE:
- case LT:
- case GE:
- case GT:
- case LEU:
- case LTU:
- case GEU:
- case GTU:
- break;
+ case SMIN: test_code = LT; break;
+ case SMAX: test_code = GT; break;
+ case UMIN: test_code = LTU; break;
+ case UMAX: test_code = GTU; break;
}
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
+ /* Issue the compare instruction. */
+ emit_insn (gen_rtx_SET (VOIDmode,
+ cc_reg,
+ gen_rtx_COMPARE (GET_MODE (cc_reg),
+ src1, src2)));
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
+ /* Set the appropriate CCR bit. */
+ emit_insn (gen_rtx_SET (VOIDmode,
+ cr_reg,
+ gen_rtx_fmt_ee (test_code,
+ GET_MODE (cr_reg),
+ cc_reg,
+ const0_rtx)));
- regno = REGNO (op0);
- switch (GET_MODE (op0))
+ /* If are taking the min/max of a nonzero constant, load that first, and
+ then do a conditional move of the other value. */
+ if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
{
- default:
- break;
+ gcc_assert (!rtx_equal_p (dest, src1));
- case CCmode:
- case CC_UNSmode:
- return ICC_OR_PSEUDO_P (regno);
+ emit_move_insn (dest, src2);
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src1)));
+ }
- case CC_FPmode:
- return FCC_OR_PSEUDO_P (regno);
+ /* Otherwise, do each half of the move. */
+ else
+ {
+ /* Emit the conditional move for the test being true if needed. */
+ if (! rtx_equal_p (dest, src1))
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src1)));
- case CC_CCRmode:
- return CR_OR_PSEUDO_P (regno);
+ /* Emit the conditional move for the test being false if needed. */
+ if (! rtx_equal_p (dest, src2))
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_EQ (cr_mode, cr_reg, const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src2)));
}
- return FALSE;
+ /* Finish up, return sequence. */
+ ret = get_insns ();
+ end_sequence ();
+ return ret;
}
-/* Return true if operator is a signed integer relational operator. */
+\f
+/* Split an integer abs operation returning a SEQUENCE containing all of the
+ insns. */
-int
-signed_relational_operator (rtx op, enum machine_mode mode)
+rtx
+frv_split_abs (rtx operands[])
{
- rtx op0;
- rtx op1;
- int regno;
+ rtx dest = operands[0];
+ rtx src = operands[1];
+ rtx cc_reg = operands[2];
+ rtx cr_reg = operands[3];
+ rtx ret;
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
+ start_sequence ();
- 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;
+ /* Issue the compare < 0 instruction. */
+ emit_insn (gen_rtx_SET (VOIDmode,
+ cc_reg,
+ gen_rtx_COMPARE (CCmode, src, const0_rtx)));
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
+ /* Set the appropriate CCR bit. */
+ emit_insn (gen_rtx_SET (VOIDmode,
+ cr_reg,
+ gen_rtx_fmt_ee (LT, CC_CCRmode, cc_reg, const0_rtx)));
- regno = REGNO (op0);
- if (GET_MODE (op0) == CCmode && ICC_OR_PSEUDO_P (regno))
- return TRUE;
+ /* 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)));
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
+ /* Emit the conditional move for the test being false if needed. */
+ if (! rtx_equal_p (dest, src))
+ emit_insn (gen_rtx_COND_EXEC (VOIDmode,
+ gen_rtx_EQ (CC_CCRmode, cr_reg, const0_rtx),
+ gen_rtx_SET (VOIDmode, dest, src)));
- return FALSE;
+ /* Finish up, return sequence. */
+ ret = get_insns ();
+ end_sequence ();
+ return ret;
}
-/* Return true if operator is a signed integer relational operator. */
+\f
+/* An internal function called by for_each_rtx to clear in a hard_reg set each
+ register used in an insn. */
-int
-unsigned_relational_operator (rtx op, enum machine_mode mode)
+static int
+frv_clear_registers_used (rtx *ptr, void *data)
{
- rtx op0;
- rtx op1;
- int regno;
-
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
-
- switch (GET_CODE (op))
+ if (GET_CODE (*ptr) == REG)
{
- default:
- return FALSE;
-
- case LEU:
- case LTU:
- case GEU:
- case GTU:
- break;
- }
+ int regno = REGNO (*ptr);
+ HARD_REG_SET *p_regs = (HARD_REG_SET *)data;
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
+ if (regno < FIRST_PSEUDO_REGISTER)
+ {
+ int reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (*ptr));
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
+ while (regno < reg_max)
+ {
+ CLEAR_HARD_REG_BIT (*p_regs, regno);
+ regno++;
+ }
+ }
+ }
- regno = REGNO (op0);
- if (GET_MODE (op0) == CC_UNSmode && ICC_OR_PSEUDO_P (regno))
- return TRUE;
+ return 0;
+}
- if (GET_MODE (op0) == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
+\f
+/* Initialize the extra fields provided by IFCVT_EXTRA_FIELDS. */
- return FALSE;
+/* 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_if_block_t *ce_info ATTRIBUTE_UNUSED)
+{
+ frv_ifcvt.added_insns_list = NULL_RTX;
+ frv_ifcvt.cur_scratch_regs = 0;
+ frv_ifcvt.num_nested_cond_exec = 0;
+ frv_ifcvt.cr_reg = NULL_RTX;
+ frv_ifcvt.nested_cc_reg = NULL_RTX;
+ frv_ifcvt.extra_int_cr = NULL_RTX;
+ frv_ifcvt.extra_fp_cr = NULL_RTX;
+ frv_ifcvt.last_nested_if_cr = NULL_RTX;
}
-/* Return true if operator is a floating point relational operator. */
+\f
+/* Internal function to add a potential insn to the list of insns to be inserted
+ if the conditional execution conversion is successful. */
-int
-float_relational_operator (rtx op, enum machine_mode mode)
+static void
+frv_ifcvt_add_insn (rtx pattern, rtx insn, int before_p)
{
- rtx op0;
- rtx op1;
- int regno;
+ rtx link = alloc_EXPR_LIST (VOIDmode, pattern, insn);
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
+ 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);
- switch (GET_CODE (op))
+ if (TARGET_DEBUG_COND_EXEC)
{
- default:
- return FALSE;
+ fprintf (stderr,
+ "\n:::::::::: frv_ifcvt_add_insn: add the following %s insn %d:\n",
+ (before_p) ? "before" : "after",
+ (int)INSN_UID (insn));
- 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;
+ debug_rtx (pattern);
}
-
- 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. */
+\f
+/* A C expression to modify the code described by the conditional if
+ information CE_INFO, possibly updating the tests in TRUE_EXPR, and
+ FALSE_EXPR for converting if-then and if-then-else code to conditional
+ instructions. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if the
+ tests cannot be converted. */
-int
-ccr_eqne_operator (rtx op, enum machine_mode mode)
+void
+frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false)
{
- enum machine_mode op_mode = GET_MODE (op);
- rtx op0;
- rtx op1;
- int regno;
+ basic_block test_bb = ce_info->test_bb; /* test basic block */
+ basic_block then_bb = ce_info->then_bb; /* THEN */
+ basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */
+ basic_block join_bb = ce_info->join_bb; /* join block or NULL */
+ rtx true_expr = *p_true;
+ rtx cr;
+ rtx cc;
+ rtx nested_cc;
+ enum machine_mode mode = GET_MODE (true_expr);
+ int j;
+ basic_block *bb;
+ int num_bb;
+ frv_tmp_reg_t *tmp_reg = &frv_ifcvt.tmp_reg;
+ rtx check_insn;
+ rtx sub_cond_exec_reg;
+ enum rtx_code code;
+ enum rtx_code code_true;
+ enum rtx_code code_false;
+ enum reg_class cc_class;
+ enum reg_class cr_class;
+ int cc_first;
+ int cc_last;
+ reg_set_iterator rsi;
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ /* 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_COND_EXEC
+ || (!TARGET_NESTED_CE && ce_info->pass > 1))
+ goto fail;
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
+ /* 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. */
+ 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);
+ SET_HARD_REG_BIT (tmp_reg->regs, ICC_TEMP);
+ SET_HARD_REG_BIT (tmp_reg->regs, ICR_TEMP);
- case EQ:
- case NE:
- break;
- }
+ /* If this is a nested IF, we need to discover whether the CC registers that
+ are set/used inside of the block are used anywhere else. If not, we can
+ change them to be the CC register that is paired with the CR register that
+ controls the outermost IF block. */
+ if (ce_info->pass > 1)
+ {
+ CLEAR_HARD_REG_SET (frv_ifcvt.nested_cc_ok_rewrite);
+ for (j = CC_FIRST; j <= CC_LAST; j++)
+ if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
+ {
+ if (REGNO_REG_SET_P (df_get_live_in (then_bb), j))
+ continue;
- op1 = XEXP (op, 1);
- if (op1 != const0_rtx)
- return FALSE;
+ if (else_bb
+ && REGNO_REG_SET_P (df_get_live_in (else_bb), j))
+ continue;
- op0 = XEXP (op, 0);
- if (GET_CODE (op0) != REG)
- return FALSE;
+ if (join_bb
+ && REGNO_REG_SET_P (df_get_live_in (join_bb), j))
+ continue;
- regno = REGNO (op0);
- if (op_mode == CC_CCRmode && CR_OR_PSEUDO_P (regno))
- return TRUE;
+ SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j);
+ }
+ }
- return FALSE;
-}
+ for (j = 0; j < frv_ifcvt.cur_scratch_regs; j++)
+ frv_ifcvt.scratch_regs[j] = NULL_RTX;
-/* Return true if operator is a minimum or maximum operator (both signed and
- unsigned). */
+ frv_ifcvt.added_insns_list = NULL_RTX;
+ frv_ifcvt.cur_scratch_regs = 0;
-int
-minmax_operator (rtx op, enum machine_mode mode)
-{
- if (mode != VOIDmode && mode != GET_MODE (op))
- return FALSE;
+ bb = (basic_block *) alloca ((2 + ce_info->num_multiple_test_blocks)
+ * sizeof (basic_block));
- switch (GET_CODE (op))
+ if (join_bb)
{
- default:
- return FALSE;
+ unsigned int regno;
- case SMIN:
- case SMAX:
- case UMIN:
- case UMAX:
- break;
+ /* Remove anything live at the beginning of the join block from being
+ available for allocation. */
+ EXECUTE_IF_SET_IN_REG_SET (df_get_live_in (join_bb), 0, regno, rsi)
+ {
+ if (regno < FIRST_PSEUDO_REGISTER)
+ CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
+ }
}
- if (! integer_register_operand (XEXP (op, 0), mode))
- return FALSE;
-
- if (! gpr_or_int10_operand (XEXP (op, 1), mode))
- return FALSE;
-
- return TRUE;
-}
+ /* Add in all of the blocks in multiple &&/|| blocks to be scanned. */
+ num_bb = 0;
+ if (ce_info->num_multiple_test_blocks)
+ {
+ basic_block multiple_test_bb = ce_info->last_test_bb;
-/* Return true if operator is an integer binary operator that can executed
- conditionally and takes 1 cycle. */
+ while (multiple_test_bb != test_bb)
+ {
+ bb[num_bb++] = multiple_test_bb;
+ multiple_test_bb = EDGE_PRED (multiple_test_bb, 0)->src;
+ }
+ }
-int
-condexec_si_binary_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ /* Add in the THEN and ELSE blocks to be scanned. */
+ bb[num_bb++] = then_bb;
+ if (else_bb)
+ bb[num_bb++] = else_bb;
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ sub_cond_exec_reg = NULL_RTX;
+ frv_ifcvt.num_nested_cond_exec = 0;
- switch (GET_CODE (op))
+ /* Scan all of the blocks for registers that must not be allocated. */
+ for (j = 0; j < num_bb; j++)
{
- default:
- return FALSE;
+ rtx last_insn = BB_END (bb[j]);
+ rtx insn = BB_HEAD (bb[j]);
+ unsigned int regno;
- 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. */
+ 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_HEAD (bb[j])),
+ (int) INSN_UID (BB_END (bb[j])));
-int
-condexec_si_media_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ /* Anything live at the beginning of the block is obviously unavailable
+ for allocation. */
+ EXECUTE_IF_SET_IN_REG_SET (df_get_live_in (bb[j]), 0, regno, rsi)
+ {
+ if (regno < FIRST_PSEUDO_REGISTER)
+ CLEAR_HARD_REG_BIT (tmp_reg->regs, regno);
+ }
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ /* Loop through the insns in the block. */
+ for (;;)
+ {
+ /* Mark any new registers that are created as being unavailable for
+ allocation. Also see if the CC register used in nested IFs can be
+ reallocated. */
+ if (INSN_P (insn))
+ {
+ rtx pattern;
+ rtx set;
+ int skip_nested_if = FALSE;
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
+ for_each_rtx (&PATTERN (insn), frv_clear_registers_used,
+ (void *)&tmp_reg->regs);
- case AND:
- case IOR:
- case XOR:
- return TRUE;
- }
-}
+ pattern = PATTERN (insn);
+ if (GET_CODE (pattern) == COND_EXEC)
+ {
+ rtx reg = XEXP (COND_EXEC_TEST (pattern), 0);
-/* Return true if operator is an integer division operator that can executed
- conditionally. */
+ if (reg != sub_cond_exec_reg)
+ {
+ sub_cond_exec_reg = reg;
+ frv_ifcvt.num_nested_cond_exec++;
+ }
+ }
-int
-condexec_si_divide_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ set = single_set_pattern (pattern);
+ if (set)
+ {
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ if (GET_CODE (dest) == REG)
+ {
+ int regno = REGNO (dest);
+ enum rtx_code src_code = GET_CODE (src);
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
+ if (CC_P (regno) && src_code == COMPARE)
+ skip_nested_if = TRUE;
- case DIV:
- case UDIV:
- return TRUE;
- }
-}
+ else if (CR_P (regno)
+ && (src_code == IF_THEN_ELSE
+ || COMPARISON_P (src)))
+ skip_nested_if = TRUE;
+ }
+ }
-/* Return true if operator is an integer unary operator that can executed
- conditionally. */
+ if (! skip_nested_if)
+ for_each_rtx (&PATTERN (insn), frv_clear_registers_used,
+ (void *)&frv_ifcvt.nested_cc_ok_rewrite);
+ }
-int
-condexec_si_unary_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ if (insn == last_insn)
+ break;
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ insn = NEXT_INSN (insn);
+ }
+ }
- switch (GET_CODE (op))
+ /* If this is a nested if, rewrite the CC registers that are available to
+ include the ones that can be rewritten, to increase the chance of being
+ able to allocate a paired CC/CR register combination. */
+ if (ce_info->pass > 1)
{
- default:
- return FALSE;
-
- case NEG:
- case NOT:
- return TRUE;
+ for (j = CC_FIRST; j <= CC_LAST; j++)
+ if (TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j))
+ SET_HARD_REG_BIT (tmp_reg->regs, j);
+ else
+ CLEAR_HARD_REG_BIT (tmp_reg->regs, j);
}
-}
-
-/* Return true if operator is a conversion-type expression that can be
- evaluated conditionally by floating-point instructions. */
-int
-condexec_sf_conv_operator (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))
+ if (dump_file)
{
- default:
- return FALSE;
+ int num_gprs = 0;
+ fprintf (dump_file, "Available GPRs: ");
- case NEG:
- case ABS:
- return TRUE;
- }
-}
+ for (j = GPR_FIRST; j <= GPR_LAST; j++)
+ if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
+ {
+ fprintf (dump_file, " %d [%s]", j, reg_names[j]);
+ if (++num_gprs > GPR_TEMP_NUM+2)
+ break;
+ }
-/* Return true if operator is an addition or subtraction expression.
- Such expressions can be evaluated conditionally by floating-point
- instructions. */
+ fprintf (dump_file, "%s\nAvailable CRs: ",
+ (num_gprs > GPR_TEMP_NUM+2) ? " ..." : "");
-int
-condexec_sf_add_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ for (j = CR_FIRST; j <= CR_LAST; j++)
+ if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
+ fprintf (dump_file, " %d [%s]", j, reg_names[j]);
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ fputs ("\n", dump_file);
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
+ if (ce_info->pass > 1)
+ {
+ fprintf (dump_file, "Modifiable CCs: ");
+ for (j = CC_FIRST; j <= CC_LAST; j++)
+ if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
+ fprintf (dump_file, " %d [%s]", j, reg_names[j]);
- case PLUS:
- case MINUS:
- return TRUE;
+ fprintf (dump_file, "\n%d nested COND_EXEC statements\n",
+ frv_ifcvt.num_nested_cond_exec);
+ }
}
-}
-
-/* Return true if the memory operand is one that can be conditionally
- executed. */
-
-int
-condexec_memory_operand (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)
+ /* 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 || mode == CC_NZmode)
{
- default:
- return FALSE;
-
- case QImode:
- case HImode:
- case SImode:
- case SFmode:
- break;
+ cr_class = ICR_REGS;
+ cc_class = ICC_REGS;
+ cc_first = ICC_FIRST;
+ cc_last = ICC_LAST;
+ }
+ else if (mode == CC_FPmode)
+ {
+ cr_class = FCR_REGS;
+ cc_class = FCC_REGS;
+ cc_first = FCC_FIRST;
+ cc_last = FCC_LAST;
+ }
+ else
+ {
+ cc_first = cc_last = 0;
+ cr_class = cc_class = NO_REGS;
}
- if (GET_CODE (op) != MEM)
- return FALSE;
-
- addr = XEXP (op, 0);
- return frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE);
-}
+ cc = XEXP (true_expr, 0);
+ nested_cc = cr = NULL_RTX;
+ if (cc_class != NO_REGS)
+ {
+ /* For nested IFs and &&/||, see if we can find a CC and CR register pair
+ so we can execute a csubcc/caddcc/cfcmps instruction. */
+ int cc_regno;
-/* 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. */
+ for (cc_regno = cc_first; cc_regno <= cc_last; cc_regno++)
+ {
+ int cr_regno = cc_regno - CC_FIRST + CR_FIRST;
-int
-intop_compare_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ if (TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cc_regno)
+ && TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cr_regno))
+ {
+ frv_ifcvt.tmp_reg.next_reg[ (int)cr_class ] = cr_regno;
+ cr = frv_alloc_temp_reg (tmp_reg, cr_class, CC_CCRmode, TRUE,
+ TRUE);
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ frv_ifcvt.tmp_reg.next_reg[ (int)cc_class ] = cc_regno;
+ nested_cc = frv_alloc_temp_reg (tmp_reg, cc_class, CCmode,
+ TRUE, TRUE);
+ break;
+ }
+ }
+ }
- switch (GET_CODE (op))
+ if (! cr)
{
- default:
- return FALSE;
+ if (dump_file)
+ fprintf (dump_file, "Could not allocate a CR temporary register\n");
- case AND:
- case IOR:
- case XOR:
- case ASHIFTRT:
- case LSHIFTRT:
- break;
+ goto fail;
}
- if (! integer_register_operand (XEXP (op, 0), SImode))
- return FALSE;
-
- if (! gpr_or_int10_operand (XEXP (op, 1), SImode))
- return FALSE;
-
- return TRUE;
-}
+ 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>");
-/* Return true if operator is an integer binary operator that can be combined
- with a setcc operation inside of a conditional execution. */
+ /* Set the CCR bit. Note for integer tests, we reverse the condition so that
+ in an IF-THEN-ELSE sequence, we are testing the TRUE case against the CCR
+ bit being true. We don't do this for floating point, because of NaNs. */
+ code = GET_CODE (true_expr);
+ if (GET_MODE (cc) != CC_FPmode)
+ {
+ code = reverse_condition (code);
+ code_true = EQ;
+ code_false = NE;
+ }
+ else
+ {
+ code_true = NE;
+ code_false = EQ;
+ }
-int
-condexec_intop_cmp_operator (rtx op, enum machine_mode mode)
-{
- enum machine_mode op_mode = GET_MODE (op);
+ check_insn = gen_rtx_SET (VOIDmode, cr,
+ gen_rtx_fmt_ee (code, CC_CCRmode, cc, const0_rtx));
- if (mode != VOIDmode && op_mode != mode)
- return FALSE;
+ /* Record the check insn to be inserted later. */
+ frv_ifcvt_add_insn (check_insn, BB_END (test_bb), TRUE);
- switch (GET_CODE (op))
- {
- default:
- return FALSE;
+ /* Update the tests. */
+ frv_ifcvt.cr_reg = cr;
+ frv_ifcvt.nested_cc_reg = nested_cc;
+ *p_true = gen_rtx_fmt_ee (code_true, CC_CCRmode, cr, const0_rtx);
+ *p_false = gen_rtx_fmt_ee (code_false, CC_CCRmode, cr, const0_rtx);
+ return;
- case AND:
- case IOR:
- case XOR:
- case ASHIFTRT:
- case LSHIFTRT:
- break;
- }
+ /* Fail, don't do this conditional execution. */
+ fail:
+ *p_true = NULL_RTX;
+ *p_false = NULL_RTX;
+ if (dump_file)
+ fprintf (dump_file, "Disabling this conditional execution.\n");
- if (! integer_register_operand (XEXP (op, 0), SImode))
- return FALSE;
+ return;
+}
- if (! integer_register_operand (XEXP (op, 1), SImode))
- return FALSE;
+\f
+/* A C expression to modify the code described by the conditional if
+ information CE_INFO, for the basic block BB, possibly updating the tests in
+ TRUE_EXPR, and FALSE_EXPR for converting the && and || parts of if-then or
+ if-then-else code to conditional instructions. Set either TRUE_EXPR or
+ FALSE_EXPR to a null pointer if the tests cannot be converted. */
- return TRUE;
-}
+/* p_true and p_false are given expressions of the form:
-/* Return 1 if operand is a valid ACC register number. */
+ (and (eq:CC_CCR (reg:CC_CCR)
+ (const_int 0))
+ (eq:CC (reg:CC)
+ (const_int 0))) */
-int
-acc_operand (rtx op, enum machine_mode mode)
+void
+frv_ifcvt_modify_multiple_tests (ce_if_block_t *ce_info,
+ basic_block bb,
+ rtx *p_true,
+ rtx *p_false)
{
- int regno;
-
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ rtx old_true = XEXP (*p_true, 0);
+ rtx old_false = XEXP (*p_false, 0);
+ rtx true_expr = XEXP (*p_true, 1);
+ rtx false_expr = XEXP (*p_false, 1);
+ rtx test_expr;
+ rtx old_test;
+ rtx cr = XEXP (old_true, 0);
+ rtx check_insn;
+ rtx new_cr = NULL_RTX;
+ rtx *p_new_cr = (rtx *)0;
+ rtx if_else;
+ rtx compare;
+ rtx cc;
+ enum reg_class cr_class;
+ enum machine_mode mode = GET_MODE (true_expr);
+ rtx (*logical_func)(rtx, rtx, rtx);
- if (GET_CODE (op) == SUBREG)
+ if (TARGET_DEBUG_COND_EXEC)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
-
- if (GET_CODE (op) != REG)
- return FALSE;
+ fprintf (stderr,
+ "\n:::::::::: frv_ifcvt_modify_multiple_tests, before modification for %s\ntrue insn:\n",
+ ce_info->and_and_p ? "&&" : "||");
- regno = REGNO (op);
- return ACC_OR_PSEUDO_P (regno);
-}
+ debug_rtx (*p_true);
-/* Return 1 if operand is a valid even ACC register number. */
+ fputs ("\nfalse insn:\n", stderr);
+ debug_rtx (*p_false);
+ }
-int
-even_acc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ if (!TARGET_MULTI_CE)
+ goto fail;
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ if (GET_CODE (cr) != REG)
+ goto fail;
- if (GET_CODE (op) == SUBREG)
+ if (mode == CCmode || mode == CC_UNSmode || mode == CC_NZmode)
+ {
+ cr_class = ICR_REGS;
+ p_new_cr = &frv_ifcvt.extra_int_cr;
+ }
+ else if (mode == CC_FPmode)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
+ cr_class = FCR_REGS;
+ p_new_cr = &frv_ifcvt.extra_fp_cr;
+ }
+ else
+ goto fail;
- op = SUBREG_REG (op);
+ /* Allocate a temp CR, reusing a previously allocated temp CR if we have 3 or
+ more &&/|| tests. */
+ new_cr = *p_new_cr;
+ if (! new_cr)
+ {
+ new_cr = *p_new_cr = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, cr_class,
+ CC_CCRmode, TRUE, TRUE);
+ if (! new_cr)
+ goto fail;
}
- if (GET_CODE (op) != REG)
- return FALSE;
+ if (ce_info->and_and_p)
+ {
+ old_test = old_false;
+ test_expr = true_expr;
+ logical_func = (GET_CODE (old_true) == EQ) ? gen_andcr : gen_andncr;
+ *p_true = gen_rtx_NE (CC_CCRmode, cr, const0_rtx);
+ *p_false = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx);
+ }
+ else
+ {
+ old_test = old_false;
+ test_expr = false_expr;
+ logical_func = (GET_CODE (old_false) == EQ) ? gen_orcr : gen_orncr;
+ *p_true = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx);
+ *p_false = gen_rtx_NE (CC_CCRmode, cr, const0_rtx);
+ }
- regno = REGNO (op);
- return (ACC_OR_PSEUDO_P (regno) && ((regno - ACC_FIRST) & 1) == 0);
-}
+ /* 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 (bb), TRUE);
-/* Return 1 if operand is zero or four. */
+ /* Now add the conditional check insn. */
+ cc = XEXP (test_expr, 0);
+ compare = gen_rtx_fmt_ee (GET_CODE (test_expr), CC_CCRmode, cc, const0_rtx);
+ if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, old_test, compare, const0_rtx);
-int
-quad_acc_operand (rtx op, enum machine_mode mode)
-{
- int regno;
+ check_insn = gen_rtx_SET (VOIDmode, new_cr, if_else);
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ /* Add the new check insn to the list of check insns that need to be
+ inserted. */
+ frv_ifcvt_add_insn (check_insn, BB_END (bb), TRUE);
- if (GET_CODE (op) == SUBREG)
+ if (TARGET_DEBUG_COND_EXEC)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
- }
+ fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, after modification\ntrue insn:\n",
+ stderr);
- if (GET_CODE (op) != REG)
- return FALSE;
+ debug_rtx (*p_true);
- regno = REGNO (op);
- return (ACC_OR_PSEUDO_P (regno) && ((regno - ACC_FIRST) & 3) == 0);
-}
+ fputs ("\nfalse insn:\n", stderr);
+ debug_rtx (*p_false);
+ }
-/* Return 1 if operand is a valid ACCG register number. */
+ return;
-int
-accg_operand (rtx op, enum machine_mode mode)
-{
- if (GET_MODE (op) != mode && mode != VOIDmode)
- return FALSE;
+ fail:
+ *p_true = *p_false = NULL_RTX;
- if (GET_CODE (op) == SUBREG)
+ /* If we allocated a CR register, release it. */
+ if (new_cr)
{
- if (GET_CODE (SUBREG_REG (op)) != REG)
- return register_operand (op, mode);
-
- op = SUBREG_REG (op);
+ CLEAR_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, REGNO (new_cr));
+ *p_new_cr = NULL_RTX;
}
- if (GET_CODE (op) != REG)
- return FALSE;
+ if (TARGET_DEBUG_COND_EXEC)
+ fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, failed.\n", stderr);
- return ACCG_OR_PSEUDO_P (REGNO (op));
+ return;
}
\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 a register which will be loaded with a value if an IF block is
+ converted to conditional execution. This is used to rewrite instructions
+ that use constants to ones that just use registers. */
-int
-direct_return_p (void)
+static rtx
+frv_ifcvt_load_value (rtx value, rtx insn ATTRIBUTE_UNUSED)
{
- 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 num_alloc = frv_ifcvt.cur_scratch_regs;
+ int i;
+ rtx reg;
-int
-frv_emit_movsi (rtx dest, rtx src)
-{
- int base_regno = -1;
- int unspec = 0;
- rtx sym = src;
- struct frv_unspec old_unspec;
+ /* We know gr0 == 0, so replace any errant uses. */
+ if (value == const0_rtx)
+ return gen_rtx_REG (SImode, GPR_FIRST);
- if (!reload_in_progress
- && !reload_completed
- && !register_operand (dest, SImode)
- && (!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. */
- || (GET_CODE (src) == REG
- && IN_RANGE_P (REGNO (src),
- FIRST_VIRTUAL_REGISTER,
- LAST_VIRTUAL_REGISTER))))
+ /* First search all registers currently loaded to see if we have an
+ applicable constant. */
+ if (CONSTANT_P (value)
+ || (GET_CODE (value) == REG && REGNO (value) == LR_REGNO))
{
- emit_insn (gen_rtx_SET (VOIDmode, dest, copy_to_mode_reg (SImode, src)));
- return TRUE;
+ for (i = 0; i < num_alloc; i++)
+ {
+ if (rtx_equal_p (SET_SRC (frv_ifcvt.scratch_regs[i]), value))
+ return SET_DEST (frv_ifcvt.scratch_regs[i]);
+ }
}
- /* Explicitly add in the PIC or small data register if needed. */
- switch (GET_CODE (src))
+ /* Have we exhausted the number of registers available? */
+ if (num_alloc >= GPR_TEMP_NUM)
{
- default:
- break;
-
- case LABEL_REF:
- 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 (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:
- handle_sym:
- if (TARGET_FDPIC)
- {
- 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)
- base_regno = PIC_REGNO;
-
- break;
- }
-
- if (base_regno >= 0)
- {
- 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. */
- if (reload_in_progress || reload_completed)
- abort ();
-
- 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;
-}
-
-\f
-/* Return a string to output a single word move. */
-
-const char *
-output_move_single (rtx operands[], rtx insn)
-{
- rtx dest = operands[0];
- rtx src = operands[1];
-
- if (GET_CODE (dest) == REG)
- {
- int dest_regno = REGNO (dest);
- enum machine_mode mode = GET_MODE (dest);
-
- if (GPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* gpr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- return "mov %1, %0";
-
- else if (FPR_P (src_regno))
- return "movfg %1, %0";
-
- else if (SPR_P (src_regno))
- return "movsg %1, %0";
- }
-
- else if (GET_CODE (src) == MEM)
- {
- /* gpr <- memory */
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "ldsb%I1%U1 %M1,%0";
-
- case HImode:
- return "ldsh%I1%U1 %M1,%0";
-
- case SImode:
- case SFmode:
- return "ld%I1%U1 %M1, %0";
- }
- }
-
- else if (GET_CODE (src) == CONST_INT
- || GET_CODE (src) == CONST_DOUBLE)
- {
- /* gpr <- integer/floating constant */
- HOST_WIDE_INT value;
-
- if (GET_CODE (src) == CONST_INT)
- value = INTVAL (src);
-
- else if (mode == SFmode)
- {
- REAL_VALUE_TYPE rv;
- long l;
-
- REAL_VALUE_FROM_CONST_DOUBLE (rv, src);
- REAL_VALUE_TO_TARGET_SINGLE (rv, l);
- value = l;
- }
-
- else
- value = CONST_DOUBLE_LOW (src);
-
- if (IN_RANGE_P (value, -32768, 32767))
- return "setlos %1, %0";
-
- return "#";
- }
-
- else if (GET_CODE (src) == SYMBOL_REF
- || GET_CODE (src) == LABEL_REF
- || GET_CODE (src) == CONST)
- {
- return "#";
- }
- }
-
- else if (FPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* fpr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- return "movgf %1, %0";
-
- else if (FPR_P (src_regno))
- {
- if (TARGET_HARD_FLOAT)
- return "fmovs %1, %0";
- else
- return "mor %1, %1, %0";
- }
- }
-
- else if (GET_CODE (src) == MEM)
- {
- /* fpr <- memory */
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "ldbf%I1%U1 %M1,%0";
-
- case HImode:
- return "ldhf%I1%U1 %M1,%0";
-
- case SImode:
- case SFmode:
- return "ldf%I1%U1 %M1, %0";
- }
- }
-
- else if (ZERO_P (src))
- return "movgf %., %0";
- }
-
- else if (SPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* spr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- return "movgs %1, %0";
- }
- }
- }
-
- else if (GET_CODE (dest) == MEM)
- {
- if (GET_CODE (src) == REG)
- {
- int src_regno = REGNO (src);
- enum machine_mode mode = GET_MODE (dest);
-
- if (GPR_P (src_regno))
- {
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "stb%I0%U0 %1, %M0";
-
- case HImode:
- return "sth%I0%U0 %1, %M0";
-
- case SImode:
- case SFmode:
- return "st%I0%U0 %1, %M0";
- }
- }
-
- else if (FPR_P (src_regno))
- {
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "stbf%I0%U0 %1, %M0";
-
- case HImode:
- return "sthf%I0%U0 %1, %M0";
-
- case SImode:
- case SFmode:
- return "stf%I0%U0 %1, %M0";
- }
- }
- }
-
- else if (ZERO_P (src))
- {
- switch (GET_MODE (dest))
- {
- default:
- break;
-
- case QImode:
- return "stb%I0%U0 %., %M0";
-
- case HImode:
- return "sth%I0%U0 %., %M0";
-
- case SImode:
- case SFmode:
- return "st%I0%U0 %., %M0";
- }
- }
- }
-
- fatal_insn ("Bad output_move_single operand", insn);
- return "";
-}
-
-\f
-/* Return a string to output a double word move. */
-
-const char *
-output_move_double (rtx operands[], rtx insn)
-{
- rtx dest = operands[0];
- rtx src = operands[1];
- enum machine_mode mode = GET_MODE (dest);
-
- if (GET_CODE (dest) == REG)
- {
- int dest_regno = REGNO (dest);
-
- if (GPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* gpr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- return "#";
-
- else if (FPR_P (src_regno))
- {
- if (((dest_regno - GPR_FIRST) & 1) == 0
- && ((src_regno - FPR_FIRST) & 1) == 0)
- return "movfgd %1, %0";
-
- return "#";
- }
- }
-
- else if (GET_CODE (src) == MEM)
- {
- /* gpr <- memory */
- if (dbl_memory_one_insn_operand (src, mode))
- return "ldd%I1%U1 %M1, %0";
-
- return "#";
- }
-
- else if (GET_CODE (src) == CONST_INT
- || GET_CODE (src) == CONST_DOUBLE)
- return "#";
- }
-
- else if (FPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* fpr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- {
- if (((dest_regno - FPR_FIRST) & 1) == 0
- && ((src_regno - GPR_FIRST) & 1) == 0)
- return "movgfd %1, %0";
-
- return "#";
- }
-
- else if (FPR_P (src_regno))
- {
- if (TARGET_DOUBLE
- && ((dest_regno - FPR_FIRST) & 1) == 0
- && ((src_regno - FPR_FIRST) & 1) == 0)
- return "fmovd %1, %0";
-
- return "#";
- }
- }
-
- else if (GET_CODE (src) == MEM)
- {
- /* fpr <- memory */
- if (dbl_memory_one_insn_operand (src, mode))
- return "lddf%I1%U1 %M1, %0";
-
- return "#";
- }
-
- else if (ZERO_P (src))
- return "#";
- }
- }
-
- else if (GET_CODE (dest) == MEM)
- {
- if (GET_CODE (src) == REG)
- {
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- {
- if (((src_regno - GPR_FIRST) & 1) == 0
- && dbl_memory_one_insn_operand (dest, mode))
- return "std%I0%U0 %1, %M0";
-
- return "#";
- }
-
- if (FPR_P (src_regno))
- {
- if (((src_regno - FPR_FIRST) & 1) == 0
- && dbl_memory_one_insn_operand (dest, mode))
- return "stdf%I0%U0 %1, %M0";
-
- return "#";
- }
- }
-
- else if (ZERO_P (src))
- {
- if (dbl_memory_one_insn_operand (dest, mode))
- return "std%I0%U0 %., %M0";
-
- return "#";
- }
- }
-
- fatal_insn ("Bad output_move_double operand", insn);
- return "";
-}
-
-\f
-/* Return a string to output a single word conditional move.
- Operand0 -- EQ/NE of ccr register and 0
- Operand1 -- CCR register
- Operand2 -- destination
- Operand3 -- source */
-
-const char *
-output_condmove_single (rtx operands[], rtx insn)
-{
- rtx dest = operands[2];
- rtx src = operands[3];
-
- if (GET_CODE (dest) == REG)
- {
- int dest_regno = REGNO (dest);
- enum machine_mode mode = GET_MODE (dest);
-
- if (GPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* gpr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- return "cmov %z3, %2, %1, %e0";
-
- else if (FPR_P (src_regno))
- return "cmovfg %3, %2, %1, %e0";
- }
-
- else if (GET_CODE (src) == MEM)
- {
- /* gpr <- memory */
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "cldsb%I3%U3 %M3, %2, %1, %e0";
-
- case HImode:
- return "cldsh%I3%U3 %M3, %2, %1, %e0";
-
- case SImode:
- case SFmode:
- return "cld%I3%U3 %M3, %2, %1, %e0";
- }
- }
-
- else if (ZERO_P (src))
- return "cmov %., %2, %1, %e0";
- }
-
- else if (FPR_P (dest_regno))
- {
- if (GET_CODE (src) == REG)
- {
- /* fpr <- some sort of register */
- int src_regno = REGNO (src);
-
- if (GPR_P (src_regno))
- return "cmovgf %3, %2, %1, %e0";
-
- else if (FPR_P (src_regno))
- {
- if (TARGET_HARD_FLOAT)
- return "cfmovs %3,%2,%1,%e0";
- else
- return "cmor %3, %3, %2, %1, %e0";
- }
- }
-
- else if (GET_CODE (src) == MEM)
- {
- /* fpr <- memory */
- if (mode == SImode || mode == SFmode)
- return "cldf%I3%U3 %M3, %2, %1, %e0";
- }
-
- else if (ZERO_P (src))
- return "cmovgf %., %2, %1, %e0";
- }
- }
-
- else if (GET_CODE (dest) == MEM)
- {
- if (GET_CODE (src) == REG)
- {
- int src_regno = REGNO (src);
- enum machine_mode mode = GET_MODE (dest);
-
- if (GPR_P (src_regno))
- {
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "cstb%I2%U2 %3, %M2, %1, %e0";
-
- case HImode:
- return "csth%I2%U2 %3, %M2, %1, %e0";
-
- case SImode:
- case SFmode:
- return "cst%I2%U2 %3, %M2, %1, %e0";
- }
- }
-
- else if (FPR_P (src_regno) && (mode == SImode || mode == SFmode))
- return "cstf%I2%U2 %3, %M2, %1, %e0";
- }
-
- else if (ZERO_P (src))
- {
- enum machine_mode mode = GET_MODE (dest);
- switch (mode)
- {
- default:
- break;
-
- case QImode:
- return "cstb%I2%U2 %., %M2, %1, %e0";
-
- case HImode:
- return "csth%I2%U2 %., %M2, %1, %e0";
-
- case SImode:
- case SFmode:
- return "cst%I2%U2 %., %M2, %1, %e0";
- }
- }
- }
-
- fatal_insn ("Bad output_condmove_single operand", insn);
- return "";
-}
-
-\f
-/* Emit the appropriate code to do a comparison, returning the register the
- comparison was done it. */
-
-static rtx
-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. */
- if (GET_MODE (op0) == CC_FPmode && GET_CODE (op1) != REG)
- op1 = force_reg (GET_MODE (op0), op1);
-
- /* Possibly disable using anything but a fixed register in order to work
- around cse moving comparisons past function calls. */
- cc_mode = SELECT_CC_MODE (test, op0, op1);
- cc_reg = ((TARGET_ALLOC_CC)
- ? gen_reg_rtx (cc_mode)
- : gen_rtx_REG (cc_mode,
- (cc_mode == CC_FPmode) ? FCC_FIRST : ICC_FIRST));
-
- emit_insn (gen_rtx_SET (VOIDmode, cc_reg,
- gen_rtx_COMPARE (cc_mode, op0, op1)));
-
- return cc_reg;
-}
-
-\f
-/* Emit code for a conditional branch. The comparison operands were previously
- stored in frv_compare_op0 and frv_compare_op1.
-
- XXX: I originally wanted to add a clobber of a CCR register to use in
- conditional execution, but that confuses the rest of the compiler. */
-
-int
-frv_emit_cond_branch (enum rtx_code test, rtx label)
-{
- rtx test_rtx;
- rtx label_ref;
- rtx if_else;
- rtx cc_reg = frv_emit_comparison (test, frv_compare_op0, frv_compare_op1);
- enum machine_mode cc_mode = GET_MODE (cc_reg);
-
- /* Branches generate:
- (set (pc)
- (if_then_else (<test>, <cc_reg>, (const_int 0))
- (label_ref <branch_label>)
- (pc))) */
- label_ref = gen_rtx_LABEL_REF (VOIDmode, label);
- 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;
-}
-
-\f
-/* Emit code to set a gpr to 1/0 based on a comparison. The comparison
- operands were previously stored in frv_compare_op0 and frv_compare_op1. */
-
-int
-frv_emit_scc (enum rtx_code test, rtx target)
-{
- rtx set;
- rtx test_rtx;
- rtx clobber;
- rtx cr_reg;
- rtx cc_reg = frv_emit_comparison (test, frv_compare_op0, frv_compare_op1);
-
- /* SCC instructions generate:
- (parallel [(set <target> (<test>, <cc_reg>, (const_int 0))
- (clobber (<ccr_reg>))]) */
- test_rtx = gen_rtx_fmt_ee (test, SImode, cc_reg, const0_rtx);
- set = gen_rtx_SET (VOIDmode, target, test_rtx);
-
- cr_reg = ((TARGET_ALLOC_CC)
- ? gen_reg_rtx (CC_CCRmode)
- : gen_rtx_REG (CC_CCRmode,
- ((GET_MODE (cc_reg) == CC_FPmode)
- ? FCR_FIRST
- : ICR_FIRST)));
-
- clobber = gen_rtx_CLOBBER (VOIDmode, cr_reg);
- emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
- return TRUE;
-}
-
-\f
-/* Split a SCC instruction into component parts, returning a SEQUENCE to hold
- the separate insns. */
-
-rtx
-frv_split_scc (rtx dest, rtx test, rtx cc_reg, rtx cr_reg, HOST_WIDE_INT value)
-{
- rtx ret;
-
- start_sequence ();
-
- /* Set the appropriate CCR bit. */
- emit_insn (gen_rtx_SET (VOIDmode,
- cr_reg,
- gen_rtx_fmt_ee (GET_CODE (test),
- GET_MODE (cr_reg),
- cc_reg,
- const0_rtx)));
-
- /* Move the value into the destination. */
- emit_move_insn (dest, GEN_INT (value));
-
- /* Move 0 into the destination if the test failed */
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_EQ (GET_MODE (cr_reg),
- cr_reg,
- const0_rtx),
- gen_rtx_SET (VOIDmode, dest, const0_rtx)));
-
- /* Finish up, return sequence. */
- ret = get_insns ();
- end_sequence ();
- return ret;
-}
-
-\f
-/* Emit the code for a conditional move, return TRUE if we could do the
- move. */
-
-int
-frv_emit_cond_move (rtx dest, rtx test_rtx, rtx src1, rtx src2)
-{
- rtx set;
- rtx clobber_cc;
- rtx test2;
- rtx cr_reg;
- rtx if_rtx;
- enum rtx_code test = GET_CODE (test_rtx);
- rtx cc_reg = frv_emit_comparison (test, frv_compare_op0, frv_compare_op1);
- enum machine_mode cc_mode = GET_MODE (cc_reg);
-
- /* Conditional move instructions generate:
- (parallel [(set <target>
- (if_then_else (<test> <cc_reg> (const_int 0))
- <src1>
- <src2>))
- (clobber (<ccr_reg>))]) */
-
- /* Handle various cases of conditional move involving two constants. */
- if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT)
- {
- 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
- constant, and optionally moving in gr0. */
- if (value1 == 0 || value2 == 0)
- ;
-
- /* If the first value is within an addi range and also the difference
- between the two fits in an addi's range, load up the difference, then
- conditionally move in 0, and then unconditionally add the first
- value. */
- else if (IN_RANGE_P (value1, -2048, 2047)
- && IN_RANGE_P (value2 - value1, -2048, 2047))
- ;
-
- /* If neither condition holds, just force the constant into a
- register. */
- else
- {
- src1 = force_reg (GET_MODE (dest), src1);
- src2 = force_reg (GET_MODE (dest), src2);
- }
- }
-
- /* If one value is a register, insure the other value is either 0 or a
- register. */
- else
- {
- if (GET_CODE (src1) == CONST_INT && INTVAL (src1) != 0)
- src1 = force_reg (GET_MODE (dest), src1);
-
- if (GET_CODE (src2) == CONST_INT && INTVAL (src2) != 0)
- src2 = force_reg (GET_MODE (dest), src2);
- }
-
- test2 = gen_rtx_fmt_ee (test, cc_mode, cc_reg, const0_rtx);
- if_rtx = gen_rtx_IF_THEN_ELSE (GET_MODE (dest), test2, src1, src2);
-
- set = gen_rtx_SET (VOIDmode, dest, if_rtx);
-
- cr_reg = ((TARGET_ALLOC_CC)
- ? gen_reg_rtx (CC_CCRmode)
- : gen_rtx_REG (CC_CCRmode,
- (cc_mode == CC_FPmode) ? FCR_FIRST : ICR_FIRST));
-
- clobber_cc = gen_rtx_CLOBBER (VOIDmode, cr_reg);
- emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber_cc)));
- return TRUE;
-}
-
-\f
-/* Split a conditional move into constituent parts, returning a SEQUENCE
- containing all of the insns. */
-
-rtx
-frv_split_cond_move (rtx operands[])
-{
- rtx dest = operands[0];
- rtx test = operands[1];
- rtx cc_reg = operands[2];
- rtx src1 = operands[3];
- rtx src2 = operands[4];
- rtx cr_reg = operands[5];
- rtx ret;
- enum machine_mode cr_mode = GET_MODE (cr_reg);
-
- start_sequence ();
-
- /* Set the appropriate CCR bit. */
- emit_insn (gen_rtx_SET (VOIDmode,
- cr_reg,
- gen_rtx_fmt_ee (GET_CODE (test),
- GET_MODE (cr_reg),
- cc_reg,
- const0_rtx)));
-
- /* Handle various cases of conditional move involving two constants. */
- if (GET_CODE (src1) == CONST_INT && GET_CODE (src2) == CONST_INT)
- {
- 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
- constant, and optionally moving in gr0. */
- if (value1 == 0)
- {
- emit_move_insn (dest, src2);
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_NE (cr_mode, cr_reg,
- const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src1)));
- }
-
- else if (value2 == 0)
- {
- emit_move_insn (dest, src1);
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_EQ (cr_mode, cr_reg,
- const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src2)));
- }
-
- /* If the first value is within an addi range and also the difference
- between the two fits in an addi's range, load up the difference, then
- conditionally move in 0, and then unconditionally add the first
- value. */
- else if (IN_RANGE_P (value1, -2048, 2047)
- && IN_RANGE_P (value2 - value1, -2048, 2047))
- {
- rtx dest_si = ((GET_MODE (dest) == SImode)
- ? dest
- : gen_rtx_SUBREG (SImode, dest, 0));
-
- emit_move_insn (dest_si, GEN_INT (value2 - value1));
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_NE (cr_mode, cr_reg,
- const0_rtx),
- gen_rtx_SET (VOIDmode, dest_si,
- const0_rtx)));
- emit_insn (gen_addsi3 (dest_si, dest_si, src1));
- }
-
- else
- abort ();
- }
- else
- {
- /* Emit the conditional move for the test being true if needed. */
- if (! rtx_equal_p (dest, src1))
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src1)));
-
- /* Emit the conditional move for the test being false if needed. */
- if (! rtx_equal_p (dest, src2))
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_EQ (cr_mode, cr_reg, const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src2)));
- }
-
- /* Finish up, return sequence. */
- ret = get_insns ();
- end_sequence ();
- return ret;
-}
-
-\f
-/* 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 (rtx dest, rtx source)
-{
- int regno = REGNO (dest);
- rtx dest1 = gen_highpart (SImode, dest);
- rtx dest2 = gen_lowpart (SImode, dest);
- rtx address = XEXP (source, 0);
-
- /* If the address is pre-modified, load the lower-numbered register
- first, then load the other register using an integer offset from
- the modified base register. This order should always be safe,
- since the pre-modification cannot affect the same registers as the
- load does.
-
- The situation for other loads is more complicated. Loading one
- of the registers could affect the value of ADDRESS, so we must
- be careful which order we do them in. */
- if (GET_CODE (address) == PRE_MODIFY
- || ! refers_to_regno_p (regno, regno + 1, address, NULL))
- {
- /* It is safe to load the lower-numbered register first. */
- emit_move_insn (dest1, change_address (source, SImode, NULL));
- emit_move_insn (dest2, frv_index_memory (source, SImode, 1));
- }
- else
- {
- /* ADDRESS is not pre-modified and the address depends on the
- lower-numbered register. Load the higher-numbered register
- first. */
- emit_move_insn (dest2, frv_index_memory (source, SImode, 1));
- emit_move_insn (dest1, change_address (source, SImode, NULL));
- }
-}
-
-/* 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 (rtx dest, rtx source)
-{
- rtx dest1 = change_address (dest, SImode, NULL);
- rtx dest2 = frv_index_memory (dest, SImode, 1);
- if (ZERO_P (source))
- {
- emit_move_insn (dest1, CONST0_RTX (SImode));
- emit_move_insn (dest2, CONST0_RTX (SImode));
- }
- else
- {
- emit_move_insn (dest1, gen_highpart (SImode, source));
- emit_move_insn (dest2, gen_lowpart (SImode, source));
- }
-}
-
-\f
-/* Split a min/max operation returning a SEQUENCE containing all of the
- insns. */
-
-rtx
-frv_split_minmax (rtx operands[])
-{
- rtx dest = operands[0];
- rtx minmax = operands[1];
- rtx src1 = operands[2];
- rtx src2 = operands[3];
- rtx cc_reg = operands[4];
- rtx cr_reg = operands[5];
- rtx ret;
- enum rtx_code test_code;
- enum machine_mode cr_mode = GET_MODE (cr_reg);
-
- start_sequence ();
-
- /* Figure out which test to use. */
- switch (GET_CODE (minmax))
- {
- default:
- abort ();
-
- case SMIN: test_code = LT; break;
- case SMAX: test_code = GT; break;
- case UMIN: test_code = LTU; break;
- case UMAX: test_code = GTU; break;
- }
-
- /* Issue the compare instruction. */
- emit_insn (gen_rtx_SET (VOIDmode,
- cc_reg,
- gen_rtx_COMPARE (GET_MODE (cc_reg),
- src1, src2)));
-
- /* Set the appropriate CCR bit. */
- emit_insn (gen_rtx_SET (VOIDmode,
- cr_reg,
- gen_rtx_fmt_ee (test_code,
- GET_MODE (cr_reg),
- cc_reg,
- const0_rtx)));
-
- /* If are taking the min/max of a nonzero constant, load that first, and
- 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 ();
-
- emit_move_insn (dest, src2);
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src1)));
- }
-
- /* Otherwise, do each half of the move. */
- else
- {
- /* Emit the conditional move for the test being true if needed. */
- if (! rtx_equal_p (dest, src1))
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_NE (cr_mode, cr_reg, const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src1)));
-
- /* Emit the conditional move for the test being false if needed. */
- if (! rtx_equal_p (dest, src2))
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_EQ (cr_mode, cr_reg, const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src2)));
- }
-
- /* Finish up, return sequence. */
- ret = get_insns ();
- end_sequence ();
- return ret;
-}
+ if (dump_file)
+ fprintf (dump_file, "Too many temporary registers allocated\n");
-\f
-/* Split an integer abs operation returning a SEQUENCE containing all of the
- insns. */
+ return NULL_RTX;
+ }
-rtx
-frv_split_abs (rtx operands[])
-{
- rtx dest = operands[0];
- rtx src = operands[1];
- rtx cc_reg = operands[2];
- rtx cr_reg = operands[3];
- rtx ret;
+ /* Allocate the new register. */
+ reg = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, GPR_REGS, SImode, TRUE, TRUE);
+ if (! reg)
+ {
+ if (dump_file)
+ fputs ("Could not find a scratch register\n", dump_file);
- start_sequence ();
+ return NULL_RTX;
+ }
- /* Issue the compare < 0 instruction. */
- emit_insn (gen_rtx_SET (VOIDmode,
- cc_reg,
- gen_rtx_COMPARE (CCmode, src, const0_rtx)));
+ frv_ifcvt.cur_scratch_regs++;
+ frv_ifcvt.scratch_regs[num_alloc] = gen_rtx_SET (VOIDmode, reg, value);
- /* Set the appropriate CCR bit. */
- emit_insn (gen_rtx_SET (VOIDmode,
- cr_reg,
- gen_rtx_fmt_ee (LT, CC_CCRmode, cc_reg, const0_rtx)));
+ if (dump_file)
+ {
+ if (GET_CODE (value) == CONST_INT)
+ fprintf (dump_file, "Register %s will hold %ld\n",
+ reg_names[ REGNO (reg)], (long)INTVAL (value));
- /* 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)));
+ else if (GET_CODE (value) == REG && REGNO (value) == LR_REGNO)
+ fprintf (dump_file, "Register %s will hold LR\n",
+ reg_names[ REGNO (reg)]);
- /* Emit the conditional move for the test being false if needed. */
- if (! rtx_equal_p (dest, src))
- emit_insn (gen_rtx_COND_EXEC (VOIDmode,
- gen_rtx_EQ (CC_CCRmode, cr_reg, const0_rtx),
- gen_rtx_SET (VOIDmode, dest, src)));
+ else
+ fprintf (dump_file, "Register %s will hold a saved value\n",
+ reg_names[ REGNO (reg)]);
+ }
- /* Finish up, return sequence. */
- ret = get_insns ();
- end_sequence ();
- return ret;
+ return reg;
}
\f
-/* An internal function called by for_each_rtx to clear in a hard_reg set each
- register used in an insn. */
+/* Update a MEM used in conditional code that might contain an offset to put
+ the offset into a scratch register, so that the conditional load/store
+ operations can be used. This function returns the original pointer if the
+ MEM is valid to use in conditional code, NULL if we can't load up the offset
+ into a temporary register, or the new MEM if we were successful. */
-static int
-frv_clear_registers_used (rtx *ptr, void *data)
+static rtx
+frv_ifcvt_rewrite_mem (rtx mem, enum machine_mode mode, rtx insn)
{
- if (GET_CODE (*ptr) == REG)
- {
- int regno = REGNO (*ptr);
- HARD_REG_SET *p_regs = (HARD_REG_SET *)data;
+ rtx addr = XEXP (mem, 0);
- if (regno < FIRST_PSEUDO_REGISTER)
+ if (!frv_legitimate_address_p_1 (mode, addr, reload_completed, TRUE, FALSE))
+ {
+ if (GET_CODE (addr) == PLUS)
{
- int reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (*ptr));
+ rtx addr_op0 = XEXP (addr, 0);
+ rtx addr_op1 = XEXP (addr, 1);
- while (regno < reg_max)
+ if (GET_CODE (addr_op0) == REG && CONSTANT_P (addr_op1))
{
- CLEAR_HARD_REG_BIT (*p_regs, regno);
- regno++;
+ rtx reg = frv_ifcvt_load_value (addr_op1, insn);
+ if (!reg)
+ return NULL_RTX;
+
+ addr = gen_rtx_PLUS (Pmode, addr_op0, reg);
}
+
+ else
+ return NULL_RTX;
}
- }
- return 0;
-}
+ else if (CONSTANT_P (addr))
+ addr = frv_ifcvt_load_value (addr, insn);
-\f
-/* Initialize the extra fields provided by IFCVT_EXTRA_FIELDS. */
+ else
+ return NULL_RTX;
-/* 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_if_block_t *ce_info ATTRIBUTE_UNUSED)
-{
- frv_ifcvt.added_insns_list = NULL_RTX;
- frv_ifcvt.cur_scratch_regs = 0;
- frv_ifcvt.num_nested_cond_exec = 0;
- frv_ifcvt.cr_reg = NULL_RTX;
- frv_ifcvt.nested_cc_reg = NULL_RTX;
- frv_ifcvt.extra_int_cr = NULL_RTX;
- frv_ifcvt.extra_fp_cr = NULL_RTX;
- frv_ifcvt.last_nested_if_cr = NULL_RTX;
+ if (addr == NULL_RTX)
+ return NULL_RTX;
+
+ else if (XEXP (mem, 0) != addr)
+ return change_address (mem, mode, addr);
+ }
+
+ return mem;
}
\f
-/* Internal function to add a potential insn to the list of insns to be inserted
- if the conditional execution conversion is successful. */
+/* Given a PATTERN, return a SET expression if this PATTERN has only a single
+ SET, possibly conditionally executed. It may also have CLOBBERs, USEs. */
-static void
-frv_ifcvt_add_insn (rtx pattern, rtx insn, int before_p)
+static rtx
+single_set_pattern (rtx pattern)
{
- rtx link = alloc_EXPR_LIST (VOIDmode, pattern, insn);
+ rtx set;
+ int i;
- 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);
+ if (GET_CODE (pattern) == COND_EXEC)
+ pattern = COND_EXEC_CODE (pattern);
- if (TARGET_DEBUG_COND_EXEC)
+ if (GET_CODE (pattern) == SET)
+ return pattern;
+
+ else if (GET_CODE (pattern) == PARALLEL)
{
- fprintf (stderr,
- "\n:::::::::: frv_ifcvt_add_insn: add the following %s insn %d:\n",
- (before_p) ? "before" : "after",
- (int)INSN_UID (insn));
+ for (i = 0, set = 0; i < XVECLEN (pattern, 0); i++)
+ {
+ rtx sub = XVECEXP (pattern, 0, i);
- debug_rtx (pattern);
+ switch (GET_CODE (sub))
+ {
+ case USE:
+ case CLOBBER:
+ break;
+
+ case SET:
+ if (set)
+ return 0;
+ else
+ set = sub;
+ break;
+
+ default:
+ return 0;
+ }
+ }
+ return set;
}
+
+ return 0;
}
\f
/* A C expression to modify the code described by the conditional if
- information CE_INFO, possibly updating the tests in TRUE_EXPR, and
- FALSE_EXPR for converting if-then and if-then-else code to conditional
- instructions. Set either TRUE_EXPR or FALSE_EXPR to a null pointer if the
- tests cannot be converted. */
+ information CE_INFO with the new PATTERN in INSN. If PATTERN is a null
+ pointer after the IFCVT_MODIFY_INSN macro executes, it is assumed that that
+ insn cannot be converted to be executed conditionally. */
-void
-frv_ifcvt_modify_tests (ce_if_block_t *ce_info, rtx *p_true, rtx *p_false)
+rtx
+frv_ifcvt_modify_insn (ce_if_block_t *ce_info,
+ rtx pattern,
+ rtx insn)
{
- basic_block test_bb = ce_info->test_bb; /* test basic block */
- basic_block then_bb = ce_info->then_bb; /* THEN */
- basic_block else_bb = ce_info->else_bb; /* ELSE or NULL */
- basic_block join_bb = ce_info->join_bb; /* join block or NULL */
- rtx true_expr = *p_true;
- rtx cr;
- rtx cc;
- rtx nested_cc;
- enum machine_mode mode = GET_MODE (true_expr);
- int j;
- basic_block *bb;
- int num_bb;
- frv_tmp_reg_t *tmp_reg = &frv_ifcvt.tmp_reg;
- rtx check_insn;
- rtx sub_cond_exec_reg;
- enum rtx_code code;
- enum rtx_code code_true;
- enum rtx_code code_false;
- enum reg_class cc_class;
- enum reg_class cr_class;
- int cc_first;
- int cc_last;
-
- /* 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))
- goto fail;
+ rtx orig_ce_pattern = pattern;
+ rtx set;
+ rtx op0;
+ rtx op1;
+ rtx test;
- /* 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. */
- 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);
- SET_HARD_REG_BIT (tmp_reg->regs, ICC_TEMP);
- SET_HARD_REG_BIT (tmp_reg->regs, ICR_TEMP);
+ gcc_assert (GET_CODE (pattern) == COND_EXEC);
- /* If this is a nested IF, we need to discover whether the CC registers that
- are set/used inside of the block are used anywhere else. If not, we can
- change them to be the CC register that is paired with the CR register that
- controls the outermost IF block. */
- if (ce_info->pass > 1)
+ test = COND_EXEC_TEST (pattern);
+ if (GET_CODE (test) == AND)
{
- CLEAR_HARD_REG_SET (frv_ifcvt.nested_cc_ok_rewrite);
- 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))
- continue;
-
- if (else_bb && REGNO_REG_SET_P (else_bb->global_live_at_start, j))
- continue;
-
- if (join_bb && REGNO_REG_SET_P (join_bb->global_live_at_start, j))
- continue;
+ rtx cr = frv_ifcvt.cr_reg;
+ rtx test_reg;
- SET_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j);
- }
- }
+ op0 = XEXP (test, 0);
+ if (! rtx_equal_p (cr, XEXP (op0, 0)))
+ goto fail;
- for (j = 0; j < frv_ifcvt.cur_scratch_regs; j++)
- frv_ifcvt.scratch_regs[j] = NULL_RTX;
+ op1 = XEXP (test, 1);
+ test_reg = XEXP (op1, 0);
+ if (GET_CODE (test_reg) != REG)
+ goto fail;
- frv_ifcvt.added_insns_list = NULL_RTX;
- frv_ifcvt.cur_scratch_regs = 0;
+ /* Is this the first nested if block in this sequence? If so, generate
+ an andcr or andncr. */
+ if (! frv_ifcvt.last_nested_if_cr)
+ {
+ rtx and_op;
- bb = (basic_block *) alloca ((2 + ce_info->num_multiple_test_blocks)
- * sizeof (basic_block));
+ frv_ifcvt.last_nested_if_cr = test_reg;
+ if (GET_CODE (op0) == NE)
+ and_op = gen_andcr (test_reg, cr, test_reg);
+ else
+ and_op = gen_andncr (test_reg, cr, test_reg);
- if (join_bb)
- {
- int regno;
+ frv_ifcvt_add_insn (and_op, insn, TRUE);
+ }
- /* 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);
- });
+ /* If this isn't the first statement in the nested if sequence, see if we
+ are dealing with the same register. */
+ else if (! rtx_equal_p (test_reg, frv_ifcvt.last_nested_if_cr))
+ goto fail;
+
+ COND_EXEC_TEST (pattern) = test = op1;
}
- /* Add in all of the blocks in multiple &&/|| blocks to be scanned. */
- num_bb = 0;
- if (ce_info->num_multiple_test_blocks)
+ /* If this isn't a nested if, reset state variables. */
+ else
{
- basic_block multiple_test_bb = ce_info->last_test_bb;
-
- while (multiple_test_bb != test_bb)
- {
- bb[num_bb++] = multiple_test_bb;
- multiple_test_bb = multiple_test_bb->pred->src;
- }
+ frv_ifcvt.last_nested_if_cr = NULL_RTX;
}
- /* Add in the THEN and ELSE blocks to be scanned. */
- bb[num_bb++] = then_bb;
- if (else_bb)
- bb[num_bb++] = else_bb;
-
- sub_cond_exec_reg = NULL_RTX;
- frv_ifcvt.num_nested_cond_exec = 0;
-
- /* Scan all of the blocks for registers that must not be allocated. */
- for (j = 0; j < num_bb; j++)
+ set = single_set_pattern (pattern);
+ if (set)
{
- rtx last_insn = BB_END (bb[j]);
- rtx insn = BB_HEAD (bb[j]);
- int regno;
+ rtx dest = SET_DEST (set);
+ rtx src = SET_SRC (set);
+ enum machine_mode mode = GET_MODE (dest);
- 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_HEAD (bb[j])),
- (int) INSN_UID (BB_END (bb[j])));
+ /* Check for normal binary operators. */
+ if (mode == SImode && ARITHMETIC_P (src))
+ {
+ op0 = XEXP (src, 0);
+ op1 = XEXP (src, 1);
- /* 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);
- });
+ if (integer_register_operand (op0, SImode) && CONSTANT_P (op1))
+ {
+ op1 = frv_ifcvt_load_value (op1, insn);
+ if (op1)
+ COND_EXEC_CODE (pattern)
+ = gen_rtx_SET (VOIDmode, dest, gen_rtx_fmt_ee (GET_CODE (src),
+ GET_MODE (src),
+ op0, op1));
+ else
+ goto fail;
+ }
+ }
- /* Loop through the insns in the block. */
- for (;;)
+ /* For multiply by a constant, we need to handle the sign extending
+ correctly. Add a USE of the value after the multiply to prevent flow
+ from cratering because only one register out of the two were used. */
+ else if (mode == DImode && GET_CODE (src) == MULT)
{
- /* Mark any new registers that are created as being unavailable for
- allocation. Also see if the CC register used in nested IFs can be
- reallocated. */
- if (INSN_P (insn))
+ op0 = XEXP (src, 0);
+ op1 = XEXP (src, 1);
+ if (GET_CODE (op0) == SIGN_EXTEND && GET_CODE (op1) == CONST_INT)
{
- rtx pattern;
- rtx set;
- int skip_nested_if = FALSE;
-
- for_each_rtx (&PATTERN (insn), frv_clear_registers_used,
- (void *)&tmp_reg->regs);
-
- pattern = PATTERN (insn);
- if (GET_CODE (pattern) == COND_EXEC)
- {
- rtx reg = XEXP (COND_EXEC_TEST (pattern), 0);
-
- if (reg != sub_cond_exec_reg)
- {
- sub_cond_exec_reg = reg;
- frv_ifcvt.num_nested_cond_exec++;
- }
- }
-
- set = single_set_pattern (pattern);
- if (set)
+ op1 = frv_ifcvt_load_value (op1, insn);
+ if (op1)
{
- rtx dest = SET_DEST (set);
- rtx src = SET_SRC (set);
-
- if (GET_CODE (dest) == REG)
- {
- int regno = REGNO (dest);
- enum rtx_code src_code = GET_CODE (src);
-
- if (CC_P (regno) && src_code == COMPARE)
- skip_nested_if = TRUE;
-
- else if (CR_P (regno)
- && (src_code == IF_THEN_ELSE
- || COMPARISON_P (src)))
- skip_nested_if = TRUE;
- }
+ op1 = gen_rtx_SIGN_EXTEND (DImode, op1);
+ COND_EXEC_CODE (pattern)
+ = gen_rtx_SET (VOIDmode, dest,
+ gen_rtx_MULT (DImode, op0, op1));
}
-
- if (! skip_nested_if)
- for_each_rtx (&PATTERN (insn), frv_clear_registers_used,
- (void *)&frv_ifcvt.nested_cc_ok_rewrite);
+ else
+ goto fail;
}
- if (insn == last_insn)
- break;
-
- insn = NEXT_INSN (insn);
+ frv_ifcvt_add_insn (gen_use (dest), insn, FALSE);
}
- }
- /* If this is a nested if, rewrite the CC registers that are available to
- include the ones that can be rewritten, to increase the chance of being
- able to allocate a paired CC/CR register combination. */
- if (ce_info->pass > 1)
- {
- for (j = CC_FIRST; j <= CC_LAST; j++)
- if (TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, j))
- SET_HARD_REG_BIT (tmp_reg->regs, j);
- else
- CLEAR_HARD_REG_BIT (tmp_reg->regs, j);
- }
+ /* If we are just loading a constant created for a nested conditional
+ execution statement, just load the constant without any conditional
+ execution, since we know that the constant will not interfere with any
+ other registers. */
+ else if (frv_ifcvt.scratch_insns_bitmap
+ && bitmap_bit_p (frv_ifcvt.scratch_insns_bitmap,
+ 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 (df_get_live_in (ce_info->join_bb),
+ 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 (df_get_live_in (ce_info->else_bb),
+ REGNO (SET_DEST (set))))))
+ pattern = set;
- if (dump_file)
- {
- int num_gprs = 0;
- fprintf (dump_file, "Available GPRs: ");
+ else if (mode == QImode || mode == HImode || mode == SImode
+ || mode == SFmode)
+ {
+ int changed_p = FALSE;
- for (j = GPR_FIRST; j <= GPR_LAST; j++)
- if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
- {
- fprintf (dump_file, " %d [%s]", j, reg_names[j]);
- if (++num_gprs > GPR_TEMP_NUM+2)
- break;
- }
+ /* Check for just loading up a constant */
+ if (CONSTANT_P (src) && integer_register_operand (dest, mode))
+ {
+ src = frv_ifcvt_load_value (src, insn);
+ if (!src)
+ goto fail;
- fprintf (dump_file, "%s\nAvailable CRs: ",
- (num_gprs > GPR_TEMP_NUM+2) ? " ..." : "");
+ changed_p = TRUE;
+ }
- for (j = CR_FIRST; j <= CR_LAST; j++)
- if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
- fprintf (dump_file, " %d [%s]", j, reg_names[j]);
+ /* See if we need to fix up stores */
+ if (GET_CODE (dest) == MEM)
+ {
+ rtx new_mem = frv_ifcvt_rewrite_mem (dest, mode, insn);
- fputs ("\n", dump_file);
+ if (!new_mem)
+ goto fail;
- if (ce_info->pass > 1)
- {
- fprintf (dump_file, "Modifiable CCs: ");
- for (j = CC_FIRST; j <= CC_LAST; j++)
- if (TEST_HARD_REG_BIT (tmp_reg->regs, j))
- fprintf (dump_file, " %d [%s]", j, reg_names[j]);
+ else if (new_mem != dest)
+ {
+ changed_p = TRUE;
+ dest = new_mem;
+ }
+ }
- fprintf (dump_file, "\n%d nested COND_EXEC statements\n",
- frv_ifcvt.num_nested_cond_exec);
- }
- }
+ /* See if we need to fix up loads */
+ if (GET_CODE (src) == MEM)
+ {
+ rtx new_mem = frv_ifcvt_rewrite_mem (src, mode, insn);
- /* 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)
- {
- cr_class = ICR_REGS;
- cc_class = ICC_REGS;
- cc_first = ICC_FIRST;
- cc_last = ICC_LAST;
- }
- else if (mode == CC_FPmode)
- {
- cr_class = FCR_REGS;
- cc_class = FCC_REGS;
- cc_first = FCC_FIRST;
- cc_last = FCC_LAST;
- }
- else
- {
- cc_first = cc_last = 0;
- cr_class = cc_class = NO_REGS;
- }
+ if (!new_mem)
+ goto fail;
+
+ else if (new_mem != src)
+ {
+ changed_p = TRUE;
+ src = new_mem;
+ }
+ }
- cc = XEXP (true_expr, 0);
- nested_cc = cr = NULL_RTX;
- if (cc_class != NO_REGS)
- {
- /* For nested IFs and &&/||, see if we can find a CC and CR register pair
- so we can execute a csubcc/caddcc/cfcmps instruction. */
- int cc_regno;
+ /* If either src or destination changed, redo SET. */
+ if (changed_p)
+ COND_EXEC_CODE (pattern) = gen_rtx_SET (VOIDmode, dest, src);
+ }
- for (cc_regno = cc_first; cc_regno <= cc_last; cc_regno++)
+ /* 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 && COMPARISON_P (src))
{
- int cr_regno = cc_regno - CC_FIRST + CR_FIRST;
+ int regno = REGNO (XEXP (src, 0));
+ rtx if_else;
- if (TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cc_regno)
- && TEST_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, cr_regno))
+ if (ce_info->pass > 1
+ && regno != (int)REGNO (frv_ifcvt.nested_cc_reg)
+ && TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, regno))
{
- frv_ifcvt.tmp_reg.next_reg[ (int)cr_class ] = cr_regno;
- cr = frv_alloc_temp_reg (tmp_reg, cr_class, CC_CCRmode, TRUE,
- TRUE);
-
- frv_ifcvt.tmp_reg.next_reg[ (int)cc_class ] = cc_regno;
- nested_cc = frv_alloc_temp_reg (tmp_reg, cc_class, CCmode,
- TRUE, TRUE);
- break;
+ src = gen_rtx_fmt_ee (GET_CODE (src),
+ CC_CCRmode,
+ frv_ifcvt.nested_cc_reg,
+ XEXP (src, 1));
}
+
+ if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, test, src, const0_rtx);
+ pattern = gen_rtx_SET (VOIDmode, dest, if_else);
+ }
+
+ /* Remap a nested compare instruction to use the paired CC/CR reg. */
+ else if (ce_info->pass > 1
+ && GET_CODE (dest) == REG
+ && CC_P (REGNO (dest))
+ && REGNO (dest) != REGNO (frv_ifcvt.nested_cc_reg)
+ && TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite,
+ REGNO (dest))
+ && GET_CODE (src) == COMPARE)
+ {
+ PUT_MODE (frv_ifcvt.nested_cc_reg, GET_MODE (dest));
+ COND_EXEC_CODE (pattern)
+ = gen_rtx_SET (VOIDmode, frv_ifcvt.nested_cc_reg, copy_rtx (src));
}
}
- if (! cr)
+ if (TARGET_DEBUG_COND_EXEC)
{
- if (dump_file)
- fprintf (dump_file, "Could not allocate a CR temporary register\n");
-
- goto fail;
- }
+ rtx orig_pattern = PATTERN (insn);
- 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>");
+ PATTERN (insn) = pattern;
+ fprintf (stderr,
+ "\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn after modification:\n",
+ ce_info->pass);
- /* Set the CCR bit. Note for integer tests, we reverse the condition so that
- in an IF-THEN-ELSE sequence, we are testing the TRUE case against the CCR
- bit being true. We don't do this for floating point, because of NaNs. */
- code = GET_CODE (true_expr);
- if (GET_MODE (cc) != CC_FPmode)
- {
- code = reverse_condition (code);
- code_true = EQ;
- code_false = NE;
- }
- else
- {
- code_true = NE;
- code_false = EQ;
+ debug_rtx (insn);
+ PATTERN (insn) = orig_pattern;
}
- check_insn = gen_rtx_SET (VOIDmode, cr,
- gen_rtx_fmt_ee (code, CC_CCRmode, cc, const0_rtx));
+ return pattern;
- /* Record the check insn to be inserted later. */
- frv_ifcvt_add_insn (check_insn, BB_END (test_bb), TRUE);
+ fail:
+ if (TARGET_DEBUG_COND_EXEC)
+ {
+ rtx orig_pattern = PATTERN (insn);
- /* Update the tests. */
- frv_ifcvt.cr_reg = cr;
- frv_ifcvt.nested_cc_reg = nested_cc;
- *p_true = gen_rtx_fmt_ee (code_true, CC_CCRmode, cr, const0_rtx);
- *p_false = gen_rtx_fmt_ee (code_false, CC_CCRmode, cr, const0_rtx);
- return;
+ PATTERN (insn) = orig_ce_pattern;
+ fprintf (stderr,
+ "\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn could not be modified:\n",
+ ce_info->pass);
- /* Fail, don't do this conditional execution. */
- fail:
- *p_true = NULL_RTX;
- *p_false = NULL_RTX;
- if (dump_file)
- fprintf (dump_file, "Disabling this conditional execution.\n");
+ debug_rtx (insn);
+ PATTERN (insn) = orig_pattern;
+ }
- return;
+ return NULL_RTX;
}
\f
-/* A C expression to modify the code described by the conditional if
- information CE_INFO, for the basic block BB, possibly updating the tests in
- TRUE_EXPR, and FALSE_EXPR for converting the && and || parts of if-then or
- if-then-else code to conditional instructions. Set either TRUE_EXPR or
- FALSE_EXPR to a null pointer if the tests cannot be converted. */
-
-/* p_true and p_false are given expressions of the form:
-
- (and (eq:CC_CCR (reg:CC_CCR)
- (const_int 0))
- (eq:CC (reg:CC)
- (const_int 0))) */
+/* A C expression to perform any final machine dependent modifications in
+ converting code to conditional execution in the code described by the
+ conditional if information CE_INFO. */
void
-frv_ifcvt_modify_multiple_tests (ce_if_block_t *ce_info,
- basic_block bb,
- rtx *p_true,
- rtx *p_false)
+frv_ifcvt_modify_final (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
{
- rtx old_true = XEXP (*p_true, 0);
- rtx old_false = XEXP (*p_false, 0);
- rtx true_expr = XEXP (*p_true, 1);
- rtx false_expr = XEXP (*p_false, 1);
- rtx test_expr;
- rtx old_test;
- rtx cr = XEXP (old_true, 0);
+ rtx existing_insn;
rtx check_insn;
- rtx new_cr = NULL_RTX;
- rtx *p_new_cr = (rtx *)0;
- rtx if_else;
- rtx compare;
- rtx cc;
- enum reg_class cr_class;
- enum machine_mode mode = GET_MODE (true_expr);
- rtx (*logical_func)(rtx, rtx, rtx);
-
- if (TARGET_DEBUG_COND_EXEC)
- {
- fprintf (stderr,
- "\n:::::::::: frv_ifcvt_modify_multiple_tests, before modification for %s\ntrue insn:\n",
- ce_info->and_and_p ? "&&" : "||");
-
- debug_rtx (*p_true);
-
- fputs ("\nfalse insn:\n", stderr);
- debug_rtx (*p_false);
- }
-
- if (TARGET_NO_MULTI_CE)
- goto fail;
-
- if (GET_CODE (cr) != REG)
- goto fail;
-
- if (mode == CCmode || mode == CC_UNSmode)
- {
- cr_class = ICR_REGS;
- p_new_cr = &frv_ifcvt.extra_int_cr;
- }
- else if (mode == CC_FPmode)
- {
- cr_class = FCR_REGS;
- p_new_cr = &frv_ifcvt.extra_fp_cr;
- }
- else
- goto fail;
+ rtx p = frv_ifcvt.added_insns_list;
+ int i;
- /* Allocate a temp CR, reusing a previously allocated temp CR if we have 3 or
- more &&/|| tests. */
- new_cr = *p_new_cr;
- if (! new_cr)
- {
- new_cr = *p_new_cr = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, cr_class,
- CC_CCRmode, TRUE, TRUE);
- if (! new_cr)
- goto fail;
- }
+ /* Loop inserting the check insns. The last check insn is the first test,
+ and is the appropriate place to insert constants. */
+ gcc_assert (p);
- if (ce_info->and_and_p)
- {
- old_test = old_false;
- test_expr = true_expr;
- logical_func = (GET_CODE (old_true) == EQ) ? gen_andcr : gen_andncr;
- *p_true = gen_rtx_NE (CC_CCRmode, cr, const0_rtx);
- *p_false = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx);
- }
- else
+ do
{
- old_test = old_false;
- test_expr = false_expr;
- logical_func = (GET_CODE (old_false) == EQ) ? gen_orcr : gen_orncr;
- *p_true = gen_rtx_EQ (CC_CCRmode, cr, const0_rtx);
- *p_false = gen_rtx_NE (CC_CCRmode, cr, const0_rtx);
- }
-
- /* 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 (bb), TRUE);
+ rtx check_and_insert_insns = XEXP (p, 0);
+ rtx old_p = p;
- /* Now add the conditional check insn. */
- cc = XEXP (test_expr, 0);
- compare = gen_rtx_fmt_ee (GET_CODE (test_expr), CC_CCRmode, cc, const0_rtx);
- if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, old_test, compare, const0_rtx);
+ check_insn = XEXP (check_and_insert_insns, 0);
+ existing_insn = XEXP (check_and_insert_insns, 1);
+ p = XEXP (p, 1);
- check_insn = gen_rtx_SET (VOIDmode, new_cr, if_else);
+ /* The jump bit is used to say that the new insn is to be inserted BEFORE
+ the existing insn, otherwise it is to be inserted AFTER. */
+ if (check_and_insert_insns->jump)
+ {
+ emit_insn_before (check_insn, existing_insn);
+ check_and_insert_insns->jump = 0;
+ }
+ else
+ emit_insn_after (check_insn, existing_insn);
- /* Add the new check insn to the list of check insns that need to be
- inserted. */
- frv_ifcvt_add_insn (check_insn, BB_END (bb), TRUE);
+ free_EXPR_LIST_node (check_and_insert_insns);
+ free_EXPR_LIST_node (old_p);
+ }
+ while (p != NULL_RTX);
- if (TARGET_DEBUG_COND_EXEC)
+ /* Load up any constants needed into temp gprs */
+ for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++)
{
- fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, after modification\ntrue insn:\n",
- stderr);
-
- debug_rtx (*p_true);
-
- fputs ("\nfalse insn:\n", stderr);
- debug_rtx (*p_false);
+ rtx insn = emit_insn_before (frv_ifcvt.scratch_regs[i], existing_insn);
+ if (! frv_ifcvt.scratch_insns_bitmap)
+ 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;
}
- return;
+ frv_ifcvt.added_insns_list = NULL_RTX;
+ frv_ifcvt.cur_scratch_regs = 0;
+}
- fail:
- *p_true = *p_false = NULL_RTX;
+\f
+/* A C expression to cancel any machine dependent modifications in converting
+ code to conditional execution in the code described by the conditional if
+ information CE_INFO. */
- /* If we allocated a CR register, release it. */
- if (new_cr)
+void
+frv_ifcvt_modify_cancel (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
+{
+ int i;
+ rtx p = frv_ifcvt.added_insns_list;
+
+ /* Loop freeing up the EXPR_LIST's allocated. */
+ while (p != NULL_RTX)
{
- CLEAR_HARD_REG_BIT (frv_ifcvt.tmp_reg.regs, REGNO (new_cr));
- *p_new_cr = NULL_RTX;
+ rtx check_and_jump = XEXP (p, 0);
+ rtx old_p = p;
+
+ p = XEXP (p, 1);
+ free_EXPR_LIST_node (check_and_jump);
+ free_EXPR_LIST_node (old_p);
}
- if (TARGET_DEBUG_COND_EXEC)
- fputs ("\n:::::::::: frv_ifcvt_modify_multiple_tests, failed.\n", stderr);
+ /* Release any temporary gprs allocated. */
+ for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++)
+ frv_ifcvt.scratch_regs[i] = NULL_RTX;
+ frv_ifcvt.added_insns_list = NULL_RTX;
+ frv_ifcvt.cur_scratch_regs = 0;
return;
}
-
\f
-/* Return a register which will be loaded with a value if an IF block is
- converted to conditional execution. This is used to rewrite instructions
- that use constants to ones that just use registers. */
+/* A C expression for the size in bytes of the trampoline, as an integer.
+ The template is:
-static rtx
-frv_ifcvt_load_value (rtx value, rtx insn ATTRIBUTE_UNUSED)
+ setlo #0, <jmp_reg>
+ setlo #0, <static_chain>
+ sethi #0, <jmp_reg>
+ sethi #0, <static_chain>
+ jmpl @(gr0,<jmp_reg>) */
+
+int
+frv_trampoline_size (void)
{
- int num_alloc = frv_ifcvt.cur_scratch_regs;
- int i;
- rtx reg;
+ if (TARGET_FDPIC)
+ /* Allocate room for the function descriptor and the lddi
+ instruction. */
+ return 8 + 6 * 4;
+ return 5 /* instructions */ * 4 /* instruction size. */;
+}
- /* We know gr0 == 0, so replace any errant uses. */
- if (value == const0_rtx)
- return gen_rtx_REG (SImode, GPR_FIRST);
+\f
+/* A C statement to initialize the variable parts of a trampoline. ADDR is an
+ RTX for the address of the trampoline; FNADDR is an RTX for the address of
+ the nested function; STATIC_CHAIN is an RTX for the static chain value that
+ should be passed to the function when it is called.
- /* First search all registers currently loaded to see if we have an
- applicable constant. */
- if (CONSTANT_P (value)
- || (GET_CODE (value) == REG && REGNO (value) == LR_REGNO))
- {
- for (i = 0; i < num_alloc; i++)
- {
- if (rtx_equal_p (SET_SRC (frv_ifcvt.scratch_regs[i]), value))
- return SET_DEST (frv_ifcvt.scratch_regs[i]);
- }
- }
+ The template is:
- /* Have we exhausted the number of registers available? */
- if (num_alloc >= GPR_TEMP_NUM)
- {
- if (dump_file)
- fprintf (dump_file, "Too many temporary registers allocated\n");
+ setlo #0, <jmp_reg>
+ setlo #0, <static_chain>
+ sethi #0, <jmp_reg>
+ sethi #0, <static_chain>
+ jmpl @(gr0,<jmp_reg>) */
- return NULL_RTX;
- }
+static void
+frv_trampoline_init (rtx m_tramp, tree fndecl, rtx static_chain)
+{
+ rtx addr = XEXP (m_tramp, 0);
+ rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+ rtx sc_reg = force_reg (Pmode, static_chain);
- /* Allocate the new register. */
- reg = frv_alloc_temp_reg (&frv_ifcvt.tmp_reg, GPR_REGS, SImode, TRUE, TRUE);
- if (! reg)
- {
- if (dump_file)
- fputs ("Could not find a scratch register\n", dump_file);
+ emit_library_call (gen_rtx_SYMBOL_REF (SImode, "__trampoline_setup"),
+ LCT_NORMAL, VOIDmode, 4,
+ addr, Pmode,
+ GEN_INT (frv_trampoline_size ()), SImode,
+ fnaddr, Pmode,
+ sc_reg, Pmode);
+}
- return NULL_RTX;
- }
+\f
+/* Many machines have some registers that cannot be copied directly to or from
+ memory or even from other types of registers. An example is the `MQ'
+ register, which on most machines, can only be copied to or from general
+ registers, but not memory. Some machines allow copying all registers to and
+ from memory, but require a scratch register for stores to some memory
+ locations (e.g., those with symbolic address on the RT, and those with
+ certain symbolic address on the SPARC when compiling PIC). In some cases,
+ both an intermediate and a scratch register are required.
- frv_ifcvt.cur_scratch_regs++;
- frv_ifcvt.scratch_regs[num_alloc] = gen_rtx_SET (VOIDmode, reg, value);
+ You should define these macros to indicate to the reload phase that it may
+ need to allocate at least one register for a reload in addition to the
+ register to contain the data. Specifically, if copying X to a register
+ RCLASS in MODE requires an intermediate register, you should define
+ `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
+ whose registers can be used as intermediate registers or scratch registers.
- if (dump_file)
- {
- if (GET_CODE (value) == CONST_INT)
- fprintf (dump_file, "Register %s will hold %ld\n",
- reg_names[ REGNO (reg)], (long)INTVAL (value));
+ If copying a register RCLASS in MODE to X requires an intermediate or scratch
+ register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
+ largest register class required. If the requirements for input and output
+ reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
+ instead of defining both macros identically.
- else if (GET_CODE (value) == REG && REGNO (value) == LR_REGNO)
- fprintf (dump_file, "Register %s will hold LR\n",
- reg_names[ REGNO (reg)]);
+ The values returned by these macros are often `GENERAL_REGS'. Return
+ `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
+ to or from a register of RCLASS in MODE without requiring a scratch register.
+ Do not define this macro if it would always return `NO_REGS'.
- else
- fprintf (dump_file, "Register %s will hold a saved value\n",
- reg_names[ REGNO (reg)]);
- }
+ If a scratch register is required (either with or without an intermediate
+ register), you should define patterns for `reload_inM' or `reload_outM', as
+ required.. These patterns, which will normally be implemented with a
+ `define_expand', should be similar to the `movM' patterns, except that
+ operand 2 is the scratch register.
- return reg;
-}
+ Define constraints for the reload register and scratch register that contain
+ a single register class. If the original reload register (whose class is
+ RCLASS) can meet the constraint given in the pattern, the value returned by
+ these macros is used for the class of the scratch register. Otherwise, two
+ additional reload registers are required. Their classes are obtained from
+ the constraints in the insn pattern.
-\f
-/* Update a MEM used in conditional code that might contain an offset to put
- the offset into a scratch register, so that the conditional load/store
- operations can be used. This function returns the original pointer if the
- MEM is valid to use in conditional code, NULL if we can't load up the offset
- into a temporary register, or the new MEM if we were successful. */
+ X might be a pseudo-register or a `subreg' of a pseudo-register, which could
+ either be in a hard register or in memory. Use `true_regnum' to find out;
+ it will return -1 if the pseudo is in memory and the hard register number if
+ it is in a register.
-static rtx
-frv_ifcvt_rewrite_mem (rtx mem, enum machine_mode mode, rtx insn)
+ These macros should not be used in the case where a particular class of
+ registers can only be copied to memory and not to another class of
+ registers. In that case, secondary reload registers are not needed and
+ would not be helpful. Instead, a stack location must be used to perform the
+ copy and the `movM' pattern should use memory as an intermediate storage.
+ This case often occurs between floating-point and general registers. */
+
+enum reg_class
+frv_secondary_reload_class (enum reg_class rclass,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x)
{
- rtx addr = XEXP (mem, 0);
+ enum reg_class ret;
- if (!frv_legitimate_address_p (mode, addr, reload_completed, TRUE, FALSE))
+ switch (rclass)
{
- if (GET_CODE (addr) == PLUS)
- {
- rtx addr_op0 = XEXP (addr, 0);
- rtx addr_op1 = XEXP (addr, 1);
-
- if (GET_CODE (addr_op0) == REG && CONSTANT_P (addr_op1))
- {
- rtx reg = frv_ifcvt_load_value (addr_op1, insn);
- if (!reg)
- return NULL_RTX;
+ default:
+ ret = NO_REGS;
+ break;
- addr = gen_rtx_PLUS (Pmode, addr_op0, reg);
- }
+ /* Accumulators/Accumulator guard registers need to go through floating
+ point registers. */
+ case QUAD_REGS:
+ case EVEN_REGS:
+ case GPR_REGS:
+ ret = NO_REGS;
+ if (x && GET_CODE (x) == REG)
+ {
+ int regno = REGNO (x);
- else
- return NULL_RTX;
+ if (ACC_P (regno) || ACCG_P (regno))
+ ret = FPR_REGS;
}
+ break;
- else if (CONSTANT_P (addr))
- addr = frv_ifcvt_load_value (addr, insn);
-
+ /* Nonzero constants should be loaded into an FPR through a GPR. */
+ case QUAD_FPR_REGS:
+ case FEVEN_REGS:
+ case FPR_REGS:
+ if (x && CONSTANT_P (x) && !ZERO_P (x))
+ ret = GPR_REGS;
else
- return NULL_RTX;
+ ret = NO_REGS;
+ break;
- if (addr == NULL_RTX)
- return NULL_RTX;
+ /* All of these types need gpr registers. */
+ case ICC_REGS:
+ case FCC_REGS:
+ case CC_REGS:
+ case ICR_REGS:
+ case FCR_REGS:
+ case CR_REGS:
+ case LCR_REG:
+ case LR_REG:
+ ret = GPR_REGS;
+ break;
- else if (XEXP (mem, 0) != addr)
- return change_address (mem, mode, addr);
+ /* The accumulators need fpr registers. */
+ case ACC_REGS:
+ case EVEN_ACC_REGS:
+ case QUAD_ACC_REGS:
+ case ACCG_REGS:
+ ret = FPR_REGS;
+ break;
}
- return mem;
+ return ret;
}
-\f
-/* Given a PATTERN, return a SET expression if this PATTERN has only a single
- SET, possibly conditionally executed. It may also have CLOBBERs, USEs. */
-
-static rtx
-single_set_pattern (rtx pattern)
-{
- rtx set;
- int i;
-
- if (GET_CODE (pattern) == COND_EXEC)
- pattern = COND_EXEC_CODE (pattern);
-
- if (GET_CODE (pattern) == SET)
- return pattern;
+/* This hook exists to catch the case where secondary_reload_class() is
+ called from init_reg_autoinc() in regclass.c - before the reload optabs
+ have been initialised. */
+
+static reg_class_t
+frv_secondary_reload (bool in_p, rtx x, reg_class_t reload_class_i,
+ enum machine_mode reload_mode,
+ secondary_reload_info * sri)
+{
+ enum reg_class rclass = NO_REGS;
+ enum reg_class reload_class = (enum reg_class) reload_class_i;
- else if (GET_CODE (pattern) == PARALLEL)
+ if (sri->prev_sri && sri->prev_sri->t_icode != CODE_FOR_nothing)
{
- for (i = 0, set = 0; i < XVECLEN (pattern, 0); i++)
- {
- rtx sub = XVECEXP (pattern, 0, i);
-
- switch (GET_CODE (sub))
- {
- case USE:
- case CLOBBER:
- break;
+ sri->icode = sri->prev_sri->t_icode;
+ return NO_REGS;
+ }
- case SET:
- if (set)
- return 0;
- else
- set = sub;
- break;
+ rclass = frv_secondary_reload_class (reload_class, reload_mode, x);
- default:
- return 0;
- }
+ if (rclass != NO_REGS)
+ {
+ enum insn_code icode
+ = direct_optab_handler (in_p ? reload_in_optab : reload_out_optab,
+ reload_mode);
+ if (icode == 0)
+ {
+ /* This happens when then the reload_[in|out]_optabs have
+ not been initialised. */
+ sri->t_icode = CODE_FOR_nothing;
+ return rclass;
}
- return set;
}
- return 0;
-}
+ /* Fall back to the default secondary reload handler. */
+ return default_secondary_reload (in_p, x, reload_class, reload_mode, sri);
+}
\f
-/* A C expression to modify the code described by the conditional if
- information CE_INFO with the new PATTERN in INSN. If PATTERN is a null
- pointer after the IFCVT_MODIFY_INSN macro executes, it is assumed that that
- insn cannot be converted to be executed conditionally. */
+/* Worker function for TARGET_CLASS_LIKELY_SPILLED_P. */
-rtx
-frv_ifcvt_modify_insn (ce_if_block_t *ce_info,
- rtx pattern,
- rtx insn)
+static bool
+frv_class_likely_spilled_p (reg_class_t rclass)
{
- rtx orig_ce_pattern = pattern;
- rtx set;
- rtx op0;
- rtx op1;
- rtx test;
+ switch (rclass)
+ {
+ default:
+ break;
- if (GET_CODE (pattern) != COND_EXEC)
- abort ();
+ 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:
+ case ICR_REGS:
+ case FCR_REGS:
+ case CR_REGS:
+ case LCR_REG:
+ case LR_REG:
+ case SPR_REGS:
+ case QUAD_ACC_REGS:
+ case EVEN_ACC_REGS:
+ case ACC_REGS:
+ case ACCG_REGS:
+ return true;
+ }
- test = COND_EXEC_TEST (pattern);
- if (GET_CODE (test) == AND)
- {
- rtx cr = frv_ifcvt.cr_reg;
- rtx test_reg;
+ return false;
+}
- op0 = XEXP (test, 0);
- if (! rtx_equal_p (cr, XEXP (op0, 0)))
- goto fail;
+\f
+/* An expression for the alignment of a structure field FIELD if the
+ alignment computed in the usual way is COMPUTED. GCC uses this
+ value instead of the value in `BIGGEST_ALIGNMENT' or
+ `BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */
- op1 = XEXP (test, 1);
- test_reg = XEXP (op1, 0);
- if (GET_CODE (test_reg) != REG)
- goto fail;
+/* The definition type of the bit field data is either char, short, long or
+ long long. The maximum bit size is the number of bits of its own type.
- /* Is this the first nested if block in this sequence? If so, generate
- an andcr or andncr. */
- if (! frv_ifcvt.last_nested_if_cr)
- {
- rtx and_op;
+ The bit field data is assigned to a storage unit that has an adequate size
+ for bit field data retention and is located at the smallest address.
- frv_ifcvt.last_nested_if_cr = test_reg;
- if (GET_CODE (op0) == NE)
- and_op = gen_andcr (test_reg, cr, test_reg);
- else
- and_op = gen_andncr (test_reg, cr, test_reg);
+ Consecutive bit field data are packed at consecutive bits having the same
+ storage unit, with regard to the type, beginning with the MSB and continuing
+ toward the LSB.
- frv_ifcvt_add_insn (and_op, insn, TRUE);
- }
+ If a field to be assigned lies over a bit field type boundary, its
+ assignment is completed by aligning it with a boundary suitable for the
+ type.
- /* If this isn't the first statement in the nested if sequence, see if we
- are dealing with the same register. */
- else if (! rtx_equal_p (test_reg, frv_ifcvt.last_nested_if_cr))
- goto fail;
+ When a bit field having a bit length of 0 is declared, it is forcibly
+ assigned to the next storage unit.
- COND_EXEC_TEST (pattern) = test = op1;
- }
+ e.g)
+ struct {
+ int a:2;
+ int b:6;
+ char c:4;
+ int d:10;
+ int :0;
+ int f:2;
+ } x;
- /* If this isn't a nested if, reset state variables. */
- else
- {
- frv_ifcvt.last_nested_if_cr = NULL_RTX;
- }
+ +0 +1 +2 +3
+ &x 00000000 00000000 00000000 00000000
+ MLM----L
+ a b
+ &x+4 00000000 00000000 00000000 00000000
+ M--L
+ c
+ &x+8 00000000 00000000 00000000 00000000
+ M----------L
+ d
+ &x+12 00000000 00000000 00000000 00000000
+ ML
+ f
+*/
- set = single_set_pattern (pattern);
- if (set)
+int
+frv_adjust_field_align (tree field, int computed)
+{
+ /* Make sure that the bitfield is not wider than the type. */
+ if (DECL_BIT_FIELD (field)
+ && !DECL_ARTIFICIAL (field))
{
- rtx dest = SET_DEST (set);
- rtx src = SET_SRC (set);
- enum machine_mode mode = GET_MODE (dest);
+ tree parent = DECL_CONTEXT (field);
+ tree prev = NULL_TREE;
+ tree cur;
- /* Check for normal binary operators. */
- if (mode == SImode && ARITHMETIC_P (src))
+ for (cur = TYPE_FIELDS (parent); cur && cur != field; cur = DECL_CHAIN (cur))
{
- op0 = XEXP (src, 0);
- op1 = XEXP (src, 1);
+ if (TREE_CODE (cur) != FIELD_DECL)
+ continue;
- if (integer_register_operand (op0, SImode) && CONSTANT_P (op1))
- {
- op1 = frv_ifcvt_load_value (op1, insn);
- if (op1)
- COND_EXEC_CODE (pattern)
- = gen_rtx_SET (VOIDmode, dest, gen_rtx_fmt_ee (GET_CODE (src),
- GET_MODE (src),
- op0, op1));
- else
- goto fail;
- }
+ prev = cur;
}
- /* For multiply by a constant, we need to handle the sign extending
- correctly. Add a USE of the value after the multiply to prevent flow
- from cratering because only one register out of the two were used. */
- else if (mode == DImode && GET_CODE (src) == MULT)
- {
- op0 = XEXP (src, 0);
- op1 = XEXP (src, 1);
- if (GET_CODE (op0) == SIGN_EXTEND && GET_CODE (op1) == CONST_INT)
- {
- op1 = frv_ifcvt_load_value (op1, insn);
- if (op1)
- {
- op1 = gen_rtx_SIGN_EXTEND (DImode, op1);
- COND_EXEC_CODE (pattern)
- = gen_rtx_SET (VOIDmode, dest,
- gen_rtx_MULT (DImode, op0, op1));
- }
- else
- goto fail;
- }
+ gcc_assert (cur);
- frv_ifcvt_add_insn (gen_rtx_USE (VOIDmode, dest), insn, FALSE);
+ /* 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. */
+ if (prev
+ && ! DECL_PACKED (field)
+ && ! integer_zerop (DECL_SIZE (field))
+ && DECL_BIT_FIELD_TYPE (field) != DECL_BIT_FIELD_TYPE (prev))
+ {
+ int prev_align = TYPE_ALIGN (TREE_TYPE (prev));
+ int cur_align = TYPE_ALIGN (TREE_TYPE (field));
+ computed = (prev_align > cur_align) ? prev_align : cur_align;
}
+ }
- /* If we are just loading a constant created for a nested conditional
- execution statement, just load the constant without any conditional
- execution, since we know that the constant will not interfere with any
- other registers. */
- else if (frv_ifcvt.scratch_insns_bitmap
- && bitmap_bit_p (frv_ifcvt.scratch_insns_bitmap,
- INSN_UID (insn))
- /* We must not unconditionally set a reg set used as
- scratch in the THEN branch if the same reg is live
- in the ELSE branch. */
- && REG_P (SET_DEST (set))
- && (! ce_info->else_bb
- || BLOCK_FOR_INSN (insn) == ce_info->else_bb
- || ! (REGNO_REG_SET_P
- (ce_info->else_bb->global_live_at_start,
- REGNO (SET_DEST (set))))))
- pattern = set;
+ return computed;
+}
+
+\f
+/* A C expression that is nonzero if it is permissible to store a value of mode
+ MODE in hard register number REGNO (or in several registers starting with
+ that one). For a machine where all registers are equivalent, a suitable
+ definition is
+
+ #define HARD_REGNO_MODE_OK(REGNO, MODE) 1
+
+ It is not necessary for this macro to check for the numbers of fixed
+ registers, because the allocation mechanism considers them to be always
+ occupied.
+
+ On some machines, double-precision values must be kept in even/odd register
+ pairs. The way to implement that is to define this macro to reject odd
+ register numbers for such modes.
+
+ The minimum requirement for a mode to be OK in a register is that the
+ `movMODE' instruction pattern support moves between the register and any
+ other hard register for which the mode is OK; and that moving a value into
+ the register and back out not alter it.
+
+ Since the same instruction used to move `SImode' will work for all narrower
+ integer modes, it is not necessary on any machine for `HARD_REGNO_MODE_OK'
+ to distinguish between these modes, provided you define patterns `movhi',
+ etc., to take advantage of this. This is useful because of the interaction
+ between `HARD_REGNO_MODE_OK' and `MODES_TIEABLE_P'; it is very desirable for
+ all integer modes to be tieable.
+
+ Many machines have special registers for floating point arithmetic. Often
+ people assume that floating point machine modes are allowed only in floating
+ point registers. This is not true. Any registers that can hold integers
+ can safely *hold* a floating point machine mode, whether or not floating
+ arithmetic can be done on it in those registers. Integer move instructions
+ can be used to move the values.
+
+ On some machines, though, the converse is true: fixed-point machine modes
+ may not go in floating registers. This is true if the floating registers
+ normalize any value stored in them, because storing a non-floating value
+ there would garble it. In this case, `HARD_REGNO_MODE_OK' should reject
+ fixed-point machine modes in floating registers. But if the floating
+ registers do not automatically normalize, if you can store any bit pattern
+ in one and retrieve it unchanged without a trap, then any machine mode may
+ go in a floating register, so you can define this macro to say so.
- else if (mode == QImode || mode == HImode || mode == SImode
- || mode == SFmode)
- {
- int changed_p = FALSE;
+ The primary significance of special floating registers is rather that they
+ are the registers acceptable in floating point arithmetic instructions.
+ However, this is of no concern to `HARD_REGNO_MODE_OK'. You handle it by
+ writing the proper constraints for those instructions.
- /* Check for just loading up a constant */
- if (CONSTANT_P (src) && integer_register_operand (dest, mode))
- {
- src = frv_ifcvt_load_value (src, insn);
- if (!src)
- goto fail;
+ On some machines, the floating registers are especially slow to access, so
+ that it is better to store a value in a stack frame than in such a register
+ if floating point arithmetic is not being done. As long as the floating
+ registers are not in class `GENERAL_REGS', they will not be used unless some
+ pattern's constraint asks for one. */
- changed_p = TRUE;
- }
+int
+frv_hard_regno_mode_ok (int regno, enum machine_mode mode)
+{
+ int base;
+ int mask;
- /* See if we need to fix up stores */
- if (GET_CODE (dest) == MEM)
- {
- rtx new_mem = frv_ifcvt_rewrite_mem (dest, mode, insn);
+ switch (mode)
+ {
+ case CCmode:
+ case CC_UNSmode:
+ case CC_NZmode:
+ return ICC_P (regno) || GPR_P (regno);
- if (!new_mem)
- goto fail;
+ case CC_CCRmode:
+ return CR_P (regno) || GPR_P (regno);
- else if (new_mem != dest)
- {
- changed_p = TRUE;
- dest = new_mem;
- }
- }
+ case CC_FPmode:
+ return FCC_P (regno) || GPR_P (regno);
- /* See if we need to fix up loads */
- if (GET_CODE (src) == MEM)
- {
- rtx new_mem = frv_ifcvt_rewrite_mem (src, mode, insn);
+ default:
+ break;
+ }
- if (!new_mem)
- goto fail;
+ /* Set BASE to the first register in REGNO's class. Set MASK to the
+ bits that must be clear in (REGNO - BASE) for the register to be
+ well-aligned. */
+ if (INTEGRAL_MODE_P (mode) || FLOAT_MODE_P (mode) || VECTOR_MODE_P (mode))
+ {
+ if (ACCG_P (regno))
+ {
+ /* ACCGs store one byte. Two-byte quantities must start in
+ even-numbered registers, four-byte ones in registers whose
+ numbers are divisible by four, and so on. */
+ base = ACCG_FIRST;
+ mask = GET_MODE_SIZE (mode) - 1;
+ }
+ else
+ {
+ /* The other registers store one word. */
+ if (GPR_P (regno) || regno == AP_FIRST)
+ base = GPR_FIRST;
- else if (new_mem != src)
- {
- changed_p = TRUE;
- src = new_mem;
- }
- }
+ else if (FPR_P (regno))
+ base = FPR_FIRST;
- /* If either src or destination changed, redo SET. */
- if (changed_p)
- COND_EXEC_CODE (pattern) = gen_rtx_SET (VOIDmode, dest, src);
- }
+ else if (ACC_P (regno))
+ base = ACC_FIRST;
- /* 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 && COMPARISON_P (src))
- {
- int regno = REGNO (XEXP (src, 0));
- rtx if_else;
+ else if (SPR_P (regno))
+ return mode == SImode;
- if (ce_info->pass > 1
- && regno != (int)REGNO (frv_ifcvt.nested_cc_reg)
- && TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite, regno))
- {
- src = gen_rtx_fmt_ee (GET_CODE (src),
- CC_CCRmode,
- frv_ifcvt.nested_cc_reg,
- XEXP (src, 1));
- }
+ /* Fill in the table. */
+ else
+ return 0;
- if_else = gen_rtx_IF_THEN_ELSE (CC_CCRmode, test, src, const0_rtx);
- pattern = gen_rtx_SET (VOIDmode, dest, if_else);
- }
+ /* Anything smaller than an SI is OK in any word-sized register. */
+ if (GET_MODE_SIZE (mode) < 4)
+ return 1;
- /* Remap a nested compare instruction to use the paired CC/CR reg. */
- else if (ce_info->pass > 1
- && GET_CODE (dest) == REG
- && CC_P (REGNO (dest))
- && REGNO (dest) != REGNO (frv_ifcvt.nested_cc_reg)
- && TEST_HARD_REG_BIT (frv_ifcvt.nested_cc_ok_rewrite,
- REGNO (dest))
- && GET_CODE (src) == COMPARE)
- {
- PUT_MODE (frv_ifcvt.nested_cc_reg, GET_MODE (dest));
- COND_EXEC_CODE (pattern)
- = gen_rtx_SET (VOIDmode, frv_ifcvt.nested_cc_reg, copy_rtx (src));
+ mask = (GET_MODE_SIZE (mode) / 4) - 1;
}
+ return (((regno - base) & mask) == 0);
}
- if (TARGET_DEBUG_COND_EXEC)
- {
- rtx orig_pattern = PATTERN (insn);
+ return 0;
+}
- PATTERN (insn) = pattern;
- fprintf (stderr,
- "\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn after modification:\n",
- ce_info->pass);
+\f
+/* A C expression for the number of consecutive hard registers, starting at
+ register number REGNO, required to hold a value of mode MODE.
- debug_rtx (insn);
- PATTERN (insn) = orig_pattern;
- }
+ On a machine where all registers are exactly one word, a suitable definition
+ of this macro is
- return pattern;
+ #define HARD_REGNO_NREGS(REGNO, MODE) \
+ ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
+ / UNITS_PER_WORD)) */
- fail:
- if (TARGET_DEBUG_COND_EXEC)
- {
- rtx orig_pattern = PATTERN (insn);
+/* On the FRV, make the CC_FP mode take 3 words in the integer registers, so
+ that we can build the appropriate instructions to properly reload the
+ values. Also, make the byte-sized accumulator guards use one guard
+ for each byte. */
- PATTERN (insn) = orig_ce_pattern;
- fprintf (stderr,
- "\n:::::::::: frv_ifcvt_modify_insn: pass = %d, insn could not be modified:\n",
- ce_info->pass);
+int
+frv_hard_regno_nregs (int regno, enum machine_mode mode)
+{
+ if (ACCG_P (regno))
+ return GET_MODE_SIZE (mode);
+ else
+ return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
- debug_rtx (insn);
- PATTERN (insn) = orig_pattern;
- }
+\f
+/* A C expression for the maximum number of consecutive registers of
+ class RCLASS needed to hold a value of mode MODE.
- return NULL_RTX;
+ This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value
+ of the macro `CLASS_MAX_NREGS (RCLASS, MODE)' should be the maximum value of
+ `HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class RCLASS.
+
+ This macro helps control the handling of multiple-word values in
+ the reload pass.
+
+ This declaration is required. */
+
+int
+frv_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
+{
+ if (rclass == ACCG_REGS)
+ /* An N-byte value requires N accumulator guards. */
+ return GET_MODE_SIZE (mode);
+ else
+ return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
}
\f
-/* A C expression to perform any final machine dependent modifications in
- converting code to conditional execution in the code described by the
- conditional if information CE_INFO. */
+/* A C expression that is nonzero if X is a legitimate constant for an
+ immediate operand on the target machine. You can assume that X satisfies
+ `CONSTANT_P', so you need not check this. In fact, `1' is a suitable
+ definition for this macro on machines where anything `CONSTANT_P' is valid. */
-void
-frv_ifcvt_modify_final (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
+int
+frv_legitimate_constant_p (rtx x)
{
- rtx existing_insn;
- rtx check_insn;
- rtx p = frv_ifcvt.added_insns_list;
- int i;
+ enum machine_mode mode = GET_MODE (x);
- /* Loop inserting the check insns. The last check insn is the first test,
- and is the appropriate place to insert constants. */
- if (! p)
- abort ();
+ /* 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.
- do
- {
- rtx check_and_insert_insns = XEXP (p, 0);
- rtx old_p = p;
+ 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.
- check_insn = XEXP (check_and_insert_insns, 0);
- existing_insn = XEXP (check_and_insert_insns, 1);
- p = XEXP (p, 1);
+ 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);
- /* The jump bit is used to say that the new insn is to be inserted BEFORE
- the existing insn, otherwise it is to be inserted AFTER. */
- if (check_and_insert_insns->jump)
- {
- emit_insn_before (check_insn, existing_insn);
- check_and_insert_insns->jump = 0;
- }
- else
- emit_insn_after (check_insn, existing_insn);
+ /* All of the integer constants are ok. */
+ if (GET_CODE (x) != CONST_DOUBLE)
+ return TRUE;
- free_EXPR_LIST_node (check_and_insert_insns);
- free_EXPR_LIST_node (old_p);
- }
- while (p != NULL_RTX);
+ /* double integer constants are ok. */
+ if (mode == VOIDmode || mode == DImode)
+ return TRUE;
+
+ /* 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. */
+ if (!TARGET_HAS_FPRS)
+ return TRUE;
- /* Load up any constants needed into temp gprs */
- for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++)
- {
- 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 ();
- bitmap_set_bit (frv_ifcvt.scratch_insns_bitmap, INSN_UID (insn));
- frv_ifcvt.scratch_regs[i] = NULL_RTX;
- }
+ if (mode == DFmode && !TARGET_DOUBLE)
+ return TRUE;
- frv_ifcvt.added_insns_list = NULL_RTX;
- frv_ifcvt.cur_scratch_regs = 0;
+ /* Otherwise store the constant away and do a load. */
+ return FALSE;
}
-\f
-/* A C expression to cancel any machine dependent modifications in converting
- code to conditional execution in the code described by the conditional if
- information CE_INFO. */
+/* 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. */
-void
-frv_ifcvt_modify_cancel (ce_if_block_t *ce_info ATTRIBUTE_UNUSED)
+enum machine_mode
+frv_select_cc_mode (enum rtx_code code, rtx x, rtx y)
{
- int i;
- rtx p = frv_ifcvt.added_insns_list;
+ if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+ return CC_FPmode;
- /* Loop freeing up the EXPR_LIST's allocated. */
- while (p != NULL_RTX)
+ switch (code)
{
- rtx check_and_jump = XEXP (p, 0);
- rtx old_p = p;
-
- p = XEXP (p, 1);
- free_EXPR_LIST_node (check_and_jump);
- free_EXPR_LIST_node (old_p);
- }
+ case EQ:
+ case NE:
+ case LT:
+ case GE:
+ return y == const0_rtx ? CC_NZmode : CCmode;
- /* Release any temporary gprs allocated. */
- for (i = 0; i < frv_ifcvt.cur_scratch_regs; i++)
- frv_ifcvt.scratch_regs[i] = NULL_RTX;
+ case GTU:
+ case GEU:
+ case LTU:
+ case LEU:
+ return y == const0_rtx ? CC_NZmode : CC_UNSmode;
- frv_ifcvt.added_insns_list = NULL_RTX;
- frv_ifcvt.cur_scratch_regs = 0;
- return;
+ default:
+ return CCmode;
+ }
}
\f
-/* A C expression for the size in bytes of the trampoline, as an integer.
- The template is:
- setlo #0, <jmp_reg>
- setlo #0, <static_chain>
- sethi #0, <jmp_reg>
- sethi #0, <static_chain>
- jmpl @(gr0,<jmp_reg>) */
+/* Worker function for TARGET_REGISTER_MOVE_COST. */
-int
-frv_trampoline_size (void)
+#define HIGH_COST 40
+#define MEDIUM_COST 3
+#define LOW_COST 1
+
+static int
+frv_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t from, reg_class_t to)
{
- if (TARGET_FDPIC)
- /* Allocate room for the function descriptor and the lddi
- instruction. */
- return 8 + 6 * 4;
- return 5 /* instructions */ * 4 /* instruction size. */;
-}
+ switch (from)
+ {
+ default:
+ break;
-\f
-/* A C statement to initialize the variable parts of a trampoline. ADDR is an
- RTX for the address of the trampoline; FNADDR is an RTX for the address of
- the nested function; STATIC_CHAIN is an RTX for the static chain value that
- should be passed to the function when it is called.
+ case QUAD_REGS:
+ case EVEN_REGS:
+ case GPR_REGS:
+ switch (to)
+ {
+ default:
+ break;
- The template is:
+ case QUAD_REGS:
+ case EVEN_REGS:
+ case GPR_REGS:
+ return LOW_COST;
- setlo #0, <jmp_reg>
- setlo #0, <static_chain>
- sethi #0, <jmp_reg>
- sethi #0, <static_chain>
- jmpl @(gr0,<jmp_reg>) */
+ case FEVEN_REGS:
+ case FPR_REGS:
+ return LOW_COST;
-void
-frv_initialize_trampoline (rtx addr, rtx fnaddr, rtx static_chain)
-{
- rtx sc_reg = force_reg (Pmode, static_chain);
+ case LCR_REG:
+ case LR_REG:
+ case SPR_REGS:
+ return LOW_COST;
+ }
- emit_library_call (gen_rtx_SYMBOL_REF (SImode, "__trampoline_setup"),
- FALSE, VOIDmode, 4,
- addr, Pmode,
- GEN_INT (frv_trampoline_size ()), SImode,
- fnaddr, Pmode,
- sc_reg, Pmode);
-}
+ case FEVEN_REGS:
+ case FPR_REGS:
+ switch (to)
+ {
+ default:
+ break;
-\f
-/* Many machines have some registers that cannot be copied directly to or from
- memory or even from other types of registers. An example is the `MQ'
- register, which on most machines, can only be copied to or from general
- registers, but not memory. Some machines allow copying all registers to and
- from memory, but require a scratch register for stores to some memory
- locations (e.g., those with symbolic address on the RT, and those with
- certain symbolic address on the SPARC when compiling PIC). In some cases,
- both an intermediate and a scratch register are required.
+ case QUAD_REGS:
+ case EVEN_REGS:
+ case GPR_REGS:
+ case ACC_REGS:
+ case EVEN_ACC_REGS:
+ case QUAD_ACC_REGS:
+ case ACCG_REGS:
+ return MEDIUM_COST;
- You should define these macros to indicate to the reload phase that it may
- need to allocate at least one register for a reload in addition to the
- register to contain the data. Specifically, if copying X to a register
- CLASS in MODE requires an intermediate register, you should define
- `SECONDARY_INPUT_RELOAD_CLASS' to return the largest register class all of
- whose registers can be used as intermediate registers or scratch registers.
+ case FEVEN_REGS:
+ case FPR_REGS:
+ return LOW_COST;
+ }
- If copying a register CLASS in MODE to X requires an intermediate or scratch
- register, `SECONDARY_OUTPUT_RELOAD_CLASS' should be defined to return the
- largest register class required. If the requirements for input and output
- reloads are the same, the macro `SECONDARY_RELOAD_CLASS' should be used
- instead of defining both macros identically.
+ case LCR_REG:
+ case LR_REG:
+ case SPR_REGS:
+ switch (to)
+ {
+ default:
+ break;
- The values returned by these macros are often `GENERAL_REGS'. Return
- `NO_REGS' if no spare register is needed; i.e., if X can be directly copied
- to or from a register of CLASS in MODE without requiring a scratch register.
- Do not define this macro if it would always return `NO_REGS'.
+ case QUAD_REGS:
+ case EVEN_REGS:
+ case GPR_REGS:
+ return MEDIUM_COST;
+ }
- If a scratch register is required (either with or without an intermediate
- register), you should define patterns for `reload_inM' or `reload_outM', as
- required.. These patterns, which will normally be implemented with a
- `define_expand', should be similar to the `movM' patterns, except that
- operand 2 is the scratch register.
+ case ACC_REGS:
+ case EVEN_ACC_REGS:
+ case QUAD_ACC_REGS:
+ case ACCG_REGS:
+ switch (to)
+ {
+ default:
+ break;
- Define constraints for the reload register and scratch register that contain
- a single register class. If the original reload register (whose class is
- CLASS) can meet the constraint given in the pattern, the value returned by
- these macros is used for the class of the scratch register. Otherwise, two
- additional reload registers are required. Their classes are obtained from
- the constraints in the insn pattern.
+ case FEVEN_REGS:
+ case FPR_REGS:
+ return MEDIUM_COST;
- X might be a pseudo-register or a `subreg' of a pseudo-register, which could
- either be in a hard register or in memory. Use `true_regnum' to find out;
- it will return -1 if the pseudo is in memory and the hard register number if
- it is in a register.
+ }
+ }
- These macros should not be used in the case where a particular class of
- registers can only be copied to memory and not to another class of
- registers. In that case, secondary reload registers are not needed and
- would not be helpful. Instead, a stack location must be used to perform the
- copy and the `movM' pattern should use memory as an intermediate storage.
- This case often occurs between floating-point and general registers. */
+ return HIGH_COST;
+}
+
+/* Worker function for TARGET_MEMORY_MOVE_COST. */
+
+static int
+frv_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+ reg_class_t rclass ATTRIBUTE_UNUSED,
+ bool in ATTRIBUTE_UNUSED)
+{
+ return 4;
+}
+
+\f
+/* Implementation of TARGET_ASM_INTEGER. In the FRV case we need to
+ use ".picptr" to generate safe relocations for PIC code. We also
+ need a fixup entry for aligned (non-debugging) code. */
+
+static bool
+frv_assemble_integer (rtx value, unsigned int size, int aligned_p)
+{
+ 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 (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];
+ const char *p;
+
+ ASM_GENERATE_INTERNAL_LABEL (buf, "LCP", label_num++);
+ p = (* targetm.strip_name_encoding) (buf);
+
+ fprintf (asm_out_file, "%s:\n", p);
+ fprintf (asm_out_file, "%s\n", FIXUP_SECTION_ASM_OP);
+ fprintf (asm_out_file, "\t.picptr\t%s\n", p);
+ fprintf (asm_out_file, "\t.previous\n");
+ }
+ assemble_integer_with_op ("\t.picptr\t", value);
+ return true;
+ }
+ if (!aligned_p)
+ {
+ /* We've set the unaligned SI op to NULL, so we always have to
+ handle the unaligned case here. */
+ assemble_integer_with_op ("\t.4byte\t", value);
+ return true;
+ }
+ }
+ return default_assemble_integer (value, size, aligned_p);
+}
-enum reg_class
-frv_secondary_reload_class (enum reg_class class,
- enum machine_mode mode ATTRIBUTE_UNUSED,
- rtx x,
- int in_p ATTRIBUTE_UNUSED)
+/* Function to set up the backend function structure. */
+
+static struct machine_function *
+frv_init_machine_status (void)
{
- enum reg_class ret;
+ return ggc_alloc_cleared_machine_function ();
+}
+\f
+/* Implement TARGET_SCHED_ISSUE_RATE. */
+
+int
+frv_issue_rate (void)
+{
+ if (!TARGET_PACK)
+ return 1;
- switch (class)
+ switch (frv_cpu_type)
{
default:
- ret = NO_REGS;
- break;
-
- /* Accumulators/Accumulator guard registers need to go through floating
- point registers. */
- case QUAD_REGS:
- case EVEN_REGS:
- case GPR_REGS:
- ret = NO_REGS;
- if (x && GET_CODE (x) == REG)
- {
- int regno = REGNO (x);
-
- if (ACC_P (regno) || ACCG_P (regno))
- ret = FPR_REGS;
- }
- break;
+ case FRV_CPU_FR300:
+ case FRV_CPU_SIMPLE:
+ return 1;
- /* Nonzero constants should be loaded into an FPR through a GPR. */
- case QUAD_FPR_REGS:
- case FEVEN_REGS:
- case FPR_REGS:
- if (x && CONSTANT_P (x) && !ZERO_P (x))
- ret = GPR_REGS;
- else
- ret = NO_REGS;
- break;
+ case FRV_CPU_FR400:
+ case FRV_CPU_FR405:
+ case FRV_CPU_FR450:
+ return 2;
- /* All of these types need gpr registers. */
- case ICC_REGS:
- case FCC_REGS:
- case CC_REGS:
- case ICR_REGS:
- case FCR_REGS:
- case CR_REGS:
- case LCR_REG:
- case LR_REG:
- ret = GPR_REGS;
- break;
+ case FRV_CPU_GENERIC:
+ case FRV_CPU_FR500:
+ case FRV_CPU_TOMCAT:
+ return 4;
- /* The accumulators need fpr registers */
- case ACC_REGS:
- case EVEN_ACC_REGS:
- case QUAD_ACC_REGS:
- case ACCG_REGS:
- ret = FPR_REGS;
- break;
+ 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. */
- return ret;
+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;
}
-\f
-/* A C expression whose value is nonzero if pseudos that have been assigned to
- registers of class CLASS would likely be spilled because registers of CLASS
- are needed for spill registers.
-
- The default value of this macro returns 1 if CLASS has exactly one register
- and zero otherwise. On most machines, this default should be used. Only
- define this macro to some other expression if pseudo allocated by
- `local-alloc.c' end up in memory because their hard registers were needed
- for spill registers. If this macro returns nonzero for those classes, those
- pseudos will only be allocated by `global.c', which knows how to reallocate
- the pseudo to another register. If there would not be another register
- available for reallocation, you should not change the definition of this
- macro since the only effect of such a definition would be to slow down
- register allocation. */
+/* Return the value of INSN's acc_group attribute. */
int
-frv_class_likely_spilled_p (enum reg_class class)
+frv_acc_group (rtx insn)
{
- switch (class)
+ /* 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))
{
- default:
- break;
+ /* We haven't seen this type of instruction before. */
+ state_t state;
+ unsigned int unit;
- case ICC_REGS:
- case FCC_REGS:
- case CC_REGS:
- case ICR_REGS:
- case FCR_REGS:
- case CR_REGS:
- case LCR_REG:
- case LR_REG:
- case SPR_REGS:
- case QUAD_ACC_REGS:
- case EVEN_ACC_REGS:
- case ACC_REGS:
- case ACCG_REGS:
- return TRUE;
- }
+ /* Issue the instruction on its own to see which unit it prefers. */
+ state = alloca (state_size ());
+ state_reset (state);
+ state_transition (state, insn);
- return FALSE;
+ /* 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
-/* An expression for the alignment of a structure field FIELD if the
- alignment computed in the usual way is COMPUTED. GCC uses this
- value instead of the value in `BIGGEST_ALIGNMENT' or
- `BIGGEST_FIELD_ALIGNMENT', if defined, for structure fields only. */
+/* 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;
+};
-/* The definition type of the bit field data is either char, short, long or
- long long. The maximum bit size is the number of bits of its own type.
+/* The current state of the packing pass, implemented by frv_pack_insns. */
+static struct {
+ /* The state of the pipeline DFA. */
+ state_t dfa_state;
- The bit field data is assigned to a storage unit that has an adequate size
- for bit field data retention and is located at the smallest address.
+ /* Which hardware registers are set within the current packet,
+ and the conditions under which they are set. */
+ regstate_t regstate[FIRST_PSEUDO_REGISTER];
- Consecutive bit field data are packed at consecutive bits having the same
- storage unit, with regard to the type, beginning with the MSB and continuing
- toward the LSB.
+ /* 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];
- If a field to be assigned lies over a bit field type boundary, its
- assignment is completed by aligning it with a boundary suitable for the
- type.
+ /* 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;
- When a bit field having a bit length of 0 is declared, it is forcibly
- assigned to the next storage unit.
+ /* The maximum number of instructions that can be packed together. */
+ unsigned int issue_rate;
- e.g)
- struct {
- int a:2;
- int b:6;
- char c:4;
- int d:10;
- int :0;
- int f:2;
- } x;
+ /* The instructions in the packet, partitioned into groups. */
+ struct frv_packet_group groups[NUM_GROUPS];
- +0 +1 +2 +3
- &x 00000000 00000000 00000000 00000000
- MLM----L
- a b
- &x+4 00000000 00000000 00000000 00000000
- M--L
- c
- &x+8 00000000 00000000 00000000 00000000
- M----------L
- d
- &x+12 00000000 00000000 00000000 00000000
- ML
- f
-*/
+ /* The instructions that make up the current packet. */
+ rtx insns[ARRAY_SIZE (frv_unit_codes)];
+ unsigned int num_insns;
+} frv_packet;
-int
-frv_adjust_field_align (tree field, int computed)
+/* 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)
{
- /* 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;
+ 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));
+}
- for (cur = TYPE_FIELDS (parent); cur && cur != field; cur = TREE_CHAIN (cur))
- {
- if (TREE_CODE (cur) != FIELD_DECL)
- continue;
- prev = cur;
- }
+/* Return true if something accessed under condition COND2 can
+ conflict with something written under condition COND1. */
- if (!cur)
- abort ();
+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;
- /* 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. */
- if (prev
- && ! DECL_PACKED (field)
- && ! integer_zerop (DECL_SIZE (field))
- && DECL_BIT_FIELD_TYPE (field) != DECL_BIT_FIELD_TYPE (prev))
- {
- int prev_align = TYPE_ALIGN (TREE_TYPE (prev));
- int cur_align = TYPE_ALIGN (TREE_TYPE (field));
- computed = (prev_align > cur_align) ? prev_align : cur_align;
- }
- }
+ /* The references might conflict if they were controlled by
+ different CRs. */
+ if ((cond1 & REGSTATE_CC_MASK) != (cond2 & REGSTATE_CC_MASK))
+ return true;
- return computed;
+ /* They definitely conflict if they are controlled by the
+ same condition. */
+ if ((cond1 & cond2 & REGSTATE_IF_EITHER) != 0)
+ return true;
+
+ return false;
}
-\f
-/* A C expression that is nonzero if it is permissible to store a value of mode
- MODE in hard register number REGNO (or in several registers starting with
- that one). For a machine where all registers are equivalent, a suitable
- definition is
- #define HARD_REGNO_MODE_OK(REGNO, MODE) 1
+/* 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. */
- It is not necessary for this macro to check for the numbers of fixed
- registers, because the allocation mechanism considers them to be always
- occupied.
+static int
+frv_registers_conflict_p_1 (rtx *x, void *data)
+{
+ unsigned int regno, i;
+ regstate_t cond;
- On some machines, double-precision values must be kept in even/odd register
- pairs. The way to implement that is to define this macro to reject odd
- register numbers for such modes.
+ cond = *(regstate_t *) data;
- The minimum requirement for a mode to be OK in a register is that the
- `movMODE' instruction pattern support moves between the register and any
- other hard register for which the mode is OK; and that moving a value into
- the register and back out not alter it.
+ 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;
- Since the same instruction used to move `SImode' will work for all narrower
- integer modes, it is not necessary on any machine for `HARD_REGNO_MODE_OK'
- to distinguish between these modes, provided you define patterns `movhi',
- etc., to take advantage of this. This is useful because of the interaction
- between `HARD_REGNO_MODE_OK' and `MODES_TIEABLE_P'; it is very desirable for
- all integer modes to be tieable.
+ 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;
- Many machines have special registers for floating point arithmetic. Often
- people assume that floating point machine modes are allowed only in floating
- point registers. This is not true. Any registers that can hold integers
- can safely *hold* a floating point machine mode, whether or not floating
- arithmetic can be done on it in those registers. Integer move instructions
- can be used to move the values.
+ /* 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;
- On some machines, though, the converse is true: fixed-point machine modes
- may not go in floating registers. This is true if the floating registers
- normalize any value stored in them, because storing a non-floating value
- there would garble it. In this case, `HARD_REGNO_MODE_OK' should reject
- fixed-point machine modes in floating registers. But if the floating
- registers do not automatically normalize, if you can store any bit pattern
- in one and retrieve it unchanged without a trap, then any machine mode may
- go in a floating register, so you can define this macro to say so.
+ if (output_dependence (frv_packet.mems[i].mem, *x))
+ return 1;
+ }
+ }
- The primary significance of special floating registers is rather that they
- are the registers acceptable in floating point arithmetic instructions.
- However, this is of no concern to `HARD_REGNO_MODE_OK'. You handle it by
- writing the proper constraints for those instructions.
+ /* 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;
+ }
- On some machines, the floating registers are especially slow to access, so
- that it is better to store a value in a stack frame than in such a register
- if floating point arithmetic is not being done. As long as the floating
- registers are not in class `GENERAL_REGS', they will not be used unless some
- pattern's constraint asks for one. */
+ /* Check subexpressions. */
+ return 0;
+}
-int
-frv_hard_regno_mode_ok (int regno, enum machine_mode mode)
+
+/* Return true if something in X might depend on an instruction
+ in the current packet. */
+
+static bool
+frv_registers_conflict_p (rtx x)
{
- int base;
- int mask;
+ 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);
+}
- switch (mode)
- {
- case CCmode:
- case CC_UNSmode:
- return ICC_P (regno) || GPR_P (regno);
- case CC_CCRmode:
- return CR_P (regno) || GPR_P (regno);
+/* A note_stores callback. DATA points to the regstate_t condition
+ under which X is modified. Update FRV_PACKET accordingly. */
- case CC_FPmode:
- return FCC_P (regno) || GPR_P (regno);
+static void
+frv_registers_update_1 (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ unsigned int regno;
- default:
- break;
- }
+ if (GET_CODE (x) == REG)
+ FOR_EACH_REGNO (regno, x)
+ frv_packet.regstate[regno] |= *(regstate_t *) data;
- /* Set BASE to the first register in REGNO's class. Set MASK to the
- bits that must be clear in (REGNO - BASE) for the register to be
- well-aligned. */
- if (INTEGRAL_MODE_P (mode) || FLOAT_MODE_P (mode) || VECTOR_MODE_P (mode))
+ if (GET_CODE (x) == MEM)
{
- if (ACCG_P (regno))
+ if (frv_packet.num_mems < ARRAY_SIZE (frv_packet.mems))
{
- /* ACCGs store one byte. Two-byte quantities must start in
- even-numbered registers, four-byte ones in registers whose
- numbers are divisible by four, and so on. */
- base = ACCG_FIRST;
- mask = GET_MODE_SIZE (mode) - 1;
+ frv_packet.mems[frv_packet.num_mems].mem = x;
+ frv_packet.mems[frv_packet.num_mems].cond = *(regstate_t *) data;
}
- else
- {
- /* The other registers store one word. */
- if (GPR_P (regno) || regno == AP_FIRST)
- base = GPR_FIRST;
+ frv_packet.num_mems++;
+ }
+}
- else if (FPR_P (regno))
- base = FPR_FIRST;
- else if (ACC_P (regno))
- base = ACC_FIRST;
+/* Update the register state information for an instruction whose
+ body is X. */
- else if (SPR_P (regno))
- return mode == SImode;
+static void
+frv_registers_update (rtx x)
+{
+ regstate_t flags;
- /* Fill in the table. */
- else
- return 0;
+ 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);
+}
- /* Anything smaller than an SI is OK in any word-sized register. */
- if (GET_MODE_SIZE (mode) < 4)
- return 1;
- mask = (GET_MODE_SIZE (mode) / 4) - 1;
- }
- return (((regno - base) & mask) == 0);
- }
+/* Initialize frv_packet for the start of a new packet. */
- return 0;
+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 = GROUP_I; group < NUM_GROUPS;
+ group = (enum frv_insn_group) (group + 1))
+ frv_packet.groups[group].num_insns = 0;
}
-\f
-/* A C expression for the number of consecutive hard registers, starting at
- register number REGNO, required to hold a value of mode MODE.
- On a machine where all registers are exactly one word, a suitable definition
- of this macro is
+/* Likewise for the start of a new basic block. */
- #define HARD_REGNO_NREGS(REGNO, MODE) \
- ((GET_MODE_SIZE (MODE) + UNITS_PER_WORD - 1) \
- / UNITS_PER_WORD)) */
+static void
+frv_start_packet_block (void)
+{
+ state_reset (frv_packet.dfa_state);
+ frv_start_packet ();
+}
-/* On the FRV, make the CC_FP mode take 3 words in the integer registers, so
- that we can build the appropriate instructions to properly reload the
- values. Also, make the byte-sized accumulator guards use one guard
- for each byte. */
-int
-frv_hard_regno_nregs (int regno, enum machine_mode mode)
+/* 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 (ACCG_P (regno))
- return GET_MODE_SIZE (mode);
- else
- return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ if (frv_packet.num_insns > 0)
+ {
+ handle_packet ();
+ state_transition (frv_packet.dfa_state, 0);
+ frv_start_packet ();
+ }
}
-\f
-/* A C expression for the maximum number of consecutive registers of
- class CLASS needed to hold a value of mode MODE.
- This is closely related to the macro `HARD_REGNO_NREGS'. In fact, the value
- of the macro `CLASS_MAX_NREGS (CLASS, MODE)' should be the maximum value of
- `HARD_REGNO_NREGS (REGNO, MODE)' for all REGNO values in the class CLASS.
+/* Return true if INSN can be added to the current packet. Update
+ the DFA state on success. */
- This macro helps control the handling of multiple-word values in
- the reload pass.
+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;
- This declaration is required. */
+ /* 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.
-int
-frv_class_max_nregs (enum reg_class class, enum machine_mode mode)
+ 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)
{
- if (class == ACCG_REGS)
- /* An N-byte value requires N accumulator guards. */
- return GET_MODE_SIZE (mode);
+ 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
- return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+ {
+ insn = emit_insn_after (PATTERN (insn), last);
+ frv_packet.insns[frv_packet.num_insns++] = insn;
+ }
+ packet_group->insns[packet_group->num_insns++] = insn;
}
-\f
-/* A C expression that is nonzero if X is a legitimate constant for an
- immediate operand on the target machine. You can assume that X satisfies
- `CONSTANT_P', so you need not check this. In fact, `1' is a suitable
- definition for this macro on machines where anything `CONSTANT_P' is valid. */
-int
-frv_legitimate_constant_p (rtx x)
+/* If packing is enabled, divide the instructions into packets and
+ return true. Call HANDLE_PACKET for each complete packet. */
+
+static bool
+frv_for_each_packet (void (*handle_packet) (void))
{
- enum machine_mode mode = GET_MODE (x);
+ rtx insn, next_insn;
- /* 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.
+ frv_packet.issue_rate = frv_issue_rate ();
- 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.
+ /* 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;
- 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);
+ /* Set up the initial packing state. */
+ dfa_start ();
+ frv_packet.dfa_state = alloca (state_size ());
- /* All of the integer constants are ok. */
- if (GET_CODE (x) != CONST_DOUBLE)
- return TRUE;
+ frv_start_packet_block ();
+ for (insn = get_insns (); insn != 0; insn = next_insn)
+ {
+ enum rtx_code code;
+ bool eh_insn_p;
- /* double integer constants are ok. */
- if (mode == VOIDmode || mode == DImode)
- return TRUE;
+ code = GET_CODE (insn);
+ next_insn = NEXT_INSN (insn);
- /* 0 is always ok. */
- if (x == CONST0_RTX (mode))
- return TRUE;
+ if (code == CODE_LABEL)
+ {
+ frv_finish_packet (handle_packet);
+ frv_start_packet_block ();
+ }
- /* If floating point is just emulated, allow any constant, since it will be
- constructed in the GPRs. */
- if (!TARGET_HAS_FPRS)
- return TRUE;
+ if (INSN_P (insn))
+ switch (GET_CODE (PATTERN (insn)))
+ {
+ case USE:
+ case CLOBBER:
+ case ADDR_VEC:
+ case ADDR_DIFF_VEC:
+ break;
- if (mode == DFmode && !TARGET_DOUBLE)
- return TRUE;
+ 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);
+ }
- /* Otherwise store the constant away and do a load. */
- return FALSE;
+ /* 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
-/* 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
- such as `GENERAL_REGS'. A value of 4 is the default; other values are
- interpreted relative to that.
-
- It is not required that the cost always equal 2 when FROM is the same as TO;
- on some machines it is expensive to move between registers if they are not
- general registers.
-
- If reload sees an insn consisting of a single `set' between two hard
- registers, and if `REGISTER_MOVE_COST' applied to their classes returns a
- value of 2, reload does not check to ensure that the constraints of the insn
- are met. Setting a cost of other than 2 will allow reload to verify that
- the constraints are met. You should do this if the `movM' pattern's
- constraints do not allow such copying. */
+/* 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. */
-#define HIGH_COST 40
-#define MEDIUM_COST 3
-#define LOW_COST 1
+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;
-int
-frv_register_move_cost (enum reg_class from, enum reg_class to)
+ /* 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;
+}
+
+/* Compare two instructions by their frv_insn_unit. */
+
+static int
+frv_compare_insns (const void *first, const void *second)
{
- switch (from)
- {
- default:
- break;
+ const rtx *const insn1 = (rtx const *) first,
+ *const insn2 = (rtx const *) second;
+ return frv_insn_unit (*insn1) - frv_insn_unit (*insn2);
+}
- case QUAD_REGS:
- case EVEN_REGS:
- case GPR_REGS:
- switch (to)
- {
- default:
- break;
+/* 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. */
- case QUAD_REGS:
- case EVEN_REGS:
- case GPR_REGS:
- return LOW_COST;
+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;
- case FEVEN_REGS:
- case FPR_REGS:
- return LOW_COST;
+ packet_group = &frv_packet.groups[group];
- case LCR_REG:
- case LR_REG:
- case SPR_REGS:
- return LOW_COST;
- }
+ /* Assume no nop is needed. */
+ packet_group->nop = 0;
- case FEVEN_REGS:
- case FPR_REGS:
- switch (to)
- {
- default:
- break;
+ if (packet_group->num_insns == 0)
+ return;
- case QUAD_REGS:
- case EVEN_REGS:
- case GPR_REGS:
- case ACC_REGS:
- case EVEN_ACC_REGS:
- case QUAD_ACC_REGS:
- case ACCG_REGS:
- return MEDIUM_COST;
+ /* Copy insns[] to sorted[]. */
+ memcpy (packet_group->sorted, packet_group->insns,
+ sizeof (rtx) * packet_group->num_insns);
- case FEVEN_REGS:
- case FPR_REGS:
- return LOW_COST;
- }
+ /* 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 LCR_REG:
- case LR_REG:
- case SPR_REGS:
- switch (to)
- {
- default:
- break;
+ /* That's always enough for branch and control insns. */
+ if (group == GROUP_B || group == GROUP_C)
+ return;
- case QUAD_REGS:
- case EVEN_REGS:
- case GPR_REGS:
- return MEDIUM_COST;
- }
+ dfa_size = state_size ();
+ state = alloca (dfa_size);
+ test_state = alloca (dfa_size);
- case ACC_REGS:
- case EVEN_ACC_REGS:
- case QUAD_ACC_REGS:
- case ACCG_REGS:
- switch (to)
- {
- default:
- break;
+ /* 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;
+ }
- case FEVEN_REGS:
- case FPR_REGS:
- return MEDIUM_COST;
+ /* If all the instructions issued in ascending order, we're done. */
+ if (first == packet_group->num_insns)
+ return;
+ /* 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 HIGH_COST;
+ gcc_unreachable ();
}
\f
-/* Implementation of TARGET_ASM_INTEGER. In the FRV case we need to
- use ".picptr" to generate safe relocations for PIC code. We also
- need a fixup entry for aligned (non-debugging) code. */
+/* Sort the current packet into assembly-language order. Set packing
+ flags as appropriate. */
-static bool
-frv_assemble_integer (rtx value, unsigned int size, int aligned_p)
+static void
+frv_reorder_packet (void)
{
- 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 (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];
- const char *p;
+ 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;
- ASM_GENERATE_INTERNAL_LABEL (buf, "LCP", label_num++);
- p = (* targetm.strip_name_encoding) (buf);
+ /* First sort each group individually. */
+ for (group = GROUP_I; group < NUM_GROUPS;
+ group = (enum frv_insn_group) (group + 1))
+ {
+ cursor[group] = 0;
+ frv_sort_insn_group (group);
+ }
- fprintf (asm_out_file, "%s:\n", p);
- fprintf (asm_out_file, "%s\n", FIXUP_SECTION_ASM_OP);
- fprintf (asm_out_file, "\t.picptr\t%s\n", p);
- fprintf (asm_out_file, "\t.previous\n");
- }
- assemble_integer_with_op ("\t.picptr\t", value);
- return true;
- }
- if (!aligned_p)
+ /* 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++)
+ {
+ group = frv_unit_groups[unit];
+ packet_group = &frv_packet.groups[group];
+ if (cursor[group] < packet_group->num_insns)
{
- /* We've set the unaligned SI op to NULL, so we always have to
- handle the unaligned case here. */
- assemble_integer_with_op ("\t.4byte\t", value);
- return true;
+ /* 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 default_assemble_integer (value, size, aligned_p);
-}
-/* Function to set up the backend function structure. */
+ gcc_assert (to == frv_packet.num_insns);
-static struct machine_function *
-frv_init_machine_status (void)
-{
- return ggc_alloc_cleared (sizeof (struct machine_function));
+ /* 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], NULL);
+ SET_PACKING_FLAG (insns[from]);
+ }
}
-\f
-/* Implement TARGET_SCHED_ISSUE_RATE. */
-static 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;
+/* Divide instructions into packets. Reorder the contents of each
+ packet so that they are in the correct assembly-language order.
- case FRV_CPU_FR400:
- return 2;
+ 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. */
- case FRV_CPU_GENERIC:
- case FRV_CPU_FR500:
- case FRV_CPU_TOMCAT:
- return 4;
- }
+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
-/* Update the register state information, to know about which registers are set
- or clobbered. */
+/* See whether we need to add nops to group GROUP in order to
+ make a valid packet. */
static void
-frv_registers_update (rtx x,
- unsigned char reg_state[],
- int modified[],
- int *p_num_mod,
- int flag)
+frv_fill_unused_units (enum frv_insn_group group)
{
- int regno, reg_max;
- rtx reg;
- rtx cond;
- const char *format;
- 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];
- /* 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;
+ /* Sort the instructions into assembly-language order.
+ Use nops to fill slots that are otherwise unused. */
+ frv_sort_insn_group (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;
+ /* 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++;
- /* 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);
+ /* Insert that many nops into the instruction stream. */
+ while (nops-- > 0)
+ frv_insert_nop_in_packet (packet_group->nop);
+}
- /* MEM resets the modification bits. */
- case MEM:
- flag &= ~REGSTATE_MODIFIED;
- break;
+/* Return true if accesses IO1 and IO2 refer to the same doubleword. */
- /* 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;
+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;
- case REG:
- regno = REGNO (x);
- reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
- /* Fall through. */
+ if (io1->var_address != 0 && io2->var_address != 0)
+ return rtx_equal_p (io1->var_address, io2->var_address);
- reg_common:
- if (flag & REGSTATE_MODIFIED)
- {
- flag &= REGSTATE_MASK;
- while (regno < reg_max)
- {
- int rs = reg_state[regno];
+ return false;
+}
- if (flag != rs)
- {
- if ((rs & REGSTATE_MODIFIED) == 0)
- {
- modified[ *p_num_mod ] = regno;
- (*p_num_mod)++;
- }
+/* Return true if operations IO1 and IO2 are guaranteed to complete
+ in order. */
- /* 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);
+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;
- reg_state[regno] = (rs | flag);
- }
- regno++;
- }
- }
- return;
- }
+ /* 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':
- frv_registers_update (XEXP (x, j), reg_state, modified, p_num_mod,
- flag);
- 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)
- frv_registers_update (XVECEXP (x, j, k), reg_state, modified,
- p_num_mod, flag);
- }
- 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 = (enum frv_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;
+/* 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, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ rtx *other = (rtx *) data;
+
+ if (REG_P (x) && *other != 0 && reg_overlap_mentioned_p (x, *other))
+ *other = 0;
}
-\f
-/* Return if any registers in a hard register set were used an insn. */
+/* A note_stores callback for which DATA points to a HARD_REG_SET.
+ Remove every modified register from the set. */
+
+static void
+frv_io_handle_set (rtx x, const_rtx pat ATTRIBUTE_UNUSED, void *data)
+{
+ HARD_REG_SET *set = (HARD_REG_SET *) data;
+ unsigned int regno;
+
+ if (REG_P (x))
+ FOR_EACH_REGNO (regno, x)
+ CLEAR_HARD_REG_BIT (*set, regno);
+}
+
+/* A for_each_rtx callback for which DATA points to a HARD_REG_SET.
+ Add every register in *X to the set. */
static int
-frv_registers_used_p (rtx x, unsigned char reg_state[], int flag)
+frv_io_handle_use_1 (rtx *x, void *data)
{
- int regno, reg_max;
- rtx reg;
- rtx cond;
- rtx dest;
- const char *format;
- int result;
- int length;
- int j;
+ HARD_REG_SET *set = (HARD_REG_SET *) data;
+ unsigned int regno;
- switch (GET_CODE (x))
- {
- default:
- break;
+ if (REG_P (*x))
+ FOR_EACH_REGNO (regno, *x)
+ SET_HARD_REG_BIT (*set, regno);
- /* Skip clobber, that doesn't use the previous value. */
- case CLOBBER:
- return FALSE;
+ return 0;
+}
- /* 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;
- }
+/* A note_stores callback that applies frv_io_handle_use_1 to an
+ entire rhs value. */
- 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;
+static void
+frv_io_handle_use (rtx *x, void *data)
+{
+ for_each_rtx (x, frv_io_handle_use_1, data);
+}
- flag |= ((REGNO (XEXP (cond, 0)) - CR_FIRST)
- | ((GET_CODE (cond) == NE)
- ? REGSTATE_IF_TRUE
- : REGSTATE_IF_FALSE));
+/* Go through block BB looking for membars to remove. There are two
+ cases where intra-block analysis is enough:
- return frv_registers_used_p (XEXP (x, 1), reg_state, flag);
- }
- else
- fatal_insn ("frv_registers_used_p", x);
+ - a membar is redundant if it occurs between two consecutive I/O
+ operations and if those operations are guaranteed to complete
+ in order.
- /* 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;
+ - a membar for a __builtin_read is redundant if the result is
+ used before the next I/O operation is issued.
- case REG:
- regno = REGNO (x);
- reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
- /* Fall through. */
+ 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.
- reg_common:
- while (regno < reg_max)
- {
- int rs = reg_state[regno];
+ Describe the block's first I/O operation in *NEXT_IO. Describe
+ an unknown operation if the block doesn't do any I/O. */
- 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 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;
- regno++;
- }
- return FALSE;
- }
+ /* 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;
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
+ /* 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.
- for (j = 0; j < length; ++j)
- {
- switch (format[j])
- {
- case 'e':
- result = frv_registers_used_p (XEXP (x, j), reg_state, flag);
- if (result != 0)
- return result;
- break;
+ Note that the initialization here should only be needed to
+ suppress warnings. */
+ next_membar = 0;
- case 'V':
- case 'E':
- if (XVEC (x, j) != 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)
{
- int k;
- for (k = 0; k < XVECLEN (x, j); ++k)
+ /* 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
+ {
+ /* 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))
{
- result = frv_registers_used_p (XVECEXP (x, j, k), reg_state,
- flag);
- if (result != 0)
- return result;
+ 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. */
- break;
- }
- }
-
- return 0;
-}
-
-/* Return if any registers in a hard register set were set in an insn. */
-
-static int
-frv_registers_set_p (rtx x, unsigned char reg_state[], int modify_p)
-{
- int regno, reg_max;
- rtx reg;
- rtx cond;
- const char *format;
- int length;
- int j;
-
- switch (GET_CODE (x))
- {
- default:
- break;
+ /* 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;
- case CLOBBER:
- return frv_registers_set_p (XEXP (x, 0), reg_state, TRUE);
+ src = SET_SRC (set);
+ if (GET_CODE (src) == ZERO_EXTEND)
+ src = XEXP (src, 0);
- 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);
+ 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));
- /* MEM resets the modification bits. */
- case MEM:
- modify_p = FALSE;
- break;
+ if (next_membar == *last_membar)
+ *last_membar = 0;
- /* 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;
+ delete_insn (next_membar);
+ next_membar = 0;
+ }
+ }
- case REG:
- regno = REGNO (x);
- reg_max = regno + HARD_REGNO_NREGS (regno, GET_MODE (x));
- /* Fall through. */
+ /* 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);
- reg_common:
- if (modify_p)
- while (regno < reg_max)
- {
- int rs = reg_state[regno];
+ note_uses (&PATTERN (insn), frv_io_handle_use, &used_regs);
+ break;
+ }
+}
- if (rs & REGSTATE_MODIFIED)
- return TRUE;
- regno++;
- }
- return FALSE;
- }
+/* 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_optimize_membar_global (basic_block bb, struct frv_io *first_io,
+ rtx membar)
+{
+ struct frv_io this_io, next_io;
+ edge succ;
+ edge_iterator ei;
- length = GET_RTX_LENGTH (GET_CODE (x));
- format = GET_RTX_FORMAT (GET_CODE (x));
+ /* 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;
- for (j = 0; j < length; ++j)
+ /* 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))
{
- switch (format[j])
- {
- case 'e':
- if (frv_registers_set_p (XEXP (x, j), reg_state, modify_p))
- return TRUE;
- break;
+ 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 ();
- case 'V':
- case 'E':
- if (XVEC (x, j) != 0)
- {
- 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;
- }
- break;
+ frv_extract_membar (&this_io, membar);
+ if (frv_io_fixed_order_p (&this_io, &next_io))
+ {
+ if (dump_file)
+ fprintf (dump_file,
+ ";; [Global] Removing membar %d since order of accesses"
+ " is guaranteed\n", INSN_UID (membar));
- default:
- /* Nothing to do. */
- break;
- }
+ delete_insn (membar);
}
-
- 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. */
+/* Remove redundant membars from the current function. */
static void
-frv_pack_insns (void)
+frv_optimize_membar (void)
{
- 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;
-
- /* Set up the instruction and register states. */
- dfa_start ();
- frv_state = (state_t) xmalloc (state_size ());
- memset (reg_state, REGSTATE_DEAD, sizeof (reg_state));
+ basic_block bb;
+ struct frv_io *first_io;
+ rtx *last_membar;
- /* 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;
+ compute_bb_for_insn ();
+ first_io = XCNEWVEC (struct frv_io, last_basic_block);
+ last_membar = XCNEWVEC (rtx, last_basic_block);
- for (insn = get_insns (); insn != NULL_RTX; insn = NEXT_INSN (insn))
- {
- enum rtx_code code = GET_CODE (insn);
- enum rtx_code pattern_code;
+ FOR_EACH_BB (bb)
+ frv_optimize_membar_local (bb, &first_io[bb->index],
+ &last_membar[bb->index]);
- /* 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;
+ FOR_EACH_BB (bb)
+ if (last_membar[bb->index] != 0)
+ frv_optimize_membar_global (bb, first_io, last_membar[bb->index]);
- for (j = 0; j < FIRST_PSEUDO_REGISTER; j++)
- reg_state[j] &= ~ REGSTATE_LIVE;
+ 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;
- live = NOTE_BASIC_BLOCK (insn)->global_live_at_start;
- EXECUTE_IF_SET_IN_REG_SET(live, 0, j,
- {
- reg_state[j] |= REGSTATE_LIVE;
- });
- }
+/* 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. */
- continue;
- }
+static void
+frv_align_label (void)
+{
+ unsigned int alignment, target, nop;
+ rtx x, last, barrier, label;
- /* Things like labels reset everything. */
- if (!INSN_P (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))
{
- next_start_vliw_p = TRUE;
- continue;
+ unsigned int subalign = 1 << label_to_alignment (x);
+ alignment = MAX (alignment, subalign);
+ label = x;
}
+ if (BARRIER_P (x))
+ barrier = x;
+ }
- /* Clear the VLIW start flag on random USE and CLOBBER insns, which is
- set on the USE insn that precedes 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;
- }
+ /* 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);
- cur_start_vliw_p = next_start_vliw_p;
- next_start_vliw_p = FALSE;
+ /* Advance the address to the end of the current packet. */
+ frv_packet_address += frv_packet.num_insns * 4;
- cur_condjump_p |= next_condjump_p;
- next_condjump_p = 0;
+ /* Work out the target address, after alignment. */
+ target = (frv_packet_address + alignment - 1) & -alignment;
- /* Unconditional branches and calls end the current VLIW insn. */
- if (code == CALL_INSN)
- {
- next_start_vliw_p = TRUE;
+ /* 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;
+ }
- /* 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)
- {
- if (any_condjump_p (insn))
- next_condjump_p = REGSTATE_CONDJUMP;
- else
- next_start_vliw_p = TRUE;
- }
+ /* 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:
- /* 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)
+ 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)
{
- 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;
+ last = emit_insn_after (PATTERN (frv_nops[nop]), last);
+ frv_packet_address += 4;
+ if (frv_num_nops > 1)
+ nop ^= 1;
}
- else
- CLEAR_VLIW_START (insn);
+ }
- /* Record which registers are modified. */
- frv_registers_update (PATTERN (insn), reg_state, modified, &num_mod, 0);
+ frv_packet_address = target;
+}
- /* Process the death notices. */
- for (link = REG_NOTES (insn);
- link != NULL_RTX;
- link = XEXP (link, 1))
- {
- rtx reg = XEXP (link, 0);
+/* Subroutine of frv_reorg, called after each packet has been constructed
+ in frv_packet. */
- 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;
- }
- }
- }
+static void
+frv_reorg_packet (void)
+{
+ frv_fill_unused_units (GROUP_I);
+ frv_fill_unused_units (GROUP_FM);
+ frv_align_label ();
+}
- free (frv_state);
- dfa_finish ();
- return;
+/* 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) \
- lang_hooks.builtin_function ((name), (type), (code), BUILT_IN_MD, NULL, NULL)
+ add_builtin_function ((name), (type), (code), BUILT_IN_MD, NULL, NULL)
struct builtin_description
{
static struct builtin_description bdesc_set[] =
{
- { CODE_FOR_mhdsets, "__MHDSETS", FRV_BUILTIN_MHDSETS, 0, 0 }
+ { CODE_FOR_mhdsets, "__MHDSETS", FRV_BUILTIN_MHDSETS, UNKNOWN, 0 }
};
/* Media intrinsics that take just one argument. */
static struct builtin_description bdesc_1arg[] =
{
- { CODE_FOR_mnot, "__MNOT", FRV_BUILTIN_MNOT, 0, 0 },
- { 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_mnot, "__MNOT", FRV_BUILTIN_MNOT, UNKNOWN, 0 },
+ { CODE_FOR_munpackh, "__MUNPACKH", FRV_BUILTIN_MUNPACKH, UNKNOWN, 0 },
+ { CODE_FOR_mbtoh, "__MBTOH", FRV_BUILTIN_MBTOH, UNKNOWN, 0 },
+ { CODE_FOR_mhtob, "__MHTOB", FRV_BUILTIN_MHTOB, UNKNOWN, 0},
+ { CODE_FOR_mabshs, "__MABSHS", FRV_BUILTIN_MABSHS, UNKNOWN, 0 },
+ { CODE_FOR_scutss, "__SCUTSS", FRV_BUILTIN_SCUTSS, UNKNOWN, 0 }
};
/* Media intrinsics that take two arguments. */
static struct builtin_description bdesc_2arg[] =
{
- { CODE_FOR_mand, "__MAND", FRV_BUILTIN_MAND, 0, 0 },
- { CODE_FOR_mor, "__MOR", FRV_BUILTIN_MOR, 0, 0 },
- { CODE_FOR_mxor, "__MXOR", FRV_BUILTIN_MXOR, 0, 0 },
- { CODE_FOR_maveh, "__MAVEH", FRV_BUILTIN_MAVEH, 0, 0 },
- { CODE_FOR_msaths, "__MSATHS", FRV_BUILTIN_MSATHS, 0, 0 },
- { CODE_FOR_msathu, "__MSATHU", FRV_BUILTIN_MSATHU, 0, 0 },
- { CODE_FOR_maddhss, "__MADDHSS", FRV_BUILTIN_MADDHSS, 0, 0 },
- { CODE_FOR_maddhus, "__MADDHUS", FRV_BUILTIN_MADDHUS, 0, 0 },
- { CODE_FOR_msubhss, "__MSUBHSS", FRV_BUILTIN_MSUBHSS, 0, 0 },
- { CODE_FOR_msubhus, "__MSUBHUS", FRV_BUILTIN_MSUBHUS, 0, 0 },
- { CODE_FOR_mqaddhss, "__MQADDHSS", FRV_BUILTIN_MQADDHSS, 0, 0 },
- { CODE_FOR_mqaddhus, "__MQADDHUS", FRV_BUILTIN_MQADDHUS, 0, 0 },
- { 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_mand, "__MAND", FRV_BUILTIN_MAND, UNKNOWN, 0},
+ { CODE_FOR_mor, "__MOR", FRV_BUILTIN_MOR, UNKNOWN, 0},
+ { CODE_FOR_mxor, "__MXOR", FRV_BUILTIN_MXOR, UNKNOWN, 0},
+ { CODE_FOR_maveh, "__MAVEH", FRV_BUILTIN_MAVEH, UNKNOWN, 0},
+ { CODE_FOR_msaths, "__MSATHS", FRV_BUILTIN_MSATHS, UNKNOWN, 0},
+ { CODE_FOR_msathu, "__MSATHU", FRV_BUILTIN_MSATHU, UNKNOWN, 0},
+ { CODE_FOR_maddhss, "__MADDHSS", FRV_BUILTIN_MADDHSS, UNKNOWN, 0},
+ { CODE_FOR_maddhus, "__MADDHUS", FRV_BUILTIN_MADDHUS, UNKNOWN, 0},
+ { CODE_FOR_msubhss, "__MSUBHSS", FRV_BUILTIN_MSUBHSS, UNKNOWN, 0},
+ { CODE_FOR_msubhus, "__MSUBHUS", FRV_BUILTIN_MSUBHUS, UNKNOWN, 0},
+ { CODE_FOR_mqaddhss, "__MQADDHSS", FRV_BUILTIN_MQADDHSS, UNKNOWN, 0},
+ { CODE_FOR_mqaddhus, "__MQADDHUS", FRV_BUILTIN_MQADDHUS, UNKNOWN, 0},
+ { CODE_FOR_mqsubhss, "__MQSUBHSS", FRV_BUILTIN_MQSUBHSS, UNKNOWN, 0},
+ { CODE_FOR_mqsubhus, "__MQSUBHUS", FRV_BUILTIN_MQSUBHUS, UNKNOWN, 0},
+ { CODE_FOR_mpackh, "__MPACKH", FRV_BUILTIN_MPACKH, UNKNOWN, 0},
+ { CODE_FOR_mcop1, "__Mcop1", FRV_BUILTIN_MCOP1, UNKNOWN, 0},
+ { CODE_FOR_mcop2, "__Mcop2", FRV_BUILTIN_MCOP2, UNKNOWN, 0},
+ { CODE_FOR_mwcut, "__MWCUT", FRV_BUILTIN_MWCUT, UNKNOWN, 0},
+ { CODE_FOR_mqsaths, "__MQSATHS", FRV_BUILTIN_MQSATHS, UNKNOWN, 0},
+ { CODE_FOR_mqlclrhs, "__MQLCLRHS", FRV_BUILTIN_MQLCLRHS, UNKNOWN, 0},
+ { CODE_FOR_mqlmths, "__MQLMTHS", FRV_BUILTIN_MQLMTHS, UNKNOWN, 0},
+ { CODE_FOR_smul, "__SMUL", FRV_BUILTIN_SMUL, UNKNOWN, 0},
+ { CODE_FOR_umul, "__UMUL", FRV_BUILTIN_UMUL, UNKNOWN, 0},
+ { CODE_FOR_addss, "__ADDSS", FRV_BUILTIN_ADDSS, UNKNOWN, 0},
+ { CODE_FOR_subss, "__SUBSS", FRV_BUILTIN_SUBSS, UNKNOWN, 0},
+ { CODE_FOR_slass, "__SLASS", FRV_BUILTIN_SLASS, UNKNOWN, 0},
+ { CODE_FOR_scan, "__SCAN", FRV_BUILTIN_SCAN, UNKNOWN, 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, UNKNOWN, 0},
+ { CODE_FOR_smsss, "__SMSSS", FRV_BUILTIN_SMSSS, UNKNOWN, 0},
+ { CODE_FOR_smu, "__SMU", FRV_BUILTIN_SMU, UNKNOWN, 0}
+};
+
+static struct builtin_description bdesc_prefetches[] =
+{
+ { CODE_FOR_frv_prefetch0, "__data_prefetch0", FRV_BUILTIN_PREFETCH0, UNKNOWN,
+ 0},
+ { CODE_FOR_frv_prefetch, "__data_prefetch", FRV_BUILTIN_PREFETCH, UNKNOWN, 0}
};
/* Media intrinsics that take two arguments, the first being an ACC number. */
static struct builtin_description bdesc_cut[] =
{
- { CODE_FOR_mcut, "__MCUT", FRV_BUILTIN_MCUT, 0, 0 },
- { CODE_FOR_mcutss, "__MCUTSS", FRV_BUILTIN_MCUTSS, 0, 0 },
- { CODE_FOR_mdcutssi, "__MDCUTSSI", FRV_BUILTIN_MDCUTSSI, 0, 0 }
+ { CODE_FOR_mcut, "__MCUT", FRV_BUILTIN_MCUT, UNKNOWN, 0},
+ { CODE_FOR_mcutss, "__MCUTSS", FRV_BUILTIN_MCUTSS, UNKNOWN, 0},
+ { CODE_FOR_mdcutssi, "__MDCUTSSI", FRV_BUILTIN_MDCUTSSI, UNKNOWN, 0}
};
/* Two-argument media intrinsics with an immediate second argument. */
static struct builtin_description bdesc_2argimm[] =
{
- { CODE_FOR_mrotli, "__MROTLI", FRV_BUILTIN_MROTLI, 0, 0 },
- { CODE_FOR_mrotri, "__MROTRI", FRV_BUILTIN_MROTRI, 0, 0 },
- { CODE_FOR_msllhi, "__MSLLHI", FRV_BUILTIN_MSLLHI, 0, 0 },
- { CODE_FOR_msrlhi, "__MSRLHI", FRV_BUILTIN_MSRLHI, 0, 0 },
- { CODE_FOR_msrahi, "__MSRAHI", FRV_BUILTIN_MSRAHI, 0, 0 },
- { CODE_FOR_mexpdhw, "__MEXPDHW", FRV_BUILTIN_MEXPDHW, 0, 0 },
- { CODE_FOR_mexpdhd, "__MEXPDHD", FRV_BUILTIN_MEXPDHD, 0, 0 },
- { CODE_FOR_mdrotli, "__MDROTLI", FRV_BUILTIN_MDROTLI, 0, 0 },
- { CODE_FOR_mcplhi, "__MCPLHI", FRV_BUILTIN_MCPLHI, 0, 0 },
- { CODE_FOR_mcpli, "__MCPLI", FRV_BUILTIN_MCPLI, 0, 0 },
- { CODE_FOR_mhsetlos, "__MHSETLOS", FRV_BUILTIN_MHSETLOS, 0, 0 },
- { 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_mrotli, "__MROTLI", FRV_BUILTIN_MROTLI, UNKNOWN, 0},
+ { CODE_FOR_mrotri, "__MROTRI", FRV_BUILTIN_MROTRI, UNKNOWN, 0},
+ { CODE_FOR_msllhi, "__MSLLHI", FRV_BUILTIN_MSLLHI, UNKNOWN, 0},
+ { CODE_FOR_msrlhi, "__MSRLHI", FRV_BUILTIN_MSRLHI, UNKNOWN, 0},
+ { CODE_FOR_msrahi, "__MSRAHI", FRV_BUILTIN_MSRAHI, UNKNOWN, 0},
+ { CODE_FOR_mexpdhw, "__MEXPDHW", FRV_BUILTIN_MEXPDHW, UNKNOWN, 0},
+ { CODE_FOR_mexpdhd, "__MEXPDHD", FRV_BUILTIN_MEXPDHD, UNKNOWN, 0},
+ { CODE_FOR_mdrotli, "__MDROTLI", FRV_BUILTIN_MDROTLI, UNKNOWN, 0},
+ { CODE_FOR_mcplhi, "__MCPLHI", FRV_BUILTIN_MCPLHI, UNKNOWN, 0},
+ { CODE_FOR_mcpli, "__MCPLI", FRV_BUILTIN_MCPLI, UNKNOWN, 0},
+ { CODE_FOR_mhsetlos, "__MHSETLOS", FRV_BUILTIN_MHSETLOS, UNKNOWN, 0},
+ { CODE_FOR_mhsetloh, "__MHSETLOH", FRV_BUILTIN_MHSETLOH, UNKNOWN, 0},
+ { CODE_FOR_mhsethis, "__MHSETHIS", FRV_BUILTIN_MHSETHIS, UNKNOWN, 0},
+ { CODE_FOR_mhsethih, "__MHSETHIH", FRV_BUILTIN_MHSETHIH, UNKNOWN, 0},
+ { CODE_FOR_mhdseth, "__MHDSETH", FRV_BUILTIN_MHDSETH, UNKNOWN, 0},
+ { CODE_FOR_mqsllhi, "__MQSLLHI", FRV_BUILTIN_MQSLLHI, UNKNOWN, 0},
+ { CODE_FOR_mqsrahi, "__MQSRAHI", FRV_BUILTIN_MQSRAHI, UNKNOWN, 0}
};
/* Media intrinsics that take two arguments and return void, the first argument
static struct builtin_description bdesc_void2arg[] =
{
- { CODE_FOR_mdunpackh, "__MDUNPACKH", FRV_BUILTIN_MDUNPACKH, 0, 0 },
- { CODE_FOR_mbtohe, "__MBTOHE", FRV_BUILTIN_MBTOHE, 0, 0 },
+ { CODE_FOR_mdunpackh, "__MDUNPACKH", FRV_BUILTIN_MDUNPACKH, UNKNOWN, 0},
+ { CODE_FOR_mbtohe, "__MBTOHE", FRV_BUILTIN_MBTOHE, UNKNOWN, 0},
};
/* Media intrinsics that take three arguments, the first being a const_int that
static struct builtin_description bdesc_void3arg[] =
{
- { CODE_FOR_mcpxrs, "__MCPXRS", FRV_BUILTIN_MCPXRS, 0, 0 },
- { CODE_FOR_mcpxru, "__MCPXRU", FRV_BUILTIN_MCPXRU, 0, 0 },
- { CODE_FOR_mcpxis, "__MCPXIS", FRV_BUILTIN_MCPXIS, 0, 0 },
- { CODE_FOR_mcpxiu, "__MCPXIU", FRV_BUILTIN_MCPXIU, 0, 0 },
- { CODE_FOR_mmulhs, "__MMULHS", FRV_BUILTIN_MMULHS, 0, 0 },
- { CODE_FOR_mmulhu, "__MMULHU", FRV_BUILTIN_MMULHU, 0, 0 },
- { CODE_FOR_mmulxhs, "__MMULXHS", FRV_BUILTIN_MMULXHS, 0, 0 },
- { CODE_FOR_mmulxhu, "__MMULXHU", FRV_BUILTIN_MMULXHU, 0, 0 },
- { CODE_FOR_mmachs, "__MMACHS", FRV_BUILTIN_MMACHS, 0, 0 },
- { CODE_FOR_mmachu, "__MMACHU", FRV_BUILTIN_MMACHU, 0, 0 },
- { CODE_FOR_mmrdhs, "__MMRDHS", FRV_BUILTIN_MMRDHS, 0, 0 },
- { CODE_FOR_mmrdhu, "__MMRDHU", FRV_BUILTIN_MMRDHU, 0, 0 },
- { CODE_FOR_mqcpxrs, "__MQCPXRS", FRV_BUILTIN_MQCPXRS, 0, 0 },
- { CODE_FOR_mqcpxru, "__MQCPXRU", FRV_BUILTIN_MQCPXRU, 0, 0 },
- { CODE_FOR_mqcpxis, "__MQCPXIS", FRV_BUILTIN_MQCPXIS, 0, 0 },
- { CODE_FOR_mqcpxiu, "__MQCPXIU", FRV_BUILTIN_MQCPXIU, 0, 0 },
- { CODE_FOR_mqmulhs, "__MQMULHS", FRV_BUILTIN_MQMULHS, 0, 0 },
- { CODE_FOR_mqmulhu, "__MQMULHU", FRV_BUILTIN_MQMULHU, 0, 0 },
- { CODE_FOR_mqmulxhs, "__MQMULXHS", FRV_BUILTIN_MQMULXHS, 0, 0 },
- { CODE_FOR_mqmulxhu, "__MQMULXHU", FRV_BUILTIN_MQMULXHU, 0, 0 },
- { CODE_FOR_mqmachs, "__MQMACHS", FRV_BUILTIN_MQMACHS, 0, 0 },
- { CODE_FOR_mqmachu, "__MQMACHU", FRV_BUILTIN_MQMACHU, 0, 0 },
- { CODE_FOR_mqxmachs, "__MQXMACHS", FRV_BUILTIN_MQXMACHS, 0, 0 },
- { CODE_FOR_mqxmacxhs, "__MQXMACXHS", FRV_BUILTIN_MQXMACXHS, 0, 0 },
- { CODE_FOR_mqmacxhs, "__MQMACXHS", FRV_BUILTIN_MQMACXHS, 0, 0 }
+ { CODE_FOR_mcpxrs, "__MCPXRS", FRV_BUILTIN_MCPXRS, UNKNOWN, 0},
+ { CODE_FOR_mcpxru, "__MCPXRU", FRV_BUILTIN_MCPXRU, UNKNOWN, 0},
+ { CODE_FOR_mcpxis, "__MCPXIS", FRV_BUILTIN_MCPXIS, UNKNOWN, 0},
+ { CODE_FOR_mcpxiu, "__MCPXIU", FRV_BUILTIN_MCPXIU, UNKNOWN, 0},
+ { CODE_FOR_mmulhs, "__MMULHS", FRV_BUILTIN_MMULHS, UNKNOWN, 0},
+ { CODE_FOR_mmulhu, "__MMULHU", FRV_BUILTIN_MMULHU, UNKNOWN, 0},
+ { CODE_FOR_mmulxhs, "__MMULXHS", FRV_BUILTIN_MMULXHS, UNKNOWN, 0},
+ { CODE_FOR_mmulxhu, "__MMULXHU", FRV_BUILTIN_MMULXHU, UNKNOWN, 0},
+ { CODE_FOR_mmachs, "__MMACHS", FRV_BUILTIN_MMACHS, UNKNOWN, 0},
+ { CODE_FOR_mmachu, "__MMACHU", FRV_BUILTIN_MMACHU, UNKNOWN, 0},
+ { CODE_FOR_mmrdhs, "__MMRDHS", FRV_BUILTIN_MMRDHS, UNKNOWN, 0},
+ { CODE_FOR_mmrdhu, "__MMRDHU", FRV_BUILTIN_MMRDHU, UNKNOWN, 0},
+ { CODE_FOR_mqcpxrs, "__MQCPXRS", FRV_BUILTIN_MQCPXRS, UNKNOWN, 0},
+ { CODE_FOR_mqcpxru, "__MQCPXRU", FRV_BUILTIN_MQCPXRU, UNKNOWN, 0},
+ { CODE_FOR_mqcpxis, "__MQCPXIS", FRV_BUILTIN_MQCPXIS, UNKNOWN, 0},
+ { CODE_FOR_mqcpxiu, "__MQCPXIU", FRV_BUILTIN_MQCPXIU, UNKNOWN, 0},
+ { CODE_FOR_mqmulhs, "__MQMULHS", FRV_BUILTIN_MQMULHS, UNKNOWN, 0},
+ { CODE_FOR_mqmulhu, "__MQMULHU", FRV_BUILTIN_MQMULHU, UNKNOWN, 0},
+ { CODE_FOR_mqmulxhs, "__MQMULXHS", FRV_BUILTIN_MQMULXHS, UNKNOWN, 0},
+ { CODE_FOR_mqmulxhu, "__MQMULXHU", FRV_BUILTIN_MQMULXHU, UNKNOWN, 0},
+ { CODE_FOR_mqmachs, "__MQMACHS", FRV_BUILTIN_MQMACHS, UNKNOWN, 0},
+ { CODE_FOR_mqmachu, "__MQMACHU", FRV_BUILTIN_MQMACHU, UNKNOWN, 0},
+ { CODE_FOR_mqxmachs, "__MQXMACHS", FRV_BUILTIN_MQXMACHS, UNKNOWN, 0},
+ { CODE_FOR_mqxmacxhs, "__MQXMACXHS", FRV_BUILTIN_MQXMACXHS, UNKNOWN, 0},
+ { CODE_FOR_mqmacxhs, "__MQMACXHS", FRV_BUILTIN_MQMACXHS, UNKNOWN, 0}
};
/* Media intrinsics that take two accumulator numbers as argument and
static struct builtin_description bdesc_voidacc[] =
{
- { CODE_FOR_maddaccs, "__MADDACCS", FRV_BUILTIN_MADDACCS, 0, 0 },
- { CODE_FOR_msubaccs, "__MSUBACCS", FRV_BUILTIN_MSUBACCS, 0, 0 },
- { CODE_FOR_masaccs, "__MASACCS", FRV_BUILTIN_MASACCS, 0, 0 },
- { CODE_FOR_mdaddaccs, "__MDADDACCS", FRV_BUILTIN_MDADDACCS, 0, 0 },
- { CODE_FOR_mdsubaccs, "__MDSUBACCS", FRV_BUILTIN_MDSUBACCS, 0, 0 },
- { CODE_FOR_mdasaccs, "__MDASACCS", FRV_BUILTIN_MDASACCS, 0, 0 }
+ { CODE_FOR_maddaccs, "__MADDACCS", FRV_BUILTIN_MADDACCS, UNKNOWN, 0},
+ { CODE_FOR_msubaccs, "__MSUBACCS", FRV_BUILTIN_MSUBACCS, UNKNOWN, 0},
+ { CODE_FOR_masaccs, "__MASACCS", FRV_BUILTIN_MASACCS, UNKNOWN, 0},
+ { CODE_FOR_mdaddaccs, "__MDADDACCS", FRV_BUILTIN_MDADDACCS, UNKNOWN, 0},
+ { CODE_FOR_mdsubaccs, "__MDSUBACCS", FRV_BUILTIN_MDSUBACCS, UNKNOWN, 0},
+ { CODE_FOR_mdasaccs, "__MDASACCS", FRV_BUILTIN_MDASACCS, UNKNOWN, 0}
+};
+
+/* 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, UNKNOWN, 0},
+ { CODE_FOR_optional_membar_hi, "__builtin_read16",
+ FRV_BUILTIN_READ16, UNKNOWN, 0},
+ { CODE_FOR_optional_membar_si, "__builtin_read32",
+ FRV_BUILTIN_READ32, UNKNOWN, 0},
+ { CODE_FOR_optional_membar_di, "__builtin_read64",
+ FRV_BUILTIN_READ64, UNKNOWN, 0}
+};
+
+/* Likewise stores. */
+
+static struct builtin_description bdesc_stores[] =
+{
+ { CODE_FOR_optional_membar_qi, "__builtin_write8",
+ FRV_BUILTIN_WRITE8, UNKNOWN, 0},
+ { CODE_FOR_optional_membar_hi, "__builtin_write16",
+ FRV_BUILTIN_WRITE16, UNKNOWN, 0},
+ { CODE_FOR_optional_membar_si, "__builtin_write32",
+ FRV_BUILTIN_WRITE32, UNKNOWN, 0},
+ { CODE_FOR_optional_membar_di, "__builtin_write64",
+ FRV_BUILTIN_WRITE64, UNKNOWN, 0},
};
/* Initialize media builtins. */
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 ("__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 ("__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
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;
reg = gen_rtx_REG (insn_data[icode].operand[opnum].mode,
ACC_FIRST + INTVAL (opval));
if (! (*insn_data[icode].operand[opnum].predicate) (reg, VOIDmode))
- REGNO (reg) = ACCG_FIRST + INTVAL (opval);
+ SET_REGNO (reg, ACCG_FIRST + INTVAL (opval));
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;
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. */
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_normal (CALL_EXPR_ARG (exp, index));
+}
+
+/* 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 (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.
{
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;
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 (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;
/* Expand builtins that take one operand. */
static rtx
-frv_expand_unop_builtin (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);
/* Expand builtins that take two operands. */
static rtx
-frv_expand_binop_builtin (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);
one. */
static rtx
-frv_expand_cut_builtin (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);
/* Expand builtins that take two operands and the second is immediate. */
static rtx
-frv_expand_binopimm_builtin (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;
ints and return void. */
static rtx
-frv_expand_voidbinop_builtin (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 (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 (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_clobber (op0);
+ emit_clobber (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 (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)
number or accumulator guard number as argument and return an SI integer. */
static rtx
-frv_expand_mrdacc_builtin (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 (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;
}
+/* 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
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);
+ 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;
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;
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 (tree decl)
+frv_in_small_data_p (const_tree decl)
{
HOST_WIDE_INT size;
- tree section_name;
+ const_tree section_name;
/* Don't apply the -G flag to internal compiler structures. We
should leave such structures in the main data section, partly
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"))
}
size = int_size_in_bytes (TREE_TYPE (decl));
- if (size > 0 && (unsigned HOST_WIDE_INT) size <= g_switch_value)
+ if (size > 0 && size <= g_switch_value)
return true;
return false;
frv_rtx_costs (rtx x,
int code ATTRIBUTE_UNUSED,
int outer_code ATTRIBUTE_UNUSED,
- int *total)
+ int *total,
+ bool speed ATTRIBUTE_UNUSED)
{
if (outer_code == MEM)
{
switch (code)
{
case CONST_INT:
- /* Make 12 bit integers really cheap. */
- if (IN_RANGE_P (INTVAL (x), -2048, 2047))
+ /* Make 12-bit integers really cheap. */
+ if (IN_RANGE (INTVAL (x), -2048, 2047))
{
*total = 0;
return true;
static void
frv_asm_out_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
{
- ctors_section ();
+ switch_to_section (ctors_section);
assemble_align (POINTER_SIZE);
if (TARGET_FDPIC)
{
- if (!frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1))
- abort ();
+ 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 (rtx symbol, int priority ATTRIBUTE_UNUSED)
{
- dtors_section ();
+ switch_to_section (dtors_section);
assemble_align (POINTER_SIZE);
if (TARGET_FDPIC)
{
- if (!frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1))
- abort ();
+ int ok = frv_assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, 1);
+
+ gcc_assert (ok);
return;
}
assemble_integer_with_op ("\t.picptr\t", symbol);
{
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"