OSDN Git Service

* config/arm/arm.c (arm_output_epilogue): Reverse the order of
[pf3gnuchains/gcc-fork.git] / gcc / config / arm / arm.c
index 42f4f17..1c2f2b2 100644 (file)
@@ -1,6 +1,6 @@
 /* Output routines for GCC for ARM.
-   Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-   Free Software Foundation, Inc.
+   Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
+   2002, 2003, 2004  Free Software Foundation, Inc.
    Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
    and Martin Simmons (@harleqn.co.uk).
    More major hacks by Richard Earnshaw (rearnsha@arm.com).
@@ -50,6 +50,7 @@
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
+#include "debug.h"
 
 /* Forward definitions of types.  */
 typedef struct minipool_node    Mnode;
@@ -58,14 +59,16 @@ typedef struct minipool_fixup   Mfix;
 const struct attribute_spec arm_attribute_table[];
 
 /* Forward function declarations.  */
+static arm_stack_offsets *arm_get_frame_offsets (void);
 static void arm_add_gc_roots (void);
-static int arm_gen_constant (enum rtx_code, enum machine_mode, HOST_WIDE_INT,
-                            rtx, rtx, int, int);
+static int arm_gen_constant (enum rtx_code, enum machine_mode, rtx,
+                            HOST_WIDE_INT, rtx, rtx, int, int);
 static unsigned bit_count (unsigned long);
 static int arm_address_register_rtx_p (rtx, int);
-static int arm_legitimate_index_p (enum machine_mode, rtx, int);
+static int arm_legitimate_index_p (enum machine_mode, rtx, RTX_CODE, int);
 static int thumb_base_register_rtx_p (rtx, enum machine_mode, int);
 inline static int thumb_index_register_rtx_p (rtx, int);
+static int thumb_far_jump_used_p (void);
 static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code);
 static rtx emit_multi_reg_push (int);
 static rtx emit_sfm (int, int);
@@ -74,7 +77,6 @@ static bool arm_assemble_integer (rtx, unsigned int, int);
 #endif
 static const char *fp_const_from_val (REAL_VALUE_TYPE *);
 static arm_cc get_arm_condition_code (rtx);
-static void init_fpa_table (void);
 static HOST_WIDE_INT int_log2 (HOST_WIDE_INT);
 static rtx is_jump_table (rtx);
 static const char *output_multi_immediate (rtx *, const char *, const char *,
@@ -85,7 +87,7 @@ static struct machine_function *arm_init_machine_status (void);
 static int number_of_first_bit_set (int);
 static void replace_symbols_in_block (tree, rtx, rtx);
 static void thumb_exit (FILE *, int, rtx);
-static void thumb_pushpop (FILE *, int, int);
+static void thumb_pushpop (FILE *, int, int, int *, int);
 static rtx is_jump_table (rtx);
 static HOST_WIDE_INT get_jump_table_size (rtx);
 static Mnode *move_minipool_fix_forward_ref (Mnode *, Mnode *, HOST_WIDE_INT);
@@ -123,7 +125,10 @@ static void arm_internal_label (FILE *, const char *, unsigned long);
 static void arm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT,
                                 tree);
 static int arm_rtx_costs_1 (rtx, enum rtx_code, enum rtx_code);
-static bool arm_rtx_costs (rtx, int, int, int *);
+static bool arm_slowmul_rtx_costs (rtx, int, int, int *);
+static bool arm_fastmul_rtx_costs (rtx, int, int, int *);
+static bool arm_xscale_rtx_costs (rtx, int, int, int *);
+static bool arm_9e_rtx_costs (rtx, int, int, int *);
 static int arm_address_cost (rtx);
 static bool arm_memory_load_p (rtx);
 static bool arm_cirrus_insn_p (rtx);
@@ -135,6 +140,7 @@ static rtx safe_vector_operand (rtx, enum machine_mode);
 static rtx arm_expand_binop_builtin (enum insn_code, tree, rtx);
 static rtx arm_expand_unop_builtin (enum insn_code, tree, rtx, int);
 static rtx arm_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
+static void emit_constant_insn (rtx cond, rtx pattern);
 
 #ifdef OBJECT_FORMAT_ELF
 static void arm_elf_asm_named_section (const char *, unsigned int);
@@ -149,6 +155,9 @@ static void aof_dump_pic_table (FILE *);
 static void aof_file_start (void);
 static void aof_file_end (void);
 #endif
+static rtx arm_struct_value_rtx (tree, int);
+static void arm_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
+                                       tree, int *, int);
 
 \f
 /* Initialize the GCC target structure.  */
@@ -219,8 +228,9 @@ static void aof_file_end (void);
 #undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
 
+/* This will be overridden in arm_override_options.  */
 #undef  TARGET_RTX_COSTS
-#define TARGET_RTX_COSTS arm_rtx_costs
+#define TARGET_RTX_COSTS arm_slowmul_rtx_costs
 #undef  TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST arm_address_cost
 
@@ -232,6 +242,19 @@ static void aof_file_end (void);
 #undef  TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN arm_expand_builtin
 
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_false
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX arm_struct_value_rtx
+
+#undef  TARGET_SETUP_INCOMING_VARARGS
+#define TARGET_SETUP_INCOMING_VARARGS arm_setup_incoming_varargs
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Obstack for minipool constant handling.  */
@@ -251,17 +274,38 @@ int making_const_table;
    stored from the compare operation.  */
 rtx arm_compare_op0, arm_compare_op1;
 
-/* What type of floating point are we tuning for?  */
-enum fputype arm_fpu_tune;
+/* The processor for which instructions should be scheduled.  */
+enum processor_type arm_tune = arm_none;
 
-/* What type of floating point instructions are available?  */
+/* Which floating point model to use.  */
+enum arm_fp_model arm_fp_model;
+
+/* Which floating point hardware is available.  */
 enum fputype arm_fpu_arch;
 
+/* Which floating point hardware to schedule for.  */
+enum fputype arm_fpu_tune;
+
+/* Whether to use floating point hardware.  */
+enum float_abi_type arm_float_abi;
+
+/* Which ABI to use.  */
+enum arm_abi_type arm_abi;
+
 /* What program mode is the cpu running in? 26-bit mode or 32-bit mode.  */
 enum prog_mode_type arm_prgmode;
 
-/* Set by the -mfp=... option.  */
-const char * target_fp_name = NULL;
+/* Set by the -mfpu=... option.  */
+const char * target_fpu_name = NULL;
+
+/* Set by the -mfpe=... option.  */
+const char * target_fpe_name = NULL;
+
+/* Set by the -mfloat-abi=... option.  */
+const char * target_float_abi_name = NULL;
+
+/* Set by the -mabi=... option.  */
+const char * target_abi_name = NULL;
 
 /* Used to parse -mstructure_size_boundary command line option.  */
 const char * structure_size_string = NULL;
@@ -269,7 +313,7 @@ int    arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
 
 /* Bit values used to identify processor capabilities.  */
 #define FL_CO_PROC    (1 << 0)        /* Has external co-processor bus */
-#define FL_FAST_MULT  (1 << 1)        /* Fast multiply */
+#define FL_ARCH3M     (1 << 1)        /* Extended multiply */
 #define FL_MODE26     (1 << 2)        /* 26-bit mode support */
 #define FL_MODE32     (1 << 3)        /* 32-bit mode support */
 #define FL_ARCH4      (1 << 4)        /* Architecture rel 4 */
@@ -280,26 +324,25 @@ int    arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
 #define FL_ARCH5E     (1 << 9)        /* DSP extensions to v5 */
 #define FL_XSCALE     (1 << 10)              /* XScale */
 #define FL_CIRRUS     (1 << 11)              /* Cirrus/DSP.  */
-#define FL_IWMMXT     (1 << 29)              /* XScale v2 or "Intel Wireless MMX technology".  */
-#define FL_ARCH6J     (1 << 12)       /* Architecture rel 6.  Adds
+#define FL_ARCH6      (1 << 12)       /* Architecture rel 6.  Adds
                                         media instructions.  */
 #define FL_VFPV2      (1 << 13)       /* Vector Floating Point V2.  */
 
+#define FL_IWMMXT     (1 << 29)              /* XScale v2 or "Intel Wireless MMX technology".  */
+
 /* The bits in this mask specify which
    instructions we are allowed to generate.  */
 static unsigned long insn_flags = 0;
 
 /* The bits in this mask specify which instruction scheduling options should
-   be used.  Note - there is an overlap with the FL_FAST_MULT.  For some
-   hardware we want to be able to generate the multiply instructions, but to
-   tune as if they were not present in the architecture.  */
+   be used.  */
 static unsigned long tune_flags = 0;
 
 /* The following are used in the arm.md file as equivalents to bits
    in the above two flag variables.  */
 
-/* Nonzero if this is an "M" variant of the processor.  */
-int arm_fast_multiply = 0;
+/* Nonzero if this chip supports the ARM Architecture 3M extensions.  */
+int arm_arch3m = 0;
 
 /* Nonzero if this chip supports the ARM Architecture 4 extensions.  */
 int arm_arch4 = 0;
@@ -310,6 +353,9 @@ int arm_arch5 = 0;
 /* Nonzero if this chip supports the ARM Architecture 5E extensions.  */
 int arm_arch5e = 0;
 
+/* Nonzero if this chip supports the ARM Architecture 6 extensions.  */
+int arm_arch6 = 0;
+
 /* Nonzero if this chip can benefit from load scheduling.  */
 int arm_ld_sched = 0;
 
@@ -328,9 +374,6 @@ int arm_tune_xscale = 0;
 /* Nonzero if this chip is an ARM6 or an ARM7.  */
 int arm_is_6_or_7 = 0;
 
-/* Nonzero if this chip is a Cirrus/DSP.  */
-int arm_is_cirrus = 0;
-
 /* Nonzero if generating Thumb instructions.  */
 int thumb_code = 0;
 
@@ -374,7 +417,9 @@ static const char * const arm_condition_codes[] =
 struct processors
 {
   const char *const name;
+  enum processor_type core;
   const unsigned long flags;
+  bool (* rtx_costs) (rtx, int, int, int *);
 };
 
 /* Not all of these give usefully different compilation alternatives,
@@ -382,83 +427,35 @@ struct processors
 static const struct processors all_cores[] =
 {
   /* ARM Cores */
