OSDN Git Service

2009-08-11 Andrew Haley <aph@redhat.com>
[pf3gnuchains/gcc-fork.git] / gcc / config / arm / arm.c
index 497564a..993d121 100644 (file)
@@ -1,6 +1,7 @@
 /* Output routines for GCC for ARM.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-   2002, 2003, 2004, 2005, 2006, 2007, 2008 Free Software Foundation, Inc.
+   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   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).
@@ -42,6 +43,7 @@
 #include "optabs.h"
 #include "toplev.h"
 #include "recog.h"
+#include "cgraph.h"
 #include "ggc.h"
 #include "except.h"
 #include "c-pragma.h"
 #include "debug.h"
 #include "langhooks.h"
 #include "df.h"
+#include "intl.h"
+#include "libfuncs.h"
 
 /* Forward definitions of types.  */
 typedef struct minipool_node    Mnode;
 typedef struct minipool_fixup   Mfix;
 
-const struct attribute_spec arm_attribute_table[];
-
 void (*arm_lang_output_object_attributes_hook)(void);
 
 /* Forward function declarations.  */
@@ -72,7 +74,10 @@ static int arm_address_register_rtx_p (rtx, int);
 static int arm_legitimate_index_p (enum machine_mode, rtx, RTX_CODE, int);
 static int thumb2_legitimate_index_p (enum machine_mode, rtx, int);
 static int thumb1_base_register_rtx_p (rtx, enum machine_mode, int);
+static rtx arm_legitimize_address (rtx, rtx, enum machine_mode);
+static rtx thumb_legitimize_address (rtx, rtx, enum machine_mode);
 inline static int thumb1_index_register_rtx_p (rtx, int);
+static bool arm_legitimate_address_p (enum machine_mode, rtx, bool);
 static int thumb_far_jump_used_p (void);
 static bool thumb_force_lr_save (void);
 static int const_ok_for_op (HOST_WIDE_INT, enum rtx_code);
@@ -109,6 +114,7 @@ static unsigned long arm_compute_save_reg_mask (void);
 static unsigned long arm_isr_value (tree);
 static unsigned long arm_compute_func_type (void);
 static tree arm_handle_fndecl_attribute (tree *, tree, tree, int, bool *);
+static tree arm_handle_pcs_attribute (tree *, tree, tree, int, bool *);
 static tree arm_handle_isr_attribute (tree *, tree, tree, int, bool *);
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
 static tree arm_handle_notshared_attribute (tree *, tree, tree, int, bool *);
@@ -122,15 +128,22 @@ static int arm_adjust_cost (rtx, rtx, rtx, int);
 static int count_insns_for_constant (HOST_WIDE_INT, int);
 static int arm_get_strip_length (int);
 static bool arm_function_ok_for_sibcall (tree, tree);
+static enum machine_mode arm_promote_function_mode (const_tree,
+                                                   enum machine_mode, int *,
+                                                   const_tree, int);
+static bool arm_return_in_memory (const_tree, const_tree);
+static rtx arm_function_value (const_tree, const_tree, bool);
+static rtx arm_libcall_value (enum machine_mode, rtx);
+
 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_size_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 bool arm_rtx_costs_1 (rtx, enum rtx_code, int*, bool);
+static bool arm_size_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *);
+static bool arm_slowmul_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
+static bool arm_fastmul_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
+static bool arm_xscale_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
+static bool arm_9e_rtx_costs (rtx, enum rtx_code, enum rtx_code, int *, bool);
 static bool arm_rtx_costs (rtx, int, int, int *, bool);
 static int arm_address_cost (rtx, bool);
 static bool arm_memory_load_p (rtx);
@@ -147,6 +160,9 @@ static void emit_constant_insn (rtx cond, rtx pattern);
 static rtx emit_set_insn (rtx, rtx);
 static int arm_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
                                  tree, bool);
+static rtx aapcs_allocate_return_reg (enum machine_mode, const_tree,
+                                     const_tree);
+static int aapcs_select_return_coproc (const_tree, const_tree);
 
 #ifdef OBJECT_FORMAT_ELF
 static void arm_elf_asm_constructor (rtx, int) ATTRIBUTE_UNUSED;
@@ -185,6 +201,9 @@ static void arm_cxx_determine_class_data_visibility (tree);
 static bool arm_cxx_class_data_always_comdat (void);
 static bool arm_cxx_use_aeabi_atexit (void);
 static void arm_init_libfuncs (void);
+static tree arm_build_builtin_va_list (void);
+static void arm_expand_builtin_va_start (tree, rtx);
+static tree arm_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
 static bool arm_handle_option (size_t, const char *, int);
 static void arm_target_help (void);
 static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
@@ -193,7 +212,51 @@ static bool arm_tls_symbol_p (rtx x);
 static int arm_issue_rate (void);
 static void arm_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 static bool arm_allocate_stack_slots_for_args (void);
+static const char *arm_invalid_parameter_type (const_tree t);
+static const char *arm_invalid_return_type (const_tree t);
+static tree arm_promoted_type (const_tree t);
+static tree arm_convert_to_type (tree type, tree expr);
+static bool arm_scalar_mode_supported_p (enum machine_mode);
+static bool arm_frame_pointer_required (void);
+
+\f
+/* Table of machine attributes.  */
+static const struct attribute_spec arm_attribute_table[] =
+{
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  /* Function calls made to this symbol must be done indirectly, because
+     it may lie outside of the 26 bit addressing range of a normal function
+     call.  */
+  { "long_call",    0, 0, false, true,  true,  NULL },
+  /* Whereas these functions are always known to reside within the 26 bit
+     addressing range.  */
+  { "short_call",   0, 0, false, true,  true,  NULL },
+  /* Specify the procedure call conventions for a function.  */
+  { "pcs",          1, 1, false, true,  true,  arm_handle_pcs_attribute },
+  /* Interrupt Service Routines have special prologue and epilogue requirements.  */
+  { "isr",          0, 1, false, false, false, arm_handle_isr_attribute },
+  { "interrupt",    0, 1, false, false, false, arm_handle_isr_attribute },
+  { "naked",        0, 0, true,  false, false, arm_handle_fndecl_attribute },
+#ifdef ARM_PE
+  /* ARM/PE has three new attributes:
+     interfacearm - ?
+     dllexport - for exporting a function/variable that will live in a dll
+     dllimport - for importing a function/variable from a dll
 
+     Microsoft allows multiple declspecs in one __declspec, separating
+     them with spaces.  We do NOT support this.  Instead, use __declspec
+     multiple times.
+  */
+  { "dllimport",    0, 0, true,  false, false, NULL },
+  { "dllexport",    0, 0, true,  false, false, NULL },
+  { "interfacearm", 0, 0, true,  false, false, arm_handle_fndecl_attribute },
+#elif TARGET_DLLIMPORT_DECL_ATTRIBUTES
+  { "dllimport",    0, 0, false, false, false, handle_dll_attribute },
+  { "dllexport",    0, 0, false, false, false, handle_dll_attribute },
+  { "notshared",    0, 0, false, true, false, arm_handle_notshared_attribute },
+#endif
+  { NULL,           0, 0, false, false, false, NULL }
+};
 \f
 /* Initialize the GCC target structure.  */
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
@@ -201,6 +264,9 @@ static bool arm_allocate_stack_slots_for_args (void);
 #define TARGET_MERGE_DECL_ATTRIBUTES merge_dllimport_decl_attributes
 #endif
 
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS arm_legitimize_address
+
 #undef  TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE arm_attribute_table
 
@@ -252,6 +318,12 @@ static bool arm_allocate_stack_slots_for_args (void);
 #undef  TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL arm_function_ok_for_sibcall
 
+#undef  TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE arm_function_value
+
+#undef  TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE arm_libcall_value
+
 #undef  TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK arm_output_mi_thunk
 #undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
@@ -278,10 +350,8 @@ static bool arm_allocate_stack_slots_for_args (void);
 #undef TARGET_INIT_LIBFUNCS
 #define TARGET_INIT_LIBFUNCS arm_init_libfuncs
 
-#undef TARGET_PROMOTE_FUNCTION_ARGS
-#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_const_tree_true
-#undef TARGET_PROMOTE_FUNCTION_RETURN
-#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
+#undef TARGET_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE arm_promote_function_mode
 #undef TARGET_PROMOTE_PROTOTYPES
 #define TARGET_PROMOTE_PROTOTYPES arm_promote_prototypes
 #undef TARGET_PASS_BY_REFERENCE
@@ -382,11 +452,39 @@ static bool arm_allocate_stack_slots_for_args (void);
 #undef TARGET_MANGLE_TYPE
 #define TARGET_MANGLE_TYPE arm_mangle_type
 
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST arm_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START arm_expand_builtin_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR arm_gimplify_va_arg_expr
+
 #ifdef HAVE_AS_TLS
 #undef TARGET_ASM_OUTPUT_DWARF_DTPREL
 #define TARGET_ASM_OUTPUT_DWARF_DTPREL arm_output_dwarf_dtprel
 #endif
 
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P    arm_legitimate_address_p
+
+#undef TARGET_INVALID_PARAMETER_TYPE
+#define TARGET_INVALID_PARAMETER_TYPE arm_invalid_parameter_type
+
+#undef TARGET_INVALID_RETURN_TYPE
+#define TARGET_INVALID_RETURN_TYPE arm_invalid_return_type
+
+#undef TARGET_PROMOTED_TYPE
+#define TARGET_PROMOTED_TYPE arm_promoted_type
+
+#undef TARGET_CONVERT_TO_TYPE
+#define TARGET_CONVERT_TO_TYPE arm_convert_to_type
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P arm_scalar_mode_supported_p
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED arm_frame_pointer_required
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Obstack for minipool constant handling.  */
@@ -402,10 +500,6 @@ extern FILE * asm_out_file;
 /* True if we are currently building a constant table.  */
 int making_const_table;
 
-/* Define the information needed to generate branch insns.  This is
-   stored from the compare operation.  */
-rtx arm_compare_op0, arm_compare_op1;
-
 /* The processor for which instructions should be scheduled.  */
 enum processor_type arm_tune = arm_none;
 
@@ -424,6 +518,9 @@ enum fputype arm_fpu_tune;
 /* Whether to use floating point hardware.  */
 enum float_abi_type arm_float_abi;
 
+/* Which __fp16 format to use.  */
+enum arm_fp16_format_type arm_fp16_format;
+
 /* Which ABI to use.  */
 enum arm_abi_type arm_abi;
 
@@ -571,10 +668,6 @@ enum machine_mode output_memory_reference_mode;
 /* The register number to be used for the PIC offset register.  */
 unsigned arm_pic_register = INVALID_REGNUM;
 
-/* Set to 1 when a return insn is output, this means that the epilogue
-   is not needed.  */
-int return_used_this_function;
-
 /* Set to 1 after arm_reorg has started.  Reset to start at the start of
    the next function.  */
 static int after_arm_reorg = 0;
@@ -582,6 +675,8 @@ static int after_arm_reorg = 0;
 /* The maximum number of insns to be used when loading a constant.  */
 static int arm_constant_limit = 3;
 
+static enum arm_pcs arm_pcs_default;
+
 /* For an explanation of these variables, see final_prescan_insn below.  */
 int arm_ccfsm_state;
 /* arm_current_cc is also used for Thumb-2 cond_exec blocks.  */
@@ -618,7 +713,7 @@ struct processors
   enum processor_type core;
   const char *arch;
   const unsigned long flags;
-  bool (* rtx_costs) (rtx, int, int, int *);
+  bool (* rtx_costs) (rtx, enum rtx_code, enum rtx_code, int *, bool);
 };
 
 /* Not all of these give usefully different compilation alternatives,
@@ -664,6 +759,7 @@ static const struct processors all_architectures[] =
   {"armv7-m", cortexm3,          "7M",  FL_CO_PROC |             FL_FOR_ARCH7M, NULL},
   {"ep9312",  ep9312,     "4T",  FL_LDSCHED | FL_CIRRUS | FL_FOR_ARCH4, NULL},
   {"iwmmxt",  iwmmxt,     "5TE", FL_LDSCHED | FL_STRONG | FL_FOR_ARCH5TE | FL_XSCALE | FL_IWMMXT , NULL},
+  {"iwmmxt2", iwmmxt2,     "5TE", FL_LDSCHED | FL_STRONG | FL_FOR_ARCH5TE | FL_XSCALE | FL_IWMMXT , NULL},
   {NULL, arm_none, NULL, 0 , NULL}
 };
 
@@ -706,22 +802,23 @@ struct fpu_desc
 
 static const struct fpu_desc all_fpus[] =
 {
-  {"fpa",      FPUTYPE_FPA},
-  {"fpe2",     FPUTYPE_FPA_EMU2},
-  {"fpe3",     FPUTYPE_FPA_EMU2},
-  {"maverick", FPUTYPE_MAVERICK},
-  {"vfp",      FPUTYPE_VFP},
-  {"vfp3",     FPUTYPE_VFP3},
-  {"vfpv3",    FPUTYPE_VFP3},
-  {"vfpv3-d16",        FPUTYPE_VFP3D16},
-  {"neon",     FPUTYPE_NEON}
+  {"fpa",              FPUTYPE_FPA},
+  {"fpe2",             FPUTYPE_FPA_EMU2},
+  {"fpe3",             FPUTYPE_FPA_EMU2},
+  {"maverick",         FPUTYPE_MAVERICK},
+  {"vfp",              FPUTYPE_VFP},
+  {"vfp3",             FPUTYPE_VFP3},
+  {"vfpv3",            FPUTYPE_VFP3},
+  {"vfpv3-d16",                FPUTYPE_VFP3D16},
+  {"neon",             FPUTYPE_NEON},
+  {"neon-fp16",                FPUTYPE_NEON_FP16}
 };
 
 
 /* Floating point models used by the different hardware.
    See fputype in arm.h.  */
 
-static const enum fputype fp_model_for_fpu[] =
+static const enum arm_fp_model fp_model_for_fpu[] =
 {
   /* No FP hardware.  */
   ARM_FP_MODEL_UNKNOWN,                /* FPUTYPE_NONE  */
@@ -732,7 +829,8 @@ static const enum fputype fp_model_for_fpu[] =
   ARM_FP_MODEL_VFP,            /* FPUTYPE_VFP  */
   ARM_FP_MODEL_VFP,            /* FPUTYPE_VFP3D16  */
   ARM_FP_MODEL_VFP,            /* FPUTYPE_VFP3  */
-  ARM_FP_MODEL_VFP             /* FPUTYPE_NEON  */
+  ARM_FP_MODEL_VFP,            /* FPUTYPE_NEON  */
+  ARM_FP_MODEL_VFP             /* FPUTYPE_NEON_FP16  */
 };
 
 
@@ -753,6 +851,23 @@ static const struct float_abi all_float_abis[] =
 };
 
 
+struct fp16_format
+{
+  const char *name;
+  enum arm_fp16_format_type fp16_format_type;
+};
+
+
+/* Available values for -mfp16-format=.  */
+
+static const struct fp16_format all_fp16_formats[] =
+{
+  {"none",             ARM_FP16_FORMAT_NONE},
+  {"ieee",             ARM_FP16_FORMAT_IEEE},
+  {"alternative",      ARM_FP16_FORMAT_ALTERNATIVE}
+};
+
+
 struct abi_name
 {
   const char *name;
@@ -910,6 +1025,136 @@ arm_init_libfuncs (void)
   set_optab_libfunc (umod_optab, DImode, NULL);
   set_optab_libfunc (smod_optab, SImode, NULL);
   set_optab_libfunc (umod_optab, SImode, NULL);
+
+  /* Half-precision float operations.  The compiler handles all operations
+     with NULL libfuncs by converting the SFmode.  */
+  switch (arm_fp16_format)
+    {
+    case ARM_FP16_FORMAT_IEEE:
+    case ARM_FP16_FORMAT_ALTERNATIVE:
+
+      /* Conversions.  */
+      set_conv_libfunc (trunc_optab, HFmode, SFmode,
+                       (arm_fp16_format == ARM_FP16_FORMAT_IEEE
+                        ? "__gnu_f2h_ieee"
+                        : "__gnu_f2h_alternative"));
+      set_conv_libfunc (sext_optab, SFmode, HFmode, 
+                       (arm_fp16_format == ARM_FP16_FORMAT_IEEE
+                        ? "__gnu_h2f_ieee"
+                        : "__gnu_h2f_alternative"));
+      
+      /* Arithmetic.  */
+      set_optab_libfunc (add_optab, HFmode, NULL);
+      set_optab_libfunc (sdiv_optab, HFmode, NULL);
+      set_optab_libfunc (smul_optab, HFmode, NULL);
+      set_optab_libfunc (neg_optab, HFmode, NULL);
+      set_optab_libfunc (sub_optab, HFmode, NULL);
+
+      /* Comparisons.  */
+      set_optab_libfunc (eq_optab, HFmode, NULL);
+      set_optab_libfunc (ne_optab, HFmode, NULL);
+      set_optab_libfunc (lt_optab, HFmode, NULL);
+      set_optab_libfunc (le_optab, HFmode, NULL);
+      set_optab_libfunc (ge_optab, HFmode, NULL);
+      set_optab_libfunc (gt_optab, HFmode, NULL);
+      set_optab_libfunc (unord_optab, HFmode, NULL);
+      break;
+
+    default:
+      break;
+    }
+
+  if (TARGET_AAPCS_BASED)
+    synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
+}
+
+/* On AAPCS systems, this is the "struct __va_list".  */
+static GTY(()) tree va_list_type;
+
+/* Return the type to use as __builtin_va_list.  */
+static tree
+arm_build_builtin_va_list (void)
+{
+  tree va_list_name;
+  tree ap_field;
+  
+  if (!TARGET_AAPCS_BASED)
+    return std_build_builtin_va_list ();
+
+  /* AAPCS \S 7.1.4 requires that va_list be a typedef for a type
+     defined as:
+
+       struct __va_list 
+       {
+        void *__ap;
+       };
+
+     The C Library ABI further reinforces this definition in \S
+     4.1.
+
+     We must follow this definition exactly.  The structure tag
+     name is visible in C++ mangled names, and thus forms a part
+     of the ABI.  The field name may be used by people who
+     #include <stdarg.h>.  */
+  /* Create the type.  */
+  va_list_type = lang_hooks.types.make_type (RECORD_TYPE);
+  /* Give it the required name.  */
+  va_list_name = build_decl (BUILTINS_LOCATION,
+                            TYPE_DECL,
+                            get_identifier ("__va_list"),
+                            va_list_type);
+  DECL_ARTIFICIAL (va_list_name) = 1;
+  TYPE_NAME (va_list_type) = va_list_name;
+  /* Create the __ap field.  */
+  ap_field = build_decl (BUILTINS_LOCATION,
+                        FIELD_DECL, 
+                        get_identifier ("__ap"),
+                        ptr_type_node);
+  DECL_ARTIFICIAL (ap_field) = 1;
+  DECL_FIELD_CONTEXT (ap_field) = va_list_type;
+  TYPE_FIELDS (va_list_type) = ap_field;
+  /* Compute its layout.  */
+  layout_type (va_list_type);
+
+  return va_list_type;
+}
+
+/* Return an expression of type "void *" pointing to the next
+   available argument in a variable-argument list.  VALIST is the
+   user-level va_list object, of type __builtin_va_list.  */
+static tree
+arm_extract_valist_ptr (tree valist)
+{
+  if (TREE_TYPE (valist) == error_mark_node)
+    return error_mark_node;
+
+  /* On an AAPCS target, the pointer is stored within "struct
+     va_list".  */
+  if (TARGET_AAPCS_BASED)
+    {
+      tree ap_field = TYPE_FIELDS (TREE_TYPE (valist));
+      valist = build3 (COMPONENT_REF, TREE_TYPE (ap_field), 
+                      valist, ap_field, NULL_TREE);
+    }
+
+  return valist;
+}
+
+/* Implement TARGET_EXPAND_BUILTIN_VA_START.  */
+static void
+arm_expand_builtin_va_start (tree valist, rtx nextarg)
+{
+  valist = arm_extract_valist_ptr (valist);
+  std_expand_builtin_va_start (valist, nextarg);
+}
+
+/* Implement TARGET_GIMPLIFY_VA_ARG_EXPR.  */
+static tree
+arm_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, 
+                         gimple_seq *post_p)
+{
+  valist = arm_extract_valist_ptr (valist);
+  return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
 }
 
 /* Implement TARGET_HANDLE_OPTION.  */
