OSDN Git Service

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