-  
-  {"arm2",     FL_CO_PROC | FL_MODE26 },
-  {"arm250",   FL_CO_PROC | FL_MODE26 },
-  {"arm3",     FL_CO_PROC | FL_MODE26 },
-  {"arm6",     FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm60",    FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm600",   FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm610",                FL_MODE26 | FL_MODE32 },
-  {"arm620",   FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm7",     FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  /* arm7m doesn't exist on its own, but only with D, (and I), but
-     those don't alter the code, so arm7m is sometimes used.  */
-  {"arm7m",    FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
-  {"arm7d",    FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm7dm",   FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
-  {"arm7di",   FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm7dmi",  FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
-  {"arm70",    FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm700",   FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm700i",  FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  {"arm710",                FL_MODE26 | FL_MODE32 },
-  {"arm720",                FL_MODE26 | FL_MODE32 },
-  {"arm710c",               FL_MODE26 | FL_MODE32 },
-  {"arm7100",               FL_MODE26 | FL_MODE32 },
-  {"arm7500",               FL_MODE26 | FL_MODE32 },
-  /* Doesn't have an external co-proc, but does have embedded fpa.  */
-  {"arm7500fe",        FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  /* V4 Architecture Processors */
-  {"arm7tdmi", FL_CO_PROC |             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
-  {"arm710t",                           FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
-  {"arm720t",                           FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
-  {"arm740t",                           FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
-  {"arm8",                  FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED },
-  {"arm810",                FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED },
-  {"arm9",                              FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
-  {"arm920",                            FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED },
-  {"arm920t",                           FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
-  {"arm940t",                           FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
-  {"arm9tdmi",                          FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED },
-  {"arm9e",                             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED },
-  {"ep9312",                            FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED |             FL_CIRRUS },
-  {"strongarm",                     FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED | FL_STRONG },
-  {"strongarm110",           FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED | FL_STRONG },
-  {"strongarm1100",          FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED | FL_STRONG },
-  {"strongarm1110",          FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 |            FL_LDSCHED | FL_STRONG },
-  /* V5 Architecture Processors */
-  {"arm10tdmi",                                 FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED             | FL_ARCH5 },
-  {"arm1020t",                          FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED             | FL_ARCH5 },
-  {"arm926ejs",                          FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB                          | FL_ARCH5 | FL_ARCH5E },
-  {"arm1026ejs",                         FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB                          | FL_ARCH5 | FL_ARCH5E },
-  {"xscale",                             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE },
-  {"iwmmxt",                             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE | FL_IWMMXT },
-  /* V6 Architecture Processors */
-  {"arm1136js",                          FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB                          | FL_ARCH5 | FL_ARCH5E | FL_ARCH6J },
-  {"arm1136jfs",                         FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB                          | FL_ARCH5 | FL_ARCH5E | FL_ARCH6J | FL_VFPV2 },
-  {NULL, 0}
+#define ARM_CORE(NAME, FLAGS, COSTS) \
+  {#NAME, arm_none, FLAGS, arm_##COSTS##_rtx_costs},
+#include "arm-cores.def"
+#undef ARM_CORE
+  {NULL, arm_none, 0, NULL}
 };
 
 static const struct processors all_architectures[] =
 {
   /* ARM Architectures */
+  /* We don't specify rtx_costs here as it will be figured out
+     from the core.  */
   
-  { "armv2",     FL_CO_PROC | FL_MODE26 },
-  { "armv2a",    FL_CO_PROC | FL_MODE26 },
-  { "armv3",     FL_CO_PROC | FL_MODE26 | FL_MODE32 },
-  { "armv3m",    FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT },
-  { "armv4",     FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_FAST_MULT | FL_ARCH4 },
+  { "armv2",     arm2,       FL_CO_PROC | FL_MODE26 , NULL},
+  { "armv2a",    arm2,       FL_CO_PROC | FL_MODE26 , NULL},
+  { "armv3",     arm6,       FL_CO_PROC | FL_MODE26 | FL_MODE32 , NULL},
+  { "armv3m",    arm7m,      FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_ARCH3M , NULL},
+  { "armv4",     arm7tdmi,   FL_CO_PROC | FL_MODE26 | FL_MODE32 | FL_ARCH3M | FL_ARCH4 , NULL},
   /* Strictly, FL_MODE26 is a permitted option for v4t, but there are no
      implementations that support it, so we will leave it out for now.  */
-  { "armv4t",    FL_CO_PROC |             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB },
-  { "armv5",     FL_CO_PROC |             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 },
-  { "armv5t",    FL_CO_PROC |             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 },
-  { "armv5te",   FL_CO_PROC |             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E },
-  { "armv6j",    FL_CO_PROC |             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E | FL_ARCH6J },
-  { "ep9312",                            FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_LDSCHED | FL_CIRRUS },
-  {"iwmmxt",                             FL_MODE32 | FL_FAST_MULT | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE | FL_IWMMXT },
-  { NULL, 0 }
+  { "armv4t",    arm7tdmi,   FL_CO_PROC |             FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB , NULL},
+  { "armv5",     arm10tdmi,  FL_CO_PROC |             FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB | FL_ARCH5 , NULL},
+  { "armv5t",    arm10tdmi,  FL_CO_PROC |             FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB | FL_ARCH5 , NULL},
+  { "armv5te",   arm1026ejs, FL_CO_PROC |             FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E , NULL},
+  { "armv6",     arm1136js,  FL_CO_PROC |             FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E | FL_ARCH6 , NULL},
+  { "armv6j",    arm1136js,  FL_CO_PROC |             FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB | FL_ARCH5 | FL_ARCH5E | FL_ARCH6 , NULL},
+  { "ep9312",   ep9312,                              FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_LDSCHED | FL_CIRRUS , NULL},
+  {"iwmmxt",     iwmmxt,                              FL_MODE32 | FL_ARCH3M | FL_ARCH4 | FL_THUMB | FL_LDSCHED | FL_STRONG | FL_ARCH5 | FL_ARCH5E | FL_XSCALE | FL_IWMMXT , NULL},
+  { NULL, arm_none, 0 , NULL}
 };
 
 /* This is a magic structure.  The 'string' field is magically filled in
@@ -473,6 +470,74 @@ struct arm_cpu_select arm_select[] =
   { NULL,      "-mtune=",      all_cores }
 };
 
+struct fpu_desc
+{
+  const char * name;
+  enum fputype fpu;
+};
+
+
+/* Available values for for -mfpu=.  */
+
+static const struct fpu_desc all_fpus[] =
+{
+  {"fpa",      FPUTYPE_FPA},
+  {"fpe2",     FPUTYPE_FPA_EMU2},
+  {"fpe3",     FPUTYPE_FPA_EMU2},
+  {"maverick", FPUTYPE_MAVERICK},
+  {"vfp",      FPUTYPE_VFP}
+};
+
+
+/* Floating point models used by the different hardware.
+   See fputype in arm.h.  */
+
+static const enum fputype fp_model_for_fpu[] =
+{
+  /* No FP hardware.  */
+  ARM_FP_MODEL_UNKNOWN,                /* FPUTYPE_NONE  */
+  ARM_FP_MODEL_FPA,            /* FPUTYPE_FPA  */
+  ARM_FP_MODEL_FPA,            /* FPUTYPE_FPA_EMU2  */
+  ARM_FP_MODEL_FPA,            /* FPUTYPE_FPA_EMU3  */
+  ARM_FP_MODEL_MAVERICK,       /* FPUTYPE_MAVERICK  */
+  ARM_FP_MODEL_VFP             /* FPUTYPE_VFP  */
+};
+
+
+struct float_abi
+{
+  const char * name;
+  enum float_abi_type abi_type;
+};
+
+
+/* Available values for -mfloat-abi=.  */
+
+static const struct float_abi all_float_abis[] =
+{
+  {"soft",     ARM_FLOAT_ABI_SOFT},
+  {"softfp",   ARM_FLOAT_ABI_SOFTFP},
+  {"hard",     ARM_FLOAT_ABI_HARD}
+};
+
+
+struct abi_name
+{
+  const char *name;
+  enum arm_abi_type abi_type;
+};
+
+
+/* Available values for -mabi=.  */
+
+static const struct abi_name arm_all_abis[] =
+{
+  {"apcs-gnu",    ARM_ABI_APCS},
+  {"atpcs",   ARM_ABI_ATPCS},
+  {"aapcs",   ARM_ABI_AAPCS},
+  {"iwmmxt",  ARM_ABI_IWMMXT}
+};
+
 /* Return the number of bits set in VALUE.  */
 static unsigned
 bit_count (unsigned long value)
@@ -494,7 +559,7 @@ void
 arm_override_options (void)
 {
   unsigned i;
-  
+
   /* Set up the flags based on the cpu/architecture selected by the user.  */
   for (i = ARRAY_SIZE (arm_select); i--;)
     {
@@ -507,9 +572,19 @@ arm_override_options (void)
           for (sel = ptr->processors; sel->name != NULL; sel++)
             if (streq (ptr->string, sel->name))
               {
-               if (i == 2)
-                 tune_flags = sel->flags;
-               else
+               /* Determine the processor core for which we should
+                  tune code-generation.  */
+               if (/* -mcpu= is a sensible default.  */
+                   i == 0
+                   /* If -march= is used, and -mcpu= has not been used,
+                      assume that we should tune for a representative
+                      CPU from that architecture.  */
+                   || i == 1
+                   /* -mtune= overrides -mcpu= and -march=.  */
+                   || i == 2)
+                 arm_tune = (enum processor_type) (sel - ptr->processors);
+
+               if (i != 2)
                  {
                    /* If we have been given an architecture and a processor
                       make sure that they are compatible.  We only generate
@@ -556,10 +631,10 @@ arm_override_options (void)
        { TARGET_CPU_xscale,    "xscale" },
        { TARGET_CPU_ep9312,    "ep9312" },
        { TARGET_CPU_iwmmxt,    "iwmmxt" },
-       { TARGET_CPU_arm926ej_s, "arm926ej-s" },
-       { TARGET_CPU_arm1026ej_s, "arm1026ej-s" },
-       { TARGET_CPU_arm1136j_s, "arm1136j_s" },
-       { TARGET_CPU_arm1136jf_s, "arm1136jf_s" },
+       { TARGET_CPU_arm926ejs, "arm926ejs" },
+       { TARGET_CPU_arm1026ejs, "arm1026ejs" },
+       { TARGET_CPU_arm1136js, "arm1136js" },
+       { TARGET_CPU_arm1136jfs, "arm1136jfs" },
        { TARGET_CPU_generic,   "arm" },
        { 0, 0 }
       };
@@ -583,7 +658,7 @@ arm_override_options (void)
        abort ();
 
       insn_flags = sel->flags;
-      
+
       /* Now check to see if the user has specified some command line
         switch that require certain abilities from the cpu.  */
       sought = 0;
@@ -653,12 +728,17 @@ arm_override_options (void)
 
          insn_flags = sel->flags;
        }
+      if (arm_tune == arm_none)
+       arm_tune = (enum processor_type) (sel - all_cores);
     }
   
-  /* If tuning has not been specified, tune for whichever processor or
-     architecture has been selected.  */
-  if (tune_flags == 0)
-    tune_flags = insn_flags;
+  /* The processor for which we should tune should now have been
+     chosen.  */
+  if (arm_tune == arm_none)
+    abort ();
+  
+  tune_flags = all_cores[(int)arm_tune].flags;
+  targetm.rtx_costs = all_cores[(int)arm_tune].rtx_costs;
 
   /* Make sure that the processor choice does not conflict with any of the
      other command line choices.  */
@@ -747,79 +827,148 @@ arm_override_options (void)
     warning ("passing floating point arguments in fp regs not yet supported");
   
   /* Initialize boolean versions of the flags, for use in the arm.md file.  */
-  arm_fast_multiply = (insn_flags & FL_FAST_MULT) != 0;
-  arm_arch4         = (insn_flags & FL_ARCH4) != 0;
-  arm_arch5         = (insn_flags & FL_ARCH5) != 0;
-  arm_arch5e        = (insn_flags & FL_ARCH5E) != 0;
-  arm_arch_xscale     = (insn_flags & FL_XSCALE) != 0;
+  arm_arch3m = (insn_flags & FL_ARCH3M) != 0;
+  arm_arch4 = (insn_flags & FL_ARCH4) != 0;
+  arm_arch5 = (insn_flags & FL_ARCH5) != 0;
+  arm_arch5e = (insn_flags & FL_ARCH5E) != 0;
+  arm_arch6 = (insn_flags & FL_ARCH6) != 0;
+  arm_arch_xscale = (insn_flags & FL_XSCALE) != 0;
+
+  arm_ld_sched = (tune_flags & FL_LDSCHED) != 0;
+  arm_is_strong = (tune_flags & FL_STRONG) != 0;
+  thumb_code = (TARGET_ARM == 0);
+  arm_is_6_or_7 = (((tune_flags & (FL_MODE26 | FL_MODE32))
+                   && !(tune_flags & FL_ARCH4))) != 0;
+  arm_tune_xscale = (tune_flags & FL_XSCALE) != 0;
+  arm_arch_iwmmxt = (insn_flags & FL_IWMMXT) != 0;
+
+  if (target_abi_name)
+    {
+      for (i = 0; i < ARRAY_SIZE (arm_all_abis); i++)
+       {
+         if (streq (arm_all_abis[i].name, target_abi_name))
+           {
+             arm_abi = arm_all_abis[i].abi_type;
+             break;
+           }
+       }
+      if (i == ARRAY_SIZE (arm_all_abis))
+       error ("invalid ABI option: -mabi=%s", target_abi_name);
+    }
+  else
+    arm_abi = ARM_DEFAULT_ABI;
 
-  arm_ld_sched      = (tune_flags & FL_LDSCHED) != 0;
-  arm_is_strong     = (tune_flags & FL_STRONG) != 0;
-  thumb_code       = (TARGET_ARM == 0);
-  arm_is_6_or_7     = (((tune_flags & (FL_MODE26 | FL_MODE32))
-                      && !(tune_flags & FL_ARCH4))) != 0;
-  arm_tune_xscale       = (tune_flags & FL_XSCALE) != 0;
-  arm_is_cirrus            = (tune_flags & FL_CIRRUS) != 0;
-  arm_arch_iwmmxt   = (insn_flags & FL_IWMMXT) != 0;
+  if (TARGET_IWMMXT && !ARM_DOUBLEWORD_ALIGN)
+    error ("iwmmxt requires an AAPCS compatible ABI for proper operation");
 
-  if (TARGET_IWMMXT && (! TARGET_ATPCS))
-    target_flags |= ARM_FLAG_ATPCS;    
+  if (TARGET_IWMMXT_ABI && !TARGET_IWMMXT)
+    error ("iwmmxt abi requires an iwmmxt capable cpu");
 
-  if (arm_is_cirrus)
+  arm_fp_model = ARM_FP_MODEL_UNKNOWN;
+  if (target_fpu_name == NULL && target_fpe_name != NULL)
     {
-      arm_fpu_tune = FPUTYPE_MAVERICK;
-
-      /* Ignore -mhard-float if -mcpu=ep9312.  */
-      if (TARGET_HARD_FLOAT)
-       target_flags ^= ARM_FLAG_SOFT_FLOAT;
+      if (streq (target_fpe_name, "2"))
+       target_fpu_name = "fpe2";
+      else if (streq (target_fpe_name, "3"))
+       target_fpu_name = "fpe3";
+      else
+       error ("invalid floating point emulation option: -mfpe=%s",
+              target_fpe_name);
+    }
+  if (target_fpu_name != NULL)
+    {
+      /* The user specified a FPU.  */
+      for (i = 0; i < ARRAY_SIZE (all_fpus); i++)
+       {
+         if (streq (all_fpus[i].name, target_fpu_name))
+           {
+             arm_fpu_arch = all_fpus[i].fpu;
+             arm_fpu_tune = arm_fpu_arch;
+             arm_fp_model = fp_model_for_fpu[arm_fpu_arch];
+             break;
+           }
+       }
+      if (arm_fp_model == ARM_FP_MODEL_UNKNOWN)
+       error ("invalid floating point option: -mfpu=%s", target_fpu_name);
     }
   else
-    /* Default value for floating point code... if no co-processor
-       bus, then schedule for emulated floating point.  Otherwise,
-       assume the user has an FPA.
-       Note: this does not prevent use of floating point instructions,
-       -msoft-float does that.  */
-    arm_fpu_tune = (tune_flags & FL_CO_PROC) ? FPUTYPE_FPA : FPUTYPE_FPA_EMU3;
-  
-  if (target_fp_name)
     {
-      if (streq (target_fp_name, "2"))
+#ifdef FPUTYPE_DEFAULT
+      /* Use the default is it is specified for this platform.  */
+      arm_fpu_arch = FPUTYPE_DEFAULT;
+      arm_fpu_tune = FPUTYPE_DEFAULT;
+#else
+      /* Pick one based on CPU type.  */
+      if ((insn_flags & FL_VFP) != 0)
+       arm_fpu_arch = FPUTYPE_VFP;
+      else if (insn_flags & FL_CIRRUS)
+       arm_fpu_arch = FPUTYPE_MAVERICK;
+      else
        arm_fpu_arch = FPUTYPE_FPA_EMU2;
-      else if (streq (target_fp_name, "3"))
-       arm_fpu_arch = FPUTYPE_FPA_EMU3;
+#endif
+      if (tune_flags & FL_CO_PROC && arm_fpu_arch == FPUTYPE_FPA_EMU2)
+       arm_fpu_tune = FPUTYPE_FPA;
       else
-       error ("invalid floating point emulation option: -mfpe-%s",
-              target_fp_name);
+       arm_fpu_tune = arm_fpu_arch;
+      arm_fp_model = fp_model_for_fpu[arm_fpu_arch];
+      if (arm_fp_model == ARM_FP_MODEL_UNKNOWN)
+       abort ();
+    }
+
+  if (target_float_abi_name != NULL)
+    {
+      /* The user specified a FP ABI.  */
+      for (i = 0; i < ARRAY_SIZE (all_float_abis); i++)
+       {
+         if (streq (all_float_abis[i].name, target_float_abi_name))
+           {
+             arm_float_abi = all_float_abis[i].abi_type;
+             break;
+           }
+       }
+      if (i == ARRAY_SIZE (all_float_abis))
+       error ("invalid floating point abi: -mfloat-abi=%s",
+              target_float_abi_name);
     }
   else
-    arm_fpu_arch = FPUTYPE_DEFAULT;
-  
-  if (TARGET_FPE)
     {
-      if (arm_fpu_tune == FPUTYPE_FPA_EMU3)
-       arm_fpu_tune = FPUTYPE_FPA_EMU2;
-      else if (arm_fpu_tune == FPUTYPE_MAVERICK)
-       warning ("-mfpe switch not supported by ep9312 target cpu - ignored.");
-      else if (arm_fpu_tune != FPUTYPE_FPA)
-       arm_fpu_tune = FPUTYPE_FPA_EMU2;
+      /* Use soft-float target flag.  */
+      if (target_flags & ARM_FLAG_SOFT_FLOAT)
+       arm_float_abi = ARM_FLOAT_ABI_SOFT;
+      else
+       arm_float_abi = ARM_FLOAT_ABI_HARD;
     }
+
+  if (arm_float_abi == ARM_FLOAT_ABI_SOFTFP)
+    sorry ("-mfloat-abi=softfp");
+  /* If soft-float is specified then don't use FPU.  */
+  if (TARGET_SOFT_FLOAT)
+    arm_fpu_arch = FPUTYPE_NONE;
   
   /* For arm2/3 there is no need to do any scheduling if there is only
      a floating point emulator, or we are doing software floating-point.  */
-  if ((TARGET_SOFT_FLOAT || arm_fpu_tune != FPUTYPE_FPA)
+  if ((TARGET_SOFT_FLOAT
+       || arm_fpu_tune == FPUTYPE_FPA_EMU2
+       || arm_fpu_tune == FPUTYPE_FPA_EMU3)
       && (tune_flags & FL_MODE32) == 0)
     flag_schedule_insns = flag_schedule_insns_after_reload = 0;
   
   arm_prgmode = TARGET_APCS_32 ? PROG_MODE_PROG32 : PROG_MODE_PROG26;
   
+  /* Override the default structure alignment for AAPCS ABI.  */
+  if (arm_abi == ARM_ABI_AAPCS)
+    arm_structure_size_boundary = 8;
+
   if (structure_size_string != NULL)
     {
       int size = strtol (structure_size_string, NULL, 0);
-      
-      if (size == 8 || size == 32)
+
+      if (size == 8 || size == 32
+         || (ARM_DOUBLEWORD_ALIGN && size == 64))
        arm_structure_size_boundary = size;
       else
-       warning ("structure size boundary can only be set to 8 or 32");
+       warning ("structure size boundary can only be set to %s",
+                ARM_DOUBLEWORD_ALIGN ? "8, 32 or 64": "8 or 32");
     }
 
   if (arm_pic_register_string != NULL)
@@ -845,23 +994,37 @@ arm_override_options (void)
       flag_schedule_insns = 0;
     }
 
-  /* If optimizing for space, don't synthesize constants.
-     For processors with load scheduling, it never costs more than 2 cycles
-     to load a constant, and the load scheduler may well reduce that to 1.  */
-  if (optimize_size || (tune_flags & FL_LDSCHED))
-    arm_constant_limit = 1;
-  
-  if (arm_tune_xscale)
-    arm_constant_limit = 2;
-
-  /* If optimizing for size, bump the number of instructions that we
-     are prepared to conditionally execute (even on a StrongARM). 
-     Otherwise for the StrongARM, which has early execution of branches,
-     a sequence that is worth skipping is shorter.  */
   if (optimize_size)
-    max_insns_skipped = 6;
-  else if (arm_is_strong)
-    max_insns_skipped = 3;
+    {
+      /* There's some dispute as to whether this should be 1 or 2.  However,
+        experiments seem to show that in pathological cases a setting of
+        1 degrades less severely than a setting of 2.  This could change if
+        other parts of the compiler change their behavior.  */
+      arm_constant_limit = 1;
+
+      /* If optimizing for size, bump the number of instructions that we
+         are prepared to conditionally execute (even on a StrongARM).  */
+      max_insns_skipped = 6;
+    }
+  else
+    {
+      /* For processors with load scheduling, it never costs more than
+         2 cycles to load a constant, and the load scheduler may well
+        reduce that to 1.  */
+      if (tune_flags & FL_LDSCHED)
+        arm_constant_limit = 1;
+
+      /* On XScale the longer latency of a load makes it more difficult
+         to achieve a good schedule, so it's faster to synthesize
+        constants that can be done in two insns.  */
+      if (arm_tune_xscale)
+        arm_constant_limit = 2;
+
+      /* StrongARM has early execution of branches, so a sequence
+         that is worth skipping is shorter.  */
+      if (arm_is_strong)
+        max_insns_skipped = 3;
+    }
 
   /* Register global variables with the garbage collector.  */
   arm_add_gc_roots ();
@@ -988,23 +1151,23 @@ arm_current_func_type (void)
   return cfun->machine->func_type;
 }
 \f
-/* Return 1 if it is possible to return using a single instruction.  */
+/* Return 1 if it is possible to return using a single instruction.  
+   If SIBLING is non-null, this is a test for a return before a sibling
+   call.  SIBLING is the call insn, so we can examine its register usage.  */
 
 int
-use_return_insn (int iscond)
+use_return_insn (int iscond, rtx sibling)
 {
   int regno;
   unsigned int func_type;
   unsigned long saved_int_regs;
+  unsigned HOST_WIDE_INT stack_adjust;
+  arm_stack_offsets *offsets;
 
   /* Never use a return instruction before reload has run.  */
   if (!reload_completed)
     return 0;
 
-  /* We need two instructions when there's a frame pointer. */
-  if (frame_pointer_needed)
-    return 0;
-  
   func_type = arm_current_func_type ();
 
   /* Naked functions and volatile functions need special
@@ -1015,19 +1178,62 @@ use_return_insn (int iscond)
   /* So do interrupt functions that use the frame pointer.  */
   if (IS_INTERRUPT (func_type) && frame_pointer_needed)
     return 0;
-  
+
+  offsets = arm_get_frame_offsets ();
+  stack_adjust = offsets->outgoing_args - offsets->saved_regs;
+
   /* As do variadic functions.  */
   if (current_function_pretend_args_size
       || cfun->machine->uses_anonymous_args
-      /* Of if the function calls __builtin_eh_return () */
+      /* Or if the function calls __builtin_eh_return () */
       || ARM_FUNC_TYPE (func_type) == ARM_FT_EXCEPTION_HANDLER
-      /* Or if there is no frame pointer and there is a stack adjustment.  */
-      || ((arm_get_frame_size () + current_function_outgoing_args_size != 0)
-         && !frame_pointer_needed))
+      /* Or if the function calls alloca */
+      || current_function_calls_alloca
+      /* Or if there is a stack adjustment.  However, if the stack pointer
+        is saved on the stack, we can use a pre-incrementing stack load.  */
+      || !(stack_adjust == 0 || (frame_pointer_needed && stack_adjust == 4)))
     return 0;
 
   saved_int_regs = arm_compute_save_reg_mask ();
 
+  /* Unfortunately, the insn
+
+       ldmib sp, {..., sp, ...}
+
+     triggers a bug on most SA-110 based devices, such that the stack
+     pointer won't be correctly restored if the instruction takes a
+     page fault.  We work around this problem by popping r3 along with
+     the other registers, since that is never slower than executing
+     another instruction.  
+
+     We test for !arm_arch5 here, because code for any architecture
+     less than this could potentially be run on one of the buggy
+     chips.  */
+  if (stack_adjust == 4 && !arm_arch5)
+    {
+      /* Validate that r3 is a call-clobbered register (always true in
+        the default abi) ...  */
+      if (!call_used_regs[3])
+       return 0;
+
+      /* ... that it isn't being used for a return value (always true
+        until we implement return-in-regs), or for a tail-call
+        argument ...  */
+      if (sibling)
+       {
+         if (GET_CODE (sibling) != CALL_INSN)
+           abort ();
+
+         if (find_regno_fusage (sibling, USE, 3))
+           return 0;
+       }
+
+      /* ... and that there are no call-saved registers in r0-r2
+        (always true in the default ABI).  */
+      if (saved_int_regs & 0x7)
+       return 0;
+    }
+
   /* Can't be done if interworking with Thumb, and any registers have been
      stacked.  */
   if (TARGET_INTERWORK && saved_int_regs != 0)
@@ -1053,8 +1259,14 @@ use_return_insn (int iscond)
 
   /* Can't be done if any of the FPA regs are pushed,
      since this also requires an insn.  */
-  if (TARGET_HARD_FLOAT)
-    for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++)
+  if (TARGET_HARD_FLOAT && TARGET_FPA)
+    for (regno = FIRST_FPA_REGNUM; regno <= LAST_FPA_REGNUM; regno++)
+      if (regs_ever_live[regno] && !call_used_regs[regno])
+       return 0;
+
+  /* Likewise VFP regs.  */
+  if (TARGET_HARD_FLOAT && TARGET_VFP)
+    for (regno = FIRST_VFP_REGNUM; regno <= LAST_VFP_REGNUM; regno++)
       if (regs_ever_live[regno] && !call_used_regs[regno])
        return 0;
 
@@ -1134,9 +1346,16 @@ const_ok_for_op (HOST_WIDE_INT i, enum rtx_code code)
    Return value is the number of insns emitted.  */
 
 int
-arm_split_constant (enum rtx_code code, enum machine_mode mode,
+arm_split_constant (enum rtx_code code, enum machine_mode mode, rtx insn,
                    HOST_WIDE_INT val, rtx target, rtx source, int subtargets)
 {
+  rtx cond;
+
+  if (insn && GET_CODE (PATTERN (insn)) == COND_EXEC)
+    cond = COND_EXEC_TEST (PATTERN (insn));
+  else
+    cond = NULL_RTX;
+
   if (subtargets || code == SET
       || (GET_CODE (target) == REG && GET_CODE (source) == REG
          && REGNO (target) != REGNO (source)))
@@ -1151,7 +1370,9 @@ arm_split_constant (enum rtx_code code, enum machine_mode mode,
         Ref: gcc -O1 -mcpu=strongarm gcc.c-torture/compile/980506-2.c
       */
       if (!after_arm_reorg
-         && (arm_gen_constant (code, mode, val, target, source, 1, 0)
+         && !cond
+         && (arm_gen_constant (code, mode, NULL_RTX, val, target, source, 
+                               1, 0)
              > arm_constant_limit + (code != SET)))
        {
          if (code == SET)
@@ -1173,13 +1394,14 @@ arm_split_constant (enum rtx_code code, enum machine_mode mode,
                                        gen_rtx_MINUS (mode, temp, source)));
              else
                emit_insn (gen_rtx_SET (VOIDmode, target,
-                                       gen_rtx (code, mode, source, temp)));
+                                       gen_rtx_fmt_ee (code, mode, source, temp)));
              return 2;
            }
        }
     }
 
-  return arm_gen_constant (code, mode, val, target, source, subtargets, 1);
+  return arm_gen_constant (code, mode, cond, val, target, source, subtargets, 
+                          1);
 }
 
 static int
@@ -1209,11 +1431,23 @@ count_insns_for_constant (HOST_WIDE_INT remainder, int i)
   return num_insns;
 }
 
+/* Emit an instruction with the indicated PATTERN.  If COND is
+   non-NULL, conditionalize the execution of the instruction on COND
+   being true.  */
+
+static void
+emit_constant_insn (rtx cond, rtx pattern)
+{
+  if (cond)
+    pattern = gen_rtx_COND_EXEC (VOIDmode, copy_rtx (cond), pattern);
+  emit_insn (pattern);
+}
+
 /* As above, but extra parameter GENERATE which, if clear, suppresses
    RTL generation.  */
 
 static int
-arm_gen_constant (enum rtx_code code, enum machine_mode mode,
+arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
                  HOST_WIDE_INT val, rtx target, rtx source, int subtargets,
                  int generate)
 {
@@ -1251,8 +1485,9 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
       if (remainder == 0xffffffff)
        {
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target,
-                                   GEN_INT (ARM_SIGN_EXTEND (val))));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target,
+                                            GEN_INT (ARM_SIGN_EXTEND (val))));
          return 1;
        }
       if (remainder == 0)
@@ -1260,7 +1495,8 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
          if (reload_completed && rtx_equal_p (target, source))
            return 0;
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target, source));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target, source));
          return 1;
        }
       break;
@@ -1269,7 +1505,8 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
       if (remainder == 0)
        {
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target, const0_rtx));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target, const0_rtx));
          return 1;
        }
       if (remainder == 0xffffffff)
@@ -1277,7 +1514,8 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
          if (reload_completed && rtx_equal_p (target, source))
            return 0;
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target, source));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target, source));
          return 1;
        }
       can_invert = 1;
@@ -1289,14 +1527,16 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
          if (reload_completed && rtx_equal_p (target, source))
            return 0;
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target, source));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target, source));
          return 1;
        }
       if (remainder == 0xffffffff)
        {
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target,
-                                   gen_rtx_NOT (mode, source)));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target,
+                                            gen_rtx_NOT (mode, source)));
          return 1;
        }
 
@@ -1309,16 +1549,18 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
       if (remainder == 0)
        {
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target,
-                                   gen_rtx_NEG (mode, source)));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target,
+                                            gen_rtx_NEG (mode, source)));
          return 1;
        }
       if (const_ok_for_arm (val))
        {
          if (generate)
-           emit_insn (gen_rtx_SET (VOIDmode, target, 
-                                   gen_rtx_MINUS (mode, GEN_INT (val),
-                                                  source)));
+           emit_constant_insn (cond,
+                               gen_rtx_SET (VOIDmode, target, 
+                                            gen_rtx_MINUS (mode, GEN_INT (val),
+                                                           source)));
          return 1;
        }
       can_negate = 1;
@@ -1335,10 +1577,12 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
       || (can_invert && const_ok_for_arm (~val)))
     {
       if (generate)
-       emit_insn (gen_rtx_SET (VOIDmode, target,
-                               (source ? gen_rtx (code, mode, source,
-                                                  GEN_INT (val))
-                                : GEN_INT (val))));
+       emit_constant_insn (cond,
+                           gen_rtx_SET (VOIDmode, target,
+                                        (source 
+                                         ? gen_rtx_fmt_ee (code, mode, source,
+                                                           GEN_INT (val))
+                                         : GEN_INT (val))));
       return 1;
     }
 
@@ -1391,10 +1635,12 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
              if (generate)
                {
                  rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
-                 emit_insn (gen_rtx_SET (VOIDmode, new_src, 
-                                         GEN_INT (temp1)));
-                 emit_insn (gen_ashrsi3 (target, new_src, 
-                                         GEN_INT (set_sign_bit_copies - 1)));
+                 emit_constant_insn (cond,
+                                     gen_rtx_SET (VOIDmode, new_src, 
+                                                  GEN_INT (temp1)));
+                 emit_constant_insn (cond,
+                                     gen_ashrsi3 (target, new_src, 
+                                                  GEN_INT (set_sign_bit_copies - 1)));
                }
              return 2;
            }
@@ -1406,10 +1652,12 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
              if (generate)
                {
                  rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
-                 emit_insn (gen_rtx_SET (VOIDmode, new_src,
-                                         GEN_INT (temp1)));
-                 emit_insn (gen_ashrsi3 (target, new_src, 
-                                         GEN_INT (set_sign_bit_copies - 1)));
+                 emit_constant_insn (cond,
+                                     gen_rtx_SET (VOIDmode, new_src,
+                                                  GEN_INT (temp1)));
+                 emit_constant_insn (cond,
+                                     gen_ashrsi3 (target, new_src, 
+                                                  GEN_INT (set_sign_bit_copies - 1)));
                }
              return 2;
            }
@@ -1434,16 +1682,18 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
                  rtx new_src = (subtargets
                                 ? (generate ? gen_reg_rtx (mode) : NULL_RTX)
                                 : target);
-                 insns = arm_gen_constant (code, mode, temp2, new_src,
+                 insns = arm_gen_constant (code, mode, cond, temp2, new_src,
                                            source, subtargets, generate);
                  source = new_src;
                  if (generate)
-                   emit_insn (gen_rtx_SET
-                              (VOIDmode, target,
-                               gen_rtx_IOR (mode,
-                                            gen_rtx_ASHIFT (mode, source,
-                                                            GEN_INT (i)),
-                                            source)));
+                   emit_constant_insn 
+                     (cond,
+                      gen_rtx_SET
+                      (VOIDmode, target,
+                       gen_rtx_IOR (mode,
+                                    gen_rtx_ASHIFT (mode, source,
+                                                    GEN_INT (i)),
+                                    source)));
                  return insns + 1;
                }
            }
@@ -1457,12 +1707,13 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
                  rtx new_src = (subtargets
                                 ? (generate ? gen_reg_rtx (mode) : NULL_RTX)
                                 : target);