@@ -1104,13 +1349,13 @@ arm_override_options (void)
       const struct processors * sel;
       unsigned int        sought;
 
-      selected_cpu = TARGET_CPU_DEFAULT;
+      selected_cpu = (enum processor_type) TARGET_CPU_DEFAULT;
       if (selected_cpu == arm_none)
        {
 #ifdef SUBTARGET_CPU_DEFAULT
          /* Use the subtarget default CPU if none was specified by
             configure.  */
-         selected_cpu = SUBTARGET_CPU_DEFAULT;
+         selected_cpu = (enum processor_type) SUBTARGET_CPU_DEFAULT;
 #endif
          /* Default to ARM6.  */
          if (selected_cpu == arm_none)
@@ -1192,6 +1437,23 @@ arm_override_options (void)
 
   tune_flags = all_cores[(int)arm_tune].flags;
 
+  if (target_fp16_format_name)
+    {
+      for (i = 0; i < ARRAY_SIZE (all_fp16_formats); i++)
+       {
+         if (streq (all_fp16_formats[i].name, target_fp16_format_name))
+           {
+             arm_fp16_format = all_fp16_formats[i].fp16_format_type;
+             break;
+           }
+       }
+      if (i == ARRAY_SIZE (all_fp16_formats))
+       error ("invalid __fp16 format option: -mfp16-format=%s",
+              target_fp16_format_name);
+    }
+  else
+    arm_fp16_format = ARM_FP16_FORMAT_NONE;
+
   if (target_abi_name)
     {
       for (i = 0; i < ARRAY_SIZE (arm_all_abis); i++)
@@ -1406,8 +1668,18 @@ arm_override_options (void)
   else
     arm_float_abi = TARGET_DEFAULT_FLOAT_ABI;
 
-  if (arm_float_abi == ARM_FLOAT_ABI_HARD && TARGET_VFP)
-    sorry ("-mfloat-abi=hard and VFP");
+  if (TARGET_AAPCS_BASED
+      && (arm_fp_model == ARM_FP_MODEL_FPA))
+    error ("FPA is unsupported in the AAPCS");
+
+  if (TARGET_AAPCS_BASED)
+    {
+      if (TARGET_CALLER_INTERWORKING)
+       error ("AAPCS does not support -mcaller-super-interworking");
+      else
+       if (TARGET_CALLEE_INTERWORKING)
+         error ("AAPCS does not support -mcallee-super-interworking");
+    }
 
   /* FPA and iWMMXt are incompatible because the insn encodings overlap.
      VFP and iWMMXt can theoretically coexist, but it's unlikely such silicon
@@ -1419,10 +1691,36 @@ arm_override_options (void)
   if (TARGET_THUMB2 && TARGET_IWMMXT)
     sorry ("Thumb-2 iWMMXt");
 
+  /* __fp16 support currently assumes the core has ldrh.  */
+  if (!arm_arch4 && arm_fp16_format != ARM_FP16_FORMAT_NONE)
+    sorry ("__fp16 and no ldrh");
+
   /* If soft-float is specified then don't use FPU.  */
   if (TARGET_SOFT_FLOAT)
     arm_fpu_arch = FPUTYPE_NONE;
 
+  if (TARGET_AAPCS_BASED)
+    {
+      if (arm_abi == ARM_ABI_IWMMXT)
+       arm_pcs_default = ARM_PCS_AAPCS_IWMMXT;
+      else if (arm_float_abi == ARM_FLOAT_ABI_HARD
+              && TARGET_HARD_FLOAT
+              && TARGET_VFP)
+       arm_pcs_default = ARM_PCS_AAPCS_VFP;
+      else
+       arm_pcs_default = ARM_PCS_AAPCS;
+    }
+  else
+    {
+      if (arm_float_abi == ARM_FLOAT_ABI_HARD && TARGET_VFP)
+       sorry ("-mfloat-abi=hard and VFP");
+
+      if (arm_abi == ARM_ABI_APCS)
+       arm_pcs_default = ARM_PCS_APCS;
+      else
+       arm_pcs_default = ARM_PCS_ATPCS;
+    }
+
   /* 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
@@ -1882,11 +2180,33 @@ const_ok_for_op (HOST_WIDE_INT i, enum rtx_code code)
   switch (code)
     {
     case PLUS:
+    case COMPARE:
+    case EQ:
+    case NE:
+    case GT:
+    case LE:
+    case LT:
+    case GE:
+    case GEU:
+    case LTU:
+    case GTU:
+    case LEU:
+    case UNORDERED:
+    case ORDERED:
+    case UNEQ:
+    case UNGE:
+    case UNLT:
+    case UNGT:
+    case UNLE:
       return const_ok_for_arm (ARM_SIGN_EXTEND (-i));
 
     case MINUS:                /* Should only occur with (MINUS I reg) => rsb */
     case XOR:
+      return 0;
+
     case IOR:
+      if (TARGET_THUMB2)
+       return const_ok_for_arm (ARM_SIGN_EXTEND (~i));
       return 0;
 
     case AND:
@@ -2063,15 +2383,20 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
                                             GEN_INT (ARM_SIGN_EXTEND (val))));
          return 1;
        }
+
       if (remainder == 0)
        {
          if (reload_completed && rtx_equal_p (target, source))
            return 0;
+
          if (generate)
            emit_constant_insn (cond,
                                gen_rtx_SET (VOIDmode, target, source));
          return 1;
        }
+
+      if (TARGET_THUMB2)
+       can_invert = 1;
       break;
 
     case AND:
@@ -2159,6 +2484,7 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
 
   /* Calculate a few attributes that may be useful for specific
      optimizations.  */
+  /* Count number of leading zeros.  */
   for (i = 31; i >= 0; i--)
     {
       if ((remainder & (1 << i)) == 0)
@@ -2167,6 +2493,7 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
        break;
     }
 
+  /* Count number of leading 1's.  */
   for (i = 31; i >= 0; i--)
     {
       if ((remainder & (1 << i)) != 0)
@@ -2175,6 +2502,7 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
        break;
     }
 
+  /* Count number of trailing zero's.  */
   for (i = 0; i <= 31; i++)
     {
       if ((remainder & (1 << i)) == 0)
@@ -2183,6 +2511,7 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
        break;
     }
 
+  /* Count number of trailing 1's.  */
   for (i = 0; i <= 31; i++)
     {
       if ((remainder & (1 << i)) != 0)
@@ -2370,6 +2699,17 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
       if (code == XOR)
        break;
 
+      /*  Convert.
+         x = y | constant ( which is composed of set_sign_bit_copies of leading 1s
+                            and the remainder 0s for e.g. 0xfff00000)
+         x = ~(~(y ashift set_sign_bit_copies) lshiftrt set_sign_bit_copies)
+
+         This can be done in 2 instructions by using shifts with mov or mvn.
+         e.g. for
+         x = x | 0xfff00000;
+         we generate.
+         mvn   r0, r0, asl #12
+         mvn   r0, r0, lsr #12  */
       if (set_sign_bit_copies > 8
          && (val & (-1 << (32 - set_sign_bit_copies))) == val)
        {
@@ -2395,6 +2735,16 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
          return 2;
        }
 
+      /* Convert
+         x = y | constant (which has set_zero_bit_copies number of trailing ones).
+          to
+         x = ~((~y lshiftrt set_zero_bit_copies) ashift set_zero_bit_copies).
+
+         For eg. r0 = r0 | 0xfff
+              mvn      r0, r0, lsr #12
+              mvn      r0, r0, asl #12
+
+      */
       if (set_zero_bit_copies > 8
          && (remainder & ((1 << set_zero_bit_copies) - 1)) == remainder)
        {
@@ -2420,6 +2770,13 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
          return 2;
        }
 
+      /* This will never be reached for Thumb2 because orn is a valid
+        instruction. This is for Thumb1 and the ARM 32 bit cases.
+
+        x = y | constant (such that ~constant is a valid constant)
+        Transform this to
+        x = ~(~y & ~constant).
+      */
       if (const_ok_for_arm (temp1 = ARM_SIGN_EXTEND (~val)))
        {
          if (generate)
@@ -2529,7 +2886,8 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
     if (remainder & (1 << i))
       num_bits_set++;
 
-  if (code == AND || (can_invert && num_bits_set > 16))
+  if ((code == AND)
+      || (code != IOR && can_invert && num_bits_set > 16))
     remainder = (~remainder) & 0xffffffff;
   else if (code == PLUS && num_bits_set > 16)
     remainder = (-remainder) & 0xffffffff;
@@ -2756,17 +3114,22 @@ arm_canonicalize_comparison (enum rtx_code code, enum machine_mode mode,
 
 /* Define how to find the value returned by a function.  */
 
-rtx
-arm_function_value(const_tree type, const_tree func ATTRIBUTE_UNUSED)
+static rtx
+arm_function_value(const_tree type, const_tree func,
+                  bool outgoing ATTRIBUTE_UNUSED)
 {
   enum machine_mode mode;
   int unsignedp ATTRIBUTE_UNUSED;
   rtx r ATTRIBUTE_UNUSED;
 
   mode = TYPE_MODE (type);
+
+  if (TARGET_AAPCS_BASED)
+    return aapcs_allocate_return_reg (mode, type, func);
+
   /* Promote integer types.  */
   if (INTEGRAL_TYPE_P (type))
-    PROMOTE_FUNCTION_MODE (mode, unsignedp, type);
+    mode = arm_promote_function_mode (type, mode, &unsignedp, func, 1);
 
   /* Promotes small structs returned in a register to full-word size
      for big-endian AAPCS.  */
@@ -2780,7 +3143,88 @@ arm_function_value(const_tree type, const_tree func ATTRIBUTE_UNUSED)
        }
     }
 
-  return LIBCALL_VALUE(mode);
+  return LIBCALL_VALUE (mode);
+}
+
+static int
+libcall_eq (const void *p1, const void *p2)
+{
+  return rtx_equal_p ((const_rtx) p1, (const_rtx) p2);
+}
+
+static hashval_t
+libcall_hash (const void *p1)
+{
+  return hash_rtx ((const_rtx) p1, VOIDmode, NULL, NULL, FALSE);
+}
+
+static void
+add_libcall (htab_t htab, rtx libcall)
+{
+  *htab_find_slot (htab, libcall, INSERT) = libcall;
+}
+
+static bool
+arm_libcall_uses_aapcs_base (rtx libcall)
+{
+  static bool init_done = false;
+  static htab_t libcall_htab;
+
+  if (!init_done)
+    {
+      init_done = true;
+
+      libcall_htab = htab_create (31, libcall_hash, libcall_eq,
+                                 NULL);
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfloat_optab, SFmode, SImode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfloat_optab, DFmode, SImode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfloat_optab, SFmode, DImode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfloat_optab, DFmode, DImode));
+      
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufloat_optab, SFmode, SImode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufloat_optab, DFmode, SImode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufloat_optab, SFmode, DImode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufloat_optab, DFmode, DImode));
+
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sext_optab, SFmode, HFmode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (trunc_optab, HFmode, SFmode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfix_optab, DImode, DFmode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufix_optab, DImode, DFmode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfix_optab, DImode, SFmode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufix_optab, DImode, SFmode));
+    }
+
+  return libcall && htab_find (libcall_htab, libcall) != NULL;
+}
+
+rtx
+arm_libcall_value (enum machine_mode mode, rtx libcall)
+{
+  if (TARGET_AAPCS_BASED && arm_pcs_default != ARM_PCS_AAPCS
+      && GET_MODE_CLASS (mode) == MODE_FLOAT)
+    {
+      /* The following libcalls return their result in integer registers,
+        even though they return a floating point value.  */
+      if (arm_libcall_uses_aapcs_base (libcall))
+       return gen_rtx_REG (mode, ARG_REGISTER(1));
+
+    }
+
+  return LIBCALL_VALUE (mode);
 }
 
 /* Determine the amount of memory needed to store the possible return
@@ -2790,10 +3234,12 @@ arm_apply_result_size (void)
 {
   int size = 16;
 
-  if (TARGET_ARM)
+  if (TARGET_32BIT)
     {
       if (TARGET_HARD_FLOAT_ABI)
        {
+         if (TARGET_VFP)
+           size += 32;
          if (TARGET_FPA)
            size += 12;
          if (TARGET_MAVERICK)
@@ -2806,27 +3252,56 @@ arm_apply_result_size (void)
   return size;
 }
 
-/* Decide whether a type should be returned in memory (true)
-   or in a register (false).  This is called as the target hook
-   TARGET_RETURN_IN_MEMORY.  */
+/* Decide whether TYPE should be returned in memory (true)
+   or in a register (false).  FNTYPE is the type of the function making
+   the call.  */
 static bool
-arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
+arm_return_in_memory (const_tree type, const_tree fntype)
 {
   HOST_WIDE_INT size;
 
-  size = int_size_in_bytes (type);
+  size = int_size_in_bytes (type);  /* Negative if not fixed size.  */
+
+  if (TARGET_AAPCS_BASED)
+    {
+      /* Simple, non-aggregate types (ie not including vectors and
+        complex) are always returned in a register (or registers).
+        We don't care about which register here, so we can short-cut
+        some of the detail.  */
+      if (!AGGREGATE_TYPE_P (type)
+         && TREE_CODE (type) != VECTOR_TYPE
+         && TREE_CODE (type) != COMPLEX_TYPE)
+       return false;
+
+      /* Any return value that is no larger than one word can be
+        returned in r0.  */
+      if (((unsigned HOST_WIDE_INT) size) <= UNITS_PER_WORD)
+       return false;
+
+      /* Check any available co-processors to see if they accept the
+        type as a register candidate (VFP, for example, can return
+        some aggregates in consecutive registers).  These aren't
+        available if the call is variadic.  */
+      if (aapcs_select_return_coproc (type, fntype) >= 0)
+       return false;
+
+      /* Vector values should be returned using ARM registers, not
+        memory (unless they're over 16 bytes, which will break since
+        we only have four call-clobbered registers to play with).  */
+      if (TREE_CODE (type) == VECTOR_TYPE)
+       return (size < 0 || size > (4 * UNITS_PER_WORD));
+
+      /* The rest go in memory.  */
+      return true;
+    }
 
-  /* Vector values should be returned using ARM registers, not memory (unless
-     they're over 16 bytes, which will break since we only have four
-     call-clobbered registers to play with).  */
   if (TREE_CODE (type) == VECTOR_TYPE)
     return (size < 0 || size > (4 * UNITS_PER_WORD));
 
   if (!AGGREGATE_TYPE_P (type) &&
-      !(TARGET_AAPCS_BASED && TREE_CODE (type) == COMPLEX_TYPE))
-    /* All simple types are returned in registers.
-       For AAPCS, complex types are treated the same as aggregates.  */
-    return 0;
+      (TREE_CODE (type) != VECTOR_TYPE))
+    /* All simple types are returned in registers.  */
+    return false;
 
   if (arm_abi != ARM_ABI_APCS)
     {
@@ -2843,7 +3318,7 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
      the aggregate is either huge or of variable size, and in either case
      we will want to return it via memory and not in a register.  */
   if (size < 0 || size > UNITS_PER_WORD)
-    return 1;
+    return true;
 
   if (TREE_CODE (type) == RECORD_TYPE)
     {
@@ -2863,18 +3338,18 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
        continue;
 
       if (field == NULL)
-       return 0; /* An empty structure.  Allowed by an extension to ANSI C.  */
+       return false; /* An empty structure.  Allowed by an extension to ANSI C.  */
 
       /* Check that the first field is valid for returning in a register.  */
 
       /* ... Floats are not allowed */
       if (FLOAT_TYPE_P (TREE_TYPE (field)))
-       return 1;
+       return true;
 
       /* ... Aggregates that are not themselves valid for returning in
         a register are not allowed.  */
       if (arm_return_in_memory (TREE_TYPE (field), NULL_TREE))
-       return 1;
+       return true;
 
       /* Now check the remaining fields, if any.  Only bitfields are allowed,
         since they are not addressable.  */
@@ -2886,10 +3361,10 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
            continue;
 
          if (!DECL_BIT_FIELD_TYPE (field))
-           return 1;
+           return true;
        }
 
-      return 0;
+      return false;
     }
 
   if (TREE_CODE (type) == UNION_TYPE)
@@ -2906,18 +3381,18 @@ arm_return_in_memory (const_tree type, const_tree fntype ATTRIBUTE_UNUSED)
            continue;
 
          if (FLOAT_TYPE_P (TREE_TYPE (field)))
-           return 1;
+           return true;
 
          if (arm_return_in_memory (TREE_TYPE (field), NULL_TREE))
-           return 1;
+           return true;
        }
 
-      return 0;
+      return false;
     }
 #endif /* not ARM_WINCE */
 
   /* Return all other types in memory.  */
-  return 1;
+  return true;
 }
 
 /* Indicate whether or not words of a double are in big-endian order.  */
@@ -2942,58 +3417,797 @@ arm_float_words_big_endian (void)
   return 1;
 }
 
-/* Initialize a variable CUM of type CUMULATIVE_ARGS
-   for a call to a function whose data type is FNTYPE.
-   For a library call, FNTYPE is NULL.  */
-void
-arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
-                         rtx libname  ATTRIBUTE_UNUSED,
-                         tree fndecl ATTRIBUTE_UNUSED)
+const struct pcs_attribute_arg
 {
-  /* On the ARM, the offset starts at 0.  */
-  pcum->nregs = 0;
-  pcum->iwmmxt_nregs = 0;
-  pcum->can_split = true;
+  const char *arg;
+  enum arm_pcs value;
+} pcs_attribute_args[] =
+  {
+    {"aapcs", ARM_PCS_AAPCS},
+    {"aapcs-vfp", ARM_PCS_AAPCS_VFP},
+#if 0
+    /* We could recognize these, but changes would be needed elsewhere
+     * to implement them.  */
+    {"aapcs-iwmmxt", ARM_PCS_AAPCS_IWMMXT},
+    {"atpcs", ARM_PCS_ATPCS},
+    {"apcs", ARM_PCS_APCS},
+#endif
+    {NULL, ARM_PCS_UNKNOWN}
+  };
 