-                 insns = arm_gen_constant (code, mode, temp1, new_src,
+                 insns = arm_gen_constant (code, mode, cond, temp1, new_src,
                                            source, subtargets, generate);
                  source = new_src;
                  if (generate)
-                   emit_insn
-                     (gen_rtx_SET (VOIDmode, target,
+                   emit_constant_insn
+                     (cond,
+                      gen_rtx_SET (VOIDmode, target,
                                    gen_rtx_IOR
                                    (mode,
                                     gen_rtx_LSHIFTRT (mode, source,
@@ -1489,9 +1740,13 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
                {
                  rtx sub = subtargets ? gen_reg_rtx (mode) : target;
 
-                 emit_insn (gen_rtx_SET (VOIDmode, sub, GEN_INT (val)));
-                 emit_insn (gen_rtx_SET (VOIDmode, target, 
-                                         gen_rtx (code, mode, source, sub)));
+                 emit_constant_insn (cond,
+                                     gen_rtx_SET (VOIDmode, sub, 
+                                                  GEN_INT (val)));
+                 emit_constant_insn (cond,
+                                     gen_rtx_SET (VOIDmode, target, 
+                                                  gen_rtx_fmt_ee (code, mode,
+                                                                  source, sub)));
                }
              return 2;
            }
@@ -1508,15 +1763,19 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
              rtx sub = subtargets ? gen_reg_rtx (mode) : target;
              rtx shift = GEN_INT (set_sign_bit_copies);
 
-             emit_insn (gen_rtx_SET (VOIDmode, sub,
-                                     gen_rtx_NOT (mode, 
-                                                  gen_rtx_ASHIFT (mode,
-                                                                  source, 
-                                                                  shift))));
-             emit_insn (gen_rtx_SET (VOIDmode, target,
-                                     gen_rtx_NOT (mode,
-                                                  gen_rtx_LSHIFTRT (mode, sub,
-                                                                    shift))));
+             emit_constant_insn 
+               (cond,
+                gen_rtx_SET (VOIDmode, sub,
+                             gen_rtx_NOT (mode, 
+                                          gen_rtx_ASHIFT (mode,
+                                                          source, 
+                                                          shift))));
+             emit_constant_insn 
+               (cond,
+                gen_rtx_SET (VOIDmode, target,
+                             gen_rtx_NOT (mode,
+                                          gen_rtx_LSHIFTRT (mode, sub,
+                                                            shift))));
            }
          return 2;
        }
@@ -1529,15 +1788,19 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
              rtx sub = subtargets ? gen_reg_rtx (mode) : target;
              rtx shift = GEN_INT (set_zero_bit_copies);
 
-             emit_insn (gen_rtx_SET (VOIDmode, sub,
-                                     gen_rtx_NOT (mode,
-                                                  gen_rtx_LSHIFTRT (mode,
-                                                                    source,
-                                                                    shift))));
-             emit_insn (gen_rtx_SET (VOIDmode, target,
-                                     gen_rtx_NOT (mode,
-                                                  gen_rtx_ASHIFT (mode, sub,
-                                                                  shift))));
+             emit_constant_insn
+               (cond,
+                gen_rtx_SET (VOIDmode, sub,
+                             gen_rtx_NOT (mode,
+                                          gen_rtx_LSHIFTRT (mode,
+                                                            source,
+                                                            shift))));
+             emit_constant_insn 
+               (cond,
+                gen_rtx_SET (VOIDmode, target,
+                             gen_rtx_NOT (mode,
+                                          gen_rtx_ASHIFT (mode, sub,
+                                                          shift))));
            }
          return 2;
        }
@@ -1547,16 +1810,19 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
          if (generate)
            {
              rtx sub = subtargets ? gen_reg_rtx (mode) : target;
-             emit_insn (gen_rtx_SET (VOIDmode, sub,
-                                     gen_rtx_NOT (mode, source)));
+             emit_constant_insn (cond,
+                                 gen_rtx_SET (VOIDmode, sub,
+                                              gen_rtx_NOT (mode, source)));
              source = sub;
              if (subtargets)
                sub = gen_reg_rtx (mode);
-             emit_insn (gen_rtx_SET (VOIDmode, sub,
-                                     gen_rtx_AND (mode, source, 
-                                                  GEN_INT (temp1))));
-             emit_insn (gen_rtx_SET (VOIDmode, target,
-                                     gen_rtx_NOT (mode, sub)));
+             emit_constant_insn (cond,
+                                 gen_rtx_SET (VOIDmode, sub,
+                                              gen_rtx_AND (mode, source, 
+                                                           GEN_INT (temp1))));
+             emit_constant_insn (cond,
+                                 gen_rtx_SET (VOIDmode, target,
+                                              gen_rtx_NOT (mode, sub)));
            }
          return 3;
        }
@@ -1575,14 +1841,16 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
              if (generate)
                {
                  rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
-                 insns = arm_gen_constant (AND, mode, remainder | shift_mask,
+                 insns = arm_gen_constant (AND, mode, cond, 
+                                           remainder | shift_mask,
                                            new_src, source, subtargets, 1);
                  source = new_src;
                }
              else
                {
                  rtx targ = subtargets ? NULL_RTX : target;
-                 insns = arm_gen_constant (AND, mode, remainder | shift_mask,
+                 insns = arm_gen_constant (AND, mode, cond,
+                                           remainder | shift_mask,
                                            targ, source, subtargets, 0);
                }
            }
@@ -1609,7 +1877,8 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
                {
                  rtx new_src = subtargets ? gen_reg_rtx (mode) : target;
 
-                 insns = arm_gen_constant (AND, mode, remainder | shift_mask,
+                 insns = arm_gen_constant (AND, mode, cond,
+                                           remainder | shift_mask,
                                            new_src, source, subtargets, 1);
                  source = new_src;
                }
@@ -1617,7 +1886,8 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
                {
                  rtx targ = subtargets ? NULL_RTX : target;
 
-                 insns = arm_gen_constant (AND, mode, remainder | shift_mask,
+                 insns = arm_gen_constant (AND, mode, cond,
+                                           remainder | shift_mask,
                                            targ, source, subtargets, 0);
                }
            }
@@ -1762,7 +2032,9 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode,
                else
                  temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx);
 
-               emit_insn (gen_rtx_SET (VOIDmode, new_src, temp1_rtx));
+               emit_constant_insn (cond,
+                                   gen_rtx_SET (VOIDmode, new_src, 
+                                                temp1_rtx));
                source = new_src;
              }
 
@@ -1847,6 +2119,24 @@ arm_canonicalize_comparison (enum rtx_code code, rtx * op1)
   return code;
 }
 
+
+/* Define how to find the value returned by a function.  */
+
+rtx arm_function_value(tree type, tree func ATTRIBUTE_UNUSED)
+{
+  enum machine_mode mode;
+  int unsignedp ATTRIBUTE_UNUSED;
+  rtx r ATTRIBUTE_UNUSED;
+
+  
+  mode = TYPE_MODE (type);
+  /* Promote integer types.  */
+  if (INTEGRAL_TYPE_P (type))
+    PROMOTE_FUNCTION_MODE (mode, unsignedp, type);
+  return LIBCALL_VALUE(mode);
+}
+
+
 /* Decide whether a type should be returned in memory (true)
    or in a register (false).  This is called by the macro
    RETURN_IN_MEMORY.  */
@@ -1861,9 +2151,9 @@ arm_return_in_memory (tree type)
 
   size = int_size_in_bytes (type);
 
-  if (TARGET_ATPCS)
+  if (arm_abi != ARM_ABI_APCS)
     {
-      /* ATPCS returns aggregate types in memory only if they are
+      /* ATPCS and later return aggregate types in memory only if they are
         larger than a word (or are variable size).  */
       return (size < 0 || size > UNITS_PER_WORD);
     }
@@ -1953,20 +2243,19 @@ arm_return_in_memory (tree type)
   return 1;
 }
 
-/* Indicate whether or not words of a double are in big-endian order. */
+/* Indicate whether or not words of a double are in big-endian order.  */
 
 int
 arm_float_words_big_endian (void)
 {
-  if (TARGET_CIRRUS)
+  if (TARGET_MAVERICK)
     return 0;
 
   /* For FPA, float words are always big-endian.  For VFP, floats words
      follow the memory system mode.  */
 
-  if (TARGET_HARD_FLOAT)
+  if (TARGET_FPA)
     {
-      /* FIXME: TARGET_HARD_FLOAT currently implies FPA.  */
       return 1;
     }
 
@@ -1987,6 +2276,7 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
   /* On the ARM, the offset starts at 0.  */
   pcum->nregs = ((fntype && aggregate_value_p (TREE_TYPE (fntype), fntype)) ? 1 : 0);
   pcum->iwmmxt_nregs = 0;
+  pcum->can_split = true;
   
   pcum->call_cookie = CALL_NORMAL;
 
@@ -2022,6 +2312,19 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
     }
 }
 
+
+/* Return true if mode/type need doubleword alignment.  */
+bool
+arm_needs_doubleword_align (enum machine_mode mode, tree type)
+{
+  return (mode == DImode
+         || mode == DFmode
+         || VECTOR_MODE_SUPPORTED_P (mode)
+         || (mode == BLKmode
+             && TYPE_ALIGN (type) > PARM_BOUNDARY));
+}
+
+
 /* Determine where to put an argument to a function.
    Value is zero to push the argument on the stack,
    or a hard register in which to store the argument.
@@ -2037,37 +2340,44 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
 
 rtx
 arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
-                 tree type ATTRIBUTE_UNUSED, int named)
+                 tree type, int named)
 {
-  if (TARGET_REALLY_IWMMXT)
+  int nregs;
+
+  /* Varargs vectors are treated the same as long long.
+     named_count avoids having to change the way arm handles 'named' */
+  if (TARGET_IWMMXT_ABI
+      && VECTOR_MODE_SUPPORTED_P (mode)
+      && pcum->named_count > pcum->nargs + 1)
     {
-      if (VECTOR_MODE_SUPPORTED_P (mode))
+      if (pcum->iwmmxt_nregs <= 9)
+       return gen_rtx_REG (mode, pcum->iwmmxt_nregs + FIRST_IWMMXT_REGNUM);
+      else
        {
-         /* varargs vectors are treated the same as long long.
-            named_count avoids having to change the way arm handles 'named' */
-         if (pcum->named_count <= pcum->nargs + 1)
-           {
-             if (pcum->nregs == 1)
-               pcum->nregs += 1;
-             if (pcum->nregs <= 2)
-               return gen_rtx_REG (mode, pcum->nregs);
-             else
-               return NULL_RTX;
-           }
-         else if (pcum->iwmmxt_nregs <= 9)
-           return gen_rtx_REG (mode, pcum->iwmmxt_nregs + FIRST_IWMMXT_REGNUM);
-         else
-           return NULL_RTX;
+         pcum->can_split = false;
+         return NULL_RTX;
        }
-      else if ((mode == DImode || mode == DFmode) && pcum->nregs & 1)
-       pcum->nregs += 1;
     }
 
+  /* Put doubleword aligned quantities in even register pairs.  */
+  if (pcum->nregs & 1
+      && ARM_DOUBLEWORD_ALIGN
+      && arm_needs_doubleword_align (mode, type))
+    pcum->nregs++;
+
   if (mode == VOIDmode)
     /* Compute operand 2 of the call insn.  */
     return GEN_INT (pcum->call_cookie);
-  
-  if (!named || pcum->nregs >= NUM_ARG_REGS)
+
+  /* Only allow splitting an arg between regs and memory if all preceding
+     args were allocated to regs.  For args passed by reference we only count
+     the reference pointer.  */
+  if (pcum->can_split)
+    nregs = 1;
+  else
+    nregs = ARM_NUM_REGS2 (mode, type);
+
+  if (!named || pcum->nregs + nregs > NUM_ARG_REGS)
     return NULL_RTX;
   
   return gen_rtx_REG (mode, pcum->nregs);
@@ -2089,6 +2399,8 @@ arm_function_arg_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
 rtx
 arm_va_arg (tree valist, tree type)
 {
+  int align;
+
   /* Variable sized types are passed by reference.  */
   if (TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
     {
@@ -2096,17 +2408,18 @@ arm_va_arg (tree valist, tree type)
       return gen_rtx_MEM (ptr_mode, force_reg (Pmode, addr));
     }
 
-  if (FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), NULL) == IWMMXT_ALIGNMENT)
+  align = FUNCTION_ARG_BOUNDARY (TYPE_MODE (type), type);
+  if (align > PARM_BOUNDARY)
     {
-      tree minus_eight;
+      tree mask;
       tree t;
 
       /* Maintain 64-bit alignment of the valist pointer by
         constructing:   valist = ((valist + (8 - 1)) & -8).  */
-      minus_eight = build_int_2 (- (IWMMXT_ALIGNMENT / BITS_PER_UNIT), -1);
-      t = build_int_2 ((IWMMXT_ALIGNMENT / BITS_PER_UNIT) - 1, 0);
+      mask = build_int_2 (- (align / BITS_PER_UNIT), -1);
+      t = build_int_2 ((align / BITS_PER_UNIT) - 1, 0);
       t = build (PLUS_EXPR,    TREE_TYPE (valist), valist, t);
-      t = build (BIT_AND_EXPR, TREE_TYPE (t), t, minus_eight);
+      t = build (BIT_AND_EXPR, TREE_TYPE (t), t, mask);
       t = build (MODIFY_EXPR,  TREE_TYPE (valist), valist, t);
       TREE_SIDE_EFFECTS (t) = 1;
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -2563,7 +2876,7 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
        {
          /* The base register doesn't really matter, we only want to
             test the index for the appropriate mode.  */
-         if (!arm_legitimate_index_p (mode, offset, 0))
+         if (!arm_legitimate_index_p (mode, offset, SET, 0))
            {
              if (!no_new_pseudos)
                offset = force_reg (Pmode, offset);
@@ -2668,7 +2981,8 @@ arm_address_register_rtx_p (rtx x, int strict_p)
 
 /* Return nonzero if X is a valid ARM state address operand.  */
 int
-arm_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
+arm_legitimate_address_p (enum machine_mode mode, rtx x, RTX_CODE outer,
+                         int strict_p)
 {
   if (arm_address_register_rtx_p (x, strict_p))
     return 1;
@@ -2680,12 +2994,13 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
           && GET_MODE_SIZE (mode) <= 4
           && arm_address_register_rtx_p (XEXP (x, 0), strict_p)
           && GET_CODE (XEXP (x, 1)) == PLUS
-          && XEXP (XEXP (x, 1), 0) == XEXP (x, 0))
-    return arm_legitimate_index_p (mode, XEXP (XEXP (x, 1), 1), strict_p);
+          && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)))
+    return arm_legitimate_index_p (mode, XEXP (XEXP (x, 1), 1), outer,
+                                  strict_p);
 
   /* After reload constants split into minipools will have addresses
      from a LABEL_REF.  */
-  else if (GET_MODE_SIZE (mode) >= 4 && reload_completed
+  else if (reload_completed
           && (GET_CODE (x) == LABEL_REF
               || (GET_CODE (x) == CONST
                   && GET_CODE (XEXP (x, 0)) == PLUS
@@ -2709,15 +3024,33 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
        }
     }
 
+  else if (TARGET_HARD_FLOAT && TARGET_VFP && mode == DFmode)
+    {
+      if (GET_CODE (x) == PLUS
+         && arm_address_register_rtx_p (XEXP (x, 0), strict_p)
+         && GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         HOST_WIDE_INT val = INTVAL (XEXP (x, 1));
+
+         /* ??? valid arm offsets are a subset of VFP offsets.
+            For now only allow this subset.  Proper fix is to add an
+            additional memory constraint for arm address modes.
+            Alternatively allow full vfp addressing and let
+            output_move_double fix it up with a sub-optimal sequence.  */
+          if (val == 4 || val == -4 || val == -8)
+           return 1;
+       }
+    }
+
   else if (GET_CODE (x) == PLUS)
     {
       rtx xop0 = XEXP (x, 0);
       rtx xop1 = XEXP (x, 1);
 
       return ((arm_address_register_rtx_p (xop0, strict_p)
-              && arm_legitimate_index_p (mode, xop1, strict_p))
+              && arm_legitimate_index_p (mode, xop1, outer, strict_p))
              || (arm_address_register_rtx_p (xop1, strict_p)
-                 && arm_legitimate_index_p (mode, xop0, strict_p)));
+                 && arm_legitimate_index_p (mode, xop0, outer, strict_p)));
     }
 
 #if 0
@@ -2728,7 +3061,7 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
       rtx xop1 = XEXP (x, 1);
 
       return (arm_address_register_rtx_p (xop0, strict_p)
-             && arm_legitimate_index_p (mode, xop1, strict_p));
+             && arm_legitimate_index_p (mode, xop1, outer, strict_p));
     }
 #endif
 
@@ -2750,17 +3083,18 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
 /* Return nonzero if INDEX is valid for an address index operand in
    ARM state.  */
 static int
-arm_legitimate_index_p (enum machine_mode mode, rtx index, int strict_p)
+arm_legitimate_index_p (enum machine_mode mode, rtx index, RTX_CODE outer,
+                       int strict_p)
 {
   HOST_WIDE_INT range;
   enum rtx_code code = GET_CODE (index);
 
-  if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT)
+  if (TARGET_HARD_FLOAT && TARGET_FPA && GET_MODE_CLASS (mode) == MODE_FLOAT)
     return (code == CONST_INT && INTVAL (index) < 1024
            && INTVAL (index) > -1024
            && (INTVAL (index) & 3) == 0);
 
-  if (TARGET_CIRRUS
+  if (TARGET_HARD_FLOAT && TARGET_MAVERICK
       && (GET_MODE_CLASS (mode) == MODE_FLOAT || mode == DImode))
     return (code == CONST_INT
            && INTVAL (index) < 255
@@ -2775,45 +3109,51 @@ arm_legitimate_index_p (enum machine_mode mode, rtx index, int strict_p)
            && INTVAL (index) < 256
            && INTVAL (index) > -256);
 
-  /* XXX What about ldrsb?  */
-  if (GET_MODE_SIZE (mode) <= 4  && code == MULT
-      && (!arm_arch4 || (mode) != HImode))
+  if (GET_MODE_SIZE (mode) <= 4
+      && ! (arm_arch4
+           && (mode == HImode
+               || (mode == QImode && outer == SIGN_EXTEND))))
     {
-      rtx xiop0 = XEXP (index, 0);
-      rtx xiop1 = XEXP (index, 1);
+      if (code == MULT)
+       {
+         rtx xiop0 = XEXP (index, 0);
+         rtx xiop1 = XEXP (index, 1);
+
+         return ((arm_address_register_rtx_p (xiop0, strict_p)
+                  && power_of_two_operand (xiop1, SImode))
+                 || (arm_address_register_rtx_p (xiop1, strict_p)
+                     && power_of_two_operand (xiop0, SImode)));
+       }
+      else if (code == LSHIFTRT || code == ASHIFTRT
+              || code == ASHIFT || code == ROTATERT)
+       {
+         rtx op = XEXP (index, 1);
 
-      return ((arm_address_register_rtx_p (xiop0, strict_p)
-              && power_of_two_operand (xiop1, SImode))
-             || (arm_address_register_rtx_p (xiop1, strict_p)
-                 && power_of_two_operand (xiop0, SImode)));
+         return (arm_address_register_rtx_p (XEXP (index, 0), strict_p)
+                 && GET_CODE (op) == CONST_INT
+                 && INTVAL (op) > 0
+                 && INTVAL (op) <= 31);
+       }
     }
 
-  if (GET_MODE_SIZE (mode) <= 4
-      && (code == LSHIFTRT || code == ASHIFTRT
-         || code == ASHIFT || code == ROTATERT)
-      && (!arm_arch4 || (mode) != HImode))
+  /* For ARM v4 we may be doing a sign-extend operation during the
+     load.  */
+  if (arm_arch4)
     {
-      rtx op = XEXP (index, 1);
-
-      return (arm_address_register_rtx_p (XEXP (index, 0), strict_p)
-             && GET_CODE (op) == CONST_INT
-             && INTVAL (op) > 0
-             && INTVAL (op) <= 31);
+      if (mode == HImode || (outer == SIGN_EXTEND && mode == QImode))
+       range = 256;
+      else
+       range = 4096;
     }
-
-  /* XXX For ARM v4 we may be doing a sign-extend operation during the
-     load, but that has a restricted addressing range and we are unable
-     to tell here whether that is the case.  To be safe we restrict all
-     loads to that range.  */
-  range = ((mode) == HImode || (mode) == QImode)
-    ? (arm_arch4 ? 256 : 4095) : 4096;
+  else
+    range = (mode == HImode) ? 4095 : 4096;
 
   return (code == CONST_INT
          && INTVAL (index) < range
          && INTVAL (index) > -range);
 }
 
-/* Return nonzero if X is valid as an ARM state addressing register.  */
+/* Return nonzero if X is valid as a Thumb state base register.  */
 static int
 thumb_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p)
 {
@@ -2828,10 +3168,11 @@ thumb_base_register_rtx_p (rtx x, enum machine_mode mode, int strict_p)
     return THUMB_REGNO_MODE_OK_FOR_BASE_P (regno, mode);
 
   return (regno <= LAST_LO_REGNUM
-         || regno >= FIRST_PSEUDO_REGISTER
+         || regno > LAST_VIRTUAL_REGISTER
          || regno == FRAME_POINTER_REGNUM
          || (GET_MODE_SIZE (mode) >= 4
              && (regno == STACK_POINTER_REGNUM
+                 || regno >= FIRST_PSEUDO_REGISTER
                  || x == hard_frame_pointer_rtx
                  || x == arg_pointer_rtx)));
 }
@@ -2908,8 +3249,6 @@ thumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
       if (GET_MODE_SIZE (mode) <= 4
          && XEXP (x, 0) != frame_pointer_rtx
          && XEXP (x, 1) != frame_pointer_rtx
-         && XEXP (x, 0) != virtual_stack_vars_rtx
-         && XEXP (x, 1) != virtual_stack_vars_rtx
          && thumb_index_register_rtx_p (XEXP (x, 0), strict_p)
          && thumb_index_register_rtx_p (XEXP (x, 1), strict_p))
        return 1;
@@ -2943,6 +3282,7 @@ thumb_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
     }
 
   else if (GET_MODE_CLASS (mode) != MODE_FLOAT
+          && GET_MODE_SIZE (mode) == 4
           && GET_CODE (x) == SYMBOL_REF
           && CONSTANT_POOL_ADDRESS_P (x)
           && !(flag_pic
@@ -2995,7 +3335,10 @@ arm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
          rtx base_reg, val;
          n = INTVAL (xop1);
 
-         if (mode == DImode || (TARGET_SOFT_FLOAT && mode == DFmode))
+         /* VFP addressing modes actually allow greater offsets, but for
+            now we just stick with the lowest common denominator.  */
+         if (mode == DImode
+             || ((TARGET_SOFT_FLOAT || TARGET_VFP) && mode == DFmode))
            {
              low_n = n & 0x0f;
              n &= ~0x0f;
@@ -3053,6 +3396,74 @@ arm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
   return x;
 }
 
+
+/* Try machine-dependent ways of modifying an illegitimate Thumb address
+   to be legitimate.  If we find one, return the new, valid address.  */
+rtx
+thumb_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
+{
+  if (GET_CODE (x) == PLUS
+      && GET_CODE (XEXP (x, 1)) == CONST_INT
+      && (INTVAL (XEXP (x, 1)) >= 32 * GET_MODE_SIZE (mode)
+         || INTVAL (XEXP (x, 1)) < 0))
+    {
+      rtx xop0 = XEXP (x, 0);
+      rtx xop1 = XEXP (x, 1);
+      HOST_WIDE_INT offset = INTVAL (xop1);
+
+      /* Try and fold the offset into a biasing of the base register and
+        then offsetting that.  Don't do this when optimizing for space
+        since it can cause too many CSEs.  */
+      if (optimize_size && offset >= 0
+         && offset < 256 + 31 * GET_MODE_SIZE (mode))
+       {
+         HOST_WIDE_INT delta;
+
+         if (offset >= 256)
+           delta = offset - (256 - GET_MODE_SIZE (mode));
+         else if (offset < 32 * GET_MODE_SIZE (mode) + 8)
+           delta = 31 * GET_MODE_SIZE (mode);
+         else
+           delta = offset & (~31 * GET_MODE_SIZE (mode));
+
+         xop0 = force_operand (plus_constant (xop0, offset - delta),
+                               NULL_RTX);
+         x = plus_constant (xop0, delta);
+       }
+      else if (offset < 0 && offset > -256)
+       /* Small negative offsets are best done with a subtract before the
+          dereference, forcing these into a register normally takes two
+          instructions.  */
+       x = force_operand (x, NULL_RTX);
+      else
+       {
+         /* For the remaining cases, force the constant into a register.  */
+         xop1 = force_reg (SImode, xop1);
+         x = gen_rtx_PLUS (SImode, xop0, xop1);
+       }
+    }
+  else if (GET_CODE (x) == PLUS
+          && s_register_operand (XEXP (x, 1), SImode)
+          && !s_register_operand (XEXP (x, 0), SImode))
+    {
+      rtx xop0 = force_operand (XEXP (x, 0), NULL_RTX);
+
+      x = gen_rtx_PLUS (SImode, xop0, XEXP (x, 1));
+    }
+
+  if (flag_pic)
+    {
+      /* We need to find and carefully transform any SYMBOL and LABEL
+        references; so go back to the original address expression.  */
+      rtx new_x = legitimize_pic_address (orig_x, mode, NULL_RTX);
+
+      if (new_x != orig_x)
+       x = new_x;
+    }
+
+  return x;
+}
+
 \f
 
 #define REG_OR_SUBREG_REG(X)                                           \
@@ -3065,129 +3476,133 @@ arm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
 #ifndef COSTS_N_INSNS
 #define COSTS_N_INSNS(N) ((N) * 4 - 2)
 #endif
-/* Worker routine for arm_rtx_costs.  */
 static inline int
-arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
+thumb_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer)
 {
   enum machine_mode mode = GET_MODE (x);
-  enum rtx_code subcode;
-  int extra_cost;
 
-  if (TARGET_THUMB)
+  switch (code)
     {
-      switch (code)
-       {
-       case ASHIFT:
-       case ASHIFTRT:
-       case LSHIFTRT:
-       case ROTATERT:  
-       case PLUS:
-       case MINUS:
-       case COMPARE:
-       case NEG:
-       case NOT:       
-         return COSTS_N_INSNS (1);
-         
-       case MULT:                                                      
-         if (GET_CODE (XEXP (x, 1)) == CONST_INT)                      
-           {                                                           
-             int cycles = 0;                                           
-             unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1));
-             
-             while (i)                                         
-               {                                                       
-                 i >>= 2;                                              
-                 cycles++;                                             
-               }                                                       
-             return COSTS_N_INSNS (2) + cycles;                        
-           }
-         return COSTS_N_INSNS (1) + 16;
-         
-       case SET:                                                       
-         return (COSTS_N_INSNS (1)                                     
-                 + 4 * ((GET_CODE (SET_SRC (x)) == MEM)                
-                        + GET_CODE (SET_DEST (x)) == MEM));
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case ROTATERT:     
+    case PLUS:
+    case MINUS:
+    case COMPARE:
+    case NEG:
+    case NOT:  
+      return COSTS_N_INSNS (1);
+      
+    case MULT:                                                 
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)                 
+       {                                                               
+         int cycles = 0;                                               
+         unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1));
          
-       case CONST_INT:                                         
-         if (outer == SET)                                             
+         while (i)                                             
            {                                                   
-             if ((unsigned HOST_WIDE_INT) INTVAL (x) < 256)            
-               return 0;                                               
-             if (thumb_shiftable_const (INTVAL (x)))                   
-               return COSTS_N_INSNS (2);                               
-             return COSTS_N_INSNS (3);                         
-           }                                                           
-         else if (outer == PLUS                                        
-                  && INTVAL (x) < 256 && INTVAL (x) > -256)            
-           return 0;                                                   
-         else if (outer == COMPARE                                     
-                  && (unsigned HOST_WIDE_INT) INTVAL (x) < 256)        
-           return 0;                                                   
-         else if (outer == ASHIFT || outer == ASHIFTRT         
-                  || outer == LSHIFTRT)                                
-           return 0;                                                   
-         return COSTS_N_INSNS (2);
-         
-       case CONST:                                                     
-       case CONST_DOUBLE:                                              
-       case LABEL_REF:                                         
-       case SYMBOL_REF:                                                
-         return COSTS_N_INSNS (3);
-         
-       case UDIV:
-       case UMOD:
-       case DIV:
-       case MOD:
-         return 100;
-
-       case TRUNCATE:
-         return 99;
-
-       case AND:
-       case XOR:
-       case IOR: 
-         /* XXX guess. */
-         return 8;
-
-       case ADDRESSOF:
-       case MEM:
-         /* XXX another guess.  */
-         /* Memory costs quite a lot for the first word, but subsequent words
-            load at the equivalent of a single insn each.  */
-         return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD)
-                 + ((GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
-                    ? 4 : 0));
-
-       case IF_THEN_ELSE:
-         /* XXX a guess. */
-         if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
-           return 14;
-         return 2;
-
-       case ZERO_EXTEND:
-         /* XXX still guessing.  */
-         switch (GET_MODE (XEXP (x, 0)))
-           {
-           case QImode:
-             return (1 + (mode == DImode ? 4 : 0)
-                     + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
-             
-           case HImode:
-             return (4 + (mode == DImode ? 4 : 0)
-                     + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
-             
-           case SImode:
-             return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
-         
-           default:
-             return 99;
-           }
+             i >>= 2;                                          
+             cycles++;                                         
+           }                                                   
+         return COSTS_N_INSNS (2) + cycles;                    
+       }
+      return COSTS_N_INSNS (1) + 16;
+      
+    case SET:                                                  
+      return (COSTS_N_INSNS (1)                                        
+             + 4 * ((GET_CODE (SET_SRC (x)) == MEM)            
+                    + GET_CODE (SET_DEST (x)) == MEM));
+      
+    case CONST_INT:                                            
+      if (outer == SET)                                                
+       {                                                       
+         if ((unsigned HOST_WIDE_INT) INTVAL (x) < 256)                
+           return 0;                                           
+         if (thumb_shiftable_const (INTVAL (x)))                       
+           return COSTS_N_INSNS (2);                           
+         return COSTS_N_INSNS (3);                             
+       }                                                               
+      else if ((outer == PLUS || outer == COMPARE)
+              && INTVAL (x) < 256 && INTVAL (x) > -256)                
+       return 0;
+      else if (outer == AND
+              && INTVAL (x) < 256 && INTVAL (x) >= -256)
+       return COSTS_N_INSNS (1);
+      else if (outer == ASHIFT || outer == ASHIFTRT            
+              || outer == LSHIFTRT)                            
+       return 0;                                                       
+      return COSTS_N_INSNS (2);
+      
+    case CONST:                                                        
+    case CONST_DOUBLE:                                         
+    case LABEL_REF:                                            
+    case SYMBOL_REF:                                           
+      return COSTS_N_INSNS (3);
+      
+    case UDIV:
+    case UMOD:
+    case DIV:
+    case MOD:
+      return 100;
+
+    case TRUNCATE:
+      return 99;
+
+    case AND:
+    case XOR:
+    case IOR: 
+      /* XXX guess.  */
+      return 8;
+
+    case ADDRESSOF:
+    case MEM:
+      /* XXX another guess.  */
+      /* Memory costs quite a lot for the first word, but subsequent words
+        load at the equivalent of a single insn each.  */
+      return (10 + 4 * ((GET_MODE_SIZE (mode) - 1) / UNITS_PER_WORD)
+             + ((GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
+                ? 4 : 0));
+
+    case IF_THEN_ELSE:
+      /* XXX a guess.  */
+      if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
+       return 14;
+      return 2;
+
+    case ZERO_EXTEND:
+      /* XXX still guessing.  */
+      switch (GET_MODE (XEXP (x, 0)))
+       {
+       case QImode:
+         return (1 + (mode == DImode ? 4 : 0)
+                 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
          
+       case HImode:
+         return (4 + (mode == DImode ? 4 : 0)
+                 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
+         
+       case SImode:
+         return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
+      
        default:
          return 99;
        }
+      
+    default:
+      return 99;
     }
-  
+}
+
+
+/* Worker routine for arm_rtx_costs.  */
+static inline int
+arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
+{
+  enum machine_mode mode = GET_MODE (x);
+  enum rtx_code subcode;
+  int extra_cost;
+
   switch (code)
     {
     case MEM:
@@ -3199,7 +3614,9 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
 
     case DIV:
     case MOD:
-      return 100;
+    case UDIV:
+    case UMOD:
+      return optimize_size ? COSTS_N_INSNS (2) : 100;
 
     case ROTATE:
       if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG)
@@ -3237,11 +3654,11 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
       if (GET_MODE_CLASS (mode) == MODE_FLOAT)
        return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1))
                      || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
-                         && const_double_rtx_ok_for_fpa (XEXP (x, 1))))
+                         && arm_const_double_rtx (XEXP (x, 1))))
                     ? 0 : 8)
                + ((REG_OR_SUBREG_REG (XEXP (x, 0))
                    || (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE
-                       && const_double_rtx_ok_for_fpa (XEXP (x, 0))))
+                       && arm_const_double_rtx (XEXP (x, 0))))
                   ? 0 : 8));
 
       if (((GET_CODE (XEXP (x, 0)) == CONST_INT
@@ -3266,7 +3683,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
        return (2 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8)
                + ((REG_OR_SUBREG_REG (XEXP (x, 1))
                    || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
-                       && const_double_rtx_ok_for_fpa (XEXP (x, 1))))
+                       && arm_const_double_rtx (XEXP (x, 1))))
                   ? 0 : 8));
 
       /* Fall through */
@@ -3316,43 +3733,11 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
       return 8;
 
     case MULT:
-      /* There is no point basing this on the tuning, since it is always the
-        fast variant if it exists at all.  */
-      if (arm_fast_multiply && mode == DImode
-         && (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1)))
-         && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
-             || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
-       return 8;
-
-      if (GET_MODE_CLASS (mode) == MODE_FLOAT
-         || mode == DImode)
-       return 30;
-
-      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
-       {
-         unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1))
-                                     & (unsigned HOST_WIDE_INT) 0xffffffff);
-         int add_cost = const_ok_for_arm (i) ? 4 : 8;
-         int j;
-         
-         /* Tune as appropriate.  */ 
-         int booth_unit_size = ((tune_flags & FL_FAST_MULT) ? 8 : 2);
-         
-         for (j = 0; i && j < 32; j += booth_unit_size)
-           {
-             i >>= booth_unit_size;
-             add_cost += 2;
-           }
-
-         return add_cost;
-       }
-
-      return (((tune_flags & FL_FAST_MULT) ? 8 : 30)
-             + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
-             + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4));
+      /* This should have been handled by the CPU specific routines.  */
+      abort ();
 
     case TRUNCATE:
-      if (arm_fast_multiply && mode == SImode
+      if (arm_arch3m && mode == SImode
          && GET_CODE (XEXP (x, 0)) == LSHIFTRT
          && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
          && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0))
@@ -3433,7 +3818,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
       return 6;
       
     case CONST_DOUBLE:                                         
-      if (const_double_rtx_ok_for_fpa (x))                     
+      if (arm_const_double_rtx (x))
        return outer == SET ? 2 : -1;                   
       else if ((outer == COMPARE || outer == PLUS)     
               && neg_const_double_rtx_ok_for_fpa (x))          
@@ -3445,43 +3830,309 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
     }
 }
 
+/* RTX costs for cores with a slow MUL implementation.  */
+
 static bool
-arm_rtx_costs (rtx x, int code, int outer_code, int *total)
+arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
 {
-  *total = arm_rtx_costs_1 (x, code, outer_code);
-  return true;
+  enum machine_mode mode = GET_MODE (x);
+
+  if (TARGET_THUMB)
+    {
+      *total = thumb_rtx_costs (x, code, outer_code);
+      return true;
+    }
+  
+  switch (code)
+    {
+    case MULT:
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT
+         || mode == DImode)
+       {
+         *total = 30;
+         return true;
+       }
+
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1))
+                                     & (unsigned HOST_WIDE_INT) 0xffffffff);
+         int cost, const_ok = const_ok_for_arm (i);
+         int j, booth_unit_size;
+
+         /* Tune as appropriate.  */ 
+         cost = const_ok ? 4 : 8;
+         booth_unit_size = 2;
+         for (j = 0; i && j < 32; j += booth_unit_size)
+           {
+             i >>= booth_unit_size;
+             cost += 2;
+           }
+
+         *total = cost;
+         return true;
+       }
+
+      *total = 30 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
+                 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4);
+      return true;
+  
+    default:
+      *total = arm_rtx_costs_1 (x, code, outer_code);
+      return true;
+    }
+}
+
+
+/* RTX cost for cores with a fast multiply unit (M variants).  */
+
+static bool
+arm_fastmul_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+  enum machine_mode mode = GET_MODE (x);
+
+  if (TARGET_THUMB)
+    {
+      *total = thumb_rtx_costs (x, code, outer_code);
+      return true;
+    }
+  
+  switch (code)
+    {
+    case MULT:
+      /* There is no point basing this on the tuning, since it is always the
+        fast variant if it exists at all.  */
+      if (mode == DImode
+         && (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1)))
+         && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+             || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
+       {
+         *total = 8;
+         return true;
+       }
+      
+
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT
+         || mode == DImode)
+       {
+         *total = 30;
+         return true;
+       }
+
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1))
+                                     & (unsigned HOST_WIDE_INT) 0xffffffff);
+         int cost, const_ok = const_ok_for_arm (i);
+         int j, booth_unit_size;
+
+         /* Tune as appropriate.  */ 
+         cost = const_ok ? 4 : 8;
+         booth_unit_size = 8;
+         for (j = 0; i && j < 32; j += booth_unit_size)
+           {
+             i >>= booth_unit_size;
+             cost += 2;
+           }
+
+         *total = cost;
+         return true;
+       }
+
+      *total = 8 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
+                + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4);
+      return true;
+  
+    default:
+      *total = arm_rtx_costs_1 (x, code, outer_code);
+      return true;
+    }
+}
+
+
+/* RTX cost for XScale CPUs.  */
+
+static bool
+arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+  enum machine_mode mode = GET_MODE (x);
+
+  if (TARGET_THUMB)
+    {
+      *total = thumb_rtx_costs (x, code, outer_code);
+      return true;
+    }
+  
+  switch (code)
+    {
+    case MULT:
+      /* There is no point basing this on the tuning, since it is always the
+        fast variant if it exists at all.  */
+      if (mode == DImode
+         && (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1)))
+         && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+             || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
+       {
+         *total = 8;
+         return true;
+       }
+      
+
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT
+         || mode == DImode)
+       {
+         *total = 30;
+         return true;
+       }
+
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+       {
+         unsigned HOST_WIDE_INT i = (INTVAL (XEXP (x, 1))
+                                     & (unsigned HOST_WIDE_INT) 0xffffffff);
+         int cost, const_ok = const_ok_for_arm (i);
+         unsigned HOST_WIDE_INT masked_const;
+
+         /* The cost will be related to two insns.
+            First a load of the constant (MOV or LDR), then a multiply.  */
+         cost = 2;
+         if (! const_ok)
+           cost += 1;      /* LDR is probably more expensive because
+                              of longer result latency.  */
+         masked_const = i & 0xffff8000;
+         if (masked_const != 0 && masked_const != 0xffff8000)
+           {
+             masked_const = i & 0xf8000000;
+             if (masked_const == 0 || masked_const == 0xf8000000)
+               cost += 1;
+             else
+               cost += 2;
+           }
+         *total = cost;
+         return true;
+       }
+
+      *total = 8 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
+                + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4);
+      return true;
+  
+    default:
+      *total = arm_rtx_costs_1 (x, code, outer_code);
+      return true;
+    }
 }
 
+
+/* RTX costs for 9e (and later) cores.  */
+
+static bool
+arm_9e_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+  enum machine_mode mode = GET_MODE (x);
+  int nonreg_cost;
+  int cost;
+  
+  if (TARGET_THUMB)
+    {
+      switch (code)
+       {
+       case MULT:
+         *total = COSTS_N_INSNS (3);
+         return true;
+         
+       default:
+         *total = thumb_rtx_costs (x, code, outer_code);
+         return true;
+       }
+    }
+  
+  switch (code)
+    {
+    case MULT:
+      /* There is no point basing this on the tuning, since it is always the
+        fast variant if it exists at all.  */
+      if (mode == DImode
+         && (GET_CODE (XEXP (x, 0)) == GET_CODE (XEXP (x, 1)))
+         && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
+             || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
+       {
+         *total = 3;
+         return true;
+       }
+      
+
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+       {
+         *total = 30;
+         return true;
+       }
+      if (mode == DImode)
+       {
+         cost = 7;
+         nonreg_cost = 8;
+       }
+      else
+       {
+         cost = 2;
+         nonreg_cost = 4;
+       }
+
+
+      *total = cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : nonreg_cost)
+                   + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : nonreg_cost);
+      return true;
+  
+    default:
+      *total = arm_rtx_costs_1 (x, code, outer_code);
+      return true;
+    }
+}
 /* All address computations that can be done are free, but rtx cost returns
    the same for practically all of them.  So we weight the different types
    of address here in the order (most pref first):
-   PRE/POST_INC/DEC, SHIFT or NON-INT sum, INT sum, REG, MEM or LABEL. */
+   PRE/POST_INC/DEC, SHIFT or NON-INT sum, INT sum, REG, MEM or LABEL.  */
+static inline int
+arm_arm_address_cost (rtx x)
+{
+  enum rtx_code c  = GET_CODE (x);
+
+  if (c == PRE_INC || c == PRE_DEC || c == POST_INC || c == POST_DEC)
+    return 0;
+  if (c == MEM || c == LABEL_REF || c == SYMBOL_REF)
+    return 10;
+
+  if (c == PLUS || c == MINUS)
+    {
+      if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+       return 2;
+
+      if (ARITHMETIC_P (XEXP (x, 0)) || ARITHMETIC_P (XEXP (x, 1)))
+       return 3;
+
+      return 4;
+    }
+
+  return 6;
+}
+
+static inline int
+arm_thumb_address_cost (rtx x)
+{
+  enum rtx_code c  = GET_CODE (x);
+
+  if (c == REG)
+    return 1;
+  if (c == PLUS
+      && GET_CODE (XEXP (x, 0)) == REG
+      && GET_CODE (XEXP (x, 1)) == CONST_INT)
+    return 1;
+
+  return 2;
+}
+
 static int
 arm_address_cost (rtx x)
 {
-#define ARM_ADDRESS_COST(X)                                                 \
-  (10 - ((GET_CODE (X) == MEM || GET_CODE (X) == LABEL_REF                  \
-         || GET_CODE (X) == SYMBOL_REF)                                     \
-        ? 0                                                                 \
-        : ((GET_CODE (X) == PRE_INC || GET_CODE (X) == PRE_DEC              \
-            || GET_CODE (X) == POST_INC || GET_CODE (X) == POST_DEC)        \
-           ? 10                                                             \
-           : (((GET_CODE (X) == PLUS || GET_CODE (X) == MINUS)              \
-               ? 6 + (GET_CODE (XEXP (X, 1)) == CONST_INT ? 2               \
-                      : ((GET_RTX_CLASS (GET_CODE (XEXP (X, 0))) == '2'     \
-                          || GET_RTX_CLASS (GET_CODE (XEXP (X, 0))) == 'c'  \
-                          || GET_RTX_CLASS (GET_CODE (XEXP (X, 1))) == '2'  \
-                          || GET_RTX_CLASS (GET_CODE (XEXP (X, 1))) == 'c') \
-                         ? 1 : 0))                                          \
-               : 4)))))
-        
-#define THUMB_ADDRESS_COST(X)                                  \
-  ((GET_CODE (X) == REG                                        \
-    || (GET_CODE (X) == PLUS && GET_CODE (XEXP (X, 0)) == REG  \
-       && GET_CODE (XEXP (X, 1)) == CONST_INT))                \
-   ? 1 : 2)
-     
-  return (TARGET_ARM ? ARM_ADDRESS_COST (x) : THUMB_ADDRESS_COST (x));
+  return TARGET_ARM ? arm_arm_address_cost (x) : arm_thumb_address_cost (x);
 }
 
 static int
@@ -3509,7 +4160,8 @@ arm_adjust_cost (rtx insn, rtx link, rtx dep, int cost)
         operand for INSN.  If we have a shifted input operand and the
         instruction we depend on is another ALU instruction, then we may
         have to account for an additional stall.  */
-      if (shift_opnum != 0 && attr_type == TYPE_NORMAL)
+      if (shift_opnum != 0
+         && (attr_type == TYPE_ALU_SHIFT || attr_type == TYPE_ALU_SHIFT_REG))
        {
          rtx shifted_operand;
          int opno;
@@ -3567,47 +4219,51 @@ arm_adjust_cost (rtx insn, rtx link, rtx dep, int cost)
   return cost;
 }
 
-static int fpa_consts_inited = 0;
+static int fp_consts_inited = 0;
 
-static const char * const strings_fpa[8] =
+/* Only zero is valid for VFP.  Other values are also valid for FPA.  */
+static const char * const strings_fp[8] =
 {
   "0",   "1",   "2",   "3",
   "4",   "5",   "0.5", "10"
 };
 
-static REAL_VALUE_TYPE values_fpa[8];
+static REAL_VALUE_TYPE values_fp[8];
 
 static void
-init_fpa_table (void)
+init_fp_table (void)
 {
   int i;
   REAL_VALUE_TYPE r;
 
-  for (i = 0; i < 8; i++)
+  if (TARGET_VFP)
+    fp_consts_inited = 1;
+  else
+    fp_consts_inited = 8;
+
+  for (i = 0; i < fp_consts_inited; i++)
     {
-      r = REAL_VALUE_ATOF (strings_fpa[i], DFmode);
-      values_fpa[i] = r;
+      r = REAL_VALUE_ATOF (strings_fp[i], DFmode);
+      values_fp[i] = r;
     }
-
-  fpa_consts_inited = 1;
 }
 
-/* Return TRUE if rtx X is a valid immediate FPA constant.  */
+/* Return TRUE if rtx X is a valid immediate FP constant.  */
 int
-const_double_rtx_ok_for_fpa (rtx x)
+arm_const_double_rtx (rtx x)
 {
   REAL_VALUE_TYPE r;
   int i;
   
-  if (!fpa_consts_inited)
-    init_fpa_table ();
+  if (!fp_consts_inited)
+    init_fp_table ();
   
   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
   if (REAL_VALUE_MINUS_ZERO (r))
     return 0;
 
-  for (i = 0; i < 8; i++)
-    if (REAL_VALUES_EQUAL (r, values_fpa[i]))
+  for (i = 0; i < fp_consts_inited; i++)
+    if (REAL_VALUES_EQUAL (r, values_fp[i]))
       return 1;
 
   return 0;
@@ -3620,8 +4276,8 @@ neg_const_double_rtx_ok_for_fpa (rtx x)
   REAL_VALUE_TYPE r;
   int i;
   
-  if (!fpa_consts_inited)
-    init_fpa_table ();
+  if (!fp_consts_inited)
+    init_fp_table ();
   
   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
   r = REAL_VALUE_NEGATE (r);
@@ -3629,7 +4285,7 @@ neg_const_double_rtx_ok_for_fpa (rtx x)
     return 0;
 
   for (i = 0; i < 8; i++)
-    if (REAL_VALUES_EQUAL (r, values_fpa[i]))
+    if (REAL_VALUES_EQUAL (r, values_fp[i]))
       return 1;
 
   return 0;
@@ -3672,6 +4328,21 @@ arm_hard_register_operand (rtx op, enum machine_mode mode)
          && REGNO (op) < FIRST_PSEUDO_REGISTER);
 }
     