-  /* Varargs vectors are treated the same as long long.
-     named_count avoids having to change the way arm handles 'named' */
-  pcum->named_count = 0;
-  pcum->nargs = 0;
+static enum arm_pcs
+arm_pcs_from_attribute (tree attr)
+{
+  const struct pcs_attribute_arg *ptr;
+  const char *arg;
 
-  if (TARGET_REALLY_IWMMXT && fntype)
-    {
-      tree fn_arg;
+  /* Get the value of the argument.  */
+  if (TREE_VALUE (attr) == NULL_TREE
+      || TREE_CODE (TREE_VALUE (attr)) != STRING_CST)
+    return ARM_PCS_UNKNOWN;
 
-      for (fn_arg = TYPE_ARG_TYPES (fntype);
-          fn_arg;
-          fn_arg = TREE_CHAIN (fn_arg))
-       pcum->named_count += 1;
+  arg = TREE_STRING_POINTER (TREE_VALUE (attr));
 
-      if (! pcum->named_count)
-       pcum->named_count = INT_MAX;
-    }
-}
+  /* Check it against the list of known arguments.  */
+  for (ptr = pcs_attribute_args; ptr->arg != NULL; ptr++)
+    if (streq (arg, ptr->arg))
+      return ptr->value;
 
+  /* An unrecognized interrupt type.  */
+  return ARM_PCS_UNKNOWN;
+}
 
-/* Return true if mode/type need doubleword alignment.  */
-bool
-arm_needs_doubleword_align (enum machine_mode mode, tree type)
+/* Get the PCS variant to use for this call.  TYPE is the function's type
+   specification, DECL is the specific declartion.  DECL may be null if
+   the call could be indirect or if this is a library call.  */
+static enum arm_pcs
+arm_get_pcs_model (const_tree type, const_tree decl)
 {
-  return (GET_MODE_ALIGNMENT (mode) > PARM_BOUNDARY
-         || (type && TYPE_ALIGN (type) > PARM_BOUNDARY));
-}
+  bool user_convention = false;
+  enum arm_pcs user_pcs = arm_pcs_default;
+  tree attr;
 
+  gcc_assert (type);
 
-/* 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.
+  attr = lookup_attribute ("pcs", TYPE_ATTRIBUTES (type));
+  if (attr)
+    {
+      user_pcs = arm_pcs_from_attribute (TREE_VALUE (attr));
+      user_convention = true;
+    }
 
-   MODE is the argument's machine mode.
-   TYPE is the data type of the argument (as a tree).
-    This is null for libcalls where that information may
-    not be available.
-   CUM is a variable of type CUMULATIVE_ARGS which gives info about
-    the preceding args and about the function being called.
+  if (TARGET_AAPCS_BASED)
+    {
+      /* Detect varargs functions.  These always use the base rules
+        (no argument is ever a candidate for a co-processor
+        register).  */
+      bool base_rules = (TYPE_ARG_TYPES (type) != 0
+                        && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (type)))
+                            != void_type_node));
+      
+      if (user_convention)
+       {
+         if (user_pcs > ARM_PCS_AAPCS_LOCAL)
+           sorry ("Non-AAPCS derived PCS variant");
+         else if (base_rules && user_pcs != ARM_PCS_AAPCS)
+           error ("Variadic functions must use the base AAPCS variant");
+       }
+
+      if (base_rules)
+       return ARM_PCS_AAPCS;
+      else if (user_convention)
+       return user_pcs;
+      else if (decl && flag_unit_at_a_time)
+       {
+         /* Local functions never leak outside this compilation unit,
+            so we are free to use whatever conventions are
+            appropriate.  */
+         /* FIXME: remove CONST_CAST_TREE when cgraph is constified.  */
+         struct cgraph_local_info *i = cgraph_local_info (CONST_CAST_TREE(decl));
+         if (i && i->local)
+           return ARM_PCS_AAPCS_LOCAL;
+       }
+    }
+  else if (user_convention && user_pcs != arm_pcs_default)
+    sorry ("PCS variant");
+
+  /* For everything else we use the target's default.  */
+  return arm_pcs_default;
+}
+
+
+static void
+aapcs_vfp_cum_init (CUMULATIVE_ARGS *pcum  ATTRIBUTE_UNUSED,
+                   const_tree fntype ATTRIBUTE_UNUSED,
+                   rtx libcall ATTRIBUTE_UNUSED, 
+                   const_tree fndecl ATTRIBUTE_UNUSED)
+{
+  /* Record the unallocated VFP registers.  */
+  pcum->aapcs_vfp_regs_free = (1 << NUM_VFP_ARG_REGS) - 1;
+  pcum->aapcs_vfp_reg_alloc = 0;
+}
+
+/* Walk down the type tree of TYPE counting consecutive base elements.
+   If *MODEP is VOIDmode, then set it to the first valid floating point
+   type.  If a non-floating point type is found, or if a floating point
+   type that doesn't match a non-VOIDmode *MODEP is found, then return -1,
+   otherwise return the count in the sub-tree.  */
+static int
+aapcs_vfp_sub_candidate (const_tree type, enum machine_mode *modep)
+{
+  enum machine_mode mode;
+  HOST_WIDE_INT size;
+
+  switch (TREE_CODE (type))
+    {
+    case REAL_TYPE:
+      mode = TYPE_MODE (type);
+      if (mode != DFmode && mode != SFmode)
+       return -1;
+
+      if (*modep == VOIDmode)
+       *modep = mode;
+
+      if (*modep == mode)
+       return 1;
+
+      break;
+
+    case COMPLEX_TYPE:
+      mode = TYPE_MODE (TREE_TYPE (type));
+      if (mode != DFmode && mode != SFmode)
+       return -1;
+
+      if (*modep == VOIDmode)
+       *modep = mode;
+
+      if (*modep == mode)
+       return 2;
+
+      break;
+
+    case VECTOR_TYPE:
+      /* Use V2SImode and V4SImode as representatives of all 64-bit
+        and 128-bit vector types, whether or not those modes are
+        supported with the present options.  */
+      size = int_size_in_bytes (type);
+      switch (size)
+       {
+       case 8:
+         mode = V2SImode;
+         break;
+       case 16:
+         mode = V4SImode;
+         break;
+       default:
+         return -1;
+       }
+
+      if (*modep == VOIDmode)
+       *modep = mode;
+
+      /* Vector modes are considered to be opaque: two vectors are
+        equivalent for the purposes of being homogeneous aggregates
+        if they are the same size.  */
+      if (*modep == mode)
+       return 1;
+
+      break;
+
+    case ARRAY_TYPE:
+      {
+       int count;
+       tree index = TYPE_DOMAIN (type);
+
+       /* Can't handle incomplete types.  */
+       if (!COMPLETE_TYPE_P(type))
+         return -1;
+
+       count = aapcs_vfp_sub_candidate (TREE_TYPE (type), modep);
+       if (count == -1
+           || !index
+           || !TYPE_MAX_VALUE (index)
+           || !host_integerp (TYPE_MAX_VALUE (index), 1)
+           || !TYPE_MIN_VALUE (index)
+           || !host_integerp (TYPE_MIN_VALUE (index), 1)
+           || count < 0)
+         return -1;
+
+       count *= (1 + tree_low_cst (TYPE_MAX_VALUE (index), 1)
+                     - tree_low_cst (TYPE_MIN_VALUE (index), 1));
+
+       /* There must be no padding.  */
+       if (!host_integerp (TYPE_SIZE (type), 1)
+           || (tree_low_cst (TYPE_SIZE (type), 1)
+               != count * GET_MODE_BITSIZE (*modep)))
+         return -1;
+
+       return count;
+      }
+      
+    case RECORD_TYPE:
+      {
+       int count = 0;
+       int sub_count;
+       tree field;
+
+       /* Can't handle incomplete types.  */
+       if (!COMPLETE_TYPE_P(type))
+         return -1;
+
+       for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+         {
+           if (TREE_CODE (field) != FIELD_DECL)
+             continue;
+
+           sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep);
+           if (sub_count < 0)
+             return -1;
+           count += sub_count;
+         }
+
+       /* There must be no padding.  */
+       if (!host_integerp (TYPE_SIZE (type), 1)
+           || (tree_low_cst (TYPE_SIZE (type), 1)
+               != count * GET_MODE_BITSIZE (*modep)))
+         return -1;
+
+       return count;
+      }
+
+    case UNION_TYPE:
+    case QUAL_UNION_TYPE:
+      {
+       /* These aren't very interesting except in a degenerate case.  */
+       int count = 0;
+       int sub_count;
+       tree field;
+
+       /* Can't handle incomplete types.  */
+       if (!COMPLETE_TYPE_P(type))
+         return -1;
+
+       for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+         {
+           if (TREE_CODE (field) != FIELD_DECL)
+             continue;
+
+           sub_count = aapcs_vfp_sub_candidate (TREE_TYPE (field), modep);
+           if (sub_count < 0)
+             return -1;
+           count = count > sub_count ? count : sub_count;
+         }
+
+       /* There must be no padding.  */
+       if (!host_integerp (TYPE_SIZE (type), 1)
+           || (tree_low_cst (TYPE_SIZE (type), 1)
+               != count * GET_MODE_BITSIZE (*modep)))
+         return -1;
+
+       return count;
+      }
+
+    default:
+      break;
+    }
+
+  return -1;
+}
+
+static bool
+aapcs_vfp_is_call_or_return_candidate (enum machine_mode mode, const_tree type,
+                                      int *base_mode,
+                                      int *count)
+{
+  if (GET_MODE_CLASS (mode) == MODE_FLOAT
+      || GET_MODE_CLASS (mode) == MODE_VECTOR_INT
+      || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+    {
+      *count = 1;
+      *base_mode = mode;
+      return true;
+    }
+  else if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+    {
+      *count = 2;
+      *base_mode = (mode == DCmode ? DFmode : SFmode);
+      return true;
+    }
+  else if (type && (mode == BLKmode || TREE_CODE (type) == VECTOR_TYPE))
+    {
+      enum machine_mode aggregate_mode = VOIDmode;
+      int ag_count = aapcs_vfp_sub_candidate (type, &aggregate_mode);
+
+      if (ag_count > 0 && ag_count <= 4)
+       {
+         *count = ag_count;
+         *base_mode = aggregate_mode;
+         return true;
+       }
+    }
+  return false;
+}
+
+static bool
+aapcs_vfp_is_return_candidate (enum arm_pcs pcs_variant,
+                              enum machine_mode mode, const_tree type)
+{
+  int count ATTRIBUTE_UNUSED;
+  int ag_mode ATTRIBUTE_UNUSED;
+
+  if (!(pcs_variant == ARM_PCS_AAPCS_VFP
+       || (pcs_variant == ARM_PCS_AAPCS_LOCAL
+           && TARGET_32BIT && TARGET_VFP && TARGET_HARD_FLOAT)))
+    return false;
+  return aapcs_vfp_is_call_or_return_candidate (mode, type, &ag_mode, &count);
+}
+
+static bool
+aapcs_vfp_is_call_candidate (CUMULATIVE_ARGS *pcum, enum machine_mode mode, 
+                            const_tree type)
+{
+  if (!(pcum->pcs_variant == ARM_PCS_AAPCS_VFP
+       || (pcum->pcs_variant == ARM_PCS_AAPCS_LOCAL
+           && TARGET_32BIT && TARGET_VFP && TARGET_HARD_FLOAT)))
+    return false;
+  return aapcs_vfp_is_call_or_return_candidate (mode, type,
+                                               &pcum->aapcs_vfp_rmode,
+                                               &pcum->aapcs_vfp_rcount);
+}
+
+static bool
+aapcs_vfp_allocate (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
+                   const_tree type  ATTRIBUTE_UNUSED)
+{
+  int shift = GET_MODE_SIZE (pcum->aapcs_vfp_rmode) / GET_MODE_SIZE (SFmode);
+  unsigned mask = (1 << (shift * pcum->aapcs_vfp_rcount)) - 1;
+  int regno;
+  
+  for (regno = 0; regno < NUM_VFP_ARG_REGS; regno += shift)
+    if (((pcum->aapcs_vfp_regs_free >> regno) & mask) == mask)
+      {
+       pcum->aapcs_vfp_reg_alloc = mask << regno;
+       if (mode == BLKmode || (mode == TImode && !TARGET_NEON))
+         {
+           int i;
+           int rcount = pcum->aapcs_vfp_rcount;
+           int rshift = shift;
+           enum machine_mode rmode = pcum->aapcs_vfp_rmode;
+           rtx par;
+           if (!TARGET_NEON)
+             {
+               /* Avoid using unsupported vector modes.  */
+               if (rmode == V2SImode)
+                 rmode = DImode;
+               else if (rmode == V4SImode)
+                 {
+                   rmode = DImode;
+                   rcount *= 2;
+                   rshift /= 2;
+                 }
+             }
+           par = gen_rtx_PARALLEL (mode, rtvec_alloc (rcount));
+           for (i = 0; i < rcount; i++)
+             {
+               rtx tmp = gen_rtx_REG (rmode, 
+                                      FIRST_VFP_REGNUM + regno + i * rshift);
+               tmp = gen_rtx_EXPR_LIST
+                 (VOIDmode, tmp, 
+                  GEN_INT (i * GET_MODE_SIZE (rmode)));
+               XVECEXP (par, 0, i) = tmp;
+             }
+
+           pcum->aapcs_reg = par;
+         }
+       else
+         pcum->aapcs_reg = gen_rtx_REG (mode, FIRST_VFP_REGNUM + regno);
+       return true;
+      }
+  return false;
+}
+
+static rtx
+aapcs_vfp_allocate_return_reg (enum arm_pcs pcs_variant ATTRIBUTE_UNUSED,
+                              enum machine_mode mode,
+                              const_tree type ATTRIBUTE_UNUSED)
+{
+  if (!(pcs_variant == ARM_PCS_AAPCS_VFP
+       || (pcs_variant == ARM_PCS_AAPCS_LOCAL
+           && TARGET_32BIT && TARGET_VFP && TARGET_HARD_FLOAT)))
+    return false;
+  if (mode == BLKmode || (mode == TImode && !TARGET_NEON))
+    {
+      int count;
+      int ag_mode;
+      int i;
+      rtx par;
+      int shift;
+      
+      aapcs_vfp_is_call_or_return_candidate (mode, type, &ag_mode, &count);
+
+      if (!TARGET_NEON)
+       {
+         if (ag_mode == V2SImode)
+           ag_mode = DImode;
+         else if (ag_mode == V4SImode)
+           {
+             ag_mode = DImode;
+             count *= 2;
+           }
+       }
+      shift = GET_MODE_SIZE(ag_mode) / GET_MODE_SIZE(SFmode);
+      par = gen_rtx_PARALLEL (mode, rtvec_alloc (count));
+      for (i = 0; i < count; i++)
+       {
+         rtx tmp = gen_rtx_REG (ag_mode, FIRST_VFP_REGNUM + i * shift);
+         tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp, 
+                                  GEN_INT (i * GET_MODE_SIZE (ag_mode)));
+         XVECEXP (par, 0, i) = tmp;
+       }
+
+      return par;
+    }
+
+  return gen_rtx_REG (mode, FIRST_VFP_REGNUM);
+}
+
+static void
+aapcs_vfp_advance (CUMULATIVE_ARGS *pcum  ATTRIBUTE_UNUSED,
+                  enum machine_mode mode  ATTRIBUTE_UNUSED,
+                  const_tree type  ATTRIBUTE_UNUSED)
+{
+  pcum->aapcs_vfp_regs_free &= ~pcum->aapcs_vfp_reg_alloc;
+  pcum->aapcs_vfp_reg_alloc = 0;
+  return;
+}
+
+#define AAPCS_CP(X)                            \
+  {                                            \
+    aapcs_ ## X ## _cum_init,                  \
+    aapcs_ ## X ## _is_call_candidate,         \
+    aapcs_ ## X ## _allocate,                  \
+    aapcs_ ## X ## _is_return_candidate,       \
+    aapcs_ ## X ## _allocate_return_reg,       \
+    aapcs_ ## X ## _advance                    \
+  }
+
+/* Table of co-processors that can be used to pass arguments in
+   registers.  Idealy no arugment should be a candidate for more than
+   one co-processor table entry, but the table is processed in order
+   and stops after the first match.  If that entry then fails to put
+   the argument into a co-processor register, the argument will go on
+   the stack.  */
+static struct 
+{
+  /* Initialize co-processor related state in CUMULATIVE_ARGS structure.  */
+  void (*cum_init) (CUMULATIVE_ARGS *, const_tree, rtx, const_tree);
+
+  /* Return true if an argument of mode MODE (or type TYPE if MODE is
+     BLKmode) is a candidate for this co-processor's registers; this
+     function should ignore any position-dependent state in
+     CUMULATIVE_ARGS and only use call-type dependent information.  */
+  bool (*is_call_candidate) (CUMULATIVE_ARGS *, enum machine_mode, const_tree);
+
+  /* Return true if the argument does get a co-processor register; it
+     should set aapcs_reg to an RTX of the register allocated as is
+     required for a return from FUNCTION_ARG.  */
+  bool (*allocate) (CUMULATIVE_ARGS *, enum machine_mode, const_tree);
+
+  /* Return true if a result of mode MODE (or type TYPE if MODE is
+     BLKmode) is can be returned in this co-processor's registers.  */
+  bool (*is_return_candidate) (enum arm_pcs, enum machine_mode, const_tree);
+
+  /* Allocate and return an RTX element to hold the return type of a
+     call, this routine must not fail and will only be called if
+     is_return_candidate returned true with the same parameters.  */
+  rtx (*allocate_return_reg) (enum arm_pcs, enum machine_mode, const_tree);
+
+  /* Finish processing this argument and prepare to start processing
+     the next one.  */
+  void (*advance) (CUMULATIVE_ARGS *, enum machine_mode, const_tree);
+} aapcs_cp_arg_layout[ARM_NUM_COPROC_SLOTS] =
+  {
+    AAPCS_CP(vfp)
+  };
+
+#undef AAPCS_CP
+
+static int
+aapcs_select_call_coproc (CUMULATIVE_ARGS *pcum, enum machine_mode mode, 
+                         tree type)
+{
+  int i;
+
+  for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++)
+    if (aapcs_cp_arg_layout[i].is_call_candidate (pcum, mode, type))
+      return i;
+
+  return -1;
+}
+
+static int
+aapcs_select_return_coproc (const_tree type, const_tree fntype)
+{
+  /* We aren't passed a decl, so we can't check that a call is local.
+     However, it isn't clear that that would be a win anyway, since it
+     might limit some tail-calling opportunities.  */
+  enum arm_pcs pcs_variant;
+
+  if (fntype)
+    {
+      const_tree fndecl = NULL_TREE;
+
+      if (TREE_CODE (fntype) == FUNCTION_DECL)
+       {
+         fndecl = fntype;
+         fntype = TREE_TYPE (fntype);
+       }
+
+      pcs_variant = arm_get_pcs_model (fntype, fndecl);
+    }
+  else
+    pcs_variant = arm_pcs_default;
+
+  if (pcs_variant != ARM_PCS_AAPCS)
+    {
+      int i;
+
+      for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++)
+       if (aapcs_cp_arg_layout[i].is_return_candidate (pcs_variant, 
+                                                       TYPE_MODE (type),
+                                                       type))
+         return i;
+    }
+  return -1;
+}
+
+static rtx
+aapcs_allocate_return_reg (enum machine_mode mode, const_tree type,
+                          const_tree fntype)
+{
+  /* We aren't passed a decl, so we can't check that a call is local.
+     However, it isn't clear that that would be a win anyway, since it
+     might limit some tail-calling opportunities.  */
+  enum arm_pcs pcs_variant;
+  int unsignedp ATTRIBUTE_UNUSED;
+
+  if (fntype)
+    {
+      const_tree fndecl = NULL_TREE;
+
+      if (TREE_CODE (fntype) == FUNCTION_DECL)
+       {
+         fndecl = fntype;
+         fntype = TREE_TYPE (fntype);
+       }
+
+      pcs_variant = arm_get_pcs_model (fntype, fndecl);
+    }
+  else
+    pcs_variant = arm_pcs_default;
+
+  /* Promote integer types.  */
+  if (type && INTEGRAL_TYPE_P (type))
+    mode = arm_promote_function_mode (type, mode, &unsignedp, fntype, 1);
+
+  if (pcs_variant != ARM_PCS_AAPCS)
+    {
+      int i;
+
+      for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++)
+       if (aapcs_cp_arg_layout[i].is_return_candidate (pcs_variant, mode,
+                                                       type))
+         return aapcs_cp_arg_layout[i].allocate_return_reg (pcs_variant,
+                                                            mode, type);
+    }
+
+  /* Promotes small structs returned in a register to full-word size
+     for big-endian AAPCS.  */
+  if (type && arm_return_in_msb (type))
+    {
+      HOST_WIDE_INT size = int_size_in_bytes (type);
+      if (size % UNITS_PER_WORD != 0)
+       {
+         size += UNITS_PER_WORD - size % UNITS_PER_WORD;
+         mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
+       }
+    }
+
+  return gen_rtx_REG (mode, R0_REGNUM);
+}
+
+rtx
+aapcs_libcall_value (enum machine_mode mode)
+{
+  return aapcs_allocate_return_reg (mode, NULL_TREE, NULL_TREE);
+}
+
+/* Lay out a function argument using the AAPCS rules.  The rule
+   numbers referred to here are those in the AAPCS.  */
+static void
+aapcs_layout_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
+                 tree type, int named)
+{
+  int nregs, nregs2;
+  int ncrn;
+
+  /* We only need to do this once per argument.  */
+  if (pcum->aapcs_arg_processed)
+    return;
+
+  pcum->aapcs_arg_processed = true;
+
+  /* Special case: if named is false then we are handling an incoming
+     anonymous argument which is on the stack.  */
+  if (!named)
+    return;
+  
+  /* Is this a potential co-processor register candidate?  */
+  if (pcum->pcs_variant != ARM_PCS_AAPCS)
+    {
+      int slot = aapcs_select_call_coproc (pcum, mode, type);
+      pcum->aapcs_cprc_slot = slot;
+
+      /* We don't have to apply any of the rules from part B of the
+        preparation phase, these are handled elsewhere in the
+        compiler.  */
+
+      if (slot >= 0)
+       {
+         /* A Co-processor register candidate goes either in its own
+            class of registers or on the stack.  */
+         if (!pcum->aapcs_cprc_failed[slot])
+           {
+             /* C1.cp - Try to allocate the argument to co-processor
+                registers.  */
+             if (aapcs_cp_arg_layout[slot].allocate (pcum, mode, type))
+               return;
+
+             /* C2.cp - Put the argument on the stack and note that we
+                can't assign any more candidates in this slot.  We also
+                need to note that we have allocated stack space, so that
+                we won't later try to split a non-cprc candidate between
+                core registers and the stack.  */
+             pcum->aapcs_cprc_failed[slot] = true;
+             pcum->can_split = false;
+           }
+
+         /* We didn't get a register, so this argument goes on the
+            stack.  */
+         gcc_assert (pcum->can_split == false);
+         return;
+       }
+    }
+
+  /* C3 - For double-word aligned arguments, round the NCRN up to the
+     next even number.  */
+  ncrn = pcum->aapcs_ncrn;
+  if ((ncrn & 1) && arm_needs_doubleword_align (mode, type))
+    ncrn++;
+
+  nregs = ARM_NUM_REGS2(mode, type);
+
+  /* Sigh, this test should really assert that nregs > 0, but a GCC
+     extension allows empty structs and then gives them empty size; it
+     then allows such a structure to be passed by value.  For some of
+     the code below we have to pretend that such an argument has
+     non-zero size so that we 'locate' it correctly either in
+     registers or on the stack.  */
+  gcc_assert (nregs >= 0);
+
+  nregs2 = nregs ? nregs : 1;
+
+  /* C4 - Argument fits entirely in core registers.  */
+  if (ncrn + nregs2 <= NUM_ARG_REGS)
+    {
+      pcum->aapcs_reg = gen_rtx_REG (mode, ncrn);
+      pcum->aapcs_next_ncrn = ncrn + nregs;
+      return;
+    }
+
+  /* C5 - Some core registers left and there are no arguments already
+     on the stack: split this argument between the remaining core
+     registers and the stack.  */
+  if (ncrn < NUM_ARG_REGS && pcum->can_split)
+    {
+      pcum->aapcs_reg = gen_rtx_REG (mode, ncrn);
+      pcum->aapcs_next_ncrn = NUM_ARG_REGS;
+      pcum->aapcs_partial = (NUM_ARG_REGS - ncrn) * UNITS_PER_WORD;
+      return;
+    }
+
+  /* C6 - NCRN is set to 4.  */
+  pcum->aapcs_next_ncrn = NUM_ARG_REGS;
+
+  /* C7,C8 - arugment goes on the stack.  We have nothing to do here.  */
+  return;
+}
+
+/* Initialize a variable CUM of type CUMULATIVE_ARGS
+   for a call to a function whose data type is FNTYPE.
+   For a library call, FNTYPE is NULL.  */
+void
+arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
+                         rtx libname,
+                         tree fndecl ATTRIBUTE_UNUSED)
+{
+  /* Long call handling.  */
+  if (fntype)
+    pcum->pcs_variant = arm_get_pcs_model (fntype, fndecl);
+  else
+    pcum->pcs_variant = arm_pcs_default;
+
+  if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
+    {
+      if (arm_libcall_uses_aapcs_base (libname))
+       pcum->pcs_variant = ARM_PCS_AAPCS;
+      pcum->aapcs_ncrn = pcum->aapcs_next_ncrn = 0;
+      pcum->aapcs_reg = NULL_RTX;
+      pcum->aapcs_partial = 0;
+      pcum->aapcs_arg_processed = false;
+      pcum->aapcs_cprc_slot = -1;
+      pcum->can_split = true;
+
+      if (pcum->pcs_variant != ARM_PCS_AAPCS)
+       {
+         int i;
+
+         for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++)
+           {
+             pcum->aapcs_cprc_failed[i] = false;
+             aapcs_cp_arg_layout[i].cum_init (pcum, fntype, libname, fndecl);
+           }
+       }
+      return;
+    }
+
+  /* Legacy ABIs */
+
+  /* On the ARM, the offset starts at 0.  */
+  pcum->nregs = 0;
+  pcum->iwmmxt_nregs = 0;
+  pcum->can_split = true;
+
+  /* Varargs vectors are treated the same as long long.
+     named_count avoids having to change the way arm handles 'named' */
+  pcum->named_count = 0;
+  pcum->nargs = 0;
+
+  if (TARGET_REALLY_IWMMXT && fntype)
+    {
+      tree fn_arg;
+
+      for (fn_arg = TYPE_ARG_TYPES (fntype);
+          fn_arg;
+          fn_arg = TREE_CHAIN (fn_arg))
+       pcum->named_count += 1;
+
+      if (! pcum->named_count)
+       pcum->named_count = INT_MAX;
+    }
+}
+
+
+/* Return true if mode/type need doubleword alignment.  */
+bool
+arm_needs_doubleword_align (enum machine_mode mode, tree type)
+{
+  return (GET_MODE_ALIGNMENT (mode) > PARM_BOUNDARY
+         || (type && 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.
+
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   CUM is a variable of type CUMULATIVE_ARGS which gives info about
+    the preceding args and about the function being called.
    NAMED is nonzero if this argument is a named parameter
     (otherwise it is an extra parameter matching an ellipsis).  */
 
@@ -3003,6 +4217,17 @@ arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
 {
   int nregs;
 
+  /* Handle the special case quickly.  Pick an arbitrary value for op2 of
+     a call insn (op3 of a call_value insn).  */
+  if (mode == VOIDmode)
+    return const0_rtx;
+
+  if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
+    {
+      aapcs_layout_arg (pcum, mode, type, named);
+      return pcum->aapcs_reg;
+    }
+
   /* Varargs vectors are treated the same as long long.
      named_count avoids having to change the way arm handles 'named' */
   if (TARGET_IWMMXT_ABI
@@ -3044,10 +4269,16 @@ arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
 
 static int
 arm_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
-                      tree type, bool named ATTRIBUTE_UNUSED)
+                      tree type, bool named)
 {
   int nregs = pcum->nregs;
 
+  if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
+    {
+      aapcs_layout_arg (pcum, mode, type, named);
+      return pcum->aapcs_partial;
+    }
+
   if (TARGET_IWMMXT_ABI && arm_vector_mode_supported_p (mode))
     return 0;
 
@@ -3059,6 +4290,39 @@ arm_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
   return 0;
 }
 
+void
+arm_function_arg_advance (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
+                         tree type, bool named)
+{
+  if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
+    {
+      aapcs_layout_arg (pcum, mode, type, named);
+
+      if (pcum->aapcs_cprc_slot >= 0)
+       {
+         aapcs_cp_arg_layout[pcum->aapcs_cprc_slot].advance (pcum, mode,
+                                                             type);
+         pcum->aapcs_cprc_slot = -1;
+       }
+
+      /* Generic stuff.  */
+      pcum->aapcs_arg_processed = false;
+      pcum->aapcs_ncrn = pcum->aapcs_next_ncrn;
+      pcum->aapcs_reg = NULL_RTX;
+      pcum->aapcs_partial = 0;
+    }
+  else
+    {
+      pcum->nargs += 1;
+      if (arm_vector_mode_supported_p (mode)
+         && pcum->named_count > pcum->nargs
+         && TARGET_IWMMXT_ABI)
+       pcum->iwmmxt_nregs += 1;
+      else
+       pcum->nregs += ARM_NUM_REGS2 (mode, type);
+    }
+}
+
 /* Variable sized types are passed by reference.  This is a GCC
    extension to the ARM ABI.  */
 
@@ -3098,42 +4362,6 @@ arm_pr_long_calls_off (struct cpp_reader * pfile ATTRIBUTE_UNUSED)
   arm_pragma_long_calls = OFF;
 }
 \f
-/* Table of machine attributes.  */
-const struct attribute_spec arm_attribute_table[] =
-{
-  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
-  /* Function calls made to this symbol must be done indirectly, because
-     it may lie outside of the 26 bit addressing range of a normal function
-     call.  */
-  { "long_call",    0, 0, false, true,  true,  NULL },
-  /* Whereas these functions are always known to reside within the 26 bit
-     addressing range.  */
-  { "short_call",   0, 0, false, true,  true,  NULL },
-  /* Interrupt Service Routines have special prologue and epilogue requirements.  */
-  { "isr",          0, 1, false, false, false, arm_handle_isr_attribute },
-  { "interrupt",    0, 1, false, false, false, arm_handle_isr_attribute },
-  { "naked",        0, 0, true,  false, false, arm_handle_fndecl_attribute },
-#ifdef ARM_PE
-  /* ARM/PE has three new attributes:
-     interfacearm - ?
-     dllexport - for exporting a function/variable that will live in a dll
-     dllimport - for importing a function/variable from a dll
-
-     Microsoft allows multiple declspecs in one __declspec, separating
-     them with spaces.  We do NOT support this.  Instead, use __declspec
-     multiple times.
-  */
-  { "dllimport",    0, 0, true,  false, false, NULL },
-  { "dllexport",    0, 0, true,  false, false, NULL },
-  { "interfacearm", 0, 0, true,  false, false, arm_handle_fndecl_attribute },
-#elif TARGET_DLLIMPORT_DECL_ATTRIBUTES
-  { "dllimport",    0, 0, false, false, false, handle_dll_attribute },
-  { "dllexport",    0, 0, false, false, false, handle_dll_attribute },
-  { "notshared",    0, 0, false, true, false, arm_handle_notshared_attribute },
-#endif
-  { NULL,           0, 0, false, false, false, NULL }
-};
-
 /* Handle an attribute requiring a FUNCTION_DECL;
    arguments as in struct attribute_spec.handler.  */
 static tree
@@ -3142,8 +4370,8 @@ arm_handle_fndecl_attribute (tree *node, tree name, tree args ATTRIBUTE_UNUSED,
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      warning (OPT_Wattributes, "%qs attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
       *no_add_attrs = true;
     }
 
@@ -3160,8 +4388,8 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
     {
       if (TREE_CODE (*node) != FUNCTION_DECL)
        {
-         warning (OPT_Wattributes, "%qs attribute only applies to functions",
-                  IDENTIFIER_POINTER (name));
+         warning (OPT_Wattributes, "%qE attribute only applies to functions",
+                  name);
          *no_add_attrs = true;
        }
       /* FIXME: the argument if any is checked for type attributes;
@@ -3174,8 +4402,8 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
        {
          if (arm_isr_value (args) == ARM_FT_UNKNOWN)
            {
-             warning (OPT_Wattributes, "%qs attribute ignored",
-                      IDENTIFIER_POINTER (name));
+             warning (OPT_Wattributes, "%qE attribute ignored",
+                      name);
              *no_add_attrs = true;
            }
        }
@@ -3202,8 +4430,8 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
            }
          else
            {
-             warning (OPT_Wattributes, "%qs attribute ignored",
-                      IDENTIFIER_POINTER (name));
+             warning (OPT_Wattributes, "%qE attribute ignored",
+                      name);
            }
        }
     }
@@ -3211,6 +4439,20 @@ arm_handle_isr_attribute (tree *node, tree name, tree args, int flags,
   return NULL_TREE;
 }
 
+/* Handle a "pcs" attribute; arguments as in struct
+   attribute_spec.handler.  */
+static tree
+arm_handle_pcs_attribute (tree *node ATTRIBUTE_UNUSED, tree name, tree args,
+                         int flags ATTRIBUTE_UNUSED, bool *no_add_attrs)
+{
+  if (arm_pcs_from_attribute (args) == ARM_PCS_UNKNOWN)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+  return NULL_TREE;
+}
+
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
 /* Handle the "notshared" attribute.  This attribute is another way of
    requesting hidden visibility.  ARM's compiler supports
@@ -3360,6 +4602,7 @@ arm_is_long_call_p (tree decl)
   /* For "f", be conservative, and only cater for cases in which the
      whole of the current function is placed in the same section.  */
   if (!flag_reorder_blocks_and_partition
+      && TREE_CODE (decl) == FUNCTION_DECL
       && arm_function_in_section_p (decl, current_function_section ()))
     return false;
 
@@ -3371,7 +4614,7 @@ arm_is_long_call_p (tree decl)
 
 /* Return nonzero if it is ok to make a tail-call to DECL.  */
 static bool
-arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
+arm_function_ok_for_sibcall (tree decl, tree exp)
 {
   unsigned long func_type;
 
@@ -3404,6 +4647,21 @@ arm_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
   if (IS_INTERRUPT (func_type))
     return false;
 
+  if (!VOID_TYPE_P (TREE_TYPE (DECL_RESULT (cfun->decl))))
+    {
+      /* Check that the return value locations are the same.  For
+        example that we aren't returning a value from the sibling in
+        a VFP register but then need to transfer it to a core
+        register.  */
+      rtx a, b;
+
+      a = arm_function_value (TREE_TYPE (exp), decl, false);
+      b = arm_function_value (TREE_TYPE (DECL_RESULT (cfun->decl)),
+                             cfun->decl, false);
+      if (!rtx_equal_p (a, b))
+       return false;
+    }
+
   /* Never tailcall if function may be called with a misaligned SP.  */
   if (IS_STACKALIGN (func_type))
     return false;
@@ -3445,24 +4703,26 @@ require_pic_register (void)
       gcc_assert (can_create_pseudo_p ());
       if (arm_pic_register != INVALID_REGNUM)
        {
-         cfun->machine->pic_reg = gen_rtx_REG (Pmode, arm_pic_register);
+         if (!cfun->machine->pic_reg)
+           cfun->machine->pic_reg = gen_rtx_REG (Pmode, arm_pic_register);
 
          /* Play games to avoid marking the function as needing pic
             if we are being called as part of the cost-estimation
             process.  */
-         if (current_ir_type () != IR_GIMPLE)
+         if (current_ir_type () != IR_GIMPLE || currently_expanding_to_rtl)
            crtl->uses_pic_offset_table = 1;
        }
       else
        {
          rtx seq;
 
-         cfun->machine->pic_reg = gen_reg_rtx (Pmode);
+         if (!cfun->machine->pic_reg)
+           cfun->machine->pic_reg = gen_reg_rtx (Pmode);
 
          /* Play games to avoid marking the function as needing pic
             if we are being called as part of the cost-estimation
             process.  */
-         if (current_ir_type () != IR_GIMPLE)
+         if (current_ir_type () != IR_GIMPLE || currently_expanding_to_rtl)
            {
              crtl->uses_pic_offset_table = 1;
              start_sequence ();
@@ -3471,7 +4731,11 @@ require_pic_register (void)
 
              seq = get_insns ();
              end_sequence ();
-             emit_insn_after (seq, entry_of_function ());
+             /* We can be called during expansion of PHI nodes, where
+                we can't yet emit instructions directly in the final
+                insn stream.  Queue the insns on the entry edge, they will
+                be committed after everything else is expanded.  */
+             insert_insn_on_edge (seq, single_succ_edge (ENTRY_BLOCK_PTR));
            }
        }
     }
@@ -3789,8 +5053,8 @@ pcrel_constant_p (rtx x)
 
 /* Return nonzero if X is a valid ARM state address operand.  */
 int
-arm_legitimate_address_p (enum machine_mode mode, rtx x, RTX_CODE outer,
-                         int strict_p)
+arm_legitimate_address_outer_p (enum machine_mode mode, rtx x, RTX_CODE outer,
+                               int strict_p)
 {
   bool use_ldrd;
   enum rtx_code code = GET_CODE (x);
@@ -3844,6 +5108,7 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, RTX_CODE outer,
       rtx xop1 = XEXP (x, 1);
 
       return ((arm_address_register_rtx_p (xop0, strict_p)
+              && GET_CODE(xop1) == CONST_INT
               && arm_legitimate_index_p (mode, xop1, outer, strict_p))
              || (arm_address_register_rtx_p (xop1, strict_p)
                  && arm_legitimate_index_p (mode, xop0, outer, strict_p)));
@@ -3873,7 +5138,7 @@ arm_legitimate_address_p (enum machine_mode mode, rtx x, RTX_CODE outer,
 }
 
 /* Return nonzero if X is a valid Thumb-2 address operand.  */
-int
+static int
 thumb2_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
 {
   bool use_ldrd;
@@ -3999,6 +5264,7 @@ arm_legitimate_index_p (enum machine_mode mode, rtx index, RTX_CODE outer,
   if (GET_MODE_SIZE (mode) <= 4
       && ! (arm_arch4
            && (mode == HImode
+               || mode == HFmode
                || (mode == QImode && outer == SIGN_EXTEND))))
     {
       if (code == MULT)
@@ -4027,13 +5293,15 @@ arm_legitimate_index_p (enum machine_mode mode, rtx index, RTX_CODE outer,
      load.  */
   if (arm_arch4)
     {
-      if (mode == HImode || (outer == SIGN_EXTEND && mode == QImode))
+      if (mode == HImode
+         || mode == HFmode
+         || (outer == SIGN_EXTEND && mode == QImode))
        range = 256;
       else
        range = 4096;
     }
   else
-    range = (mode == HImode) ? 4095 : 4096;
+    range = (mode == HImode || mode == HFmode) ? 4095 : 4096;
 
   return (code == CONST_INT
          && INTVAL (index) < range
@@ -4094,15 +5362,17 @@ thumb2_legitimate_index_p (enum machine_mode mode, rtx index, int strict_p)
 
   if (mode == DImode || mode == DFmode)
     {
-      HOST_WIDE_INT val = INTVAL (index);
-      /* ??? Can we assume ldrd for thumb2?  */
-      /* Thumb-2 ldrd only has reg+const addressing modes.  */
-      if (code != CONST_INT)
+      if (code == CONST_INT)
+       {
+         HOST_WIDE_INT val = INTVAL (index);
+         /* ??? Can we assume ldrd for thumb2?  */
+         /* Thumb-2 ldrd only has reg+const addressing modes.  */
+         /* ldrd supports offsets of +-1020.
+            However the ldr fallback does not.  */
+         return val > -256 && val < 256 && (val & 3) == 0;
+       }
+      else
        return 0;
-
-      /* ldrd supports offsets of +-1020.
-         However the ldr fallback does not.  */
-      return val > -256 && val < 256 && (val & 3) == 0;
     }
 
   if (code == MULT)
@@ -4179,7 +5449,7 @@ thumb1_index_register_rtx_p (rtx x, int strict_p)
    addresses based on the frame pointer or arg pointer until the
    reload pass starts.  This is so that eliminating such addresses
    into stack based ones won't produce impossible code.  */
-int
+static int
 thumb1_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
 {
   /* ??? Not clear if this is right.  Experiment.  */
@@ -4204,7 +5474,8 @@ thumb1_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
     return 1;
 
   /* This is PC relative data after arm_reorg runs.  */
-  else if (GET_MODE_SIZE (mode) >= 4 && reload_completed
+  else if ((GET_MODE_SIZE (mode) >= 4 || mode == HFmode)
+          && reload_completed
           && (GET_CODE (x) == LABEL_REF
               || (GET_CODE (x) == CONST
                   && GET_CODE (XEXP (x, 0)) == PLUS
@@ -4293,6 +5564,17 @@ thumb_legitimate_offset_p (enum machine_mode mode, HOST_WIDE_INT val)
     }
 }
 
+bool
+arm_legitimate_address_p (enum machine_mode mode, rtx x, bool strict_p)
+{
+  if (TARGET_ARM)
+    return arm_legitimate_address_outer_p (mode, x, SET, strict_p);
+  else if (TARGET_THUMB2)
+    return thumb2_legitimate_address_p (mode, x, strict_p);
+  else /* if (TARGET_THUMB1) */
+    return thumb1_legitimate_address_p (mode, x, strict_p);
+}
+
 /* Build the SYMBOL_REF for __tls_get_addr.  */
 
 static GTY(()) rtx tls_get_addr_libfunc;
@@ -4469,6 +5751,14 @@ legitimize_tls_address (rtx x, rtx reg)
 rtx
 arm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
 {
+  if (!TARGET_ARM)
+    {
+      /* TODO: legitimize_address for Thumb2.  */
+      if (TARGET_THUMB2)
+        return x;
+      return thumb_legitimize_address (x, orig_x, mode);
+    }
+
   if (arm_tls_symbol_p (x))
     return legitimize_tls_address (x, NULL_RTX);
 
@@ -4520,7 +5810,7 @@ arm_legitimize_address (rtx x, rtx orig_x, enum machine_mode mode)
     }
 
   /* XXX We don't allow MINUS any more -- see comment in
-     arm_legitimate_address_p ().  */
+     arm_legitimate_address_outer_p ().  */
   else if (GET_CODE (x) == MINUS)
     {
       rtx xop0 = XEXP (x, 0);
@@ -4667,7 +5957,7 @@ thumb_legitimize_reload_address (rtx *x_p,
 
       x = copy_rtx (x);
       push_reload (orig_x, NULL_RTX, x_p, NULL, MODE_BASE_REG_CLASS (mode),
-                  Pmode, VOIDmode, 0, 0, opnum, type);
+                  Pmode, VOIDmode, 0, 0, opnum, (enum reload_type) type);
       return x;
     }
 
@@ -4684,7 +5974,7 @@ thumb_legitimize_reload_address (rtx *x_p,
 
       x = copy_rtx (x);
       push_reload (orig_x, NULL_RTX, x_p, NULL, MODE_BASE_REG_CLASS (mode),
-                  Pmode, VOIDmode, 0, 0, opnum, type);
+                  Pmode, VOIDmode, 0, 0, opnum, (enum reload_type) type);
       return x;
     }
 
@@ -4879,130 +6169,232 @@ thumb1_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer)
     }
 }
 
-
-/* Worker routine for arm_rtx_costs.  */
-/* ??? This needs updating for thumb2.  */
-static inline int
-arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
+static inline bool
+arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
   enum rtx_code subcode;
+  rtx operand;
+  enum rtx_code code = GET_CODE (x);
   int extra_cost;
+  *total = 0;
 
   switch (code)
     {
     case MEM:
       /* 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));
+      *total = COSTS_N_INSNS (2 + ARM_NUM_REGS (mode));
+      return true;
 
     case DIV:
     case MOD:
     case UDIV:
     case UMOD:
-      return optimize_size ? COSTS_N_INSNS (2) : 100;
+      if (TARGET_HARD_FLOAT && mode == SFmode)
+       *total = COSTS_N_INSNS (2);
+      else if (TARGET_HARD_FLOAT && mode == DFmode)
+       *total = COSTS_N_INSNS (4);
+      else
+       *total = COSTS_N_INSNS (20);
+      return false;
 
     case ROTATE:
-      if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG)
-       return 4;
+      if (GET_CODE (XEXP (x, 1)) == REG)
+       *total = COSTS_N_INSNS (1); /* Need to subtract from 32 */
+      else if (GET_CODE (XEXP (x, 1)) != CONST_INT)
+       *total = rtx_cost (XEXP (x, 1), code, speed);
+
       /* Fall through */
     case ROTATERT:
       if (mode != SImode)
-       return 8;
+       {
+         *total += COSTS_N_INSNS (4);
+         return true;
+       }
+
       /* Fall through */
     case ASHIFT: case LSHIFTRT: case ASHIFTRT:
+      *total += rtx_cost (XEXP (x, 0), code, speed);
       if (mode == DImode)
-       return (8 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : 8)
-               + ((GET_CODE (XEXP (x, 0)) == REG
-                   || (GET_CODE (XEXP (x, 0)) == SUBREG
-                       && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG))
-                  ? 0 : 8));
+       {
+         *total += COSTS_N_INSNS (3);
+         return true;
+       }
 
-      extra_cost = 1;
+      *total += COSTS_N_INSNS (1);
       /* Increase the cost of complex shifts because they aren't any faster,
          and reduce dual issue opportunities.  */
       if (arm_tune_cortex_a9
          && outer != SET && GET_CODE (XEXP (x, 1)) != CONST_INT)
-       extra_cost++;
-
-      return (extra_cost + ((GET_CODE (XEXP (x, 0)) == REG
-                   || (GET_CODE (XEXP (x, 0)) == SUBREG
-                       && GET_CODE (SUBREG_REG (XEXP (x, 0))) == REG))
-                  ? 0 : 4)
-             + ((GET_CODE (XEXP (x, 1)) == REG
-                 || (GET_CODE (XEXP (x, 1)) == SUBREG
-                     && GET_CODE (SUBREG_REG (XEXP (x, 1))) == REG)
-                 || (GET_CODE (XEXP (x, 1)) == CONST_INT))
-                ? 0 : 4));
+       ++*total;
+
+      return true;
 
     case MINUS:
-      if (GET_CODE (XEXP (x, 1)) == MULT && mode == SImode && arm_arch_thumb2)
+      if (TARGET_THUMB2)
        {
-         extra_cost = rtx_cost (XEXP (x, 1), code, true);
-         if (!REG_OR_SUBREG_REG (XEXP (x, 0)))
-           extra_cost += 4 * ARM_NUM_REGS (mode);
-         return extra_cost;
+         if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+           {
+             if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+               *total = COSTS_N_INSNS (1);
+             else
+               *total = COSTS_N_INSNS (20);
+           }
+         else
+           *total = COSTS_N_INSNS (ARM_NUM_REGS (mode));
+         /* Thumb2 does not have RSB, so all arguments must be
+            registers (subtracting a constant is canonicalized as
+            addition of the negated constant).  */
+         return false;
        }
 
       if (mode == DImode)
-       return (4 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 8)
-               + ((REG_OR_SUBREG_REG (XEXP (x, 0))
-                   || (GET_CODE (XEXP (x, 0)) == CONST_INT
-                      && const_ok_for_arm (INTVAL (XEXP (x, 0)))))
-                  ? 0 : 8));
+       {
+         *total = COSTS_N_INSNS (ARM_NUM_REGS (mode));
+         if (GET_CODE (XEXP (x, 0)) == CONST_INT
+             && const_ok_for_arm (INTVAL (XEXP (x, 0))))
+           {
+             *total += rtx_cost (XEXP (x, 1), code, speed);
+             return true;
+           }
+
+         if (GET_CODE (XEXP (x, 1)) == CONST_INT
+             && const_ok_for_arm (INTVAL (XEXP (x, 1))))
+           {
+             *total += rtx_cost (XEXP (x, 0), code, speed);
+             return true;
+           }
+
+         return false;
+       }
 
       if (GET_MODE_CLASS (mode) == MODE_FLOAT)
-       return (2 + ((REG_OR_SUBREG_REG (XEXP (x, 1))
-                     || (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
-                         && arm_const_double_rtx (XEXP (x, 1))))
-                    ? 0 : 8)
-               + ((REG_OR_SUBREG_REG (XEXP (x, 0))
-                   || (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE
-                       && arm_const_double_rtx (XEXP (x, 0))))
-                  ? 0 : 8));
-
-      if (((GET_CODE (XEXP (x, 0)) == CONST_INT
-           && const_ok_for_arm (INTVAL (XEXP (x, 0)))
-           && REG_OR_SUBREG_REG (XEXP (x, 1))))
-         || (((subcode = GET_CODE (XEXP (x, 1))) == ASHIFT
-              || subcode == ASHIFTRT || subcode == LSHIFTRT
-              || subcode == ROTATE || subcode == ROTATERT
-              || (subcode == MULT
-                  && GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT
-                  && ((INTVAL (XEXP (XEXP (x, 1), 1)) &
-                       (INTVAL (XEXP (XEXP (x, 1), 1)) - 1)) == 0)))
-             && REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 0))
-             && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 1), 1))
-                 || GET_CODE (XEXP (XEXP (x, 1), 1)) == CONST_INT)
-             && REG_OR_SUBREG_REG (XEXP (x, 0))))
-       return 1;
+       {
+         if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+           {
+             *total = COSTS_N_INSNS (1);
+             if (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE
+                 && arm_const_double_rtx (XEXP (x, 0)))
+               {
+                 *total += rtx_cost (XEXP (x, 1), code, speed);
+                 return true;
+               }
+
+             if (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
+                 && arm_const_double_rtx (XEXP (x, 1)))
+               {
+                 *total += rtx_cost (XEXP (x, 0), code, speed);
+                 return true;
+               }
+
+             return false;
+           }
+         *total = COSTS_N_INSNS (20);
+         return false;
+       }
+
+      *total = COSTS_N_INSNS (1);
+      if (GET_CODE (XEXP (x, 0)) == CONST_INT
+         && const_ok_for_arm (INTVAL (XEXP (x, 0))))
+       {
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         return true;
+       }
+
+      subcode = GET_CODE (XEXP (x, 1));
+      if (subcode == ASHIFT || subcode == ASHIFTRT
+         || subcode == LSHIFTRT
+         || subcode == ROTATE || subcode == ROTATERT)
+       {
+         *total += rtx_cost (XEXP (x, 0), code, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 1), 0), subcode, speed);
+         return true;
+       }
+
+      /* A shift as a part of RSB costs no more than RSB itself.  */
+      if (GET_CODE (XEXP (x, 0)) == MULT
+         && power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode))
+       {
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), code, speed);
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         return true;
+       }
+
+      if (subcode == MULT
+         && power_of_two_operand (XEXP (XEXP (x, 1), 1), SImode))
+       {
+         *total += rtx_cost (XEXP (x, 0), code, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 1), 0), subcode, speed);
+         return true;
+       }
+
+      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == RTX_COMPARE
+         || GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == RTX_COMM_COMPARE)
+       {
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, speed);
+         if (GET_CODE (XEXP (XEXP (x, 1), 0)) == REG
+             && REGNO (XEXP (XEXP (x, 1), 0)) != CC_REGNUM)
+           *total += COSTS_N_INSNS (1);
+
+         return true;
+       }
+
       /* Fall through */
 
     case PLUS:
-      if (arm_arch6 && mode == SImode
+      if (code == PLUS && arm_arch6 && mode == SImode
          && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
              || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
-       return 1 + (GET_CODE (XEXP (XEXP (x, 0), 0)) == MEM ? 10 : 0)
-                + (GET_CODE (XEXP (x, 1)) == MEM ? 10 : 0);
+       {
+         *total = COSTS_N_INSNS (1);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), GET_CODE (XEXP (x, 0)),
+                             speed);
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         return true;
+       }
 
-      if (GET_CODE (XEXP (x, 0)) == MULT)
+      /* MLA: All arguments must be registers.  We filter out
+        multiplication by a power of two, so that we fall down into
+        the code below.  */
+      if (GET_CODE (XEXP (x, 0)) == MULT
+         && !power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode))
        {
-         extra_cost = rtx_cost (XEXP (x, 0), code, true);
-         if (!REG_OR_SUBREG_REG (XEXP (x, 1)))
-           extra_cost += 4 * ARM_NUM_REGS (mode);
-         return extra_cost;
+         /* The cost comes from the cost of the multiply.  */
+         return false;
        }
 
       if (GET_MODE_CLASS (mode) == MODE_FLOAT)
-       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
-                       && arm_const_double_rtx (XEXP (x, 1))))
-                  ? 0 : 8));
+       {
+         if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+           {
+             *total = COSTS_N_INSNS (1);
+             if (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
+                 && arm_const_double_rtx (XEXP (x, 1)))
+               {
+                 *total += rtx_cost (XEXP (x, 0), code, speed);
+                 return true;
+               }
+
+             return false;
+           }
+
+         *total = COSTS_N_INSNS (20);
+         return false;
+       }
+
+      if (GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == RTX_COMPARE
+         || GET_RTX_CLASS (GET_CODE (XEXP (x, 0))) == RTX_COMM_COMPARE)
+       {
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 1), code, speed);
+         if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+             && REGNO (XEXP (XEXP (x, 0), 0)) != CC_REGNUM)
+           *total += COSTS_N_INSNS (1);
+         return true;
+       }
 
       /* Fall through */
+
     case AND: case XOR: case IOR:
       extra_cost = 0;
 
@@ -5016,38 +6408,54 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
           && GET_CODE (XEXP (x, 1)) != CONST_INT)
          || (REG_OR_SUBREG_REG (XEXP (x, 0))
              && ARM_FRAME_RTX (REG_OR_SUBREG_RTX (XEXP (x, 0)))))
-       extra_cost = 4;
+       *total = 4;
 
       if (mode == DImode)
-       return (4 + extra_cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 8)
-               + ((REG_OR_SUBREG_REG (XEXP (x, 1))
-                   || (GET_CODE (XEXP (x, 1)) == CONST_INT
-                       && const_ok_for_op (INTVAL (XEXP (x, 1)), code)))
-                  ? 0 : 8));
-
-      if (REG_OR_SUBREG_REG (XEXP (x, 0)))
-       return (1 + (GET_CODE (XEXP (x, 1)) == CONST_INT ? 0 : extra_cost)
-               + ((REG_OR_SUBREG_REG (XEXP (x, 1))
-                   || (GET_CODE (XEXP (x, 1)) == CONST_INT
-                       && const_ok_for_op (INTVAL (XEXP (x, 1)), code)))
-                  ? 0 : 4));
-
-      else if (REG_OR_SUBREG_REG (XEXP (x, 1)))
-       return (1 + extra_cost
-               + ((((subcode = GET_CODE (XEXP (x, 0))) == ASHIFT
-                    || subcode == LSHIFTRT || subcode == ASHIFTRT
-                    || subcode == ROTATE || subcode == ROTATERT
-                    || (subcode == MULT
-                        && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
-                        && ((INTVAL (XEXP (XEXP (x, 0), 1)) &
-                             (INTVAL (XEXP (XEXP (x, 0), 1)) - 1)) == 0)))
-                   && (REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 0)))
-                   && ((REG_OR_SUBREG_REG (XEXP (XEXP (x, 0), 1))
-                        && !arm_tune_cortex_a9)
-                       || GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT))
-                  ? 0 : 4));
+       {
+         *total += COSTS_N_INSNS (2);
+         if (GET_CODE (XEXP (x, 1)) == CONST_INT
+             && const_ok_for_op (INTVAL (XEXP (x, 1)), code))
+           {
+             *total += rtx_cost (XEXP (x, 0), code, speed);
+             return true;
+           }
 
-      return 8;
+         return false;
+       }
+
+      *total += COSTS_N_INSNS (1);
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && const_ok_for_op (INTVAL (XEXP (x, 1)), code))
+       {
+         *total += rtx_cost (XEXP (x, 0), code, speed);
+         return true;
+       }
+      subcode = GET_CODE (XEXP (x, 0));
+      if (subcode == ASHIFT || subcode == ASHIFTRT
+         || subcode == LSHIFTRT
+         || subcode == ROTATE || subcode == ROTATERT)
+       {
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+         return true;
+       }
+
+      if (subcode == MULT
+         && power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode))
+       {
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+         return true;
+       }
+
+      if (subcode == UMIN || subcode == UMAX
+         || subcode == SMIN || subcode == SMAX)
+       {
+         *total = COSTS_N_INSNS (3);
+         return true;
+       }
+
+      return false;
 
     case MULT:
       /* This should have been handled by the CPU specific routines.  */
@@ -5061,108 +6469,286 @@ arm_rtx_costs_1 (rtx x, enum rtx_code code, enum rtx_code outer)
              == GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)))
          && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND
              || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND))
-       return 8;
-      return 99;
+       {
+         *total = rtx_cost (XEXP (XEXP (x, 0), 0), LSHIFTRT, speed);
+         return true;
+       }
+      *total = COSTS_N_INSNS (2); /* Plus the cost of the MULT */
+      return false;
 
     case NEG:
       if (GET_MODE_CLASS (mode) == MODE_FLOAT)
-       return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 6);
+       {
+         if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+           {
+             *total = COSTS_N_INSNS (1);
+             return false;
+           }
+         *total = COSTS_N_INSNS (2);
+         return false;
+       }
+
       /* Fall through */
     case NOT:
-      if (mode == DImode)
-       return 4 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4);
+      *total = COSTS_N_INSNS (ARM_NUM_REGS(mode));
+      if (mode == SImode && code == NOT)
+       {
+         subcode = GET_CODE (XEXP (x, 0));
+         if (subcode == ASHIFT || subcode == ASHIFTRT
+             || subcode == LSHIFTRT
+             || subcode == ROTATE || subcode == ROTATERT
+             || (subcode == MULT
+                 && power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode)))
+           {
+             *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+             /* Register shifts cost an extra cycle.  */
+             if (GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
+               *total += COSTS_N_INSNS (1) + rtx_cost (XEXP (XEXP (x, 0), 1),
+                                                       subcode, speed);
+             return true;
+           }
+       }
 