+/* An arm register operand.  */
+int
+arm_general_register_operand (rtx op, enum machine_mode mode)
+{
+  if (GET_MODE (op) != mode && mode != VOIDmode)
+    return 0;
+
+  if (GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
+
+  return (GET_CODE (op) == REG
+         && (REGNO (op) <= LAST_ARM_REGNUM
+             || REGNO (op) >= FIRST_PSEUDO_REGISTER));
+}
+
 /* Only accept reg, subreg(reg), const_int.  */
 int
 reg_or_int_operand (rtx op, enum machine_mode mode)
@@ -3704,37 +4375,6 @@ arm_reload_memory_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
                  && REGNO (op) >= FIRST_PSEUDO_REGISTER)));
 }
 
-/* Return 1 if OP is a valid memory address, but not valid for a signed byte
-   memory access (architecture V4).
-   MODE is QImode if called when computing constraints, or VOIDmode when
-   emitting patterns.  In this latter case we cannot use memory_operand()
-   because it will fail on badly formed MEMs, which is precisely what we are
-   trying to catch.  */
-int
-bad_signed_byte_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  if (GET_CODE (op) != MEM)
-    return 0;
-
-  op = XEXP (op, 0);
-
-  /* A sum of anything more complex than reg + reg or reg + const is bad.  */
-  if ((GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
-      && (!s_register_operand (XEXP (op, 0), VOIDmode)
-         || (!s_register_operand (XEXP (op, 1), VOIDmode)
-             && GET_CODE (XEXP (op, 1)) != CONST_INT)))
-    return 1;
-
-  /* Big constants are also bad.  */
-  if (GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT
-      && (INTVAL (XEXP (op, 1)) > 0xff
-         || -INTVAL (XEXP (op, 1)) > 0xff))
-    return 1;
-
-  /* Everything else is good, or can will automatically be made so.  */
-  return 0;
-}
-
 /* Return TRUE for valid operands for the rhs of an ARM instruction.  */
 int
 arm_rhs_operand (rtx op, enum machine_mode mode)
@@ -3843,9 +4483,10 @@ f_register_operand (rtx op, enum machine_mode mode)
              || REGNO_REG_CLASS (REGNO (op)) == FPA_REGS));
 }
 
-/* Return TRUE for valid operands for the rhs of an FPA instruction.  */
+/* Return TRUE for valid operands for the rhs of an floating point insns.
+   Allows regs or certain consts on FPA, just regs for everything else.  */
 int
-fpa_rhs_operand (rtx op, enum machine_mode mode)
+arm_float_rhs_operand (rtx op, enum machine_mode mode)
 {
   if (s_register_operand (op, mode))
     return TRUE;
@@ -3853,14 +4494,14 @@ fpa_rhs_operand (rtx op, enum machine_mode mode)
   if (GET_MODE (op) != mode && mode != VOIDmode)
     return FALSE;
 
-  if (GET_CODE (op) == CONST_DOUBLE)
-    return const_double_rtx_ok_for_fpa (op);
+  if (TARGET_FPA && GET_CODE (op) == CONST_DOUBLE)
+    return arm_const_double_rtx (op);
 
   return FALSE;
 }
 
 int
-fpa_add_operand (rtx op, enum machine_mode mode)
+arm_float_add_operand (rtx op, enum machine_mode mode)
 {
   if (s_register_operand (op, mode))
     return TRUE;
@@ -3868,13 +4509,27 @@ fpa_add_operand (rtx op, enum machine_mode mode)
   if (GET_MODE (op) != mode && mode != VOIDmode)
     return FALSE;
 
-  if (GET_CODE (op) == CONST_DOUBLE)
-    return (const_double_rtx_ok_for_fpa (op) 
+  if (TARGET_FPA && GET_CODE (op) == CONST_DOUBLE)
+    return (arm_const_double_rtx (op)
            || neg_const_double_rtx_ok_for_fpa (op));
 
   return FALSE;
 }
 
+
+/* Return TRUE if OP is suitable for the rhs of a floating point comparison.
+   Depends which fpu we are targeting.  */
+
+int
+arm_float_compare_operand (rtx op, enum machine_mode mode)
+{
+  if (TARGET_VFP)
+    return vfp_compare_operand (op, mode);
+  else
+    return arm_float_rhs_operand (op, mode);
+}
+
+
 /* Return nonzero if OP is a valid Cirrus memory address pattern.  */
 int
 cirrus_memory_offset (rtx op)
@@ -3912,6 +4567,15 @@ cirrus_memory_offset (rtx op)
   return 0;
 }
 
+int
+arm_extendqisi_mem_op (rtx op, enum machine_mode mode)
+{
+  if (!memory_operand (op, mode))
+    return 0;
+
+  return arm_legitimate_address_p (mode, XEXP (op, 0), SIGN_EXTEND, 0);
+}
+
 /* Return nonzero if OP is a Cirrus or general register.  */
 int
 cirrus_register_operand (rtx op, enum machine_mode mode)
@@ -3951,6 +4615,84 @@ cirrus_shift_const (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
          && INTVAL (op) < 64);
 }
 
+
+/* Return TRUE if OP is a valid VFP memory address pattern.  */
+/* Copied from cirrus_memory_offset but with restricted offset range.  */
+
+int
+vfp_mem_operand (rtx op)
+{
+  /* Reject eliminable registers.  */
+
+  if (! (reload_in_progress || reload_completed)
+      && (   reg_mentioned_p (frame_pointer_rtx, op)
+         || reg_mentioned_p (arg_pointer_rtx, op)
+         || reg_mentioned_p (virtual_incoming_args_rtx, op)
+         || reg_mentioned_p (virtual_outgoing_args_rtx, op)
+         || reg_mentioned_p (virtual_stack_dynamic_rtx, op)
+         || reg_mentioned_p (virtual_stack_vars_rtx, op)))
+    return FALSE;
+
+  /* Constants are converted into offsets from labels.  */
+  if (GET_CODE (op) == MEM)
+    {
+      rtx ind;
+
+      ind = XEXP (op, 0);
+
+      if (reload_completed
+         && (GET_CODE (ind) == LABEL_REF
+             || (GET_CODE (ind) == CONST
+                 && GET_CODE (XEXP (ind, 0)) == PLUS
+                 && GET_CODE (XEXP (XEXP (ind, 0), 0)) == LABEL_REF
+                 && GET_CODE (XEXP (XEXP (ind, 0), 1)) == CONST_INT)))
+       return TRUE;
+
+      /* Match: (mem (reg)).  */
+      if (GET_CODE (ind) == REG)
+       return arm_address_register_rtx_p (ind, 0);
+
+      /* Match:
+        (mem (plus (reg)
+                   (const))).  */
+      if (GET_CODE (ind) == PLUS
+         && GET_CODE (XEXP (ind, 0)) == REG
+         && REG_MODE_OK_FOR_BASE_P (XEXP (ind, 0), VOIDmode)
+         && GET_CODE (XEXP (ind, 1)) == CONST_INT
+         && INTVAL (XEXP (ind, 1)) > -1024
+         && INTVAL (XEXP (ind, 1)) <  1024)
+       return TRUE;
+    }
+
+  return FALSE;
+}
+
+
+/* Return TRUE if OP is a REG or constant zero.  */
+int
+vfp_compare_operand (rtx op, enum machine_mode mode)
+{
+  if (s_register_operand (op, mode))
+    return TRUE;
+
+  return (GET_CODE (op) == CONST_DOUBLE
+         && arm_const_double_rtx (op));
+}
+
+
+/* Return GENERAL_REGS if a scratch register required to reload x to/from
+   VFP registers.  Otherwise return NO_REGS.  */
+
+enum reg_class
+vfp_secondary_reload_class (enum machine_mode mode, rtx x)
+{
+  if (vfp_mem_operand (x) || s_register_operand (x, mode))
+    return NO_REGS;
+
+  return GENERAL_REGS;
+}
+
+
 /* Returns TRUE if INSN is an "LDR REG, ADDR" instruction.
    Use by the Cirrus Maverick code which has to workaround
    a hardware bug triggered by such instructions.  */
@@ -4186,7 +4928,7 @@ nonimmediate_di_operand (rtx op, enum machine_mode mode)
   return FALSE;
 }
 
-/* Return TRUE for a valid operand of a DFmode operation when -msoft-float.
+/* Return TRUE for a valid operand of a DFmode operation when soft-float.
    Either: REG, SUBREG, CONST_DOUBLE or MEM(DImode_address).
    Note that this disallows MEM(REG+REG), but allows
    MEM(PRE/POST_INC/DEC(REG)).  */
@@ -4518,7 +5260,6 @@ load_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
       /* Now check it more carefully.  */
       if (GET_CODE (SET_DEST (elt)) != REG
           || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
-          || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
           || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
           || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
         return 0;
@@ -4578,7 +5319,6 @@ store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
       /* Now check it more carefully.  */
       if (GET_CODE (SET_DEST (elt)) != REG
           || GET_CODE (XEXP (SET_SRC (elt), 0)) != REG
-          || REGNO (XEXP (SET_SRC (elt), 0)) != REGNO (SET_DEST (elt))
           || GET_CODE (XEXP (SET_SRC (elt), 1)) != CONST_INT
           || INTVAL (XEXP (SET_SRC (elt), 1)) != (count - 1) * 4)
         return 0;
@@ -5341,7 +6081,7 @@ arm_gen_movstrqi (rtx *operands)
            {
              rtx tmp = gen_reg_rtx (SImode);
 
-             emit_insn (gen_addsi3 (dst, dst, GEN_INT (2)));
+             emit_insn (gen_addsi3 (dst, dst, const2_rtx));
              emit_insn (gen_lshrsi3 (tmp, part_bytes_reg, GEN_INT (16)));
              part_bytes_reg = tmp;
            }
@@ -5535,7 +6275,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
        case LE:
        case GT:
        case GE:
-         if (TARGET_CIRRUS)
+         if (TARGET_HARD_FLOAT && TARGET_MAVERICK)
            return CCFPmode;
          return CCFPEmode;
 
@@ -5574,21 +6314,21 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
   if (GET_CODE (x) == IF_THEN_ELSE
       && (XEXP (x, 2) == const0_rtx
          || XEXP (x, 2) == const1_rtx)
-      && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-      && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
+      && COMPARISON_P (XEXP (x, 0))
+      && COMPARISON_P (XEXP (x, 1)))
     return arm_select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1), 
                                         INTVAL (XEXP (x, 2)));
 
   /* Alternate canonicalizations of the above.  These are somewhat cleaner.  */
   if (GET_CODE (x) == AND
-      && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-      && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
+      && COMPARISON_P (XEXP (x, 0))
+      && COMPARISON_P (XEXP (x, 1)))
     return arm_select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1),
                                         DOM_CC_X_AND_Y);
 
   if (GET_CODE (x) == IOR
-      && GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == '<'
-      && GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == '<')
+      && COMPARISON_P (XEXP (x, 0))
+      && COMPARISON_P (XEXP (x, 1)))
     return arm_select_dominance_cc_mode (XEXP (x, 0), XEXP (x, 1),
                                         DOM_CC_X_OR_Y);
 
@@ -6263,7 +7003,7 @@ add_minipool_forward_ref (Mfix *fix)
         we have not already found an insertion point, then
         make sure that all such 8-byte aligned quantities are
         placed at the start of the pool.  */