-      return 1 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4);
+      return false;
 
     case IF_THEN_ELSE:
       if (GET_CODE (XEXP (x, 1)) == PC || GET_CODE (XEXP (x, 2)) == PC)
-       return 14;
-      return 2;
+       {
+         *total = COSTS_N_INSNS (4);
+         return true;
+       }
 
+      operand = XEXP (x, 0);
+
+      if (!((GET_RTX_CLASS (GET_CODE (operand)) == RTX_COMPARE
+            || GET_RTX_CLASS (GET_CODE (operand)) == RTX_COMM_COMPARE)
+           && GET_CODE (XEXP (operand, 0)) == REG
+           && REGNO (XEXP (operand, 0)) == CC_REGNUM))
+       *total += COSTS_N_INSNS (1);
+      *total += (rtx_cost (XEXP (x, 1), code, speed)
+                + rtx_cost (XEXP (x, 2), code, speed));
+      return true;
+
+    case NE:
+      if (mode == SImode && XEXP (x, 1) == const0_rtx)
+       {
+         *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, speed);
+         return true;
+       }
+      goto scc_insn;
+
+    case GE:
+      if ((GET_CODE (XEXP (x, 0)) != REG || REGNO (XEXP (x, 0)) != CC_REGNUM)
+         && mode == SImode && XEXP (x, 1) == const0_rtx)
+       {
+         *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, speed);
+         return true;
+       }
+      goto scc_insn;
+
+    case LT:
+      if ((GET_CODE (XEXP (x, 0)) != REG || REGNO (XEXP (x, 0)) != CC_REGNUM)
+         && mode == SImode && XEXP (x, 1) == const0_rtx)
+       {
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, speed);
+         return true;
+       }
+      goto scc_insn;
+
+    case EQ:
+    case GT:
+    case LE:
+    case GEU:
+    case LTU:
+    case GTU:
+    case LEU:
+    case UNORDERED:
+    case ORDERED:
+    case UNEQ:
+    case UNGE:
+    case UNLT:
+    case UNGT:
+    case UNLE:
+    scc_insn:
+      /* SCC insns.  In the case where the comparison has already been
+        performed, then they cost 2 instructions.  Otherwise they need
+        an additional comparison before them.  */
+      *total = COSTS_N_INSNS (2);
+      if (GET_CODE (XEXP (x, 0)) == REG && REGNO (XEXP (x, 0)) == CC_REGNUM)
+       {
+         return true;
+       }
+
+      /* Fall through */
     case COMPARE:
-      return 1;
+      if (GET_CODE (XEXP (x, 0)) == REG && REGNO (XEXP (x, 0)) == CC_REGNUM)
+       {
+         *total = 0;
+         return true;
+       }
+
+      *total += COSTS_N_INSNS (1);
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT
+         && const_ok_for_op (INTVAL (XEXP (x, 1)), code))
+       {
+         *total += rtx_cost (XEXP (x, 0), code, speed);
+         return true;
+       }
+
+      subcode = GET_CODE (XEXP (x, 0));
+      if (subcode == ASHIFT || subcode == ASHIFTRT
+         || subcode == LSHIFTRT
+         || subcode == ROTATE || subcode == ROTATERT)
+       {
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+         return true;
+       }
+
+      if (subcode == MULT
+         && power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode))
+       {
+         *total += rtx_cost (XEXP (x, 1), code, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+         return true;
+       }
+      
+      return false;
+
+    case UMIN:
+    case UMAX:
+    case SMIN:
+    case SMAX:
+      *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, speed);
+      if (GET_CODE (XEXP (x, 1)) != CONST_INT
+         || !const_ok_for_arm (INTVAL (XEXP (x, 1))))
+       *total += rtx_cost (XEXP (x, 1), code, speed);
+      return true;
 
     case ABS:
-      return 4 + (mode == DImode ? 4 : 0);
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+       {
+         if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+           {
+             *total = COSTS_N_INSNS (1);
+             return false;
+           }
+         *total = COSTS_N_INSNS (20);
+         return false;
+       }
+      *total = COSTS_N_INSNS (1);
+      if (mode == DImode)
+       *total += COSTS_N_INSNS (3);
+      return false;
 
     case SIGN_EXTEND:
-      if (arm_arch_thumb2 && mode == SImode)
-       return 1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0);
+      if (GET_MODE_CLASS (mode) == MODE_INT)
+       {
+         *total = 0;
+         if (mode == DImode)
+           *total += COSTS_N_INSNS (1);
+
+         if (GET_MODE (XEXP (x, 0)) != SImode)
+           {
+             if (arm_arch6)
+               {
+                 if (GET_CODE (XEXP (x, 0)) != MEM)
+                   *total += COSTS_N_INSNS (1);
+               }
+             else if (!arm_arch4 || GET_CODE (XEXP (x, 0)) != MEM)
+               *total += COSTS_N_INSNS (2);
+           }
+
+         return false;
+       }
 
-      if (GET_MODE (XEXP (x, 0)) == QImode)
-       return (4 + (mode == DImode ? 4 : 0)
-               + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
       /* Fall through */
     case ZERO_EXTEND:
-      if (arm_arch6 && mode == SImode)
-       return 1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0);
-
-      switch (GET_MODE (XEXP (x, 0)))
+      *total = 0;
+      if (GET_MODE_CLASS (mode) == MODE_INT)
        {
-       case QImode:
-         return (1 + (mode == DImode ? 4 : 0)
-                 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
+         if (mode == DImode)
+           *total += COSTS_N_INSNS (1);
 
-       case HImode:
-         return (4 + (mode == DImode ? 4 : 0)
-                 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
+         if (GET_MODE (XEXP (x, 0)) != SImode)
+           {
+             if (arm_arch6)
+               {
+                 if (GET_CODE (XEXP (x, 0)) != MEM)
+                   *total += COSTS_N_INSNS (1);
+               }
+             else if (!arm_arch4 || GET_CODE (XEXP (x, 0)) != MEM)
+               *total += COSTS_N_INSNS (GET_MODE (XEXP (x, 0)) == QImode ?
+                                        1 : 2);
+           }
 
-       case SImode:
-         return (1 + (GET_CODE (XEXP (x, 0)) == MEM ? 10 : 0));
+         return false;
+       }
 
+      switch (GET_MODE (XEXP (x, 0)))
+       {
        case V8QImode:
        case V4HImode:
        case V2SImode:
        case V4QImode:
        case V2HImode:
-           return 1;
+         *total = COSTS_N_INSNS (1);
+         return false;
 
        default:
          gcc_unreachable ();
        }
       gcc_unreachable ();
 
+    case ZERO_EXTRACT:
+    case SIGN_EXTRACT:
+      *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, speed);
+      return true;
+
     case CONST_INT:
-      if (const_ok_for_arm (INTVAL (x)))
-       return outer == SET ? 2 : -1;
-      else if (outer == AND
-              && const_ok_for_arm (~INTVAL (x)))
-       return -1;
-      else if ((outer == COMPARE
-               || outer == PLUS || outer == MINUS)
-              && const_ok_for_arm (-INTVAL (x)))
-       return -1;
+      if (const_ok_for_arm (INTVAL (x))
+         || const_ok_for_arm (~INTVAL (x)))
+       *total = COSTS_N_INSNS (1);
       else
-       return 5;
+       *total = COSTS_N_INSNS (arm_gen_constant (SET, mode, NULL_RTX,
+                                                 INTVAL (x), NULL_RTX,
+                                                 NULL_RTX, 0, 0));
+      return true;
 
     case CONST:
     case LABEL_REF:
     case SYMBOL_REF:
-      return 6;
+      *total = COSTS_N_INSNS (3);
+      return true;
 
     case HIGH:
+      *total = COSTS_N_INSNS (1);
+      return true;
+
     case LO_SUM:
-      return (outer == SET) ? 1 : -1;
+      *total = COSTS_N_INSNS (1);
+      *total += rtx_cost (XEXP (x, 0), code, speed);
+      return true;
 
     case CONST_DOUBLE:
-      if (arm_const_double_rtx (x) || vfp3_const_double_rtx (x))
-       return outer == SET ? 2 : -1;
-      else if ((outer == COMPARE || outer == PLUS)
-              && neg_const_double_rtx_ok_for_fpa (x))
-       return -1;
-      return 7;
+      if (TARGET_HARD_FLOAT && vfp3_const_double_rtx (x))
+       *total = COSTS_N_INSNS (1);
+      else
+       *total = COSTS_N_INSNS (4);
+      return true;
 
     default:
-      return 99;
+      *total = COSTS_N_INSNS (4);
+      return false;
     }
 }
 
 /* RTX costs when optimizing for size.  */
 static bool
-arm_size_rtx_costs (rtx x, int code, int outer_code, int *total)
+arm_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
+                   int *total)
 {
   enum machine_mode mode = GET_MODE (x);
-
   if (TARGET_THUMB1)
     {
       /* XXX TBD.  For now, use the standard costs.  */
@@ -5256,6 +6842,16 @@ arm_size_rtx_costs (rtx x, int code, int outer_code, int *total)
          return false;
        }
 
+      /* A shift as a part of ADD costs nothing.  */
+      if (GET_CODE (XEXP (x, 0)) == MULT
+         && power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode))
+       {
+         *total = COSTS_N_INSNS (TARGET_THUMB2 ? 2 : 1);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), code, false);
+         *total += rtx_cost (XEXP (x, 1), code, false);
+         return true;
+       }
+
       /* Fall through */
     case AND: case XOR: case IOR:
       if (mode == SImode)
@@ -5349,7 +6945,10 @@ arm_size_rtx_costs (rtx x, int code, int outer_code, int *total)
 
     case CONST_INT:
       if (const_ok_for_arm (INTVAL (x)))
-       *total = COSTS_N_INSNS (outer_code == SET ? 1 : 0);
+       /* A multiplication by a constant requires another instruction
+          to load the constant to a register.  */
+       *total = COSTS_N_INSNS ((outer_code == SET || outer_code == MULT)
+                               ? 1 : 0);
       else if (const_ok_for_arm (~INTVAL (x)))
        *total = COSTS_N_INSNS (outer_code == AND ? 0 : 1);
       else if (const_ok_for_arm (-INTVAL (x)))
@@ -5392,19 +6991,24 @@ arm_size_rtx_costs (rtx x, int code, int outer_code, int *total)
 
 /* RTX costs when optimizing for size.  */
 static bool
-arm_rtx_costs (rtx x, int code, int outer_code, int *total, bool speed)
+arm_rtx_costs (rtx x, int code, int outer_code, int *total,
+              bool speed)
 {
   if (!speed)
-    return arm_size_rtx_costs (x, code, outer_code, total);
+    return arm_size_rtx_costs (x, (enum rtx_code) code,
+                              (enum rtx_code) outer_code, total);
   else
-    return all_cores[(int)arm_tune].rtx_costs (x, code, outer_code, total);
+    return all_cores[(int)arm_tune].rtx_costs (x, (enum rtx_code) code,
+                                              (enum rtx_code) outer_code,
+                                              total, speed);
 }
 
 /* RTX costs for cores with a slow MUL implementation.  Thumb-2 is not
    supported on any "slowmul" cores, so it can be ignored.  */
 
 static bool
-arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
+arm_slowmul_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
+                      int *total, bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
 
@@ -5420,8 +7024,8 @@ arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
       if (GET_MODE_CLASS (mode) == MODE_FLOAT
          || mode == DImode)
        {
-         *total = 30;
-         return true;
+         *total = COSTS_N_INSNS (20);
+         return false;
        }
 
       if (GET_CODE (XEXP (x, 1)) == CONST_INT)
@@ -5437,20 +7041,19 @@ arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
          for (j = 0; i && j < 32; j += booth_unit_size)
            {
              i >>= booth_unit_size;
-             cost += 2;
+             cost++;
            }
 
-         *total = cost;
+         *total = COSTS_N_INSNS (cost);
+         *total += rtx_cost (XEXP (x, 0), code, speed);
          return true;
        }
 
-      *total = 30 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
-                 + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4);
-      return true;
+      *total = COSTS_N_INSNS (20);
+      return false;
 
     default:
-      *total = arm_rtx_costs_1 (x, code, outer_code);
-      return true;
+      return arm_rtx_costs_1 (x, outer_code, total, speed);;
     }
 }
 
@@ -5458,7 +7061,8 @@ arm_slowmul_rtx_costs (rtx x, int code, int outer_code, int *total)
 /* 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)
+arm_fastmul_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
+                      int *total, bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
 
@@ -5479,16 +7083,15 @@ arm_fastmul_rtx_costs (rtx x, int code, int outer_code, int *total)
          && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
              || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
        {
-         *total = 8;
-         return true;
+         *total = COSTS_N_INSNS(2);
+         return false;
        }
 
 
-      if (GET_MODE_CLASS (mode) == MODE_FLOAT
-         || mode == DImode)
+      if (mode == DImode)
        {
-         *total = 30;
-         return true;
+         *total = COSTS_N_INSNS (5);
+         return false;
        }
 
       if (GET_CODE (XEXP (x, 1)) == CONST_INT)
@@ -5504,20 +7107,34 @@ arm_fastmul_rtx_costs (rtx x, int code, int outer_code, int *total)
          for (j = 0; i && j < 32; j += booth_unit_size)
            {
              i >>= booth_unit_size;
-             cost += 2;
+             cost++;
            }
 
-         *total = cost;
-         return true;
+         *total = COSTS_N_INSNS(cost);
+         return false;
        }
 
-      *total = 8 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
-                + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4);
-      return true;
+      if (mode == SImode)
+       {
+         *total = COSTS_N_INSNS (4);
+         return false;
+       }
+
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+       {
+         if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+           {
+             *total = COSTS_N_INSNS (1);
+             return false;
+           }
+       }
+
+      /* Requires a lib call */
+      *total = COSTS_N_INSNS (20);
+      return false;
 
     default:
-      *total = arm_rtx_costs_1 (x, code, outer_code);
-      return true;
+      return arm_rtx_costs_1 (x, outer_code, total, speed);
     }
 }
 
@@ -5526,7 +7143,7 @@ arm_fastmul_rtx_costs (rtx x, int code, int outer_code, int *total)
    so it can be ignored.  */
 
 static bool
-arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
+arm_xscale_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code, int *total, bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
 
@@ -5538,6 +7155,15 @@ arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
 
   switch (code)
     {
+    case COMPARE:
+      if (GET_CODE (XEXP (x, 0)) != MULT)
+       return arm_rtx_costs_1 (x, outer_code, total, speed);
+
+      /* A COMPARE of a MULT is slow on XScale; the muls instruction
+        will stall until the multiplication is complete.  */
+      *total = COSTS_N_INSNS (3);
+      return false;
+
     case MULT:
       /* There is no point basing this on the tuning, since it is always the
         fast variant if it exists at all.  */
@@ -5546,60 +7172,58 @@ arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
          && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
              || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
        {
-         *total = 8;
-         return true;
+         *total = COSTS_N_INSNS (2);
+         return false;
        }
 
 
-      if (GET_MODE_CLASS (mode) == MODE_FLOAT
-         || mode == DImode)
+      if (mode == DImode)
        {
-         *total = 30;
-         return true;
+         *total = COSTS_N_INSNS (5);
+         return false;
        }
 
       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);
+         /* If operand 1 is a constant we can more accurately
+            calculate the cost of the multiply.  The multiplier can
+            retire 15 bits on the first cycle and a further 12 on the
+            second.  We do, of course, have to load the constant into
+            a register first.  */
+         unsigned HOST_WIDE_INT i = INTVAL (XEXP (x, 1));
+         /* There's a general overhead of one cycle.  */
+         int cost = 1;
          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.  */
+         if (i & 0x80000000)
+           i = ~i;
+
+         i &= (unsigned HOST_WIDE_INT) 0xffffffff;
+
          masked_const = i & 0xffff8000;
-         if (masked_const != 0 && masked_const != 0xffff8000)
+         if (masked_const != 0)
            {
+             cost++;
              masked_const = i & 0xf8000000;
-             if (masked_const == 0 || masked_const == 0xf8000000)
-               cost += 1;
-             else
-               cost += 2;
+             if (masked_const != 0)
+               cost++;
            }
-         *total = cost;
-         return true;
+         *total = COSTS_N_INSNS (cost);
+         return false;
        }
 
-      *total = 8 + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : 4)
-                + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : 4);
-      return true;
+      if (mode == SImode)
+       {
+         *total = COSTS_N_INSNS (3);
+         return false;
+       }
 
-    case COMPARE:
-      /* A COMPARE of a MULT is slow on XScale; the muls instruction
-        will stall until the multiplication is complete.  */
-      if (GET_CODE (XEXP (x, 0)) == MULT)
-       *total = 4 + rtx_cost (XEXP (x, 0), code, true);
-      else
-       *total = arm_rtx_costs_1 (x, code, outer_code);
-      return true;
+      /* Requires a lib call */
+      *total = COSTS_N_INSNS (20);
+      return false;
 
     default:
-      *total = arm_rtx_costs_1 (x, code, outer_code);
-      return true;
+      return arm_rtx_costs_1 (x, outer_code, total, speed);
     }
 }
 
@@ -5607,11 +7231,10 @@ arm_xscale_rtx_costs (rtx x, int code, int outer_code, int *total)
 /* RTX costs for 9e (and later) cores.  */
 
 static bool
-arm_9e_rtx_costs (rtx x, int code, int outer_code, int *total)
+arm_9e_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
+                 int *total, bool speed)
 {
   enum machine_mode mode = GET_MODE (x);
-  int nonreg_cost;
-  int cost;
 
   if (TARGET_THUMB1)
     {
@@ -5637,35 +7260,37 @@ arm_9e_rtx_costs (rtx x, int code, int outer_code, int *total)
          && (GET_CODE (XEXP (x, 0)) == ZERO_EXTEND
              || GET_CODE (XEXP (x, 0)) == SIGN_EXTEND))
        {
-         *total = 3;
-         return true;
+         *total = COSTS_N_INSNS (2);
+         return false;
        }
 
 
-      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
-       {
-         *total = 30;
-         return true;
-       }
       if (mode == DImode)
        {
-         cost = 7;
-         nonreg_cost = 8;
+         *total = COSTS_N_INSNS (5);
+         return false;
        }
-      else
+
+      if (mode == SImode)
        {
-         cost = 2;
-         nonreg_cost = 4;
+         *total = COSTS_N_INSNS (2);
+         return false;
        }
 
+      if (GET_MODE_CLASS (mode) == MODE_FLOAT)
+       {
+         if (TARGET_HARD_FLOAT && (mode == SFmode || mode == DFmode))
+           {
+             *total = COSTS_N_INSNS (1);
+             return false;
+           }
+       }
 
-      *total = cost + (REG_OR_SUBREG_REG (XEXP (x, 0)) ? 0 : nonreg_cost)
-                   + (REG_OR_SUBREG_REG (XEXP (x, 1)) ? 0 : nonreg_cost);
-      return true;
+      *total = COSTS_N_INSNS (20);
+      return false;
 
     default:
-      *total = arm_rtx_costs_1 (x, code, outer_code);
-      return true;
+      return arm_rtx_costs_1 (x, outer_code, total, speed);
     }
 }
 /* All address computations that can be done are free, but rtx cost returns
@@ -5682,9 +7307,9 @@ arm_arm_address_cost (rtx x)
   if (c == MEM || c == LABEL_REF || c == SYMBOL_REF)
     return 10;
 
-  if (c == PLUS || c == MINUS)
+  if (c == PLUS)
     {
-      if (GET_CODE (XEXP (x, 0)) == CONST_INT)
+      if (GET_CODE (XEXP (x, 1)) == CONST_INT)
        return 2;
 
       if (ARITHMETIC_P (XEXP (x, 0)) || ARITHMETIC_P (XEXP (x, 1)))
@@ -6035,7 +7660,7 @@ neon_valid_immediate (rtx op, enum machine_mode mode, int inverse,
       break;                                   \
     }
 
-  unsigned int i, elsize, idx = 0, n_elts = CONST_VECTOR_NUNITS (op);
+  unsigned int i, elsize = 0, idx = 0, n_elts = CONST_VECTOR_NUNITS (op);
   unsigned int innersize = GET_MODE_SIZE (GET_MODE_INNER (mode));
   unsigned char bytes[16];
   int immtype = -1, matches;
@@ -6477,10 +8102,13 @@ arm_coproc_mem_operand (rtx op, bool wb)
 }
 
 /* Return TRUE if OP is a memory operand which we can load or store a vector
-   to/from. If CORE is true, we're moving from ARM registers not Neon
-   registers.  */
+   to/from. TYPE is one of the following values:
+    0 - Vector load/stor (vldr)
+    1 - Core registers (ldm)
+    2 - Element/structure loads (vld1)
+ */
 int
-neon_vector_mem_operand (rtx op, bool core)
+neon_vector_mem_operand (rtx op, int type)
 {
   rtx ind;
 
@@ -6513,23 +8141,15 @@ neon_vector_mem_operand (rtx op, bool core)
     return arm_address_register_rtx_p (ind, 0);
 
   /* Allow post-increment with Neon registers.  */
-  if (!core && GET_CODE (ind) == POST_INC)
+  if (type != 1 && (GET_CODE (ind) == POST_INC || GET_CODE (ind) == PRE_DEC))
     return arm_address_register_rtx_p (XEXP (ind, 0), 0);
 
-#if 0
-  /* FIXME: We can support this too if we use VLD1/VST1.  */
-  if (!core
-      && GET_CODE (ind) == POST_MODIFY
-      && arm_address_register_rtx_p (XEXP (ind, 0), 0)
-      && GET_CODE (XEXP (ind, 1)) == PLUS
-      && rtx_equal_p (XEXP (XEXP (ind, 1), 0), XEXP (ind, 0)))
-    ind = XEXP (ind, 1);
-#endif
+  /* FIXME: vld1 allows register post-modify.  */
 
   /* Match:
      (plus (reg)
           (const)).  */
-  if (!core
+  if (type == 0
       && GET_CODE (ind) == PLUS
       && GET_CODE (XEXP (ind, 0)) == REG
       && REG_MODE_OK_FOR_BASE_P (XEXP (ind, 0), VOIDmode)
@@ -6596,10 +8216,17 @@ arm_eliminable_register (rtx x)
 enum reg_class
 coproc_secondary_reload_class (enum machine_mode mode, rtx x, bool wb)
 {
+  if (mode == HFmode)
+    {
+      if (s_register_operand (x, mode) || neon_vector_mem_operand (x, 2))
+       return NO_REGS;
+      return GENERAL_REGS;
+    }
+
   if (TARGET_NEON
       && (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
           || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
-      && neon_vector_mem_operand (x, FALSE))
+      && neon_vector_mem_operand (x, 0))
      return NO_REGS;
 
   if (arm_coproc_mem_operand (x, wb) || s_register_operand (x, mode))
@@ -6959,7 +8586,7 @@ adjacent_mem_locations (rtx a, rtx b)
       /* Don't accept any offset that will require multiple
         instructions to handle, since this would cause the
         arith_adjacentmem pattern to output an overlong sequence.  */
-      if (!const_ok_for_op (PLUS, val0) || !const_ok_for_op (PLUS, val1))
+      if (!const_ok_for_op (val0, PLUS) || !const_ok_for_op (val1, PLUS))
        return 0;
 
       /* Don't allow an eliminable register: register elimination can make
@@ -7000,6 +8627,8 @@ load_multiple_sequence (rtx *operands, int nops, int *regs, int *base,
      though could be easily extended if required.  */
   gcc_assert (nops >= 2 && nops <= 4);
 
+  memset (order, 0, 4 * sizeof (int));
+
   /* Loop over the operands and check that the memory references are
      suitable (i.e. immediate offsets from the same base register).  At
      the same time, extract the target register, and the memory
@@ -7227,6 +8856,8 @@ store_multiple_sequence (rtx *operands, int nops, int *regs, int *base,
      extended if required.  */
   gcc_assert (nops >= 2 && nops <= 4);
 
+  memset (order, 0, 4 * sizeof (int));
+
   /* Loop over the operands and check that the memory references are
      suitable (i.e. immediate offsets from the same base register).  At
      the same time, extract the target register, and the memory
@@ -9761,8 +11392,7 @@ vfp_emit_fstmd (int base_reg, int count)
     }
 
   par = emit_insn (par);
-  REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
-                                      REG_NOTES (par));
+  add_reg_note (par, REG_FRAME_RELATED_EXPR, dwarf);
   RTX_FRAME_RELATED_P (par) = 1;
 
   return count * 8;
@@ -9985,7 +11615,7 @@ output_move_double (rtx *operands)
 
   if (code0 == REG)
     {
-      int reg0 = REGNO (operands[0]);
+      unsigned int reg0 = REGNO (operands[0]);
 
       otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
 
@@ -10045,36 +11675,36 @@ output_move_double (rtx *operands)
                }
              else
                {
-                 /* IWMMXT allows offsets larger than ldrd can handle,
-                    fix these up with a pair of ldr.  */
-                 if (GET_CODE (otherops[2]) == CONST_INT
-                     && (INTVAL(otherops[2]) <= -256
-                         || INTVAL(otherops[2]) >= 256))
+                 /* Use a single insn if we can.
+                    FIXME: IWMMXT allows offsets larger than ldrd can
+                    handle, fix these up with a pair of ldr.  */
+                 if (TARGET_THUMB2
+                     || GET_CODE (otherops[2]) != CONST_INT
+                     || (INTVAL (otherops[2]) > -256
+                         && INTVAL (otherops[2]) < 256))
+                   output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
+                 else
                    {
                      output_asm_insn ("ldr%?\t%0, [%1, %2]!", otherops);
-                     otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
-                     output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
+                     output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
                    }
-                 else
-                   output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
                }
            }
          else
            {
-             /* IWMMXT allows offsets larger than ldrd can handle,
+             /* Use a single insn if we can.
+                FIXME: IWMMXT allows offsets larger than ldrd can handle,
                 fix these up with a pair of ldr.  */
-             if (GET_CODE (otherops[2]) == CONST_INT
-                 && (INTVAL(otherops[2]) <= -256
-                     || INTVAL(otherops[2]) >= 256))
+             if (TARGET_THUMB2
+                 || GET_CODE (otherops[2]) != CONST_INT
+                 || (INTVAL (otherops[2]) > -256
+                     && INTVAL (otherops[2]) < 256))
+               output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
+             else
                {
-                 otherops[0] = gen_rtx_REG (SImode, 1 + reg0);
-                 output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
-                 otherops[0] = operands[0];
+                 output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
                  output_asm_insn ("ldr%?\t%0, [%1], %2", otherops);
                }
-             else
-               /* We only allow constant increments, so this is safe.  */
-               output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
            }
          break;
 
@@ -10128,6 +11758,7 @@ output_move_double (rtx *operands)
                  operands[1] = otherops[0];
                  if (TARGET_LDRD
                      && (GET_CODE (otherops[2]) == REG
+                         || TARGET_THUMB2
                          || (GET_CODE (otherops[2]) == CONST_INT
                              && INTVAL (otherops[2]) > -256
                              && INTVAL (otherops[2]) < 256)))
@@ -10240,23 +11871,19 @@ output_move_double (rtx *operands)
 
          /* IWMMXT allows offsets larger than ldrd can handle,
             fix these up with a pair of ldr.  */
-         if (GET_CODE (otherops[2]) == CONST_INT
+         if (!TARGET_THUMB2
+             && GET_CODE (otherops[2]) == CONST_INT
              && (INTVAL(otherops[2]) <= -256
                  || INTVAL(otherops[2]) >= 256))
            {
-             rtx reg1;
-             reg1 = gen_rtx_REG (SImode, 1 + REGNO (operands[1]));
              if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
                {
                  output_asm_insn ("ldr%?\t%0, [%1, %2]!", otherops);
-                 otherops[0] = reg1;
-                 output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
+                 output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
                }
              else
                {
-                 otherops[0] = reg1;
-                 output_asm_insn ("ldr%?\t%0, [%1, #4]", otherops);
-                 otherops[0] = operands[1];
+                 output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
                  output_asm_insn ("ldr%?\t%0, [%1], %2", otherops);
                }
            }
@@ -10291,6 +11918,7 @@ output_move_double (rtx *operands)
            }
          if (TARGET_LDRD
              && (GET_CODE (otherops[2]) == REG
+                 || TARGET_THUMB2
                  || (GET_CODE (otherops[2]) == CONST_INT
                      && INTVAL (otherops[2]) > -256
                      && INTVAL (otherops[2]) < 256)))
@@ -10304,9 +11932,9 @@ output_move_double (rtx *operands)
 
         default:
          otherops[0] = adjust_address (operands[0], SImode, 4);
-         otherops[1] = gen_rtx_REG (SImode, 1 + REGNO (operands[1]));
+         otherops[1] = operands[1];
          output_asm_insn ("str%?\t%1, %0", operands);
-         output_asm_insn ("str%?\t%1, %0", otherops);
+         output_asm_insn ("str%?\t%H1, %0", otherops);
        }
     }
 
@@ -10314,7 +11942,7 @@ output_move_double (rtx *operands)
 }
 
 /* Output a move, load or store for quad-word vectors in ARM registers.  Only
-   handles MEMs accepted by neon_vector_mem_operand with CORE=true.  */
+   handles MEMs accepted by neon_vector_mem_operand with TYPE=1.  */
 
 const char *
 output_move_quad (rtx *operands)
@@ -10510,6 +12138,13 @@ output_move_neon (rtx *operands)
       ops[1] = reg;
       break;
 
+    case PRE_DEC:
+      /* FIXME: We should be using vld1/vst1 here in BE mode?  */
+      templ = "v%smdb%%?\t%%0!, %%h1";
+      ops[0] = XEXP (addr, 0);
+      ops[1] = reg;
+      break;
+    
     case POST_MODIFY:
       /* FIXME: Not currently enabled in neon_vector_mem_operand.  */
       gcc_unreachable ();
@@ -11174,7 +12809,7 @@ output_return_instruction (rtx operand, int really_return, int reverse)
 
   sprintf (conditional, "%%?%%%c0", reverse ? 'D' : 'd');
 
-  return_used_this_function = 1;
+  cfun->machine->return_used_this_function = 1;
 
   offsets = arm_get_frame_offsets ();
   live_regs_mask = offsets->saved_regs_mask;
@@ -11439,7 +13074,6 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
   if (crtl->calls_eh_return)
     asm_fprintf (f, "\t@ Calls __builtin_eh_return.\n");
 
-  return_used_this_function = 0;
 }
 
 const char *
@@ -11460,7 +13094,8 @@ arm_output_epilogue (rtx sibling)
 
   /* If we have already generated the return instruction
      then it is futile to generate anything else.  */
-  if (use_return_insn (FALSE, sibling) && return_used_this_function)
+  if (use_return_insn (FALSE, sibling) && 
+      (cfun->machine->return_used_this_function != 0))
     return "";
 
   func_type = arm_current_func_type ();
@@ -11660,7 +13295,7 @@ arm_output_epilogue (rtx sibling)
         (where frame pointer is required to point at first register)
         and ARM-non-apcs-frame. Therefore, such change is postponed
         until real need arise.  */
-      HOST_WIDE_INT amount;
+      unsigned HOST_WIDE_INT amount;
       int rfe;
       /* Restore stack pointer if necessary.  */
       if (TARGET_ARM && frame_pointer_needed)
@@ -11907,7 +13542,7 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
       /* ??? Probably not safe to set this here, since it assumes that a
         function will be emitted as assembly immediately after we generate
         RTL for it.  This does not happen for inline functions.  */
-      return_used_this_function = 0;
+      cfun->machine->return_used_this_function = 0;
     }
   else /* TARGET_32BIT */
     {
@@ -11915,7 +13550,7 @@ arm_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
       offsets = arm_get_frame_offsets ();
 
       gcc_assert (!use_return_insn (FALSE, NULL)
-                 || !return_used_this_function
+                 || (cfun->machine->return_used_this_function != 0)
                  || offsets->saved_regs == offsets->outgoing_args
                  || frame_pointer_needed);
 
@@ -12047,8 +13682,8 @@ emit_multi_reg_push (unsigned long mask)
   RTX_FRAME_RELATED_P (tmp) = 1;
   XVECEXP (dwarf, 0, 0) = tmp;
 
-  REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
-                                      REG_NOTES (par));
+  add_reg_note (par, REG_FRAME_RELATED_EXPR, dwarf);
+
   return par;
 }
 
@@ -12114,8 +13749,8 @@ emit_sfm (int base_reg, int count)
   XVECEXP (dwarf, 0, 0) = tmp;
 
   par = emit_insn (par);
-  REG_NOTES (par) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
-                                      REG_NOTES (par));
+  add_reg_note (par, REG_FRAME_RELATED_EXPR, dwarf);
+
   return par;
 }
 
@@ -12293,22 +13928,23 @@ arm_get_frame_offsets (void)
        {
          int reg = -1;
 
-         for (i = 4; i <= (TARGET_THUMB1 ? LAST_LO_REGNUM : 11); i++)
-           {
-             if ((offsets->saved_regs_mask & (1 << i)) == 0)
-               {
-                 reg = i;
-                 break;
-               }
-           }
-
-         if (reg == -1 && arm_size_return_regs () <= 12
-             && !crtl->tail_call_emit)
+         /* If it is safe to use r3, then do so.  This sometimes 
+            generates better code on Thumb-2 by avoiding the need to
+            use 32-bit push/pop instructions.  */
+         if (!crtl->tail_call_emit
+             && arm_size_return_regs () <= 12)
            {
-             /* Push/pop an argument register (r3) if all callee saved
-                registers are already being pushed.  */
              reg = 3;
            }
+         else
+           for (i = 4; i <= (TARGET_THUMB1 ? LAST_LO_REGNUM : 11); i++)
+             {
+               if ((offsets->saved_regs_mask & (1 << i)) == 0)
+                 {
+                   reg = i;
+                   break;
+                 }
+             }
 
          if (reg != -1)
            {
@@ -12534,8 +14170,7 @@ thumb_set_frame_pointer (arm_stack_offsets *offsets)
       dwarf = gen_rtx_SET (VOIDmode, hard_frame_pointer_rtx,
                           plus_constant (stack_pointer_rtx, amount));
       RTX_FRAME_RELATED_P (dwarf) = 1;
-      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
-                                           REG_NOTES (insn));
+      add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
     }
 
   RTX_FRAME_RELATED_P (insn) = 1;
@@ -12598,8 +14233,7 @@ arm_expand_prologue (void)
       dwarf = gen_rtx_SET (VOIDmode, r0, dwarf);
       insn = gen_movsi (r0, stack_pointer_rtx);
       RTX_FRAME_RELATED_P (insn) = 1;
-      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                           dwarf, REG_NOTES (insn));
+      add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
       emit_insn (insn);
       emit_insn (gen_andsi3 (r1, r0, GEN_INT (~(HOST_WIDE_INT)7)));
       emit_insn (gen_movsi (stack_pointer_rtx, r1));
@@ -12652,11 +14286,11 @@ arm_expand_prologue (void)
            insn = emit_set_insn (gen_rtx_REG (SImode, 3), ip_rtx);
          else if (args_to_push == 0)
            {
+             rtx dwarf;
+
              gcc_assert(arm_compute_static_chain_stack_bytes() == 4);
              saved_regs += 4;
 
-             rtx dwarf;
-
              insn = gen_rtx_PRE_DEC (SImode, stack_pointer_rtx);
              insn = emit_set_insn (gen_frame_mem (SImode, insn), ip_rtx);
              fp_offset = 4;
@@ -12666,8 +14300,7 @@ arm_expand_prologue (void)
                                   plus_constant (stack_pointer_rtx,
                                                  -fp_offset));
              RTX_FRAME_RELATED_P (insn) = 1;
-             REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                                   dwarf, REG_NOTES (insn));
+             add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
            }
          else
            {
@@ -13377,6 +15010,49 @@ arm_print_operand (FILE *stream, rtx x, int code)
       }
       return;
 
+    /* Memory operand for vld1/vst1 instruction.  */
+    case 'A':
+      {
+       rtx addr;
+       bool postinc = FALSE;
+       gcc_assert (GET_CODE (x) == MEM);
+       addr = XEXP (x, 0);
+       if (GET_CODE (addr) == POST_INC)
+         {
+           postinc = 1;
+           addr = XEXP (addr, 0);
+         }
+       asm_fprintf (stream, "[%r]", REGNO (addr));
+       if (postinc)
+         fputs("!", stream);
+      }
+      return;
+
+    /* Register specifier for vld1.16/vst1.16.  Translate the S register
+       number into a D register number and element index.  */
+    case 'z':
+      {
+        int mode = GET_MODE (x);
+        int regno;
+
+        if (GET_MODE_SIZE (mode) != 2 || GET_CODE (x) != REG)
+          {
+           output_operand_lossage ("invalid operand for code '%c'", code);
+           return;
+          }
+
+        regno = REGNO (x);
+        if (!VFP_REGNO_OK_FOR_SINGLE (regno))
+          {
+           output_operand_lossage ("invalid operand for code '%c'", code);
+           return;
+          }
+
+       regno = regno - FIRST_VFP_REGNUM;
+       fprintf (stream, "d%d[%d]", regno/2, ((regno % 2) ? 2 : 0));
+      }
+      return;
+      
     default:
       if (x == 0)
        {
@@ -13410,6 +15086,12 @@ arm_print_operand (FILE *stream, rtx x, int code)
        default:
          gcc_assert (GET_CODE (x) != NEG);
          fputc ('#', stream);
+         if (GET_CODE (x) == HIGH)
+           {
+             fputs (":lower16:", stream);
+             x = XEXP (x, 0);
+           }
+           
          output_addr_const (stream, x);
          break;
        }