-      if (TARGET_REALLY_IWMMXT
+      if (ARM_DOUBLEWORD_ALIGN
          && max_mp == NULL
          && fix->fix_size == 8
          && mp->fix_size != 8)
@@ -6447,7 +7187,8 @@ add_minipool_backward_ref (Mfix *fix)
            {
              /* For now, we do not allow the insertion of 8-byte alignment
                 requiring nodes anywhere but at the start of the pool.  */
-             if (TARGET_REALLY_IWMMXT && fix->fix_size == 8 && mp->fix_size != 8)
+             if (ARM_DOUBLEWORD_ALIGN
+                 && fix->fix_size == 8 && mp->fix_size != 8)
                return NULL;
              else
                min_mp = mp;
@@ -6466,7 +7207,7 @@ add_minipool_backward_ref (Mfix *fix)
             we have not already found an insertion point, then
             make sure that all such 8-byte aligned quantities are
             placed at the start of the pool.  */
-         else if (TARGET_REALLY_IWMMXT
+         else if (ARM_DOUBLEWORD_ALIGN
                   && min_mp == NULL
                   && fix->fix_size == 8
                   && mp->fix_size < 8)
@@ -6564,7 +7305,7 @@ dump_minipool (rtx scan)
   Mnode * nmp;
   int align64 = 0;
 
-  if (TARGET_REALLY_IWMMXT)
+  if (ARM_DOUBLEWORD_ALIGN)
     for (mp = minipool_vector_head; mp != NULL; mp = mp->next)
       if (mp->refcount > 0 && mp->fix_size == 8)
        {
@@ -6572,8 +7313,8 @@ dump_minipool (rtx scan)
          break;
        }
 
-  if (rtl_dump_file)
-    fprintf (rtl_dump_file,
+  if (dump_file)
+    fprintf (dump_file,
             ";; Emitting minipool after insn %u; address %ld; align %d (bytes)\n",
             INSN_UID (scan), (unsigned long) minipool_barrier->address, align64 ? 8 : 4);
 
@@ -6585,14 +7326,14 @@ dump_minipool (rtx scan)
     {
       if (mp->refcount > 0)
        {
-         if (rtl_dump_file)
+         if (dump_file)
            {
-             fprintf (rtl_dump_file, 
+             fprintf (dump_file, 
                       ";;  Offset %u, min %ld, max %ld ",
                       (unsigned) mp->offset, (unsigned long) mp->min_address,
                       (unsigned long) mp->max_address);
-             arm_print_value (rtl_dump_file, mp->value);
-             fputc ('\n', rtl_dump_file);
+             arm_print_value (dump_file, mp->value);
+             fputc ('\n', dump_file);
            }
 
          switch (mp->fix_size)
@@ -6806,22 +7547,22 @@ push_minipool_fix (rtx insn, HOST_WIDE_INT address, rtx *loc,
   if (fix->forwards == 0 && fix->backwards == 0)
     abort ();
 
-  /* With iWMMXt enabled, the pool is aligned to an 8-byte boundary.
+  /* With AAPCS/iWMMXt enabled, the pool is aligned to an 8-byte boundary.
      So there might be an empty word before the start of the pool.
      Hence we reduce the forward range by 4 to allow for this
      possibility.  */
-  if (TARGET_REALLY_IWMMXT && fix->fix_size == 8)
+  if (ARM_DOUBLEWORD_ALIGN && fix->fix_size == 8)
     fix->forwards -= 4;
 
-  if (rtl_dump_file)
+  if (dump_file)
     {
-      fprintf (rtl_dump_file,
+      fprintf (dump_file,
               ";; %smode fixup for i%d; addr %lu, range (%ld,%ld): ",
               GET_MODE_NAME (mode),
               INSN_UID (insn), (unsigned long) address, 
               -1 * (long)fix->backwards, (long)fix->forwards);
-      arm_print_value (rtl_dump_file, fix->value);
-      fprintf (rtl_dump_file, "\n");
+      arm_print_value (dump_file, fix->value);
+      fprintf (dump_file, "\n");
     }
 
   /* Add it to the chain of fixes.  */
@@ -6851,6 +7592,9 @@ note_invalid_constants (rtx insn, HOST_WIDE_INT address, int do_pushes)
   if (!constrain_operands (1))
     fatal_insn_not_found (insn);
 
+  if (recog_data.n_alternatives == 0)
+    return false;
+
   /* Fill in recog_op_alt with information about the constraints of this insn.  */
   preprocess_constraints ();
 
@@ -6880,9 +7624,21 @@ note_invalid_constants (rtx insn, HOST_WIDE_INT address, int do_pushes)
                   && CONSTANT_POOL_ADDRESS_P (XEXP (op, 0)))
            {
              if (do_pushes)
-               push_minipool_fix (insn, address, recog_data.operand_loc[opno],
-                                  recog_data.operand_mode[opno],
-                                  get_pool_constant (XEXP (op, 0)));
+               {
+                 rtx cop = avoid_constant_pool_reference (op);
+
+                 /* Casting the address of something to a mode narrower
+                    than a word can cause avoid_constant_pool_reference()
+                    to return the pool reference itself.  That's no good to
+                    us here.  Lets just hope that we can use the 
+                    constant pool value directly.  */
+                 if (op == cop)
+                   cop = get_pool_constant (XEXP (op, 0));
+
+                 push_minipool_fix (insn, address,
+                                    recog_data.operand_loc[opno],
+                                    recog_data.operand_mode[opno], cop);
+               }
 
              result = true;
            }
@@ -7066,13 +7822,13 @@ fp_immediate_constant (rtx x)
   REAL_VALUE_TYPE r;
   int i;
   
-  if (!fpa_consts_inited)
-    init_fpa_table ();
+  if (!fp_consts_inited)
+    init_fp_table ();
   
   REAL_VALUE_FROM_CONST_DOUBLE (r, x);
   for (i = 0; i < 8; i++)
-    if (REAL_VALUES_EQUAL (r, values_fpa[i]))
-      return strings_fpa[i];
+    if (REAL_VALUES_EQUAL (r, values_fp[i]))
+      return strings_fp[i];
 
   abort ();
 }
@@ -7083,12 +7839,12 @@ fp_const_from_val (REAL_VALUE_TYPE *r)
 {
   int i;
 
-  if (!fpa_consts_inited)
-    init_fpa_table ();
+  if (!fp_consts_inited)
+    init_fp_table ();
 
   for (i = 0; i < 8; i++)
-    if (REAL_VALUES_EQUAL (*r, values_fpa[i]))
-      return strings_fpa[i];
+    if (REAL_VALUES_EQUAL (*r, values_fp[i]))
+      return strings_fp[i];
 
   abort ();
 }
@@ -7133,6 +7889,143 @@ print_multi_reg (FILE *stream, const char *instr, int reg, int mask)
   fprintf (stream, "\n");
 }
 
+
+/* Output a FLDMX instruction to STREAM.
+   BASE if the register containing the address.
+   REG and COUNT specify the register range.
+   Extra registers may be added to avoid hardware bugs.  */
+
+static void
+arm_output_fldmx (FILE * stream, unsigned int base, int reg, int count)
+{
+  int i;
+
+  /* Workaround ARM10 VFPr1 bug.  */
+  if (count == 2 && !arm_arch6)
+    {
+      if (reg == 15)
+       reg--;
+      count++;
+    }
+
+  fputc ('\t', stream);
+  asm_fprintf (stream, "fldmfdx\t%r!, {", base);
+
+  for (i = reg; i < reg + count; i++)
+    {
+      if (i > reg)
+       fputs (", ", stream);
+      asm_fprintf (stream, "d%d", i);
+    }
+  fputs ("}\n", stream);
+
+}
+
+
+/* Output the assembly for a store multiple.  */
+
+const char *
+vfp_output_fstmx (rtx * operands)
+{
+  char pattern[100];
+  int p;
+  int base;
+  int i;
+
+  strcpy (pattern, "fstmfdx\t%m0!, {%P1");
+  p = strlen (pattern);
+
+  if (GET_CODE (operands[1]) != REG)
+    abort ();
+
+  base = (REGNO (operands[1]) - FIRST_VFP_REGNUM) / 2;
+  for (i = 1; i < XVECLEN (operands[2], 0); i++)
+    {
+      p += sprintf (&pattern[p], ", d%d", base + i);
+    }
+  strcpy (&pattern[p], "}");
+
+  output_asm_insn (pattern, operands);
+  return "";
+}
+
+
+/* Emit RTL to save block of VFP register pairs to the stack.  Returns the
+   number of bytes pushed.  */
+
+static int
+vfp_emit_fstmx (int base_reg, int count)
+{
+  rtx par;
+  rtx dwarf;
+  rtx tmp, reg;
+  int i;
+
+  /* Workaround ARM10 VFPr1 bug.  Data corruption can occur when exactly two
+     register pairs are stored by a store multiple insn.  We avoid this
+     by pushing an extra pair.  */
+  if (count == 2 && !arm_arch6)
+    {
+      if (base_reg == LAST_VFP_REGNUM - 3)
+       base_reg -= 2;
+      count++;
+    }
+
+  /* ??? The frame layout is implementation defined.  We describe
+     standard format 1 (equivalent to a FSTMD insn and unused pad word).
+     We really need some way of representing the whole block so that the
+     unwinder can figure it out at runtime.  */
+  par = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (count));
+  dwarf = gen_rtx_SEQUENCE (VOIDmode, rtvec_alloc (count + 1));
+
+  reg = gen_rtx_REG (DFmode, base_reg);
+  base_reg += 2;
+
+  XVECEXP (par, 0, 0)
+    = gen_rtx_SET (VOIDmode,
+                  gen_rtx_MEM (BLKmode,
+                               gen_rtx_PRE_DEC (BLKmode, stack_pointer_rtx)),
+                  gen_rtx_UNSPEC (BLKmode,
+                                  gen_rtvec (1, reg),
+                                  UNSPEC_PUSH_MULT));
+
+  tmp = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                    gen_rtx_PLUS (SImode, stack_pointer_rtx,
+                                  GEN_INT (-(count * 8 + 4))));
+  RTX_FRAME_RELATED_P (tmp) = 1;
+  XVECEXP (dwarf, 0, 0) = tmp;
+
+  tmp = gen_rtx_SET (VOIDmode,
+                    gen_rtx_MEM (DFmode, stack_pointer_rtx),
+                    reg);
+  RTX_FRAME_RELATED_P (tmp) = 1;
+  XVECEXP (dwarf, 0, 1) = tmp;
+
+  for (i = 1; i < count; i++)
+    {
+      reg = gen_rtx_REG (DFmode, base_reg);
+      base_reg += 2;
+      XVECEXP (par, 0, i) = gen_rtx_USE (VOIDmode, reg);
+
+      tmp = gen_rtx_SET (VOIDmode,
+                        gen_rtx_MEM (DFmode,
+                                     gen_rtx_PLUS (SImode,
+                                                   stack_pointer_rtx,
+                                                   GEN_INT (i * 8))),
+                        reg);
+      RTX_FRAME_RELATED_P (tmp) = 1;
+      XVECEXP (dwarf, 0, i + 1) = tmp;
+    }
+
+  par = emit_insn (par);
+  REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
+                                      REG_NOTES (par));
+  RTX_FRAME_RELATED_P (par) = 1;
+
+  return count * 8 + 4;
+}
+
+
 /* Output a 'call' insn.  */
 const char *
 output_call (rtx *operands)
@@ -7914,7 +8807,7 @@ output_ascii_pseudo_op (FILE *stream, const unsigned char *p, int len)
        case '\\':
          putc ('\\', stream);
          len_so_far++;
-         /* drop through.  */
+         /* Drop through.  */
 
        default:
          if (c >= ' ' && c <= '~')
@@ -7935,8 +8828,7 @@ output_ascii_pseudo_op (FILE *stream, const unsigned char *p, int len)
 }
 \f
 /* Compute the register sabe mask for registers 0 through 12
-   inclusive.  This code is used by both arm_compute_save_reg_mask
-   and arm_compute_initial_elimination_offset.  */
+   inclusive.  This code is used by arm_compute_save_reg_mask.  */
 static unsigned long
 arm_compute_save_reg0_reg12_mask (void)
 {
@@ -8072,7 +8964,51 @@ arm_compute_save_reg_mask (void)
   return save_reg_mask;
 }
 
-/* Generate a function exit sequence.  If REALLY_RETURN is true, then do
+
+/* Return the number of bytes required to save VFP registers.  */
+static int
+arm_get_vfp_saved_size (void)
+{
+  unsigned int regno;
+  int count;
+  int saved;
+
+  saved = 0;
+  /* Space for saved VFP registers.  */
+  if (TARGET_HARD_FLOAT && TARGET_VFP)
+    {
+      count = 0;
+      for (regno = FIRST_VFP_REGNUM;
+          regno < LAST_VFP_REGNUM;
+          regno += 2)
+       {
+         if ((!regs_ever_live[regno] || call_used_regs[regno])
+             && (!regs_ever_live[regno + 1] || call_used_regs[regno + 1]))
+           {
+             if (count > 0)
+               {
+                 /* Workaround ARM10 VFPr1 bug.  */
+                 if (count == 2 && !arm_arch6)
+                   count++;
+                 saved += count * 8 + 4;
+               }
+             count = 0;
+           }
+         else
+           count++;
+       }
+      if (count > 0)
+       {
+         if (count == 2 && !arm_arch6)
+           count++;
+         saved += count * 8 + 4;
+       }
+    }
+  return saved;
+}
+
+
+/* Generate a function exit sequence.  If REALLY_RETURN is false, then do
    everything bar the final return instruction.  */
 const char *
 output_return_instruction (rtx operand, int really_return, int reverse)
@@ -8082,6 +9018,7 @@ output_return_instruction (rtx operand, int really_return, int reverse)
   int reg;
   unsigned long live_regs_mask;
   unsigned long func_type;
+  arm_stack_offsets *offsets;
 
   func_type = arm_current_func_type ();
 
@@ -8090,8 +9027,9 @@ output_return_instruction (rtx operand, int really_return, int reverse)
 
   if (IS_VOLATILE (func_type) && TARGET_ABORT_NORETURN)
     {
-      /* If this function was declared non-returning, and we have found a tail 
-        call, then we have to trust that the called function won't return.  */
+      /* If this function was declared non-returning, and we have
+        found a tail call, then we have to trust that the called
+        function won't return.  */
       if (really_return)
        {
          rtx ops[2];
@@ -8130,15 +9068,25 @@ output_return_instruction (rtx operand, int really_return, int reverse)
        return_reg = reg_names[LR_REGNUM];
 
       if ((live_regs_mask & (1 << IP_REGNUM)) == (1 << IP_REGNUM))
-       /* There are two possible reasons for the IP register being saved.
-          Either a stack frame was created, in which case IP contains the
-          old stack pointer, or an ISR routine corrupted it.  If this in an
-          ISR routine then just restore IP, otherwise restore IP into SP.  */
-       if (! IS_INTERRUPT (func_type))
-         {
-           live_regs_mask &= ~ (1 << IP_REGNUM);
-           live_regs_mask |=   (1 << SP_REGNUM);
-         }
+       {
+         /* There are three possible reasons for the IP register
+            being saved.  1) a stack frame was created, in which case
+            IP contains the old stack pointer, or 2) an ISR routine
+            corrupted it, or 3) it was saved to align the stack on
+            iWMMXt.  In case 1, restore IP into SP, otherwise just
+            restore IP.  */
+         if (frame_pointer_needed)
+           {
+             live_regs_mask &= ~ (1 << IP_REGNUM);
+             live_regs_mask |=   (1 << SP_REGNUM);
+           }
+         else
+           {
+             if (! IS_INTERRUPT (func_type)
+                 && ! TARGET_REALLY_IWMMXT)
+               abort ();
+           }
+       }
 
       /* On some ARM architectures it is faster to use LDR rather than
         LDM to load a single register.  On other architectures, the
@@ -8163,11 +9111,30 @@ output_return_instruction (rtx operand, int really_return, int reverse)
          char *p;
          int first = 1;
 
-         /* Generate the load multiple instruction to restore the registers.  */
-         if (frame_pointer_needed)
-           sprintf (instr, "ldm%sea\t%%|fp, {", conditional);
-         else if (live_regs_mask & (1 << SP_REGNUM))
-           sprintf (instr, "ldm%sfd\t%%|sp, {", conditional);
+         /* Generate the load multiple instruction to restore the
+            registers.  Note we can get here, even if
+            frame_pointer_needed is true, but only if sp already
+            points to the base of the saved core registers.  */
+         if (live_regs_mask & (1 << SP_REGNUM))
+           {
+             unsigned HOST_WIDE_INT stack_adjust;
+
+             offsets = arm_get_frame_offsets ();
+             stack_adjust = offsets->outgoing_args - offsets->saved_regs;
+             if (stack_adjust != 0 && stack_adjust != 4)
+               abort ();
+
+             if (stack_adjust && arm_arch5)
+               sprintf (instr, "ldm%sib\t%%|sp, {", conditional);
+             else
+               {
+                 /* If we can't use ldmib (SA110 bug), then try to pop r3
+                    instead.  */
+                 if (stack_adjust)
+                   live_regs_mask |= 1 << 3;
+                 sprintf (instr, "ldm%sfd\t%%|sp, {", conditional);
+               }
+           }
          else
            sprintf (instr, "ldm%sfd\t%%|sp!, {", conditional);
 
@@ -8374,7 +9341,7 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
 }
 
 const char *
-arm_output_epilogue (int really_return)
+arm_output_epilogue (rtx sibling)
 {
   int reg;
   unsigned long saved_regs_mask;
@@ -8383,14 +9350,16 @@ arm_output_epilogue (int really_return)
      frame that is $fp + 4 for a non-variadic function.  */
   int floats_offset = 0;
   rtx operands[3];
-  int frame_size = arm_get_frame_size ();
   FILE * f = asm_out_file;
   rtx eh_ofs = cfun->machine->eh_epilogue_sp_ofs;
   unsigned int lrm_count = 0;
+  int really_return = (sibling == NULL);
+  int start_reg;
+  arm_stack_offsets *offsets;
 
   /* If we have already generated the return instruction
      then it is futile to generate anything else.  */
-  if (use_return_insn (FALSE) && return_used_this_function)
+  if (use_return_insn (FALSE, sibling) && return_used_this_function)
     return "";
 
   func_type = arm_current_func_type ();
@@ -8417,14 +9386,13 @@ arm_output_epilogue (int really_return)
        be doing a return,  so we can't tail-call.  */
     abort ();
   
+  offsets = arm_get_frame_offsets ();
   saved_regs_mask = arm_compute_save_reg_mask ();
 
   if (TARGET_IWMMXT)
     lrm_count = bit_count (saved_regs_mask);
 
-  /* XXX We should adjust floats_offset for any anonymous args, and then
-     re-adjust vfp_offset below to compensate.  */
-
+  floats_offset = offsets->saved_args;
   /* Compute how far away the floats will be.  */
   for (reg = 0; reg <= LAST_ARM_REGNUM; reg++)
     if (saved_regs_mask & (1 << reg))
@@ -8432,11 +9400,12 @@ arm_output_epilogue (int really_return)
   
   if (frame_pointer_needed)
     {
-      int vfp_offset = 4;
+      /* This variable is for the Virtual Frame Pointer, not VFP regs.  */
+      int vfp_offset = offsets->frame;
 
       if (arm_fpu_arch == FPUTYPE_FPA_EMU2)
        {
-         for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
+         for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
            if (regs_ever_live[reg] && !call_used_regs[reg])
              {
                floats_offset += 12;
@@ -8446,9 +9415,9 @@ arm_output_epilogue (int really_return)
        }
       else
        {
-         int start_reg = LAST_ARM_FP_REGNUM;
+         start_reg = LAST_FPA_REGNUM;
 
-         for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
+         for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
            {
              if (regs_ever_live[reg] && !call_used_regs[reg])
                {
@@ -8479,6 +9448,39 @@ arm_output_epilogue (int really_return)
                         FP_REGNUM, floats_offset - vfp_offset);
        }
 
+      if (TARGET_HARD_FLOAT && TARGET_VFP)
+       {
+         int saved_size;
+
+         /* The fldmx insn does not have base+offset addressing modes,
+            so we use IP to hold the address.  */
+         saved_size = arm_get_vfp_saved_size ();
+
+         if (saved_size > 0)
+           {
+             floats_offset += saved_size;
+             asm_fprintf (f, "\tsub\t%r, %r, #%d\n", IP_REGNUM,
+                          FP_REGNUM, floats_offset - vfp_offset);
+           }
+         start_reg = FIRST_VFP_REGNUM;
+         for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2)
+           {
+             if ((!regs_ever_live[reg] || call_used_regs[reg])
+                 && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1]))
+               {
+                 if (start_reg != reg)
+                   arm_output_fldmx (f, IP_REGNUM,
+                                     (start_reg - FIRST_VFP_REGNUM) / 2,
+                                     (reg - start_reg) / 2);
+                 start_reg = reg + 2;
+               }
+           }
+         if (start_reg != reg)
+           arm_output_fldmx (f, IP_REGNUM,
+                             (start_reg - FIRST_VFP_REGNUM) / 2,
+                             (reg - start_reg) / 2);
+       }
+
       if (TARGET_IWMMXT)
        {
          /* The frame pointer is guaranteed to be non-double-word aligned.
@@ -8490,7 +9492,7 @@ arm_output_epilogue (int really_return)
             the live_regs_mask.  */
          lrm_count += (lrm_count % 2 ? 2 : 1);
              
-         for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++)
+         for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--)
            if (regs_ever_live[reg] && !call_used_regs[reg])
              {
                asm_fprintf (f, "\twldrd\t%r, [%r, #-%d]\n", 
@@ -8526,9 +9528,12 @@ arm_output_epilogue (int really_return)
          longer indicate the safe area of stack, and we can get stack
          corruption.  Using SP as the base register means that it will
          be reset correctly to the original value, should an interrupt
-         occur.  */
-      asm_fprintf (f, "\tsub\t%r,%r,#%d\n", SP_REGNUM, FP_REGNUM,
-                  4 * bit_count (saved_regs_mask));
+         occur.  If the stack pointer already points at the right
+         place, then omit the subtraction.  */
+      if (offsets->outgoing_args != (1 + (int) bit_count (saved_regs_mask))
+         || current_function_calls_alloca)
+       asm_fprintf (f, "\tsub\t%r, %r, #%d\n", SP_REGNUM, FP_REGNUM,
+                    4 * bit_count (saved_regs_mask));
       print_multi_reg (f, "ldmfd\t%r", SP_REGNUM, saved_regs_mask);
 
       if (IS_INTERRUPT (func_type))
@@ -8539,26 +9544,25 @@ arm_output_epilogue (int really_return)
   else
     {
       /* Restore stack pointer if necessary.  */
-      if (frame_size + current_function_outgoing_args_size != 0)
+      if (offsets->outgoing_args != offsets->saved_regs)
        {
          operands[0] = operands[1] = stack_pointer_rtx;
-         operands[2] = GEN_INT (frame_size
-                                + current_function_outgoing_args_size);
+         operands[2] = GEN_INT (offsets->outgoing_args - offsets->saved_regs);
          output_add_immediate (operands);
        }
 
       if (arm_fpu_arch == FPUTYPE_FPA_EMU2)
        {
-         for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
+         for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++)
            if (regs_ever_live[reg] && !call_used_regs[reg])
              asm_fprintf (f, "\tldfe\t%r, [%r], #12\n",
                           reg, SP_REGNUM);
        }
       else
        {
-         int start_reg = FIRST_ARM_FP_REGNUM;
+         start_reg = FIRST_FPA_REGNUM;
 
-         for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
+         for (reg = FIRST_FPA_REGNUM; reg <= LAST_FPA_REGNUM; reg++)
            {
              if (regs_ever_live[reg] && !call_used_regs[reg])
                {
@@ -8586,10 +9590,30 @@ arm_output_epilogue (int really_return)
                         start_reg, reg - start_reg, SP_REGNUM);
        }
 
+      if (TARGET_HARD_FLOAT && TARGET_VFP)
+       {
+         start_reg = FIRST_VFP_REGNUM;
+         for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2)
+           {
+             if ((!regs_ever_live[reg] || call_used_regs[reg])
+                 && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1]))
+               {
+                 if (start_reg != reg)
+                   arm_output_fldmx (f, SP_REGNUM,
+                                     (start_reg - FIRST_VFP_REGNUM) / 2,
+                                     (reg - start_reg) / 2);
+                 start_reg = reg + 2;
+               }
+           }
+         if (start_reg != reg)
+           arm_output_fldmx (f, SP_REGNUM,
+                             (start_reg - FIRST_VFP_REGNUM) / 2,
+                             (reg - start_reg) / 2);
+       }
       if (TARGET_IWMMXT)
        for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++)
          if (regs_ever_live[reg] && !call_used_regs[reg])
-           asm_fprintf (f, "\twldrd\t%r, [%r, #+8]!\n", reg, SP_REGNUM);
+           asm_fprintf (f, "\twldrd\t%r, [%r], #8\n", reg, SP_REGNUM);
 
       /* If we can, restore the LR into the PC.  */
       if (ARM_FUNC_TYPE (func_type) == ARM_FT_NORMAL
@@ -8685,8 +9709,10 @@ arm_output_epilogue (int really_return)
 
 static void
 arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
-                             HOST_WIDE_INT frame_size)
+                             HOST_WIDE_INT frame_size ATTRIBUTE_UNUSED)
 {
+  arm_stack_offsets *offsets;
+
   if (TARGET_THUMB)
     {
       /* ??? Probably not safe to set this here, since it assumes that a
@@ -8697,11 +9723,11 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
   else
     {
       /* We need to take into account any stack-frame rounding.  */
-      frame_size = arm_get_frame_size ();
+      offsets = arm_get_frame_offsets ();
 
-      if (use_return_insn (FALSE)
+      if (use_return_insn (FALSE, NULL)
          && return_used_this_function
-         && (frame_size + current_function_outgoing_args_size) != 0
+         && offsets->saved_regs != offsets->outgoing_args
          && !frame_pointer_needed)
        abort ();
 
@@ -8888,6 +9914,7 @@ emit_sfm (int base_reg, int count)
   return par;
 }
 
+
 /* Compute the distance from register FROM to register TO.
    These can be the arg pointer (26), the soft frame pointer (25),
    the stack pointer (13) or the hard frame pointer (11).
@@ -8930,60 +9957,152 @@ emit_sfm (int base_reg, int count)
 
   The sign of the number returned reflects the direction of stack
   growth, so the values are positive for all eliminations except
-  from the soft frame pointer to the hard frame pointer.  */
-unsigned int
-arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
+  from the soft frame pointer to the hard frame pointer.
+
+  SFP may point just inside the local variables block to ensure correct
+  alignment.  */
+
+
+/* Calculate stack offsets.  These are used to calculate register elimination
+   offsets and in prologue/epilogue code.  */
+
+static arm_stack_offsets *
+arm_get_frame_offsets (void)
 {
-  unsigned int local_vars    = arm_get_frame_size ();
-  unsigned int outgoing_args = current_function_outgoing_args_size;
-  unsigned int stack_frame;
-  unsigned int call_saved_registers;
+  struct arm_stack_offsets *offsets;
   unsigned long func_type;
+  int leaf;
+  int saved;
+  HOST_WIDE_INT frame_size;
+
+  offsets = &cfun->machine->stack_offsets;
   
-  func_type = arm_current_func_type ();
+  /* We need to know if we are a leaf function.  Unfortunately, it
+     is possible to be called after start_sequence has been called,
+     which causes get_insns to return the insns for the sequence,
+     not the function, which will cause leaf_function_p to return
+     the incorrect result.
 
-  /* Volatile functions never return, so there is
-     no need to save call saved registers.  */
-  call_saved_registers = 0;
-  if (! IS_VOLATILE (func_type))
+     to know about leaf functions once reload has completed, and the
+     frame size cannot be changed after that time, so we can safely
+     use the cached value.  */
+
+  if (reload_completed)
+    return offsets;
+
+  /* Initially this is the size of the local variables.  It will translated
+     into an offset once we have determined the size of preceding data.  */
+  frame_size = ROUND_UP_WORD (get_frame_size ());
+
+  leaf = leaf_function_p ();
+
+  /* Space for variadic functions.  */
+  offsets->saved_args = current_function_pretend_args_size;
+
+  offsets->frame = offsets->saved_args + (frame_pointer_needed ? 4 : 0);
+
+  if (TARGET_ARM)
+    {
+      unsigned int regno;
+
+      saved = bit_count (arm_compute_save_reg_mask ()) * 4;
+
+      /* We know that SP will be doubleword aligned on entry, and we must
+        preserve that condition at any subroutine call.  We also require the
+        soft frame pointer to be doubleword aligned.  */
+
+      if (TARGET_REALLY_IWMMXT)
+       {
+         /* Check for the call-saved iWMMXt registers.  */
+         for (regno = FIRST_IWMMXT_REGNUM;
+              regno <= LAST_IWMMXT_REGNUM;
+              regno++)
+           if (regs_ever_live [regno] && ! call_used_regs [regno])
+             saved += 8;
+       }
+
+      func_type = arm_current_func_type ();
+      if (! IS_VOLATILE (func_type))
+       {
+         /* Space for saved FPA registers.  */
+         for (regno = FIRST_FPA_REGNUM; regno <= LAST_FPA_REGNUM; regno++)
+         if (regs_ever_live[regno] && ! call_used_regs[regno])
+           saved += 12;
+
+         /* Space for saved VFP registers.  */
+         if (TARGET_HARD_FLOAT && TARGET_VFP)
+           saved += arm_get_vfp_saved_size ();
+       }
+    }
+  else /* TARGET_THUMB */
+    {
+      int reg;
+      int count_regs;
+
+      saved = 0;
+      count_regs = 0;
+      for (reg = 8; reg < 13; reg ++)
+       if (THUMB_REG_PUSHED_P (reg))
+         count_regs ++;
+      if (count_regs)
+       saved += 4 * count_regs;
+      count_regs = 0;
+      for (reg = 0; reg <= LAST_LO_REGNUM; reg ++)
+       if (THUMB_REG_PUSHED_P (reg))
+         count_regs ++;
+      if (count_regs || ! leaf_function_p ()
+         || thumb_far_jump_used_p ())
+       saved += 4 * (count_regs + 1);
+      if (TARGET_BACKTRACE)
+       {
+         if ((count_regs & 0xFF) == 0 && (regs_ever_live[3] != 0))
+           saved += 20;
+         else
+           saved += 16;
+       }
+    }
+
+  /* Saved registers include the stack frame.  */
+  offsets->saved_regs = offsets->saved_args + saved;
+  offsets->soft_frame = offsets->saved_regs;
+  /* A leaf function does not need any stack alignment if it has nothing
+     on the stack.  */
+  if (leaf && frame_size == 0)
+    {
+      offsets->outgoing_args = offsets->soft_frame;
+      return offsets;
+    }
+
+  /* Ensure SFP has the correct alignment.  */
+  if (ARM_DOUBLEWORD_ALIGN
+      && (offsets->soft_frame & 7))
+    offsets->soft_frame += 4;
+
+  offsets->outgoing_args = offsets->soft_frame + frame_size
+                          + current_function_outgoing_args_size;
+
+  if (ARM_DOUBLEWORD_ALIGN)
     {
-      unsigned int reg_mask;
-      unsigned int reg;
+      /* Ensure SP remains doubleword aligned.  */
+      if (offsets->outgoing_args & 7)
+       offsets->outgoing_args += 4;
+      if (offsets->outgoing_args & 7)
+       abort ();
+    }
 
-      /* Make sure that we compute which registers will be saved
-        on the stack using the same algorithm that is used by
-        the prologue creation code.  */
-      reg_mask = arm_compute_save_reg_mask ();
-
-      /* Now count the number of bits set in save_reg_mask.
-        If we have already counted the registers in the stack
-        frame, do not count them again.  Non call-saved registers
-        might be saved in the call-save area of the stack, if
-        doing so will preserve the stack's alignment.  Hence we
-        must count them here.  For each set bit we need 4 bytes
-        of stack space.  */
-      if (frame_pointer_needed)
-       reg_mask &= 0x07ff;
-      call_saved_registers += 4 * bit_count (reg_mask);
+  return offsets;
+}
 
-      /* If the hard floating point registers are going to be
-        used then they must be saved on the stack as well.
-         Each register occupies 12 bytes of stack space.  */
-      for (reg = FIRST_ARM_FP_REGNUM; reg <= LAST_ARM_FP_REGNUM; reg++)
-       if (regs_ever_live[reg] && ! call_used_regs[reg])
-         call_saved_registers += 12;
 
-      if (TARGET_REALLY_IWMMXT)
-       /* Check for the call-saved iWMMXt registers.  */
-       for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++)
-         if (regs_ever_live[reg] && ! call_used_regs [reg])
-           call_saved_registers += 8;
-    }
+/* Calculate the relative offsets for the different stack pointers.  Positive
+   offsets are in the direction of stack growth.  */
 
-  /* The stack frame contains 4 registers - the old frame pointer,
-     the old stack pointer, the return address and PC of the start
-     of the function.  */
-  stack_frame = frame_pointer_needed ? 16 : 0;
+unsigned int
+arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
+{
+  arm_stack_offsets *offsets;
+
+  offsets = arm_get_frame_offsets ();
 
   /* OK, now we have enough information to compute the distances.
      There must be an entry in these switch tables for each pair
@@ -9000,24 +10119,22 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
        case FRAME_POINTER_REGNUM:
          /* This is the reverse of the soft frame pointer
             to hard frame pointer elimination below.  */
-         if (call_saved_registers == 0 && stack_frame == 0)
-           return 0;
-         return (call_saved_registers + stack_frame - 4);
+         return offsets->soft_frame - offsets->saved_args;
 
        case ARM_HARD_FRAME_POINTER_REGNUM:
          /* If there is no stack frame then the hard
             frame pointer and the arg pointer coincide.  */
-         if (stack_frame == 0 && call_saved_registers != 0)
+         if (offsets->frame == offsets->saved_regs)
            return 0;
-         /* FIXME:  Not sure about this.  Maybe we should always return 0 ?  */
-         return (frame_pointer_needed
-                 && current_function_needs_context
-                 && ! cfun->machine->uses_anonymous_args) ? 4 : 0;
+          /* FIXME:  Not sure about this.  Maybe we should always return 0 ?  */
+          return (frame_pointer_needed
+                  && current_function_needs_context
+                  && ! cfun->machine->uses_anonymous_args) ? 4 : 0;
 
        case STACK_POINTER_REGNUM:
          /* If nothing has been pushed on the stack at all
             then this will return -4.  This *is* correct!  */
-         return call_saved_registers + stack_frame + local_vars + outgoing_args - 4;
+         return offsets->outgoing_args - (offsets->saved_args + 4);
 
        default:
          abort ();
@@ -9035,12 +10152,11 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
             stack frame.  The soft frame pointer to the bottom entry
             in the stack frame.  If there is no stack frame at all,
             then they are identical.  */
-         if (call_saved_registers == 0 && stack_frame == 0)
-           return 0;
-         return - (call_saved_registers + stack_frame - 4);
+
+         return offsets->frame - offsets->soft_frame;
 
        case STACK_POINTER_REGNUM:
-         return local_vars + outgoing_args;
+         return offsets->outgoing_args - offsets->soft_frame;
 
        default:
          abort ();
@@ -9057,85 +10173,6 @@ arm_compute_initial_elimination_offset (unsigned int from, unsigned int to)
     }
 }
 