@@ -13577,7 +15259,7 @@ static enum arm_cond_code
 get_arm_condition_code (rtx comparison)
 {
   enum machine_mode mode = GET_MODE (XEXP (comparison, 0));
-  int code;
+  enum arm_cond_code code;
   enum rtx_code comp_code = GET_CODE (comparison);
 
   if (GET_MODE_CLASS (mode) != MODE_CC)
@@ -13788,12 +15470,6 @@ arm_final_prescan_insn (rtx insn)
      reversed if it appears to fail.  */
   int reverse = 0;
 
-  /* JUMP_CLOBBERS will be one implies that the conditions if a branch is
-     taken are clobbered, even if the rtl suggests otherwise.  It also
-     means that we have to grub around within the jump expression to find
-     out what the conditions are when the jump isn't taken.  */
-  int jump_clobbers = 0;
-
   /* If we start with a return insn, we only succeed if we find another one.  */
   int seeking_return = 0;
 
@@ -13872,14 +15548,6 @@ arm_final_prescan_insn (rtx insn)
       int then_not_else = TRUE;
       rtx this_insn = start_insn, label = 0;
 
-      /* If the jump cannot be done with one instruction, we cannot
-        conditionally execute the instruction in the inverse case.  */
-      if (get_attr_conds (insn) == CONDS_JUMP_CLOB)
-       {
-         jump_clobbers = 1;
-         return;
-       }
-
       /* Register the insn jumped to.  */
       if (reverse)
         {
@@ -13922,13 +15590,7 @@ arm_final_prescan_insn (rtx insn)
                 control falls in from somewhere else.  */
              if (this_insn == label)
                {
-                 if (jump_clobbers)
-                   {
-                     arm_ccfsm_state = 2;
-                     this_insn = next_nonnote_insn (this_insn);
-                   }
-                 else
-                   arm_ccfsm_state = 1;
+                 arm_ccfsm_state = 1;
                  succeed = TRUE;
                }
              else
@@ -13943,13 +15605,7 @@ arm_final_prescan_insn (rtx insn)
              this_insn = next_nonnote_insn (this_insn);
              if (this_insn && this_insn == label)
                {
-                 if (jump_clobbers)
-                   {
-                     arm_ccfsm_state = 2;
-                     this_insn = next_nonnote_insn (this_insn);
-                   }
-                 else
-                   arm_ccfsm_state = 1;
+                 arm_ccfsm_state = 1;
                  succeed = TRUE;
                }
              else
@@ -13977,13 +15633,7 @@ arm_final_prescan_insn (rtx insn)
              if (this_insn && this_insn == label
                  && insns_skipped < max_insns_skipped)
                {
-                 if (jump_clobbers)
-                   {
-                     arm_ccfsm_state = 2;
-                     this_insn = next_nonnote_insn (this_insn);
-                   }
-                 else
-                   arm_ccfsm_state = 1;
+                 arm_ccfsm_state = 1;
                  succeed = TRUE;
                }
              else
@@ -14089,25 +15739,11 @@ arm_final_prescan_insn (rtx insn)
                }
              arm_target_insn = this_insn;
            }
-         if (jump_clobbers)
-           {
-             gcc_assert (!reverse);
-             arm_current_cc =
-                 get_arm_condition_code (XEXP (XEXP (XEXP (SET_SRC (body),
-                                                           0), 0), 1));
-             if (GET_CODE (XEXP (XEXP (SET_SRC (body), 0), 0)) == AND)
-               arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
-             if (GET_CODE (XEXP (SET_SRC (body), 0)) == NE)
-               arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
-           }
-         else
-           {
-             /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
-                what it was.  */
-             if (!reverse)
-               arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body),
-                                                              0));
-           }
+
+         /* If REVERSE is true, ARM_CURRENT_CC needs to be inverted from
+            what it was.  */
+         if (!reverse)
+           arm_current_cc = get_arm_condition_code (XEXP (SET_SRC (body), 0));
 
          if (reverse || then_not_else)
            arm_current_cc = ARM_INVERSE_CONDITION_CODE (arm_current_cc);
@@ -14174,6 +15810,12 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
       if (mode == DFmode)
        return VFP_REGNO_OK_FOR_DOUBLE (regno);
 
+      /* VFP registers can hold HFmode values, but there is no point in
+        putting them there unless we have the NEON extensions for
+        loading/storing them, too.  */
+      if (mode == HFmode)
+       return TARGET_NEON_FP16 && VFP_REGNO_OK_FOR_SINGLE (regno);
+
       if (TARGET_NEON)
         return (VALID_NEON_DREG_MODE (mode) && VFP_REGNO_OK_FOR_DOUBLE (regno))
                || (VALID_NEON_QREG_MODE (mode)
@@ -14196,13 +15838,13 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
        return VALID_IWMMXT_REG_MODE (mode);
     }
   
-  /* We allow any value to be stored in the general registers.
+  /* We allow almost any value to be stored in the general registers.
      Restrict doubleword quantities to even register pairs so that we can
-     use ldrd.  Do not allow Neon structure opaque modes in general registers;
-     they would use too many.  */
+     use ldrd.  Do not allow very large Neon structure opaque modes in
+     general registers; they would use too many.  */
   if (regno <= LAST_ARM_REGNUM)
     return !(TARGET_LDRD && GET_MODE_SIZE (mode) > 4 && (regno & 1) != 0)
-      && !VALID_NEON_STRUCT_MODE (mode);
+      && ARM_NUM_REGS (mode) <= 4;
 
   if (regno == FRAME_POINTER_REGNUM
       || regno == ARG_POINTER_REGNUM)
@@ -14219,7 +15861,8 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
 
 /* For efficiency and historical reasons LO_REGS, HI_REGS and CC_REGS are
    not used in arm mode.  */
-int
+
+enum reg_class
 arm_regno_class (int regno)
 {
   if (TARGET_THUMB1)
@@ -14373,7 +16016,7 @@ static const struct builtin_description bdesc_2arg[] =
 {
 #define IWMMXT_BUILTIN(code, string, builtin) \
   { FL_IWMMXT, CODE_FOR_##code, "__builtin_arm_" string, \
-    ARM_BUILTIN_##builtin, 0, 0 },
+    ARM_BUILTIN_##builtin, UNKNOWN, 0 },
 
   IWMMXT_BUILTIN (addv8qi3, "waddb", WADDB)
   IWMMXT_BUILTIN (addv4hi3, "waddh", WADDH)
@@ -14435,7 +16078,7 @@ static const struct builtin_description bdesc_2arg[] =
   IWMMXT_BUILTIN (iwmmxt_wmaddu, "wmaddu", WMADDU)
 
 #define IWMMXT_BUILTIN2(code, builtin) \
-  { FL_IWMMXT, CODE_FOR_##code, NULL, ARM_BUILTIN_##builtin, 0, 0 },
+  { FL_IWMMXT, CODE_FOR_##code, NULL, ARM_BUILTIN_##builtin, UNKNOWN, 0 },
 
   IWMMXT_BUILTIN2 (iwmmxt_wpackhss, WPACKHSS)
   IWMMXT_BUILTIN2 (iwmmxt_wpackwss, WPACKWSS)
@@ -14832,7 +16475,7 @@ arm_init_tls_builtins (void)
   TREE_READONLY (decl) = 1;
 }
 
-typedef enum {
+enum neon_builtin_type_bits {
   T_V8QI  = 0x0001,
   T_V4HI  = 0x0002,
   T_V2SI  = 0x0004,
@@ -14846,7 +16489,7 @@ typedef enum {
   T_TI   = 0x0400,
   T_EI   = 0x0800,
   T_OI   = 0x1000
-} neon_builtin_type_bits;
+};
 
 #define v8qi_UP  T_V8QI
 #define v4hi_UP  T_V4HI
@@ -14909,7 +16552,7 @@ typedef enum {
 typedef struct {
   const char *name;
   const neon_itype itype;
-  const neon_builtin_type_bits bits;
+  const int bits;
   const enum insn_code codes[T_MAX];
   const unsigned int num_vars;
   unsigned int base_fcode;
@@ -15659,6 +17302,15 @@ arm_init_neon_builtins (void)
 }
 
 static void
+arm_init_fp16_builtins (void)
+{
+  tree fp16_type = make_node (REAL_TYPE);
+  TYPE_PRECISION (fp16_type) = 16;
+  layout_type (fp16_type);
+  (*lang_hooks.types.register_builtin_type) (fp16_type, "__fp16");
+}
+
+static void
 arm_init_builtins (void)
 {
   arm_init_tls_builtins ();
@@ -15668,6 +17320,71 @@ arm_init_builtins (void)
 
   if (TARGET_NEON)
     arm_init_neon_builtins ();
+
+  if (arm_fp16_format)
+    arm_init_fp16_builtins ();
+}
+
+/* Implement TARGET_INVALID_PARAMETER_TYPE.  */
+
+static const char *
+arm_invalid_parameter_type (const_tree t)
+{
+  if (SCALAR_FLOAT_TYPE_P (t) && TYPE_PRECISION (t) == 16)
+    return N_("function parameters cannot have __fp16 type");
+  return NULL;
+}
+
+/* Implement TARGET_INVALID_PARAMETER_TYPE.  */
+
+static const char *
+arm_invalid_return_type (const_tree t)
+{
+  if (SCALAR_FLOAT_TYPE_P (t) && TYPE_PRECISION (t) == 16)
+    return N_("functions cannot return __fp16 type");
+  return NULL;
+}
+
+/* Implement TARGET_PROMOTED_TYPE.  */
+
+static tree
+arm_promoted_type (const_tree t)
+{
+  if (SCALAR_FLOAT_TYPE_P (t) && TYPE_PRECISION (t) == 16)
+    return float_type_node;
+  return NULL_TREE;
+}
+
+/* Implement TARGET_CONVERT_TO_TYPE.
+   Specifically, this hook implements the peculiarity of the ARM
+   half-precision floating-point C semantics that requires conversions between
+   __fp16 to or from double to do an intermediate conversion to float.  */
+
+static tree
+arm_convert_to_type (tree type, tree expr)
+{
+  tree fromtype = TREE_TYPE (expr);
+  if (!SCALAR_FLOAT_TYPE_P (fromtype) || !SCALAR_FLOAT_TYPE_P (type))
+    return NULL_TREE;
+  if ((TYPE_PRECISION (fromtype) == 16 && TYPE_PRECISION (type) > 32)
+      || (TYPE_PRECISION (type) == 16 && TYPE_PRECISION (fromtype) > 32))
+    return convert (type, convert (float_type_node, expr));
+  return NULL_TREE;
+}
+
+/* Implement TARGET_SCALAR_MODE_SUPPORTED_P.
+   This simply adds HFmode as a supported mode; even though we don't
+   implement arithmetic on this type directly, it's supported by
+   optabs conversions, much the way the double-word arithmetic is
+   special-cased in the default hook.  */
+
+static bool
+arm_scalar_mode_supported_p (enum machine_mode mode)
+{
+  if (mode == HFmode)
+    return (arm_fp16_format != ARM_FP16_FORMAT_NONE);
+  else
+    return default_scalar_mode_supported_p (mode);
 }
 
 /* Errors in the source file can cause expand_expr to return const0_rtx
@@ -15826,7 +17543,7 @@ arm_expand_neon_args (rtx target, int icode, int have_retval,
 
   for (;;)
     {
-      builtin_arg thisarg = va_arg (ap, int);
+      builtin_arg thisarg = (builtin_arg) va_arg (ap, int);
 
       if (thisarg == NEON_ARG_STOP)
         break;
@@ -16439,7 +18156,7 @@ thumb_pushpop (FILE *f, unsigned long mask, int push, int *cfa_offset,
 
   if (push && pushed_words && dwarf2out_do_frame ())
     {
-      char *l = dwarf2out_cfi_label ();
+      char *l = dwarf2out_cfi_label (false);
       int pushed_mask = real_regs;
 
       *cfa_offset += pushed_words * 4;
@@ -16747,6 +18464,7 @@ thumb_shiftable_const (unsigned HOST_WIDE_INT val)
   unsigned HOST_WIDE_INT mask = 0xff;
   int i;
 
+  val = val & (unsigned HOST_WIDE_INT)0xffffffffu;
   if (val == 0) /* XXX */
     return 0;
 
@@ -16856,7 +18574,7 @@ thumb_unexpanded_epilogue (void)
   int had_to_push_lr;
   int size;
 
-  if (return_used_this_function)
+  if (cfun->machine->return_used_this_function != 0)
     return "";
 
   if (IS_NAKED (arm_current_func_type ()))
@@ -17180,9 +18898,7 @@ thumb1_expand_prologue (void)
                               plus_constant (stack_pointer_rtx,
                                              -amount));
          RTX_FRAME_RELATED_P (dwarf) = 1;
-         REG_NOTES (insn)
-           = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf,
-                                REG_NOTES (insn));
+         add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
        }
     }
 
@@ -17337,7 +19053,7 @@ thumb1_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
         the stack pointer.  */
       if (dwarf2out_do_frame ())
        {
-         char *l = dwarf2out_cfi_label ();
+         char *l = dwarf2out_cfi_label (false);
 
          cfa_offset = cfa_offset + crtl->args.pretend_args_size;
          dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
@@ -17386,7 +19102,7 @@ thumb1_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
       if (dwarf2out_do_frame ())
        {
-         char *l = dwarf2out_cfi_label ();
+         char *l = dwarf2out_cfi_label (false);
 
          cfa_offset = cfa_offset + 16;
          dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
@@ -17865,6 +19581,10 @@ arm_file_start (void)
              fpu_name = "neon";
              set_float_abi_attributes = 1;
              break;
+           case FPUTYPE_NEON_FP16:
+             fpu_name = "neon-fp16";
+             set_float_abi_attributes = 1;
+             break;
            default:
              abort();
            }
@@ -17918,6 +19638,11 @@ arm_file_start (void)
        val = 6;
       asm_fprintf (asm_out_file, "\t.eabi_attribute 30, %d\n", val);
 
+      /* Tag_ABI_FP_16bit_format.  */
+      if (arm_fp16_format)
+       asm_fprintf (asm_out_file, "\t.eabi_attribute 38, %d\n",
+                    (int)arm_fp16_format);
+
       if (arm_lang_output_object_attributes_hook)
        arm_lang_output_object_attributes_hook();
     }
@@ -18147,6 +19872,23 @@ arm_emit_vector_const (FILE *file, rtx x)
   return 1;
 }
 
+/* Emit a fp16 constant appropriately padded to occupy a 4-byte word.
+   HFmode constant pool entries are actually loaded with ldr.  */
+void
+arm_emit_fp16_const (rtx c)
+{
+  REAL_VALUE_TYPE r;
+  long bits;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (r, c);
+  bits = real_to_target (NULL, &r, HFmode);
+  if (WORDS_BIG_ENDIAN)
+    assemble_zeros (2);
+  assemble_integer (GEN_INT (bits), 2, BITS_PER_WORD, 1);
+  if (!WORDS_BIG_ENDIAN)
+    assemble_zeros (2);
+}
+
 const char *
 arm_output_load_gr (rtx *operands)
 {
@@ -18184,19 +19926,24 @@ arm_output_load_gr (rtx *operands)
    that way.  */
 
 static void
-arm_setup_incoming_varargs (CUMULATIVE_ARGS *cum,
+arm_setup_incoming_varargs (CUMULATIVE_ARGS *pcum,
                            enum machine_mode mode,
                            tree type,
                            int *pretend_size,
                            int second_time ATTRIBUTE_UNUSED)
 {
-  int nregs = cum->nregs;
-  if (nregs & 1
-      && ARM_DOUBLEWORD_ALIGN
-      && arm_needs_doubleword_align (mode, type))
-    nregs++;
-
+  int nregs;
+  
   cfun->machine->uses_anonymous_args = 1;
+  if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
+    {
+      nregs = pcum->aapcs_ncrn;
+      if ((nregs & 1) && arm_needs_doubleword_align (mode, type))
+       nregs++;
+    }
+  else
+    nregs = pcum->nregs;
+  
   if (nregs < NUM_ARG_REGS)
     *pretend_size = (NUM_ARG_REGS - nregs) * UNITS_PER_WORD;
 }
@@ -18330,6 +20077,19 @@ arm_promote_prototypes (const_tree t ATTRIBUTE_UNUSED)
     return !TARGET_AAPCS_BASED;
 }
 
+static enum machine_mode
+arm_promote_function_mode (const_tree type ATTRIBUTE_UNUSED,
+                           enum machine_mode mode,
+                           int *punsignedp ATTRIBUTE_UNUSED,
+                           const_tree fntype ATTRIBUTE_UNUSED,
+                           int for_return ATTRIBUTE_UNUSED)
+{
+  if (GET_MODE_CLASS (mode) == MODE_INT
+      && GET_MODE_SIZE (mode) < 4)
+    return SImode;
+
+  return mode;
+}
 
 /* AAPCS based ABIs use short enums by default.  */
 
@@ -18580,9 +20340,10 @@ arm_vector_mode_supported_p (enum machine_mode mode)
       || mode == V16QImode || mode == V4SFmode || mode == V2DImode))
     return true;
 
-  if ((mode == V2SImode)
-      || (mode == V4HImode)
-      || (mode == V8QImode))
+  if ((TARGET_NEON || TARGET_IWMMXT)
+      && ((mode == V2SImode)
+         || (mode == V4HImode)
+         || (mode == V8QImode)))
     return true;
 
   return false;
@@ -18949,7 +20710,7 @@ arm_emit_tls_decoration (FILE *fp, rtx x)
   rtx val;
 
   val = XVECEXP (x, 0, 0);
-  reloc = INTVAL (XVECEXP (x, 0, 1));
+  reloc = (enum tls_reloc) INTVAL (XVECEXP (x, 0, 1));
 
   output_addr_const (fp, val);
 
@@ -19067,6 +20828,32 @@ arm_output_shift(rtx * operands, int set_flags)
   return "";
 }
 
+/* Output a Thumb-1 casesi dispatch sequence.  */
+const char *
+thumb1_output_casesi (rtx *operands)
+{
+  rtx diff_vec = PATTERN (next_real_insn (operands[0]));
+  addr_diff_vec_flags flags;
+
+  gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+
+  flags = ADDR_DIFF_VEC_FLAGS (diff_vec);
+
+  switch (GET_MODE(diff_vec))
+    {
+    case QImode:
+      return (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned ? 
+             "bl\t%___gnu_thumb1_case_uqi" : "bl\t%___gnu_thumb1_case_sqi");
+    case HImode:
+      return (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned ? 
+             "bl\t%___gnu_thumb1_case_uhi" : "bl\t%___gnu_thumb1_case_shi");
+    case SImode:
+      return "bl\t%___gnu_thumb1_case_si";
+    default:
+      gcc_unreachable ();
+    }
+}
+
 /* Output a Thumb-2 casesi instruction.  */
 const char *
 thumb2_output_casesi (rtx *operands)
@@ -19161,6 +20948,25 @@ arm_mangle_type (const_tree type)
 {
   arm_mangle_map_entry *pos = arm_mangle_map;
 
+  /* The ARM ABI documents (10th October 2008) say that "__va_list"
+     has to be managled as if it is in the "std" namespace.  */
+  if (TARGET_AAPCS_BASED 
+      && lang_hooks.types_compatible_p (CONST_CAST_TREE (type), va_list_type))
+    {
+      static bool warned;
+      if (!warned && warn_psabi)
+       {
+         warned = true;
+         inform (input_location,
+                 "the mangling of %<va_list%> has changed in GCC 4.4");
+       }
+      return "St9__va_list";
+    }
+
+  /* Half-precision float.  */
+  if (TREE_CODE (type) == REAL_TYPE && TYPE_PRECISION (type) == 16)
+    return "Dh";
+
   if (TREE_CODE (type) != VECTOR_TYPE)
     return NULL;
 
@@ -19219,4 +21025,14 @@ arm_optimization_options (int level, int size ATTRIBUTE_UNUSED)
     flag_section_anchors = 2;
 }
 
+/* Implement TARGET_FRAME_POINTER_REQUIRED.  */
+
+bool
+arm_frame_pointer_required (void)
+{
+  return (cfun->has_nonlocal_label
+          || SUBTARGET_FRAME_POINTER_REQUIRED
+          || (TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ()));
+}
+
 #include "gt-arm.h"