-/* Calculate the size of the stack frame, taking into account any
-   padding that is required to ensure stack-alignment.  */
-HOST_WIDE_INT
-arm_get_frame_size (void)
-{
-  int regno;
-
-  int base_size = ROUND_UP_WORD (get_frame_size ());
-  int entry_size = 0;
-  unsigned long func_type = arm_current_func_type ();
-  int leaf;
-
-  if (! TARGET_ARM)
-    abort();
-
-  if (! TARGET_ATPCS)
-    return base_size;
-
-  /* We need to know if we are a leaf function.  Unfortunately, it
-     is possible to be called after start_sequence has been called,
-     which causes get_insns to return the insns for the sequence,
-     not the function, which will cause leaf_function_p to return
-     the incorrect result.
-
-     To work around this, we cache the computed frame size.  This
-     works because we will only be calling RTL expanders that need
-     to know about leaf functions once reload has completed, and the
-     frame size cannot be changed after that time, so we can safely
-     use the cached value.  */
-
-  if (reload_completed)
-    return cfun->machine->frame_size;
-
-  leaf = leaf_function_p ();
-
-  /* A leaf function does not need any stack alignment if it has nothing
-     on the stack.  */
-  if (leaf && base_size == 0)
-    {
-      cfun->machine->frame_size = 0;
-      return 0;
-    }
-
-  /* We know that SP will be word aligned on entry, and we must
-     preserve that condition at any subroutine call.  But those are
-     the only constraints.  */
-
-  /* Space for variadic functions.  */
-  if (current_function_pretend_args_size)
-    entry_size += current_function_pretend_args_size;
-
-  /* Space for saved registers.  */
-  entry_size += bit_count (arm_compute_save_reg_mask ()) * 4;
-
-  /* Space for saved FPA registers.  */
-  if (! IS_VOLATILE (func_type))
-    {
-      for (regno = FIRST_ARM_FP_REGNUM; regno <= LAST_ARM_FP_REGNUM; regno++)
-      if (regs_ever_live[regno] && ! call_used_regs[regno])
-       entry_size += 12;
-    }
-
-  if (TARGET_REALLY_IWMMXT)
-    {
-      /* Check for the call-saved iWMMXt registers.  */
-      for (regno = FIRST_IWMMXT_REGNUM; regno <= LAST_IWMMXT_REGNUM; regno++)
-       if (regs_ever_live [regno] && ! call_used_regs [regno])
-         entry_size += 8;
-    }
-
-  if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
-    base_size += 4;
-  if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
-    abort ();
-
-  cfun->machine->frame_size = base_size;
-
-  return base_size;
-}
 
 /* Generate the prologue instructions for entry into an ARM function.  */
 void
@@ -9149,7 +10186,9 @@ arm_expand_prologue (void)
   unsigned long func_type;
   int fp_offset = 0;
   int saved_pretend_args = 0;
+  int saved_regs = 0;
   unsigned int args_to_push;
+  arm_stack_offsets *offsets;
 
   func_type = arm_current_func_type ();
 
@@ -9295,11 +10334,12 @@ arm_expand_prologue (void)
   if (live_regs_mask)
     {
       insn = emit_multi_reg_push (live_regs_mask);
+      saved_regs += bit_count (live_regs_mask) * 4;
       RTX_FRAME_RELATED_P (insn) = 1;
     }
 
   if (TARGET_IWMMXT)
-    for (reg = FIRST_IWMMXT_REGNUM; reg <= LAST_IWMMXT_REGNUM; reg++)
+    for (reg = LAST_IWMMXT_REGNUM; reg >= FIRST_IWMMXT_REGNUM; reg--)
       if (regs_ever_live[reg] && ! call_used_regs [reg])
        {
          insn = gen_rtx_PRE_DEC (V2SImode, stack_pointer_rtx);
@@ -9307,15 +10347,18 @@ arm_expand_prologue (void)
          insn = emit_insn (gen_rtx_SET (VOIDmode, insn,
                                         gen_rtx_REG (V2SImode, reg)));
          RTX_FRAME_RELATED_P (insn) = 1;
+         saved_regs += 8;
        }
 
   if (! IS_VOLATILE (func_type))
     {
+      int start_reg;
+
       /* Save any floating point call-saved registers used by this
         function.  */
       if (arm_fpu_arch == FPUTYPE_FPA_EMU2)
        {
-         for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
+         for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
            if (regs_ever_live[reg] && !call_used_regs[reg])
              {
                insn = gen_rtx_PRE_DEC (XFmode, stack_pointer_rtx);
@@ -9323,13 +10366,14 @@ arm_expand_prologue (void)
                insn = emit_insn (gen_rtx_SET (VOIDmode, insn,
                                               gen_rtx_REG (XFmode, reg)));
                RTX_FRAME_RELATED_P (insn) = 1;
+               saved_regs += 12;
              }
        }
       else
        {
-         int start_reg = LAST_ARM_FP_REGNUM;
+         start_reg = LAST_FPA_REGNUM;
 
-         for (reg = LAST_ARM_FP_REGNUM; reg >= FIRST_ARM_FP_REGNUM; reg--)
+         for (reg = LAST_FPA_REGNUM; reg >= FIRST_FPA_REGNUM; reg--)
            {
              if (regs_ever_live[reg] && !call_used_regs[reg])
                {
@@ -9346,6 +10390,7 @@ arm_expand_prologue (void)
                    {
                      insn = emit_sfm (reg + 1, start_reg - reg);
                      RTX_FRAME_RELATED_P (insn) = 1;
+                     saved_regs += (start_reg - reg) * 12;
                    }
                  start_reg = reg - 1;
                }
@@ -9354,9 +10399,29 @@ arm_expand_prologue (void)
          if (start_reg != reg)
            {
              insn = emit_sfm (reg + 1, start_reg - reg);
+             saved_regs += (start_reg - reg) * 12;
              RTX_FRAME_RELATED_P (insn) = 1;
            }
        }
+      if (TARGET_HARD_FLOAT && TARGET_VFP)
+       {
+         start_reg = FIRST_VFP_REGNUM;
+
+         for (reg = FIRST_VFP_REGNUM; reg < LAST_VFP_REGNUM; reg += 2)
+           {
+             if ((!regs_ever_live[reg] || call_used_regs[reg])
+                 && (!regs_ever_live[reg + 1] || call_used_regs[reg + 1]))
+               {
+                 if (start_reg != reg)
+                   saved_regs += vfp_emit_fstmx (start_reg,
+                                                 (reg - start_reg) / 2);
+                 start_reg = reg + 2;
+               }
+           }
+         if (start_reg != reg)
+           saved_regs += vfp_emit_fstmx (start_reg,
+                                         (reg - start_reg) / 2);
+       }
     }
 
   if (frame_pointer_needed)
@@ -9385,14 +10450,16 @@ arm_expand_prologue (void)
        }
     }
 
-  amount = GEN_INT (-(arm_get_frame_size ()
-                     + current_function_outgoing_args_size));
-
-  if (amount != const0_rtx)
+  offsets = arm_get_frame_offsets ();
+  if (offsets->outgoing_args != offsets->saved_args + saved_regs)
     {
       /* This add can produce multiple insns for a large constant, so we
         need to get tricky.  */
       rtx last = get_last_insn ();
+
+      amount = GEN_INT (offsets->saved_args + saved_regs
+                       - offsets->outgoing_args);
+
       insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
                                    amount));
       do
@@ -9676,6 +10743,27 @@ arm_print_operand (FILE *stream, rtx x, int code)
        }
       return;
 
+      /* Print a VFP double precision register name.  */
+    case 'P':
+      {
+       int mode = GET_MODE (x);
+       int num;
+
+       if (mode != DImode && mode != DFmode)
+         abort ();
+
+       if (GET_CODE (x) != REG
+           || !IS_VFP_REGNUM (REGNO (x)))
+         abort ();
+
+       num = REGNO(x) - FIRST_VFP_REGNUM;
+       if (num & 1)
+         abort ();
+
+       fprintf (stream, "d%d", num >> 1);
+      }
+      return;
+
     default:
       if (x == 0)
        abort ();
@@ -9711,7 +10799,7 @@ arm_assemble_integer (rtx x, unsigned int size, int aligned_p)
       output_addr_const (asm_out_file, x);
 
       /* Mark symbols as position independent.  We only do this in the
-        .text segment, not in the .data segment. */
+        .text segment, not in the .data segment.  */
       if (NEED_GOT_RELOC && flag_pic && making_const_table &&
          (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF))
        {
@@ -10156,7 +11244,7 @@ arm_final_prescan_insn (rtx insn)
              /* Fail if a conditional return is undesirable (eg on a
                 StrongARM), but still allow this if optimizing for size.  */
              else if (GET_CODE (scanbody) == RETURN
-                      && !use_return_insn (TRUE)
+                      && !use_return_insn (TRUE, NULL)
                       && !optimize_size)
                fail = TRUE;
              else if (GET_CODE (scanbody) == RETURN
@@ -10222,7 +11310,7 @@ arm_final_prescan_insn (rtx insn)
                }
              if (!this_insn)
                {
-                 /* Oh, dear! we ran off the end.. give up */
+                 /* Oh, dear! we ran off the end.. give up */
                  recog (PATTERN (insn), insn, NULL);
                  arm_ccfsm_state = 0;
                  arm_target_insn = NULL;
@@ -10271,7 +11359,7 @@ int
 arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
 {
   if (GET_MODE_CLASS (mode) == MODE_CC)
-    return regno == CC_REGNUM;
+    return regno == CC_REGNUM || regno == VFPCC_REGNUM;
   
   if (TARGET_THUMB)
     /* For the Thumb we only allow values bigger than SImode in
@@ -10289,6 +11377,17 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
        get sign extended to 64bits-- aldyh.  */
     return (GET_MODE_CLASS (mode) == MODE_FLOAT) || (mode == DImode);
 
+  if (IS_VFP_REGNUM (regno))
+    {
+      if (mode == SFmode || mode == SImode)
+       return TRUE;
+
+      /* DFmode values are only valid in even register pairs.  */
+      if (mode == DFmode)
+       return ((regno - FIRST_VFP_REGNUM) & 1) == 0;
+      return FALSE;
+    }
+
   if (IS_IWMMXT_GR_REGNUM (regno))
     return mode == SImode;
 
@@ -10307,8 +11406,8 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
   /* The only registers left are the FPA registers
      which we only allow to hold FP values.  */
   return GET_MODE_CLASS (mode) == MODE_FLOAT
-    && regno >= FIRST_ARM_FP_REGNUM
-    && regno <= LAST_ARM_FP_REGNUM;
+    && regno >= FIRST_FPA_REGNUM
+    && regno <= LAST_FPA_REGNUM;
 }
 
 int
@@ -10330,12 +11429,15 @@ arm_regno_class (int regno)
       || regno == ARG_POINTER_REGNUM)
     return GENERAL_REGS;
   
-  if (regno == CC_REGNUM)
+  if (regno == CC_REGNUM || regno == VFPCC_REGNUM)
     return NO_REGS;
 
   if (IS_CIRRUS_REGNUM (regno))
     return CIRRUS_REGS;
 
+  if (IS_VFP_REGNUM (regno))
+    return VFP_REGS;
+
   if (IS_IWMMXT_REGNUM (regno))
     return IWMMXT_REGS;
 
@@ -10399,7 +11501,7 @@ arm_debugger_arg_offset (int value, rtx addr)
      which is the frame pointer
      a constant integer
 
-     then... */
+     then...  */
   
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
@@ -10471,8 +11573,8 @@ static const struct builtin_description bdesc_2arg[] =
   IWMMXT_BUILTIN (ussubv4hi3, "wsubhus", WSUBUSH)
   IWMMXT_BUILTIN (ussubv2si3, "wsubwus", WSUBUSW)
   IWMMXT_BUILTIN (mulv4hi3, "wmulul", WMULUL)
-  IWMMXT_BUILTIN (smulv4hi3_highpart, "wmulsh", WMULSH)
-  IWMMXT_BUILTIN (umulv4hi3_highpart, "wmuluh", WMULUH)
+  IWMMXT_BUILTIN (smulv4hi3_highpart, "wmulsm", WMULSM)
+  IWMMXT_BUILTIN (umulv4hi3_highpart, "wmulum", WMULUM)
   IWMMXT_BUILTIN (eqv8qi3, "wcmpeqb", WCMPEQB)
   IWMMXT_BUILTIN (eqv4hi3, "wcmpeqh", WCMPEQH)
   IWMMXT_BUILTIN (eqv2si3, "wcmpeqw", WCMPEQW)
@@ -10531,13 +11633,13 @@ static const struct builtin_description bdesc_2arg[] =
   IWMMXT_BUILTIN2 (lshrv2si3_di,    WSRLW)
   IWMMXT_BUILTIN2 (lshrv2si3,       WSRLWI)
   IWMMXT_BUILTIN2 (lshrdi3_di,      WSRLD)
-  IWMMXT_BUILTIN2 (lshrdi3,         WSRLDI)
+  IWMMXT_BUILTIN2 (lshrdi3_iwmmxt,  WSRLDI)
   IWMMXT_BUILTIN2 (ashrv4hi3_di,    WSRAH)
   IWMMXT_BUILTIN2 (ashrv4hi3,       WSRAHI)
   IWMMXT_BUILTIN2 (ashrv2si3_di,    WSRAW)
   IWMMXT_BUILTIN2 (ashrv2si3,       WSRAWI)
   IWMMXT_BUILTIN2 (ashrdi3_di,      WSRAD)
-  IWMMXT_BUILTIN2 (ashrdi3,         WSRADI)
+  IWMMXT_BUILTIN2 (ashrdi3_iwmmxt,  WSRADI)
   IWMMXT_BUILTIN2 (rorv4hi3_di,     WRORH)
   IWMMXT_BUILTIN2 (rorv4hi3,        WRORHI)
   IWMMXT_BUILTIN2 (rorv2si3_di,     WRORW)
@@ -10580,6 +11682,10 @@ arm_init_iwmmxt_builtins (void)
   size_t i;
   tree endlink = void_list_node;
 
+  tree V2SI_type_node = build_vector_type_for_mode (intSI_type_node, V2SImode);
+  tree V4HI_type_node = build_vector_type_for_mode (intHI_type_node, V4HImode);
+  tree V8QI_type_node = build_vector_type_for_mode (intQI_type_node, V8QImode);
+
   tree int_ftype_int
     = build_function_type (integer_type_node,
                           tree_cons (NULL_TREE, integer_type_node, endlink));
@@ -11102,9 +12208,9 @@ arm_expand_builtin (tree exp,
     case ARM_BUILTIN_SETWCX:
       arg0 = TREE_VALUE (arglist);
       arg1 = TREE_VALUE (TREE_CHAIN (arglist));
-      op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+      op0 = force_reg (SImode, expand_expr (arg0, NULL_RTX, VOIDmode, 0));
       op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
-      emit_insn (gen_iwmmxt_tmcr (op0, op1));
+      emit_insn (gen_iwmmxt_tmcr (op1, op0));
       return 0;
 
     case ARM_BUILTIN_GETWCX:
@@ -11424,7 +12530,8 @@ thumb_exit (FILE *f, int reg_containing_return_addr, rtx eh_ofs)
     }
 
   /* Pop as many registers as we can.  */
-  thumb_pushpop (f, regs_available_for_popping, FALSE);
+  thumb_pushpop (f, regs_available_for_popping, FALSE, NULL,
+                regs_available_for_popping);
 
   /* Process the registers we popped.  */
   if (reg_containing_return_addr == -1)
@@ -11505,7 +12612,8 @@ thumb_exit (FILE *f, int reg_containing_return_addr, rtx eh_ofs)
       int  popped_into;
       int  move_to;
       
-      thumb_pushpop (f, regs_available_for_popping, FALSE);
+      thumb_pushpop (f, regs_available_for_popping, FALSE, NULL,
+                    regs_available_for_popping);
 
       /* We have popped either FP or SP.
         Move whichever one it is into the correct register.  */
@@ -11525,7 +12633,8 @@ thumb_exit (FILE *f, int reg_containing_return_addr, rtx eh_ofs)
     {
       int  popped_into;
       
-      thumb_pushpop (f, regs_available_for_popping, FALSE);
+      thumb_pushpop (f, regs_available_for_popping, FALSE, NULL,
+                    regs_available_for_popping);
 
       popped_into = number_of_first_bit_set (regs_available_for_popping);
 
@@ -11555,12 +12664,20 @@ thumb_exit (FILE *f, int reg_containing_return_addr, rtx eh_ofs)
   asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
 }
 
-/* Emit code to push or pop registers to or from the stack.  */
+/* Emit code to push or pop registers to or from the stack.  F is the
+   assembly file.  MASK is the registers to push or pop.  PUSH is
+   nonzero if we should push, and zero if we should pop.  For debugging
+   output, if pushing, adjust CFA_OFFSET by the amount of space added
+   to the stack.  REAL_REGS should have the same number of bits set as
+   MASK, and will be used instead (in the same order) to describe which
+   registers were saved - this is used to mark the save slots when we
+   push high registers after moving them to low registers.  */
 static void
-thumb_pushpop (FILE *f, int mask, int push)
+thumb_pushpop (FILE *f, int mask, int push, int *cfa_offset, int real_regs)
 {
   int regno;
   int lo_mask = mask & 0xFF;
+  int pushed_words = 0;
 
   if (lo_mask == 0 && !push && (mask & (1 << 15)))
     {
@@ -11581,6 +12698,8 @@ thumb_pushpop (FILE *f, int mask, int push)
          
          if ((lo_mask & ~1) != 0)
            fprintf (f, ", ");
+
+         pushed_words++;
        }
     }
   
@@ -11591,6 +12710,8 @@ thumb_pushpop (FILE *f, int mask, int push)
        fprintf (f, ", ");
       
       asm_fprintf (f, "%r", LR_REGNUM);
+
+      pushed_words++;
     }
   else if (!push && (mask & (1 << PC_REGNUM)))
     {
@@ -11615,6 +12736,23 @@ thumb_pushpop (FILE *f, int mask, int push)
     }
        
   fprintf (f, "}\n");
+
+  if (push && pushed_words && dwarf2out_do_frame ())
+    {
+      char *l = dwarf2out_cfi_label ();
+      int pushed_mask = real_regs;
+
+      *cfa_offset += pushed_words * 4;
+      dwarf2out_def_cfa (l, SP_REGNUM, *cfa_offset);
+
+      pushed_words = 0;
+      pushed_mask = real_regs;
+      for (regno = 0; regno <= 14; regno++, pushed_mask >>= 1)
+       {
+         if (pushed_mask & 1)
+           dwarf2out_reg_save (l, regno, 4 * pushed_words++ - *cfa_offset);
+       }
+    }
 }
 \f
 void
@@ -11643,8 +12781,8 @@ thumb_shiftable_const (unsigned HOST_WIDE_INT val)
 
 /* Returns nonzero if the current function contains,
    or might contain a far jump.  */
-int
-thumb_far_jump_used_p (int in_prologue)
+static int
+thumb_far_jump_used_p (void)
 {
   rtx insn;
 
@@ -11662,7 +12800,7 @@ thumb_far_jump_used_p (int in_prologue)
   /* If this function is not being called from the prologue/epilogue
      generation code then it must be being called from the
      INITIAL_ELIMINATION_OFFSET macro.  */
-  if (!in_prologue)
+  if (!(ARM_DOUBLEWORD_ALIGN || reload_completed))
     {
       /* In this case we know that we are being asked about the elimination
         of the arg pointer register.  If that register is not being used,
@@ -11681,7 +12819,10 @@ thumb_far_jump_used_p (int in_prologue)
 
         A false negative will not result in bad code being generated, but it
         will result in a needless push and pop of the link register.  We
-        hope that this does not occur too often.  */
+        hope that this does not occur too often.
+
+        If we need doubleword stack alignment this could affect the other
+        elimination offsets so we can't risk getting it wrong.  */
       if (regs_ever_live [ARG_POINTER_REGNUM])
        cfun->machine->arg_pointer_live = 1;
       else if (!cfun->machine->arg_pointer_live)
@@ -11727,7 +12868,7 @@ is_called_in_ARM_mode (tree func)
 #endif
 }
 
-/* The bits which aren't usefully expanded as rtl. */
+/* The bits which aren't usefully expanded as rtl.  */
 const char *
 thumb_unexpanded_epilogue (void)
 {
@@ -11812,8 +12953,8 @@ thumb_unexpanded_epilogue (void)
 
          mask &= (2 << regno) - 1;     /* A noop if regno == 8 */
 
-         /* Pop the values into the low register(s). */
-         thumb_pushpop (asm_out_file, mask, 0);
+         /* Pop the values into the low register(s).  */
+         thumb_pushpop (asm_out_file, mask, 0, NULL, mask);
 
          /* Move the value(s) into the high registers.  */
          for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
@@ -11832,7 +12973,7 @@ thumb_unexpanded_epilogue (void)
     }
 
   had_to_push_lr = (live_regs_mask || !leaf_function
-                   || thumb_far_jump_used_p (1));
+                   || thumb_far_jump_used_p ());
   
   if (TARGET_BACKTRACE
       && ((live_regs_mask & 0xFF) == 0)
@@ -11840,7 +12981,7 @@ thumb_unexpanded_epilogue (void)
     {
       /* The stack backtrace structure creation code had to
         push R7 in order to get a work register, so we pop
-        it now.   */
+        it now.  */
       live_regs_mask |= (1 << LAST_LO_REGNUM);
     }
   
@@ -11855,7 +12996,8 @@ thumb_unexpanded_epilogue (void)
         structure was created which includes an adjusted stack
         pointer, so just pop everything.  */
       if (live_regs_mask)
-       thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
+       thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL,
+                      live_regs_mask);
       
       if (eh_ofs)
        thumb_exit (asm_out_file, 2, eh_ofs);
@@ -11875,11 +13017,13 @@ thumb_unexpanded_epilogue (void)
       live_regs_mask &= ~(1 << PC_REGNUM);
       
       if (live_regs_mask)
-       thumb_pushpop (asm_out_file, live_regs_mask, FALSE);
+       thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL,
+                      live_regs_mask);
 
       if (had_to_push_lr)
        /* Get the return address into a temporary register.  */
-       thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0);
+       thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0, NULL,
+                      1 << LAST_ARG_REGNUM);
       
       /* Remove the argument registers that were pushed onto the stack.  */
       asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n",
@@ -11935,98 +13079,66 @@ arm_init_expanders (void)
   init_machine_status = arm_init_machine_status;
 }
 
-HOST_WIDE_INT
-thumb_get_frame_size (void)
-{
-  int regno;
-
-  int base_size = ROUND_UP_WORD (get_frame_size ());
-  int count_regs = 0;
-  int entry_size = 0;
-  int leaf;
-
-  if (! TARGET_THUMB)
-    abort ();
-
-  if (! TARGET_ATPCS)
-    return base_size;
-
-  /* We need to know if we are a leaf function.  Unfortunately, it
-     is possible to be called after start_sequence has been called,
-     which causes get_insns to return the insns for the sequence,
-     not the function, which will cause leaf_function_p to return
-     the incorrect result.
-
-     To work around this, we cache the computed frame size.  This
-     works because we will only be calling RTL expanders that need
-     to know about leaf functions once reload has completed, and the
-     frame size cannot be changed after that time, so we can safely
-     use the cached value.  */
-
-  if (reload_completed)
-    return cfun->machine->frame_size;
-
-  leaf = leaf_function_p ();
-
-  /* A leaf function does not need any stack alignment if it has nothing
-     on the stack.  */
-  if (leaf && base_size == 0)
-    {
-      cfun->machine->frame_size = 0;
-      return 0;
-    }
 
-  /* We know that SP will be word aligned on entry, and we must
-     preserve that condition at any subroutine call.  But those are
-     the only constraints.  */
+/* Like arm_compute_initial_elimination offset.  Simpler because
+   THUMB_HARD_FRAME_POINTER isn't actually the ABI specified frame pointer.  */
 
-  /* Space for variadic functions.  */
-  if (current_function_pretend_args_size)
-    entry_size += current_function_pretend_args_size;
+HOST_WIDE_INT
+thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to)
+{
+  arm_stack_offsets *offsets;
 
-  /* Space for pushed lo registers.  */
-  for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
-    if (THUMB_REG_PUSHED_P (regno))
-      count_regs++;
+  offsets = arm_get_frame_offsets ();
 
-  /* Space for backtrace structure.  */
-  if (TARGET_BACKTRACE)
+  switch (from)
     {
-      if (count_regs == 0 && regs_ever_live[LAST_ARG_REGNUM] != 0)
-       entry_size += 20;
-      else
-       entry_size += 16;
-    }
+    case ARG_POINTER_REGNUM:
+      switch (to)
+       {
+       case STACK_POINTER_REGNUM:
+         return offsets->outgoing_args - offsets->saved_args;
 
-  if (count_regs || !leaf || thumb_far_jump_used_p (1))
-    count_regs++;      /* LR */
+       case FRAME_POINTER_REGNUM:
+         return offsets->soft_frame - offsets->saved_args;
 
-  entry_size += count_regs * 4;
-  count_regs = 0;
+       case THUMB_HARD_FRAME_POINTER_REGNUM:
+       case ARM_HARD_FRAME_POINTER_REGNUM:
+         return offsets->saved_regs - offsets->saved_args;
 
-  /* Space for pushed hi regs.  */
-  for (regno = 8; regno < 13; regno++)
-    if (THUMB_REG_PUSHED_P (regno))
-      count_regs++;
+       default:
+         abort();
+       }
+      break;
 
-  entry_size += count_regs * 4;
+    case FRAME_POINTER_REGNUM:
+      switch (to)
+       {
+       case STACK_POINTER_REGNUM:
+         return offsets->outgoing_args - offsets->soft_frame;
 
-  if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
-    base_size += 4;
-  if ((entry_size + base_size + current_function_outgoing_args_size) & 7)
-    abort ();
+       case THUMB_HARD_FRAME_POINTER_REGNUM:
+       case ARM_HARD_FRAME_POINTER_REGNUM:
+         return offsets->saved_regs - offsets->soft_frame;
 
-  cfun->machine->frame_size = base_size;
+       default:
+         abort();
+       }
+      break;
 
-  return base_size;
+    default:
+      abort ();
+    }
 }
 
+
 /* Generate the rest of a function's prologue.  */
 void
 thumb_expand_prologue (void)
 {
-  HOST_WIDE_INT amount = (thumb_get_frame_size ()
-                         + current_function_outgoing_args_size);
+  rtx insn, dwarf;
+
+  HOST_WIDE_INT amount;
+  arm_stack_offsets *offsets;
   unsigned long func_type;
 
   func_type = arm_current_func_type ();
@@ -12041,16 +13153,24 @@ thumb_expand_prologue (void)
       return;
     }
 
+  offsets = arm_get_frame_offsets ();
+
   if (frame_pointer_needed)
-    emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx));
+    {
+      insn = emit_insn (gen_movsi (hard_frame_pointer_rtx,
+                                  stack_pointer_rtx));
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
 
+  amount = offsets->outgoing_args - offsets->saved_regs;
   if (amount)
     {
-      amount = ROUND_UP_WORD (amount);
-      
       if (amount < 512)
-       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                              GEN_INT (- amount)));
+       {
+         insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                       GEN_INT (- amount)));
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
       else
        {
          int regno;
@@ -12078,10 +13198,10 @@ thumb_expand_prologue (void)
 
          if (regno > LAST_LO_REGNUM) /* Very unlikely.  */
            {
-             rtx spare = gen_rtx (REG, SImode, IP_REGNUM);
+             rtx spare = gen_rtx_REG (SImode, IP_REGNUM);
 
              /* Choose an arbitrary, non-argument low register.  */
-             reg = gen_rtx (REG, SImode, LAST_LO_REGNUM);
+             reg = gen_rtx_REG (SImode, LAST_LO_REGNUM);
 
              /* Save it by copying it into a high, scratch register.  */
              emit_insn (gen_movsi (spare, reg));
@@ -12090,8 +13210,16 @@ thumb_expand_prologue (void)
 
              /* Decrement the stack.  */
              emit_insn (gen_movsi (reg, GEN_INT (- amount)));
-             emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                    reg));
+             insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
+                                           stack_pointer_rtx, reg));
+             RTX_FRAME_RELATED_P (insn) = 1;
+             dwarf = gen_rtx_SET (SImode, stack_pointer_rtx,
+                                  plus_constant (stack_pointer_rtx,
+                                                 GEN_INT (- amount)));
+             RTX_FRAME_RELATED_P (dwarf) = 1;
+             REG_NOTES (insn)
+               = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
+                                    REG_NOTES (insn));
 
              /* Restore the low register's original value.  */
              emit_insn (gen_movsi (reg, spare));
@@ -12104,13 +13232,28 @@ thumb_expand_prologue (void)
            }
          else
            {
-             reg = gen_rtx (REG, SImode, regno);
+             reg = gen_rtx_REG (SImode, regno);
 
              emit_insn (gen_movsi (reg, GEN_INT (- amount)));
-             emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                    reg));
+
+             insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
+                                           stack_pointer_rtx, reg));
+             RTX_FRAME_RELATED_P (insn) = 1;
+             dwarf = gen_rtx_SET (SImode, stack_pointer_rtx,
+                                  plus_constant (stack_pointer_rtx,
+                                                 GEN_INT (- amount)));
+             RTX_FRAME_RELATED_P (dwarf) = 1;
+             REG_NOTES (insn)
+               = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
+                                    REG_NOTES (insn));
            }
        }
+      /* If the frame pointer is needed, emit a special barrier that
+        will prevent the scheduler from moving stores to the frame
+        before the stack adjustment.  */
+      if (frame_pointer_needed)
+       emit_insn (gen_stack_tie (stack_pointer_rtx,
+                                 hard_frame_pointer_rtx));
     }
   
   if (current_function_profile || TARGET_NO_SCHED_PRO)
@@ -12120,27 +13263,28 @@ thumb_expand_prologue (void)
 void
 thumb_expand_epilogue (void)
 {
-  HOST_WIDE_INT amount = (thumb_get_frame_size ()
-                         + current_function_outgoing_args_size);
+  HOST_WIDE_INT amount;
+  arm_stack_offsets *offsets;
   int regno;
 
   /* Naked functions don't have prologues.  */
   if (IS_NAKED (arm_current_func_type ()))
     return;
 
+  offsets = arm_get_frame_offsets ();
+  amount = offsets->outgoing_args - offsets->saved_regs;
+
   if (frame_pointer_needed)
     emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
   else if (amount)
     {
-      amount = ROUND_UP_WORD (amount);
-      
       if (amount < 512)
        emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
                               GEN_INT (amount)));
       else
        {
          /* r3 is always free in the epilogue.  */
-         rtx reg = gen_rtx (REG, SImode, LAST_ARG_REGNUM);
+         rtx reg = gen_rtx_REG (SImode, LAST_ARG_REGNUM);
 
          emit_insn (gen_movsi (reg, GEN_INT (amount)));
          emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
@@ -12169,6 +13313,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
   int live_regs_mask = 0;
   int high_regs_pushed = 0;
+  int cfa_offset = 0;
   int regno;
 
   if (IS_NAKED (arm_current_func_type ()))
@@ -12195,7 +13340,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
         the assembler to bypass the ARM code when this function
         is called from a Thumb encoded function elsewhere in the
         same file.  Hence the definition of STUB_NAME here must
-        agree with the definition in gas/config/tc-arm.c  */
+        agree with the definition in gas/config/tc-arm.c.  */
       
 #define STUB_NAME ".real_start_of"
       
@@ -12231,13 +13376,23 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
        asm_fprintf (f, "\tsub\t%r, %r, #%d\n", 
                     SP_REGNUM, SP_REGNUM,
                     current_function_pretend_args_size);
+
+      /* We don't need to record the stores for unwinding (would it
+        help the debugger any if we did?), but record the change in
+        the stack pointer.  */
+      if (dwarf2out_do_frame ())
+       {
+         char *l = dwarf2out_cfi_label ();
+         cfa_offset = cfa_offset + current_function_pretend_args_size;
+         dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
+       }
     }
 
   for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
     if (THUMB_REG_PUSHED_P (regno))
       live_regs_mask |= 1 << regno;
 
-  if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p (1))
+  if (live_regs_mask || !leaf_function_p () || thumb_far_jump_used_p ())
     live_regs_mask |= 1 << LR_REGNUM;
 
   if (TARGET_BACKTRACE)
@@ -12270,7 +13425,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
          if (regs_ever_live [LAST_ARG_REGNUM] == 0)
            work_register = LAST_ARG_REGNUM;
-         else    /* We must push a register of our own */
+         else    /* We must push a register of our own */
            live_regs_mask |= (1 << LAST_LO_REGNUM);
        }
 
@@ -12286,9 +13441,16 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
       asm_fprintf
        (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n",
         SP_REGNUM, SP_REGNUM);
-      
+
+      if (dwarf2out_do_frame ())
+       {
+         char *l = dwarf2out_cfi_label ();
+         cfa_offset = cfa_offset + 16;
+         dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
+       }
+
       if (live_regs_mask)
-       thumb_pushpop (f, live_regs_mask, 1);
+       thumb_pushpop (f, live_regs_mask, 1, &cfa_offset, live_regs_mask);
       
       for (offset = 0, wr = 1 << 15; wr != 0; wr >>= 1)
        if (wr & live_regs_mask)
@@ -12332,7 +13494,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
                   ARM_HARD_FRAME_POINTER_REGNUM, work_register);
     }
   else if (live_regs_mask)
-    thumb_pushpop (f, live_regs_mask, 1);
+    thumb_pushpop (f, live_regs_mask, 1, &cfa_offset, live_regs_mask);
 
   for (regno = 8; regno < 13; regno++)
     if (THUMB_REG_PUSHED_P (regno))
@@ -12360,6 +13522,8 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
       while (high_regs_pushed > 0)
        {
+         int real_regs_mask = 0;
+
          for (regno = LAST_LO_REGNUM; regno >= 0; regno--)
            {
              if (mask & (1 << regno))
@@ -12367,6 +13531,7 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
                  asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg);
                  
                  high_regs_pushed--;
+                 real_regs_mask |= (1 << next_hi_reg);
                  
                  if (high_regs_pushed)
                    {
@@ -12382,8 +13547,8 @@ thumb_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
                    }
                }
            }
-         
-         thumb_pushpop (f, mask, 1);
+
+         thumb_pushpop (f, mask, 1, &cfa_offset, real_regs_mask);
        }
 
       if (pushable_regs == 0
@@ -12417,8 +13582,8 @@ thumb_load_double_from_address (rtx *operands)
   switch (GET_CODE (addr))
     {
     case REG:
-      operands[2] = gen_rtx (MEM, SImode,
-                            plus_constant (XEXP (operands[1], 0), 4));
+      operands[2] = gen_rtx_MEM (SImode,
+                                plus_constant (XEXP (operands[1], 0), 4));
 
       if (REGNO (operands[0]) == REGNO (addr))
        {
@@ -12434,8 +13599,8 @@ thumb_load_double_from_address (rtx *operands)
       
     case CONST:
       /* Compute <address> + 4 for the high order load.  */
-      operands[2] = gen_rtx (MEM, SImode,
-                            plus_constant (XEXP (operands[1], 0), 4));
+      operands[2] = gen_rtx_MEM (SImode,
+                                plus_constant (XEXP (operands[1], 0), 4));
       
       output_asm_insn ("ldr\t%0, %1", operands);
       output_asm_insn ("ldr\t%H0, %2", operands);
@@ -12478,8 +13643,8 @@ thumb_load_double_from_address (rtx *operands)
       else
        {
          /* Compute <address> + 4 for the high order load.  */
-         operands[2] = gen_rtx (MEM, SImode,
-                                plus_constant (XEXP (operands[1], 0), 4));
+         operands[2] = gen_rtx_MEM (SImode,
+                                    plus_constant (XEXP (operands[1], 0), 4));
          
          /* If the computed address is held in the low order register
             then load the high order register first, otherwise always
@@ -12500,8 +13665,8 @@ thumb_load_double_from_address (rtx *operands)
     case LABEL_REF:
       /* With no registers to worry about we can just load the value
          directly.  */
-      operands[2] = gen_rtx (MEM, SImode,
-                            plus_constant (XEXP (operands[1], 0), 4));
+      operands[2] = gen_rtx_MEM (SImode,
+                                plus_constant (XEXP (operands[1], 0), 4));
          
       output_asm_insn ("ldr\t%H0, %2", operands);
       output_asm_insn ("ldr\t%0, %1", operands);
@@ -12588,8 +13753,8 @@ thumb_expand_movstrqi (rtx *operands)
   if (len >= 4)
     {
       rtx reg = gen_reg_rtx (SImode);
-      emit_insn (gen_movsi (reg, gen_rtx (MEM, SImode, in)));
-      emit_insn (gen_movsi (gen_rtx (MEM, SImode, out), reg));
+      emit_insn (gen_movsi (reg, gen_rtx_MEM (SImode, in)));
+      emit_insn (gen_movsi (gen_rtx_MEM (SImode, out), reg));
       len -= 4;
       offset += 4;
     }
@@ -12597,9 +13762,9 @@ thumb_expand_movstrqi (rtx *operands)
   if (len >= 2)
     {
       rtx reg = gen_reg_rtx (HImode);
-      emit_insn (gen_movhi (reg, gen_rtx (MEM, HImode, 
-                                         plus_constant (in, offset))));
-      emit_insn (gen_movhi (gen_rtx (MEM, HImode, plus_constant (out, offset)),
+      emit_insn (gen_movhi (reg, gen_rtx_MEM (HImode, 
+                                             plus_constant (in, offset))));
+      emit_insn (gen_movhi (gen_rtx_MEM (HImode, plus_constant (out, offset)),
                            reg));
       len -= 2;
       offset += 2;
@@ -12608,9 +13773,9 @@ thumb_expand_movstrqi (rtx *operands)
   if (len)
     {
       rtx reg = gen_reg_rtx (QImode);
-      emit_insn (gen_movqi (reg, gen_rtx (MEM, QImode,
-                                         plus_constant (in, offset))));
-      emit_insn (gen_movqi (gen_rtx (MEM, QImode, plus_constant (out, offset)),
+      emit_insn (gen_movqi (reg, gen_rtx_MEM (QImode,
+                                             plus_constant (in, offset))));
+      emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (out, offset)),
                            reg));
     }
 }
@@ -12619,10 +13784,19 @@ int
 thumb_cmp_operand (rtx op, enum machine_mode mode)
 {
   return ((GET_CODE (op) == CONST_INT
-          && (unsigned HOST_WIDE_INT) (INTVAL (op)) < 256)
+          && INTVAL (op) < 256
+          && INTVAL (op) >= 0)
          || s_register_operand (op, mode));
 }
 
+int
+thumb_cmpneg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return (GET_CODE (op) == CONST_INT
+         && INTVAL (op) < 0
+         && INTVAL (op) > -256);
+}
+
 /* Return TRUE if a result can be stored in OP without clobbering the
    condition code register.  Prior to reload we only accept a
    register.  After reload we have to be able to handle memory as
@@ -12862,7 +14036,7 @@ aof_globalize_label (FILE *stream, const char *name)
 }
 
 static void
-aof_file_start ()
+aof_file_start (void)
 {
   fputs ("__r0\tRN\t0\n", asm_out_file);
   fputs ("__a1\tRN\t0\n", asm_out_file);
@@ -12971,8 +14145,7 @@ arm_encode_section_info (tree decl, rtx rtl, int first)
   /* This doesn't work with AOF syntax, since the string table may be in
      a different AREA.  */
 #ifndef AOF_ASSEMBLER
-  if (optimize > 0 && TREE_CONSTANT (decl)
-      && (!flag_writable_strings || TREE_CODE (decl) != STRING_CST))
+  if (optimize > 0 && TREE_CONSTANT (decl))
     SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
 #endif
 
@@ -13009,6 +14182,8 @@ arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
                     HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
                     tree function)
 {
+  static int thunk_label = 0;
+  char label[256];
   int mi_delta = delta;
   const char *const mi_op = mi_delta < 0 ? "sub" : "add";
   int shift = 0;
@@ -13016,6 +14191,14 @@ arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
                     ? 1 : 0);
   if (mi_delta < 0)
     mi_delta = - mi_delta;
+  if (TARGET_THUMB)
+    {
+      int labelno = thunk_label++;
+      ASM_GENERATE_INTERNAL_LABEL (label, "LTHUMBFUNC", labelno);
+      fputs ("\tldr\tr12, ", file);
+      assemble_name (file, label);
+      fputc ('\n', file);
+    }
   while (mi_delta != 0)
     {
       if ((mi_delta & (3 << shift)) == 0)
@@ -13029,17 +14212,26 @@ arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
           shift += 8;
         }
     }
-  fputs ("\tb\t", file);
-  assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
-  if (NEED_PLT_RELOC)
-    fputs ("(PLT)", file);
-  fputc ('\n', file);
+  if (TARGET_THUMB)
+    {
+      fprintf (file, "\tbx\tr12\n");
+      ASM_OUTPUT_ALIGN (file, 2);
+      assemble_name (file, label);
+      fputs (":\n", file);
+      assemble_integer (XEXP (DECL_RTL (function), 0), 4, BITS_PER_WORD, 1);
+    }
+  else
+    {
+      fputs ("\tb\t", file);
+      assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
+      if (NEED_PLT_RELOC)
+        fputs ("(PLT)", file);
+      fputc ('\n', file);
+    }
 }
 
 int
-arm_emit_vector_const (file, x)
-     FILE * file;
-     rtx    x;
+arm_emit_vector_const (FILE *file, rtx x)
 {
   int i;
   const char * pattern;
@@ -13068,8 +14260,7 @@ arm_emit_vector_const (file, x)
 }
 
 const char *
-arm_output_load_gr (operands)
-     rtx * operands;
+arm_output_load_gr (rtx *operands)
 {
   rtx reg;
   rtx offset;
@@ -13096,3 +14287,155 @@ arm_output_load_gr (operands)
 
   return "";
 }
+
+static rtx
+arm_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+                     int incoming ATTRIBUTE_UNUSED)
+{
+#if 0
+  /* FIXME: The ARM backend has special code to handle structure
+        returns, and will reserve its own hidden first argument.  So
+        if this macro is enabled a *second* hidden argument will be
+        reserved, which will break binary compatibility with old
+        toolchains and also thunk handling.  One day this should be
+        fixed.  */
+  return 0;
+#else
+  /* Register in which address to store a structure value
+     is passed to a function.  */
+  return gen_rtx_REG (Pmode, ARG_REGISTER (1));
+#endif
+}
+
+/* Worker function for TARGET_SETUP_INCOMING_VARARGS.
+
+   On the ARM, PRETEND_SIZE is set in order to have the prologue push the last
+   named arg and all anonymous args onto the stack.
+   XXX I know the prologue shouldn't be pushing registers, but it is faster
+   that way.  */
+
+static void
+arm_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
+                           enum machine_mode mode ATTRIBUTE_UNUSED,
+                           tree type ATTRIBUTE_UNUSED,
+                           int *pretend_size,
+                           int second_time ATTRIBUTE_UNUSED)
+{
+  cfun->machine->uses_anonymous_args = 1;
+  if (cum->nregs < NUM_ARG_REGS)
+    *pretend_size = (NUM_ARG_REGS - cum->nregs) * UNITS_PER_WORD;
+}
+
+/* Return nonzero if the CONSUMER instruction (a store) does not need
+   PRODUCER's value to calculate the address.  */
+
+int
+arm_no_early_store_addr_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx addr = PATTERN (consumer);
+
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (addr) == COND_EXEC)
+    addr = COND_EXEC_CODE (addr);
+  if (GET_CODE (addr) == PARALLEL)
+    addr = XVECEXP (addr, 0, 0);
+  addr = XEXP (addr, 0);
+  
+  return !reg_overlap_mentioned_p (value, addr);
+}
+
+/* Return nonzero if the CONSUMER instruction (an ALU op) does not
+   have an early register shift value or amount dependency on the
+   result of PRODUCER.  */
+
+int
+arm_no_early_alu_shift_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx op = PATTERN (consumer);
+  rtx early_op;
+
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (op) == COND_EXEC)
+    op = COND_EXEC_CODE (op);
+  if (GET_CODE (op) == PARALLEL)
+    op = XVECEXP (op, 0, 0);
+  op = XEXP (op, 1);
+  
+  early_op = XEXP (op, 0);
+  /* This is either an actual independent shift, or a shift applied to
+     the first operand of another operation.  We want the whole shift
+     operation.  */
+  if (GET_CODE (early_op) == REG)
+    early_op = op;
+
+  return !reg_overlap_mentioned_p (value, early_op);
+}
+
+/* Return nonzero if the CONSUMER instruction (an ALU op) does not
+   have an early register shift value dependency on the result of
+   PRODUCER.  */
+
+int
+arm_no_early_alu_shift_value_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx op = PATTERN (consumer);
+  rtx early_op;
+
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (op) == COND_EXEC)
+    op = COND_EXEC_CODE (op);
+  if (GET_CODE (op) == PARALLEL)
+    op = XVECEXP (op, 0, 0);
+  op = XEXP (op, 1);
+  
+  early_op = XEXP (op, 0);
+
+  /* This is either an actual independent shift, or a shift applied to
+     the first operand of another operation.  We want the value being
+     shifted, in either case.  */
+  if (GET_CODE (early_op) != REG)
+    early_op = XEXP (early_op, 0);
+  
+  return !reg_overlap_mentioned_p (value, early_op);
+}
+
+/* Return nonzero if the CONSUMER (a mul or mac op) does not
+   have an early register mult dependency on the result of
+   PRODUCER.  */
+
+int
+arm_no_early_mul_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx op = PATTERN (consumer);
+
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (op) == COND_EXEC)
+    op = COND_EXEC_CODE (op);
+  if (GET_CODE (op) == PARALLEL)
+    op = XVECEXP (op, 0, 0);
+  op = XEXP (op, 1);
+  
+  return (GET_CODE (op) == PLUS
+         && !reg_overlap_mentioned_p (value, XEXP (op, 0)));
+}
+