OSDN Git Service

* config/arm/arm.c (arm_print_operand): Fix invalid alignment
[pf3gnuchains/gcc-fork.git] / gcc / config / arm / arm.c
index 4fee4da..ba081d1 100644 (file)
@@ -1,6 +1,6 @@
 /* Output routines for GCC for ARM.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
    Free Software Foundation, Inc.
    Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
    and Martin Simmons (@harleqn.co.uk).
@@ -64,6 +64,11 @@ typedef struct minipool_fixup   Mfix;
 
 void (*arm_lang_output_object_attributes_hook)(void);
 
+struct four_ints
+{
+  int i[4];
+};
+
 /* Forward function declarations.  */
 static bool arm_needs_doubleword_align (enum machine_mode, const_tree);
 static int arm_compute_static_chain_stack_bytes (void);
@@ -82,7 +87,6 @@ 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);
 static rtx emit_sfm (int, int);
 static unsigned arm_size_return_regs (void);
 static bool arm_assemble_integer (rtx, unsigned int, int);
@@ -126,11 +130,16 @@ static tree arm_handle_notshared_attribute (tree *, tree, tree, int, bool *);
 #endif
 static void arm_output_function_epilogue (FILE *, HOST_WIDE_INT);
 static void arm_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void thumb1_output_function_prologue (FILE *, HOST_WIDE_INT);
 static int arm_comp_type_attributes (const_tree, const_tree);
 static void arm_set_default_type_attributes (tree);
 static int arm_adjust_cost (rtx, rtx, rtx, int);
-static int count_insns_for_constant (HOST_WIDE_INT, int);
+static int optimal_immediate_sequence (enum rtx_code code,
+                                      unsigned HOST_WIDE_INT val,
+                                      struct four_ints *return_sequence);
+static int optimal_immediate_sequence_1 (enum rtx_code code,
+                                        unsigned HOST_WIDE_INT val,
+                                        struct four_ints *return_sequence,
+                                        int i);
 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,
@@ -138,20 +147,25 @@ static enum machine_mode arm_promote_function_mode (const_tree,
                                                    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_1 (enum machine_mode);
 static rtx arm_libcall_value (enum machine_mode, const_rtx);
-
+static bool arm_function_value_regno_p (const unsigned int);
 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 bool arm_have_conditional_execution (void);
+static bool arm_cannot_force_const_mem (enum machine_mode, rtx);
+static bool arm_legitimate_constant_p (enum machine_mode, rtx);
 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 bool arm_rtx_costs (rtx, int, int, int, int *, bool);
 static int arm_address_cost (rtx, bool);
+static int arm_register_move_cost (enum machine_mode, reg_class_t, reg_class_t);
+static int arm_memory_move_cost (enum machine_mode, reg_class_t, bool);
 static bool arm_memory_load_p (rtx);
 static bool arm_cirrus_insn_p (rtx);
 static void cirrus_reorg (rtx);
@@ -161,17 +175,19 @@ static rtx safe_vector_operand (rtx, enum machine_mode);
 static rtx arm_expand_binop_builtin (enum insn_code, tree, rtx);
 static rtx arm_expand_unop_builtin (enum insn_code, tree, rtx, int);
 static rtx arm_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
+static tree arm_builtin_decl (unsigned, bool);
 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,
+static int arm_arg_partial_bytes (cumulative_args_t, enum machine_mode,
                                  tree, bool);
-static rtx arm_function_arg (CUMULATIVE_ARGS *, enum machine_mode,
+static rtx arm_function_arg (cumulative_args_t, enum machine_mode,
                             const_tree, bool);
-static void arm_function_arg_advance (CUMULATIVE_ARGS *, enum machine_mode,
+static void arm_function_arg_advance (cumulative_args_t, enum machine_mode,
                                      const_tree, bool);
 static unsigned int arm_function_arg_boundary (enum machine_mode, const_tree);
 static rtx aapcs_allocate_return_reg (enum machine_mode, const_tree,
                                      const_tree);
+static rtx aapcs_libcall_value (enum machine_mode);
 static int aapcs_select_return_coproc (const_tree, const_tree);
 
 #ifdef OBJECT_FORMAT_ELF
@@ -185,9 +201,9 @@ static void arm_encode_section_info (tree, rtx, int);
 static void arm_file_end (void);
 static void arm_file_start (void);
 
-static void arm_setup_incoming_varargs (CUMULATIVE_ARGS *, enum machine_mode,
+static void arm_setup_incoming_varargs (cumulative_args_t, enum machine_mode,
                                        tree, int *, int);
-static bool arm_pass_by_reference (CUMULATIVE_ARGS *,
+static bool arm_pass_by_reference (cumulative_args_t,
                                   enum machine_mode, const_tree, bool);
 static bool arm_promote_prototypes (const_tree);
 static bool arm_default_short_enums (void);
@@ -201,8 +217,6 @@ static bool arm_output_ttype (rtx);
 static void arm_asm_emit_except_personality (rtx);
 static void arm_asm_init_sections (void);
 #endif
-static enum unwind_info_type arm_except_unwind_info (struct gcc_options *);
-static void arm_dwarf_handle_frame_unspec (const char *, rtx, int);
 static rtx arm_dwarf_register_span (rtx);
 
 static tree arm_cxx_guard_type (void);
@@ -219,9 +233,6 @@ 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 void arm_option_override (void);
-static bool arm_handle_option (struct gcc_options *, struct gcc_options *,
-                              const struct cl_decoded_option *, location_t);
-static void arm_target_help (void);
 static unsigned HOST_WIDE_INT arm_shift_truncation_mask (enum machine_mode);
 static bool arm_cannot_copy_insn_p (rtx);
 static bool arm_tls_symbol_p (rtx x);
@@ -243,6 +254,8 @@ static rtx arm_pic_static_addr (rtx orig, rtx reg);
 static bool cortex_a9_sched_adjust_cost (rtx, rtx, rtx, int *);
 static bool xscale_sched_adjust_cost (rtx, rtx, rtx, int *);
 static bool fa726te_sched_adjust_cost (rtx, rtx, rtx, int *);
+static bool arm_array_mode_supported_p (enum machine_mode,
+                                       unsigned HOST_WIDE_INT);
 static enum machine_mode arm_preferred_simd_mode (enum machine_mode);
 static bool arm_class_likely_spilled_p (reg_class_t);
 static bool arm_vector_alignment_reachable (const_tree type, bool is_packed);
@@ -253,6 +266,11 @@ static bool arm_builtin_support_vector_misalignment (enum machine_mode mode,
 static void arm_conditional_register_usage (void);
 static reg_class_t arm_preferred_rename_class (reg_class_t rclass);
 static unsigned int arm_autovectorize_vector_sizes (void);
+static int arm_default_branch_cost (bool, bool);
+static int arm_cortex_a5_branch_cost (bool, bool);
+
+static bool arm_vectorize_vec_perm_const_ok (enum machine_mode vmode,
+                                            const unsigned char *sel);
 
 \f
 /* Table of machine attributes.  */
@@ -299,15 +317,6 @@ static const struct attribute_spec arm_attribute_table[] =
 #endif
   { NULL,           0, 0, false, false, false, NULL, false }
 };
-
-/* Set default optimization options.  */
-static const struct default_options arm_option_optimization_table[] =
-  {
-    /* Enable section anchors by default at -O1 or higher.  */
-    { OPT_LEVELS_1_PLUS, OPT_fsection_anchors, NULL, 1 },
-    { OPT_LEVELS_1_PLUS, OPT_fomit_frame_pointer, NULL, 1 },
-    { OPT_LEVELS_NONE, 0, NULL, 0 }
-  };
 \f
 /* Initialize the GCC target structure.  */
 #if TARGET_DLLIMPORT_DECL_ATTRIBUTES
@@ -347,16 +356,8 @@ static const struct default_options arm_option_optimization_table[] =
 #undef  TARGET_ASM_FUNCTION_EPILOGUE
 #define TARGET_ASM_FUNCTION_EPILOGUE arm_output_function_epilogue
 
-#undef  TARGET_DEFAULT_TARGET_FLAGS
-#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_SCHED_PROLOG)
-#undef  TARGET_HANDLE_OPTION
-#define TARGET_HANDLE_OPTION arm_handle_option
-#undef  TARGET_HELP
-#define TARGET_HELP arm_target_help
 #undef  TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE arm_option_override
-#undef  TARGET_OPTION_OPTIMIZATION_TABLE
-#define TARGET_OPTION_OPTIMIZATION_TABLE arm_option_optimization_table
 
 #undef  TARGET_COMP_TYPE_ATTRIBUTES
 #define TARGET_COMP_TYPE_ATTRIBUTES arm_comp_type_attributes
@@ -367,6 +368,12 @@ static const struct default_options arm_option_optimization_table[] =
 #undef  TARGET_SCHED_ADJUST_COST
 #define TARGET_SCHED_ADJUST_COST arm_adjust_cost
 
+#undef TARGET_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST arm_register_move_cost
+
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST arm_memory_move_cost
+
 #undef TARGET_ENCODE_SECTION_INFO
 #ifdef ARM_PE
 #define TARGET_ENCODE_SECTION_INFO  arm_pe_encode_section_info
@@ -389,6 +396,9 @@ static const struct default_options arm_option_optimization_table[] =
 #undef  TARGET_LIBCALL_VALUE
 #define TARGET_LIBCALL_VALUE arm_libcall_value
 
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P arm_function_value_regno_p
+
 #undef  TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK arm_output_mi_thunk
 #undef  TARGET_ASM_CAN_OUTPUT_MI_THUNK
@@ -403,6 +413,8 @@ static const struct default_options arm_option_optimization_table[] =
 #define TARGET_SHIFT_TRUNCATION_MASK arm_shift_truncation_mask
 #undef TARGET_VECTOR_MODE_SUPPORTED_P
 #define TARGET_VECTOR_MODE_SUPPORTED_P arm_vector_mode_supported_p
+#undef TARGET_ARRAY_MODE_SUPPORTED_P
+#define TARGET_ARRAY_MODE_SUPPORTED_P arm_array_mode_supported_p
 #undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
 #define TARGET_VECTORIZE_PREFERRED_SIMD_MODE arm_preferred_simd_mode
 #undef TARGET_VECTORIZE_AUTOVECTORIZE_VECTOR_SIZES
@@ -416,6 +428,8 @@ static const struct default_options arm_option_optimization_table[] =
 #define TARGET_INIT_BUILTINS  arm_init_builtins
 #undef  TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN arm_expand_builtin
+#undef  TARGET_BUILTIN_DECL
+#define TARGET_BUILTIN_DECL arm_builtin_decl
 
 #undef TARGET_INIT_LIBFUNCS
 #define TARGET_INIT_LIBFUNCS arm_init_libfuncs
@@ -512,12 +526,6 @@ static const struct default_options arm_option_optimization_table[] =
 #define TARGET_ASM_INIT_SECTIONS arm_asm_init_sections
 #endif /* ARM_UNWIND_INFO */
 
-#undef TARGET_EXCEPT_UNWIND_INFO
-#define TARGET_EXCEPT_UNWIND_INFO  arm_except_unwind_info
-
-#undef TARGET_DWARF_HANDLE_FRAME_UNSPEC
-#define TARGET_DWARF_HANDLE_FRAME_UNSPEC arm_dwarf_handle_frame_unspec
-
 #undef TARGET_DWARF_REGISTER_SPAN
 #define TARGET_DWARF_REGISTER_SPAN arm_dwarf_register_span
 
@@ -532,6 +540,9 @@ static const struct default_options arm_option_optimization_table[] =
 #undef TARGET_HAVE_CONDITIONAL_EXECUTION
 #define TARGET_HAVE_CONDITIONAL_EXECUTION arm_have_conditional_execution
 
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P arm_legitimate_constant_p
+
 #undef TARGET_CANNOT_FORCE_CONST_MEM
 #define TARGET_CANNOT_FORCE_CONST_MEM arm_cannot_force_const_mem
 
@@ -604,6 +615,10 @@ static const struct default_options arm_option_optimization_table[] =
 #define TARGET_PREFERRED_RENAME_CLASS \
   arm_preferred_rename_class
 
+#undef TARGET_VECTORIZE_VEC_PERM_CONST_OK
+#define TARGET_VECTORIZE_VEC_PERM_CONST_OK \
+  arm_vectorize_vec_perm_const_ok
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Obstack for minipool constant handling.  */
@@ -631,21 +646,6 @@ int arm_fpu_attr;
 /* Which floating popint hardware to use.  */
 const struct arm_fpu_desc *arm_fpu_desc;
 
-/* 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;
-
-/* Which thread pointer model to use.  */
-enum arm_tp_type target_thread_pointer = TP_AUTO;
-
-/* Used to parse -mstructure_size_boundary command line option.  */
-int    arm_structure_size_boundary = DEFAULT_STRUCTURE_SIZE_BOUNDARY;
-
 /* Used for Thumb call_via trampolines.  */
 rtx thumb_call_via_label[14];
 static int thumb_call_reg_needed;
@@ -672,12 +672,13 @@ static int thumb_call_reg_needed;
 #define FL_THUMB2     (1 << 16)              /* Thumb-2.  */
 #define FL_NOTM              (1 << 17)       /* Instructions not present in the 'M'
                                         profile.  */
-#define FL_DIV       (1 << 18)       /* Hardware divide.  */
+#define FL_THUMB_DIV  (1 << 18)              /* Hardware divide (Thumb mode).  */
 #define FL_VFPV3      (1 << 19)       /* Vector Floating Point V3.  */
 #define FL_NEON       (1 << 20)       /* Neon instructions.  */
 #define FL_ARCH7EM    (1 << 21)              /* Instructions present in the ARMv7E-M
                                         architecture.  */
 #define FL_ARCH7      (1 << 22)       /* Architecture 7.  */
+#define FL_ARM_DIV    (1 << 23)              /* Hardware divide (ARM mode).  */
 
 #define FL_IWMMXT     (1 << 29)              /* XScale v2 or "Intel Wireless MMX technology".  */
 
@@ -704,8 +705,8 @@ static int thumb_call_reg_needed;
 #define FL_FOR_ARCH6M  (FL_FOR_ARCH6 & ~FL_NOTM)
 #define FL_FOR_ARCH7   ((FL_FOR_ARCH6T2 & ~FL_NOTM) | FL_ARCH7)
 #define FL_FOR_ARCH7A  (FL_FOR_ARCH7 | FL_NOTM | FL_ARCH6K)
-#define FL_FOR_ARCH7R  (FL_FOR_ARCH7A | FL_DIV)
-#define FL_FOR_ARCH7M  (FL_FOR_ARCH7 | FL_DIV)
+#define FL_FOR_ARCH7R  (FL_FOR_ARCH7A | FL_THUMB_DIV)
+#define FL_FOR_ARCH7M  (FL_FOR_ARCH7 | FL_THUMB_DIV)
 #define FL_FOR_ARCH7EM  (FL_FOR_ARCH7M | FL_ARCH7EM)
 
 /* The bits in this mask specify which
@@ -791,7 +792,8 @@ int arm_cpp_interwork = 0;
 int arm_arch_thumb2;
 
 /* Nonzero if chip supports integer division instruction.  */
-int arm_arch_hwdiv;
+int arm_arch_arm_hwdiv;
+int arm_arch_thumb_hwdiv;
 
 /* In case of a PRE_INC, POST_INC, PRE_DEC, POST_DEC memory reference,
    we must report the mode of the memory reference from
@@ -864,48 +866,117 @@ const struct tune_params arm_slowmul_tune =
 {
   arm_slowmul_rtx_costs,
   NULL,
-  3,
-  ARM_PREFETCH_NOT_BENEFICIAL
+  3,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  true,                                                /* Prefer constant pool.  */
+  arm_default_branch_cost
 };
 
 const struct tune_params arm_fastmul_tune =
 {
   arm_fastmul_rtx_costs,
   NULL,
-  1,
-  ARM_PREFETCH_NOT_BENEFICIAL
+  1,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  true,                                                /* Prefer constant pool.  */
+  arm_default_branch_cost
+};
+
+/* StrongARM has early execution of branches, so a sequence that is worth
+   skipping is shorter.  Set max_insns_skipped to a lower value.  */
+
+const struct tune_params arm_strongarm_tune =
+{
+  arm_fastmul_rtx_costs,
+  NULL,
+  1,                                           /* Constant limit.  */
+  3,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  true,                                                /* Prefer constant pool.  */
+  arm_default_branch_cost
 };
 
 const struct tune_params arm_xscale_tune =
 {
   arm_xscale_rtx_costs,
   xscale_sched_adjust_cost,
-  2,
-  ARM_PREFETCH_NOT_BENEFICIAL
+  2,                                           /* Constant limit.  */
+  3,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  true,                                                /* Prefer constant pool.  */
+  arm_default_branch_cost
 };
 
 const struct tune_params arm_9e_tune =
 {
   arm_9e_rtx_costs,
   NULL,
-  1,
-  ARM_PREFETCH_NOT_BENEFICIAL
+  1,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  true,                                                /* Prefer constant pool.  */
+  arm_default_branch_cost
+};
+
+const struct tune_params arm_v6t2_tune =
+{
+  arm_9e_rtx_costs,
+  NULL,
+  1,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  false,                                       /* Prefer constant pool.  */
+  arm_default_branch_cost
+};
+
+/* Generic Cortex tuning.  Use more specific tunings if appropriate.  */
+const struct tune_params arm_cortex_tune =
+{
+  arm_9e_rtx_costs,
+  NULL,
+  1,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  false,                                       /* Prefer constant pool.  */
+  arm_default_branch_cost
+};
+
+/* Branches can be dual-issued on Cortex-A5, so conditional execution is
+   less appealing.  Set max_insns_skipped to a low value.  */
+
+const struct tune_params arm_cortex_a5_tune =
+{
+  arm_9e_rtx_costs,
+  NULL,
+  1,                                           /* Constant limit.  */
+  1,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  false,                                       /* Prefer constant pool.  */
+  arm_cortex_a5_branch_cost
 };
 
 const struct tune_params arm_cortex_a9_tune =
 {
   arm_9e_rtx_costs,
   cortex_a9_sched_adjust_cost,
-  1,
-  ARM_PREFETCH_BENEFICIAL(4,32,32)
+  1,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_BENEFICIAL(4,32,32),
+  false,                                       /* Prefer constant pool.  */
+  arm_default_branch_cost
 };
 
 const struct tune_params arm_fa726te_tune =
 {
   arm_9e_rtx_costs,
   fa726te_sched_adjust_cost,
-  1,
-  ARM_PREFETCH_NOT_BENEFICIAL
+  1,                                           /* Constant limit.  */
+  5,                                           /* Max cond insns.  */
+  ARM_PREFETCH_NOT_BENEFICIAL,
+  true,                                                /* Prefer constant pool.  */
+  arm_default_branch_cost
 };
 
 
@@ -927,33 +998,10 @@ static const struct processors all_architectures[] =
   /* We don't specify tuning costs here as it will be figured out
      from the core.  */
 
-  {"armv2",   arm2,       "2",   FL_CO_PROC | FL_MODE26 | FL_FOR_ARCH2, NULL},
-  {"armv2a",  arm2,       "2",   FL_CO_PROC | FL_MODE26 | FL_FOR_ARCH2, NULL},
-  {"armv3",   arm6,       "3",   FL_CO_PROC | FL_MODE26 | FL_FOR_ARCH3, NULL},
-  {"armv3m",  arm7m,      "3M",  FL_CO_PROC | FL_MODE26 | FL_FOR_ARCH3M, NULL},
-  {"armv4",   arm7tdmi,   "4",   FL_CO_PROC | FL_MODE26 | FL_FOR_ARCH4, NULL},
-  /* Strictly, FL_MODE26 is a permitted option for v4t, but there are no
-     implementations that support it, so we will leave it out for now.  */
-  {"armv4t",  arm7tdmi,   "4T",  FL_CO_PROC |             FL_FOR_ARCH4T, NULL},
-  {"armv5",   arm10tdmi,  "5",   FL_CO_PROC |             FL_FOR_ARCH5, NULL},
-  {"armv5t",  arm10tdmi,  "5T",  FL_CO_PROC |             FL_FOR_ARCH5T, NULL},
-  {"armv5e",  arm1026ejs, "5E",  FL_CO_PROC |             FL_FOR_ARCH5E, NULL},
-  {"armv5te", arm1026ejs, "5TE", FL_CO_PROC |             FL_FOR_ARCH5TE, NULL},
-  {"armv6",   arm1136js,  "6",   FL_CO_PROC |             FL_FOR_ARCH6, NULL},
-  {"armv6j",  arm1136js,  "6J",  FL_CO_PROC |             FL_FOR_ARCH6J, NULL},
-  {"armv6k",  mpcore,    "6K",  FL_CO_PROC |             FL_FOR_ARCH6K, NULL},
-  {"armv6z",  arm1176jzs, "6Z",  FL_CO_PROC |             FL_FOR_ARCH6Z, NULL},
-  {"armv6zk", arm1176jzs, "6ZK", FL_CO_PROC |             FL_FOR_ARCH6ZK, NULL},
-  {"armv6t2", arm1156t2s, "6T2", FL_CO_PROC |             FL_FOR_ARCH6T2, NULL},
-  {"armv6-m", cortexm1,          "6M",                           FL_FOR_ARCH6M, NULL},
-  {"armv7",   cortexa8,          "7",   FL_CO_PROC |             FL_FOR_ARCH7, NULL},
-  {"armv7-a", cortexa8,          "7A",  FL_CO_PROC |             FL_FOR_ARCH7A, NULL},
-  {"armv7-r", cortexr4,          "7R",  FL_CO_PROC |             FL_FOR_ARCH7R, NULL},
-  {"armv7-m", cortexm3,          "7M",  FL_CO_PROC |             FL_FOR_ARCH7M, NULL},
-  {"armv7e-m", cortexm4,  "7EM", FL_CO_PROC |            FL_FOR_ARCH7EM, 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},
+#define ARM_ARCH(NAME, CORE, ARCH, FLAGS) \
+  {NAME, CORE, #ARCH, FLAGS, NULL},
+#include "arm-arches.def"
+#undef ARM_ARCH
   {NULL, arm_none, NULL, 0 , NULL}
 };
 
@@ -972,80 +1020,13 @@ char arm_arch_name[] = "__ARM_ARCH_0UNK__";
 
 static const struct arm_fpu_desc all_fpus[] =
 {
-  {"fpa",              ARM_FP_MODEL_FPA, 0, VFP_NONE, false, false},
-  {"fpe2",             ARM_FP_MODEL_FPA, 2, VFP_NONE, false, false},
-  {"fpe3",             ARM_FP_MODEL_FPA, 3, VFP_NONE, false, false},
-  {"maverick",         ARM_FP_MODEL_MAVERICK, 0, VFP_NONE, false, false},
-  {"vfp",              ARM_FP_MODEL_VFP, 2, VFP_REG_D16, false, false},
-  {"vfpv3",            ARM_FP_MODEL_VFP, 3, VFP_REG_D32, false, false},
-  {"vfpv3-fp16",       ARM_FP_MODEL_VFP, 3, VFP_REG_D32, false, true},
-  {"vfpv3-d16",                ARM_FP_MODEL_VFP, 3, VFP_REG_D16, false, false},
-  {"vfpv3-d16-fp16",   ARM_FP_MODEL_VFP, 3, VFP_REG_D16, false, true},
-  {"vfpv3xd",          ARM_FP_MODEL_VFP, 3, VFP_REG_SINGLE, false, false},
-  {"vfpv3xd-fp16",     ARM_FP_MODEL_VFP, 3, VFP_REG_SINGLE, false, true},
-  {"neon",             ARM_FP_MODEL_VFP, 3, VFP_REG_D32, true , false},
-  {"neon-fp16",                ARM_FP_MODEL_VFP, 3, VFP_REG_D32, true , true },
-  {"vfpv4",            ARM_FP_MODEL_VFP, 4, VFP_REG_D32, false, true},
-  {"vfpv4-d16",                ARM_FP_MODEL_VFP, 4, VFP_REG_D16, false, true},
-  {"fpv4-sp-d16",      ARM_FP_MODEL_VFP, 4, VFP_REG_SINGLE, false, true},
-  {"neon-vfpv4",       ARM_FP_MODEL_VFP, 4, VFP_REG_D32, true, true},
-  /* Compatibility aliases.  */
-  {"vfp3",             ARM_FP_MODEL_VFP, 3, VFP_REG_D32, false, false},
-};
-
-
-struct float_abi
-{
-  const char * name;
-  enum float_abi_type abi_type;
-};
-
-
-/* Available values for -mfloat-abi=.  */
-
-static const struct float_abi all_float_abis[] =
-{
-  {"soft",     ARM_FLOAT_ABI_SOFT},
-  {"softfp",   ARM_FLOAT_ABI_SOFTFP},
-  {"hard",     ARM_FLOAT_ABI_HARD}
-};
-
-
-struct 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;
-  enum arm_abi_type abi_type;
+#define ARM_FPU(NAME, MODEL, REV, VFP_REGS, NEON, FP16) \
+  { NAME, MODEL, REV, VFP_REGS, NEON, FP16 },
+#include "arm-fpus.def"
+#undef ARM_FPU
 };
 
 
-/* Available values for -mabi=.  */
-
-static const struct abi_name arm_all_abis[] =
-{
-  {"apcs-gnu",    ARM_ABI_APCS},
-  {"atpcs",   ARM_ABI_ATPCS},
-  {"aapcs",   ARM_ABI_AAPCS},
-  {"iwmmxt",  ARM_ABI_IWMMXT},
-  {"aapcs-linux",   ARM_ABI_AAPCS_LINUX}
-};
-
 /* Supported TLS relocations.  */
 
 enum tls_reloc {
@@ -1053,7 +1034,8 @@ enum tls_reloc {
   TLS_LDM32,
   TLS_LDO32,
   TLS_IE32,
-  TLS_LE32
+  TLS_LE32,
+  TLS_DESCSEQ  /* GNU scheme */
 };
 
 /* The maximum number of insns to be used when loading a constant.  */
@@ -1086,11 +1068,58 @@ bit_count (unsigned long value)
   return count;
 }
 
+typedef struct
+{
+  enum machine_mode mode;
+  const char *name;
+} arm_fixed_mode_set;
+
+/* A small helper for setting fixed-point library libfuncs.  */
+
+static void
+arm_set_fixed_optab_libfunc (optab optable, enum machine_mode mode,
+                            const char *funcname, const char *modename,
+                            int num_suffix)
+{
+  char buffer[50];
+
+  if (num_suffix == 0)
+    sprintf (buffer, "__gnu_%s%s", funcname, modename);
+  else
+    sprintf (buffer, "__gnu_%s%s%d", funcname, modename, num_suffix);
+
+  set_optab_libfunc (optable, mode, buffer);
+}
+
+static void
+arm_set_fixed_conv_libfunc (convert_optab optable, enum machine_mode to,
+                           enum machine_mode from, const char *funcname,
+                           const char *toname, const char *fromname)
+{
+  char buffer[50];
+  const char *maybe_suffix_2 = "";
+
+  /* Follow the logic for selecting a "2" suffix in fixed-bit.h.  */
+  if (ALL_FIXED_POINT_MODE_P (from) && ALL_FIXED_POINT_MODE_P (to)
+      && UNSIGNED_FIXED_POINT_MODE_P (from) == UNSIGNED_FIXED_POINT_MODE_P (to)
+      && ALL_FRACT_MODE_P (from) == ALL_FRACT_MODE_P (to))
+    maybe_suffix_2 = "2";
+
+  sprintf (buffer, "__gnu_%s%s%s%s", funcname, fromname, toname,
+          maybe_suffix_2);
+
+  set_conv_libfunc (optable, to, from, buffer);
+}
+
 /* Set up library functions unique to ARM.  */
 
 static void
 arm_init_libfuncs (void)
 {
+  /* For Linux, we have access to kernel support for atomic operations.  */
+  if (arm_abi == ARM_ABI_AAPCS_LINUX)
+    init_sync_libfuncs (2 * UNITS_PER_WORD);
+
   /* There are no special library functions unless we are using the
      ARM BPABI.  */
   if (!TARGET_BPABI)
@@ -1205,11 +1234,11 @@ arm_init_libfuncs (void)
                        (arm_fp16_format == ARM_FP16_FORMAT_IEEE
                         ? "__gnu_f2h_ieee"
                         : "__gnu_f2h_alternative"));
-      set_conv_libfunc (sext_optab, SFmode, HFmode, 
+      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);
@@ -1231,6 +1260,137 @@ arm_init_libfuncs (void)
       break;
     }
 
+  /* Use names prefixed with __gnu_ for fixed-point helper functions.  */
+  {
+    const arm_fixed_mode_set fixed_arith_modes[] =
+      {
+       { QQmode, "qq" },
+       { UQQmode, "uqq" },
+       { HQmode, "hq" },
+       { UHQmode, "uhq" },
+       { SQmode, "sq" },
+       { USQmode, "usq" },
+       { DQmode, "dq" },
+       { UDQmode, "udq" },
+       { TQmode, "tq" },
+       { UTQmode, "utq" },
+       { HAmode, "ha" },
+       { UHAmode, "uha" },
+       { SAmode, "sa" },
+       { USAmode, "usa" },
+       { DAmode, "da" },
+       { UDAmode, "uda" },
+       { TAmode, "ta" },
+       { UTAmode, "uta" }
+      };
+    const arm_fixed_mode_set fixed_conv_modes[] =
+      {
+       { QQmode, "qq" },
+       { UQQmode, "uqq" },
+       { HQmode, "hq" },
+       { UHQmode, "uhq" },
+       { SQmode, "sq" },
+       { USQmode, "usq" },
+       { DQmode, "dq" },
+       { UDQmode, "udq" },
+       { TQmode, "tq" },
+       { UTQmode, "utq" },
+       { HAmode, "ha" },
+       { UHAmode, "uha" },
+       { SAmode, "sa" },
+       { USAmode, "usa" },
+       { DAmode, "da" },
+       { UDAmode, "uda" },
+       { TAmode, "ta" },
+       { UTAmode, "uta" },
+       { QImode, "qi" },
+       { HImode, "hi" },
+       { SImode, "si" },
+       { DImode, "di" },
+       { TImode, "ti" },
+       { SFmode, "sf" },
+       { DFmode, "df" }
+      };
+    unsigned int i, j;
+
+    for (i = 0; i < ARRAY_SIZE (fixed_arith_modes); i++)
+      {
+       arm_set_fixed_optab_libfunc (add_optab, fixed_arith_modes[i].mode,
+                                    "add", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (ssadd_optab, fixed_arith_modes[i].mode,
+                                    "ssadd", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (usadd_optab, fixed_arith_modes[i].mode,
+                                    "usadd", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (sub_optab, fixed_arith_modes[i].mode,
+                                    "sub", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (sssub_optab, fixed_arith_modes[i].mode,
+                                    "sssub", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (ussub_optab, fixed_arith_modes[i].mode,
+                                    "ussub", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (smul_optab, fixed_arith_modes[i].mode,
+                                    "mul", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (ssmul_optab, fixed_arith_modes[i].mode,
+                                    "ssmul", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (usmul_optab, fixed_arith_modes[i].mode,
+                                    "usmul", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (sdiv_optab, fixed_arith_modes[i].mode,
+                                    "div", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (udiv_optab, fixed_arith_modes[i].mode,
+                                    "udiv", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (ssdiv_optab, fixed_arith_modes[i].mode,
+                                    "ssdiv", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (usdiv_optab, fixed_arith_modes[i].mode,
+                                    "usdiv", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (neg_optab, fixed_arith_modes[i].mode,
+                                    "neg", fixed_arith_modes[i].name, 2);
+       arm_set_fixed_optab_libfunc (ssneg_optab, fixed_arith_modes[i].mode,
+                                    "ssneg", fixed_arith_modes[i].name, 2);
+       arm_set_fixed_optab_libfunc (usneg_optab, fixed_arith_modes[i].mode,
+                                    "usneg", fixed_arith_modes[i].name, 2);
+       arm_set_fixed_optab_libfunc (ashl_optab, fixed_arith_modes[i].mode,
+                                    "ashl", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (ashr_optab, fixed_arith_modes[i].mode,
+                                    "ashr", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (lshr_optab, fixed_arith_modes[i].mode,
+                                    "lshr", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (ssashl_optab, fixed_arith_modes[i].mode,
+                                    "ssashl", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (usashl_optab, fixed_arith_modes[i].mode,
+                                    "usashl", fixed_arith_modes[i].name, 3);
+       arm_set_fixed_optab_libfunc (cmp_optab, fixed_arith_modes[i].mode,
+                                    "cmp", fixed_arith_modes[i].name, 2);
+      }
+
+    for (i = 0; i < ARRAY_SIZE (fixed_conv_modes); i++)
+      for (j = 0; j < ARRAY_SIZE (fixed_conv_modes); j++)
+       {
+         if (i == j
+             || (!ALL_FIXED_POINT_MODE_P (fixed_conv_modes[i].mode)
+                 && !ALL_FIXED_POINT_MODE_P (fixed_conv_modes[j].mode)))
+           continue;
+
+         arm_set_fixed_conv_libfunc (fract_optab, fixed_conv_modes[i].mode,
+                                     fixed_conv_modes[j].mode, "fract",
+                                     fixed_conv_modes[i].name,
+                                     fixed_conv_modes[j].name);
+         arm_set_fixed_conv_libfunc (satfract_optab,
+                                     fixed_conv_modes[i].mode,
+                                     fixed_conv_modes[j].mode, "satfract",
+                                     fixed_conv_modes[i].name,
+                                     fixed_conv_modes[j].name);
+         arm_set_fixed_conv_libfunc (fractuns_optab,
+                                     fixed_conv_modes[i].mode,
+                                     fixed_conv_modes[j].mode, "fractuns",
+                                     fixed_conv_modes[i].name,
+                                     fixed_conv_modes[j].name);
+         arm_set_fixed_conv_libfunc (satfractuns_optab,
+                                     fixed_conv_modes[i].mode,
+                                     fixed_conv_modes[j].mode, "satfractuns",
+                                     fixed_conv_modes[i].name,
+                                     fixed_conv_modes[j].name);
+       }
+  }
+
   if (TARGET_AAPCS_BASED)
     synchronize_libfunc = init_one_libfunc ("__sync_synchronize");
 }
@@ -1244,14 +1404,14 @@ 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 
+       struct __va_list
        {
         void *__ap;
        };
@@ -1275,7 +1435,7 @@ arm_build_builtin_va_list (void)
   TYPE_STUB_DECL (va_list_type) = va_list_name;
   /* Create the __ap field.  */
   ap_field = build_decl (BUILTINS_LOCATION,
-                        FIELD_DECL, 
+                        FIELD_DECL,
                         get_identifier ("__ap"),
                         ptr_type_node);
   DECL_ARTIFICIAL (ap_field) = 1;
@@ -1301,7 +1461,7 @@ arm_extract_valist_ptr (tree valist)
   if (TARGET_AAPCS_BASED)
     {
       tree ap_field = TYPE_FIELDS (TREE_TYPE (valist));
-      valist = build3 (COMPONENT_REF, TREE_TYPE (ap_field), 
+      valist = build3 (COMPONENT_REF, TREE_TYPE (ap_field),
                       valist, ap_field, NULL_TREE);
     }
 
@@ -1318,154 +1478,25 @@ arm_expand_builtin_va_start (tree valist, rtx nextarg)
 
 /* Implement TARGET_GIMPLIFY_VA_ARG_EXPR.  */
 static tree
-arm_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p, 
+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);
 }
 
-/* Lookup NAME in SEL.  */
-
-static const struct processors *
-arm_find_cpu (const char *name, const struct processors *sel, const char *desc)
-{
-  if (!(name && *name))
-    return NULL;
-
-  for (; sel->name != NULL; sel++)
-    {
-      if (streq (name, sel->name))
-       return sel;
-    }
-
-  error ("bad value (%s) for %s switch", name, desc);
-  return NULL;
-}
-
-/* Implement TARGET_HANDLE_OPTION.  */
-
-static bool
-arm_handle_option (struct gcc_options *opts, struct gcc_options *opts_set,
-                  const struct cl_decoded_option *decoded,
-                  location_t loc ATTRIBUTE_UNUSED)
-{
-  size_t code = decoded->opt_index;
-  const char *arg = decoded->arg;
-
-  gcc_assert (opts == &global_options);
-  gcc_assert (opts_set == &global_options_set);
-
-  switch (code)
-    {
-    case OPT_march_:
-      arm_selected_arch = arm_find_cpu(arg, all_architectures, "-march");
-      return true;
-
-    case OPT_mcpu_:
-      arm_selected_cpu = arm_find_cpu(arg, all_cores, "-mcpu");
-      return true;
-
-    case OPT_mtune_:
-      arm_selected_tune = arm_find_cpu(arg, all_cores, "-mtune");
-      return true;
-
-    default:
-      return true;
-    }
-}
-
-static void
-arm_target_help (void)
-{
-  int i;
-  static int columns = 0;
-  int remaining;
-
-  /* If we have not done so already, obtain the desired maximum width of
-     the output.  Note - this is a duplication of the code at the start of
-     gcc/opts.c:print_specific_help() - the two copies should probably be
-     replaced by a single function.  */
-  if (columns == 0)
-    {
-      const char *p;
-
-      p = getenv ("COLUMNS");
-      if (p != NULL)
-       {
-         int value = atoi (p);
-
-         if (value > 0)
-           columns = value;
-       }
-
-      if (columns == 0)
-       /* Use a reasonable default.  */
-       columns = 80;
-    }
-
-  printf ("  Known ARM CPUs (for use with the -mcpu= and -mtune= options):\n");
-
-  /* The - 2 is because we know that the last entry in the array is NULL.  */
-  i = ARRAY_SIZE (all_cores) - 2;
-  gcc_assert (i > 0);
-  printf ("    %s", all_cores[i].name);
-  remaining = columns - (strlen (all_cores[i].name) + 4);
-  gcc_assert (remaining >= 0);
-
-  while (i--)
-    {
-      int len = strlen (all_cores[i].name);
-
-      if (remaining > len + 2)
-       {
-         printf (", %s", all_cores[i].name);
-         remaining -= len + 2;
-       }
-      else
-       {
-         if (remaining > 0)
-           printf (",");
-         printf ("\n    %s", all_cores[i].name);
-         remaining = columns - (len + 4);
-       }
-    }
-
-  printf ("\n\n  Known ARM architectures (for use with the -march= option):\n");
-
-  i = ARRAY_SIZE (all_architectures) - 2;
-  gcc_assert (i > 0);
-  
-  printf ("    %s", all_architectures[i].name);
-  remaining = columns - (strlen (all_architectures[i].name) + 4);
-  gcc_assert (remaining >= 0);
-
-  while (i--)
-    {
-      int len = strlen (all_architectures[i].name);
-
-      if (remaining > len + 2)
-       {
-         printf (", %s", all_architectures[i].name);
-         remaining -= len + 2;
-       }
-      else
-       {
-         if (remaining > 0)
-           printf (",");
-         printf ("\n    %s", all_architectures[i].name);
-         remaining = columns - (len + 4);
-       }
-    }
-  printf ("\n");
-
-}
-
 /* Fix up any incompatible options that the user has specified.  */
 static void
 arm_option_override (void)
 {
-  unsigned i;
+  if (global_options_set.x_arm_arch_option)
+    arm_selected_arch = &all_architectures[arm_arch_option];
+
+  if (global_options_set.x_arm_cpu_option)
+    arm_selected_cpu = &all_cores[(int) arm_cpu_option];
+
+  if (global_options_set.x_arm_tune_option)
+    arm_selected_tune = &all_cores[(int) arm_tune_option];
 
 #ifdef SUBTARGET_OVERRIDE_OPTIONS
   SUBTARGET_OVERRIDE_OPTIONS;
@@ -1592,39 +1623,6 @@ arm_option_override (void)
   tune_flags = arm_selected_tune->flags;
   current_tune = arm_selected_tune->tune;
 
-  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++)
-       {
-         if (streq (arm_all_abis[i].name, target_abi_name))
-           {
-             arm_abi = arm_all_abis[i].abi_type;
-             break;
-           }
-       }
-      if (i == ARRAY_SIZE (arm_all_abis))
-       error ("invalid ABI option: -mabi=%s", target_abi_name);
-    }
-  else
-    arm_abi = ARM_DEFAULT_ABI;
-
   /* Make sure that the processor choice does not conflict with any of the
      other command line choices.  */
   if (TARGET_ARM && !(insn_flags & FL_NOTM))
@@ -1689,6 +1687,10 @@ arm_option_override (void)
   if (TARGET_APCS_FLOAT)
     warning (0, "passing floating point arguments in fp regs not yet supported");
 
+  if (TARGET_LITTLE_WORDS)
+    warning (OPT_Wdeprecated, "%<mwords-little-endian%> is deprecated and "
+            "will be removed in a future release");
+
   /* Initialize boolean versions of the flags, for use in the arm.md file.  */
   arm_arch3m = (insn_flags & FL_ARCH3M) != 0;
   arm_arch4 = (insn_flags & FL_ARCH4) != 0;
@@ -1711,7 +1713,8 @@ arm_option_override (void)
   arm_tune_wbuf = (tune_flags & FL_WBUF) != 0;
   arm_tune_xscale = (tune_flags & FL_XSCALE) != 0;
   arm_arch_iwmmxt = (insn_flags & FL_IWMMXT) != 0;
-  arm_arch_hwdiv = (insn_flags & FL_DIV) != 0;
+  arm_arch_thumb_hwdiv = (insn_flags & FL_THUMB_DIV) != 0;
+  arm_arch_arm_hwdiv = (insn_flags & FL_ARM_DIV) != 0;
   arm_tune_cortex_a9 = (arm_tune == cortexa9) != 0;
 
   /* If we are not using the default (ARM mode) section anchor offset
@@ -1752,19 +1755,11 @@ arm_option_override (void)
   if (TARGET_IWMMXT_ABI && !TARGET_IWMMXT)
     error ("iwmmxt abi requires an iwmmxt capable cpu");
 
-  if (target_fpu_name == NULL && target_fpe_name != NULL)
+  if (!global_options_set.x_arm_fpu_index)
     {
-      if (streq (target_fpe_name, "2"))
-       target_fpu_name = "fpe2";
-      else if (streq (target_fpe_name, "3"))
-       target_fpu_name = "fpe3";
-      else
-       error ("invalid floating point emulation option: -mfpe=%s",
-              target_fpe_name);
-    }
+      const char *target_fpu_name;
+      bool ok;
 
-  if (target_fpu_name == NULL)
-    {
 #ifdef FPUTYPE_DEFAULT
       target_fpu_name = FPUTYPE_DEFAULT;
 #else
@@ -1773,23 +1768,13 @@ arm_option_override (void)
       else
        target_fpu_name = "fpe2";
 #endif
-    }
 
-  arm_fpu_desc = NULL;
-  for (i = 0; i < ARRAY_SIZE (all_fpus); i++)
-    {
-      if (streq (all_fpus[i].name, target_fpu_name))
-       {
-         arm_fpu_desc = &all_fpus[i];
-         break;
-       }
+      ok = opt_enum_arg_to_value (OPT_mfpu_, target_fpu_name, &arm_fpu_index,
+                                 CL_TARGET);
+      gcc_assert (ok);
     }
 
-  if (!arm_fpu_desc)
-    {
-      error ("invalid floating point option: -mfpu=%s", target_fpu_name);
-      return;
-    }
+  arm_fpu_desc = &all_fpus[arm_fpu_index];
 
   switch (arm_fpu_desc->model)
     {
@@ -1814,24 +1799,6 @@ arm_option_override (void)
       gcc_unreachable();
     }
 
-  if (target_float_abi_name != NULL)
-    {
-      /* The user specified a FP ABI.  */
-      for (i = 0; i < ARRAY_SIZE (all_float_abis); i++)
-       {
-         if (streq (all_float_abis[i].name, target_float_abi_name))
-           {
-             arm_float_abi = all_float_abis[i].abi_type;
-             break;
-           }
-       }
-      if (i == ARRAY_SIZE (all_float_abis))
-       error ("invalid floating point abi: -mfloat-abi=%s",
-              target_float_abi_name);
-    }
-  else
-    arm_float_abi = TARGET_DEFAULT_FLOAT_ABI;
-
   if (TARGET_AAPCS_BASED
       && (arm_fpu_desc->model == ARM_FP_MODEL_FPA))
     error ("FPA is unsupported in the AAPCS");
@@ -1892,18 +1859,6 @@ arm_option_override (void)
       && (tune_flags & FL_MODE32) == 0)
     flag_schedule_insns = flag_schedule_insns_after_reload = 0;
 
-  if (target_thread_switch)
-    {
-      if (strcmp (target_thread_switch, "soft") == 0)
-       target_thread_pointer = TP_SOFT;
-      else if (strcmp (target_thread_switch, "auto") == 0)
-       target_thread_pointer = TP_AUTO;
-      else if (strcmp (target_thread_switch, "cp15") == 0)
-       target_thread_pointer = TP_CP15;
-      else
-       error ("invalid thread pointer option: -mtp=%s", target_thread_switch);
-    }
-
   /* Use the cp15 method if it is available.  */
   if (target_thread_pointer == TP_AUTO)
     {
@@ -1917,19 +1872,25 @@ arm_option_override (void)
     error ("can not use -mtp=cp15 with 16-bit Thumb");
 
   /* Override the default structure alignment for AAPCS ABI.  */
-  if (TARGET_AAPCS_BASED)
-    arm_structure_size_boundary = 8;
-
-  if (structure_size_string != NULL)
+  if (!global_options_set.x_arm_structure_size_boundary)
     {
-      int size = strtol (structure_size_string, NULL, 0);
-
-      if (size == 8 || size == 32
-         || (ARM_DOUBLEWORD_ALIGN && size == 64))
-       arm_structure_size_boundary = size;
-      else
-       warning (0, "structure size boundary can only be set to %s",
-                ARM_DOUBLEWORD_ALIGN ? "8, 32 or 64": "8 or 32");
+      if (TARGET_AAPCS_BASED)
+       arm_structure_size_boundary = 8;
+    }
+  else
+    {
+      if (arm_structure_size_boundary != 8
+         && arm_structure_size_boundary != 32
+         && !(ARM_DOUBLEWORD_ALIGN && arm_structure_size_boundary == 64))
+       {
+         if (ARM_DOUBLEWORD_ALIGN)
+           warning (0,
+                    "structure size boundary can only be set to 8, 32 or 64");
+         else
+           warning (0, "structure size boundary can only be set to 8 or 32");
+         arm_structure_size_boundary
+           = (TARGET_AAPCS_BASED ? 8 : DEFAULT_STRUCTURE_SIZE_BOUNDARY);
+       }
     }
 
   if (!TARGET_ARM && TARGET_VXWORKS_RTP && flag_pic)
@@ -1978,6 +1939,28 @@ arm_option_override (void)
        fix_cm3_ldrd = 0;
     }
 
+  /* Enable -munaligned-access by default for
+     - all ARMv6 architecture-based processors
+     - ARMv7-A, ARMv7-R, and ARMv7-M architecture-based processors.
+
+     Disable -munaligned-access by default for
+     - all pre-ARMv6 architecture-based processors
+     - ARMv6-M architecture-based processors.  */
+
+  if (unaligned_access == 2)
+    {
+      if (arm_arch6 && (arm_arch_notm || arm_arch7))
+       unaligned_access = 1;
+      else
+       unaligned_access = 0;
+    }
+  else if (unaligned_access == 1
+          && !(arm_arch6 && (arm_arch_notm || arm_arch7)))
+    {
+      warning (0, "target CPU does not support unaligned accesses");
+      unaligned_access = 0;
+    }
+
   if (TARGET_THUMB1 && flag_schedule_insns)
     {
       /* Don't warn since it's on by default in -O2.  */
@@ -1991,12 +1974,7 @@ arm_option_override (void)
       max_insns_skipped = 6;
     }
   else
-    {
-      /* StrongARM has early execution of branches, so a sequence
-         that is worth skipping is shorter.  */
-      if (arm_tune_strongarm)
-        max_insns_skipped = 3;
-    }
+    max_insns_skipped = current_tune->max_insns_skipped;
 
   /* Hot/Cold partitioning is not currently supported, since we can't
      handle literal pool placement in that case.  */
@@ -2018,7 +1996,8 @@ arm_option_override (void)
                           global_options_set.x_param_values);
 
   /* ARM EABI defaults to strict volatile bitfields.  */
-  if (TARGET_AAPCS_BASED && flag_strict_volatile_bitfields < 0)
+  if (TARGET_AAPCS_BASED && flag_strict_volatile_bitfields < 0
+      && abi_version_at_least(2))
     flag_strict_volatile_bitfields = 1;
 
   /* Enable sw prefetching at -O3 for CPUS that have prefetch, and we have deemed
@@ -2358,7 +2337,7 @@ use_return_insn (int iscond, rtx sibling)
       if (saved_int_regs != 0 && saved_int_regs != (1 << LR_REGNUM))
        return 0;
 
-      if (flag_pic 
+      if (flag_pic
          && arm_pic_register != INVALID_REGNUM
          && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
        return 0;
@@ -2414,7 +2393,7 @@ const_ok_for_arm (HOST_WIDE_INT i)
 
   /* Get the number of trailing zeros.  */
   lowbit = ffs((int) i) - 1;
-  
+
   /* Only even shifts are allowed in ARM mode so round down to the
      nearest even number.  */
   if (TARGET_ARM)
@@ -2453,7 +2432,7 @@ const_ok_for_arm (HOST_WIDE_INT i)
 }
 
 /* Return true if I is a valid constant for the operation CODE.  */
-static int
+int
 const_ok_for_op (HOST_WIDE_INT i, enum rtx_code code)
 {
   if (const_ok_for_arm (i))
@@ -2461,7 +2440,22 @@ const_ok_for_op (HOST_WIDE_INT i, enum rtx_code code)
 
   switch (code)
     {
+    case SET:
+      /* See if we can use movw.  */
+      if (arm_arch_thumb2 && (i & 0xffff0000) == 0)
+       return 1;
+      else
+       /* Otherwise, try mvn.  */
+       return const_ok_for_arm (ARM_SIGN_EXTEND (~i));
+
     case PLUS:
+      /* See if we can use addw or subw.  */
+      if (TARGET_THUMB2
+         && ((i & 0xfffff000) == 0
+             || ((-i) & 0xfffff000) == 0))
+       return 1;
+      /* else fall through.  */
+
     case COMPARE:
     case EQ:
     case NE:
@@ -2577,68 +2571,41 @@ arm_split_constant (enum rtx_code code, enum machine_mode mode, rtx insn,
                           1);
 }
 
-/* Return the number of instructions required to synthesize the given
-   constant, if we start emitting them from bit-position I.  */
+/* Return a sequence of integers, in RETURN_SEQUENCE that fit into
+   ARM/THUMB2 immediates, and add up to VAL.
+   Thr function return value gives the number of insns required.  */
 static int
-count_insns_for_constant (HOST_WIDE_INT remainder, int i)
-{
-  HOST_WIDE_INT temp1;
-  int step_size = TARGET_ARM ? 2 : 1;
-  int num_insns = 0;
-
-  gcc_assert (TARGET_ARM || i == 0);
-
-  do
-    {
-      int end;
-
-      if (i <= 0)
-       i += 32;
-      if (remainder & (((1 << step_size) - 1) << (i - step_size)))
-       {
-         end = i - 8;
-         if (end < 0)
-           end += 32;
-         temp1 = remainder & ((0x0ff << end)
-                                   | ((i < end) ? (0xff >> (32 - end)) : 0));
-         remainder &= ~temp1;
-         num_insns++;
-         i -= 8 - step_size;
-       }
-      i -= step_size;
-    } while (remainder);
-  return num_insns;
-}
-
-static int
-find_best_start (unsigned HOST_WIDE_INT remainder)
+optimal_immediate_sequence (enum rtx_code code, unsigned HOST_WIDE_INT val,
+                           struct four_ints *return_sequence)
 {
   int best_consecutive_zeros = 0;
   int i;
   int best_start = 0;
+  int insns1, insns2;
+  struct four_ints tmp_sequence;
 
   /* If we aren't targetting ARM, the best place to start is always at
-     the bottom.  */
-  if (! TARGET_ARM)
-    return 0;
-
-  for (i = 0; i < 32; i += 2)
+     the bottom, otherwise look more closely.  */
+  if (TARGET_ARM)
     {
-      int consecutive_zeros = 0;
-
-      if (!(remainder & (3 << i)))
+      for (i = 0; i < 32; i += 2)
        {
-         while ((i < 32) && !(remainder & (3 << i)))
-           {
-             consecutive_zeros += 2;
-             i += 2;
-           }
-         if (consecutive_zeros > best_consecutive_zeros)
+         int consecutive_zeros = 0;
+
+         if (!(val & (3 << i)))
            {
-             best_consecutive_zeros = consecutive_zeros;
-             best_start = i - consecutive_zeros;
+             while ((i < 32) && !(val & (3 << i)))
+               {
+                 consecutive_zeros += 2;
+                 i += 2;
+               }
+             if (consecutive_zeros > best_consecutive_zeros)
+               {
+                 best_consecutive_zeros = consecutive_zeros;
+                 best_start = i - consecutive_zeros;
+               }
+             i -= 2;
            }
-         i -= 2;
        }
     }
 
@@ -2665,13 +2632,161 @@ find_best_start (unsigned HOST_WIDE_INT remainder)
      the constant starting from `best_start', and also starting from
      zero (i.e. with bit 31 first to be output).  If `best_start' doesn't
      yield a shorter sequence, we may as well use zero.  */
+  insns1 = optimal_immediate_sequence_1 (code, val, return_sequence, best_start);
   if (best_start != 0
-      && ((((unsigned HOST_WIDE_INT) 1) << best_start) < remainder)
-      && (count_insns_for_constant (remainder, 0) <=
-         count_insns_for_constant (remainder, best_start)))
-    best_start = 0;
+      && ((((unsigned HOST_WIDE_INT) 1) << best_start) < val))
+    {
+      insns2 = optimal_immediate_sequence_1 (code, val, &tmp_sequence, 0);
+      if (insns2 <= insns1)
+       {
+         *return_sequence = tmp_sequence;
+         insns1 = insns2;
+       }
+    }
+
+  return insns1;
+}
+
+/* As for optimal_immediate_sequence, but starting at bit-position I.  */
+static int
+optimal_immediate_sequence_1 (enum rtx_code code, unsigned HOST_WIDE_INT val,
+                            struct four_ints *return_sequence, int i)
+{
+  int remainder = val & 0xffffffff;
+  int insns = 0;
+
+  /* Try and find a way of doing the job in either two or three
+     instructions.
+
+     In ARM mode we can use 8-bit constants, rotated to any 2-bit aligned
+     location.  We start at position I.  This may be the MSB, or
+     optimial_immediate_sequence may have positioned it at the largest block
+     of zeros that are aligned on a 2-bit boundary. We then fill up the temps,
+     wrapping around to the top of the word when we drop off the bottom.
+     In the worst case this code should produce no more than four insns.
+
+     In Thumb2 mode, we can use 32/16-bit replicated constants, and 8-bit
+     constants, shifted to any arbitrary location.  We should always start
+     at the MSB.  */
+  do
+    {
+      int end;
+      unsigned int b1, b2, b3, b4;
+      unsigned HOST_WIDE_INT result;
+      int loc;
+
+      gcc_assert (insns < 4);
+
+      if (i <= 0)
+       i += 32;
+
+      /* First, find the next normal 12/8-bit shifted/rotated immediate.  */
+      if (remainder & ((TARGET_ARM ? (3 << (i - 2)) : (1 << (i - 1)))))
+       {
+         loc = i;
+         if (i <= 12 && TARGET_THUMB2 && code == PLUS)
+           /* We can use addw/subw for the last 12 bits.  */
+           result = remainder;
+         else
+           {
+             /* Use an 8-bit shifted/rotated immediate.  */
+             end = i - 8;
+             if (end < 0)
+               end += 32;
+             result = remainder & ((0x0ff << end)
+                                  | ((i < end) ? (0xff >> (32 - end))
+                                               : 0));
+             i -= 8;
+           }
+       }
+      else
+       {
+         /* Arm allows rotates by a multiple of two. Thumb-2 allows
+            arbitrary shifts.  */
+         i -= TARGET_ARM ? 2 : 1;
+         continue;
+       }
+
+      /* Next, see if we can do a better job with a thumb2 replicated
+        constant.
+
+         We do it this way around to catch the cases like 0x01F001E0 where
+        two 8-bit immediates would work, but a replicated constant would
+        make it worse.
+
+         TODO: 16-bit constants that don't clear all the bits, but still win.
+         TODO: Arithmetic splitting for set/add/sub, rather than bitwise.  */
+      if (TARGET_THUMB2)
+       {
+         b1 = (remainder & 0xff000000) >> 24;
+         b2 = (remainder & 0x00ff0000) >> 16;
+         b3 = (remainder & 0x0000ff00) >> 8;
+         b4 = remainder & 0xff;
+
+         if (loc > 24)
+           {
+             /* The 8-bit immediate already found clears b1 (and maybe b2),
+                but must leave b3 and b4 alone.  */
+
+             /* First try to find a 32-bit replicated constant that clears
+                almost everything.  We can assume that we can't do it in one,
+                or else we wouldn't be here.  */
+             unsigned int tmp = b1 & b2 & b3 & b4;
+             unsigned int tmp2 = tmp + (tmp << 8) + (tmp << 16)
+                                 + (tmp << 24);
+             unsigned int matching_bytes = (tmp == b1) + (tmp == b2)
+                                           + (tmp == b3) + (tmp == b4);
+             if (tmp
+                 && (matching_bytes >= 3
+                     || (matching_bytes == 2
+                         && const_ok_for_op (remainder & ~tmp2, code))))
+               {
+                 /* At least 3 of the bytes match, and the fourth has at
+                    least as many bits set, or two of the bytes match
+                    and it will only require one more insn to finish.  */
+                 result = tmp2;
+                 i = tmp != b1 ? 32
+                     : tmp != b2 ? 24
+                     : tmp != b3 ? 16
+                     : 8;
+               }
+
+             /* Second, try to find a 16-bit replicated constant that can
+                leave three of the bytes clear.  If b2 or b4 is already
+                zero, then we can.  If the 8-bit from above would not
+                clear b2 anyway, then we still win.  */
+             else if (b1 == b3 && (!b2 || !b4
+                              || (remainder & 0x00ff0000 & ~result)))
+               {
+                 result = remainder & 0xff00ff00;
+                 i = 24;
+               }
+           }
+         else if (loc > 16)
+           {
+             /* The 8-bit immediate already found clears b2 (and maybe b3)
+                and we don't get here unless b1 is alredy clear, but it will
+                leave b4 unchanged.  */
+
+             /* If we can clear b2 and b4 at once, then we win, since the
+                8-bits couldn't possibly reach that far.  */
+             if (b2 == b4)
+               {
+                 result = remainder & 0x00ff00ff;
+                 i = 16;
+               }
+           }
+       }
 
-  return best_start;
+      return_sequence->i[insns++] = result;
+      remainder &= ~result;
+
+      if (code == SET || code == MINUS)
+       code = PLUS;
+    }
+  while (remainder);
+
+  return insns;
 }
 
 /* Emit an instruction with the indicated PATTERN.  If COND is
@@ -2688,7 +2803,6 @@ emit_constant_insn (rtx cond, rtx pattern)
 
 /* As above, but extra parameter GENERATE which, if clear, suppresses
    RTL generation.  */
-/* ??? This needs more work for thumb2.  */
 
 static int
 arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
@@ -2698,17 +2812,16 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
   int can_invert = 0;
   int can_negate = 0;
   int final_invert = 0;
-  int can_negate_initial = 0;
   int i;
-  int num_bits_set = 0;
   int set_sign_bit_copies = 0;
   int clear_sign_bit_copies = 0;
   int clear_zero_bit_copies = 0;
   int set_zero_bit_copies = 0;
-  int insns = 0;
+  int insns = 0, neg_insns, inv_insns;
   unsigned HOST_WIDE_INT temp1, temp2;
   unsigned HOST_WIDE_INT remainder = val & 0xffffffff;
-  int step_size = TARGET_ARM ? 2 : 1;
+  struct four_ints *immediates;
+  struct four_ints pos_immediates, neg_immediates, inv_immediates;
 
   /* Find out which operations are safe for a given CODE.  Also do a quick
      check for degenerate cases; these can occur when DImode operations
@@ -2717,12 +2830,10 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
     {
     case SET:
       can_invert = 1;
-      can_negate = 1;
       break;
 
     case PLUS:
       can_negate = 1;
-      can_negate_initial = 1;
       break;
 
     case IOR:
@@ -2745,9 +2856,6 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
                                gen_rtx_SET (VOIDmode, target, source));
          return 1;
        }
-
-      if (TARGET_THUMB2)
-       can_invert = 1;
       break;
 
     case AND:
@@ -2789,6 +2897,7 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
                                             gen_rtx_NOT (mode, source)));
          return 1;
        }
+      final_invert = 1;
       break;
 
     case MINUS:
@@ -2811,7 +2920,6 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
                                                            source)));
          return 1;
        }
-      can_negate = 1;
 
       break;
 
@@ -2820,9 +2928,7 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
     }
 
   /* If we can do it in one insn get out quickly.  */
-  if (const_ok_for_arm (val)
-      || (can_negate_initial && const_ok_for_arm (-val))
-      || (can_invert && const_ok_for_arm (~val)))
+  if (const_ok_for_op (val, code))
     {
       if (generate)
        emit_constant_insn (cond,
@@ -2875,15 +2981,6 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
   switch (code)
     {
     case SET:
-      /* See if we can use movw.  */
-      if (arm_arch_thumb2 && (remainder & 0xffff0000) == 0)
-       {
-         if (generate)
-           emit_constant_insn (cond, gen_rtx_SET (VOIDmode, target,
-                                                  GEN_INT (val)));
-         return 1;
-       }
-
       /* See if we can do this by sign_extending a constant that is known
         to be negative.  This is a good, way of doing it, since the shift
         may well merge into a subsequent insn.  */
@@ -3234,121 +3331,97 @@ arm_gen_constant (enum rtx_code code, enum machine_mode mode, rtx cond,
       break;
     }
 
-  for (i = 0; i < 32; i++)
-    if (remainder & (1 << i))
-      num_bits_set++;
-
-  if ((code == AND)
-      || (code != IOR && can_invert && num_bits_set > 16))
-    remainder ^= 0xffffffff;
-  else if (code == PLUS && num_bits_set > 16)
-    remainder = (-remainder) & 0xffffffff;
-
-  /* For XOR, if more than half the bits are set and there's a sequence
-     of more than 8 consecutive ones in the pattern then we can XOR by the
-     inverted constant and then invert the final result; this may save an
-     instruction and might also lead to the final mvn being merged with
-     some other operation.  */
-  else if (code == XOR && num_bits_set > 16
-          && (count_insns_for_constant (remainder ^ 0xffffffff,
-                                        find_best_start
-                                        (remainder ^ 0xffffffff))
-              < count_insns_for_constant (remainder,
-                                          find_best_start (remainder))))
-    {
-      remainder ^= 0xffffffff;
-      final_invert = 1;
+  /* Calculate what the instruction sequences would be if we generated it
+     normally, negated, or inverted.  */
+  if (code == AND)
+    /* AND cannot be split into multiple insns, so invert and use BIC.  */
+    insns = 99;
+  else
+    insns = optimal_immediate_sequence (code, remainder, &pos_immediates);
+
+  if (can_negate)
+    neg_insns = optimal_immediate_sequence (code, (-remainder) & 0xffffffff,
+                                           &neg_immediates);
+  else
+    neg_insns = 99;
+
+  if (can_invert || final_invert)
+    inv_insns = optimal_immediate_sequence (code, remainder ^ 0xffffffff,
+                                           &inv_immediates);
+  else
+    inv_insns = 99;
+
+  immediates = &pos_immediates;
+
+  /* Is the negated immediate sequence more efficient?  */
+  if (neg_insns < insns && neg_insns <= inv_insns)
+    {
+      insns = neg_insns;
+      immediates = &neg_immediates;
+    }
+  else
+    can_negate = 0;
+
+  /* Is the inverted immediate sequence more efficient?
+     We must allow for an extra NOT instruction for XOR operations, although
+     there is some chance that the final 'mvn' will get optimized later.  */
+  if ((inv_insns + 1) < insns || (!final_invert && inv_insns < insns))
+    {
+      insns = inv_insns;
+      immediates = &inv_immediates;
     }
   else
     {
       can_invert = 0;
-      can_negate = 0;
+      final_invert = 0;
     }
 
-  /* Now try and find a way of doing the job in either two or three
-     instructions.
-     We start by looking for the largest block of zeros that are aligned on
-     a 2-bit boundary, we then fill up the temps, wrapping around to the
-     top of the word when we drop off the bottom.
-     In the worst case this code should produce no more than four insns.
-     Thumb-2 constants are shifted, not rotated, so the MSB is always the
-     best place to start.  */
+  /* Now output the chosen sequence as instructions.  */
+  if (generate)
+    {
+      for (i = 0; i < insns; i++)
+       {
+         rtx new_src, temp1_rtx;
 
-  /* ??? Use thumb2 replicated constants when the high and low halfwords are
-     the same.  */
-  {
-    /* Now start emitting the insns.  */
-    i = find_best_start (remainder);
-    do
-      {
-       int end;
+         temp1 = immediates->i[i];
 
-       if (i <= 0)
-         i += 32;
-       if (remainder & (3 << (i - 2)))
-         {
-           end = i - 8;
-           if (end < 0)
-             end += 32;
-           temp1 = remainder & ((0x0ff << end)
-                                | ((i < end) ? (0xff >> (32 - end)) : 0));
-           remainder &= ~temp1;
-
-           if (generate)
-             {
-               rtx new_src, temp1_rtx;
+         if (code == SET || code == MINUS)
+           new_src = (subtargets ? gen_reg_rtx (mode) : target);
+         else if ((final_invert || i < (insns - 1)) && subtargets)
+           new_src = gen_reg_rtx (mode);
+         else
+           new_src = target;
 
-               if (code == SET || code == MINUS)
-                 {
-                   new_src = (subtargets ? gen_reg_rtx (mode) : target);
-                   if (can_invert && code != MINUS)
-                     temp1 = ~temp1;
-                 }
-               else
-                 {
-                   if ((final_invert || remainder) && subtargets)
-                     new_src = gen_reg_rtx (mode);
-                   else
-                     new_src = target;
-                   if (can_invert)
-                     temp1 = ~temp1;
-                   else if (can_negate)
-                     temp1 = -temp1;
-                 }
+         if (can_invert)
+           temp1 = ~temp1;
+         else if (can_negate)
+           temp1 = -temp1;
 
-               temp1 = trunc_int_for_mode (temp1, mode);
-               temp1_rtx = GEN_INT (temp1);
+         temp1 = trunc_int_for_mode (temp1, mode);
+         temp1_rtx = GEN_INT (temp1);
 
-               if (code == SET)
-                 ;
-               else if (code == MINUS)
-                 temp1_rtx = gen_rtx_MINUS (mode, temp1_rtx, source);
-               else
-                 temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx);
+         if (code == SET)
+           ;
+         else if (code == MINUS)
+           temp1_rtx = gen_rtx_MINUS (mode, temp1_rtx, source);
+         else
+           temp1_rtx = gen_rtx_fmt_ee (code, mode, source, temp1_rtx);
 
-               emit_constant_insn (cond,
-                                   gen_rtx_SET (VOIDmode, new_src,
-                                                temp1_rtx));
-               source = new_src;
-             }
+         emit_constant_insn (cond,
+                             gen_rtx_SET (VOIDmode, new_src,
+                                          temp1_rtx));
+         source = new_src;
 
-           if (code == SET)
-             {
-               can_invert = 0;
-               code = PLUS;
-             }
-           else if (code == MINUS)
+         if (code == SET)
+           {
+             can_negate = can_invert;
+             can_invert = 0;
              code = PLUS;
-
-           insns++;
-           i -= 8 - step_size;
-         }
-       /* Arm allows rotates by a multiple of two. Thumb-2 allows arbitrary
-          shifts.  */
-       i -= step_size;
-      }
-    while (remainder);
-  }
+           }
+         else if (code == MINUS)
+           code = PLUS;
+       }
+    }
 
   if (final_invert)
     {
@@ -3433,6 +3506,19 @@ arm_canonicalize_comparison (enum rtx_code code, rtx *op0, rtx *op1)
       return code;
     }
 
+  /* If *op0 is (zero_extend:SI (subreg:QI (reg:SI) 0)) and comparing
+     with const0_rtx, change it to (and:SI (reg:SI) (const_int 255)),
+     to facilitate possible combining with a cmp into 'ands'.  */
+  if (mode == SImode
+      && GET_CODE (*op0) == ZERO_EXTEND
+      && GET_CODE (XEXP (*op0, 0)) == SUBREG
+      && GET_MODE (XEXP (*op0, 0)) == QImode
+      && GET_MODE (SUBREG_REG (XEXP (*op0, 0))) == SImode
+      && subreg_lowpart_p (XEXP (*op0, 0))
+      && *op1 == const0_rtx)
+    *op0 = gen_rtx_AND (SImode, SUBREG_REG (XEXP (*op0, 0)),
+                       GEN_INT (255));
+
   /* Comparisons smaller than DImode.  Only adjust comparisons against
      an out-of-range constant.  */
   if (GET_CODE (*op1) != CONST_INT
@@ -3527,7 +3613,7 @@ arm_function_value(const_tree type, const_tree func,
        }
     }
 
-  return LIBCALL_VALUE (mode);
+  return arm_libcall_value_1 (mode);
 }
 
 static int
@@ -3568,7 +3654,7 @@ arm_libcall_uses_aapcs_base (const_rtx libcall)
                   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,
@@ -3583,6 +3669,10 @@ arm_libcall_uses_aapcs_base (const_rtx libcall)
       add_libcall (libcall_htab,
                   convert_optab_libfunc (trunc_optab, HFmode, SFmode));
       add_libcall (libcall_htab,
+                  convert_optab_libfunc (sfix_optab, SImode, DFmode));
+      add_libcall (libcall_htab,
+                  convert_optab_libfunc (ufix_optab, SImode, DFmode));
+      add_libcall (libcall_htab,
                   convert_optab_libfunc (sfix_optab, DImode, DFmode));
       add_libcall (libcall_htab,
                   convert_optab_libfunc (ufix_optab, DImode, DFmode));
@@ -3590,12 +3680,59 @@ arm_libcall_uses_aapcs_base (const_rtx libcall)
                   convert_optab_libfunc (sfix_optab, DImode, SFmode));
       add_libcall (libcall_htab,
                   convert_optab_libfunc (ufix_optab, DImode, SFmode));
+
+      /* Values from double-precision helper functions are returned in core
+        registers if the selected core only supports single-precision
+        arithmetic, even if we are using the hard-float ABI.  The same is
+        true for single-precision helpers, but we will never be using the
+        hard-float ABI on a CPU which doesn't support single-precision
+        operations in hardware.  */
+      add_libcall (libcall_htab, optab_libfunc (add_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (sdiv_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (smul_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (neg_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (sub_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (eq_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (lt_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (le_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (ge_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (gt_optab, DFmode));
+      add_libcall (libcall_htab, optab_libfunc (unord_optab, DFmode));
+      add_libcall (libcall_htab, convert_optab_libfunc (sext_optab, DFmode,
+                                                       SFmode));
+      add_libcall (libcall_htab, convert_optab_libfunc (trunc_optab, SFmode,
+                                                       DFmode));
     }
 
   return libcall && htab_find (libcall_htab, libcall) != NULL;
 }
 
-rtx
+static rtx
+arm_libcall_value_1 (enum machine_mode mode)
+{
+  if (TARGET_AAPCS_BASED)
+    return aapcs_libcall_value (mode);
+  else if (TARGET_32BIT
+          && TARGET_HARD_FLOAT_ABI
+          && TARGET_FPA
+          && GET_MODE_CLASS (mode) == MODE_FLOAT)
+    return gen_rtx_REG (mode, FIRST_FPA_REGNUM);
+  else if (TARGET_32BIT
+          && TARGET_HARD_FLOAT_ABI
+          && TARGET_MAVERICK
+          && GET_MODE_CLASS (mode) == MODE_FLOAT)
+    return gen_rtx_REG (mode, FIRST_CIRRUS_FP_REGNUM);
+  else if (TARGET_IWMMXT_ABI
+          && arm_vector_mode_supported_p (mode))
+    return gen_rtx_REG (mode, FIRST_IWMMXT_REGNUM);
+  else
+    return gen_rtx_REG (mode, ARG_REGISTER (1));
+}
+
+/* Define how to find the value returned by a library function
+   assuming the value has mode MODE.  */
+
+static rtx
 arm_libcall_value (enum machine_mode mode, const_rtx libcall)
 {
   if (TARGET_AAPCS_BASED && arm_pcs_default != ARM_PCS_AAPCS
@@ -3608,7 +3745,33 @@ arm_libcall_value (enum machine_mode mode, const_rtx libcall)
 
     }
 
-  return LIBCALL_VALUE (mode);
+  return arm_libcall_value_1 (mode);
+}
+
+/* Implement TARGET_FUNCTION_VALUE_REGNO_P.  */
+
+static bool
+arm_function_value_regno_p (const unsigned int regno)
+{
+  if (regno == ARG_REGISTER (1)
+      || (TARGET_32BIT
+         && TARGET_AAPCS_BASED
+         && TARGET_VFP
+         && TARGET_HARD_FLOAT
+         && regno == FIRST_VFP_REGNUM)
+      || (TARGET_32BIT
+         && TARGET_HARD_FLOAT_ABI
+         && TARGET_MAVERICK
+         && regno == FIRST_CIRRUS_FP_REGNUM)
+      || (TARGET_IWMMXT_ABI
+         && regno == FIRST_IWMMXT_REGNUM)
+      || (TARGET_32BIT
+         && TARGET_HARD_FLOAT_ABI
+         && TARGET_FPA
+         && regno == FIRST_FPA_REGNUM))
+    return true;
+
+  return false;
 }
 
 /* Determine the amount of memory needed to store the possible return
@@ -3866,7 +4029,7 @@ arm_get_pcs_model (const_tree type, const_tree decl)
         (no argument is ever a candidate for a co-processor
         register).  */
       bool base_rules = stdarg_p (type);
-      
+
       if (user_convention)
        {
          if (user_pcs > ARM_PCS_AAPCS_LOCAL)
@@ -3901,7 +4064,7 @@ arm_get_pcs_model (const_tree type, const_tree decl)
 static void
 aapcs_vfp_cum_init (CUMULATIVE_ARGS *pcum  ATTRIBUTE_UNUSED,
                    const_tree fntype ATTRIBUTE_UNUSED,
-                   rtx libcall ATTRIBUTE_UNUSED, 
+                   rtx libcall ATTRIBUTE_UNUSED,
                    const_tree fndecl ATTRIBUTE_UNUSED)
 {
   /* Record the unallocated VFP registers.  */
@@ -4006,7 +4169,7 @@ aapcs_vfp_sub_candidate (const_tree type, enum machine_mode *modep)
 
        return count;
       }
-      
+
     case RECORD_TYPE:
       {
        int count = 0;
@@ -4101,6 +4264,11 @@ use_vfp_abi (enum arm_pcs pcs_variant, bool is_double)
          (TARGET_VFP_DOUBLE || !is_double));
 }
 
+/* Return true if an argument whose type is TYPE, or mode is MODE, is
+   suitable for passing or returning in VFP registers for the PCS
+   variant selected.  If it is, then *BASE_MODE is updated to contain
+   a machine mode describing each element of the argument's type and
+   *COUNT to hold the number of such elements.  */
 static bool
 aapcs_vfp_is_call_or_return_candidate (enum arm_pcs pcs_variant,
                                       enum machine_mode mode, const_tree type,
@@ -4108,9 +4276,20 @@ aapcs_vfp_is_call_or_return_candidate (enum arm_pcs pcs_variant,
 {
   enum machine_mode new_mode = VOIDmode;
 
-  if (GET_MODE_CLASS (mode) == MODE_FLOAT
-      || GET_MODE_CLASS (mode) == MODE_VECTOR_INT
-      || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
+  /* If we have the type information, prefer that to working things
+     out from the mode.  */
+  if (type)
+    {
+      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode);
+
+      if (ag_count > 0 && ag_count <= 4)
+       *count = ag_count;
+      else
+       return false;
+    }
+  else if (GET_MODE_CLASS (mode) == MODE_FLOAT
+          || GET_MODE_CLASS (mode) == MODE_VECTOR_INT
+          || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT)
     {
       *count = 1;
       new_mode = mode;
@@ -4120,15 +4299,6 @@ aapcs_vfp_is_call_or_return_candidate (enum arm_pcs pcs_variant,
       *count = 2;
       new_mode = (mode == DCmode ? DFmode : SFmode);
     }
-  else if (type && (mode == BLKmode || TREE_CODE (type) == VECTOR_TYPE))
-    {
-      int ag_count = aapcs_vfp_sub_candidate (type, &new_mode);
-
-      if (ag_count > 0 && ag_count <= 4)
-       *count = ag_count;
-      else
-       return false;
-    }
   else
     return false;
 
@@ -4154,7 +4324,7 @@ aapcs_vfp_is_return_candidate (enum arm_pcs pcs_variant,
 }
 
 static bool
-aapcs_vfp_is_call_candidate (CUMULATIVE_ARGS *pcum, enum machine_mode mode, 
+aapcs_vfp_is_call_candidate (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
                             const_tree type)
 {
   if (!use_vfp_abi (pcum->pcs_variant, false))
@@ -4172,7 +4342,7 @@ aapcs_vfp_allocate (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
   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)
       {
@@ -4199,10 +4369,10 @@ aapcs_vfp_allocate (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
            par = gen_rtx_PARALLEL (mode, rtvec_alloc (rcount));
            for (i = 0; i < rcount; i++)
              {
-               rtx tmp = gen_rtx_REG (rmode, 
+               rtx tmp = gen_rtx_REG (rmode,
                                       FIRST_VFP_REGNUM + regno + i * rshift);
                tmp = gen_rtx_EXPR_LIST
-                 (VOIDmode, tmp, 
+                 (VOIDmode, tmp,
                   GEN_INT (i * GET_MODE_SIZE (rmode)));
                XVECEXP (par, 0, i) = tmp;
              }
@@ -4222,7 +4392,7 @@ aapcs_vfp_allocate_return_reg (enum arm_pcs pcs_variant ATTRIBUTE_UNUSED,
                               const_tree type ATTRIBUTE_UNUSED)
 {
   if (!use_vfp_abi (pcs_variant, false))
-    return false;
+    return NULL;
 
   if (mode == BLKmode || (mode == TImode && !TARGET_NEON))
     {
@@ -4231,7 +4401,7 @@ aapcs_vfp_allocate_return_reg (enum arm_pcs pcs_variant ATTRIBUTE_UNUSED,
       int i;
       rtx par;
       int shift;
-      
+
       aapcs_vfp_is_call_or_return_candidate (pcs_variant, mode, type,
                                             &ag_mode, &count);
 
@@ -4250,7 +4420,7 @@ aapcs_vfp_allocate_return_reg (enum arm_pcs pcs_variant ATTRIBUTE_UNUSED,
       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, 
+         tmp = gen_rtx_EXPR_LIST (VOIDmode, tmp,
                                   GEN_INT (i * GET_MODE_SIZE (ag_mode)));
          XVECEXP (par, 0, i) = tmp;
        }
@@ -4287,7 +4457,7 @@ aapcs_vfp_advance (CUMULATIVE_ARGS *pcum  ATTRIBUTE_UNUSED,
    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 
+static struct
 {
   /* Initialize co-processor related state in CUMULATIVE_ARGS structure.  */
   void (*cum_init) (CUMULATIVE_ARGS *, const_tree, rtx, const_tree);
@@ -4323,7 +4493,7 @@ static struct
 #undef AAPCS_CP
 
 static int
-aapcs_select_call_coproc (CUMULATIVE_ARGS *pcum, enum machine_mode mode, 
+aapcs_select_call_coproc (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
                          const_tree type)
 {
   int i;
@@ -4363,7 +4533,7 @@ aapcs_select_return_coproc (const_tree type, const_tree fntype)
       int i;
 
       for (i = 0; i < ARM_NUM_COPROC_SLOTS; i++)
-       if (aapcs_cp_arg_layout[i].is_return_candidate (pcs_variant, 
+       if (aapcs_cp_arg_layout[i].is_return_candidate (pcs_variant,
                                                        TYPE_MODE (type),
                                                        type))
          return i;
@@ -4426,9 +4596,13 @@ aapcs_allocate_return_reg (enum machine_mode mode, const_tree type,
   return gen_rtx_REG (mode, R0_REGNUM);
 }
 
-rtx
+static rtx
 aapcs_libcall_value (enum machine_mode mode)
 {
+  if (BYTES_BIG_ENDIAN && ALL_FIXED_POINT_MODE_P (mode)
+      && GET_MODE_SIZE (mode) <= 4)
+    mode = SImode;
+
   return aapcs_allocate_return_reg (mode, NULL_TREE, NULL_TREE);
 }
 
@@ -4451,7 +4625,7 @@ aapcs_layout_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
      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)
     {
@@ -4551,7 +4725,7 @@ arm_init_cumulative_args (CUMULATIVE_ARGS *pcum, tree fntype,
     {
       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;
@@ -4628,9 +4802,10 @@ arm_needs_doubleword_align (enum machine_mode mode, const_tree type)
    indeed make it pass in the stack if necessary).  */
 
 static rtx
-arm_function_arg (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
+arm_function_arg (cumulative_args_t pcum_v, enum machine_mode mode,
                  const_tree type, bool named)
 {
+  CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
   int nregs;
 
   /* Handle the special case quickly.  Pick an arbitrary value for op2 of
@@ -4688,9 +4863,10 @@ arm_function_arg_boundary (enum machine_mode mode, const_tree type)
 }
 
 static int
-arm_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
+arm_arg_partial_bytes (cumulative_args_t pcum_v, enum machine_mode mode,
                       tree type, bool named)
 {
+  CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
   int nregs = pcum->nregs;
 
   if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
@@ -4715,9 +4891,11 @@ arm_arg_partial_bytes (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
    (TYPE is null for libcalls where that information may not be available.)  */
 
 static void
-arm_function_arg_advance (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
+arm_function_arg_advance (cumulative_args_t pcum_v, enum machine_mode mode,
                          const_tree type, bool named)
 {
+  CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
+
   if (pcum->pcs_variant <= ARM_PCS_AAPCS_LOCAL)
     {
       aapcs_layout_arg (pcum, mode, type, named);
@@ -4751,7 +4929,7 @@ arm_function_arg_advance (CUMULATIVE_ARGS *pcum, enum machine_mode mode,
    extension to the ARM ABI.  */
 
 static bool
-arm_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+arm_pass_by_reference (cumulative_args_t cum ATTRIBUTE_UNUSED,
                       enum machine_mode mode ATTRIBUTE_UNUSED,
                       const_tree type, bool named ATTRIBUTE_UNUSED)
 {
@@ -5090,6 +5268,14 @@ arm_function_ok_for_sibcall (tree decl, tree exp)
   if (IS_STACKALIGN (func_type))
     return false;
 
+  /* The AAPCS says that, on bare-metal, calls to unresolved weak
+     references should become a NOP.  Don't convert such calls into
+     sibling calls.  */
+  if (TARGET_AAPCS_BASED
+      && arm_abi == ARM_ABI_AAPCS
+      && DECL_WEAK (decl))
+    return false;
+
   /* Everything else is ok.  */
   return true;
 }
@@ -5392,11 +5578,7 @@ arm_load_pic_register (unsigned long saved_regs ATTRIBUTE_UNUSED)
 
       if (TARGET_32BIT)
        {
-         emit_insn (gen_pic_load_addr_32bit (pic_reg, pic_rtx));
-         if (TARGET_ARM)
-           emit_insn (gen_pic_add_dot_plus_eight (pic_reg, pic_reg, labelno));
-         else
-           emit_insn (gen_pic_add_dot_plus_four (pic_reg, pic_reg, labelno));
+         emit_insn (gen_pic_load_addr_unified (pic_reg, pic_rtx, labelno));
        }
       else /* TARGET_THUMB1 */
        {
@@ -5409,10 +5591,10 @@ arm_load_pic_register (unsigned long saved_regs ATTRIBUTE_UNUSED)
                                     thumb_find_work_register (saved_regs));
              emit_insn (gen_pic_load_addr_thumb1 (pic_tmp, pic_rtx));
              emit_insn (gen_movsi (pic_offset_table_rtx, pic_tmp));
+             emit_insn (gen_pic_add_dot_plus_four (pic_reg, pic_reg, labelno));
            }
          else
-           emit_insn (gen_pic_load_addr_thumb1 (pic_reg, pic_rtx));
-         emit_insn (gen_pic_add_dot_plus_four (pic_reg, pic_reg, labelno));
+           emit_insn (gen_pic_load_addr_unified (pic_reg, pic_rtx, labelno));
        }
     }
 
@@ -5442,20 +5624,7 @@ arm_pic_static_addr (rtx orig, rtx reg)
                                UNSPEC_SYMBOL_OFFSET);
   offset_rtx = gen_rtx_CONST (Pmode, offset_rtx);
 
-  if (TARGET_32BIT)
-    {
-      emit_insn (gen_pic_load_addr_32bit (reg, offset_rtx));
-      if (TARGET_ARM)
-        insn = emit_insn (gen_pic_add_dot_plus_eight (reg, reg, labelno));
-      else
-        insn = emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
-    }
-  else /* TARGET_THUMB1 */
-    {
-      emit_insn (gen_pic_load_addr_thumb1 (reg, offset_rtx));
-      insn = emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
-    }
-
+  insn = emit_insn (gen_pic_load_addr_unified (reg, offset_rtx, labelno));
   return insn;
 }
 
@@ -5498,7 +5667,7 @@ static bool
 will_be_in_index_register (const_rtx x)
 {
   /* arm.md: calculate_pic_address will split this into a register.  */
-  return GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PIC_SYM;
+  return GET_CODE (x) == UNSPEC && (XINT (x, 1) == UNSPEC_PIC_SYM);
 }
 
 /* Return nonzero if X is a valid ARM state address operand.  */
@@ -5594,7 +5763,7 @@ thumb2_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
 {
   bool use_ldrd;
   enum rtx_code code = GET_CODE (x);
-  
+
   if (arm_address_register_rtx_p (x, strict_p))
     return 1;
 
@@ -5622,7 +5791,7 @@ thumb2_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
       offset = INTVAL(addend);
       if (GET_MODE_SIZE (mode) <= 4)
        return (offset > -256 && offset < 256);
-      
+
       return (use_ldrd && offset > -1024 && offset < 1024
              && (offset & 3) == 0);
     }
@@ -5778,14 +5947,14 @@ static bool
 thumb2_index_mul_operand (rtx op)
 {
   HOST_WIDE_INT val;
-  
+
   if (GET_CODE(op) != CONST_INT)
     return false;
 
   val = INTVAL(op);
   return (val == 1 || val == 2 || val == 4 || val == 8);
 }
-  
+
 /* Return nonzero if INDEX is a valid Thumb-2 address index operand.  */
 static int
 thumb2_legitimate_index_p (enum machine_mode mode, rtx index, int strict_p)
@@ -5929,7 +6098,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.  */
-static int
+int
 thumb1_legitimate_address_p (enum machine_mode mode, rtx x, int strict_p)
 {
   /* ??? Not clear if this is right.  Experiment.  */
@@ -6116,6 +6285,7 @@ arm_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
 {
   rtx insns, label, labelno, sum;
 
+  gcc_assert (reloc != TLS_DESCSEQ);
   start_sequence ();
 
   labelno = GEN_INT (pic_labelno++);
@@ -6130,12 +6300,11 @@ arm_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
 
   if (TARGET_ARM)
     emit_insn (gen_pic_add_dot_plus_eight (reg, reg, labelno));
-  else if (TARGET_THUMB2)
-    emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
-  else /* TARGET_THUMB1 */
+  else
     emit_insn (gen_pic_add_dot_plus_four (reg, reg, labelno));
 
-  *valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX, LCT_PURE, /* LCT_CONST?  */
+  *valuep = emit_library_call_value (get_tls_get_addr (), NULL_RTX,
+                                    LCT_PURE, /* LCT_CONST?  */
                                     Pmode, 1, reg, Pmode);
 
   insns = get_insns ();
@@ -6144,6 +6313,29 @@ arm_call_tls_get_addr (rtx x, rtx reg, rtx *valuep, int reloc)
   return insns;
 }
 
+static rtx
+arm_tls_descseq_addr (rtx x, rtx reg)
+{
+  rtx labelno = GEN_INT (pic_labelno++);
+  rtx label = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, labelno), UNSPEC_PIC_LABEL);
+  rtx sum = gen_rtx_UNSPEC (Pmode,
+                           gen_rtvec (4, x, GEN_INT (TLS_DESCSEQ),
+                                      gen_rtx_CONST (VOIDmode, label),
+                                      GEN_INT (!TARGET_ARM)),
+                           UNSPEC_TLS);
+  rtx reg0 = load_tls_operand (sum, gen_rtx_REG (SImode, 0));
+
+  emit_insn (gen_tlscall (x, labelno));
+  if (!reg)
+    reg = gen_reg_rtx (SImode);
+  else
+    gcc_assert (REGNO (reg) != 0);
+
+  emit_move_insn (reg, reg0);
+
+  return reg;
+}
+
 rtx
 legitimize_tls_address (rtx x, rtx reg)
 {
@@ -6153,26 +6345,51 @@ legitimize_tls_address (rtx x, rtx reg)
   switch (model)
     {
     case TLS_MODEL_GLOBAL_DYNAMIC:
-      insns = arm_call_tls_get_addr (x, reg, &ret, TLS_GD32);
-      dest = gen_reg_rtx (Pmode);
-      emit_libcall_block (insns, dest, ret, x);
+      if (TARGET_GNU2_TLS)
+       {
+         reg = arm_tls_descseq_addr (x, reg);
+
+         tp = arm_load_tp (NULL_RTX);
+
+         dest = gen_rtx_PLUS (Pmode, tp, reg);
+       }
+      else
+       {
+         /* Original scheme */
+         insns = arm_call_tls_get_addr (x, reg, &ret, TLS_GD32);
+         dest = gen_reg_rtx (Pmode);
+         emit_libcall_block (insns, dest, ret, x);
+       }
       return dest;
 
     case TLS_MODEL_LOCAL_DYNAMIC:
-      insns = arm_call_tls_get_addr (x, reg, &ret, TLS_LDM32);
+      if (TARGET_GNU2_TLS)
+       {
+         reg = arm_tls_descseq_addr (x, reg);
 
-      /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
-        share the LDM result with other LD model accesses.  */
-      eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx),
-                           UNSPEC_TLS);
-      dest = gen_reg_rtx (Pmode);
-      emit_libcall_block (insns, dest, ret, eqv);
+         tp = arm_load_tp (NULL_RTX);
+
+         dest = gen_rtx_PLUS (Pmode, tp, reg);
+       }
+      else
+       {
+         insns = arm_call_tls_get_addr (x, reg, &ret, TLS_LDM32);
 
-      /* Load the addend.  */
-      addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x, GEN_INT (TLS_LDO32)),
-                              UNSPEC_TLS);
-      addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
-      return gen_rtx_PLUS (Pmode, dest, addend);
+         /* Attach a unique REG_EQUIV, to allow the RTL optimizers to
+            share the LDM result with other LD model accesses.  */
+         eqv = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const1_rtx),
+                               UNSPEC_TLS);
+         dest = gen_reg_rtx (Pmode);
+         emit_libcall_block (insns, dest, ret, eqv);
+
+         /* Load the addend.  */
+         addend = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, x,
+                                                    GEN_INT (TLS_LDO32)),
+                                  UNSPEC_TLS);
+         addend = force_reg (SImode, gen_rtx_CONST (SImode, addend));
+         dest = gen_rtx_PLUS (Pmode, dest, addend);
+       }
+      return dest;
 
     case TLS_MODEL_INITIAL_EXEC:
       labelno = GEN_INT (pic_labelno++);
@@ -6411,37 +6628,165 @@ arm_legitimize_reload_address (rtx *p,
                               int opnum, int type,
                               int ind_levels ATTRIBUTE_UNUSED)
 {
+  /* We must recognize output that we have already generated ourselves.  */
+  if (GET_CODE (*p) == PLUS
+      && GET_CODE (XEXP (*p, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (*p, 0), 0)) == REG
+      && GET_CODE (XEXP (XEXP (*p, 0), 1)) == CONST_INT
+      && GET_CODE (XEXP (*p, 1)) == CONST_INT)
+    {
+      push_reload (XEXP (*p, 0), NULL_RTX, &XEXP (*p, 0), NULL,
+                  MODE_BASE_REG_CLASS (mode), GET_MODE (*p),
+                  VOIDmode, 0, 0, opnum, (enum reload_type) type);
+      return true;
+    }
+
   if (GET_CODE (*p) == PLUS
       && GET_CODE (XEXP (*p, 0)) == REG
       && ARM_REGNO_OK_FOR_BASE_P (REGNO (XEXP (*p, 0)))
+      /* If the base register is equivalent to a constant, let the generic
+        code handle it.  Otherwise we will run into problems if a future
+        reload pass decides to rematerialize the constant.  */
+      && !reg_equiv_constant (ORIGINAL_REGNO (XEXP (*p, 0)))
       && GET_CODE (XEXP (*p, 1)) == CONST_INT)
     {
       HOST_WIDE_INT val = INTVAL (XEXP (*p, 1));
       HOST_WIDE_INT low, high;
 
-      if (mode == DImode || (mode == DFmode && TARGET_SOFT_FLOAT))
-       low = ((val & 0xf) ^ 0x8) - 0x8;
-      else if (TARGET_MAVERICK && TARGET_HARD_FLOAT)
-       /* Need to be careful, -256 is not a valid offset.  */
-       low = val >= 0 ? (val & 0xff) : -((-val) & 0xff);
-      else if (mode == SImode
-              || (mode == SFmode && TARGET_SOFT_FLOAT)
-              || ((mode == HImode || mode == QImode) && ! arm_arch4))
-       /* Need to be careful, -4096 is not a valid offset.  */
-       low = val >= 0 ? (val & 0xfff) : -((-val) & 0xfff);
-      else if ((mode == HImode || mode == QImode) && arm_arch4)
-       /* Need to be careful, -256 is not a valid offset.  */
-       low = val >= 0 ? (val & 0xff) : -((-val) & 0xff);
-      else if (GET_MODE_CLASS (mode) == MODE_FLOAT
-              && TARGET_HARD_FLOAT && TARGET_FPA)
-       /* Need to be careful, -1024 is not a valid offset.  */
-       low = val >= 0 ? (val & 0x3ff) : -((-val) & 0x3ff);
-      else
+      /* Detect coprocessor load/stores.  */
+      bool coproc_p = ((TARGET_HARD_FLOAT
+                       && (TARGET_VFP || TARGET_FPA || TARGET_MAVERICK)
+                       && (mode == SFmode || mode == DFmode
+                           || (mode == DImode && TARGET_MAVERICK)))
+                      || (TARGET_REALLY_IWMMXT
+                          && VALID_IWMMXT_REG_MODE (mode))
+                      || (TARGET_NEON
+                          && (VALID_NEON_DREG_MODE (mode)
+                              || VALID_NEON_QREG_MODE (mode))));
+
+      /* For some conditions, bail out when lower two bits are unaligned.  */
+      if ((val & 0x3) != 0
+         /* Coprocessor load/store indexes are 8-bits + '00' appended.  */
+         && (coproc_p
+             /* For DI, and DF under soft-float: */
+             || ((mode == DImode || mode == DFmode)
+                 /* Without ldrd, we use stm/ldm, which does not
+                    fair well with unaligned bits.  */
+                 && (! TARGET_LDRD
+                     /* Thumb-2 ldrd/strd is [-1020,+1020] in steps of 4.  */
+                     || TARGET_THUMB2))))
        return false;
 
-      high = ((((val - low) & (unsigned HOST_WIDE_INT) 0xffffffff)
-              ^ (unsigned HOST_WIDE_INT) 0x80000000)
-             - (unsigned HOST_WIDE_INT) 0x80000000);
+      /* When breaking down a [reg+index] reload address into [(reg+high)+low],
+        of which the (reg+high) gets turned into a reload add insn,
+        we try to decompose the index into high/low values that can often
+        also lead to better reload CSE.
+        For example:
+                ldr r0, [r2, #4100]  // Offset too large
+                ldr r1, [r2, #4104]  // Offset too large
+
+        is best reloaded as:
+                add t1, r2, #4096
+                ldr r0, [t1, #4]
+                add t2, r2, #4096
+                ldr r1, [t2, #8]
+
+        which post-reload CSE can simplify in most cases to eliminate the
+        second add instruction:
+                add t1, r2, #4096
+                ldr r0, [t1, #4]
+                ldr r1, [t1, #8]
+
+        The idea here is that we want to split out the bits of the constant
+        as a mask, rather than as subtracting the maximum offset that the
+        respective type of load/store used can handle.
+
+        When encountering negative offsets, we can still utilize it even if
+        the overall offset is positive; sometimes this may lead to an immediate
+        that can be constructed with fewer instructions.
+        For example:
+                ldr r0, [r2, #0x3FFFFC]
+
+        This is best reloaded as:
+                add t1, r2, #0x400000
+                ldr r0, [t1, #-4]
+
+        The trick for spotting this for a load insn with N bits of offset
+        (i.e. bits N-1:0) is to look at bit N; if it is set, then chose a
+        negative offset that is going to make bit N and all the bits below
+        it become zero in the remainder part.
+
+        The SIGN_MAG_LOW_ADDR_BITS macro below implements this, with respect
+        to sign-magnitude addressing (i.e. separate +- bit, or 1's complement),
+        used in most cases of ARM load/store instructions.  */
+
+#define SIGN_MAG_LOW_ADDR_BITS(VAL, N)                                 \
+      (((VAL) & ((1 << (N)) - 1))                                      \
+       ? (((VAL) & ((1 << ((N) + 1)) - 1)) ^ (1 << (N))) - (1 << (N))  \
+       : 0)
+
+      if (coproc_p)
+       {
+         low = SIGN_MAG_LOW_ADDR_BITS (val, 10);
+
+         /* NEON quad-word load/stores are made of two double-word accesses,
+            so the valid index range is reduced by 8. Treat as 9-bit range if
+            we go over it.  */
+         if (TARGET_NEON && VALID_NEON_QREG_MODE (mode) && low >= 1016)
+           low = SIGN_MAG_LOW_ADDR_BITS (val, 9);
+       }
+      else if (GET_MODE_SIZE (mode) == 8)
+       {
+         if (TARGET_LDRD)
+           low = (TARGET_THUMB2
+                  ? SIGN_MAG_LOW_ADDR_BITS (val, 10)
+                  : SIGN_MAG_LOW_ADDR_BITS (val, 8));
+         else
+           /* For pre-ARMv5TE (without ldrd), we use ldm/stm(db/da/ib)
+              to access doublewords. The supported load/store offsets are
+              -8, -4, and 4, which we try to produce here.  */
+           low = ((val & 0xf) ^ 0x8) - 0x8;
+       }
+      else if (GET_MODE_SIZE (mode) < 8)
+       {
+         /* NEON element load/stores do not have an offset.  */
+         if (TARGET_NEON_FP16 && mode == HFmode)
+           return false;
+
+         if (TARGET_THUMB2)
+           {
+             /* Thumb-2 has an asymmetrical index range of (-256,4096).
+                Try the wider 12-bit range first, and re-try if the result
+                is out of range.  */
+             low = SIGN_MAG_LOW_ADDR_BITS (val, 12);
+             if (low < -255)
+               low = SIGN_MAG_LOW_ADDR_BITS (val, 8);
+           }
+         else
+           {
+             if (mode == HImode || mode == HFmode)
+               {
+                 if (arm_arch4)
+                   low = SIGN_MAG_LOW_ADDR_BITS (val, 8);
+                 else
+                   {
+                     /* The storehi/movhi_bytes fallbacks can use only
+                        [-4094,+4094] of the full ldrb/strb index range.  */
+                     low = SIGN_MAG_LOW_ADDR_BITS (val, 12);
+                     if (low == 4095 || low == -4095)
+                       return false;
+                   }
+               }
+             else
+               low = SIGN_MAG_LOW_ADDR_BITS (val, 12);
+           }
+       }
+      else
+       return false;
+
+      high = ((((val - low) & (unsigned HOST_WIDE_INT) 0xffffffff)
+              ^ (unsigned HOST_WIDE_INT) 0x80000000)
+             - (unsigned HOST_WIDE_INT) 0x80000000);
       /* Check for overflow or zero */
       if (low == 0 || high == 0 || (high + low != val))
        return false;
@@ -6548,10 +6893,48 @@ arm_tls_referenced_p (rtx x)
   return for_each_rtx (&x, arm_tls_operand_p_1, NULL);
 }
 
+/* Implement TARGET_LEGITIMATE_CONSTANT_P.
+
+   On the ARM, allow any integer (invalid ones are removed later by insn
+   patterns), nice doubles and symbol_refs which refer to the function's
+   constant pool XXX.
+
+   When generating pic allow anything.  */
+
+static bool
+arm_legitimate_constant_p_1 (enum machine_mode mode, rtx x)
+{
+  /* At present, we have no support for Neon structure constants, so forbid
+     them here.  It might be possible to handle simple cases like 0 and -1
+     in future.  */
+  if (TARGET_NEON && VALID_NEON_STRUCT_MODE (mode))
+    return false;
+
+  return flag_pic || !label_mentioned_p (x);
+}
+
+static bool
+thumb_legitimate_constant_p (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
+{
+  return (GET_CODE (x) == CONST_INT
+         || GET_CODE (x) == CONST_DOUBLE
+         || CONSTANT_ADDRESS_P (x)
+         || flag_pic);
+}
+
+static bool
+arm_legitimate_constant_p (enum machine_mode mode, rtx x)
+{
+  return (!arm_cannot_force_const_mem (mode, x)
+         && (TARGET_32BIT
+             ? arm_legitimate_constant_p_1 (mode, x)
+             : thumb_legitimate_constant_p (mode, x)));
+}
+
 /* Implement TARGET_CANNOT_FORCE_CONST_MEM.  */
 
-bool
-arm_cannot_force_const_mem (rtx x)
+static bool
+arm_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
 {
   rtx base, offset;
 
@@ -6728,7 +7111,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+       *total = rtx_cost (XEXP (x, 1), code, 1, speed);
 
       /* Fall through */
     case ROTATERT:
@@ -6740,7 +7123,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
 
       /* Fall through */
     case ASHIFT: case LSHIFTRT: case ASHIFTRT:
-      *total += rtx_cost (XEXP (x, 0), code, speed);
+      *total += rtx_cost (XEXP (x, 0), code, 0, speed);
       if (mode == DImode)
        {
          *total += COSTS_N_INSNS (3);
@@ -6763,14 +7146,14 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
          if (GET_CODE (XEXP (x, 0)) == CONST_INT
              && const_ok_for_arm (INTVAL (XEXP (x, 0))))
            {
-             *total += rtx_cost (XEXP (x, 1), code, speed);
+             *total += rtx_cost (XEXP (x, 1), code, 1, 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);
+             *total += rtx_cost (XEXP (x, 0), code, 0, speed);
              return true;
            }
 
@@ -6787,14 +7170,14 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
              if (GET_CODE (XEXP (x, 0)) == CONST_DOUBLE
                  && arm_const_double_rtx (XEXP (x, 0)))
                {
-                 *total += rtx_cost (XEXP (x, 1), code, speed);
+                 *total += rtx_cost (XEXP (x, 1), code, 1, 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);
+                 *total += rtx_cost (XEXP (x, 0), code, 0, speed);
                  return true;
                }
 
@@ -6808,7 +7191,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       if (GET_CODE (XEXP (x, 0)) == CONST_INT
          && const_ok_for_arm (INTVAL (XEXP (x, 0))))
        {
-         *total += rtx_cost (XEXP (x, 1), code, speed);
+         *total += rtx_cost (XEXP (x, 1), code, 1, speed);
          return true;
        }
 
@@ -6817,8 +7200,8 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
          || subcode == LSHIFTRT
          || subcode == ROTATE || subcode == ROTATERT)
        {
-         *total += rtx_cost (XEXP (x, 0), code, speed);
-         *total += rtx_cost (XEXP (XEXP (x, 1), 0), subcode, speed);
+         *total += rtx_cost (XEXP (x, 0), code, 0, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 1), 0), subcode, 0, speed);
          return true;
        }
 
@@ -6826,23 +7209,23 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), code, 0, speed);
+         *total += rtx_cost (XEXP (x, 1), code, 1, 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);
+         *total += rtx_cost (XEXP (x, 0), code, 0, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 1), 0), subcode, 0, 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);
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, 0, speed);
          if (GET_CODE (XEXP (XEXP (x, 1), 0)) == REG
              && REGNO (XEXP (XEXP (x, 1), 0)) != CC_REGNUM)
            *total += COSTS_N_INSNS (1);
@@ -6859,8 +7242,8 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
        {
          *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);
+                             0, speed);
+         *total += rtx_cost (XEXP (x, 1), code, 1, speed);
          return true;
        }
 
@@ -6884,7 +7267,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
              if (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
                  && arm_const_double_rtx (XEXP (x, 1)))
                {
-                 *total += rtx_cost (XEXP (x, 0), code, speed);
+                 *total += rtx_cost (XEXP (x, 0), code, 0, speed);
                  return true;
                }
 
@@ -6898,7 +7281,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 1), code, 1, speed);
          if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
              && REGNO (XEXP (XEXP (x, 0), 0)) != CC_REGNUM)
            *total += COSTS_N_INSNS (1);
@@ -6925,7 +7308,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
          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);
+             *total += rtx_cost (XEXP (x, 0), code, 0, speed);
              return true;
            }
 
@@ -6936,7 +7319,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+         *total += rtx_cost (XEXP (x, 0), code, 0, speed);
          return true;
        }
       subcode = GET_CODE (XEXP (x, 0));
@@ -6944,16 +7327,16 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
          || subcode == LSHIFTRT
          || subcode == ROTATE || subcode == ROTATERT)
        {
-         *total += rtx_cost (XEXP (x, 1), code, speed);
-         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+         *total += rtx_cost (XEXP (x, 1), code, 1, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, 0, 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);
+         *total += rtx_cost (XEXP (x, 1), code, 1, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, 0, speed);
          return true;
        }
 
@@ -6979,7 +7362,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
          && (GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == ZERO_EXTEND
              || GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == SIGN_EXTEND))
        {
-         *total = rtx_cost (XEXP (XEXP (x, 0), 0), LSHIFTRT, speed);
+         *total = rtx_cost (XEXP (XEXP (x, 0), 0), LSHIFTRT, 0, speed);
          return true;
        }
       *total = COSTS_N_INSNS (2); /* Plus the cost of the MULT */
@@ -7011,11 +7394,11 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
              || (subcode == MULT
                  && power_of_two_operand (XEXP (XEXP (x, 0), 1), SImode)))
            {
-             *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+             *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, 0, 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);
+                                                       subcode, 1, speed);
              return true;
            }
        }
@@ -7036,14 +7419,14 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
            && 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));
+      *total += (rtx_cost (XEXP (x, 1), code, 1, speed)
+                + rtx_cost (XEXP (x, 2), code, 2, 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);
+         *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, 0, speed);
          return true;
        }
       goto scc_insn;
@@ -7052,7 +7435,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+         *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, 0, speed);
          return true;
        }
       goto scc_insn;
@@ -7061,7 +7444,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, 0, speed);
          return true;
        }
       goto scc_insn;
@@ -7102,7 +7485,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
       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);
+         *total += rtx_cost (XEXP (x, 0), code, 0, speed);
          return true;
        }
 
@@ -7111,29 +7494,29 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
          || subcode == LSHIFTRT
          || subcode == ROTATE || subcode == ROTATERT)
        {
-         *total += rtx_cost (XEXP (x, 1), code, speed);
-         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, speed);
+         *total += rtx_cost (XEXP (x, 1), code, 1, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, 0, 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);
+         *total += rtx_cost (XEXP (x, 1), code, 1, speed);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), subcode, 0, 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);
+      *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, 0, 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);
+       *total += rtx_cost (XEXP (x, 1), code, 1, speed);
       return true;
 
     case ABS:
@@ -7210,7 +7593,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
 
     case ZERO_EXTRACT:
     case SIGN_EXTRACT:
-      *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, speed);
+      *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, 0, speed);
       return true;
 
     case CONST_INT:
@@ -7235,7 +7618,7 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
 
     case LO_SUM:
       *total = COSTS_N_INSNS (1);
-      *total += rtx_cost (XEXP (x, 0), code, speed);
+      *total += rtx_cost (XEXP (x, 0), code, 0, speed);
       return true;
 
     case CONST_DOUBLE:
@@ -7246,6 +7629,18 @@ arm_rtx_costs_1 (rtx x, enum rtx_code outer, int* total, bool speed)
        *total = COSTS_N_INSNS (4);
       return true;
 
+    case SET:
+      return false;
+      
+    case UNSPEC:
+      /* We cost this as high as our memory costs to allow this to
+        be hoisted from loops.  */
+      if (XINT (x, 1) == UNSPEC_PIC_UNIFIED)
+       {
+         *total = COSTS_N_INSNS (2 + ARM_NUM_REGS (mode));
+       }
+      return true;
+
     default:
       *total = COSTS_N_INSNS (4);
       return false;
@@ -7421,7 +7816,7 @@ arm_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
     case ROTATE:
       if (mode == SImode && GET_CODE (XEXP (x, 1)) == REG)
        {
-         *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, false);
+         *total = COSTS_N_INSNS (2) + rtx_cost (XEXP (x, 0), code, 0, false);
          return true;
        }
       /* Fall through */
@@ -7431,15 +7826,15 @@ arm_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
     case ASHIFTRT:
       if (mode == DImode && GET_CODE (XEXP (x, 1)) == CONST_INT)
        {
-         *total = COSTS_N_INSNS (3) + rtx_cost (XEXP (x, 0), code, false);
+         *total = COSTS_N_INSNS (3) + rtx_cost (XEXP (x, 0), code, 0, false);
          return true;
        }
       else if (mode == SImode)
        {
-         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, false);
+         *total = COSTS_N_INSNS (1) + rtx_cost (XEXP (x, 0), code, 0, false);
          /* Slightly disparage register shifts, but not by much.  */
          if (GET_CODE (XEXP (x, 1)) != CONST_INT)
-           *total += 1 + rtx_cost (XEXP (x, 1), code, false);
+           *total += 1 + rtx_cost (XEXP (x, 1), code, 1, false);
          return true;
        }
 
@@ -7491,8 +7886,8 @@ arm_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
          && 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);
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), code, 0, false);
+         *total += rtx_cost (XEXP (x, 1), code, 1, false);
          return true;
        }
 
@@ -7593,6 +7988,9 @@ arm_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
       *total = COSTS_N_INSNS (1) + 1;
       return true;
 
+    case SET:
+      return false;
+
     default:
       if (mode != VOIDmode)
        *total = COSTS_N_INSNS (ARM_NUM_REGS (mode));
@@ -7604,8 +8002,8 @@ arm_size_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
 
 /* 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 opno ATTRIBUTE_UNUSED,
+              int *total, bool speed)
 {
   if (!speed)
     return arm_size_rtx_costs (x, (enum rtx_code) code,
@@ -7658,7 +8056,7 @@ arm_slowmul_rtx_costs (rtx x, enum rtx_code code, enum rtx_code outer_code,
            }
 
          *total = COSTS_N_INSNS (cost);
-         *total += rtx_cost (XEXP (x, 0), code, speed);
+         *total += rtx_cost (XEXP (x, 0), code, 0, speed);
          return true;
        }
 
@@ -8027,9 +8425,9 @@ cortex_a9_sched_adjust_cost (rtx insn, rtx link, rtx dep, int * cost)
          {
            if (GET_CODE (PATTERN (insn)) == SET)
              {
-               if (GET_MODE_CLASS 
+               if (GET_MODE_CLASS
                    (GET_MODE (SET_DEST (PATTERN (insn)))) == MODE_FLOAT
-                 || GET_MODE_CLASS 
+                 || GET_MODE_CLASS
                    (GET_MODE (SET_SRC (PATTERN (insn)))) == MODE_FLOAT)
                  {
                    enum attr_type attr_type_insn = get_attr_type (insn);
@@ -8049,7 +8447,7 @@ cortex_a9_sched_adjust_cost (rtx insn, rtx link, rtx dep, int * cost)
                      {
                        /* FMACS is a special case where the dependant
                           instruction can be issued 3 cycles before
-                          the normal latency in case of an output 
+                          the normal latency in case of an output
                           dependency.  */
                        if ((attr_type_insn == TYPE_FMACS
                             || attr_type_insn == TYPE_FMACD)
@@ -8113,12 +8511,69 @@ fa726te_sched_adjust_cost (rtx insn, rtx link, rtx dep, int * cost)
   return true;
 }
 
+/* Implement TARGET_REGISTER_MOVE_COST.
+
+   Moves between FPA_REGS and GENERAL_REGS are two memory insns.
+   Moves between VFP_REGS and GENERAL_REGS are a single insn, but
+   it is typically more expensive than a single memory access.  We set
+   the cost to less than two memory accesses so that floating
+   point to integer conversion does not go through memory.  */
+
+int
+arm_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                       reg_class_t from, reg_class_t to)
+{
+  if (TARGET_32BIT)
+    {
+      if ((from == FPA_REGS && to != FPA_REGS)
+         || (from != FPA_REGS && to == FPA_REGS))
+       return 20;
+      else if ((IS_VFP_CLASS (from) && !IS_VFP_CLASS (to))
+              || (!IS_VFP_CLASS (from) && IS_VFP_CLASS (to)))
+       return 15;
+      else if ((from == IWMMXT_REGS && to != IWMMXT_REGS)
+              || (from != IWMMXT_REGS && to == IWMMXT_REGS))
+       return 4;
+      else if (from == IWMMXT_GR_REGS || to == IWMMXT_GR_REGS)
+       return 20;
+      else if ((from == CIRRUS_REGS && to != CIRRUS_REGS)
+              || (from != CIRRUS_REGS && to == CIRRUS_REGS))
+       return 20;
+      else
+       return 2;
+    }
+  else
+    {
+      if (from == HI_REGS || to == HI_REGS)
+       return 4;
+      else
+       return 2;
+    }
+}
+
+/* Implement TARGET_MEMORY_MOVE_COST.  */
+
+int
+arm_memory_move_cost (enum machine_mode mode, reg_class_t rclass,
+                     bool in ATTRIBUTE_UNUSED)
+{
+  if (TARGET_32BIT)
+    return 10;
+  else
+    {
+      if (GET_MODE_SIZE (mode) < 4)
+       return 8;
+      else
+       return ((2 * GET_MODE_SIZE (mode)) * (rclass == LO_REGS ? 1 : 2));
+    }
+}
+
 /* This function implements the target macro TARGET_SCHED_ADJUST_COST.
    It corrects the value of COST based on the relationship between
    INSN and DEP through the dependence LINK.  It returns the new
    value. There is a per-core adjust_cost hook to adjust scheduler costs
-   and the per-core hook can choose to completely override the generic 
-   adjust_cost function. Only put bits of code into arm_adjust_cost that 
+   and the per-core hook can choose to completely override the generic
+   adjust_cost function. Only put bits of code into arm_adjust_cost that
    are common across all cores.  */
 static int
 arm_adjust_cost (rtx insn, rtx link, rtx dep, int cost)
@@ -8162,7 +8617,7 @@ arm_adjust_cost (rtx insn, rtx link, rtx dep, int cost)
         constant pool are cached, and that others will miss.  This is a
         hack.  */
 
-      if ((GET_CODE (src_mem) == SYMBOL_REF 
+      if ((GET_CODE (src_mem) == SYMBOL_REF
           && CONSTANT_POOL_ADDRESS_P (src_mem))
          || reg_mentioned_p (stack_pointer_rtx, src_mem)
          || reg_mentioned_p (frame_pointer_rtx, src_mem)
@@ -8173,6 +8628,21 @@ arm_adjust_cost (rtx insn, rtx link, rtx dep, int cost)
   return cost;
 }
 
+static int
+arm_default_branch_cost (bool speed_p, bool predictable_p ATTRIBUTE_UNUSED)
+{
+  if (TARGET_32BIT)
+    return (TARGET_THUMB2 && !speed_p) ? 1 : 4;
+  else
+    return (optimize > 0) ? 2 : 0;
+}
+
+static int
+arm_cortex_a5_branch_cost (bool speed_p, bool predictable_p)
+{
+  return speed_p ? 0 : arm_default_branch_cost (speed_p, predictable_p);
+}
+
 static int fp_consts_inited = 0;
 
 /* Only zero is valid for VFP.  Other values are also valid for FPA.  */
@@ -8630,6 +9100,66 @@ neon_immediate_valid_for_logic (rtx op, enum machine_mode mode, int inverse,
   return 1;
 }
 
+/* Return TRUE if rtx OP is legal for use in a VSHR or VSHL instruction.  If
+   the immediate is valid, write a constant suitable for using as an operand
+   to VSHR/VSHL to *MODCONST and the corresponding element width to
+   *ELEMENTWIDTH. ISLEFTSHIFT is for determine left or right shift,
+   because they have different limitations.  */
+
+int
+neon_immediate_valid_for_shift (rtx op, enum machine_mode mode,
+                               rtx *modconst, int *elementwidth,
+                               bool isleftshift)
+{
+  unsigned int innersize = GET_MODE_SIZE (GET_MODE_INNER (mode));
+  unsigned int n_elts = CONST_VECTOR_NUNITS (op), i;
+  unsigned HOST_WIDE_INT last_elt = 0;
+  unsigned HOST_WIDE_INT maxshift;
+
+  /* Split vector constant out into a byte vector.  */
+  for (i = 0; i < n_elts; i++)
+    {
+      rtx el = CONST_VECTOR_ELT (op, i);
+      unsigned HOST_WIDE_INT elpart;
+
+      if (GET_CODE (el) == CONST_INT)
+        elpart = INTVAL (el);
+      else if (GET_CODE (el) == CONST_DOUBLE)
+        return 0;
+      else
+        gcc_unreachable ();
+
+      if (i != 0 && elpart != last_elt)
+        return 0;
+
+      last_elt = elpart;
+    }
+
+  /* Shift less than element size.  */
+  maxshift = innersize * 8;
+
+  if (isleftshift)
+    {
+      /* Left shift immediate value can be from 0 to <size>-1.  */
+      if (last_elt >= maxshift)
+        return 0;
+    }
+  else
+    {
+      /* Right shift immediate value can be from 1 to <size>.  */
+      if (last_elt == 0 || last_elt > maxshift)
+       return 0;
+    }
+
+  if (elementwidth)
+    *elementwidth = innersize * 8;
+
+  if (modconst)
+    *modconst = CONST_VECTOR_ELT (op, 0);
+
+  return 1;
+}
+
 /* Return a string suitable for output of Neon immediate logic operation
    MNEM.  */
 
@@ -8652,6 +9182,28 @@ neon_output_logic_immediate (const char *mnem, rtx *op2, enum machine_mode mode,
   return templ;
 }
 
+/* Return a string suitable for output of Neon immediate shift operation
+   (VSHR or VSHL) MNEM.  */
+
+char *
+neon_output_shift_immediate (const char *mnem, char sign, rtx *op2,
+                            enum machine_mode mode, int quad,
+                            bool isleftshift)
+{
+  int width, is_valid;
+  static char templ[40];
+
+  is_valid = neon_immediate_valid_for_shift (*op2, mode, op2, &width, isleftshift);
+  gcc_assert (is_valid != 0);
+
+  if (quad)
+    sprintf (templ, "%s.%c%d\t%%q0, %%q1, %%2", mnem, sign, width);
+  else
+    sprintf (templ, "%s.%c%d\t%%P0, %%P1, %%2", mnem, sign, width);
+
+  return templ;
+}
+
 /* Output a sequence of pairwise operations to implement a reduction.
    NOTE: We do "too much work" here, because pairwise operations work on two
    registers-worth of operands in one go. Unfortunately we can't exploit those
@@ -9124,6 +9676,11 @@ neon_struct_mem_operand (rtx op)
   if (GET_CODE (ind) == REG)
     return arm_address_register_rtx_p (ind, 0);
 
+  /* vldm/vstm allows POST_INC (ia) and PRE_DEC (db).  */
+  if (GET_CODE (ind) == POST_INC
+      || GET_CODE (ind) == PRE_DEC)
+    return arm_address_register_rtx_p (XEXP (ind, 0), 0);
+
   return FALSE;
 }
 
@@ -9155,7 +9712,7 @@ coproc_secondary_reload_class (enum machine_mode mode, rtx x, bool wb)
   /* The neon move patterns handle all legitimate vector and struct
      addresses.  */
   if (TARGET_NEON
-      && MEM_P (x)
+      && (MEM_P (x) || GET_CODE (x) == CONST_VECTOR)
       && (GET_MODE_CLASS (mode) == MODE_VECTOR_INT
          || GET_MODE_CLASS (mode) == MODE_VECTOR_FLOAT
          || VALID_NEON_STRUCT_MODE (mode)))
@@ -9175,8 +9732,9 @@ arm_return_in_msb (const_tree valtype)
 {
   return (TARGET_AAPCS_BASED
           && BYTES_BIG_ENDIAN
-          && (AGGREGATE_TYPE_P (valtype)
-              || TREE_CODE (valtype) == COMPLEX_TYPE));
+         && (AGGREGATE_TYPE_P (valtype)
+             || TREE_CODE (valtype) == COMPLEX_TYPE
+             || FIXED_POINT_TYPE_P (valtype)));
 }
 
 /* Returns TRUE if INSN is an "LDR REG, ADDR" instruction.
@@ -9442,7 +10000,8 @@ static int
 arm_note_pic_base (rtx *x, void *date ATTRIBUTE_UNUSED)
 {
   if (GET_CODE (*x) == UNSPEC
-      && XINT (*x, 1) == UNSPEC_PIC_BASE)
+      && (XINT (*x, 1) == UNSPEC_PIC_BASE
+         || XINT (*x, 1) == UNSPEC_PIC_UNIFIED))
     return 1;
   return 0;
 }
@@ -9450,6 +10009,11 @@ arm_note_pic_base (rtx *x, void *date ATTRIBUTE_UNUSED)
 static bool
 arm_cannot_copy_insn_p (rtx insn)
 {
+  /* The tls call insn cannot be copied, as it is paired with a data
+     word.  */
+  if (recog_memoized (insn) == CODE_FOR_tlscall)
+    return true;
+
   return for_each_rtx (&PATTERN (insn), arm_note_pic_base, NULL);
 }
 
@@ -9662,7 +10226,7 @@ compute_offset_order (int nops, HOST_WIDE_INT *unsorted_offsets, int *order,
    from that base register.
    REGS is an array filled in with the destination register numbers.
    SAVED_ORDER (if nonnull), is an array filled in with an order that maps
-   insn numbers to to an ascending order of stores.  If CHECK_REGS is true,
+   insn numbers to an ascending order of stores.  If CHECK_REGS is true,
    the sequence of registers in REGS matches the loads from ascending memory
    locations, and the function verifies that the register numbers are
    themselves ascending.  If CHECK_REGS is false, the register numbers
@@ -9810,7 +10374,7 @@ load_multiple_sequence (rtx *operands, int nops, int *regs, int *saved_order,
    array filled in with the source register numbers, REG_RTXS (if nonnull) is
    likewise filled with the corresponding rtx's.
    SAVED_ORDER (if nonnull), is an array filled in with an order that maps insn
-   numbers to to an ascending order of stores.
+   numbers to an ascending order of stores.
    If CHECK_REGS is true, the sequence of registers in *REGS matches the stores
    from ascending memory locations, and the function verifies that the register
    numbers are themselves ascending.  If CHECK_REGS is false, the register
@@ -9828,6 +10392,9 @@ store_multiple_sequence (rtx *operands, int nops, int nops_total,
   rtx base_reg_rtx = NULL;
   int i, stm_case;
 
+  /* Write back of base register is currently only supported for Thumb 1.  */
+  int base_writeback = TARGET_THUMB1;
+
   /* Can only handle up to MAX_LDM_STM_OPS insns at present, though could be
      easily extended if required.  */
   gcc_assert (nops >= 2 && nops <= MAX_LDM_STM_OPS);
@@ -9885,7 +10452,9 @@ store_multiple_sequence (rtx *operands, int nops, int nops_total,
          /* If it isn't an integer register, then we can't do this.  */
          if (unsorted_regs[i] < 0
              || (TARGET_THUMB1 && unsorted_regs[i] > LAST_LO_REGNUM)
-             || (TARGET_THUMB2 && unsorted_regs[i] == base_reg)
+             /* The effects are unpredictable if the base register is
+                both updated and stored.  */
+             || (base_writeback && unsorted_regs[i] == base_reg)
              || (TARGET_THUMB2 && unsorted_regs[i] == SP_REGNUM)
              || unsorted_regs[i] > 14)
            return 0;
@@ -10347,105 +10916,439 @@ gen_const_stm_seq (rtx *operands, int nops)
   return true;
 }
 
-int
-arm_gen_movmemqi (rtx *operands)
-{
-  HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes;
-  HOST_WIDE_INT srcoffset, dstoffset;
-  int i;
-  rtx src, dst, srcbase, dstbase;
-  rtx part_bytes_reg = NULL;
-  rtx mem;
+/* Copy a block of memory using plain ldr/str/ldrh/strh instructions, to permit
+   unaligned copies on processors which support unaligned semantics for those
+   instructions.  INTERLEAVE_FACTOR can be used to attempt to hide load latency
+   (using more registers) by doing e.g. load/load/store/store for a factor of 2.
+   An interleave factor of 1 (the minimum) will perform no interleaving. 
+   Load/store multiple are used for aligned addresses where possible.  */
 
-  if (GET_CODE (operands[2]) != CONST_INT
-      || GET_CODE (operands[3]) != CONST_INT
-      || INTVAL (operands[2]) > 64
-      || INTVAL (operands[3]) & 3)
-    return 0;
+static void
+arm_block_move_unaligned_straight (rtx dstbase, rtx srcbase,
+                                  HOST_WIDE_INT length,
+                                  unsigned int interleave_factor)
+{
+  rtx *regs = XALLOCAVEC (rtx, interleave_factor);
+  int *regnos = XALLOCAVEC (int, interleave_factor);
+  HOST_WIDE_INT block_size_bytes = interleave_factor * UNITS_PER_WORD;
+  HOST_WIDE_INT i, j;
+  HOST_WIDE_INT remaining = length, words;
+  rtx halfword_tmp = NULL, byte_tmp = NULL;
+  rtx dst, src;
+  bool src_aligned = MEM_ALIGN (srcbase) >= BITS_PER_WORD;
+  bool dst_aligned = MEM_ALIGN (dstbase) >= BITS_PER_WORD;
+  HOST_WIDE_INT srcoffset, dstoffset;
+  HOST_WIDE_INT src_autoinc, dst_autoinc;
+  rtx mem, addr;
+  
+  gcc_assert (1 <= interleave_factor && interleave_factor <= 4);
+  
+  /* Use hard registers if we have aligned source or destination so we can use
+     load/store multiple with contiguous registers.  */
+  if (dst_aligned || src_aligned)
+    for (i = 0; i < interleave_factor; i++)
+      regs[i] = gen_rtx_REG (SImode, i);
+  else
+    for (i = 0; i < interleave_factor; i++)
+      regs[i] = gen_reg_rtx (SImode);
 
-  dstbase = operands[0];
-  srcbase = operands[1];
+  dst = copy_addr_to_reg (XEXP (dstbase, 0));
+  src = copy_addr_to_reg (XEXP (srcbase, 0));
 
-  dst = copy_to_mode_reg (SImode, XEXP (dstbase, 0));
-  src = copy_to_mode_reg (SImode, XEXP (srcbase, 0));
+  srcoffset = dstoffset = 0;
+  
+  /* Calls to arm_gen_load_multiple and arm_gen_store_multiple update SRC/DST.
+     For copying the last bytes we want to subtract this offset again.  */
+  src_autoinc = dst_autoinc = 0;
 
-  in_words_to_go = ARM_NUM_INTS (INTVAL (operands[2]));
-  out_words_to_go = INTVAL (operands[2]) / 4;
-  last_bytes = INTVAL (operands[2]) & 3;
-  dstoffset = srcoffset = 0;
+  for (i = 0; i < interleave_factor; i++)
+    regnos[i] = i;
 
-  if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0)
-    part_bytes_reg = gen_rtx_REG (SImode, (in_words_to_go - 1) & 3);
+  /* Copy BLOCK_SIZE_BYTES chunks.  */
 
-  for (i = 0; in_words_to_go >= 2; i+=4)
+  for (i = 0; i + block_size_bytes <= length; i += block_size_bytes)
     {
-      if (in_words_to_go > 4)
-       emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, 4, src,
-                                         TRUE, srcbase, &srcoffset));
+      /* Load words.  */
+      if (src_aligned && interleave_factor > 1)
+       {
+         emit_insn (arm_gen_load_multiple (regnos, interleave_factor, src,
+                                           TRUE, srcbase, &srcoffset));
+         src_autoinc += UNITS_PER_WORD * interleave_factor;
+       }
       else
-       emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, in_words_to_go,
-                                         src, FALSE, srcbase,
-                                         &srcoffset));
+       {
+         for (j = 0; j < interleave_factor; j++)
+           {
+             addr = plus_constant (src, srcoffset + j * UNITS_PER_WORD
+                                        - src_autoinc);
+             mem = adjust_automodify_address (srcbase, SImode, addr,
+                                              srcoffset + j * UNITS_PER_WORD);
+             emit_insn (gen_unaligned_loadsi (regs[j], mem));
+           }
+         srcoffset += block_size_bytes;
+       }
 
-      if (out_words_to_go)
+      /* Store words.  */
+      if (dst_aligned && interleave_factor > 1)
        {
-         if (out_words_to_go > 4)
-           emit_insn (arm_gen_store_multiple (arm_regs_in_sequence, 4, dst,
-                                              TRUE, dstbase, &dstoffset));
-         else if (out_words_to_go != 1)
-           emit_insn (arm_gen_store_multiple (arm_regs_in_sequence,
-                                              out_words_to_go, dst,
-                                              (last_bytes == 0
-                                               ? FALSE : TRUE),
-                                              dstbase, &dstoffset));
-         else
+         emit_insn (arm_gen_store_multiple (regnos, interleave_factor, dst,
+                                            TRUE, dstbase, &dstoffset));
+         dst_autoinc += UNITS_PER_WORD * interleave_factor;
+       }
+      else
+       {
+         for (j = 0; j < interleave_factor; j++)
            {
-             mem = adjust_automodify_address (dstbase, SImode, dst, dstoffset);
-             emit_move_insn (mem, gen_rtx_REG (SImode, 0));
-             if (last_bytes != 0)
-               {
-                 emit_insn (gen_addsi3 (dst, dst, GEN_INT (4)));
-                 dstoffset += 4;
-               }
+             addr = plus_constant (dst, dstoffset + j * UNITS_PER_WORD
+                                        - dst_autoinc);
+             mem = adjust_automodify_address (dstbase, SImode, addr,
+                                              dstoffset + j * UNITS_PER_WORD);
+             emit_insn (gen_unaligned_storesi (mem, regs[j]));
            }
+         dstoffset += block_size_bytes;
        }
 
-      in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4;
-      out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4;
+      remaining -= block_size_bytes;
     }
+  
+  /* Copy any whole words left (note these aren't interleaved with any
+     subsequent halfword/byte load/stores in the interests of simplicity).  */
+  
+  words = remaining / UNITS_PER_WORD;
 
-  /* OUT_WORDS_TO_GO will be zero here if there are byte stores to do.  */
-  if (out_words_to_go)
+  gcc_assert (words < interleave_factor);
+  
+  if (src_aligned && words > 1)
     {
-      rtx sreg;
-
-      mem = adjust_automodify_address (srcbase, SImode, src, srcoffset);
-      sreg = copy_to_reg (mem);
-
-      mem = adjust_automodify_address (dstbase, SImode, dst, dstoffset);
-      emit_move_insn (mem, sreg);
-      in_words_to_go--;
-
-      gcc_assert (!in_words_to_go);    /* Sanity check */
+      emit_insn (arm_gen_load_multiple (regnos, words, src, TRUE, srcbase,
+                                       &srcoffset));
+      src_autoinc += UNITS_PER_WORD * words;
     }
-
-  if (in_words_to_go)
+  else
     {
-      gcc_assert (in_words_to_go > 0);
-
-      mem = adjust_automodify_address (srcbase, SImode, src, srcoffset);
-      part_bytes_reg = copy_to_mode_reg (SImode, mem);
+      for (j = 0; j < words; j++)
+       {
+         addr = plus_constant (src,
+                               srcoffset + j * UNITS_PER_WORD - src_autoinc);
+         mem = adjust_automodify_address (srcbase, SImode, addr,
+                                          srcoffset + j * UNITS_PER_WORD);
+         emit_insn (gen_unaligned_loadsi (regs[j], mem));
+       }
+      srcoffset += words * UNITS_PER_WORD;
     }
 
-  gcc_assert (!last_bytes || part_bytes_reg);
+  if (dst_aligned && words > 1)
+    {
+      emit_insn (arm_gen_store_multiple (regnos, words, dst, TRUE, dstbase,
+                                        &dstoffset));
+      dst_autoinc += words * UNITS_PER_WORD;
+    }
+  else
+    {
+      for (j = 0; j < words; j++)
+       {
+         addr = plus_constant (dst,
+                               dstoffset + j * UNITS_PER_WORD - dst_autoinc);
+         mem = adjust_automodify_address (dstbase, SImode, addr,
+                                          dstoffset + j * UNITS_PER_WORD);
+         emit_insn (gen_unaligned_storesi (mem, regs[j]));
+       }
+      dstoffset += words * UNITS_PER_WORD;
+    }
 
-  if (BYTES_BIG_ENDIAN && last_bytes)
+  remaining -= words * UNITS_PER_WORD;
+  
+  gcc_assert (remaining < 4);
+  
+  /* Copy a halfword if necessary.  */
+  
+  if (remaining >= 2)
     {
-      rtx tmp = gen_reg_rtx (SImode);
+      halfword_tmp = gen_reg_rtx (SImode);
 
-      /* The bytes we want are in the top end of the word.  */
-      emit_insn (gen_lshrsi3 (tmp, part_bytes_reg,
-                             GEN_INT (8 * (4 - last_bytes))));
+      addr = plus_constant (src, srcoffset - src_autoinc);
+      mem = adjust_automodify_address (srcbase, HImode, addr, srcoffset);
+      emit_insn (gen_unaligned_loadhiu (halfword_tmp, mem));
+
+      /* Either write out immediately, or delay until we've loaded the last
+        byte, depending on interleave factor.  */
+      if (interleave_factor == 1)
+       {
+         addr = plus_constant (dst, dstoffset - dst_autoinc);
+         mem = adjust_automodify_address (dstbase, HImode, addr, dstoffset);
+         emit_insn (gen_unaligned_storehi (mem,
+                      gen_lowpart (HImode, halfword_tmp)));
+         halfword_tmp = NULL;
+         dstoffset += 2;
+       }
+
+      remaining -= 2;
+      srcoffset += 2;
+    }
+  
+  gcc_assert (remaining < 2);
+  
+  /* Copy last byte.  */
+  
+  if ((remaining & 1) != 0)
+    {
+      byte_tmp = gen_reg_rtx (SImode);
+
+      addr = plus_constant (src, srcoffset - src_autoinc);
+      mem = adjust_automodify_address (srcbase, QImode, addr, srcoffset);
+      emit_move_insn (gen_lowpart (QImode, byte_tmp), mem);
+
+      if (interleave_factor == 1)
+       {
+         addr = plus_constant (dst, dstoffset - dst_autoinc);
+         mem = adjust_automodify_address (dstbase, QImode, addr, dstoffset);
+         emit_move_insn (mem, gen_lowpart (QImode, byte_tmp));
+         byte_tmp = NULL;
+         dstoffset++;
+       }
+
+      remaining--;
+      srcoffset++;
+    }
+  
+  /* Store last halfword if we haven't done so already.  */
+  
+  if (halfword_tmp)
+    {
+      addr = plus_constant (dst, dstoffset - dst_autoinc);
+      mem = adjust_automodify_address (dstbase, HImode, addr, dstoffset);
+      emit_insn (gen_unaligned_storehi (mem,
+                  gen_lowpart (HImode, halfword_tmp)));
+      dstoffset += 2;
+    }
+
+  /* Likewise for last byte.  */
+
+  if (byte_tmp)
+    {
+      addr = plus_constant (dst, dstoffset - dst_autoinc);
+      mem = adjust_automodify_address (dstbase, QImode, addr, dstoffset);
+      emit_move_insn (mem, gen_lowpart (QImode, byte_tmp));
+      dstoffset++;
+    }
+  
+  gcc_assert (remaining == 0 && srcoffset == dstoffset);
+}
+
+/* From mips_adjust_block_mem:
+
+   Helper function for doing a loop-based block operation on memory
+   reference MEM.  Each iteration of the loop will operate on LENGTH
+   bytes of MEM.
+
+   Create a new base register for use within the loop and point it to
+   the start of MEM.  Create a new memory reference that uses this
+   register.  Store them in *LOOP_REG and *LOOP_MEM respectively.  */
+
+static void
+arm_adjust_block_mem (rtx mem, HOST_WIDE_INT length, rtx *loop_reg,
+                     rtx *loop_mem)
+{
+  *loop_reg = copy_addr_to_reg (XEXP (mem, 0));
+  
+  /* Although the new mem does not refer to a known location,
+     it does keep up to LENGTH bytes of alignment.  */
+  *loop_mem = change_address (mem, BLKmode, *loop_reg);
+  set_mem_align (*loop_mem, MIN (MEM_ALIGN (mem), length * BITS_PER_UNIT));
+}
+
+/* From mips_block_move_loop:
+
+   Move LENGTH bytes from SRC to DEST using a loop that moves BYTES_PER_ITER
+   bytes at a time.  LENGTH must be at least BYTES_PER_ITER.  Assume that
+   the memory regions do not overlap.  */
+
+static void
+arm_block_move_unaligned_loop (rtx dest, rtx src, HOST_WIDE_INT length,
+                              unsigned int interleave_factor,
+                              HOST_WIDE_INT bytes_per_iter)
+{
+  rtx label, src_reg, dest_reg, final_src, test;
+  HOST_WIDE_INT leftover;
+  
+  leftover = length % bytes_per_iter;
+  length -= leftover;
+  
+  /* Create registers and memory references for use within the loop.  */
+  arm_adjust_block_mem (src, bytes_per_iter, &src_reg, &src);
+  arm_adjust_block_mem (dest, bytes_per_iter, &dest_reg, &dest);
+  
+  /* Calculate the value that SRC_REG should have after the last iteration of
+     the loop.  */
+  final_src = expand_simple_binop (Pmode, PLUS, src_reg, GEN_INT (length),
+                                  0, 0, OPTAB_WIDEN);
+
+  /* Emit the start of the loop.  */
+  label = gen_label_rtx ();
+  emit_label (label);
+  
+  /* Emit the loop body.  */
+  arm_block_move_unaligned_straight (dest, src, bytes_per_iter,
+                                    interleave_factor);
+
+  /* Move on to the next block.  */
+  emit_move_insn (src_reg, plus_constant (src_reg, bytes_per_iter));
+  emit_move_insn (dest_reg, plus_constant (dest_reg, bytes_per_iter));
+  
+  /* Emit the loop condition.  */
+  test = gen_rtx_NE (VOIDmode, src_reg, final_src);
+  emit_jump_insn (gen_cbranchsi4 (test, src_reg, final_src, label));
+  
+  /* Mop up any left-over bytes.  */
+  if (leftover)
+    arm_block_move_unaligned_straight (dest, src, leftover, interleave_factor);
+}
+
+/* Emit a block move when either the source or destination is unaligned (not
+   aligned to a four-byte boundary).  This may need further tuning depending on
+   core type, optimize_size setting, etc.  */
+
+static int
+arm_movmemqi_unaligned (rtx *operands)
+{
+  HOST_WIDE_INT length = INTVAL (operands[2]);
+  
+  if (optimize_size)
+    {
+      bool src_aligned = MEM_ALIGN (operands[1]) >= BITS_PER_WORD;
+      bool dst_aligned = MEM_ALIGN (operands[0]) >= BITS_PER_WORD;
+      /* Inlined memcpy using ldr/str/ldrh/strh can be quite big: try to limit
+        size of code if optimizing for size.  We'll use ldm/stm if src_aligned
+        or dst_aligned though: allow more interleaving in those cases since the
+        resulting code can be smaller.  */
+      unsigned int interleave_factor = (src_aligned || dst_aligned) ? 2 : 1;
+      HOST_WIDE_INT bytes_per_iter = (src_aligned || dst_aligned) ? 8 : 4;
+      
+      if (length > 12)
+       arm_block_move_unaligned_loop (operands[0], operands[1], length,
+                                      interleave_factor, bytes_per_iter);
+      else
+       arm_block_move_unaligned_straight (operands[0], operands[1], length,
+                                          interleave_factor);
+    }
+  else
+    {
+      /* Note that the loop created by arm_block_move_unaligned_loop may be
+        subject to loop unrolling, which makes tuning this condition a little
+        redundant.  */
+      if (length > 32)
+       arm_block_move_unaligned_loop (operands[0], operands[1], length, 4, 16);
+      else
+       arm_block_move_unaligned_straight (operands[0], operands[1], length, 4);
+    }
+  
+  return 1;
+}
+
+int
+arm_gen_movmemqi (rtx *operands)
+{
+  HOST_WIDE_INT in_words_to_go, out_words_to_go, last_bytes;
+  HOST_WIDE_INT srcoffset, dstoffset;
+  int i;
+  rtx src, dst, srcbase, dstbase;
+  rtx part_bytes_reg = NULL;
+  rtx mem;
+
+  if (GET_CODE (operands[2]) != CONST_INT
+      || GET_CODE (operands[3]) != CONST_INT
+      || INTVAL (operands[2]) > 64)
+    return 0;
+
+  if (unaligned_access && (INTVAL (operands[3]) & 3) != 0)
+    return arm_movmemqi_unaligned (operands);
+
+  if (INTVAL (operands[3]) & 3)
+    return 0;
+
+  dstbase = operands[0];
+  srcbase = operands[1];
+
+  dst = copy_to_mode_reg (SImode, XEXP (dstbase, 0));
+  src = copy_to_mode_reg (SImode, XEXP (srcbase, 0));
+
+  in_words_to_go = ARM_NUM_INTS (INTVAL (operands[2]));
+  out_words_to_go = INTVAL (operands[2]) / 4;
+  last_bytes = INTVAL (operands[2]) & 3;
+  dstoffset = srcoffset = 0;
+
+  if (out_words_to_go != in_words_to_go && ((in_words_to_go - 1) & 3) != 0)
+    part_bytes_reg = gen_rtx_REG (SImode, (in_words_to_go - 1) & 3);
+
+  for (i = 0; in_words_to_go >= 2; i+=4)
+    {
+      if (in_words_to_go > 4)
+       emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, 4, src,
+                                         TRUE, srcbase, &srcoffset));
+      else
+       emit_insn (arm_gen_load_multiple (arm_regs_in_sequence, in_words_to_go,
+                                         src, FALSE, srcbase,
+                                         &srcoffset));
+
+      if (out_words_to_go)
+       {
+         if (out_words_to_go > 4)
+           emit_insn (arm_gen_store_multiple (arm_regs_in_sequence, 4, dst,
+                                              TRUE, dstbase, &dstoffset));
+         else if (out_words_to_go != 1)
+           emit_insn (arm_gen_store_multiple (arm_regs_in_sequence,
+                                              out_words_to_go, dst,
+                                              (last_bytes == 0
+                                               ? FALSE : TRUE),
+                                              dstbase, &dstoffset));
+         else
+           {
+             mem = adjust_automodify_address (dstbase, SImode, dst, dstoffset);
+             emit_move_insn (mem, gen_rtx_REG (SImode, 0));
+             if (last_bytes != 0)
+               {
+                 emit_insn (gen_addsi3 (dst, dst, GEN_INT (4)));
+                 dstoffset += 4;
+               }
+           }
+       }
+
+      in_words_to_go -= in_words_to_go < 4 ? in_words_to_go : 4;
+      out_words_to_go -= out_words_to_go < 4 ? out_words_to_go : 4;
+    }
+
+  /* OUT_WORDS_TO_GO will be zero here if there are byte stores to do.  */
+  if (out_words_to_go)
+    {
+      rtx sreg;
+
+      mem = adjust_automodify_address (srcbase, SImode, src, srcoffset);
+      sreg = copy_to_reg (mem);
+
+      mem = adjust_automodify_address (dstbase, SImode, dst, dstoffset);
+      emit_move_insn (mem, sreg);
+      in_words_to_go--;
+
+      gcc_assert (!in_words_to_go);    /* Sanity check */
+    }
+
+  if (in_words_to_go)
+    {
+      gcc_assert (in_words_to_go > 0);
+
+      mem = adjust_automodify_address (srcbase, SImode, src, srcoffset);
+      part_bytes_reg = copy_to_mode_reg (SImode, mem);
+    }
+
+  gcc_assert (!last_bytes || part_bytes_reg);
+
+  if (BYTES_BIG_ENDIAN && last_bytes)
+    {
+      rtx tmp = gen_reg_rtx (SImode);
+
+      /* The bytes we want are in the top end of the word.  */
+      emit_insn (gen_lshrsi3 (tmp, part_bytes_reg,
+                             GEN_INT (8 * (4 - last_bytes))));
       part_bytes_reg = tmp;
 
       while (last_bytes)
@@ -10678,7 +11581,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
 
   /* A compare with a shifted operand.  Because of canonicalization, the
      comparison will have to be swapped when we emit the assembler.  */
-  if (GET_MODE (y) == SImode 
+  if (GET_MODE (y) == SImode
       && (REG_P (y) || (GET_CODE (y) == SUBREG))
       && (GET_CODE (x) == ASHIFT || GET_CODE (x) == ASHIFTRT
          || GET_CODE (x) == LSHIFTRT || GET_CODE (x) == ROTATE
@@ -10687,7 +11590,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
 
   /* This operation is performed swapped, but since we only rely on the Z
      flag we don't need an additional mode.  */
-  if (GET_MODE (y) == SImode 
+  if (GET_MODE (y) == SImode
       && (REG_P (y) || (GET_CODE (y) == SUBREG))
       && GET_CODE (x) == NEG
       && (op ==        EQ || op == NE))
@@ -10788,7 +11691,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
            return CC_Zmode;
 
          /* We can do an equality test in three Thumb instructions.  */
-         if (!TARGET_ARM)
+         if (!TARGET_32BIT)
            return CC_Zmode;
 
          /* FALLTHROUGH */
@@ -10800,7 +11703,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
          /* DImode unsigned comparisons can be implemented by cmp +
             cmpeq without a scratch register.  Not worth doing in
             Thumb-2.  */
-         if (TARGET_ARM)
+         if (TARGET_32BIT)
            return CC_CZmode;
 
          /* FALLTHROUGH */
@@ -10827,7 +11730,7 @@ arm_select_cc_mode (enum rtx_code op, rtx x, rtx y)
    return the rtx for register 0 in the proper mode.  FP means this is a
    floating point compare: I don't think that it is needed on the arm.  */
 rtx
-arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y)
+arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y, rtx scratch)
 {
   enum machine_mode mode;
   rtx cc_reg;
@@ -10852,11 +11755,18 @@ arm_gen_compare_reg (enum rtx_code code, rtx x, rtx y)
         CC_CZmode is cheaper.  */
       if (mode == CC_Zmode && y != const0_rtx)
        {
+         gcc_assert (!reload_completed);
          x = expand_binop (DImode, xor_optab, x, y, NULL_RTX, 0, OPTAB_WIDEN);
          y = const0_rtx;
        }
+
       /* A scratch register is required.  */
-      clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_SCRATCH (SImode));
+      if (reload_completed)
+       gcc_assert (scratch != NULL && GET_MODE (scratch) == SImode);
+      else
+       scratch = gen_rtx_SCRATCH (SImode);
+
+      clobber = gen_rtx_CLOBBER (VOIDmode, scratch);
       set = gen_rtx_SET (VOIDmode, cc_reg, gen_rtx_COMPARE (mode, x, y));
       emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, set, clobber)));
     }
@@ -11171,7 +12081,7 @@ arm_must_pass_in_stack (enum machine_mode mode, const_tree type)
    aggregate types are placed in the lowest memory address.  */
 
 bool
-arm_pad_arg_upward (enum machine_mode mode, const_tree type)
+arm_pad_arg_upward (enum machine_mode mode ATTRIBUTE_UNUSED, const_tree type)
 {
   if (!TARGET_AAPCS_BASED)
     return DEFAULT_FUNCTION_ARG_PADDING(mode, type) == upward;
@@ -11184,21 +12094,33 @@ arm_pad_arg_upward (enum machine_mode mode, const_tree type)
 
 
 /* Similarly, for use by BLOCK_REG_PADDING (MODE, TYPE, FIRST).
-   For non-AAPCS, return !BYTES_BIG_ENDIAN if the least significant
-   byte of the register has useful data, and return the opposite if the
-   most significant byte does.
-   For AAPCS, small aggregates and small complex types are always padded
-   upwards.  */
+   Return !BYTES_BIG_ENDIAN if the least significant byte of the
+   register has useful data, and return the opposite if the most
+   significant byte does.  */
 
 bool
-arm_pad_reg_upward (enum machine_mode mode ATTRIBUTE_UNUSED,
+arm_pad_reg_upward (enum machine_mode mode,
                     tree type, int first ATTRIBUTE_UNUSED)
 {
-  if (TARGET_AAPCS_BASED
-      && BYTES_BIG_ENDIAN
-      && (AGGREGATE_TYPE_P (type) || TREE_CODE (type) == COMPLEX_TYPE)
-      && int_size_in_bytes (type) <= 4)
-    return true;
+  if (TARGET_AAPCS_BASED && BYTES_BIG_ENDIAN)
+    {
+      /* For AAPCS, small aggregates, small fixed-point types,
+        and small complex types are always padded upwards.  */
+      if (type)
+       {
+         if ((AGGREGATE_TYPE_P (type)
+              || TREE_CODE (type) == COMPLEX_TYPE
+              || FIXED_POINT_TYPE_P (type))
+             && int_size_in_bytes (type) <= 4)
+           return true;
+       }
+      else
+       {
+         if ((COMPLEX_MODE_P (mode) || ALL_FIXED_POINT_MODE_P (mode))
+             && GET_MODE_SIZE (mode) <= 4)
+           return true;
+       }
+    }
 
   /* Otherwise, use default padding.  */
   return !BYTES_BIG_ENDIAN;
@@ -11395,8 +12317,7 @@ is_jump_table (rtx insn)
 {
   rtx table;
 
-  if (GET_CODE (insn) == JUMP_INSN
-      && JUMP_LABEL (insn) != NULL
+  if (jump_to_label_p (insn)
       && ((table = next_real_insn (JUMP_LABEL (insn)))
          == next_real_insn (insn))
       && table != NULL
@@ -11449,6 +12370,19 @@ get_jump_table_size (rtx insn)
   return 0;
 }
 
+/* Return the maximum amount of padding that will be inserted before
+   label LABEL.  */
+
+static HOST_WIDE_INT
+get_label_padding (rtx label)
+{
+  HOST_WIDE_INT align, min_insn_size;
+
+  align = 1 << label_to_alignment (label);
+  min_insn_size = TARGET_THUMB ? 2 : 4;
+  return align > min_insn_size ? align - min_insn_size : 0;
+}
+
 /* Move a minipool fix MP from its current location to before MAX_MP.
    If MAX_MP is NULL, then MP doesn't need moving, but the addressing
    constraints may need updating.  */
@@ -11995,8 +12929,12 @@ create_fix_barrier (Mfix *fix, HOST_WIDE_INT max_address)
         within range.  */
       gcc_assert (GET_CODE (from) != BARRIER);
 
-      /* Count the length of this insn.  */
-      count += get_attr_length (from);
+      /* Count the length of this insn.  This must stay in sync with the
+        code that pushes minipool fixes.  */
+      if (LABEL_P (from))
+       count += get_label_padding (from);
+      else
+       count += get_attr_length (from);
 
       /* If there is a jump table, add its length.  */
       tmp = is_jump_table (from);
@@ -12009,7 +12947,7 @@ create_fix_barrier (Mfix *fix, HOST_WIDE_INT max_address)
             still put the pool after the table.  */
          new_cost = arm_barrier_cost (from);
 
-         if (count < max_count 
+         if (count < max_count
              && (!selected || new_cost <= selected_cost))
            {
              selected = tmp;
@@ -12391,7 +13329,7 @@ arm_reorg (void)
 
   if (TARGET_THUMB2)
     thumb2_reorg ();
-  
+
   minipool_fix_head = minipool_fix_tail = NULL;
 
   /* The first insn must always be a note, or the code below won't
@@ -12426,6 +13364,11 @@ arm_reorg (void)
              insn = table;
            }
        }
+      else if (LABEL_P (insn))
+       /* Add the worst-case padding due to alignment.  We don't add
+          the _current_ padding because the minipool insertions
+          themselves might change it.  */
+       address += get_label_padding (insn);
     }
 
   fix = minipool_fix_head;
@@ -12673,7 +13616,7 @@ vfp_output_fstmd (rtx * operands)
   int base;
   int i;
 
-  strcpy (pattern, "fstmfdd\t%m0!, {%P1");
+  strcpy (pattern, "fstmfdd%?\t%m0!, {%P1");
   p = strlen (pattern);
 
   gcc_assert (GET_CODE (operands[1]) == REG);
@@ -12992,11 +13935,23 @@ output_mov_double_arm_from_fpa (rtx *operands)
 /* Output a move between double words.  It must be REG<-MEM
    or MEM<-REG.  */
 const char *
-output_move_double (rtx *operands)
+output_move_double (rtx *operands, bool emit, int *count)
 {
   enum rtx_code code0 = GET_CODE (operands[0]);
   enum rtx_code code1 = GET_CODE (operands[1]);
   rtx otherops[3];
+  if (count)
+    *count = 1;
+
+  /* The only case when this might happen is when
+     you are looking at the length of a DImode instruction
+     that has an invalid constant in it.  */
+  if (code0 == REG && code1 != MEM)
+    {
+      gcc_assert (!emit);
+      *count = 2;
+      return "";
+    }
 
   if (code0 == REG)
     {
@@ -13009,35 +13964,47 @@ output_move_double (rtx *operands)
       switch (GET_CODE (XEXP (operands[1], 0)))
        {
        case REG:
-         if (TARGET_LDRD
-             && !(fix_cm3_ldrd && reg0 == REGNO(XEXP (operands[1], 0))))
-           output_asm_insn ("ldr%(d%)\t%0, [%m1]", operands);
-         else
-           output_asm_insn ("ldm%(ia%)\t%m1, %M0", operands);
+
+         if (emit)
+           {
+             if (TARGET_LDRD
+                 && !(fix_cm3_ldrd && reg0 == REGNO(XEXP (operands[1], 0))))
+               output_asm_insn ("ldr%(d%)\t%0, [%m1]", operands);
+             else
+               output_asm_insn ("ldm%(ia%)\t%m1, %M0", operands);
+           }
          break;
 
        case PRE_INC:
          gcc_assert (TARGET_LDRD);
-         output_asm_insn ("ldr%(d%)\t%0, [%m1, #8]!", operands);
+         if (emit)
+           output_asm_insn ("ldr%(d%)\t%0, [%m1, #8]!", operands);
          break;
 
        case PRE_DEC:
-         if (TARGET_LDRD)
-           output_asm_insn ("ldr%(d%)\t%0, [%m1, #-8]!", operands);
-         else
-           output_asm_insn ("ldm%(db%)\t%m1!, %M0", operands);
+         if (emit)
+           {
+             if (TARGET_LDRD)
+               output_asm_insn ("ldr%(d%)\t%0, [%m1, #-8]!", operands);
+             else
+               output_asm_insn ("ldm%(db%)\t%m1!, %M0", operands);
+           }
          break;
 
        case POST_INC:
-         if (TARGET_LDRD)
-           output_asm_insn ("ldr%(d%)\t%0, [%m1], #8", operands);
-         else
-           output_asm_insn ("ldm%(ia%)\t%m1!, %M0", operands);
+         if (emit)
+           {
+             if (TARGET_LDRD)
+               output_asm_insn ("ldr%(d%)\t%0, [%m1], #8", operands);
+             else
+               output_asm_insn ("ldm%(ia%)\t%m1!, %M0", operands);
+           }
          break;
 
        case POST_DEC:
          gcc_assert (TARGET_LDRD);
-         output_asm_insn ("ldr%(d%)\t%0, [%m1], #-8", operands);
+         if (emit)
+           output_asm_insn ("ldr%(d%)\t%0, [%m1], #-8", operands);
          break;
 
        case PRE_MODIFY:
@@ -13055,8 +14022,13 @@ output_move_double (rtx *operands)
              if (reg_overlap_mentioned_p (otherops[0], otherops[2]))
                {
                  /* Registers overlap so split out the increment.  */
-                 output_asm_insn ("add%?\t%1, %1, %2", otherops);
-                 output_asm_insn ("ldr%(d%)\t%0, [%1] @split", otherops);
+                 if (emit)
+                   {
+                     output_asm_insn ("add%?\t%1, %1, %2", otherops);
+                     output_asm_insn ("ldr%(d%)\t%0, [%1] @split", otherops);
+                   }
+                 if (count)
+                   *count = 2;
                }
              else
                {
@@ -13067,11 +14039,20 @@ output_move_double (rtx *operands)
                      || GET_CODE (otherops[2]) != CONST_INT
                      || (INTVAL (otherops[2]) > -256
                          && INTVAL (otherops[2]) < 256))
-                   output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
+                   {
+                     if (emit)
+                       output_asm_insn ("ldr%(d%)\t%0, [%1, %2]!", otherops);
+                   }
                  else
                    {
-                     output_asm_insn ("ldr%?\t%0, [%1, %2]!", otherops);
-                     output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
+                     if (emit)
+                       {
+                         output_asm_insn ("ldr%?\t%0, [%1, %2]!", otherops);
+                         output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
+                       }
+                     if (count)
+                       *count = 2;
+
                    }
                }
            }
@@ -13084,11 +14065,19 @@ output_move_double (rtx *operands)
                  || GET_CODE (otherops[2]) != CONST_INT
                  || (INTVAL (otherops[2]) > -256
                      && INTVAL (otherops[2]) < 256))
-               output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
+               {
+                 if (emit)
+                   output_asm_insn ("ldr%(d%)\t%0, [%1], %2", otherops);
+               }
              else
                {
-                 output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
-                 output_asm_insn ("ldr%?\t%0, [%1], %2", otherops);
+                 if (emit)
+                   {
+                     output_asm_insn ("ldr%?\t%H0, [%1, #4]", otherops);
+                     output_asm_insn ("ldr%?\t%0, [%1], %2", otherops);
+                   }
+                 if (count)
+                   *count = 2;
                }
            }
          break;
@@ -13101,12 +14090,19 @@ output_move_double (rtx *operands)
          /* Use the second register of the pair to avoid problematic
             overlap.  */
          otherops[1] = operands[1];
-         output_asm_insn ("adr%?\t%0, %1", otherops);
+         if (emit)
+           output_asm_insn ("adr%?\t%0, %1", otherops);
          operands[1] = otherops[0];
-         if (TARGET_LDRD)
-           output_asm_insn ("ldr%(d%)\t%0, [%1]", operands);
-         else
-           output_asm_insn ("ldm%(ia%)\t%1, %M0", operands);
+         if (emit)
+           {
+             if (TARGET_LDRD)
+               output_asm_insn ("ldr%(d%)\t%0, [%1]", operands);
+             else
+               output_asm_insn ("ldm%(ia%)\t%1, %M0", operands);
+           }
+
+         if (count)
+           *count = 2;
          break;
 
          /* ??? This needs checking for thumb2.  */
@@ -13125,17 +14121,20 @@ output_move_double (rtx *operands)
                      switch ((int) INTVAL (otherops[2]))
                        {
                        case -8:
-                         output_asm_insn ("ldm%(db%)\t%1, %M0", otherops);
+                         if (emit)
+                           output_asm_insn ("ldm%(db%)\t%1, %M0", otherops);
                          return "";
                        case -4:
                          if (TARGET_THUMB2)
                            break;
-                         output_asm_insn ("ldm%(da%)\t%1, %M0", otherops);
+                         if (emit)
+                           output_asm_insn ("ldm%(da%)\t%1, %M0", otherops);
                          return "";
                        case 4:
                          if (TARGET_THUMB2)
                            break;
-                         output_asm_insn ("ldm%(ib%)\t%1, %M0", otherops);
+                         if (emit)
+                           output_asm_insn ("ldm%(ib%)\t%1, %M0", otherops);
                          return "";
                        }
                    }
@@ -13163,29 +14162,47 @@ output_move_double (rtx *operands)
                      if (reg_overlap_mentioned_p (operands[0], otherops[2])
                          || (fix_cm3_ldrd && reg0 == REGNO (otherops[1])))
                        {
-                         output_asm_insn ("add%?\t%0, %1, %2", otherops);
-                         output_asm_insn ("ldr%(d%)\t%0, [%1]", operands);
+                         if (emit)
+                           {
+                             output_asm_insn ("add%?\t%0, %1, %2", otherops);
+                             output_asm_insn ("ldr%(d%)\t%0, [%1]", operands);
+                           }
+                         if (count)
+                           *count = 2;
                        }
                      else
                        {
                          otherops[0] = operands[0];
-                         output_asm_insn ("ldr%(d%)\t%0, [%1, %2]", otherops);
+                         if (emit)
+                           output_asm_insn ("ldr%(d%)\t%0, [%1, %2]", otherops);
                        }
                      return "";
                    }
 
                  if (GET_CODE (otherops[2]) == CONST_INT)
                    {
-                     if (!(const_ok_for_arm (INTVAL (otherops[2]))))
-                       output_asm_insn ("sub%?\t%0, %1, #%n2", otherops);
-                     else
-                       output_asm_insn ("add%?\t%0, %1, %2", otherops);
+                     if (emit)
+                       {
+                         if (!(const_ok_for_arm (INTVAL (otherops[2]))))
+                           output_asm_insn ("sub%?\t%0, %1, #%n2", otherops);
+                         else
+                           output_asm_insn ("add%?\t%0, %1, %2", otherops);
+                       }
                    }
                  else
-                   output_asm_insn ("add%?\t%0, %1, %2", otherops);
+                   {
+                     if (emit)
+                       output_asm_insn ("add%?\t%0, %1, %2", otherops);
+                   }
                }
              else
-               output_asm_insn ("sub%?\t%0, %1, %2", otherops);
+               {
+                 if (emit)
+                   output_asm_insn ("sub%?\t%0, %1, %2", otherops);
+               }
+
+             if (count)
+               *count = 2;
 
              if (TARGET_LDRD)
                return "ldr%(d%)\t%0, [%1]";
@@ -13198,13 +14215,24 @@ output_move_double (rtx *operands)
              /* Take care of overlapping base/data reg.  */
              if (reg_mentioned_p (operands[0], operands[1]))
                {
-                 output_asm_insn ("ldr%?\t%0, %1", otherops);
-                 output_asm_insn ("ldr%?\t%0, %1", operands);
+                 if (emit)
+                   {
+                     output_asm_insn ("ldr%?\t%0, %1", otherops);
+                     output_asm_insn ("ldr%?\t%0, %1", operands);
+                   }
+                 if (count)
+                   *count = 2;
+
                }
              else
                {
-                 output_asm_insn ("ldr%?\t%0, %1", operands);
-                 output_asm_insn ("ldr%?\t%0, %1", otherops);
+                 if (emit)
+                   {
+                     output_asm_insn ("ldr%?\t%0, %1", operands);
+                     output_asm_insn ("ldr%?\t%0, %1", otherops);
+                   }
+                 if (count)
+                   *count = 2;
                }
            }
        }
@@ -13218,34 +14246,45 @@ output_move_double (rtx *operands)
       switch (GET_CODE (XEXP (operands[0], 0)))
         {
        case REG:
-         if (TARGET_LDRD)
-           output_asm_insn ("str%(d%)\t%1, [%m0]", operands);
-         else
-           output_asm_insn ("stm%(ia%)\t%m0, %M1", operands);
+         if (emit)
+           {
+             if (TARGET_LDRD)
+               output_asm_insn ("str%(d%)\t%1, [%m0]", operands);
+             else
+               output_asm_insn ("stm%(ia%)\t%m0, %M1", operands);
+           }
          break;
 
         case PRE_INC:
          gcc_assert (TARGET_LDRD);
-         output_asm_insn ("str%(d%)\t%1, [%m0, #8]!", operands);
+         if (emit)
+           output_asm_insn ("str%(d%)\t%1, [%m0, #8]!", operands);
          break;
 
         case PRE_DEC:
-         if (TARGET_LDRD)
-           output_asm_insn ("str%(d%)\t%1, [%m0, #-8]!", operands);
-         else
-           output_asm_insn ("stm%(db%)\t%m0!, %M1", operands);
-         break;
-
-        case POST_INC:
-         if (TARGET_LDRD)
-           output_asm_insn ("str%(d%)\t%1, [%m0], #8", operands);
-         else
-           output_asm_insn ("stm%(ia%)\t%m0!, %M1", operands);
+         if (emit)
+           {
+             if (TARGET_LDRD)
+               output_asm_insn ("str%(d%)\t%1, [%m0, #-8]!", operands);
+             else
+               output_asm_insn ("stm%(db%)\t%m0!, %M1", operands);
+           }
+         break;
+
+        case POST_INC:
+         if (emit)
+           {
+             if (TARGET_LDRD)
+               output_asm_insn ("str%(d%)\t%1, [%m0], #8", operands);
+             else
+               output_asm_insn ("stm%(ia%)\t%m0!, %M1", operands);
+           }
          break;
 
         case POST_DEC:
          gcc_assert (TARGET_LDRD);
-         output_asm_insn ("str%(d%)\t%1, [%m0], #-8", operands);
+         if (emit)
+           output_asm_insn ("str%(d%)\t%1, [%m0], #-8", operands);
          break;
 
        case PRE_MODIFY:
@@ -13263,19 +14302,35 @@ output_move_double (rtx *operands)
            {
              if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
                {
-                 output_asm_insn ("str%?\t%0, [%1, %2]!", otherops);
-                 output_asm_insn ("str%?\t%H0, [%1, #4]", otherops);
+                 if (emit)
+                   {
+                     output_asm_insn ("str%?\t%0, [%1, %2]!", otherops);
+                     output_asm_insn ("str%?\t%H0, [%1, #4]", otherops);
+                   }
+                 if (count)
+                   *count = 2;
                }
              else
                {
-                 output_asm_insn ("str%?\t%H0, [%1, #4]", otherops);
-                 output_asm_insn ("str%?\t%0, [%1], %2", otherops);
+                 if (emit)
+                   {
+                     output_asm_insn ("str%?\t%H0, [%1, #4]", otherops);
+                     output_asm_insn ("str%?\t%0, [%1], %2", otherops);
+                   }
+                 if (count)
+                   *count = 2;
                }
            }
          else if (GET_CODE (XEXP (operands[0], 0)) == PRE_MODIFY)
-           output_asm_insn ("str%(d%)\t%0, [%1, %2]!", otherops);
+           {
+             if (emit)
+               output_asm_insn ("str%(d%)\t%0, [%1, %2]!", otherops);
+           }
          else
-           output_asm_insn ("str%(d%)\t%0, [%1], %2", otherops);
+           {
+             if (emit)
+               output_asm_insn ("str%(d%)\t%0, [%1], %2", otherops);
+           }
          break;
 
        case PLUS:
@@ -13285,19 +14340,22 @@ output_move_double (rtx *operands)
              switch ((int) INTVAL (XEXP (XEXP (operands[0], 0), 1)))
                {
                case -8:
-                 output_asm_insn ("stm%(db%)\t%m0, %M1", operands);
+                 if (emit)
+                   output_asm_insn ("stm%(db%)\t%m0, %M1", operands);
                  return "";
 
                case -4:
                  if (TARGET_THUMB2)
                    break;
-                 output_asm_insn ("stm%(da%)\t%m0, %M1", operands);
+                 if (emit)
+                   output_asm_insn ("stm%(da%)\t%m0, %M1", operands);
                  return "";
 
                case 4:
                  if (TARGET_THUMB2)
                    break;
-                 output_asm_insn ("stm%(ib%)\t%m0, %M1", operands);
+                 if (emit)
+                   output_asm_insn ("stm%(ib%)\t%m0, %M1", operands);
                  return "";
                }
            }
@@ -13310,7 +14368,8 @@ output_move_double (rtx *operands)
            {
              otherops[0] = operands[1];
              otherops[1] = XEXP (XEXP (operands[0], 0), 0);
-             output_asm_insn ("str%(d%)\t%0, [%1, %2]", otherops);
+             if (emit)
+               output_asm_insn ("str%(d%)\t%0, [%1, %2]", otherops);
              return "";
            }
          /* Fall through */
@@ -13318,8 +14377,13 @@ output_move_double (rtx *operands)
         default:
          otherops[0] = adjust_address (operands[0], SImode, 4);
          otherops[1] = operands[1];
-         output_asm_insn ("str%?\t%1, %0", operands);
-         output_asm_insn ("str%?\t%H1, %0", otherops);
+         if (emit)
+           {
+             output_asm_insn ("str%?\t%1, %0", operands);
+             output_asm_insn ("str%?\t%H1, %0", otherops);
+           }
+         if (count)
+           *count = 2;
        }
     }
 
@@ -13529,7 +14593,7 @@ output_move_neon (rtx *operands)
       ops[0] = XEXP (addr, 0);
       ops[1] = reg;
       break;
-    
+
     case POST_MODIFY:
       /* FIXME: Not currently enabled in neon_vector_mem_operand.  */
       gcc_unreachable ();
@@ -14021,7 +15085,7 @@ arm_compute_save_reg0_reg12_mask (void)
 }
 
 
-/* Compute the number of bytes used to store the static chain register on the 
+/* Compute the number of bytes used to store the static chain register on the
    stack, above the stack frame. We need to know this accurately to get the
    alignment of the rest of the stack frame correct. */
 
@@ -14360,7 +15424,7 @@ output_return_instruction (rtx operand, int really_return, int reverse)
                     then try to pop r3 instead.  */
                  if (stack_adjust)
                    live_regs_mask |= 1 << 3;
-                 
+
                  if (TARGET_UNIFIED_ASM)
                    sprintf (instr, "ldmfd%s\t%%|sp, {", conditional);
                  else
@@ -14501,11 +15565,9 @@ arm_output_function_prologue (FILE *f, HOST_WIDE_INT frame_size)
 {
   unsigned long func_type;
 
+  /* ??? Do we want to print some of the below anyway?  */
   if (TARGET_THUMB1)
-    {
-      thumb1_output_function_prologue (f, frame_size);
-      return;
-    }
+    return;
 
   /* Sanity check.  */
   gcc_assert (!arm_ccfsm_state && !arm_target_insn);
@@ -14576,7 +15638,7 @@ 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) && 
+  if (use_return_insn (FALSE, sibling) &&
       (cfun->machine->return_used_this_function != 0))
     return "";
 
@@ -14784,7 +15846,7 @@ arm_output_epilogue (rtx sibling)
        {
          operands[0] = stack_pointer_rtx;
          operands[1] = hard_frame_pointer_rtx;
-         
+
          operands[2] = GEN_INT (offsets->frame - offsets->saved_regs);
          output_add_immediate (operands);
        }
@@ -14811,6 +15873,7 @@ arm_output_epilogue (rtx sibling)
                  && !crtl->calls_eh_return
                  && bit_count(saved_regs_mask) * 4 == count
                  && !IS_INTERRUPT (func_type)
+                 && !IS_STACKALIGN (func_type)
                  && !crtl->tail_call_emit)
                {
                  unsigned long mask;
@@ -14831,7 +15894,7 @@ arm_output_epilogue (rtx sibling)
                  }
                }
            }
-         
+
          if (amount)
            {
              operands[1] = operands[0];
@@ -15463,7 +16526,7 @@ arm_get_frame_offsets (void)
        {
          int reg = -1;
 
-         /* If it is safe to use r3, then do so.  This sometimes 
+         /* 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 (! any_sibcall_uses_r3 ()
@@ -15763,9 +16826,8 @@ arm_expand_prologue (void)
 
   if (IS_STACKALIGN (func_type))
     {
-      rtx dwarf;
-      rtx r0;
-      rtx r1;
+      rtx r0, r1;
+
       /* Handle a word-aligned stack pointer.  We generate the following:
 
          mov r0, sp
@@ -15781,15 +16843,18 @@ arm_expand_prologue (void)
 
       r0 = gen_rtx_REG (SImode, 0);
       r1 = gen_rtx_REG (SImode, 1);
-      /* Use a real rtvec rather than NULL_RTVEC so the rest of the
-        compiler won't choke.  */
-      dwarf = gen_rtx_UNSPEC (SImode, rtvec_alloc (0), UNSPEC_STACK_ALIGN);
-      dwarf = gen_rtx_SET (VOIDmode, r0, dwarf);
-      insn = gen_movsi (r0, stack_pointer_rtx);
+
+      insn = emit_insn (gen_movsi (r0, stack_pointer_rtx));
       RTX_FRAME_RELATED_P (insn) = 1;
-      add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
-      emit_insn (insn);
+      add_reg_note (insn, REG_CFA_REGISTER, NULL);
+
       emit_insn (gen_andsi3 (r1, r0, GEN_INT (~(HOST_WIDE_INT)7)));
+
+      /* ??? The CFA changes here, which may cause GDB to conclude that it
+        has entered a different function.  That said, the unwind info is
+        correct, individually, before and after this instruction because
+        we've described the save of SP, which will override the default
+        handling of SP as restoring from the CFA.  */
       emit_insn (gen_movsi (stack_pointer_rtx, r1));
     }
 
@@ -15803,7 +16868,7 @@ arm_expand_prologue (void)
          /* Interrupt functions must not corrupt any registers.
             Creating a frame pointer however, corrupts the IP
             register, so we must push it first.  */
-         insn = emit_multi_reg_push (1 << IP_REGNUM);
+         emit_multi_reg_push (1 << IP_REGNUM);
 
          /* Do not set RTX_FRAME_RELATED_P on this insn.
             The dwarf stack unwinding code only wants to see one
@@ -15907,7 +16972,7 @@ arm_expand_prologue (void)
       && TARGET_ARM)
     {
       rtx lr = gen_rtx_REG (SImode, LR_REGNUM);
-      
+
       emit_set_insn (lr, plus_constant (lr, -4));
     }
 
@@ -15975,7 +17040,7 @@ arm_expand_prologue (void)
        }
     }
 
-  if (flag_stack_usage)
+  if (flag_stack_usage_info)
     current_function_static_stack_size
       = offsets->outgoing_args - offsets->saved_args;
 
@@ -16118,7 +17183,7 @@ arm_print_operand (FILE *stream, rtx x, int code)
       if (TARGET_UNIFIED_ASM)
        arm_print_condition (stream);
       break;
-  
+
     case '.':
       /* The current condition code for a condition code setting instruction.
         Preceded by 's' in unified syntax, otherwise followed by 's'.  */
@@ -16172,8 +17237,17 @@ arm_print_operand (FILE *stream, rtx x, int code)
          output_addr_const (stream, x);
          break;
 
+       case CONST:
+         if (GET_CODE (XEXP (x, 0)) == PLUS
+             && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)
+           {
+             output_addr_const (stream, x);
+             break;
+           }
+         /* Fall through.  */
+
        default:
-         gcc_unreachable ();
+         output_operand_lossage ("Unsupported operand for code '%c'", code);
        }
       return;
 
@@ -16620,7 +17694,7 @@ arm_print_operand (FILE *stream, rtx x, int code)
       {
        rtx addr;
        bool postinc = FALSE;
-       unsigned align, modesize, align_bits;
+       unsigned align, memsize, align_bits;
 
        gcc_assert (GET_CODE (x) == MEM);
        addr = XEXP (x, 0);
@@ -16635,18 +17709,18 @@ arm_print_operand (FILE *stream, rtx x, int code)
           instruction (for some alignments) as an aid to the memory subsystem
           of the target.  */
        align = MEM_ALIGN (x) >> 3;
-       modesize = GET_MODE_SIZE (GET_MODE (x));
-       
+       memsize = MEM_SIZE (x);
+
        /* Only certain alignment specifiers are supported by the hardware.  */
-       if (modesize == 16 && (align % 32) == 0)
+       if (memsize == 32 && (align % 32) == 0)
          align_bits = 256;
-       else if ((modesize == 8 || modesize == 16) && (align % 16) == 0)
+       else if ((memsize == 16 || memsize == 32) && (align % 16) == 0)
          align_bits = 128;
-       else if ((align % 8) == 0)
+       else if (memsize >= 8 && (align % 8) == 0)
          align_bits = 64;
        else
          align_bits = 0;
-       
+
        if (align_bits != 0)
          asm_fprintf (stream, ":%d", align_bits);
 
@@ -16692,6 +17766,11 @@ arm_print_operand (FILE *stream, rtx x, int code)
       }
       return;
 
+    case 'v':
+       gcc_assert (GET_CODE (x) == CONST_DOUBLE);
+       fprintf (stream, "#%d", vfp3_const_double_for_fract_bits (x));
+       return;
+
     /* Register specifier for vld1.16/vst1.16.  Translate the S register
        number into a D register number and element index.  */
     case 'z':
@@ -16716,7 +17795,7 @@ arm_print_operand (FILE *stream, rtx x, int code)
        fprintf (stream, "d%d[%d]", regno/2, ((regno % 2) ? 2 : 0));
       }
       return;
-      
+
     default:
       if (x == 0)
        {
@@ -16755,7 +17834,7 @@ arm_print_operand (FILE *stream, rtx x, int code)
              fputs (":lower16:", stream);
              x = XEXP (x, 0);
            }
-           
+
          output_addr_const (stream, x);
          break;
        }
@@ -16969,8 +18048,8 @@ arm_elf_asm_cdtor (rtx symbol, int priority, bool is_ctor)
 
   if (!TARGET_AAPCS_BASED)
     {
-      (is_ctor ? 
-       default_named_section_asm_out_constructor 
+      (is_ctor ?
+       default_named_section_asm_out_constructor
        : default_named_section_asm_out_destructor) (symbol, priority);
       return;
     }
@@ -16979,7 +18058,7 @@ arm_elf_asm_cdtor (rtx symbol, int priority, bool is_ctor)
   if (priority != DEFAULT_INIT_PRIORITY)
     {
       char buf[18];
-      sprintf (buf, "%s.%.5u", 
+      sprintf (buf, "%s.%.5u",
               is_ctor ? ".init_array" : ".fini_array",
               priority);
       s = get_section (buf, SECTION_WRITE, NULL_TREE);
@@ -17051,10 +18130,11 @@ arm_elf_asm_destructor (rtx symbol, int priority)
    decremented/zeroed by arm_asm_output_opcode as the insns are output.  */
 
 /* Returns the index of the ARM condition code string in
-   `arm_condition_codes'.  COMPARISON should be an rtx like
-   `(eq (...) (...))'.  */
-static enum arm_cond_code
-get_arm_condition_code (rtx comparison)
+   `arm_condition_codes', or ARM_NV if the comparison is invalid.
+   COMPARISON should be an rtx like `(eq (...) (...))'.  */
+
+enum arm_cond_code
+maybe_get_arm_condition_code (rtx comparison)
 {
   enum machine_mode mode = GET_MODE (XEXP (comparison, 0));
   enum arm_cond_code code;
@@ -17078,11 +18158,11 @@ get_arm_condition_code (rtx comparison)
     case CC_DLTUmode: code = ARM_CC;
 
     dominance:
-      gcc_assert (comp_code == EQ || comp_code == NE);
-
       if (comp_code == EQ)
        return ARM_INVERSE_CONDITION_CODE (code);
-      return code;
+      if (comp_code == NE)
+       return code;
+      return ARM_NV;
 
     case CC_NOOVmode:
       switch (comp_code)
@@ -17091,7 +18171,7 @@ get_arm_condition_code (rtx comparison)
        case EQ: return ARM_EQ;
        case GE: return ARM_PL;
        case LT: return ARM_MI;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CC_Zmode:
@@ -17099,7 +18179,7 @@ get_arm_condition_code (rtx comparison)
        {
        case NE: return ARM_NE;
        case EQ: return ARM_EQ;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CC_Nmode:
@@ -17107,7 +18187,7 @@ get_arm_condition_code (rtx comparison)
        {
        case NE: return ARM_MI;
        case EQ: return ARM_PL;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CCFPEmode:
@@ -17132,7 +18212,7 @@ get_arm_condition_code (rtx comparison)
          /* UNEQ and LTGT do not have a representation.  */
        case UNEQ: /* Fall through.  */
        case LTGT: /* Fall through.  */
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CC_SWPmode:
@@ -17148,7 +18228,7 @@ get_arm_condition_code (rtx comparison)
        case GTU: return ARM_CC;
        case LEU: return ARM_CS;
        case LTU: return ARM_HI;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CC_Cmode:
@@ -17156,7 +18236,7 @@ get_arm_condition_code (rtx comparison)
        {
        case LTU: return ARM_CS;
        case GEU: return ARM_CC;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CC_CZmode:
@@ -17168,7 +18248,7 @@ get_arm_condition_code (rtx comparison)
        case GTU: return ARM_HI;
        case LEU: return ARM_LS;
        case LTU: return ARM_CC;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CC_NCVmode:
@@ -17178,7 +18258,7 @@ get_arm_condition_code (rtx comparison)
        case LT: return ARM_LT;
        case GEU: return ARM_CS;
        case LTU: return ARM_CC;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     case CCmode:
@@ -17194,13 +18274,22 @@ get_arm_condition_code (rtx comparison)
        case GTU: return ARM_HI;
        case LEU: return ARM_LS;
        case LTU: return ARM_CC;
-       default: gcc_unreachable ();
+       default: return ARM_NV;
        }
 
     default: gcc_unreachable ();
     }
 }
 
+/* Like maybe_get_arm_condition_code, but never return ARM_NV.  */
+static enum arm_cond_code
+get_arm_condition_code (rtx comparison)
+{
+  enum arm_cond_code code = maybe_get_arm_condition_code (comparison);
+  gcc_assert (code != ARM_NV);
+  return code;
+}
+
 /* Tell arm_asm_output_opcode to output IT blocks for conditionally executed
    instructions.  */
 void
@@ -17292,6 +18381,7 @@ arm_final_prescan_insn (rtx insn)
 
   /* If we start with a return insn, we only succeed if we find another one.  */
   int seeking_return = 0;
+  enum rtx_code return_code = UNKNOWN;
 
   /* START_INSN will hold the insn from where we start looking.  This is the
      first insn after the following code_label if REVERSE is true.  */
@@ -17330,7 +18420,7 @@ arm_final_prescan_insn (rtx insn)
          else
            return;
        }
-      else if (GET_CODE (body) == RETURN)
+      else if (ANY_RETURN_P (body))
         {
          start_insn = next_nonnote_insn (start_insn);
          if (GET_CODE (start_insn) == BARRIER)
@@ -17341,6 +18431,7 @@ arm_final_prescan_insn (rtx insn)
            {
              reverse = TRUE;
              seeking_return = 1;
+             return_code = GET_CODE (body);
            }
          else
            return;
@@ -17381,11 +18472,15 @@ arm_final_prescan_insn (rtx insn)
          label = XEXP (XEXP (SET_SRC (body), 2), 0);
          then_not_else = FALSE;
        }
-      else if (GET_CODE (XEXP (SET_SRC (body), 1)) == RETURN)
-       seeking_return = 1;
-      else if (GET_CODE (XEXP (SET_SRC (body), 2)) == RETURN)
+      else if (ANY_RETURN_P (XEXP (SET_SRC (body), 1)))
+       {
+         seeking_return = 1;
+         return_code = GET_CODE (XEXP (SET_SRC (body), 1));
+       }
+      else if (ANY_RETURN_P (XEXP (SET_SRC (body), 2)))
         {
          seeking_return = 1;
+         return_code = GET_CODE (XEXP (SET_SRC (body), 2));
          then_not_else = FALSE;
         }
       else
@@ -17482,12 +18577,11 @@ arm_final_prescan_insn (rtx insn)
                }
              /* Fail if a conditional return is undesirable (e.g. on a
                 StrongARM), but still allow this if optimizing for size.  */
-             else if (GET_CODE (scanbody) == RETURN
+             else if (GET_CODE (scanbody) == return_code
                       && !use_return_insn (TRUE, NULL)
                       && !optimize_size)
                fail = TRUE;
-             else if (GET_CODE (scanbody) == RETURN
-                      && seeking_return)
+             else if (GET_CODE (scanbody) == return_code)
                {
                  arm_ccfsm_state = 2;
                  succeed = TRUE;
@@ -17656,7 +18750,7 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
       if (IS_IWMMXT_REGNUM (regno))
        return VALID_IWMMXT_REG_MODE (mode);
     }
-  
+
   /* 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 very large Neon structure opaque modes in
@@ -17678,6 +18772,29 @@ arm_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
          && regno <= LAST_FPA_REGNUM);
 }
 
+/* Implement MODES_TIEABLE_P.  */
+
+bool
+arm_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
+{
+  if (GET_MODE_CLASS (mode1) == GET_MODE_CLASS (mode2))
+    return true;
+
+  /* We specifically want to allow elements of "structure" modes to
+     be tieable to the structure.  This more general condition allows
+     other rarer situations too.  */
+  if (TARGET_NEON
+      && (VALID_NEON_DREG_MODE (mode1)
+         || VALID_NEON_QREG_MODE (mode1)
+         || VALID_NEON_STRUCT_MODE (mode1))
+      && (VALID_NEON_DREG_MODE (mode2)
+         || VALID_NEON_QREG_MODE (mode2)
+         || VALID_NEON_STRUCT_MODE (mode2)))
+    return true;
+
+  return false;
+}
+
 /* For efficiency and historical reasons LO_REGS, HI_REGS and CC_REGS are
    not used in arm mode.  */
 
@@ -17812,79 +18929,1079 @@ arm_debugger_arg_offset (int value, rtx addr)
   return value;
 }
 \f
-#define def_mbuiltin(MASK, NAME, TYPE, CODE)                           \
-  do                                                                   \
-    {                                                                  \
-      if ((MASK) & insn_flags)                                         \
-        add_builtin_function ((NAME), (TYPE), (CODE),                  \
-                            BUILT_IN_MD, NULL, NULL_TREE);             \
-    }                                                                  \
-  while (0)
+typedef enum {
+  T_V8QI,
+  T_V4HI,
+  T_V2SI,
+  T_V2SF,
+  T_DI,
+  T_V16QI,
+  T_V8HI,
+  T_V4SI,
+  T_V4SF,
+  T_V2DI,
+  T_TI,
+  T_EI,
+  T_OI,
+  T_MAX                /* Size of enum.  Keep last.  */
+} neon_builtin_type_mode;
+
+#define TYPE_MODE_BIT(X) (1 << (X))
+
+#define TB_DREG (TYPE_MODE_BIT (T_V8QI) | TYPE_MODE_BIT (T_V4HI)       \
+                | TYPE_MODE_BIT (T_V2SI) | TYPE_MODE_BIT (T_V2SF)      \
+                | TYPE_MODE_BIT (T_DI))
+#define TB_QREG (TYPE_MODE_BIT (T_V16QI) | TYPE_MODE_BIT (T_V8HI)      \
+                | TYPE_MODE_BIT (T_V4SI) | TYPE_MODE_BIT (T_V4SF)      \
+                | TYPE_MODE_BIT (T_V2DI) | TYPE_MODE_BIT (T_TI))
 
-struct builtin_description
+#define v8qi_UP  T_V8QI
+#define v4hi_UP  T_V4HI
+#define v2si_UP  T_V2SI
+#define v2sf_UP  T_V2SF
+#define di_UP    T_DI
+#define v16qi_UP T_V16QI
+#define v8hi_UP  T_V8HI
+#define v4si_UP  T_V4SI
+#define v4sf_UP  T_V4SF
+#define v2di_UP  T_V2DI
+#define ti_UP   T_TI
+#define ei_UP   T_EI
+#define oi_UP   T_OI
+
+#define UP(X) X##_UP
+
+typedef enum {
+  NEON_BINOP,
+  NEON_TERNOP,
+  NEON_UNOP,
+  NEON_GETLANE,
+  NEON_SETLANE,
+  NEON_CREATE,
+  NEON_DUP,
+  NEON_DUPLANE,
+  NEON_COMBINE,
+  NEON_SPLIT,
+  NEON_LANEMUL,
+  NEON_LANEMULL,
+  NEON_LANEMULH,
+  NEON_LANEMAC,
+  NEON_SCALARMUL,
+  NEON_SCALARMULL,
+  NEON_SCALARMULH,
+  NEON_SCALARMAC,
+  NEON_CONVERT,
+  NEON_FIXCONV,
+  NEON_SELECT,
+  NEON_RESULTPAIR,
+  NEON_REINTERP,
+  NEON_VTBL,
+  NEON_VTBX,
+  NEON_LOAD1,
+  NEON_LOAD1LANE,
+  NEON_STORE1,
+  NEON_STORE1LANE,
+  NEON_LOADSTRUCT,
+  NEON_LOADSTRUCTLANE,
+  NEON_STORESTRUCT,
+  NEON_STORESTRUCTLANE,
+  NEON_LOGICBINOP,
+  NEON_SHIFTINSERT,
+  NEON_SHIFTIMM,
+  NEON_SHIFTACC
+} neon_itype;
+
+typedef struct {
+  const char *name;
+  const neon_itype itype;
+  const neon_builtin_type_mode mode;
+  const enum insn_code code;
+  unsigned int fcode;
+} neon_builtin_datum;
+
+#define CF(N,X) CODE_FOR_neon_##N##X
+
+#define VAR1(T, N, A) \
+  {#N, NEON_##T, UP (A), CF (N, A), 0}
+#define VAR2(T, N, A, B) \
+  VAR1 (T, N, A), \
+  {#N, NEON_##T, UP (B), CF (N, B), 0}
+#define VAR3(T, N, A, B, C) \
+  VAR2 (T, N, A, B), \
+  {#N, NEON_##T, UP (C), CF (N, C), 0}
+#define VAR4(T, N, A, B, C, D) \
+  VAR3 (T, N, A, B, C), \
+  {#N, NEON_##T, UP (D), CF (N, D), 0}
+#define VAR5(T, N, A, B, C, D, E) \
+  VAR4 (T, N, A, B, C, D), \
+  {#N, NEON_##T, UP (E), CF (N, E), 0}
+#define VAR6(T, N, A, B, C, D, E, F) \
+  VAR5 (T, N, A, B, C, D, E), \
+  {#N, NEON_##T, UP (F), CF (N, F), 0}
+#define VAR7(T, N, A, B, C, D, E, F, G) \
+  VAR6 (T, N, A, B, C, D, E, F), \
+  {#N, NEON_##T, UP (G), CF (N, G), 0}
+#define VAR8(T, N, A, B, C, D, E, F, G, H) \
+  VAR7 (T, N, A, B, C, D, E, F, G), \
+  {#N, NEON_##T, UP (H), CF (N, H), 0}
+#define VAR9(T, N, A, B, C, D, E, F, G, H, I) \
+  VAR8 (T, N, A, B, C, D, E, F, G, H), \
+  {#N, NEON_##T, UP (I), CF (N, I), 0}
+#define VAR10(T, N, A, B, C, D, E, F, G, H, I, J) \
+  VAR9 (T, N, A, B, C, D, E, F, G, H, I), \
+  {#N, NEON_##T, UP (J), CF (N, J), 0}
+
+/* The mode entries in the following table correspond to the "key" type of the
+   instruction variant, i.e. equivalent to that which would be specified after
+   the assembler mnemonic, which usually refers to the last vector operand.
+   (Signed/unsigned/polynomial types are not differentiated between though, and
+   are all mapped onto the same mode for a given element size.) The modes
+   listed per instruction should be the same as those defined for that
+   instruction's pattern in neon.md.  */
+
+static neon_builtin_datum neon_builtin_data[] =
 {
-  const unsigned int       mask;
-  const enum insn_code     icode;
-  const char * const       name;
-  const enum arm_builtins  code;
-  const enum rtx_code      comparison;
-  const unsigned int       flag;
+  VAR10 (BINOP, vadd,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR3 (BINOP, vaddl, v8qi, v4hi, v2si),
+  VAR3 (BINOP, vaddw, v8qi, v4hi, v2si),
+  VAR6 (BINOP, vhadd, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR8 (BINOP, vqadd, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR3 (BINOP, vaddhn, v8hi, v4si, v2di),
+  VAR8 (BINOP, vmul, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR8 (TERNOP, vmla, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR3 (TERNOP, vmlal, v8qi, v4hi, v2si),
+  VAR8 (TERNOP, vmls, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR3 (TERNOP, vmlsl, v8qi, v4hi, v2si),
+  VAR4 (BINOP, vqdmulh, v4hi, v2si, v8hi, v4si),
+  VAR2 (TERNOP, vqdmlal, v4hi, v2si),
+  VAR2 (TERNOP, vqdmlsl, v4hi, v2si),
+  VAR3 (BINOP, vmull, v8qi, v4hi, v2si),
+  VAR2 (SCALARMULL, vmull_n, v4hi, v2si),
+  VAR2 (LANEMULL, vmull_lane, v4hi, v2si),
+  VAR2 (SCALARMULL, vqdmull_n, v4hi, v2si),
+  VAR2 (LANEMULL, vqdmull_lane, v4hi, v2si),
+  VAR4 (SCALARMULH, vqdmulh_n, v4hi, v2si, v8hi, v4si),
+  VAR4 (LANEMULH, vqdmulh_lane, v4hi, v2si, v8hi, v4si),
+  VAR2 (BINOP, vqdmull, v4hi, v2si),
+  VAR8 (BINOP, vshl, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR8 (BINOP, vqshl, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR8 (SHIFTIMM, vshr_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR3 (SHIFTIMM, vshrn_n, v8hi, v4si, v2di),
+  VAR3 (SHIFTIMM, vqshrn_n, v8hi, v4si, v2di),
+  VAR3 (SHIFTIMM, vqshrun_n, v8hi, v4si, v2di),
+  VAR8 (SHIFTIMM, vshl_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR8 (SHIFTIMM, vqshl_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR8 (SHIFTIMM, vqshlu_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR3 (SHIFTIMM, vshll_n, v8qi, v4hi, v2si),
+  VAR8 (SHIFTACC, vsra_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR10 (BINOP, vsub,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR3 (BINOP, vsubl, v8qi, v4hi, v2si),
+  VAR3 (BINOP, vsubw, v8qi, v4hi, v2si),
+  VAR8 (BINOP, vqsub, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR6 (BINOP, vhsub, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR3 (BINOP, vsubhn, v8hi, v4si, v2di),
+  VAR8 (BINOP, vceq, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR8 (BINOP, vcge, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR6 (BINOP, vcgeu, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR8 (BINOP, vcgt, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR6 (BINOP, vcgtu, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR2 (BINOP, vcage, v2sf, v4sf),
+  VAR2 (BINOP, vcagt, v2sf, v4sf),
+  VAR6 (BINOP, vtst, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR8 (BINOP, vabd, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR3 (BINOP, vabdl, v8qi, v4hi, v2si),
+  VAR6 (TERNOP, vaba, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR3 (TERNOP, vabal, v8qi, v4hi, v2si),
+  VAR8 (BINOP, vmax, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR8 (BINOP, vmin, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR4 (BINOP, vpadd, v8qi, v4hi, v2si, v2sf),
+  VAR6 (UNOP, vpaddl, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR6 (BINOP, vpadal, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR4 (BINOP, vpmax, v8qi, v4hi, v2si, v2sf),
+  VAR4 (BINOP, vpmin, v8qi, v4hi, v2si, v2sf),
+  VAR2 (BINOP, vrecps, v2sf, v4sf),
+  VAR2 (BINOP, vrsqrts, v2sf, v4sf),
+  VAR8 (SHIFTINSERT, vsri_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR8 (SHIFTINSERT, vsli_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di),
+  VAR8 (UNOP, vabs, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR6 (UNOP, vqabs, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR8 (UNOP, vneg, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR6 (UNOP, vqneg, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR6 (UNOP, vcls, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR6 (UNOP, vclz, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  VAR2 (UNOP, vcnt, v8qi, v16qi),
+  VAR4 (UNOP, vrecpe, v2si, v2sf, v4si, v4sf),
+  VAR4 (UNOP, vrsqrte, v2si, v2sf, v4si, v4sf),
+  VAR6 (UNOP, vmvn, v8qi, v4hi, v2si, v16qi, v8hi, v4si),
+  /* FIXME: vget_lane supports more variants than this!  */
+  VAR10 (GETLANE, vget_lane,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (SETLANE, vset_lane,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (CREATE, vcreate, v8qi, v4hi, v2si, v2sf, di),
+  VAR10 (DUP, vdup_n,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (DUPLANE, vdup_lane,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (COMBINE, vcombine, v8qi, v4hi, v2si, v2sf, di),
+  VAR5 (SPLIT, vget_high, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (SPLIT, vget_low, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR3 (UNOP, vmovn, v8hi, v4si, v2di),
+  VAR3 (UNOP, vqmovn, v8hi, v4si, v2di),
+  VAR3 (UNOP, vqmovun, v8hi, v4si, v2di),
+  VAR3 (UNOP, vmovl, v8qi, v4hi, v2si),
+  VAR6 (LANEMUL, vmul_lane, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR6 (LANEMAC, vmla_lane, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR2 (LANEMAC, vmlal_lane, v4hi, v2si),
+  VAR2 (LANEMAC, vqdmlal_lane, v4hi, v2si),
+  VAR6 (LANEMAC, vmls_lane, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR2 (LANEMAC, vmlsl_lane, v4hi, v2si),
+  VAR2 (LANEMAC, vqdmlsl_lane, v4hi, v2si),
+  VAR6 (SCALARMUL, vmul_n, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR6 (SCALARMAC, vmla_n, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR2 (SCALARMAC, vmlal_n, v4hi, v2si),
+  VAR2 (SCALARMAC, vqdmlal_n, v4hi, v2si),
+  VAR6 (SCALARMAC, vmls_n, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR2 (SCALARMAC, vmlsl_n, v4hi, v2si),
+  VAR2 (SCALARMAC, vqdmlsl_n, v4hi, v2si),
+  VAR10 (BINOP, vext,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR8 (UNOP, vrev64, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR4 (UNOP, vrev32, v8qi, v4hi, v16qi, v8hi),
+  VAR2 (UNOP, vrev16, v8qi, v16qi),
+  VAR4 (CONVERT, vcvt, v2si, v2sf, v4si, v4sf),
+  VAR4 (FIXCONV, vcvt_n, v2si, v2sf, v4si, v4sf),
+  VAR10 (SELECT, vbsl,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR1 (VTBL, vtbl1, v8qi),
+  VAR1 (VTBL, vtbl2, v8qi),
+  VAR1 (VTBL, vtbl3, v8qi),
+  VAR1 (VTBL, vtbl4, v8qi),
+  VAR1 (VTBX, vtbx1, v8qi),
+  VAR1 (VTBX, vtbx2, v8qi),
+  VAR1 (VTBX, vtbx3, v8qi),
+  VAR1 (VTBX, vtbx4, v8qi),
+  VAR8 (RESULTPAIR, vtrn, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR8 (RESULTPAIR, vzip, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR8 (RESULTPAIR, vuzp, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf),
+  VAR5 (REINTERP, vreinterpretv8qi, v8qi, v4hi, v2si, v2sf, di),
+  VAR5 (REINTERP, vreinterpretv4hi, v8qi, v4hi, v2si, v2sf, di),
+  VAR5 (REINTERP, vreinterpretv2si, v8qi, v4hi, v2si, v2sf, di),
+  VAR5 (REINTERP, vreinterpretv2sf, v8qi, v4hi, v2si, v2sf, di),
+  VAR5 (REINTERP, vreinterpretdi, v8qi, v4hi, v2si, v2sf, di),
+  VAR5 (REINTERP, vreinterpretv16qi, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (REINTERP, vreinterpretv8hi, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (REINTERP, vreinterpretv4si, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (REINTERP, vreinterpretv4sf, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR5 (REINTERP, vreinterpretv2di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (LOAD1, vld1,
+         v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (LOAD1LANE, vld1_lane,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (LOAD1, vld1_dup,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (STORE1, vst1,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (STORE1LANE, vst1_lane,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR9 (LOADSTRUCT,
+       vld2, v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf),
+  VAR7 (LOADSTRUCTLANE, vld2_lane,
+       v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR5 (LOADSTRUCT, vld2_dup, v8qi, v4hi, v2si, v2sf, di),
+  VAR9 (STORESTRUCT, vst2,
+       v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf),
+  VAR7 (STORESTRUCTLANE, vst2_lane,
+       v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR9 (LOADSTRUCT,
+       vld3, v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf),
+  VAR7 (LOADSTRUCTLANE, vld3_lane,
+       v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR5 (LOADSTRUCT, vld3_dup, v8qi, v4hi, v2si, v2sf, di),
+  VAR9 (STORESTRUCT, vst3,
+       v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf),
+  VAR7 (STORESTRUCTLANE, vst3_lane,
+       v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR9 (LOADSTRUCT, vld4,
+       v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf),
+  VAR7 (LOADSTRUCTLANE, vld4_lane,
+       v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR5 (LOADSTRUCT, vld4_dup, v8qi, v4hi, v2si, v2sf, di),
+  VAR9 (STORESTRUCT, vst4,
+       v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf),
+  VAR7 (STORESTRUCTLANE, vst4_lane,
+       v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf),
+  VAR10 (LOGICBINOP, vand,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (LOGICBINOP, vorr,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (BINOP, veor,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (LOGICBINOP, vbic,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di),
+  VAR10 (LOGICBINOP, vorn,
+        v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di)
 };
 
-static const struct builtin_description bdesc_2arg[] =
+#undef CF
+#undef VAR1
+#undef VAR2
+#undef VAR3
+#undef VAR4
+#undef VAR5
+#undef VAR6
+#undef VAR7
+#undef VAR8
+#undef VAR9
+#undef VAR10
+
+/* Neon defines builtins from ARM_BUILTIN_MAX upwards, though they don't have
+   symbolic names defined here (which would require too much duplication).
+   FIXME?  */
+enum arm_builtins
+{
+  ARM_BUILTIN_GETWCX,
+  ARM_BUILTIN_SETWCX,
+
+  ARM_BUILTIN_WZERO,
+
+  ARM_BUILTIN_WAVG2BR,
+  ARM_BUILTIN_WAVG2HR,
+  ARM_BUILTIN_WAVG2B,
+  ARM_BUILTIN_WAVG2H,
+
+  ARM_BUILTIN_WACCB,
+  ARM_BUILTIN_WACCH,
+  ARM_BUILTIN_WACCW,
+
+  ARM_BUILTIN_WMACS,
+  ARM_BUILTIN_WMACSZ,
+  ARM_BUILTIN_WMACU,
+  ARM_BUILTIN_WMACUZ,
+
+  ARM_BUILTIN_WSADB,
+  ARM_BUILTIN_WSADBZ,
+  ARM_BUILTIN_WSADH,
+  ARM_BUILTIN_WSADHZ,
+
+  ARM_BUILTIN_WALIGN,
+
+  ARM_BUILTIN_TMIA,
+  ARM_BUILTIN_TMIAPH,
+  ARM_BUILTIN_TMIABB,
+  ARM_BUILTIN_TMIABT,
+  ARM_BUILTIN_TMIATB,
+  ARM_BUILTIN_TMIATT,
+
+  ARM_BUILTIN_TMOVMSKB,
+  ARM_BUILTIN_TMOVMSKH,
+  ARM_BUILTIN_TMOVMSKW,
+
+  ARM_BUILTIN_TBCSTB,
+  ARM_BUILTIN_TBCSTH,
+  ARM_BUILTIN_TBCSTW,
+
+  ARM_BUILTIN_WMADDS,
+  ARM_BUILTIN_WMADDU,
+
+  ARM_BUILTIN_WPACKHSS,
+  ARM_BUILTIN_WPACKWSS,
+  ARM_BUILTIN_WPACKDSS,
+  ARM_BUILTIN_WPACKHUS,
+  ARM_BUILTIN_WPACKWUS,
+  ARM_BUILTIN_WPACKDUS,
+
+  ARM_BUILTIN_WADDB,
+  ARM_BUILTIN_WADDH,
+  ARM_BUILTIN_WADDW,
+  ARM_BUILTIN_WADDSSB,
+  ARM_BUILTIN_WADDSSH,
+  ARM_BUILTIN_WADDSSW,
+  ARM_BUILTIN_WADDUSB,
+  ARM_BUILTIN_WADDUSH,
+  ARM_BUILTIN_WADDUSW,
+  ARM_BUILTIN_WSUBB,
+  ARM_BUILTIN_WSUBH,
+  ARM_BUILTIN_WSUBW,
+  ARM_BUILTIN_WSUBSSB,
+  ARM_BUILTIN_WSUBSSH,
+  ARM_BUILTIN_WSUBSSW,
+  ARM_BUILTIN_WSUBUSB,
+  ARM_BUILTIN_WSUBUSH,
+  ARM_BUILTIN_WSUBUSW,
+
+  ARM_BUILTIN_WAND,
+  ARM_BUILTIN_WANDN,
+  ARM_BUILTIN_WOR,
+  ARM_BUILTIN_WXOR,
+
+  ARM_BUILTIN_WCMPEQB,
+  ARM_BUILTIN_WCMPEQH,
+  ARM_BUILTIN_WCMPEQW,
+  ARM_BUILTIN_WCMPGTUB,
+  ARM_BUILTIN_WCMPGTUH,
+  ARM_BUILTIN_WCMPGTUW,
+  ARM_BUILTIN_WCMPGTSB,
+  ARM_BUILTIN_WCMPGTSH,
+  ARM_BUILTIN_WCMPGTSW,
+
+  ARM_BUILTIN_TEXTRMSB,
+  ARM_BUILTIN_TEXTRMSH,
+  ARM_BUILTIN_TEXTRMSW,
+  ARM_BUILTIN_TEXTRMUB,
+  ARM_BUILTIN_TEXTRMUH,
+  ARM_BUILTIN_TEXTRMUW,
+  ARM_BUILTIN_TINSRB,
+  ARM_BUILTIN_TINSRH,
+  ARM_BUILTIN_TINSRW,
+
+  ARM_BUILTIN_WMAXSW,
+  ARM_BUILTIN_WMAXSH,
+  ARM_BUILTIN_WMAXSB,
+  ARM_BUILTIN_WMAXUW,
+  ARM_BUILTIN_WMAXUH,
+  ARM_BUILTIN_WMAXUB,
+  ARM_BUILTIN_WMINSW,
+  ARM_BUILTIN_WMINSH,
+  ARM_BUILTIN_WMINSB,
+  ARM_BUILTIN_WMINUW,
+  ARM_BUILTIN_WMINUH,
+  ARM_BUILTIN_WMINUB,
+
+  ARM_BUILTIN_WMULUM,
+  ARM_BUILTIN_WMULSM,
+  ARM_BUILTIN_WMULUL,
+
+  ARM_BUILTIN_PSADBH,
+  ARM_BUILTIN_WSHUFH,
+
+  ARM_BUILTIN_WSLLH,
+  ARM_BUILTIN_WSLLW,
+  ARM_BUILTIN_WSLLD,
+  ARM_BUILTIN_WSRAH,
+  ARM_BUILTIN_WSRAW,
+  ARM_BUILTIN_WSRAD,
+  ARM_BUILTIN_WSRLH,
+  ARM_BUILTIN_WSRLW,
+  ARM_BUILTIN_WSRLD,
+  ARM_BUILTIN_WRORH,
+  ARM_BUILTIN_WRORW,
+  ARM_BUILTIN_WRORD,
+  ARM_BUILTIN_WSLLHI,
+  ARM_BUILTIN_WSLLWI,
+  ARM_BUILTIN_WSLLDI,
+  ARM_BUILTIN_WSRAHI,
+  ARM_BUILTIN_WSRAWI,
+  ARM_BUILTIN_WSRADI,
+  ARM_BUILTIN_WSRLHI,
+  ARM_BUILTIN_WSRLWI,
+  ARM_BUILTIN_WSRLDI,
+  ARM_BUILTIN_WRORHI,
+  ARM_BUILTIN_WRORWI,
+  ARM_BUILTIN_WRORDI,
+
+  ARM_BUILTIN_WUNPCKIHB,
+  ARM_BUILTIN_WUNPCKIHH,
+  ARM_BUILTIN_WUNPCKIHW,
+  ARM_BUILTIN_WUNPCKILB,
+  ARM_BUILTIN_WUNPCKILH,
+  ARM_BUILTIN_WUNPCKILW,
+
+  ARM_BUILTIN_WUNPCKEHSB,
+  ARM_BUILTIN_WUNPCKEHSH,
+  ARM_BUILTIN_WUNPCKEHSW,
+  ARM_BUILTIN_WUNPCKEHUB,
+  ARM_BUILTIN_WUNPCKEHUH,
+  ARM_BUILTIN_WUNPCKEHUW,
+  ARM_BUILTIN_WUNPCKELSB,
+  ARM_BUILTIN_WUNPCKELSH,
+  ARM_BUILTIN_WUNPCKELSW,
+  ARM_BUILTIN_WUNPCKELUB,
+  ARM_BUILTIN_WUNPCKELUH,
+  ARM_BUILTIN_WUNPCKELUW,
+
+  ARM_BUILTIN_THREAD_POINTER,
+
+  ARM_BUILTIN_NEON_BASE,
+
+  ARM_BUILTIN_MAX = ARM_BUILTIN_NEON_BASE + ARRAY_SIZE (neon_builtin_data)
+};
+
+static GTY(()) tree arm_builtin_decls[ARM_BUILTIN_MAX];
+
+static void
+arm_init_neon_builtins (void)
 {
-#define IWMMXT_BUILTIN(code, string, builtin) \
-  { FL_IWMMXT, CODE_FOR_##code, "__builtin_arm_" string, \
-    ARM_BUILTIN_##builtin, UNKNOWN, 0 },
+  unsigned int i, fcode;
+  tree decl;
 
-  IWMMXT_BUILTIN (addv8qi3, "waddb", WADDB)
-  IWMMXT_BUILTIN (addv4hi3, "waddh", WADDH)
-  IWMMXT_BUILTIN (addv2si3, "waddw", WADDW)
-  IWMMXT_BUILTIN (subv8qi3, "wsubb", WSUBB)
-  IWMMXT_BUILTIN (subv4hi3, "wsubh", WSUBH)
-  IWMMXT_BUILTIN (subv2si3, "wsubw", WSUBW)
-  IWMMXT_BUILTIN (ssaddv8qi3, "waddbss", WADDSSB)
-  IWMMXT_BUILTIN (ssaddv4hi3, "waddhss", WADDSSH)
-  IWMMXT_BUILTIN (ssaddv2si3, "waddwss", WADDSSW)
-  IWMMXT_BUILTIN (sssubv8qi3, "wsubbss", WSUBSSB)
-  IWMMXT_BUILTIN (sssubv4hi3, "wsubhss", WSUBSSH)
-  IWMMXT_BUILTIN (sssubv2si3, "wsubwss", WSUBSSW)
-  IWMMXT_BUILTIN (usaddv8qi3, "waddbus", WADDUSB)
-  IWMMXT_BUILTIN (usaddv4hi3, "waddhus", WADDUSH)
-  IWMMXT_BUILTIN (usaddv2si3, "waddwus", WADDUSW)
-  IWMMXT_BUILTIN (ussubv8qi3, "wsubbus", WSUBUSB)
-  IWMMXT_BUILTIN (ussubv4hi3, "wsubhus", WSUBUSH)
-  IWMMXT_BUILTIN (ussubv2si3, "wsubwus", WSUBUSW)
-  IWMMXT_BUILTIN (mulv4hi3, "wmulul", WMULUL)
-  IWMMXT_BUILTIN (smulv4hi3_highpart, "wmulsm", WMULSM)
-  IWMMXT_BUILTIN (umulv4hi3_highpart, "wmulum", WMULUM)
-  IWMMXT_BUILTIN (eqv8qi3, "wcmpeqb", WCMPEQB)
-  IWMMXT_BUILTIN (eqv4hi3, "wcmpeqh", WCMPEQH)
-  IWMMXT_BUILTIN (eqv2si3, "wcmpeqw", WCMPEQW)
-  IWMMXT_BUILTIN (gtuv8qi3, "wcmpgtub", WCMPGTUB)
-  IWMMXT_BUILTIN (gtuv4hi3, "wcmpgtuh", WCMPGTUH)
-  IWMMXT_BUILTIN (gtuv2si3, "wcmpgtuw", WCMPGTUW)
-  IWMMXT_BUILTIN (gtv8qi3, "wcmpgtsb", WCMPGTSB)
-  IWMMXT_BUILTIN (gtv4hi3, "wcmpgtsh", WCMPGTSH)
-  IWMMXT_BUILTIN (gtv2si3, "wcmpgtsw", WCMPGTSW)
-  IWMMXT_BUILTIN (umaxv8qi3, "wmaxub", WMAXUB)
-  IWMMXT_BUILTIN (smaxv8qi3, "wmaxsb", WMAXSB)
-  IWMMXT_BUILTIN (umaxv4hi3, "wmaxuh", WMAXUH)
-  IWMMXT_BUILTIN (smaxv4hi3, "wmaxsh", WMAXSH)
-  IWMMXT_BUILTIN (umaxv2si3, "wmaxuw", WMAXUW)
-  IWMMXT_BUILTIN (smaxv2si3, "wmaxsw", WMAXSW)
-  IWMMXT_BUILTIN (uminv8qi3, "wminub", WMINUB)
-  IWMMXT_BUILTIN (sminv8qi3, "wminsb", WMINSB)
-  IWMMXT_BUILTIN (uminv4hi3, "wminuh", WMINUH)
-  IWMMXT_BUILTIN (sminv4hi3, "wminsh", WMINSH)
-  IWMMXT_BUILTIN (uminv2si3, "wminuw", WMINUW)
-  IWMMXT_BUILTIN (sminv2si3, "wminsw", WMINSW)
-  IWMMXT_BUILTIN (iwmmxt_anddi3, "wand", WAND)
-  IWMMXT_BUILTIN (iwmmxt_nanddi3, "wandn", WANDN)
-  IWMMXT_BUILTIN (iwmmxt_iordi3, "wor", WOR)
-  IWMMXT_BUILTIN (iwmmxt_xordi3, "wxor", WXOR)
-  IWMMXT_BUILTIN (iwmmxt_uavgv8qi3, "wavg2b", WAVG2B)
-  IWMMXT_BUILTIN (iwmmxt_uavgv4hi3, "wavg2h", WAVG2H)
+  tree neon_intQI_type_node;
+  tree neon_intHI_type_node;
+  tree neon_polyQI_type_node;
+  tree neon_polyHI_type_node;
+  tree neon_intSI_type_node;
+  tree neon_intDI_type_node;
+  tree neon_float_type_node;
+
+  tree intQI_pointer_node;
+  tree intHI_pointer_node;
+  tree intSI_pointer_node;
+  tree intDI_pointer_node;
+  tree float_pointer_node;
+
+  tree const_intQI_node;
+  tree const_intHI_node;
+  tree const_intSI_node;
+  tree const_intDI_node;
+  tree const_float_node;
+
+  tree const_intQI_pointer_node;
+  tree const_intHI_pointer_node;
+  tree const_intSI_pointer_node;
+  tree const_intDI_pointer_node;
+  tree const_float_pointer_node;
+
+  tree V8QI_type_node;
+  tree V4HI_type_node;
+  tree V2SI_type_node;
+  tree V2SF_type_node;
+  tree V16QI_type_node;
+  tree V8HI_type_node;
+  tree V4SI_type_node;
+  tree V4SF_type_node;
+  tree V2DI_type_node;
+
+  tree intUQI_type_node;
+  tree intUHI_type_node;
+  tree intUSI_type_node;
+  tree intUDI_type_node;
+
+  tree intEI_type_node;
+  tree intOI_type_node;
+  tree intCI_type_node;
+  tree intXI_type_node;
+
+  tree V8QI_pointer_node;
+  tree V4HI_pointer_node;
+  tree V2SI_pointer_node;
+  tree V2SF_pointer_node;
+  tree V16QI_pointer_node;
+  tree V8HI_pointer_node;
+  tree V4SI_pointer_node;
+  tree V4SF_pointer_node;
+  tree V2DI_pointer_node;
+
+  tree void_ftype_pv8qi_v8qi_v8qi;
+  tree void_ftype_pv4hi_v4hi_v4hi;
+  tree void_ftype_pv2si_v2si_v2si;
+  tree void_ftype_pv2sf_v2sf_v2sf;
+  tree void_ftype_pdi_di_di;
+  tree void_ftype_pv16qi_v16qi_v16qi;
+  tree void_ftype_pv8hi_v8hi_v8hi;
+  tree void_ftype_pv4si_v4si_v4si;
+  tree void_ftype_pv4sf_v4sf_v4sf;
+  tree void_ftype_pv2di_v2di_v2di;
+
+  tree reinterp_ftype_dreg[5][5];
+  tree reinterp_ftype_qreg[5][5];
+  tree dreg_types[5], qreg_types[5];
+
+  /* Create distinguished type nodes for NEON vector element types,
+     and pointers to values of such types, so we can detect them later.  */
+  neon_intQI_type_node = make_signed_type (GET_MODE_PRECISION (QImode));
+  neon_intHI_type_node = make_signed_type (GET_MODE_PRECISION (HImode));
+  neon_polyQI_type_node = make_signed_type (GET_MODE_PRECISION (QImode));
+  neon_polyHI_type_node = make_signed_type (GET_MODE_PRECISION (HImode));
+  neon_intSI_type_node = make_signed_type (GET_MODE_PRECISION (SImode));
+  neon_intDI_type_node = make_signed_type (GET_MODE_PRECISION (DImode));
+  neon_float_type_node = make_node (REAL_TYPE);
+  TYPE_PRECISION (neon_float_type_node) = FLOAT_TYPE_SIZE;
+  layout_type (neon_float_type_node);
+
+  /* Define typedefs which exactly correspond to the modes we are basing vector
+     types on.  If you change these names you'll need to change
+     the table used by arm_mangle_type too.  */
+  (*lang_hooks.types.register_builtin_type) (neon_intQI_type_node,
+                                            "__builtin_neon_qi");
+  (*lang_hooks.types.register_builtin_type) (neon_intHI_type_node,
+                                            "__builtin_neon_hi");
+  (*lang_hooks.types.register_builtin_type) (neon_intSI_type_node,
+                                            "__builtin_neon_si");
+  (*lang_hooks.types.register_builtin_type) (neon_float_type_node,
+                                            "__builtin_neon_sf");
+  (*lang_hooks.types.register_builtin_type) (neon_intDI_type_node,
+                                            "__builtin_neon_di");
+  (*lang_hooks.types.register_builtin_type) (neon_polyQI_type_node,
+                                            "__builtin_neon_poly8");
+  (*lang_hooks.types.register_builtin_type) (neon_polyHI_type_node,
+                                            "__builtin_neon_poly16");
+
+  intQI_pointer_node = build_pointer_type (neon_intQI_type_node);
+  intHI_pointer_node = build_pointer_type (neon_intHI_type_node);
+  intSI_pointer_node = build_pointer_type (neon_intSI_type_node);
+  intDI_pointer_node = build_pointer_type (neon_intDI_type_node);
+  float_pointer_node = build_pointer_type (neon_float_type_node);
+
+  /* Next create constant-qualified versions of the above types.  */
+  const_intQI_node = build_qualified_type (neon_intQI_type_node,
+                                          TYPE_QUAL_CONST);
+  const_intHI_node = build_qualified_type (neon_intHI_type_node,
+                                          TYPE_QUAL_CONST);
+  const_intSI_node = build_qualified_type (neon_intSI_type_node,
+                                          TYPE_QUAL_CONST);
+  const_intDI_node = build_qualified_type (neon_intDI_type_node,
+                                          TYPE_QUAL_CONST);
+  const_float_node = build_qualified_type (neon_float_type_node,
+                                          TYPE_QUAL_CONST);
+
+  const_intQI_pointer_node = build_pointer_type (const_intQI_node);
+  const_intHI_pointer_node = build_pointer_type (const_intHI_node);
+  const_intSI_pointer_node = build_pointer_type (const_intSI_node);
+  const_intDI_pointer_node = build_pointer_type (const_intDI_node);
+  const_float_pointer_node = build_pointer_type (const_float_node);
+
+  /* Now create vector types based on our NEON element types.  */
+  /* 64-bit vectors.  */
+  V8QI_type_node =
+    build_vector_type_for_mode (neon_intQI_type_node, V8QImode);
+  V4HI_type_node =
+    build_vector_type_for_mode (neon_intHI_type_node, V4HImode);
+  V2SI_type_node =
+    build_vector_type_for_mode (neon_intSI_type_node, V2SImode);
+  V2SF_type_node =
+    build_vector_type_for_mode (neon_float_type_node, V2SFmode);
+  /* 128-bit vectors.  */
+  V16QI_type_node =
+    build_vector_type_for_mode (neon_intQI_type_node, V16QImode);
+  V8HI_type_node =
+    build_vector_type_for_mode (neon_intHI_type_node, V8HImode);
+  V4SI_type_node =
+    build_vector_type_for_mode (neon_intSI_type_node, V4SImode);
+  V4SF_type_node =
+    build_vector_type_for_mode (neon_float_type_node, V4SFmode);
+  V2DI_type_node =
+    build_vector_type_for_mode (neon_intDI_type_node, V2DImode);
+
+  /* Unsigned integer types for various mode sizes.  */
+  intUQI_type_node = make_unsigned_type (GET_MODE_PRECISION (QImode));
+  intUHI_type_node = make_unsigned_type (GET_MODE_PRECISION (HImode));
+  intUSI_type_node = make_unsigned_type (GET_MODE_PRECISION (SImode));
+  intUDI_type_node = make_unsigned_type (GET_MODE_PRECISION (DImode));
+
+  (*lang_hooks.types.register_builtin_type) (intUQI_type_node,
+                                            "__builtin_neon_uqi");
+  (*lang_hooks.types.register_builtin_type) (intUHI_type_node,
+                                            "__builtin_neon_uhi");
+  (*lang_hooks.types.register_builtin_type) (intUSI_type_node,
+                                            "__builtin_neon_usi");
+  (*lang_hooks.types.register_builtin_type) (intUDI_type_node,
+                                            "__builtin_neon_udi");
+
+  /* Opaque integer types for structures of vectors.  */
+  intEI_type_node = make_signed_type (GET_MODE_PRECISION (EImode));
+  intOI_type_node = make_signed_type (GET_MODE_PRECISION (OImode));
+  intCI_type_node = make_signed_type (GET_MODE_PRECISION (CImode));
+  intXI_type_node = make_signed_type (GET_MODE_PRECISION (XImode));
+
+  (*lang_hooks.types.register_builtin_type) (intTI_type_node,
+                                            "__builtin_neon_ti");
+  (*lang_hooks.types.register_builtin_type) (intEI_type_node,
+                                            "__builtin_neon_ei");
+  (*lang_hooks.types.register_builtin_type) (intOI_type_node,
+                                            "__builtin_neon_oi");
+  (*lang_hooks.types.register_builtin_type) (intCI_type_node,
+                                            "__builtin_neon_ci");
+  (*lang_hooks.types.register_builtin_type) (intXI_type_node,
+                                            "__builtin_neon_xi");
+
+  /* Pointers to vector types.  */
+  V8QI_pointer_node = build_pointer_type (V8QI_type_node);
+  V4HI_pointer_node = build_pointer_type (V4HI_type_node);
+  V2SI_pointer_node = build_pointer_type (V2SI_type_node);
+  V2SF_pointer_node = build_pointer_type (V2SF_type_node);
+  V16QI_pointer_node = build_pointer_type (V16QI_type_node);
+  V8HI_pointer_node = build_pointer_type (V8HI_type_node);
+  V4SI_pointer_node = build_pointer_type (V4SI_type_node);
+  V4SF_pointer_node = build_pointer_type (V4SF_type_node);
+  V2DI_pointer_node = build_pointer_type (V2DI_type_node);
+
+  /* Operations which return results as pairs.  */
+  void_ftype_pv8qi_v8qi_v8qi =
+    build_function_type_list (void_type_node, V8QI_pointer_node, V8QI_type_node,
+                             V8QI_type_node, NULL);
+  void_ftype_pv4hi_v4hi_v4hi =
+    build_function_type_list (void_type_node, V4HI_pointer_node, V4HI_type_node,
+                             V4HI_type_node, NULL);
+  void_ftype_pv2si_v2si_v2si =
+    build_function_type_list (void_type_node, V2SI_pointer_node, V2SI_type_node,
+                             V2SI_type_node, NULL);
+  void_ftype_pv2sf_v2sf_v2sf =
+    build_function_type_list (void_type_node, V2SF_pointer_node, V2SF_type_node,
+                             V2SF_type_node, NULL);
+  void_ftype_pdi_di_di =
+    build_function_type_list (void_type_node, intDI_pointer_node,
+                             neon_intDI_type_node, neon_intDI_type_node, NULL);
+  void_ftype_pv16qi_v16qi_v16qi =
+    build_function_type_list (void_type_node, V16QI_pointer_node,
+                             V16QI_type_node, V16QI_type_node, NULL);
+  void_ftype_pv8hi_v8hi_v8hi =
+    build_function_type_list (void_type_node, V8HI_pointer_node, V8HI_type_node,
+                             V8HI_type_node, NULL);
+  void_ftype_pv4si_v4si_v4si =
+    build_function_type_list (void_type_node, V4SI_pointer_node, V4SI_type_node,
+                             V4SI_type_node, NULL);
+  void_ftype_pv4sf_v4sf_v4sf =
+    build_function_type_list (void_type_node, V4SF_pointer_node, V4SF_type_node,
+                             V4SF_type_node, NULL);
+  void_ftype_pv2di_v2di_v2di =
+    build_function_type_list (void_type_node, V2DI_pointer_node, V2DI_type_node,
+                             V2DI_type_node, NULL);
+
+  dreg_types[0] = V8QI_type_node;
+  dreg_types[1] = V4HI_type_node;
+  dreg_types[2] = V2SI_type_node;
+  dreg_types[3] = V2SF_type_node;
+  dreg_types[4] = neon_intDI_type_node;
+
+  qreg_types[0] = V16QI_type_node;
+  qreg_types[1] = V8HI_type_node;
+  qreg_types[2] = V4SI_type_node;
+  qreg_types[3] = V4SF_type_node;
+  qreg_types[4] = V2DI_type_node;
+
+  for (i = 0; i < 5; i++)
+    {
+      int j;
+      for (j = 0; j < 5; j++)
+        {
+          reinterp_ftype_dreg[i][j]
+            = build_function_type_list (dreg_types[i], dreg_types[j], NULL);
+          reinterp_ftype_qreg[i][j]
+            = build_function_type_list (qreg_types[i], qreg_types[j], NULL);
+        }
+    }
+
+  for (i = 0, fcode = ARM_BUILTIN_NEON_BASE;
+       i < ARRAY_SIZE (neon_builtin_data);
+       i++, fcode++)
+    {
+      neon_builtin_datum *d = &neon_builtin_data[i];
+
+      const char* const modenames[] = {
+       "v8qi", "v4hi", "v2si", "v2sf", "di",
+       "v16qi", "v8hi", "v4si", "v4sf", "v2di",
+       "ti", "ei", "oi"
+      };
+      char namebuf[60];
+      tree ftype = NULL;
+      int is_load = 0, is_store = 0;
+
+      gcc_assert (ARRAY_SIZE (modenames) == T_MAX);
+
+      d->fcode = fcode;
+
+      switch (d->itype)
+       {
+       case NEON_LOAD1:
+       case NEON_LOAD1LANE:
+       case NEON_LOADSTRUCT:
+       case NEON_LOADSTRUCTLANE:
+         is_load = 1;
+         /* Fall through.  */
+       case NEON_STORE1:
+       case NEON_STORE1LANE:
+       case NEON_STORESTRUCT:
+       case NEON_STORESTRUCTLANE:
+         if (!is_load)
+           is_store = 1;
+         /* Fall through.  */
+       case NEON_UNOP:
+       case NEON_BINOP:
+       case NEON_LOGICBINOP:
+       case NEON_SHIFTINSERT:
+       case NEON_TERNOP:
+       case NEON_GETLANE:
+       case NEON_SETLANE:
+       case NEON_CREATE:
+       case NEON_DUP:
+       case NEON_DUPLANE:
+       case NEON_SHIFTIMM:
+       case NEON_SHIFTACC:
+       case NEON_COMBINE:
+       case NEON_SPLIT:
+       case NEON_CONVERT:
+       case NEON_FIXCONV:
+       case NEON_LANEMUL:
+       case NEON_LANEMULL:
+       case NEON_LANEMULH:
+       case NEON_LANEMAC:
+       case NEON_SCALARMUL:
+       case NEON_SCALARMULL:
+       case NEON_SCALARMULH:
+       case NEON_SCALARMAC:
+       case NEON_SELECT:
+       case NEON_VTBL:
+       case NEON_VTBX:
+         {
+           int k;
+           tree return_type = void_type_node, args = void_list_node;
+
+           /* Build a function type directly from the insn_data for
+              this builtin.  The build_function_type() function takes
+              care of removing duplicates for us.  */
+           for (k = insn_data[d->code].n_generator_args - 1; k >= 0; k--)
+             {
+               tree eltype;
+
+               if (is_load && k == 1)
+                 {
+                   /* Neon load patterns always have the memory
+                      operand in the operand 1 position.  */
+                   gcc_assert (insn_data[d->code].operand[k].predicate
+                               == neon_struct_operand);
+
+                   switch (d->mode)
+                     {
+                     case T_V8QI:
+                     case T_V16QI:
+                       eltype = const_intQI_pointer_node;
+                       break;
+
+                     case T_V4HI:
+                     case T_V8HI:
+                       eltype = const_intHI_pointer_node;
+                       break;
+
+                     case T_V2SI:
+                     case T_V4SI:
+                       eltype = const_intSI_pointer_node;
+                       break;
+
+                     case T_V2SF:
+                     case T_V4SF:
+                       eltype = const_float_pointer_node;
+                       break;
+
+                     case T_DI:
+                     case T_V2DI:
+                       eltype = const_intDI_pointer_node;
+                       break;
+
+                     default: gcc_unreachable ();
+                     }
+                 }
+               else if (is_store && k == 0)
+                 {
+                   /* Similarly, Neon store patterns use operand 0 as
+                      the memory location to store to.  */
+                   gcc_assert (insn_data[d->code].operand[k].predicate
+                               == neon_struct_operand);
+
+                   switch (d->mode)
+                     {
+                     case T_V8QI:
+                     case T_V16QI:
+                       eltype = intQI_pointer_node;
+                       break;
+
+                     case T_V4HI:
+                     case T_V8HI:
+                       eltype = intHI_pointer_node;
+                       break;
+
+                     case T_V2SI:
+                     case T_V4SI:
+                       eltype = intSI_pointer_node;
+                       break;
+
+                     case T_V2SF:
+                     case T_V4SF:
+                       eltype = float_pointer_node;
+                       break;
+
+                     case T_DI:
+                     case T_V2DI:
+                       eltype = intDI_pointer_node;
+                       break;
+
+                     default: gcc_unreachable ();
+                     }
+                 }
+               else
+                 {
+                   switch (insn_data[d->code].operand[k].mode)
+                     {
+                     case VOIDmode: eltype = void_type_node; break;
+                       /* Scalars.  */
+                     case QImode: eltype = neon_intQI_type_node; break;
+                     case HImode: eltype = neon_intHI_type_node; break;
+                     case SImode: eltype = neon_intSI_type_node; break;
+                     case SFmode: eltype = neon_float_type_node; break;
+                     case DImode: eltype = neon_intDI_type_node; break;
+                     case TImode: eltype = intTI_type_node; break;
+                     case EImode: eltype = intEI_type_node; break;
+                     case OImode: eltype = intOI_type_node; break;
+                     case CImode: eltype = intCI_type_node; break;
+                     case XImode: eltype = intXI_type_node; break;
+                       /* 64-bit vectors.  */
+                     case V8QImode: eltype = V8QI_type_node; break;
+                     case V4HImode: eltype = V4HI_type_node; break;
+                     case V2SImode: eltype = V2SI_type_node; break;
+                     case V2SFmode: eltype = V2SF_type_node; break;
+                       /* 128-bit vectors.  */
+                     case V16QImode: eltype = V16QI_type_node; break;
+                     case V8HImode: eltype = V8HI_type_node; break;
+                     case V4SImode: eltype = V4SI_type_node; break;
+                     case V4SFmode: eltype = V4SF_type_node; break;
+                     case V2DImode: eltype = V2DI_type_node; break;
+                     default: gcc_unreachable ();
+                     }
+                 }
+
+               if (k == 0 && !is_store)
+                 return_type = eltype;
+               else
+                 args = tree_cons (NULL_TREE, eltype, args);
+             }
+
+           ftype = build_function_type (return_type, args);
+         }
+         break;
+
+       case NEON_RESULTPAIR:
+         {
+           switch (insn_data[d->code].operand[1].mode)
+             {
+             case V8QImode: ftype = void_ftype_pv8qi_v8qi_v8qi; break;
+             case V4HImode: ftype = void_ftype_pv4hi_v4hi_v4hi; break;
+             case V2SImode: ftype = void_ftype_pv2si_v2si_v2si; break;
+             case V2SFmode: ftype = void_ftype_pv2sf_v2sf_v2sf; break;
+             case DImode: ftype = void_ftype_pdi_di_di; break;
+             case V16QImode: ftype = void_ftype_pv16qi_v16qi_v16qi; break;
+             case V8HImode: ftype = void_ftype_pv8hi_v8hi_v8hi; break;
+             case V4SImode: ftype = void_ftype_pv4si_v4si_v4si; break;
+             case V4SFmode: ftype = void_ftype_pv4sf_v4sf_v4sf; break;
+             case V2DImode: ftype = void_ftype_pv2di_v2di_v2di; break;
+             default: gcc_unreachable ();
+             }
+         }
+         break;
+
+       case NEON_REINTERP:
+         {
+           /* We iterate over 5 doubleword types, then 5 quadword
+              types.  */
+           int rhs = d->mode % 5;
+           switch (insn_data[d->code].operand[0].mode)
+             {
+             case V8QImode: ftype = reinterp_ftype_dreg[0][rhs]; break;
+             case V4HImode: ftype = reinterp_ftype_dreg[1][rhs]; break;
+             case V2SImode: ftype = reinterp_ftype_dreg[2][rhs]; break;
+             case V2SFmode: ftype = reinterp_ftype_dreg[3][rhs]; break;
+             case DImode: ftype = reinterp_ftype_dreg[4][rhs]; break;
+             case V16QImode: ftype = reinterp_ftype_qreg[0][rhs]; break;
+             case V8HImode: ftype = reinterp_ftype_qreg[1][rhs]; break;
+             case V4SImode: ftype = reinterp_ftype_qreg[2][rhs]; break;
+             case V4SFmode: ftype = reinterp_ftype_qreg[3][rhs]; break;
+             case V2DImode: ftype = reinterp_ftype_qreg[4][rhs]; break;
+             default: gcc_unreachable ();
+             }
+         }
+         break;
+
+       default:
+         gcc_unreachable ();
+       }
+
+      gcc_assert (ftype != NULL);
+
+      sprintf (namebuf, "__builtin_neon_%s%s", d->name, modenames[d->mode]);
+
+      decl = add_builtin_function (namebuf, ftype, fcode, BUILT_IN_MD, NULL,
+                                  NULL_TREE);
+      arm_builtin_decls[fcode] = decl;
+    }
+}
+
+#define def_mbuiltin(MASK, NAME, TYPE, CODE)                           \
+  do                                                                   \
+    {                                                                  \
+      if ((MASK) & insn_flags)                                         \
+       {                                                               \
+         tree bdecl;                                                   \
+         bdecl = add_builtin_function ((NAME), (TYPE), (CODE),         \
+                                       BUILT_IN_MD, NULL, NULL_TREE);  \
+         arm_builtin_decls[CODE] = bdecl;                              \
+       }                                                               \
+    }                                                                  \
+  while (0)
+
+struct builtin_description
+{
+  const unsigned int       mask;
+  const enum insn_code     icode;
+  const char * const       name;
+  const enum arm_builtins  code;
+  const enum rtx_code      comparison;
+  const unsigned int       flag;
+};
+  
+static const struct builtin_description bdesc_2arg[] =
+{
+#define IWMMXT_BUILTIN(code, string, builtin) \
+  { FL_IWMMXT, CODE_FOR_##code, "__builtin_arm_" string, \
+    ARM_BUILTIN_##builtin, UNKNOWN, 0 },
+
+  IWMMXT_BUILTIN (addv8qi3, "waddb", WADDB)
+  IWMMXT_BUILTIN (addv4hi3, "waddh", WADDH)
+  IWMMXT_BUILTIN (addv2si3, "waddw", WADDW)
+  IWMMXT_BUILTIN (subv8qi3, "wsubb", WSUBB)
+  IWMMXT_BUILTIN (subv4hi3, "wsubh", WSUBH)
+  IWMMXT_BUILTIN (subv2si3, "wsubw", WSUBW)
+  IWMMXT_BUILTIN (ssaddv8qi3, "waddbss", WADDSSB)
+  IWMMXT_BUILTIN (ssaddv4hi3, "waddhss", WADDSSH)
+  IWMMXT_BUILTIN (ssaddv2si3, "waddwss", WADDSSW)
+  IWMMXT_BUILTIN (sssubv8qi3, "wsubbss", WSUBSSB)
+  IWMMXT_BUILTIN (sssubv4hi3, "wsubhss", WSUBSSH)
+  IWMMXT_BUILTIN (sssubv2si3, "wsubwss", WSUBSSW)
+  IWMMXT_BUILTIN (usaddv8qi3, "waddbus", WADDUSB)
+  IWMMXT_BUILTIN (usaddv4hi3, "waddhus", WADDUSH)
+  IWMMXT_BUILTIN (usaddv2si3, "waddwus", WADDUSW)
+  IWMMXT_BUILTIN (ussubv8qi3, "wsubbus", WSUBUSB)
+  IWMMXT_BUILTIN (ussubv4hi3, "wsubhus", WSUBUSH)
+  IWMMXT_BUILTIN (ussubv2si3, "wsubwus", WSUBUSW)
+  IWMMXT_BUILTIN (mulv4hi3, "wmulul", WMULUL)
+  IWMMXT_BUILTIN (smulv4hi3_highpart, "wmulsm", WMULSM)
+  IWMMXT_BUILTIN (umulv4hi3_highpart, "wmulum", WMULUM)
+  IWMMXT_BUILTIN (eqv8qi3, "wcmpeqb", WCMPEQB)
+  IWMMXT_BUILTIN (eqv4hi3, "wcmpeqh", WCMPEQH)
+  IWMMXT_BUILTIN (eqv2si3, "wcmpeqw", WCMPEQW)
+  IWMMXT_BUILTIN (gtuv8qi3, "wcmpgtub", WCMPGTUB)
+  IWMMXT_BUILTIN (gtuv4hi3, "wcmpgtuh", WCMPGTUH)
+  IWMMXT_BUILTIN (gtuv2si3, "wcmpgtuw", WCMPGTUW)
+  IWMMXT_BUILTIN (gtv8qi3, "wcmpgtsb", WCMPGTSB)
+  IWMMXT_BUILTIN (gtv4hi3, "wcmpgtsh", WCMPGTSH)
+  IWMMXT_BUILTIN (gtv2si3, "wcmpgtsw", WCMPGTSW)
+  IWMMXT_BUILTIN (umaxv8qi3, "wmaxub", WMAXUB)
+  IWMMXT_BUILTIN (smaxv8qi3, "wmaxsb", WMAXSB)
+  IWMMXT_BUILTIN (umaxv4hi3, "wmaxuh", WMAXUH)
+  IWMMXT_BUILTIN (smaxv4hi3, "wmaxsh", WMAXSH)
+  IWMMXT_BUILTIN (umaxv2si3, "wmaxuw", WMAXUW)
+  IWMMXT_BUILTIN (smaxv2si3, "wmaxsw", WMAXSW)
+  IWMMXT_BUILTIN (uminv8qi3, "wminub", WMINUB)
+  IWMMXT_BUILTIN (sminv8qi3, "wminsb", WMINSB)
+  IWMMXT_BUILTIN (uminv4hi3, "wminuh", WMINUH)
+  IWMMXT_BUILTIN (sminv4hi3, "wminsh", WMINSH)
+  IWMMXT_BUILTIN (uminv2si3, "wminuw", WMINUW)
+  IWMMXT_BUILTIN (sminv2si3, "wminsw", WMINSW)
+  IWMMXT_BUILTIN (iwmmxt_anddi3, "wand", WAND)
+  IWMMXT_BUILTIN (iwmmxt_nanddi3, "wandn", WANDN)
+  IWMMXT_BUILTIN (iwmmxt_iordi3, "wor", WOR)
+  IWMMXT_BUILTIN (iwmmxt_xordi3, "wxor", WXOR)
+  IWMMXT_BUILTIN (iwmmxt_uavgv8qi3, "wavg2b", WAVG2B)
+  IWMMXT_BUILTIN (iwmmxt_uavgv4hi3, "wavg2h", WAVG2H)
   IWMMXT_BUILTIN (iwmmxt_uavgrndv8qi3, "wavg2br", WAVG2BR)
   IWMMXT_BUILTIN (iwmmxt_uavgrndv4hi3, "wavg2hr", WAVG2HR)
   IWMMXT_BUILTIN (iwmmxt_wunpckilb, "wunpckilb", WUNPCKILB)
@@ -17955,203 +20072,145 @@ static const struct builtin_description bdesc_1arg[] =
   IWMMXT_BUILTIN (iwmmxt_wunpckelsw, "wunpckelsw", WUNPCKELSW)
 };
 
-/* Set up all the iWMMXt builtins.  This is
-   not called if TARGET_IWMMXT is zero.  */
+/* Set up all the iWMMXt builtins.  This is not called if
+   TARGET_IWMMXT is zero.  */
 
 static void
 arm_init_iwmmxt_builtins (void)
 {
   const struct builtin_description * d;
   size_t i;
-  tree endlink = void_list_node;
 
   tree V2SI_type_node = build_vector_type_for_mode (intSI_type_node, V2SImode);
   tree V4HI_type_node = build_vector_type_for_mode (intHI_type_node, V4HImode);
   tree V8QI_type_node = build_vector_type_for_mode (intQI_type_node, V8QImode);
 
   tree int_ftype_int
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, integer_type_node, endlink));
+    = build_function_type_list (integer_type_node,
+                               integer_type_node, NULL_TREE);
   tree v8qi_ftype_v8qi_v8qi_int
-    = build_function_type (V8QI_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     tree_cons (NULL_TREE, V8QI_type_node,
-                                                tree_cons (NULL_TREE,
-                                                           integer_type_node,
-                                                           endlink))));
+    = build_function_type_list (V8QI_type_node,
+                               V8QI_type_node, V8QI_type_node,
+                               integer_type_node, NULL_TREE);
   tree v4hi_ftype_v4hi_int
-    = build_function_type (V4HI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (V4HI_type_node,
+                               V4HI_type_node, integer_type_node, NULL_TREE);
   tree v2si_ftype_v2si_int
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (V2SI_type_node,
+                               V2SI_type_node, integer_type_node, NULL_TREE);
   tree v2si_ftype_di_di
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, long_long_integer_type_node,
-                                     tree_cons (NULL_TREE, long_long_integer_type_node,
-                                                endlink)));
+    = build_function_type_list (V2SI_type_node,
+                               long_long_integer_type_node,
+                               long_long_integer_type_node,
+                               NULL_TREE);
   tree di_ftype_di_int
-    = build_function_type (long_long_integer_type_node,
-                          tree_cons (NULL_TREE, long_long_integer_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (long_long_integer_type_node,
+                               long_long_integer_type_node,
+                               integer_type_node, NULL_TREE);
   tree di_ftype_di_int_int
-    = build_function_type (long_long_integer_type_node,
-                          tree_cons (NULL_TREE, long_long_integer_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                tree_cons (NULL_TREE,
-                                                           integer_type_node,
-                                                           endlink))));
+    = build_function_type_list (long_long_integer_type_node,
+                               long_long_integer_type_node,
+                               integer_type_node,
+                               integer_type_node, NULL_TREE);
   tree int_ftype_v8qi
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     endlink));
+    = build_function_type_list (integer_type_node,
+                               V8QI_type_node, NULL_TREE);
   tree int_ftype_v4hi
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     endlink));
+    = build_function_type_list (integer_type_node,
+                               V4HI_type_node, NULL_TREE);
   tree int_ftype_v2si
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     endlink));
+    = build_function_type_list (integer_type_node,
+                               V2SI_type_node, NULL_TREE);
   tree int_ftype_v8qi_int
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (integer_type_node,
+                               V8QI_type_node, integer_type_node, NULL_TREE);
   tree int_ftype_v4hi_int
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (integer_type_node,
+                               V4HI_type_node, integer_type_node, NULL_TREE);
   tree int_ftype_v2si_int
-    = build_function_type (integer_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (integer_type_node,
+                               V2SI_type_node, integer_type_node, NULL_TREE);
   tree v8qi_ftype_v8qi_int_int
-    = build_function_type (V8QI_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                tree_cons (NULL_TREE,
-                                                           integer_type_node,
-                                                           endlink))));
+    = build_function_type_list (V8QI_type_node,
+                               V8QI_type_node, integer_type_node,
+                               integer_type_node, NULL_TREE);
   tree v4hi_ftype_v4hi_int_int
-    = build_function_type (V4HI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                tree_cons (NULL_TREE,
-                                                           integer_type_node,
-                                                           endlink))));
+    = build_function_type_list (V4HI_type_node,
+                               V4HI_type_node, integer_type_node,
+                               integer_type_node, NULL_TREE);
   tree v2si_ftype_v2si_int_int
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                tree_cons (NULL_TREE,
-                                                           integer_type_node,
-                                                           endlink))));
+    = build_function_type_list (V2SI_type_node,
+                               V2SI_type_node, integer_type_node,
+                               integer_type_node, NULL_TREE);
   /* Miscellaneous.  */
   tree v8qi_ftype_v4hi_v4hi
-    = build_function_type (V8QI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, V4HI_type_node,
-                                                endlink)));
+    = build_function_type_list (V8QI_type_node,
+                               V4HI_type_node, V4HI_type_node, NULL_TREE);
   tree v4hi_ftype_v2si_v2si
-    = build_function_type (V4HI_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     tree_cons (NULL_TREE, V2SI_type_node,
-                                                endlink)));
+    = build_function_type_list (V4HI_type_node,
+                               V2SI_type_node, V2SI_type_node, NULL_TREE);
   tree v2si_ftype_v4hi_v4hi
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, V4HI_type_node,
-                                                endlink)));
+    = build_function_type_list (V2SI_type_node,
+                               V4HI_type_node, V4HI_type_node, NULL_TREE);
   tree v2si_ftype_v8qi_v8qi
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     tree_cons (NULL_TREE, V8QI_type_node,
-                                                endlink)));
+    = build_function_type_list (V2SI_type_node,
+                               V8QI_type_node, V8QI_type_node, NULL_TREE);
   tree v4hi_ftype_v4hi_di
-    = build_function_type (V4HI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE,
-                                                long_long_integer_type_node,
-                                                endlink)));
+    = build_function_type_list (V4HI_type_node,
+                               V4HI_type_node, long_long_integer_type_node,
+                               NULL_TREE);
   tree v2si_ftype_v2si_di
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     tree_cons (NULL_TREE,
-                                                long_long_integer_type_node,
-                                                endlink)));
+    = build_function_type_list (V2SI_type_node,
+                               V2SI_type_node, long_long_integer_type_node,
+                               NULL_TREE);
   tree void_ftype_int_int
-    = build_function_type (void_type_node,
-                          tree_cons (NULL_TREE, integer_type_node,
-                                     tree_cons (NULL_TREE, integer_type_node,
-                                                endlink)));
+    = build_function_type_list (void_type_node,
+                               integer_type_node, integer_type_node,
+                               NULL_TREE);
   tree di_ftype_void
-    = build_function_type (long_long_unsigned_type_node, endlink);
+    = build_function_type_list (long_long_unsigned_type_node, NULL_TREE);
   tree di_ftype_v8qi
-    = build_function_type (long_long_integer_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     endlink));
+    = build_function_type_list (long_long_integer_type_node,
+                               V8QI_type_node, NULL_TREE);
   tree di_ftype_v4hi
-    = build_function_type (long_long_integer_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     endlink));
+    = build_function_type_list (long_long_integer_type_node,
+                               V4HI_type_node, NULL_TREE);
   tree di_ftype_v2si
-    = build_function_type (long_long_integer_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     endlink));
+    = build_function_type_list (long_long_integer_type_node,
+                               V2SI_type_node, NULL_TREE);
   tree v2si_ftype_v4hi
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     endlink));
+    = build_function_type_list (V2SI_type_node,
+                               V4HI_type_node, NULL_TREE);
   tree v4hi_ftype_v8qi
-    = build_function_type (V4HI_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     endlink));
+    = build_function_type_list (V4HI_type_node,
+                               V8QI_type_node, NULL_TREE);
 
   tree di_ftype_di_v4hi_v4hi
-    = build_function_type (long_long_unsigned_type_node,
-                          tree_cons (NULL_TREE,
-                                     long_long_unsigned_type_node,
-                                     tree_cons (NULL_TREE, V4HI_type_node,
-                                                tree_cons (NULL_TREE,
-                                                           V4HI_type_node,
-                                                           endlink))));
+    = build_function_type_list (long_long_unsigned_type_node,
+                               long_long_unsigned_type_node,
+                               V4HI_type_node, V4HI_type_node,
+                               NULL_TREE);
 
   tree di_ftype_v4hi_v4hi
-    = build_function_type (long_long_unsigned_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, V4HI_type_node,
-                                                endlink)));
+    = build_function_type_list (long_long_unsigned_type_node,
+                               V4HI_type_node,V4HI_type_node,
+                               NULL_TREE);
 
   /* Normal vector binops.  */
   tree v8qi_ftype_v8qi_v8qi
-    = build_function_type (V8QI_type_node,
-                          tree_cons (NULL_TREE, V8QI_type_node,
-                                     tree_cons (NULL_TREE, V8QI_type_node,
-                                                endlink)));
+    = build_function_type_list (V8QI_type_node,
+                               V8QI_type_node, V8QI_type_node, NULL_TREE);
   tree v4hi_ftype_v4hi_v4hi
-    = build_function_type (V4HI_type_node,
-                          tree_cons (NULL_TREE, V4HI_type_node,
-                                     tree_cons (NULL_TREE, V4HI_type_node,
-                                                endlink)));
+    = build_function_type_list (V4HI_type_node,
+                               V4HI_type_node,V4HI_type_node, NULL_TREE);
   tree v2si_ftype_v2si_v2si
-    = build_function_type (V2SI_type_node,
-                          tree_cons (NULL_TREE, V2SI_type_node,
-                                     tree_cons (NULL_TREE, V2SI_type_node,
-                                                endlink)));
+    = build_function_type_list (V2SI_type_node,
+                               V2SI_type_node, V2SI_type_node, NULL_TREE);
   tree di_ftype_di_di
-    = build_function_type (long_long_unsigned_type_node,
-                          tree_cons (NULL_TREE, long_long_unsigned_type_node,
-                                     tree_cons (NULL_TREE,
-                                                long_long_unsigned_type_node,
-                                                endlink)));
+    = build_function_type_list (long_long_unsigned_type_node,
+                               long_long_unsigned_type_node,
+                               long_long_unsigned_type_node,
+                               NULL_TREE);
 
   /* Add all builtins that are more or less simple operations on two
      operands.  */
@@ -18190,95 +20249,101 @@ arm_init_iwmmxt_builtins (void)
     }
 
   /* Add the remaining MMX insns with somewhat more complicated types.  */
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wzero", di_ftype_void, ARM_BUILTIN_WZERO);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_setwcx", void_ftype_int_int, ARM_BUILTIN_SETWCX);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_getwcx", int_ftype_int, ARM_BUILTIN_GETWCX);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllh", v4hi_ftype_v4hi_di, ARM_BUILTIN_WSLLH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllw", v2si_ftype_v2si_di, ARM_BUILTIN_WSLLW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wslld", di_ftype_di_di, ARM_BUILTIN_WSLLD);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllhi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSLLHI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsllwi", v2si_ftype_v2si_int, ARM_BUILTIN_WSLLWI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wslldi", di_ftype_di_int, ARM_BUILTIN_WSLLDI);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlh", v4hi_ftype_v4hi_di, ARM_BUILTIN_WSRLH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlw", v2si_ftype_v2si_di, ARM_BUILTIN_WSRLW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrld", di_ftype_di_di, ARM_BUILTIN_WSRLD);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlhi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSRLHI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrlwi", v2si_ftype_v2si_int, ARM_BUILTIN_WSRLWI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrldi", di_ftype_di_int, ARM_BUILTIN_WSRLDI);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrah", v4hi_ftype_v4hi_di, ARM_BUILTIN_WSRAH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsraw", v2si_ftype_v2si_di, ARM_BUILTIN_WSRAW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrad", di_ftype_di_di, ARM_BUILTIN_WSRAD);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrahi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSRAHI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsrawi", v2si_ftype_v2si_int, ARM_BUILTIN_WSRAWI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsradi", di_ftype_di_int, ARM_BUILTIN_WSRADI);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorh", v4hi_ftype_v4hi_di, ARM_BUILTIN_WRORH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorw", v2si_ftype_v2si_di, ARM_BUILTIN_WRORW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrord", di_ftype_di_di, ARM_BUILTIN_WRORD);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorhi", v4hi_ftype_v4hi_int, ARM_BUILTIN_WRORHI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrorwi", v2si_ftype_v2si_int, ARM_BUILTIN_WRORWI);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wrordi", di_ftype_di_int, ARM_BUILTIN_WRORDI);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wshufh", v4hi_ftype_v4hi_int, ARM_BUILTIN_WSHUFH);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadb", v2si_ftype_v8qi_v8qi, ARM_BUILTIN_WSADB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadh", v2si_ftype_v4hi_v4hi, ARM_BUILTIN_WSADH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadbz", v2si_ftype_v8qi_v8qi, ARM_BUILTIN_WSADBZ);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wsadhz", v2si_ftype_v4hi_v4hi, ARM_BUILTIN_WSADHZ);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmsb", int_ftype_v8qi_int, ARM_BUILTIN_TEXTRMSB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmsh", int_ftype_v4hi_int, ARM_BUILTIN_TEXTRMSH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmsw", int_ftype_v2si_int, ARM_BUILTIN_TEXTRMSW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmub", int_ftype_v8qi_int, ARM_BUILTIN_TEXTRMUB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmuh", int_ftype_v4hi_int, ARM_BUILTIN_TEXTRMUH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_textrmuw", int_ftype_v2si_int, ARM_BUILTIN_TEXTRMUW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tinsrb", v8qi_ftype_v8qi_int_int, ARM_BUILTIN_TINSRB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tinsrh", v4hi_ftype_v4hi_int_int, ARM_BUILTIN_TINSRH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tinsrw", v2si_ftype_v2si_int_int, ARM_BUILTIN_TINSRW);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_waccb", di_ftype_v8qi, ARM_BUILTIN_WACCB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wacch", di_ftype_v4hi, ARM_BUILTIN_WACCH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_waccw", di_ftype_v2si, ARM_BUILTIN_WACCW);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmovmskb", int_ftype_v8qi, ARM_BUILTIN_TMOVMSKB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmovmskh", int_ftype_v4hi, ARM_BUILTIN_TMOVMSKH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmovmskw", int_ftype_v2si, ARM_BUILTIN_TMOVMSKW);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackhss", v8qi_ftype_v4hi_v4hi, ARM_BUILTIN_WPACKHSS);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackhus", v8qi_ftype_v4hi_v4hi, ARM_BUILTIN_WPACKHUS);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackwus", v4hi_ftype_v2si_v2si, ARM_BUILTIN_WPACKWUS);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackwss", v4hi_ftype_v2si_v2si, ARM_BUILTIN_WPACKWSS);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackdus", v2si_ftype_di_di, ARM_BUILTIN_WPACKDUS);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wpackdss", v2si_ftype_di_di, ARM_BUILTIN_WPACKDSS);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehub", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKEHUB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehuh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKEHUH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehuw", di_ftype_v2si, ARM_BUILTIN_WUNPCKEHUW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehsb", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKEHSB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehsh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKEHSH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckehsw", di_ftype_v2si, ARM_BUILTIN_WUNPCKEHSW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelub", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKELUB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckeluh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKELUH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckeluw", di_ftype_v2si, ARM_BUILTIN_WUNPCKELUW);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelsb", v4hi_ftype_v8qi, ARM_BUILTIN_WUNPCKELSB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelsh", v2si_ftype_v4hi, ARM_BUILTIN_WUNPCKELSH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wunpckelsw", di_ftype_v2si, ARM_BUILTIN_WUNPCKELSW);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacs", di_ftype_di_v4hi_v4hi, ARM_BUILTIN_WMACS);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacsz", di_ftype_v4hi_v4hi, ARM_BUILTIN_WMACSZ);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacu", di_ftype_di_v4hi_v4hi, ARM_BUILTIN_WMACU);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_wmacuz", di_ftype_v4hi_v4hi, ARM_BUILTIN_WMACUZ);
-
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_walign", v8qi_ftype_v8qi_v8qi_int, ARM_BUILTIN_WALIGN);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmia", di_ftype_di_int_int, ARM_BUILTIN_TMIA);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiaph", di_ftype_di_int_int, ARM_BUILTIN_TMIAPH);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiabb", di_ftype_di_int_int, ARM_BUILTIN_TMIABB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiabt", di_ftype_di_int_int, ARM_BUILTIN_TMIABT);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiatb", di_ftype_di_int_int, ARM_BUILTIN_TMIATB);
-  def_mbuiltin (FL_IWMMXT, "__builtin_arm_tmiatt", di_ftype_di_int_int, ARM_BUILTIN_TMIATT);
+#define iwmmx_mbuiltin(NAME, TYPE, CODE)                       \
+  def_mbuiltin (FL_IWMMXT, "__builtin_arm_" NAME, (TYPE),      \
+               ARM_BUILTIN_ ## CODE)
+
+  iwmmx_mbuiltin ("wzero", di_ftype_void, WZERO);
+  iwmmx_mbuiltin ("setwcx", void_ftype_int_int, SETWCX);
+  iwmmx_mbuiltin ("getwcx", int_ftype_int, GETWCX);
+
+  iwmmx_mbuiltin ("wsllh", v4hi_ftype_v4hi_di, WSLLH);
+  iwmmx_mbuiltin ("wsllw", v2si_ftype_v2si_di, WSLLW);
+  iwmmx_mbuiltin ("wslld", di_ftype_di_di, WSLLD);
+  iwmmx_mbuiltin ("wsllhi", v4hi_ftype_v4hi_int, WSLLHI);
+  iwmmx_mbuiltin ("wsllwi", v2si_ftype_v2si_int, WSLLWI);
+  iwmmx_mbuiltin ("wslldi", di_ftype_di_int, WSLLDI);
+
+  iwmmx_mbuiltin ("wsrlh", v4hi_ftype_v4hi_di, WSRLH);
+  iwmmx_mbuiltin ("wsrlw", v2si_ftype_v2si_di, WSRLW);
+  iwmmx_mbuiltin ("wsrld", di_ftype_di_di, WSRLD);
+  iwmmx_mbuiltin ("wsrlhi", v4hi_ftype_v4hi_int, WSRLHI);
+  iwmmx_mbuiltin ("wsrlwi", v2si_ftype_v2si_int, WSRLWI);
+  iwmmx_mbuiltin ("wsrldi", di_ftype_di_int, WSRLDI);
+
+  iwmmx_mbuiltin ("wsrah", v4hi_ftype_v4hi_di, WSRAH);
+  iwmmx_mbuiltin ("wsraw", v2si_ftype_v2si_di, WSRAW);
+  iwmmx_mbuiltin ("wsrad", di_ftype_di_di, WSRAD);
+  iwmmx_mbuiltin ("wsrahi", v4hi_ftype_v4hi_int, WSRAHI);
+  iwmmx_mbuiltin ("wsrawi", v2si_ftype_v2si_int, WSRAWI);
+  iwmmx_mbuiltin ("wsradi", di_ftype_di_int, WSRADI);
+
+  iwmmx_mbuiltin ("wrorh", v4hi_ftype_v4hi_di, WRORH);
+  iwmmx_mbuiltin ("wrorw", v2si_ftype_v2si_di, WRORW);
+  iwmmx_mbuiltin ("wrord", di_ftype_di_di, WRORD);
+  iwmmx_mbuiltin ("wrorhi", v4hi_ftype_v4hi_int, WRORHI);
+  iwmmx_mbuiltin ("wrorwi", v2si_ftype_v2si_int, WRORWI);
+  iwmmx_mbuiltin ("wrordi", di_ftype_di_int, WRORDI);
+
+  iwmmx_mbuiltin ("wshufh", v4hi_ftype_v4hi_int, WSHUFH);
+
+  iwmmx_mbuiltin ("wsadb", v2si_ftype_v8qi_v8qi, WSADB);
+  iwmmx_mbuiltin ("wsadh", v2si_ftype_v4hi_v4hi, WSADH);
+  iwmmx_mbuiltin ("wsadbz", v2si_ftype_v8qi_v8qi, WSADBZ);
+  iwmmx_mbuiltin ("wsadhz", v2si_ftype_v4hi_v4hi, WSADHZ);
+
+  iwmmx_mbuiltin ("textrmsb", int_ftype_v8qi_int, TEXTRMSB);
+  iwmmx_mbuiltin ("textrmsh", int_ftype_v4hi_int, TEXTRMSH);
+  iwmmx_mbuiltin ("textrmsw", int_ftype_v2si_int, TEXTRMSW);
+  iwmmx_mbuiltin ("textrmub", int_ftype_v8qi_int, TEXTRMUB);
+  iwmmx_mbuiltin ("textrmuh", int_ftype_v4hi_int, TEXTRMUH);
+  iwmmx_mbuiltin ("textrmuw", int_ftype_v2si_int, TEXTRMUW);
+  iwmmx_mbuiltin ("tinsrb", v8qi_ftype_v8qi_int_int, TINSRB);
+  iwmmx_mbuiltin ("tinsrh", v4hi_ftype_v4hi_int_int, TINSRH);
+  iwmmx_mbuiltin ("tinsrw", v2si_ftype_v2si_int_int, TINSRW);
+
+  iwmmx_mbuiltin ("waccb", di_ftype_v8qi, WACCB);
+  iwmmx_mbuiltin ("wacch", di_ftype_v4hi, WACCH);
+  iwmmx_mbuiltin ("waccw", di_ftype_v2si, WACCW);
+
+  iwmmx_mbuiltin ("tmovmskb", int_ftype_v8qi, TMOVMSKB);
+  iwmmx_mbuiltin ("tmovmskh", int_ftype_v4hi, TMOVMSKH);
+  iwmmx_mbuiltin ("tmovmskw", int_ftype_v2si, TMOVMSKW);
+
+  iwmmx_mbuiltin ("wpackhss", v8qi_ftype_v4hi_v4hi, WPACKHSS);
+  iwmmx_mbuiltin ("wpackhus", v8qi_ftype_v4hi_v4hi, WPACKHUS);
+  iwmmx_mbuiltin ("wpackwus", v4hi_ftype_v2si_v2si, WPACKWUS);
+  iwmmx_mbuiltin ("wpackwss", v4hi_ftype_v2si_v2si, WPACKWSS);
+  iwmmx_mbuiltin ("wpackdus", v2si_ftype_di_di, WPACKDUS);
+  iwmmx_mbuiltin ("wpackdss", v2si_ftype_di_di, WPACKDSS);
+
+  iwmmx_mbuiltin ("wunpckehub", v4hi_ftype_v8qi, WUNPCKEHUB);
+  iwmmx_mbuiltin ("wunpckehuh", v2si_ftype_v4hi, WUNPCKEHUH);
+  iwmmx_mbuiltin ("wunpckehuw", di_ftype_v2si, WUNPCKEHUW);
+  iwmmx_mbuiltin ("wunpckehsb", v4hi_ftype_v8qi, WUNPCKEHSB);
+  iwmmx_mbuiltin ("wunpckehsh", v2si_ftype_v4hi, WUNPCKEHSH);
+  iwmmx_mbuiltin ("wunpckehsw", di_ftype_v2si, WUNPCKEHSW);
+  iwmmx_mbuiltin ("wunpckelub", v4hi_ftype_v8qi, WUNPCKELUB);
+  iwmmx_mbuiltin ("wunpckeluh", v2si_ftype_v4hi, WUNPCKELUH);
+  iwmmx_mbuiltin ("wunpckeluw", di_ftype_v2si, WUNPCKELUW);
+  iwmmx_mbuiltin ("wunpckelsb", v4hi_ftype_v8qi, WUNPCKELSB);
+  iwmmx_mbuiltin ("wunpckelsh", v2si_ftype_v4hi, WUNPCKELSH);
+  iwmmx_mbuiltin ("wunpckelsw", di_ftype_v2si, WUNPCKELSW);
+
+  iwmmx_mbuiltin ("wmacs", di_ftype_di_v4hi_v4hi, WMACS);
+  iwmmx_mbuiltin ("wmacsz", di_ftype_v4hi_v4hi, WMACSZ);
+  iwmmx_mbuiltin ("wmacu", di_ftype_di_v4hi_v4hi, WMACU);
+  iwmmx_mbuiltin ("wmacuz", di_ftype_v4hi_v4hi, WMACUZ);
+
+  iwmmx_mbuiltin ("walign", v8qi_ftype_v8qi_v8qi_int, WALIGN);
+  iwmmx_mbuiltin ("tmia", di_ftype_di_int_int, TMIA);
+  iwmmx_mbuiltin ("tmiaph", di_ftype_di_int_int, TMIAPH);
+  iwmmx_mbuiltin ("tmiabb", di_ftype_di_int_int, TMIABB);
+  iwmmx_mbuiltin ("tmiabt", di_ftype_di_int_int, TMIABT);
+  iwmmx_mbuiltin ("tmiatb", di_ftype_di_int_int, TMIATB);
+  iwmmx_mbuiltin ("tmiatt", di_ftype_di_int_int, TMIATT);
+
+#undef iwmmx_mbuiltin
 }
 
 static void
@@ -18292,5406 +20357,5164 @@ arm_init_tls_builtins (void)
                               NULL, NULL_TREE);
   TREE_NOTHROW (decl) = 1;
   TREE_READONLY (decl) = 1;
+  arm_builtin_decls[ARM_BUILTIN_THREAD_POINTER] = decl;
 }
 
-enum neon_builtin_type_bits {
-  T_V8QI  = 0x0001,
-  T_V4HI  = 0x0002,
-  T_V2SI  = 0x0004,
-  T_V2SF  = 0x0008,
-  T_DI    = 0x0010,
-  T_V16QI = 0x0020,
-  T_V8HI  = 0x0040,
-  T_V4SI  = 0x0080,
-  T_V4SF  = 0x0100,
-  T_V2DI  = 0x0200,
-  T_TI   = 0x0400,
-  T_EI   = 0x0800,
-  T_OI   = 0x1000
-};
+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");
+}
 
-#define v8qi_UP  T_V8QI
-#define v4hi_UP  T_V4HI
-#define v2si_UP  T_V2SI
-#define v2sf_UP  T_V2SF
-#define di_UP    T_DI
-#define v16qi_UP T_V16QI
-#define v8hi_UP  T_V8HI
-#define v4si_UP  T_V4SI
-#define v4sf_UP  T_V4SF
-#define v2di_UP  T_V2DI
-#define ti_UP   T_TI
-#define ei_UP   T_EI
-#define oi_UP   T_OI
+static void
+arm_init_builtins (void)
+{
+  arm_init_tls_builtins ();
 
-#define UP(X) X##_UP
+  if (TARGET_REALLY_IWMMXT)
+    arm_init_iwmmxt_builtins ();
 
-#define T_MAX 13
+  if (TARGET_NEON)
+    arm_init_neon_builtins ();
 
-typedef enum {
-  NEON_BINOP,
-  NEON_TERNOP,
-  NEON_UNOP,
-  NEON_GETLANE,
-  NEON_SETLANE,
-  NEON_CREATE,
-  NEON_DUP,
-  NEON_DUPLANE,
-  NEON_COMBINE,
-  NEON_SPLIT,
-  NEON_LANEMUL,
-  NEON_LANEMULL,
-  NEON_LANEMULH,
-  NEON_LANEMAC,
-  NEON_SCALARMUL,
-  NEON_SCALARMULL,
-  NEON_SCALARMULH,
-  NEON_SCALARMAC,
-  NEON_CONVERT,
-  NEON_FIXCONV,
-  NEON_SELECT,
-  NEON_RESULTPAIR,
-  NEON_REINTERP,
-  NEON_VTBL,
-  NEON_VTBX,
-  NEON_LOAD1,
-  NEON_LOAD1LANE,
-  NEON_STORE1,
-  NEON_STORE1LANE,
-  NEON_LOADSTRUCT,
-  NEON_LOADSTRUCTLANE,
-  NEON_STORESTRUCT,
-  NEON_STORESTRUCTLANE,
-  NEON_LOGICBINOP,
-  NEON_SHIFTINSERT,
-  NEON_SHIFTIMM,
-  NEON_SHIFTACC
-} neon_itype;
+  if (arm_fp16_format)
+    arm_init_fp16_builtins ();
+}
 
-typedef struct {
-  const char *name;
-  const neon_itype itype;
-  const int bits;
-  const enum insn_code codes[T_MAX];
-  const unsigned int num_vars;
-  unsigned int base_fcode;
-} neon_builtin_datum;
+/* Return the ARM builtin for CODE.  */
+
+static tree
+arm_builtin_decl (unsigned code, bool initialize_p ATTRIBUTE_UNUSED)
+{
+  if (code >= ARM_BUILTIN_MAX)
+    return error_mark_node;
+
+  return arm_builtin_decls[code];
+}
+
+/* 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 if (ALL_FIXED_POINT_MODE_P (mode))
+    return true;
+  else
+    return default_scalar_mode_supported_p (mode);
+}
+
+/* Errors in the source file can cause expand_expr to return const0_rtx
+   where we expect a vector.  To avoid crashing, use one of the vector
+   clear instructions.  */
+
+static rtx
+safe_vector_operand (rtx x, enum machine_mode mode)
+{
+  if (x != const0_rtx)
+    return x;
+  x = gen_reg_rtx (mode);
+
+  emit_insn (gen_iwmmxt_clrdi (mode == DImode ? x
+                              : gen_rtx_SUBREG (DImode, x, 0)));
+  return x;
+}
+
+/* Subroutine of arm_expand_builtin to take care of binop insns.  */
+
+static rtx
+arm_expand_binop_builtin (enum insn_code icode,
+                         tree exp, rtx target)
+{
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  tree arg1 = CALL_EXPR_ARG (exp, 1);
+  rtx op0 = expand_normal (arg0);
+  rtx op1 = expand_normal (arg1);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+
+  if (VECTOR_MODE_P (mode0))
+    op0 = safe_vector_operand (op0, mode0);
+  if (VECTOR_MODE_P (mode1))
+    op1 = safe_vector_operand (op1, mode1);
 
-#define CF(N,X) CODE_FOR_neon_##N##X
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    target = gen_reg_rtx (tmode);
 
-#define VAR1(T, N, A) \
-  #N, NEON_##T, UP (A), { CF (N, A) }, 1, 0
-#define VAR2(T, N, A, B) \
-  #N, NEON_##T, UP (A) | UP (B), { CF (N, A), CF (N, B) }, 2, 0
-#define VAR3(T, N, A, B, C) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C), \
-  { CF (N, A), CF (N, B), CF (N, C) }, 3, 0
-#define VAR4(T, N, A, B, C, D) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D) }, 4, 0
-#define VAR5(T, N, A, B, C, D, E) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D) | UP (E), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D), CF (N, E) }, 5, 0
-#define VAR6(T, N, A, B, C, D, E, F) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D) | UP (E) | UP (F), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D), CF (N, E), CF (N, F) }, 6, 0
-#define VAR7(T, N, A, B, C, D, E, F, G) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D) | UP (E) | UP (F) | UP (G), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D), CF (N, E), CF (N, F), \
-    CF (N, G) }, 7, 0
-#define VAR8(T, N, A, B, C, D, E, F, G, H) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D) | UP (E) | UP (F) | UP (G) \
-                | UP (H), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D), CF (N, E), CF (N, F), \
-    CF (N, G), CF (N, H) }, 8, 0
-#define VAR9(T, N, A, B, C, D, E, F, G, H, I) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D) | UP (E) | UP (F) | UP (G) \
-                | UP (H) | UP (I), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D), CF (N, E), CF (N, F), \
-    CF (N, G), CF (N, H), CF (N, I) }, 9, 0
-#define VAR10(T, N, A, B, C, D, E, F, G, H, I, J) \
-  #N, NEON_##T, UP (A) | UP (B) | UP (C) | UP (D) | UP (E) | UP (F) | UP (G) \
-                | UP (H) | UP (I) | UP (J), \
-  { CF (N, A), CF (N, B), CF (N, C), CF (N, D), CF (N, E), CF (N, F), \
-    CF (N, G), CF (N, H), CF (N, I), CF (N, J) }, 10, 0
+  gcc_assert (GET_MODE (op0) == mode0 && GET_MODE (op1) == mode1);
 
-/* The mode entries in the following table correspond to the "key" type of the
-   instruction variant, i.e. equivalent to that which would be specified after
-   the assembler mnemonic, which usually refers to the last vector operand.
-   (Signed/unsigned/polynomial types are not differentiated between though, and
-   are all mapped onto the same mode for a given element size.) The modes
-   listed per instruction should be the same as those defined for that
-   instruction's pattern in neon.md.
-   WARNING: Variants should be listed in the same increasing order as
-   neon_builtin_type_bits.  */
+  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+    op0 = copy_to_mode_reg (mode0, op0);
+  if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+    op1 = copy_to_mode_reg (mode1, op1);
 
-static neon_builtin_datum neon_builtin_data[] =
-{
-  { VAR10 (BINOP, vadd,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR3 (BINOP, vaddl, v8qi, v4hi, v2si) },
-  { VAR3 (BINOP, vaddw, v8qi, v4hi, v2si) },
-  { VAR6 (BINOP, vhadd, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR8 (BINOP, vqadd, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR3 (BINOP, vaddhn, v8hi, v4si, v2di) },
-  { VAR8 (BINOP, vmul, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR8 (TERNOP, vmla, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR3 (TERNOP, vmlal, v8qi, v4hi, v2si) },
-  { VAR8 (TERNOP, vmls, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR3 (TERNOP, vmlsl, v8qi, v4hi, v2si) },
-  { VAR4 (BINOP, vqdmulh, v4hi, v2si, v8hi, v4si) },
-  { VAR2 (TERNOP, vqdmlal, v4hi, v2si) },
-  { VAR2 (TERNOP, vqdmlsl, v4hi, v2si) },
-  { VAR3 (BINOP, vmull, v8qi, v4hi, v2si) },
-  { VAR2 (SCALARMULL, vmull_n, v4hi, v2si) },
-  { VAR2 (LANEMULL, vmull_lane, v4hi, v2si) },
-  { VAR2 (SCALARMULL, vqdmull_n, v4hi, v2si) },
-  { VAR2 (LANEMULL, vqdmull_lane, v4hi, v2si) },
-  { VAR4 (SCALARMULH, vqdmulh_n, v4hi, v2si, v8hi, v4si) },
-  { VAR4 (LANEMULH, vqdmulh_lane, v4hi, v2si, v8hi, v4si) },
-  { VAR2 (BINOP, vqdmull, v4hi, v2si) },
-  { VAR8 (BINOP, vshl, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR8 (BINOP, vqshl, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR8 (SHIFTIMM, vshr_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR3 (SHIFTIMM, vshrn_n, v8hi, v4si, v2di) },
-  { VAR3 (SHIFTIMM, vqshrn_n, v8hi, v4si, v2di) },
-  { VAR3 (SHIFTIMM, vqshrun_n, v8hi, v4si, v2di) },
-  { VAR8 (SHIFTIMM, vshl_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR8 (SHIFTIMM, vqshl_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR8 (SHIFTIMM, vqshlu_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR3 (SHIFTIMM, vshll_n, v8qi, v4hi, v2si) },
-  { VAR8 (SHIFTACC, vsra_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR10 (BINOP, vsub,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR3 (BINOP, vsubl, v8qi, v4hi, v2si) },
-  { VAR3 (BINOP, vsubw, v8qi, v4hi, v2si) },
-  { VAR8 (BINOP, vqsub, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR6 (BINOP, vhsub, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR3 (BINOP, vsubhn, v8hi, v4si, v2di) },
-  { VAR8 (BINOP, vceq, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR8 (BINOP, vcge, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR8 (BINOP, vcgt, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR2 (BINOP, vcage, v2sf, v4sf) },
-  { VAR2 (BINOP, vcagt, v2sf, v4sf) },
-  { VAR6 (BINOP, vtst, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR8 (BINOP, vabd, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR3 (BINOP, vabdl, v8qi, v4hi, v2si) },
-  { VAR6 (TERNOP, vaba, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR3 (TERNOP, vabal, v8qi, v4hi, v2si) },
-  { VAR8 (BINOP, vmax, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR8 (BINOP, vmin, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR4 (BINOP, vpadd, v8qi, v4hi, v2si, v2sf) },
-  { VAR6 (UNOP, vpaddl, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR6 (BINOP, vpadal, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR4 (BINOP, vpmax, v8qi, v4hi, v2si, v2sf) },
-  { VAR4 (BINOP, vpmin, v8qi, v4hi, v2si, v2sf) },
-  { VAR2 (BINOP, vrecps, v2sf, v4sf) },
-  { VAR2 (BINOP, vrsqrts, v2sf, v4sf) },
-  { VAR8 (SHIFTINSERT, vsri_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR8 (SHIFTINSERT, vsli_n, v8qi, v4hi, v2si, di, v16qi, v8hi, v4si, v2di) },
-  { VAR8 (UNOP, vabs, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR6 (UNOP, vqabs, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR8 (UNOP, vneg, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR6 (UNOP, vqneg, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR6 (UNOP, vcls, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR6 (UNOP, vclz, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  { VAR2 (UNOP, vcnt, v8qi, v16qi) },
-  { VAR4 (UNOP, vrecpe, v2si, v2sf, v4si, v4sf) },
-  { VAR4 (UNOP, vrsqrte, v2si, v2sf, v4si, v4sf) },
-  { VAR6 (UNOP, vmvn, v8qi, v4hi, v2si, v16qi, v8hi, v4si) },
-  /* FIXME: vget_lane supports more variants than this!  */
-  { VAR10 (GETLANE, vget_lane,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (SETLANE, vset_lane,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (CREATE, vcreate, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR10 (DUP, vdup_n,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (DUPLANE, vdup_lane,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (COMBINE, vcombine, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR5 (SPLIT, vget_high, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (SPLIT, vget_low, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR3 (UNOP, vmovn, v8hi, v4si, v2di) },
-  { VAR3 (UNOP, vqmovn, v8hi, v4si, v2di) },
-  { VAR3 (UNOP, vqmovun, v8hi, v4si, v2di) },
-  { VAR3 (UNOP, vmovl, v8qi, v4hi, v2si) },
-  { VAR6 (LANEMUL, vmul_lane, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR6 (LANEMAC, vmla_lane, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR2 (LANEMAC, vmlal_lane, v4hi, v2si) },
-  { VAR2 (LANEMAC, vqdmlal_lane, v4hi, v2si) },
-  { VAR6 (LANEMAC, vmls_lane, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR2 (LANEMAC, vmlsl_lane, v4hi, v2si) },
-  { VAR2 (LANEMAC, vqdmlsl_lane, v4hi, v2si) },
-  { VAR6 (SCALARMUL, vmul_n, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR6 (SCALARMAC, vmla_n, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR2 (SCALARMAC, vmlal_n, v4hi, v2si) },
-  { VAR2 (SCALARMAC, vqdmlal_n, v4hi, v2si) },
-  { VAR6 (SCALARMAC, vmls_n, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR2 (SCALARMAC, vmlsl_n, v4hi, v2si) },
-  { VAR2 (SCALARMAC, vqdmlsl_n, v4hi, v2si) },
-  { VAR10 (BINOP, vext,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR8 (UNOP, vrev64, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR4 (UNOP, vrev32, v8qi, v4hi, v16qi, v8hi) },
-  { VAR2 (UNOP, vrev16, v8qi, v16qi) },
-  { VAR4 (CONVERT, vcvt, v2si, v2sf, v4si, v4sf) },
-  { VAR4 (FIXCONV, vcvt_n, v2si, v2sf, v4si, v4sf) },
-  { VAR10 (SELECT, vbsl,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR1 (VTBL, vtbl1, v8qi) },
-  { VAR1 (VTBL, vtbl2, v8qi) },
-  { VAR1 (VTBL, vtbl3, v8qi) },
-  { VAR1 (VTBL, vtbl4, v8qi) },
-  { VAR1 (VTBX, vtbx1, v8qi) },
-  { VAR1 (VTBX, vtbx2, v8qi) },
-  { VAR1 (VTBX, vtbx3, v8qi) },
-  { VAR1 (VTBX, vtbx4, v8qi) },
-  { VAR8 (RESULTPAIR, vtrn, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR8 (RESULTPAIR, vzip, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR8 (RESULTPAIR, vuzp, v8qi, v4hi, v2si, v2sf, v16qi, v8hi, v4si, v4sf) },
-  { VAR5 (REINTERP, vreinterpretv8qi, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR5 (REINTERP, vreinterpretv4hi, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR5 (REINTERP, vreinterpretv2si, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR5 (REINTERP, vreinterpretv2sf, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR5 (REINTERP, vreinterpretdi, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR5 (REINTERP, vreinterpretv16qi, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (REINTERP, vreinterpretv8hi, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (REINTERP, vreinterpretv4si, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (REINTERP, vreinterpretv4sf, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR5 (REINTERP, vreinterpretv2di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (LOAD1, vld1,
-           v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (LOAD1LANE, vld1_lane,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (LOAD1, vld1_dup,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (STORE1, vst1,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (STORE1LANE, vst1_lane,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR9 (LOADSTRUCT,
-         vld2, v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf) },
-  { VAR7 (LOADSTRUCTLANE, vld2_lane,
-         v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR5 (LOADSTRUCT, vld2_dup, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR9 (STORESTRUCT, vst2,
-         v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf) },
-  { VAR7 (STORESTRUCTLANE, vst2_lane,
-         v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR9 (LOADSTRUCT,
-         vld3, v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf) },
-  { VAR7 (LOADSTRUCTLANE, vld3_lane,
-         v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR5 (LOADSTRUCT, vld3_dup, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR9 (STORESTRUCT, vst3,
-         v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf) },
-  { VAR7 (STORESTRUCTLANE, vst3_lane,
-         v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR9 (LOADSTRUCT, vld4,
-         v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf) },
-  { VAR7 (LOADSTRUCTLANE, vld4_lane,
-         v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR5 (LOADSTRUCT, vld4_dup, v8qi, v4hi, v2si, v2sf, di) },
-  { VAR9 (STORESTRUCT, vst4,
-         v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf) },
-  { VAR7 (STORESTRUCTLANE, vst4_lane,
-         v8qi, v4hi, v2si, v2sf, v8hi, v4si, v4sf) },
-  { VAR10 (LOGICBINOP, vand,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (LOGICBINOP, vorr,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (BINOP, veor,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (LOGICBINOP, vbic,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) },
-  { VAR10 (LOGICBINOP, vorn,
-          v8qi, v4hi, v2si, v2sf, di, v16qi, v8hi, v4si, v4sf, v2di) }
-};
+  pat = GEN_FCN (icode) (target, op0, op1);
+  if (! pat)
+    return 0;
+  emit_insn (pat);
+  return target;
+}
 
-#undef CF
-#undef VAR1
-#undef VAR2
-#undef VAR3
-#undef VAR4
-#undef VAR5
-#undef VAR6
-#undef VAR7
-#undef VAR8
-#undef VAR9
-#undef VAR10
+/* Subroutine of arm_expand_builtin to take care of unop insns.  */
 
-static void
-arm_init_neon_builtins (void)
+static rtx
+arm_expand_unop_builtin (enum insn_code icode,
+                        tree exp, rtx target, int do_load)
 {
-  unsigned int i, fcode = ARM_BUILTIN_NEON_BASE;
+  rtx pat;
+  tree arg0 = CALL_EXPR_ARG (exp, 0);
+  rtx op0 = expand_normal (arg0);
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
 
-  tree neon_intQI_type_node;
-  tree neon_intHI_type_node;
-  tree neon_polyQI_type_node;
-  tree neon_polyHI_type_node;
-  tree neon_intSI_type_node;
-  tree neon_intDI_type_node;
-  tree neon_float_type_node;
+  if (! target
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    target = gen_reg_rtx (tmode);
+  if (do_load)
+    op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0));
+  else
+    {
+      if (VECTOR_MODE_P (mode0))
+       op0 = safe_vector_operand (op0, mode0);
 
-  tree intQI_pointer_node;
-  tree intHI_pointer_node;
-  tree intSI_pointer_node;
-  tree intDI_pointer_node;
-  tree float_pointer_node;
+      if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+       op0 = copy_to_mode_reg (mode0, op0);
+    }
 
-  tree const_intQI_node;
-  tree const_intHI_node;
-  tree const_intSI_node;
-  tree const_intDI_node;
-  tree const_float_node;
+  pat = GEN_FCN (icode) (target, op0);
+  if (! pat)
+    return 0;
+  emit_insn (pat);
+  return target;
+}
 
-  tree const_intQI_pointer_node;
-  tree const_intHI_pointer_node;
-  tree const_intSI_pointer_node;
-  tree const_intDI_pointer_node;
-  tree const_float_pointer_node;
+typedef enum {
+  NEON_ARG_COPY_TO_REG,
+  NEON_ARG_CONSTANT,
+  NEON_ARG_MEMORY,
+  NEON_ARG_STOP
+} builtin_arg;
 
-  tree V8QI_type_node;
-  tree V4HI_type_node;
-  tree V2SI_type_node;
-  tree V2SF_type_node;
-  tree V16QI_type_node;
-  tree V8HI_type_node;
-  tree V4SI_type_node;
-  tree V4SF_type_node;
-  tree V2DI_type_node;
+#define NEON_MAX_BUILTIN_ARGS 5
 
-  tree intUQI_type_node;
-  tree intUHI_type_node;
-  tree intUSI_type_node;
-  tree intUDI_type_node;
+/* EXP is a pointer argument to a Neon load or store intrinsic.  Derive
+   and return an expression for the accessed memory.
 
-  tree intEI_type_node;
-  tree intOI_type_node;
-  tree intCI_type_node;
-  tree intXI_type_node;
+   The intrinsic function operates on a block of registers that has
+   mode REG_MODE.  This block contains vectors of type TYPE_MODE.
+   The function references the memory at EXP in mode MEM_MODE;
+   this mode may be BLKmode if no more suitable mode is available.  */
 
-  tree V8QI_pointer_node;
-  tree V4HI_pointer_node;
-  tree V2SI_pointer_node;
-  tree V2SF_pointer_node;
-  tree V16QI_pointer_node;
-  tree V8HI_pointer_node;
-  tree V4SI_pointer_node;
-  tree V4SF_pointer_node;
-  tree V2DI_pointer_node;
+static tree
+neon_dereference_pointer (tree exp, enum machine_mode mem_mode,
+                         enum machine_mode reg_mode,
+                         neon_builtin_type_mode type_mode)
+{
+  HOST_WIDE_INT reg_size, vector_size, nvectors, nelems;
+  tree elem_type, upper_bound, array_type;
 
-  tree void_ftype_pv8qi_v8qi_v8qi;
-  tree void_ftype_pv4hi_v4hi_v4hi;
-  tree void_ftype_pv2si_v2si_v2si;
-  tree void_ftype_pv2sf_v2sf_v2sf;
-  tree void_ftype_pdi_di_di;
-  tree void_ftype_pv16qi_v16qi_v16qi;
-  tree void_ftype_pv8hi_v8hi_v8hi;
-  tree void_ftype_pv4si_v4si_v4si;
-  tree void_ftype_pv4sf_v4sf_v4sf;
-  tree void_ftype_pv2di_v2di_v2di;
+  /* Work out the size of the register block in bytes.  */
+  reg_size = GET_MODE_SIZE (reg_mode);
 
-  tree reinterp_ftype_dreg[5][5];
-  tree reinterp_ftype_qreg[5][5];
-  tree dreg_types[5], qreg_types[5];
+  /* Work out the size of each vector in bytes.  */
+  gcc_assert (TYPE_MODE_BIT (type_mode) & (TB_DREG | TB_QREG));
+  vector_size = (TYPE_MODE_BIT (type_mode) & TB_QREG ? 16 : 8);
 
-  /* Create distinguished type nodes for NEON vector element types,
-     and pointers to values of such types, so we can detect them later.  */
-  neon_intQI_type_node = make_signed_type (GET_MODE_PRECISION (QImode));
-  neon_intHI_type_node = make_signed_type (GET_MODE_PRECISION (HImode));
-  neon_polyQI_type_node = make_signed_type (GET_MODE_PRECISION (QImode));
-  neon_polyHI_type_node = make_signed_type (GET_MODE_PRECISION (HImode));
-  neon_intSI_type_node = make_signed_type (GET_MODE_PRECISION (SImode));
-  neon_intDI_type_node = make_signed_type (GET_MODE_PRECISION (DImode));
-  neon_float_type_node = make_node (REAL_TYPE);
-  TYPE_PRECISION (neon_float_type_node) = FLOAT_TYPE_SIZE;
-  layout_type (neon_float_type_node);
+  /* Work out how many vectors there are.  */
+  gcc_assert (reg_size % vector_size == 0);
+  nvectors = reg_size / vector_size;
 
-  /* Define typedefs which exactly correspond to the modes we are basing vector
-     types on.  If you change these names you'll need to change
-     the table used by arm_mangle_type too.  */
-  (*lang_hooks.types.register_builtin_type) (neon_intQI_type_node,
-                                            "__builtin_neon_qi");
-  (*lang_hooks.types.register_builtin_type) (neon_intHI_type_node,
-                                            "__builtin_neon_hi");
-  (*lang_hooks.types.register_builtin_type) (neon_intSI_type_node,
-                                            "__builtin_neon_si");
-  (*lang_hooks.types.register_builtin_type) (neon_float_type_node,
-                                            "__builtin_neon_sf");
-  (*lang_hooks.types.register_builtin_type) (neon_intDI_type_node,
-                                            "__builtin_neon_di");
-  (*lang_hooks.types.register_builtin_type) (neon_polyQI_type_node,
-                                            "__builtin_neon_poly8");
-  (*lang_hooks.types.register_builtin_type) (neon_polyHI_type_node,
-                                            "__builtin_neon_poly16");
+  /* Work out how many elements are being loaded or stored.
+     MEM_MODE == REG_MODE implies a one-to-one mapping between register
+     and memory elements; anything else implies a lane load or store.  */
+  if (mem_mode == reg_mode)
+    nelems = vector_size * nvectors;
+  else
+    nelems = nvectors;
 
-  intQI_pointer_node = build_pointer_type (neon_intQI_type_node);
-  intHI_pointer_node = build_pointer_type (neon_intHI_type_node);
-  intSI_pointer_node = build_pointer_type (neon_intSI_type_node);
-  intDI_pointer_node = build_pointer_type (neon_intDI_type_node);
-  float_pointer_node = build_pointer_type (neon_float_type_node);
+  /* Work out the type of each element.  */
+  gcc_assert (POINTER_TYPE_P (TREE_TYPE (exp)));
+  elem_type = TREE_TYPE (TREE_TYPE (exp));
 
-  /* Next create constant-qualified versions of the above types.  */
-  const_intQI_node = build_qualified_type (neon_intQI_type_node,
-                                          TYPE_QUAL_CONST);
-  const_intHI_node = build_qualified_type (neon_intHI_type_node,
-                                          TYPE_QUAL_CONST);
-  const_intSI_node = build_qualified_type (neon_intSI_type_node,
-                                          TYPE_QUAL_CONST);
-  const_intDI_node = build_qualified_type (neon_intDI_type_node,
-                                          TYPE_QUAL_CONST);
-  const_float_node = build_qualified_type (neon_float_type_node,
-                                          TYPE_QUAL_CONST);
+  /* Create a type that describes the full access.  */
+  upper_bound = build_int_cst (size_type_node, nelems - 1);
+  array_type = build_array_type (elem_type, build_index_type (upper_bound));
 
-  const_intQI_pointer_node = build_pointer_type (const_intQI_node);
-  const_intHI_pointer_node = build_pointer_type (const_intHI_node);
-  const_intSI_pointer_node = build_pointer_type (const_intSI_node);
-  const_intDI_pointer_node = build_pointer_type (const_intDI_node);
-  const_float_pointer_node = build_pointer_type (const_float_node);
+  /* Dereference EXP using that type.  */
+  exp = convert (build_pointer_type (array_type), exp);
+  return fold_build2 (MEM_REF, array_type, exp,
+                     build_int_cst (TREE_TYPE (exp), 0));
+}
 
-  /* Now create vector types based on our NEON element types.  */
-  /* 64-bit vectors.  */
-  V8QI_type_node =
-    build_vector_type_for_mode (neon_intQI_type_node, V8QImode);
-  V4HI_type_node =
-    build_vector_type_for_mode (neon_intHI_type_node, V4HImode);
-  V2SI_type_node =
-    build_vector_type_for_mode (neon_intSI_type_node, V2SImode);
-  V2SF_type_node =
-    build_vector_type_for_mode (neon_float_type_node, V2SFmode);
-  /* 128-bit vectors.  */
-  V16QI_type_node =
-    build_vector_type_for_mode (neon_intQI_type_node, V16QImode);
-  V8HI_type_node =
-    build_vector_type_for_mode (neon_intHI_type_node, V8HImode);
-  V4SI_type_node =
-    build_vector_type_for_mode (neon_intSI_type_node, V4SImode);
-  V4SF_type_node =
-    build_vector_type_for_mode (neon_float_type_node, V4SFmode);
-  V2DI_type_node =
-    build_vector_type_for_mode (neon_intDI_type_node, V2DImode);
+/* Expand a Neon builtin.  */
+static rtx
+arm_expand_neon_args (rtx target, int icode, int have_retval,
+                     neon_builtin_type_mode type_mode,
+                     tree exp, ...)
+{
+  va_list ap;
+  rtx pat;
+  tree arg[NEON_MAX_BUILTIN_ARGS];
+  rtx op[NEON_MAX_BUILTIN_ARGS];
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode[NEON_MAX_BUILTIN_ARGS];
+  enum machine_mode other_mode;
+  int argc = 0;
+  int opno;
 
-  /* Unsigned integer types for various mode sizes.  */
-  intUQI_type_node = make_unsigned_type (GET_MODE_PRECISION (QImode));
-  intUHI_type_node = make_unsigned_type (GET_MODE_PRECISION (HImode));
-  intUSI_type_node = make_unsigned_type (GET_MODE_PRECISION (SImode));
-  intUDI_type_node = make_unsigned_type (GET_MODE_PRECISION (DImode));
+  if (have_retval
+      && (!target
+         || GET_MODE (target) != tmode
+         || !(*insn_data[icode].operand[0].predicate) (target, tmode)))
+    target = gen_reg_rtx (tmode);
 
-  (*lang_hooks.types.register_builtin_type) (intUQI_type_node,
-                                            "__builtin_neon_uqi");
-  (*lang_hooks.types.register_builtin_type) (intUHI_type_node,
-                                            "__builtin_neon_uhi");
-  (*lang_hooks.types.register_builtin_type) (intUSI_type_node,
-                                            "__builtin_neon_usi");
-  (*lang_hooks.types.register_builtin_type) (intUDI_type_node,
-                                            "__builtin_neon_udi");
+  va_start (ap, exp);
 
-  /* Opaque integer types for structures of vectors.  */
-  intEI_type_node = make_signed_type (GET_MODE_PRECISION (EImode));
-  intOI_type_node = make_signed_type (GET_MODE_PRECISION (OImode));
-  intCI_type_node = make_signed_type (GET_MODE_PRECISION (CImode));
-  intXI_type_node = make_signed_type (GET_MODE_PRECISION (XImode));
+  for (;;)
+    {
+      builtin_arg thisarg = (builtin_arg) va_arg (ap, int);
 
-  (*lang_hooks.types.register_builtin_type) (intTI_type_node,
-                                            "__builtin_neon_ti");
-  (*lang_hooks.types.register_builtin_type) (intEI_type_node,
-                                            "__builtin_neon_ei");
-  (*lang_hooks.types.register_builtin_type) (intOI_type_node,
-                                            "__builtin_neon_oi");
-  (*lang_hooks.types.register_builtin_type) (intCI_type_node,
-                                            "__builtin_neon_ci");
-  (*lang_hooks.types.register_builtin_type) (intXI_type_node,
-                                            "__builtin_neon_xi");
+      if (thisarg == NEON_ARG_STOP)
+        break;
+      else
+        {
+          opno = argc + have_retval;
+          mode[argc] = insn_data[icode].operand[opno].mode;
+          arg[argc] = CALL_EXPR_ARG (exp, argc);
+          if (thisarg == NEON_ARG_MEMORY)
+            {
+              other_mode = insn_data[icode].operand[1 - opno].mode;
+              arg[argc] = neon_dereference_pointer (arg[argc], mode[argc],
+                                                    other_mode, type_mode);
+            }
+          op[argc] = expand_normal (arg[argc]);
 
-  /* Pointers to vector types.  */
-  V8QI_pointer_node = build_pointer_type (V8QI_type_node);
-  V4HI_pointer_node = build_pointer_type (V4HI_type_node);
-  V2SI_pointer_node = build_pointer_type (V2SI_type_node);
-  V2SF_pointer_node = build_pointer_type (V2SF_type_node);
-  V16QI_pointer_node = build_pointer_type (V16QI_type_node);
-  V8HI_pointer_node = build_pointer_type (V8HI_type_node);
-  V4SI_pointer_node = build_pointer_type (V4SI_type_node);
-  V4SF_pointer_node = build_pointer_type (V4SF_type_node);
-  V2DI_pointer_node = build_pointer_type (V2DI_type_node);
+          switch (thisarg)
+            {
+            case NEON_ARG_COPY_TO_REG:
+              /*gcc_assert (GET_MODE (op[argc]) == mode[argc]);*/
+              if (!(*insn_data[icode].operand[opno].predicate)
+                     (op[argc], mode[argc]))
+                op[argc] = copy_to_mode_reg (mode[argc], op[argc]);
+              break;
 
-  /* Operations which return results as pairs.  */
-  void_ftype_pv8qi_v8qi_v8qi =
-    build_function_type_list (void_type_node, V8QI_pointer_node, V8QI_type_node,
-                             V8QI_type_node, NULL);
-  void_ftype_pv4hi_v4hi_v4hi =
-    build_function_type_list (void_type_node, V4HI_pointer_node, V4HI_type_node,
-                             V4HI_type_node, NULL);
-  void_ftype_pv2si_v2si_v2si =
-    build_function_type_list (void_type_node, V2SI_pointer_node, V2SI_type_node,
-                             V2SI_type_node, NULL);
-  void_ftype_pv2sf_v2sf_v2sf =
-    build_function_type_list (void_type_node, V2SF_pointer_node, V2SF_type_node,
-                             V2SF_type_node, NULL);
-  void_ftype_pdi_di_di =
-    build_function_type_list (void_type_node, intDI_pointer_node,
-                             neon_intDI_type_node, neon_intDI_type_node, NULL);
-  void_ftype_pv16qi_v16qi_v16qi =
-    build_function_type_list (void_type_node, V16QI_pointer_node,
-                             V16QI_type_node, V16QI_type_node, NULL);
-  void_ftype_pv8hi_v8hi_v8hi =
-    build_function_type_list (void_type_node, V8HI_pointer_node, V8HI_type_node,
-                             V8HI_type_node, NULL);
-  void_ftype_pv4si_v4si_v4si =
-    build_function_type_list (void_type_node, V4SI_pointer_node, V4SI_type_node,
-                             V4SI_type_node, NULL);
-  void_ftype_pv4sf_v4sf_v4sf =
-    build_function_type_list (void_type_node, V4SF_pointer_node, V4SF_type_node,
-                             V4SF_type_node, NULL);
-  void_ftype_pv2di_v2di_v2di =
-    build_function_type_list (void_type_node, V2DI_pointer_node, V2DI_type_node,
-                             V2DI_type_node, NULL);
+            case NEON_ARG_CONSTANT:
+              /* FIXME: This error message is somewhat unhelpful.  */
+              if (!(*insn_data[icode].operand[opno].predicate)
+                    (op[argc], mode[argc]))
+               error ("argument must be a constant");
+              break;
 
-  dreg_types[0] = V8QI_type_node;
-  dreg_types[1] = V4HI_type_node;
-  dreg_types[2] = V2SI_type_node;
-  dreg_types[3] = V2SF_type_node;
-  dreg_types[4] = neon_intDI_type_node;
+            case NEON_ARG_MEMORY:
+             gcc_assert (MEM_P (op[argc]));
+             PUT_MODE (op[argc], mode[argc]);
+             /* ??? arm_neon.h uses the same built-in functions for signed
+                and unsigned accesses, casting where necessary.  This isn't
+                alias safe.  */
+             set_mem_alias_set (op[argc], 0);
+             if (!(*insn_data[icode].operand[opno].predicate)
+                    (op[argc], mode[argc]))
+               op[argc] = (replace_equiv_address
+                           (op[argc], force_reg (Pmode, XEXP (op[argc], 0))));
+              break;
 
-  qreg_types[0] = V16QI_type_node;
-  qreg_types[1] = V8HI_type_node;
-  qreg_types[2] = V4SI_type_node;
-  qreg_types[3] = V4SF_type_node;
-  qreg_types[4] = V2DI_type_node;
+            case NEON_ARG_STOP:
+              gcc_unreachable ();
+            }
 
-  for (i = 0; i < 5; i++)
-    {
-      int j;
-      for (j = 0; j < 5; j++)
-        {
-          reinterp_ftype_dreg[i][j]
-            = build_function_type_list (dreg_types[i], dreg_types[j], NULL);
-          reinterp_ftype_qreg[i][j]
-            = build_function_type_list (qreg_types[i], qreg_types[j], NULL);
+          argc++;
         }
     }
 
-  for (i = 0; i < ARRAY_SIZE (neon_builtin_data); i++)
-    {
-      neon_builtin_datum *d = &neon_builtin_data[i];
-      unsigned int j, codeidx = 0;
+  va_end (ap);
 
-      d->base_fcode = fcode;
+  if (have_retval)
+    switch (argc)
+      {
+      case 1:
+       pat = GEN_FCN (icode) (target, op[0]);
+       break;
 
-      for (j = 0; j < T_MAX; j++)
-       {
-         const char* const modenames[] = {
-           "v8qi", "v4hi", "v2si", "v2sf", "di",
-           "v16qi", "v8hi", "v4si", "v4sf", "v2di"
-         };
-         char namebuf[60];
-         tree ftype = NULL;
-         enum insn_code icode;
-         int is_load = 0, is_store = 0;
+      case 2:
+       pat = GEN_FCN (icode) (target, op[0], op[1]);
+       break;
 
-          if ((d->bits & (1 << j)) == 0)
-            continue;
+      case 3:
+       pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
+       break;
 
-          icode = d->codes[codeidx++];
+      case 4:
+       pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
+       break;
 
-          switch (d->itype)
-            {
-           case NEON_LOAD1:
-           case NEON_LOAD1LANE:
-           case NEON_LOADSTRUCT:
-           case NEON_LOADSTRUCTLANE:
-             is_load = 1;
-             /* Fall through.  */
-           case NEON_STORE1:
-           case NEON_STORE1LANE:
-           case NEON_STORESTRUCT:
-           case NEON_STORESTRUCTLANE:
-             if (!is_load)
-               is_store = 1;
-             /* Fall through.  */
-            case NEON_UNOP:
-           case NEON_BINOP:
-           case NEON_LOGICBINOP:
-           case NEON_SHIFTINSERT:
-           case NEON_TERNOP:
-           case NEON_GETLANE:
-           case NEON_SETLANE:
-           case NEON_CREATE:
-           case NEON_DUP:
-           case NEON_DUPLANE:
-           case NEON_SHIFTIMM:
-           case NEON_SHIFTACC:
-           case NEON_COMBINE:
-           case NEON_SPLIT:
-           case NEON_CONVERT:
-           case NEON_FIXCONV:
-           case NEON_LANEMUL:
-           case NEON_LANEMULL:
-           case NEON_LANEMULH:
-           case NEON_LANEMAC:
-           case NEON_SCALARMUL:
-           case NEON_SCALARMULL:
-           case NEON_SCALARMULH:
-           case NEON_SCALARMAC:
-           case NEON_SELECT:
-           case NEON_VTBL:
-           case NEON_VTBX:
-             {
-               int k;
-               tree return_type = void_type_node, args = void_list_node;
+      case 5:
+       pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3], op[4]);
+       break;
 
-               /* Build a function type directly from the insn_data for this
-                  builtin.  The build_function_type() function takes care of
-                  removing duplicates for us.  */
-               for (k = insn_data[icode].n_generator_args - 1; k >= 0; k--)
-                 {
-                   tree eltype;
+      default:
+       gcc_unreachable ();
+      }
+  else
+    switch (argc)
+      {
+      case 1:
+       pat = GEN_FCN (icode) (op[0]);
+       break;
 
-                   if (is_load && k == 1)
-                     {
-                       /* Neon load patterns always have the memory operand
-                          (a SImode pointer) in the operand 1 position.  We
-                          want a const pointer to the element type in that
-                          position.  */
-                       gcc_assert (insn_data[icode].operand[k].mode == SImode);
+      case 2:
+       pat = GEN_FCN (icode) (op[0], op[1]);
+       break;
 
-                       switch (1 << j)
-                         {
-                         case T_V8QI:
-                         case T_V16QI:
-                           eltype = const_intQI_pointer_node;
-                           break;
+      case 3:
+       pat = GEN_FCN (icode) (op[0], op[1], op[2]);
+       break;
 
-                         case T_V4HI:
-                         case T_V8HI:
-                           eltype = const_intHI_pointer_node;
-                           break;
+      case 4:
+       pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
+       break;
 
-                         case T_V2SI:
-                         case T_V4SI:
-                           eltype = const_intSI_pointer_node;
-                           break;
+      case 5:
+       pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3], op[4]);
+        break;
 
-                         case T_V2SF:
-                         case T_V4SF:
-                           eltype = const_float_pointer_node;
-                           break;
+      default:
+       gcc_unreachable ();
+      }
 
-                         case T_DI:
-                         case T_V2DI:
-                           eltype = const_intDI_pointer_node;
-                           break;
+  if (!pat)
+    return 0;
 
-                         default: gcc_unreachable ();
-                         }
-                     }
-                   else if (is_store && k == 0)
-                     {
-                       /* Similarly, Neon store patterns use operand 0 as
-                          the memory location to store to (a SImode pointer).
-                          Use a pointer to the element type of the store in
-                          that position.  */
-                       gcc_assert (insn_data[icode].operand[k].mode == SImode);
+  emit_insn (pat);
 
-                       switch (1 << j)
-                         {
-                         case T_V8QI:
-                         case T_V16QI:
-                           eltype = intQI_pointer_node;
-                           break;
+  return target;
+}
 
-                         case T_V4HI:
-                         case T_V8HI:
-                           eltype = intHI_pointer_node;
-                           break;
+/* Expand a Neon builtin. These are "special" because they don't have symbolic
+   constants defined per-instruction or per instruction-variant. Instead, the
+   required info is looked up in the table neon_builtin_data.  */
+static rtx
+arm_expand_neon_builtin (int fcode, tree exp, rtx target)
+{
+  neon_builtin_datum *d = &neon_builtin_data[fcode - ARM_BUILTIN_NEON_BASE];
+  neon_itype itype = d->itype;
+  enum insn_code icode = d->code;
+  neon_builtin_type_mode type_mode = d->mode;
 
-                         case T_V2SI:
-                         case T_V4SI:
-                           eltype = intSI_pointer_node;
-                           break;
+  switch (itype)
+    {
+    case NEON_UNOP:
+    case NEON_CONVERT:
+    case NEON_DUPLANE:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT, NEON_ARG_STOP);
 
-                         case T_V2SF:
-                         case T_V4SF:
-                           eltype = float_pointer_node;
-                           break;
+    case NEON_BINOP:
+    case NEON_SETLANE:
+    case NEON_SCALARMUL:
+    case NEON_SCALARMULL:
+    case NEON_SCALARMULH:
+    case NEON_SHIFTINSERT:
+    case NEON_LOGICBINOP:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
+        NEON_ARG_STOP);
 
-                         case T_DI:
-                         case T_V2DI:
-                           eltype = intDI_pointer_node;
-                           break;
+    case NEON_TERNOP:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
+        NEON_ARG_CONSTANT, NEON_ARG_STOP);
 
-                         default: gcc_unreachable ();
-                         }
-                     }
-                   else
-                     {
-                       switch (insn_data[icode].operand[k].mode)
-                         {
-                         case VOIDmode: eltype = void_type_node; break;
-                         /* Scalars.  */
-                         case QImode: eltype = neon_intQI_type_node; break;
-                         case HImode: eltype = neon_intHI_type_node; break;
-                         case SImode: eltype = neon_intSI_type_node; break;
-                         case SFmode: eltype = neon_float_type_node; break;
-                         case DImode: eltype = neon_intDI_type_node; break;
-                         case TImode: eltype = intTI_type_node; break;
-                         case EImode: eltype = intEI_type_node; break;
-                         case OImode: eltype = intOI_type_node; break;
-                         case CImode: eltype = intCI_type_node; break;
-                         case XImode: eltype = intXI_type_node; break;
-                         /* 64-bit vectors.  */
-                         case V8QImode: eltype = V8QI_type_node; break;
-                         case V4HImode: eltype = V4HI_type_node; break;
-                         case V2SImode: eltype = V2SI_type_node; break;
-                         case V2SFmode: eltype = V2SF_type_node; break;
-                         /* 128-bit vectors.  */
-                         case V16QImode: eltype = V16QI_type_node; break;
-                         case V8HImode: eltype = V8HI_type_node; break;
-                         case V4SImode: eltype = V4SI_type_node; break;
-                         case V4SFmode: eltype = V4SF_type_node; break;
-                         case V2DImode: eltype = V2DI_type_node; break;
-                         default: gcc_unreachable ();
-                         }
-                     }
+    case NEON_GETLANE:
+    case NEON_FIXCONV:
+    case NEON_SHIFTIMM:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT, NEON_ARG_CONSTANT,
+        NEON_ARG_STOP);
 
-                   if (k == 0 && !is_store)
-                     return_type = eltype;
-                   else
-                     args = tree_cons (NULL_TREE, eltype, args);
-                 }
+    case NEON_CREATE:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
 
-               ftype = build_function_type (return_type, args);
-             }
-             break;
+    case NEON_DUP:
+    case NEON_SPLIT:
+    case NEON_REINTERP:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
 
-           case NEON_RESULTPAIR:
-              {
-                switch (insn_data[icode].operand[1].mode)
-                  {
-                 case V8QImode: ftype = void_ftype_pv8qi_v8qi_v8qi; break;
-                  case V4HImode: ftype = void_ftype_pv4hi_v4hi_v4hi; break;
-                  case V2SImode: ftype = void_ftype_pv2si_v2si_v2si; break;
-                  case V2SFmode: ftype = void_ftype_pv2sf_v2sf_v2sf; break;
-                  case DImode: ftype = void_ftype_pdi_di_di; break;
-                  case V16QImode: ftype = void_ftype_pv16qi_v16qi_v16qi; break;
-                  case V8HImode: ftype = void_ftype_pv8hi_v8hi_v8hi; break;
-                  case V4SImode: ftype = void_ftype_pv4si_v4si_v4si; break;
-                  case V4SFmode: ftype = void_ftype_pv4sf_v4sf_v4sf; break;
-                  case V2DImode: ftype = void_ftype_pv2di_v2di_v2di; break;
-                  default: gcc_unreachable ();
-                  }
-              }
-              break;
+    case NEON_COMBINE:
+    case NEON_VTBL:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
 
-           case NEON_REINTERP:
-              {
-                /* We iterate over 5 doubleword types, then 5 quadword
-                   types.  */
-                int rhs = j % 5;
-                switch (insn_data[icode].operand[0].mode)
-                  {
-                  case V8QImode: ftype = reinterp_ftype_dreg[0][rhs]; break;
-                  case V4HImode: ftype = reinterp_ftype_dreg[1][rhs]; break;
-                  case V2SImode: ftype = reinterp_ftype_dreg[2][rhs]; break;
-                  case V2SFmode: ftype = reinterp_ftype_dreg[3][rhs]; break;
-                  case DImode: ftype = reinterp_ftype_dreg[4][rhs]; break;
-                  case V16QImode: ftype = reinterp_ftype_qreg[0][rhs]; break;
-                  case V8HImode: ftype = reinterp_ftype_qreg[1][rhs]; break;
-                  case V4SImode: ftype = reinterp_ftype_qreg[2][rhs]; break;
-                 case V4SFmode: ftype = reinterp_ftype_qreg[3][rhs]; break;
-                  case V2DImode: ftype = reinterp_ftype_qreg[4][rhs]; break;
-                  default: gcc_unreachable ();
-                  }
-              }
-              break;
+    case NEON_RESULTPAIR:
+      return arm_expand_neon_args (target, icode, 0, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
+        NEON_ARG_STOP);
 
-            default:
-              gcc_unreachable ();
-            }
+    case NEON_LANEMUL:
+    case NEON_LANEMULL:
+    case NEON_LANEMULH:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
+        NEON_ARG_CONSTANT, NEON_ARG_STOP);
 
-          gcc_assert (ftype != NULL);
+    case NEON_LANEMAC:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
+        NEON_ARG_CONSTANT, NEON_ARG_CONSTANT, NEON_ARG_STOP);
 
-          sprintf (namebuf, "__builtin_neon_%s%s", d->name, modenames[j]);
+    case NEON_SHIFTACC:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
+        NEON_ARG_CONSTANT, NEON_ARG_STOP);
 
-          add_builtin_function (namebuf, ftype, fcode++, BUILT_IN_MD, NULL,
-                               NULL_TREE);
-        }
+    case NEON_SCALARMAC:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
+        NEON_ARG_CONSTANT, NEON_ARG_STOP);
+
+    case NEON_SELECT:
+    case NEON_VTBX:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
+        NEON_ARG_STOP);
+
+    case NEON_LOAD1:
+    case NEON_LOADSTRUCT:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+       NEON_ARG_MEMORY, NEON_ARG_STOP);
+
+    case NEON_LOAD1LANE:
+    case NEON_LOADSTRUCTLANE:
+      return arm_expand_neon_args (target, icode, 1, type_mode, exp,
+       NEON_ARG_MEMORY, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
+       NEON_ARG_STOP);
+
+    case NEON_STORE1:
+    case NEON_STORESTRUCT:
+      return arm_expand_neon_args (target, icode, 0, type_mode, exp,
+       NEON_ARG_MEMORY, NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
+
+    case NEON_STORE1LANE:
+    case NEON_STORESTRUCTLANE:
+      return arm_expand_neon_args (target, icode, 0, type_mode, exp,
+       NEON_ARG_MEMORY, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
+       NEON_ARG_STOP);
     }
+
+  gcc_unreachable ();
 }
 
-static void
-arm_init_fp16_builtins (void)
+/* Emit code to reinterpret one Neon type as another, without altering bits.  */
+void
+neon_reinterpret (rtx dest, rtx src)
 {
-  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");
+  emit_move_insn (dest, gen_lowpart (GET_MODE (dest), src));
 }
 
-static void
-arm_init_builtins (void)
+/* Emit code to place a Neon pair result in memory locations (with equal
+   registers).  */
+void
+neon_emit_pair_result_insn (enum machine_mode mode,
+                           rtx (*intfn) (rtx, rtx, rtx, rtx), rtx destaddr,
+                            rtx op1, rtx op2)
 {
-  arm_init_tls_builtins ();
-
-  if (TARGET_REALLY_IWMMXT)
-    arm_init_iwmmxt_builtins ();
+  rtx mem = gen_rtx_MEM (mode, destaddr);
+  rtx tmp1 = gen_reg_rtx (mode);
+  rtx tmp2 = gen_reg_rtx (mode);
 
-  if (TARGET_NEON)
-    arm_init_neon_builtins ();
+  emit_insn (intfn (tmp1, op1, op2, tmp2));
 
-  if (arm_fp16_format)
-    arm_init_fp16_builtins ();
+  emit_move_insn (mem, tmp1);
+  mem = adjust_address (mem, mode, GET_MODE_SIZE (mode));
+  emit_move_insn (mem, tmp2);
 }
 
-/* Implement TARGET_INVALID_PARAMETER_TYPE.  */
+/* Set up OPERANDS for a register copy from SRC to DEST, taking care
+   not to early-clobber SRC registers in the process.
 
-static const char *
-arm_invalid_parameter_type (const_tree t)
+   We assume that the operands described by SRC and DEST represent a
+   decomposed copy of OPERANDS[1] into OPERANDS[0].  COUNT is the
+   number of components into which the copy has been decomposed.  */
+void
+neon_disambiguate_copy (rtx *operands, rtx *dest, rtx *src, unsigned int count)
 {
-  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.  */
+  unsigned int i;
 
-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;
+  if (!reg_overlap_mentioned_p (operands[0], operands[1])
+      || REGNO (operands[0]) < REGNO (operands[1]))
+    {
+      for (i = 0; i < count; i++)
+       {
+         operands[2 * i] = dest[i];
+         operands[2 * i + 1] = src[i];
+       }
+    }
+  else
+    {
+      for (i = 0; i < count; i++)
+       {
+         operands[2 * i] = dest[count - i - 1];
+         operands[2 * i + 1] = src[count - i - 1];
+       }
+    }
 }
 
-/* Implement TARGET_PROMOTED_TYPE.  */
+/* Split operands into moves from op[1] + op[2] into op[0].  */
 
-static tree
-arm_promoted_type (const_tree t)
+void
+neon_split_vcombine (rtx operands[3])
 {
-  if (SCALAR_FLOAT_TYPE_P (t) && TYPE_PRECISION (t) == 16)
-    return float_type_node;
-  return NULL_TREE;
-}
+  unsigned int dest = REGNO (operands[0]);
+  unsigned int src1 = REGNO (operands[1]);
+  unsigned int src2 = REGNO (operands[2]);
+  enum machine_mode halfmode = GET_MODE (operands[1]);
+  unsigned int halfregs = HARD_REGNO_NREGS (src1, halfmode);
+  rtx destlo, desthi;
 
-/* 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.  */
+  if (src1 == dest && src2 == dest + halfregs)
+    {
+      /* No-op move.  Can't split to nothing; emit something.  */
+      emit_note (NOTE_INSN_DELETED);
+      return;
+    }
 
-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;
-}
+  /* Preserve register attributes for variable tracking.  */
+  destlo = gen_rtx_REG_offset (operands[0], halfmode, dest, 0);
+  desthi = gen_rtx_REG_offset (operands[0], halfmode, dest + halfregs,
+                              GET_MODE_SIZE (halfmode));
 
-/* 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.  */
+  /* Special case of reversed high/low parts.  Use VSWP.  */
+  if (src2 == dest && src1 == dest + halfregs)
+    {
+      rtx x = gen_rtx_SET (VOIDmode, destlo, operands[1]);
+      rtx y = gen_rtx_SET (VOIDmode, desthi, operands[2]);
+      emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, x, y)));
+      return;
+    }
 
-static bool
-arm_scalar_mode_supported_p (enum machine_mode mode)
-{
-  if (mode == HFmode)
-    return (arm_fp16_format != ARM_FP16_FORMAT_NONE);
+  if (!reg_overlap_mentioned_p (operands[2], destlo))
+    {
+      /* Try to avoid unnecessary moves if part of the result
+        is in the right place already.  */
+      if (src1 != dest)
+       emit_move_insn (destlo, operands[1]);
+      if (src2 != dest + halfregs)
+       emit_move_insn (desthi, operands[2]);
+    }
   else
-    return default_scalar_mode_supported_p (mode);
+    {
+      if (src2 != dest + halfregs)
+       emit_move_insn (desthi, operands[2]);
+      if (src1 != dest)
+       emit_move_insn (destlo, operands[1]);
+    }
 }
 
-/* Errors in the source file can cause expand_expr to return const0_rtx
-   where we expect a vector.  To avoid crashing, use one of the vector
-   clear instructions.  */
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
 
 static rtx
-safe_vector_operand (rtx x, enum machine_mode mode)
+arm_expand_builtin (tree exp,
+                   rtx target,
+                   rtx subtarget ATTRIBUTE_UNUSED,
+                   enum machine_mode mode ATTRIBUTE_UNUSED,
+                   int ignore ATTRIBUTE_UNUSED)
 {
-  if (x != const0_rtx)
-    return x;
-  x = gen_reg_rtx (mode);
+  const struct builtin_description * d;
+  enum insn_code    icode;
+  tree              fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+  tree              arg0;
+  tree              arg1;
+  tree              arg2;
+  rtx               op0;
+  rtx               op1;
+  rtx               op2;
+  rtx               pat;
+  int               fcode = DECL_FUNCTION_CODE (fndecl);
+  size_t            i;
+  enum machine_mode tmode;
+  enum machine_mode mode0;
+  enum machine_mode mode1;
+  enum machine_mode mode2;
 
-  emit_insn (gen_iwmmxt_clrdi (mode == DImode ? x
-                              : gen_rtx_SUBREG (DImode, x, 0)));
-  return x;
-}
+  if (fcode >= ARM_BUILTIN_NEON_BASE)
+    return arm_expand_neon_builtin (fcode, exp, target);
 
-/* Subroutine of arm_expand_builtin to take care of binop insns.  */
+  switch (fcode)
+    {
+    case ARM_BUILTIN_TEXTRMSB:
+    case ARM_BUILTIN_TEXTRMUB:
+    case ARM_BUILTIN_TEXTRMSH:
+    case ARM_BUILTIN_TEXTRMUH:
+    case ARM_BUILTIN_TEXTRMSW:
+    case ARM_BUILTIN_TEXTRMUW:
+      icode = (fcode == ARM_BUILTIN_TEXTRMSB ? CODE_FOR_iwmmxt_textrmsb
+              : fcode == ARM_BUILTIN_TEXTRMUB ? CODE_FOR_iwmmxt_textrmub
+              : fcode == ARM_BUILTIN_TEXTRMSH ? CODE_FOR_iwmmxt_textrmsh
+              : fcode == ARM_BUILTIN_TEXTRMUH ? CODE_FOR_iwmmxt_textrmuh
+              : CODE_FOR_iwmmxt_textrmw);
 
-static rtx
-arm_expand_binop_builtin (enum insn_code icode,
-                         tree exp, rtx target)
-{
-  rtx pat;
-  tree arg0 = CALL_EXPR_ARG (exp, 0);
-  tree arg1 = CALL_EXPR_ARG (exp, 1);
-  rtx op0 = expand_normal (arg0);
-  rtx op1 = expand_normal (arg1);
-  enum machine_mode tmode = insn_data[icode].operand[0].mode;
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[2].mode;
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      op0 = expand_normal (arg0);
+      op1 = expand_normal (arg1);
+      tmode = insn_data[icode].operand[0].mode;
+      mode0 = insn_data[icode].operand[1].mode;
+      mode1 = insn_data[icode].operand[2].mode;
 
-  if (VECTOR_MODE_P (mode0))
-    op0 = safe_vector_operand (op0, mode0);
-  if (VECTOR_MODE_P (mode1))
-    op1 = safe_vector_operand (op1, mode1);
+      if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+       op0 = copy_to_mode_reg (mode0, op0);
+      if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+       {
+         /* @@@ better error message */
+         error ("selector must be an immediate");
+         return gen_reg_rtx (tmode);
+       }
+      if (target == 0
+         || GET_MODE (target) != tmode
+         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+      pat = GEN_FCN (icode) (target, op0, op1);
+      if (! pat)
+       return 0;
+      emit_insn (pat);
+      return target;
 
-  if (! target
-      || GET_MODE (target) != tmode
-      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
-    target = gen_reg_rtx (tmode);
+    case ARM_BUILTIN_TINSRB:
+    case ARM_BUILTIN_TINSRH:
+    case ARM_BUILTIN_TINSRW:
+      icode = (fcode == ARM_BUILTIN_TINSRB ? CODE_FOR_iwmmxt_tinsrb
+              : fcode == ARM_BUILTIN_TINSRH ? CODE_FOR_iwmmxt_tinsrh
+              : CODE_FOR_iwmmxt_tinsrw);
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      arg2 = CALL_EXPR_ARG (exp, 2);
+      op0 = expand_normal (arg0);
+      op1 = expand_normal (arg1);
+      op2 = expand_normal (arg2);
+      tmode = insn_data[icode].operand[0].mode;
+      mode0 = insn_data[icode].operand[1].mode;
+      mode1 = insn_data[icode].operand[2].mode;
+      mode2 = insn_data[icode].operand[3].mode;
 
-  gcc_assert (GET_MODE (op0) == mode0 && GET_MODE (op1) == mode1);
+      if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
+       op0 = copy_to_mode_reg (mode0, op0);
+      if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+       op1 = copy_to_mode_reg (mode1, op1);
+      if (! (*insn_data[icode].operand[3].predicate) (op2, mode2))
+       {
+         /* @@@ better error message */
+         error ("selector must be an immediate");
+         return const0_rtx;
+       }
+      if (target == 0
+         || GET_MODE (target) != tmode
+         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+      pat = GEN_FCN (icode) (target, op0, op1, op2);
+      if (! pat)
+       return 0;
+      emit_insn (pat);
+      return target;
 
-  if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
-    op0 = copy_to_mode_reg (mode0, op0);
-  if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
-    op1 = copy_to_mode_reg (mode1, op1);
+    case ARM_BUILTIN_SETWCX:
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      op0 = force_reg (SImode, expand_normal (arg0));
+      op1 = expand_normal (arg1);
+      emit_insn (gen_iwmmxt_tmcr (op1, op0));
+      return 0;
+
+    case ARM_BUILTIN_GETWCX:
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      op0 = expand_normal (arg0);
+      target = gen_reg_rtx (SImode);
+      emit_insn (gen_iwmmxt_tmrc (target, op0));
+      return target;
 
-  pat = GEN_FCN (icode) (target, op0, op1);
-  if (! pat)
-    return 0;
-  emit_insn (pat);
-  return target;
-}
+    case ARM_BUILTIN_WSHUFH:
+      icode = CODE_FOR_iwmmxt_wshufh;
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      op0 = expand_normal (arg0);
+      op1 = expand_normal (arg1);
+      tmode = insn_data[icode].operand[0].mode;
+      mode1 = insn_data[icode].operand[1].mode;
+      mode2 = insn_data[icode].operand[2].mode;
 
-/* Subroutine of arm_expand_builtin to take care of unop insns.  */
+      if (! (*insn_data[icode].operand[1].predicate) (op0, mode1))
+       op0 = copy_to_mode_reg (mode1, op0);
+      if (! (*insn_data[icode].operand[2].predicate) (op1, mode2))
+       {
+         /* @@@ better error message */
+         error ("mask must be an immediate");
+         return const0_rtx;
+       }
+      if (target == 0
+         || GET_MODE (target) != tmode
+         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+      pat = GEN_FCN (icode) (target, op0, op1);
+      if (! pat)
+       return 0;
+      emit_insn (pat);
+      return target;
 
-static rtx
-arm_expand_unop_builtin (enum insn_code icode,
-                        tree exp, rtx target, int do_load)
-{
-  rtx pat;
-  tree arg0 = CALL_EXPR_ARG (exp, 0);
-  rtx op0 = expand_normal (arg0);
-  enum machine_mode tmode = insn_data[icode].operand[0].mode;
-  enum machine_mode mode0 = insn_data[icode].operand[1].mode;
+    case ARM_BUILTIN_WSADB:
+      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadb, exp, target);
+    case ARM_BUILTIN_WSADH:
+      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadh, exp, target);
+    case ARM_BUILTIN_WSADBZ:
+      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadbz, exp, target);
+    case ARM_BUILTIN_WSADHZ:
+      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadhz, exp, target);
 
-  if (! target
-      || GET_MODE (target) != tmode
-      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
-    target = gen_reg_rtx (tmode);
-  if (do_load)
-    op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0));
-  else
-    {
-      if (VECTOR_MODE_P (mode0))
-       op0 = safe_vector_operand (op0, mode0);
+      /* Several three-argument builtins.  */
+    case ARM_BUILTIN_WMACS:
+    case ARM_BUILTIN_WMACU:
+    case ARM_BUILTIN_WALIGN:
+    case ARM_BUILTIN_TMIA:
+    case ARM_BUILTIN_TMIAPH:
+    case ARM_BUILTIN_TMIATT:
+    case ARM_BUILTIN_TMIATB:
+    case ARM_BUILTIN_TMIABT:
+    case ARM_BUILTIN_TMIABB:
+      icode = (fcode == ARM_BUILTIN_WMACS ? CODE_FOR_iwmmxt_wmacs
+              : fcode == ARM_BUILTIN_WMACU ? CODE_FOR_iwmmxt_wmacu
+              : fcode == ARM_BUILTIN_TMIA ? CODE_FOR_iwmmxt_tmia
+              : fcode == ARM_BUILTIN_TMIAPH ? CODE_FOR_iwmmxt_tmiaph
+              : fcode == ARM_BUILTIN_TMIABB ? CODE_FOR_iwmmxt_tmiabb
+              : fcode == ARM_BUILTIN_TMIABT ? CODE_FOR_iwmmxt_tmiabt
+              : fcode == ARM_BUILTIN_TMIATB ? CODE_FOR_iwmmxt_tmiatb
+              : fcode == ARM_BUILTIN_TMIATT ? CODE_FOR_iwmmxt_tmiatt
+              : CODE_FOR_iwmmxt_walign);
+      arg0 = CALL_EXPR_ARG (exp, 0);
+      arg1 = CALL_EXPR_ARG (exp, 1);
+      arg2 = CALL_EXPR_ARG (exp, 2);
+      op0 = expand_normal (arg0);
+      op1 = expand_normal (arg1);
+      op2 = expand_normal (arg2);
+      tmode = insn_data[icode].operand[0].mode;
+      mode0 = insn_data[icode].operand[1].mode;
+      mode1 = insn_data[icode].operand[2].mode;
+      mode2 = insn_data[icode].operand[3].mode;
 
       if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
        op0 = copy_to_mode_reg (mode0, op0);
-    }
-
-  pat = GEN_FCN (icode) (target, op0);
-  if (! pat)
-    return 0;
-  emit_insn (pat);
-  return target;
-}
+      if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
+       op1 = copy_to_mode_reg (mode1, op1);
+      if (! (*insn_data[icode].operand[3].predicate) (op2, mode2))
+       op2 = copy_to_mode_reg (mode2, op2);
+      if (target == 0
+         || GET_MODE (target) != tmode
+         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+      pat = GEN_FCN (icode) (target, op0, op1, op2);
+      if (! pat)
+       return 0;
+      emit_insn (pat);
+      return target;
 
-static int
-neon_builtin_compare (const void *a, const void *b)
-{
-  const neon_builtin_datum *const key = (const neon_builtin_datum *) a;
-  const neon_builtin_datum *const memb = (const neon_builtin_datum *) b;
-  unsigned int soughtcode = key->base_fcode;
+    case ARM_BUILTIN_WZERO:
+      target = gen_reg_rtx (DImode);
+      emit_insn (gen_iwmmxt_clrdi (target));
+      return target;
 
-  if (soughtcode >= memb->base_fcode
-      && soughtcode < memb->base_fcode + memb->num_vars)
-    return 0;
-  else if (soughtcode < memb->base_fcode)
-    return -1;
-  else
-    return 1;
-}
+    case ARM_BUILTIN_THREAD_POINTER:
+      return arm_load_tp (target);
 
-static enum insn_code
-locate_neon_builtin_icode (int fcode, neon_itype *itype)
-{
-  neon_builtin_datum key
-    = { NULL, (neon_itype) 0, 0, { CODE_FOR_nothing }, 0, 0 };
-  neon_builtin_datum *found;
-  int idx;
+    default:
+      break;
+    }
 
-  key.base_fcode = fcode;
-  found = (neon_builtin_datum *)
-    bsearch (&key, &neon_builtin_data[0], ARRAY_SIZE (neon_builtin_data),
-                  sizeof (neon_builtin_data[0]), neon_builtin_compare);
-  gcc_assert (found);
-  idx = fcode - (int) found->base_fcode;
-  gcc_assert (idx >= 0 && idx < T_MAX && idx < (int)found->num_vars);
+  for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
+    if (d->code == (const enum arm_builtins) fcode)
+      return arm_expand_binop_builtin (d->icode, exp, target);
 
-  if (itype)
-    *itype = found->itype;
+  for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
+    if (d->code == (const enum arm_builtins) fcode)
+      return arm_expand_unop_builtin (d->icode, exp, target, 0);
 
-  return found->codes[idx];
+  /* @@@ Should really do something sensible here.  */
+  return NULL_RTX;
 }
+\f
+/* Return the number (counting from 0) of
+   the least significant set bit in MASK.  */
 
-typedef enum {
-  NEON_ARG_COPY_TO_REG,
-  NEON_ARG_CONSTANT,
-  NEON_ARG_STOP
-} builtin_arg;
+inline static int
+number_of_first_bit_set (unsigned mask)
+{
+  return ctz_hwi (mask);
+}
 
-#define NEON_MAX_BUILTIN_ARGS 5
+/* Like emit_multi_reg_push, but allowing for a different set of
+   registers to be described as saved.  MASK is the set of registers
+   to be saved; REAL_REGS is the set of registers to be described as
+   saved.  If REAL_REGS is 0, only describe the stack adjustment.  */
 
-/* Expand a Neon builtin.  */
 static rtx
-arm_expand_neon_args (rtx target, int icode, int have_retval,
-                     tree exp, ...)
+thumb1_emit_multi_reg_push (unsigned long mask, unsigned long real_regs)
 {
-  va_list ap;
-  rtx pat;
-  tree arg[NEON_MAX_BUILTIN_ARGS];
-  rtx op[NEON_MAX_BUILTIN_ARGS];
-  enum machine_mode tmode = insn_data[icode].operand[0].mode;
-  enum machine_mode mode[NEON_MAX_BUILTIN_ARGS];
-  int argc = 0;
-
-  if (have_retval
-      && (!target
-         || GET_MODE (target) != tmode
-         || !(*insn_data[icode].operand[0].predicate) (target, tmode)))
-    target = gen_reg_rtx (tmode);
-
-  va_start (ap, exp);
+  unsigned long regno;
+  rtx par[10], tmp, reg, insn;
+  int i, j;
 
-  for (;;)
+  /* Build the parallel of the registers actually being stored.  */
+  for (i = 0; mask; ++i, mask &= mask - 1)
     {
-      builtin_arg thisarg = (builtin_arg) va_arg (ap, int);
+      regno = ctz_hwi (mask);
+      reg = gen_rtx_REG (SImode, regno);
 
-      if (thisarg == NEON_ARG_STOP)
-        break;
+      if (i == 0)
+       tmp = gen_rtx_UNSPEC (BLKmode, gen_rtvec (1, reg), UNSPEC_PUSH_MULT);
       else
-        {
-          arg[argc] = CALL_EXPR_ARG (exp, argc);
-          op[argc] = expand_normal (arg[argc]);
-          mode[argc] = insn_data[icode].operand[argc + have_retval].mode;
-
-          switch (thisarg)
-            {
-            case NEON_ARG_COPY_TO_REG:
-              /*gcc_assert (GET_MODE (op[argc]) == mode[argc]);*/
-              if (!(*insn_data[icode].operand[argc + have_retval].predicate)
-                     (op[argc], mode[argc]))
-                op[argc] = copy_to_mode_reg (mode[argc], op[argc]);
-              break;
-
-            case NEON_ARG_CONSTANT:
-              /* FIXME: This error message is somewhat unhelpful.  */
-              if (!(*insn_data[icode].operand[argc + have_retval].predicate)
-                    (op[argc], mode[argc]))
-               error ("argument must be a constant");
-              break;
-
-            case NEON_ARG_STOP:
-              gcc_unreachable ();
-            }
+       tmp = gen_rtx_USE (VOIDmode, reg);
 
-          argc++;
-        }
+      par[i] = tmp;
     }
 
-  va_end (ap);
-
-  if (have_retval)
-    switch (argc)
-      {
-      case 1:
-       pat = GEN_FCN (icode) (target, op[0]);
-       break;
+  tmp = plus_constant (stack_pointer_rtx, -4 * i);
+  tmp = gen_rtx_PRE_MODIFY (Pmode, stack_pointer_rtx, tmp);
+  tmp = gen_frame_mem (BLKmode, tmp);
+  tmp = gen_rtx_SET (VOIDmode, tmp, par[0]);
+  par[0] = tmp;
 
-      case 2:
-       pat = GEN_FCN (icode) (target, op[0], op[1]);
-       break;
+  tmp = gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (i, par));
+  insn = emit_insn (tmp);
 
-      case 3:
-       pat = GEN_FCN (icode) (target, op[0], op[1], op[2]);
-       break;
+  /* Always build the stack adjustment note for unwind info.  */
+  tmp = plus_constant (stack_pointer_rtx, -4 * i);
+  tmp = gen_rtx_SET (VOIDmode, stack_pointer_rtx, tmp);
+  par[0] = tmp;
 
-      case 4:
-       pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3]);
-       break;
+  /* Build the parallel of the registers recorded as saved for unwind.  */
+  for (j = 0; real_regs; ++j, real_regs &= real_regs - 1)
+    {
+      regno = ctz_hwi (real_regs);
+      reg = gen_rtx_REG (SImode, regno);
 
-      case 5:
-       pat = GEN_FCN (icode) (target, op[0], op[1], op[2], op[3], op[4]);
-       break;
+      tmp = plus_constant (stack_pointer_rtx, j * 4);
+      tmp = gen_frame_mem (SImode, tmp);
+      tmp = gen_rtx_SET (VOIDmode, tmp, reg);
+      RTX_FRAME_RELATED_P (tmp) = 1;
+      par[j + 1] = tmp;
+    }
 
-      default:
-       gcc_unreachable ();
-      }
+  if (j == 0)
+    tmp = par[0];
   else
-    switch (argc)
-      {
-      case 1:
-       pat = GEN_FCN (icode) (op[0]);
-       break;
-
-      case 2:
-       pat = GEN_FCN (icode) (op[0], op[1]);
-       break;
-
-      case 3:
-       pat = GEN_FCN (icode) (op[0], op[1], op[2]);
-       break;
-
-      case 4:
-       pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3]);
-       break;
-
-      case 5:
-       pat = GEN_FCN (icode) (op[0], op[1], op[2], op[3], op[4]);
-        break;
-
-      default:
-       gcc_unreachable ();
-      }
-
-  if (!pat)
-    return 0;
+    {
+      RTX_FRAME_RELATED_P (par[0]) = 1;
+      tmp = gen_rtx_SEQUENCE (VOIDmode, gen_rtvec_v (j + 1, par));
+    }
 
-  emit_insn (pat);
+  add_reg_note (insn, REG_FRAME_RELATED_EXPR, tmp);
 
-  return target;
+  return insn;
 }
 
-/* Expand a Neon builtin. These are "special" because they don't have symbolic
-   constants defined per-instruction or per instruction-variant. Instead, the
-   required info is looked up in the table neon_builtin_data.  */
-static rtx
-arm_expand_neon_builtin (int fcode, tree exp, rtx target)
+/* Emit code to push or pop registers to or from the stack.  F is the
+   assembly file.  MASK is the registers to pop.  */
+static void
+thumb_pop (FILE *f, unsigned long mask)
 {
-  neon_itype itype;
-  enum insn_code icode = locate_neon_builtin_icode (fcode, &itype);
+  int regno;
+  int lo_mask = mask & 0xFF;
+  int pushed_words = 0;
 
-  switch (itype)
+  gcc_assert (mask);
+
+  if (lo_mask == 0 && (mask & (1 << PC_REGNUM)))
     {
-    case NEON_UNOP:
-    case NEON_CONVERT:
-    case NEON_DUPLANE:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT, NEON_ARG_STOP);
+      /* Special case.  Do not generate a POP PC statement here, do it in
+        thumb_exit() */
+      thumb_exit (f, -1);
+      return;
+    }
 
-    case NEON_BINOP:
-    case NEON_SETLANE:
-    case NEON_SCALARMUL:
-    case NEON_SCALARMULL:
-    case NEON_SCALARMULH:
-    case NEON_SHIFTINSERT:
-    case NEON_LOGICBINOP:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
-        NEON_ARG_STOP);
+  fprintf (f, "\tpop\t{");
 
-    case NEON_TERNOP:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
-        NEON_ARG_CONSTANT, NEON_ARG_STOP);
+  /* Look at the low registers first.  */
+  for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1)
+    {
+      if (lo_mask & 1)
+       {
+         asm_fprintf (f, "%r", regno);
 
-    case NEON_GETLANE:
-    case NEON_FIXCONV:
-    case NEON_SHIFTIMM:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT, NEON_ARG_CONSTANT,
-        NEON_ARG_STOP);
+         if ((lo_mask & ~1) != 0)
+           fprintf (f, ", ");
 
-    case NEON_CREATE:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
+         pushed_words++;
+       }
+    }
 
-    case NEON_DUP:
-    case NEON_SPLIT:
-    case NEON_REINTERP:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
+  if (mask & (1 << PC_REGNUM))
+    {
+      /* Catch popping the PC.  */
+      if (TARGET_INTERWORK || TARGET_BACKTRACE
+         || crtl->calls_eh_return)
+       {
+         /* The PC is never poped directly, instead
+            it is popped into r3 and then BX is used.  */
+         fprintf (f, "}\n");
 
-    case NEON_COMBINE:
-    case NEON_VTBL:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
+         thumb_exit (f, -1);
 
-    case NEON_RESULTPAIR:
-      return arm_expand_neon_args (target, icode, 0, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
-        NEON_ARG_STOP);
+         return;
+       }
+      else
+       {
+         if (mask & 0xFF)
+           fprintf (f, ", ");
 
-    case NEON_LANEMUL:
-    case NEON_LANEMULL:
-    case NEON_LANEMULH:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
-        NEON_ARG_CONSTANT, NEON_ARG_STOP);
+         asm_fprintf (f, "%r", PC_REGNUM);
+       }
+    }
 
-    case NEON_LANEMAC:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
-        NEON_ARG_CONSTANT, NEON_ARG_CONSTANT, NEON_ARG_STOP);
+  fprintf (f, "}\n");
+}
 
-    case NEON_SHIFTACC:
-      return arm_expand_neon_args (target, icode, 1, exp,
-        NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
-        NEON_ARG_CONSTANT, NEON_ARG_STOP);
+/* Generate code to return from a thumb function.
+   If 'reg_containing_return_addr' is -1, then the return address is
+   actually on the stack, at the stack pointer.  */
+static void
+thumb_exit (FILE *f, int reg_containing_return_addr)
+{
+  unsigned regs_available_for_popping;
+  unsigned regs_to_pop;
+  int pops_needed;
+  unsigned available;
+  unsigned required;
+  int mode;
+  int size;
+  int restore_a4 = FALSE;
 
-    case NEON_SCALARMAC:
-      return arm_expand_neon_args (target, icode, 1, exp,
-       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
-        NEON_ARG_CONSTANT, NEON_ARG_STOP);
+  /* Compute the registers we need to pop.  */
+  regs_to_pop = 0;
+  pops_needed = 0;
 
-    case NEON_SELECT:
-    case NEON_VTBX:
-      return arm_expand_neon_args (target, icode, 1, exp,
-       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG,
-        NEON_ARG_STOP);
+  if (reg_containing_return_addr == -1)
+    {
+      regs_to_pop |= 1 << LR_REGNUM;
+      ++pops_needed;
+    }
 
-    case NEON_LOAD1:
-    case NEON_LOADSTRUCT:
-      return arm_expand_neon_args (target, icode, 1, exp,
-       NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
+  if (TARGET_BACKTRACE)
+    {
+      /* Restore the (ARM) frame pointer and stack pointer.  */
+      regs_to_pop |= (1 << ARM_HARD_FRAME_POINTER_REGNUM) | (1 << SP_REGNUM);
+      pops_needed += 2;
+    }
 
-    case NEON_LOAD1LANE:
-    case NEON_LOADSTRUCTLANE:
-      return arm_expand_neon_args (target, icode, 1, exp,
-       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
-       NEON_ARG_STOP);
+  /* If there is nothing to pop then just emit the BX instruction and
+     return.  */
+  if (pops_needed == 0)
+    {
+      if (crtl->calls_eh_return)
+       asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, ARM_EH_STACKADJ_REGNUM);
 
-    case NEON_STORE1:
-    case NEON_STORESTRUCT:
-      return arm_expand_neon_args (target, icode, 0, exp,
-       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_STOP);
+      asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
+      return;
+    }
+  /* Otherwise if we are not supporting interworking and we have not created
+     a backtrace structure and the function was not entered in ARM mode then
+     just pop the return address straight into the PC.  */
+  else if (!TARGET_INTERWORK
+          && !TARGET_BACKTRACE
+          && !is_called_in_ARM_mode (current_function_decl)
+          && !crtl->calls_eh_return)
+    {
+      asm_fprintf (f, "\tpop\t{%r}\n", PC_REGNUM);
+      return;
+    }
 
-    case NEON_STORE1LANE:
-    case NEON_STORESTRUCTLANE:
-      return arm_expand_neon_args (target, icode, 0, exp,
-       NEON_ARG_COPY_TO_REG, NEON_ARG_COPY_TO_REG, NEON_ARG_CONSTANT,
-       NEON_ARG_STOP);
+  /* Find out how many of the (return) argument registers we can corrupt.  */
+  regs_available_for_popping = 0;
+
+  /* If returning via __builtin_eh_return, the bottom three registers
+     all contain information needed for the return.  */
+  if (crtl->calls_eh_return)
+    size = 12;
+  else
+    {
+      /* If we can deduce the registers used from the function's
+        return value.  This is more reliable that examining
+        df_regs_ever_live_p () because that will be set if the register is
+        ever used in the function, not just if the register is used
+        to hold a return value.  */
+
+      if (crtl->return_rtx != 0)
+       mode = GET_MODE (crtl->return_rtx);
+      else
+       mode = DECL_MODE (DECL_RESULT (current_function_decl));
+
+      size = GET_MODE_SIZE (mode);
+
+      if (size == 0)
+       {
+         /* In a void function we can use any argument register.
+            In a function that returns a structure on the stack
+            we can use the second and third argument registers.  */
+         if (mode == VOIDmode)
+           regs_available_for_popping =
+             (1 << ARG_REGISTER (1))
+             | (1 << ARG_REGISTER (2))
+             | (1 << ARG_REGISTER (3));
+         else
+           regs_available_for_popping =
+             (1 << ARG_REGISTER (2))
+             | (1 << ARG_REGISTER (3));
+       }
+      else if (size <= 4)
+       regs_available_for_popping =
+         (1 << ARG_REGISTER (2))
+         | (1 << ARG_REGISTER (3));
+      else if (size <= 8)
+       regs_available_for_popping =
+         (1 << ARG_REGISTER (3));
     }
 
-  gcc_unreachable ();
-}
+  /* Match registers to be popped with registers into which we pop them.  */
+  for (available = regs_available_for_popping,
+       required  = regs_to_pop;
+       required != 0 && available != 0;
+       available &= ~(available & - available),
+       required  &= ~(required  & - required))
+    -- pops_needed;
 
-/* Emit code to reinterpret one Neon type as another, without altering bits.  */
-void
-neon_reinterpret (rtx dest, rtx src)
-{
-  emit_move_insn (dest, gen_lowpart (GET_MODE (dest), src));
-}
+  /* If we have any popping registers left over, remove them.  */
+  if (available > 0)
+    regs_available_for_popping &= ~available;
 
-/* Emit code to place a Neon pair result in memory locations (with equal
-   registers).  */
-void
-neon_emit_pair_result_insn (enum machine_mode mode,
-                           rtx (*intfn) (rtx, rtx, rtx, rtx), rtx destaddr,
-                            rtx op1, rtx op2)
-{
-  rtx mem = gen_rtx_MEM (mode, destaddr);
-  rtx tmp1 = gen_reg_rtx (mode);
-  rtx tmp2 = gen_reg_rtx (mode);
+  /* Otherwise if we need another popping register we can use
+     the fourth argument register.  */
+  else if (pops_needed)
+    {
+      /* If we have not found any free argument registers and
+        reg a4 contains the return address, we must move it.  */
+      if (regs_available_for_popping == 0
+         && reg_containing_return_addr == LAST_ARG_REGNUM)
+       {
+         asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM);
+         reg_containing_return_addr = LR_REGNUM;
+       }
+      else if (size > 12)
+       {
+         /* Register a4 is being used to hold part of the return value,
+            but we have dire need of a free, low register.  */
+         restore_a4 = TRUE;
 
-  emit_insn (intfn (tmp1, op1, tmp2, op2));
+         asm_fprintf (f, "\tmov\t%r, %r\n",IP_REGNUM, LAST_ARG_REGNUM);
+       }
 
-  emit_move_insn (mem, tmp1);
-  mem = adjust_address (mem, mode, GET_MODE_SIZE (mode));
-  emit_move_insn (mem, tmp2);
-}
+      if (reg_containing_return_addr != LAST_ARG_REGNUM)
+       {
+         /* The fourth argument register is available.  */
+         regs_available_for_popping |= 1 << LAST_ARG_REGNUM;
 
-/* Set up operands for a register copy from src to dest, taking care not to
-   clobber registers in the process.
-   FIXME: This has rather high polynomial complexity (O(n^3)?) but shouldn't
-   be called with a large N, so that should be OK.  */
+         --pops_needed;
+       }
+    }
 
-void
-neon_disambiguate_copy (rtx *operands, rtx *dest, rtx *src, unsigned int count)
-{
-  unsigned int copied = 0, opctr = 0;
-  unsigned int done = (1 << count) - 1;
-  unsigned int i, j;
+  /* Pop as many registers as we can.  */
+  thumb_pop (f, regs_available_for_popping);
 
-  while (copied != done)
+  /* Process the registers we popped.  */
+  if (reg_containing_return_addr == -1)
     {
-      for (i = 0; i < count; i++)
-        {
-          int good = 1;
+      /* The return address was popped into the lowest numbered register.  */
+      regs_to_pop &= ~(1 << LR_REGNUM);
 
-          for (j = 0; good && j < count; j++)
-            if (i != j && (copied & (1 << j)) == 0
-                && reg_overlap_mentioned_p (src[j], dest[i]))
-              good = 0;
+      reg_containing_return_addr =
+       number_of_first_bit_set (regs_available_for_popping);
 
-          if (good)
-            {
-              operands[opctr++] = dest[i];
-              operands[opctr++] = src[i];
-              copied |= 1 << i;
-            }
-        }
+      /* Remove this register for the mask of available registers, so that
+         the return address will not be corrupted by further pops.  */
+      regs_available_for_popping &= ~(1 << reg_containing_return_addr);
     }
 
-  gcc_assert (opctr == count * 2);
-}
+  /* If we popped other registers then handle them here.  */
+  if (regs_available_for_popping)
+    {
+      int frame_pointer;
 
-/* Expand an expression EXP that calls a built-in function,
-   with result going to TARGET if that's convenient
-   (and in mode MODE if that's convenient).
-   SUBTARGET may be used as the target for computing one of EXP's operands.
-   IGNORE is nonzero if the value is to be ignored.  */
+      /* Work out which register currently contains the frame pointer.  */
+      frame_pointer = number_of_first_bit_set (regs_available_for_popping);
 
-static rtx
-arm_expand_builtin (tree exp,
-                   rtx target,
-                   rtx subtarget ATTRIBUTE_UNUSED,
-                   enum machine_mode mode ATTRIBUTE_UNUSED,
-                   int ignore ATTRIBUTE_UNUSED)
-{
-  const struct builtin_description * d;
-  enum insn_code    icode;
-  tree              fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
-  tree              arg0;
-  tree              arg1;
-  tree              arg2;
-  rtx               op0;
-  rtx               op1;
-  rtx               op2;
-  rtx               pat;
-  int               fcode = DECL_FUNCTION_CODE (fndecl);
-  size_t            i;
-  enum machine_mode tmode;
-  enum machine_mode mode0;
-  enum machine_mode mode1;
-  enum machine_mode mode2;
+      /* Move it into the correct place.  */
+      asm_fprintf (f, "\tmov\t%r, %r\n",
+                  ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer);
 
-  if (fcode >= ARM_BUILTIN_NEON_BASE)
-    return arm_expand_neon_builtin (fcode, exp, target);
+      /* (Temporarily) remove it from the mask of popped registers.  */
+      regs_available_for_popping &= ~(1 << frame_pointer);
+      regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM);
 
-  switch (fcode)
-    {
-    case ARM_BUILTIN_TEXTRMSB:
-    case ARM_BUILTIN_TEXTRMUB:
-    case ARM_BUILTIN_TEXTRMSH:
-    case ARM_BUILTIN_TEXTRMUH:
-    case ARM_BUILTIN_TEXTRMSW:
-    case ARM_BUILTIN_TEXTRMUW:
-      icode = (fcode == ARM_BUILTIN_TEXTRMSB ? CODE_FOR_iwmmxt_textrmsb
-              : fcode == ARM_BUILTIN_TEXTRMUB ? CODE_FOR_iwmmxt_textrmub
-              : fcode == ARM_BUILTIN_TEXTRMSH ? CODE_FOR_iwmmxt_textrmsh
-              : fcode == ARM_BUILTIN_TEXTRMUH ? CODE_FOR_iwmmxt_textrmuh
-              : CODE_FOR_iwmmxt_textrmw);
+      if (regs_available_for_popping)
+       {
+         int stack_pointer;
 
-      arg0 = CALL_EXPR_ARG (exp, 0);
-      arg1 = CALL_EXPR_ARG (exp, 1);
-      op0 = expand_normal (arg0);
-      op1 = expand_normal (arg1);
-      tmode = insn_data[icode].operand[0].mode;
-      mode0 = insn_data[icode].operand[1].mode;
-      mode1 = insn_data[icode].operand[2].mode;
+         /* We popped the stack pointer as well,
+            find the register that contains it.  */
+         stack_pointer = number_of_first_bit_set (regs_available_for_popping);
 
-      if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
-       op0 = copy_to_mode_reg (mode0, op0);
-      if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
-       {
-         /* @@@ better error message */
-         error ("selector must be an immediate");
-         return gen_reg_rtx (tmode);
-       }
-      if (target == 0
-         || GET_MODE (target) != tmode
-         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
-       target = gen_reg_rtx (tmode);
-      pat = GEN_FCN (icode) (target, op0, op1);
-      if (! pat)
-       return 0;
-      emit_insn (pat);
-      return target;
+         /* Move it into the stack register.  */
+         asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, stack_pointer);
 
-    case ARM_BUILTIN_TINSRB:
-    case ARM_BUILTIN_TINSRH:
-    case ARM_BUILTIN_TINSRW:
-      icode = (fcode == ARM_BUILTIN_TINSRB ? CODE_FOR_iwmmxt_tinsrb
-              : fcode == ARM_BUILTIN_TINSRH ? CODE_FOR_iwmmxt_tinsrh
-              : CODE_FOR_iwmmxt_tinsrw);
-      arg0 = CALL_EXPR_ARG (exp, 0);
-      arg1 = CALL_EXPR_ARG (exp, 1);
-      arg2 = CALL_EXPR_ARG (exp, 2);
-      op0 = expand_normal (arg0);
-      op1 = expand_normal (arg1);
-      op2 = expand_normal (arg2);
-      tmode = insn_data[icode].operand[0].mode;
-      mode0 = insn_data[icode].operand[1].mode;
-      mode1 = insn_data[icode].operand[2].mode;
-      mode2 = insn_data[icode].operand[3].mode;
+         /* At this point we have popped all necessary registers, so
+            do not worry about restoring regs_available_for_popping
+            to its correct value:
 
-      if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
-       op0 = copy_to_mode_reg (mode0, op0);
-      if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
-       op1 = copy_to_mode_reg (mode1, op1);
-      if (! (*insn_data[icode].operand[3].predicate) (op2, mode2))
+            assert (pops_needed == 0)
+            assert (regs_available_for_popping == (1 << frame_pointer))
+            assert (regs_to_pop == (1 << STACK_POINTER))  */
+       }
+      else
        {
-         /* @@@ better error message */
-         error ("selector must be an immediate");
-         return const0_rtx;
+         /* Since we have just move the popped value into the frame
+            pointer, the popping register is available for reuse, and
+            we know that we still have the stack pointer left to pop.  */
+         regs_available_for_popping |= (1 << frame_pointer);
        }
-      if (target == 0
-         || GET_MODE (target) != tmode
-         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
-       target = gen_reg_rtx (tmode);
-      pat = GEN_FCN (icode) (target, op0, op1, op2);
-      if (! pat)
-       return 0;
-      emit_insn (pat);
-      return target;
+    }
 
-    case ARM_BUILTIN_SETWCX:
-      arg0 = CALL_EXPR_ARG (exp, 0);
-      arg1 = CALL_EXPR_ARG (exp, 1);
-      op0 = force_reg (SImode, expand_normal (arg0));
-      op1 = expand_normal (arg1);
-      emit_insn (gen_iwmmxt_tmcr (op1, op0));
-      return 0;
+  /* If we still have registers left on the stack, but we no longer have
+     any registers into which we can pop them, then we must move the return
+     address into the link register and make available the register that
+     contained it.  */
+  if (regs_available_for_popping == 0 && pops_needed > 0)
+    {
+      regs_available_for_popping |= 1 << reg_containing_return_addr;
 
-    case ARM_BUILTIN_GETWCX:
-      arg0 = CALL_EXPR_ARG (exp, 0);
-      op0 = expand_normal (arg0);
-      target = gen_reg_rtx (SImode);
-      emit_insn (gen_iwmmxt_tmrc (target, op0));
-      return target;
+      asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM,
+                  reg_containing_return_addr);
 
-    case ARM_BUILTIN_WSHUFH:
-      icode = CODE_FOR_iwmmxt_wshufh;
-      arg0 = CALL_EXPR_ARG (exp, 0);
-      arg1 = CALL_EXPR_ARG (exp, 1);
-      op0 = expand_normal (arg0);
-      op1 = expand_normal (arg1);
-      tmode = insn_data[icode].operand[0].mode;
-      mode1 = insn_data[icode].operand[1].mode;
-      mode2 = insn_data[icode].operand[2].mode;
+      reg_containing_return_addr = LR_REGNUM;
+    }
 
-      if (! (*insn_data[icode].operand[1].predicate) (op0, mode1))
-       op0 = copy_to_mode_reg (mode1, op0);
-      if (! (*insn_data[icode].operand[2].predicate) (op1, mode2))
-       {
-         /* @@@ better error message */
-         error ("mask must be an immediate");
-         return const0_rtx;
-       }
-      if (target == 0
-         || GET_MODE (target) != tmode
-         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
-       target = gen_reg_rtx (tmode);
-      pat = GEN_FCN (icode) (target, op0, op1);
-      if (! pat)
-       return 0;
-      emit_insn (pat);
-      return target;
+  /* If we have registers left on the stack then pop some more.
+     We know that at most we will want to pop FP and SP.  */
+  if (pops_needed > 0)
+    {
+      int  popped_into;
+      int  move_to;
 
-    case ARM_BUILTIN_WSADB:
-      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadb, exp, target);
-    case ARM_BUILTIN_WSADH:
-      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadh, exp, target);
-    case ARM_BUILTIN_WSADBZ:
-      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadbz, exp, target);
-    case ARM_BUILTIN_WSADHZ:
-      return arm_expand_binop_builtin (CODE_FOR_iwmmxt_wsadhz, exp, target);
+      thumb_pop (f, regs_available_for_popping);
 
-      /* Several three-argument builtins.  */
-    case ARM_BUILTIN_WMACS:
-    case ARM_BUILTIN_WMACU:
-    case ARM_BUILTIN_WALIGN:
-    case ARM_BUILTIN_TMIA:
-    case ARM_BUILTIN_TMIAPH:
-    case ARM_BUILTIN_TMIATT:
-    case ARM_BUILTIN_TMIATB:
-    case ARM_BUILTIN_TMIABT:
-    case ARM_BUILTIN_TMIABB:
-      icode = (fcode == ARM_BUILTIN_WMACS ? CODE_FOR_iwmmxt_wmacs
-              : fcode == ARM_BUILTIN_WMACU ? CODE_FOR_iwmmxt_wmacu
-              : fcode == ARM_BUILTIN_TMIA ? CODE_FOR_iwmmxt_tmia
-              : fcode == ARM_BUILTIN_TMIAPH ? CODE_FOR_iwmmxt_tmiaph
-              : fcode == ARM_BUILTIN_TMIABB ? CODE_FOR_iwmmxt_tmiabb
-              : fcode == ARM_BUILTIN_TMIABT ? CODE_FOR_iwmmxt_tmiabt
-              : fcode == ARM_BUILTIN_TMIATB ? CODE_FOR_iwmmxt_tmiatb
-              : fcode == ARM_BUILTIN_TMIATT ? CODE_FOR_iwmmxt_tmiatt
-              : CODE_FOR_iwmmxt_walign);
-      arg0 = CALL_EXPR_ARG (exp, 0);
-      arg1 = CALL_EXPR_ARG (exp, 1);
-      arg2 = CALL_EXPR_ARG (exp, 2);
-      op0 = expand_normal (arg0);
-      op1 = expand_normal (arg1);
-      op2 = expand_normal (arg2);
-      tmode = insn_data[icode].operand[0].mode;
-      mode0 = insn_data[icode].operand[1].mode;
-      mode1 = insn_data[icode].operand[2].mode;
-      mode2 = insn_data[icode].operand[3].mode;
+      /* We have popped either FP or SP.
+        Move whichever one it is into the correct register.  */
+      popped_into = number_of_first_bit_set (regs_available_for_popping);
+      move_to     = number_of_first_bit_set (regs_to_pop);
 
-      if (! (*insn_data[icode].operand[1].predicate) (op0, mode0))
-       op0 = copy_to_mode_reg (mode0, op0);
-      if (! (*insn_data[icode].operand[2].predicate) (op1, mode1))
-       op1 = copy_to_mode_reg (mode1, op1);
-      if (! (*insn_data[icode].operand[3].predicate) (op2, mode2))
-       op2 = copy_to_mode_reg (mode2, op2);
-      if (target == 0
-         || GET_MODE (target) != tmode
-         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
-       target = gen_reg_rtx (tmode);
-      pat = GEN_FCN (icode) (target, op0, op1, op2);
-      if (! pat)
-       return 0;
-      emit_insn (pat);
-      return target;
+      asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into);
 
-    case ARM_BUILTIN_WZERO:
-      target = gen_reg_rtx (DImode);
-      emit_insn (gen_iwmmxt_clrdi (target));
-      return target;
+      regs_to_pop &= ~(1 << move_to);
 
-    case ARM_BUILTIN_THREAD_POINTER:
-      return arm_load_tp (target);
+      --pops_needed;
+    }
 
-    default:
-      break;
+  /* If we still have not popped everything then we must have only
+     had one register available to us and we are now popping the SP.  */
+  if (pops_needed > 0)
+    {
+      int  popped_into;
+
+      thumb_pop (f, regs_available_for_popping);
+
+      popped_into = number_of_first_bit_set (regs_available_for_popping);
+
+      asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, popped_into);
+      /*
+       assert (regs_to_pop == (1 << STACK_POINTER))
+       assert (pops_needed == 1)
+      */
     }
 
-  for (i = 0, d = bdesc_2arg; i < ARRAY_SIZE (bdesc_2arg); i++, d++)
-    if (d->code == (const enum arm_builtins) fcode)
-      return arm_expand_binop_builtin (d->icode, exp, target);
+  /* If necessary restore the a4 register.  */
+  if (restore_a4)
+    {
+      if (reg_containing_return_addr != LR_REGNUM)
+       {
+         asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM);
+         reg_containing_return_addr = LR_REGNUM;
+       }
 
-  for (i = 0, d = bdesc_1arg; i < ARRAY_SIZE (bdesc_1arg); i++, d++)
-    if (d->code == (const enum arm_builtins) fcode)
-      return arm_expand_unop_builtin (d->icode, exp, target, 0);
+      asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM);
+    }
 
-  /* @@@ Should really do something sensible here.  */
-  return NULL_RTX;
+  if (crtl->calls_eh_return)
+    asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, ARM_EH_STACKADJ_REGNUM);
+
+  /* Return to caller.  */
+  asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
 }
 \f
-/* Return the number (counting from 0) of
-   the least significant set bit in MASK.  */
+/* Scan INSN just before assembler is output for it.
+   For Thumb-1, we track the status of the condition codes; this
+   information is used in the cbranchsi4_insn pattern.  */
+void
+thumb1_final_prescan_insn (rtx insn)
+{
+  if (flag_print_asm_name)
+    asm_fprintf (asm_out_file, "%@ 0x%04x\n",
+                INSN_ADDRESSES (INSN_UID (insn)));
+  /* Don't overwrite the previous setter when we get to a cbranch.  */
+  if (INSN_CODE (insn) != CODE_FOR_cbranchsi4_insn)
+    {
+      enum attr_conds conds;
 
-inline static int
-number_of_first_bit_set (unsigned mask)
+      if (cfun->machine->thumb1_cc_insn)
+       {
+         if (modified_in_p (cfun->machine->thumb1_cc_op0, insn)
+             || modified_in_p (cfun->machine->thumb1_cc_op1, insn))
+           CC_STATUS_INIT;
+       }
+      conds = get_attr_conds (insn);
+      if (conds == CONDS_SET)
+       {
+         rtx set = single_set (insn);
+         cfun->machine->thumb1_cc_insn = insn;
+         cfun->machine->thumb1_cc_op0 = SET_DEST (set);
+         cfun->machine->thumb1_cc_op1 = const0_rtx;
+         cfun->machine->thumb1_cc_mode = CC_NOOVmode;
+         if (INSN_CODE (insn) == CODE_FOR_thumb1_subsi3_insn)
+           {
+             rtx src1 = XEXP (SET_SRC (set), 1);
+             if (src1 == const0_rtx)
+               cfun->machine->thumb1_cc_mode = CCmode;
+           }
+       }
+      else if (conds != CONDS_NOCOND)
+       cfun->machine->thumb1_cc_insn = NULL_RTX;
+    }
+}
+
+int
+thumb_shiftable_const (unsigned HOST_WIDE_INT val)
 {
-  int bit;
+  unsigned HOST_WIDE_INT mask = 0xff;
+  int i;
+
+  val = val & (unsigned HOST_WIDE_INT)0xffffffffu;
+  if (val == 0) /* XXX */
+    return 0;
 
-  for (bit = 0;
-       (mask & (1 << bit)) == 0;
-       ++bit)
-    continue;
+  for (i = 0; i < 25; i++)
+    if ((val & (mask << i)) == val)
+      return 1;
 
-  return bit;
+  return 0;
 }
 
-/* Emit code to push or pop registers to or from the stack.  F is the
-   assembly file.  MASK is the registers to push or pop.  PUSH is
-   nonzero if we should push, and zero if we should pop.  For debugging
-   output, if pushing, adjust CFA_OFFSET by the amount of space added
-   to the stack.  REAL_REGS should have the same number of bits set as
-   MASK, and will be used instead (in the same order) to describe which
-   registers were saved - this is used to mark the save slots when we
-   push high registers after moving them to low registers.  */
-static void
-thumb_pushpop (FILE *f, unsigned long mask, int push, int *cfa_offset,
-              unsigned long real_regs)
+/* Returns nonzero if the current function contains,
+   or might contain a far jump.  */
+static int
+thumb_far_jump_used_p (void)
 {
-  int regno;
-  int lo_mask = mask & 0xFF;
-  int pushed_words = 0;
+  rtx insn;
 
-  gcc_assert (mask);
+  /* This test is only important for leaf functions.  */
+  /* assert (!leaf_function_p ()); */
+
+  /* If we have already decided that far jumps may be used,
+     do not bother checking again, and always return true even if
+     it turns out that they are not being used.  Once we have made
+     the decision that far jumps are present (and that hence the link
+     register will be pushed onto the stack) we cannot go back on it.  */
+  if (cfun->machine->far_jump_used)
+    return 1;
 
-  if (lo_mask == 0 && !push && (mask & (1 << PC_REGNUM)))
+  /* If this function is not being called from the prologue/epilogue
+     generation code then it must be being called from the
+     INITIAL_ELIMINATION_OFFSET macro.  */
+  if (!(ARM_DOUBLEWORD_ALIGN || reload_completed))
     {
-      /* Special case.  Do not generate a POP PC statement here, do it in
-        thumb_exit() */
-      thumb_exit (f, -1);
-      return;
+      /* In this case we know that we are being asked about the elimination
+        of the arg pointer register.  If that register is not being used,
+        then there are no arguments on the stack, and we do not have to
+        worry that a far jump might force the prologue to push the link
+        register, changing the stack offsets.  In this case we can just
+        return false, since the presence of far jumps in the function will
+        not affect stack offsets.
+
+        If the arg pointer is live (or if it was live, but has now been
+        eliminated and so set to dead) then we do have to test to see if
+        the function might contain a far jump.  This test can lead to some
+        false negatives, since before reload is completed, then length of
+        branch instructions is not known, so gcc defaults to returning their
+        longest length, which in turn sets the far jump attribute to true.
+
+        A false negative will not result in bad code being generated, but it
+        will result in a needless push and pop of the link register.  We
+        hope that this does not occur too often.
+
+        If we need doubleword stack alignment this could affect the other
+        elimination offsets so we can't risk getting it wrong.  */
+      if (df_regs_ever_live_p (ARG_POINTER_REGNUM))
+       cfun->machine->arg_pointer_live = 1;
+      else if (!cfun->machine->arg_pointer_live)
+       return 0;
     }
 
-  if (push && arm_except_unwind_info (&global_options) == UI_TARGET)
+  /* Check to see if the function contains a branch
+     insn with the far jump attribute set.  */
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
-      fprintf (f, "\t.save\t{");
-      for (regno = 0; regno < 15; regno++)
+      if (GET_CODE (insn) == JUMP_INSN
+         /* Ignore tablejump patterns.  */
+         && GET_CODE (PATTERN (insn)) != ADDR_VEC
+         && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
+         && get_attr_far_jump (insn) == FAR_JUMP_YES
+         )
        {
-         if (real_regs & (1 << regno))
-           {
-             if (real_regs & ((1 << regno) -1))
-               fprintf (f, ", ");
-             asm_fprintf (f, "%r", regno);
-           }
+         /* Record the fact that we have decided that
+            the function does use far jumps.  */
+         cfun->machine->far_jump_used = 1;
+         return 1;
        }
-      fprintf (f, "}\n");
     }
 
-  fprintf (f, "\t%s\t{", push ? "push" : "pop");
+  return 0;
+}
 
-  /* Look at the low registers first.  */
-  for (regno = 0; regno <= LAST_LO_REGNUM; regno++, lo_mask >>= 1)
-    {
-      if (lo_mask & 1)
-       {
-         asm_fprintf (f, "%r", regno);
+/* Return nonzero if FUNC must be entered in ARM mode.  */
+int
+is_called_in_ARM_mode (tree func)
+{
+  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
 
-         if ((lo_mask & ~1) != 0)
-           fprintf (f, ", ");
+  /* Ignore the problem about functions whose address is taken.  */
+  if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func))
+    return TRUE;
 
-         pushed_words++;
-       }
-    }
+#ifdef ARM_PE
+  return lookup_attribute ("interfacearm", DECL_ATTRIBUTES (func)) != NULL_TREE;
+#else
+  return FALSE;
+#endif
+}
 
-  if (push && (mask & (1 << LR_REGNUM)))
-    {
-      /* Catch pushing the LR.  */
-      if (mask & 0xFF)
-       fprintf (f, ", ");
+/* Given the stack offsets and register mask in OFFSETS, decide how
+   many additional registers to push instead of subtracting a constant
+   from SP.  For epilogues the principle is the same except we use pop.
+   FOR_PROLOGUE indicates which we're generating.  */
+static int
+thumb1_extra_regs_pushed (arm_stack_offsets *offsets, bool for_prologue)
+{
+  HOST_WIDE_INT amount;
+  unsigned long live_regs_mask = offsets->saved_regs_mask;
+  /* Extract a mask of the ones we can give to the Thumb's push/pop
+     instruction.  */
+  unsigned long l_mask = live_regs_mask & (for_prologue ? 0x40ff : 0xff);
+  /* Then count how many other high registers will need to be pushed.  */
+  unsigned long high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
+  int n_free, reg_base;
 
-      asm_fprintf (f, "%r", LR_REGNUM);
+  if (!for_prologue && frame_pointer_needed)
+    amount = offsets->locals_base - offsets->saved_regs;
+  else
+    amount = offsets->outgoing_args - offsets->saved_regs;
 
-      pushed_words++;
-    }
-  else if (!push && (mask & (1 << PC_REGNUM)))
-    {
-      /* Catch popping the PC.  */
-      if (TARGET_INTERWORK || TARGET_BACKTRACE
-         || crtl->calls_eh_return)
-       {
-         /* The PC is never poped directly, instead
-            it is popped into r3 and then BX is used.  */
-         fprintf (f, "}\n");
+  /* If the stack frame size is 512 exactly, we can save one load
+     instruction, which should make this a win even when optimizing
+     for speed.  */
+  if (!optimize_size && amount != 512)
+    return 0;
 
-         thumb_exit (f, -1);
+  /* Can't do this if there are high registers to push.  */
+  if (high_regs_pushed != 0)
+    return 0;
+
+  /* Shouldn't do it in the prologue if no registers would normally
+     be pushed at all.  In the epilogue, also allow it if we'll have
+     a pop insn for the PC.  */
+  if  (l_mask == 0
+       && (for_prologue
+          || TARGET_BACKTRACE
+          || (live_regs_mask & 1 << LR_REGNUM) == 0
+          || TARGET_INTERWORK
+          || crtl->args.pretend_args_size != 0))
+    return 0;
 
-         return;
-       }
-      else
-       {
-         if (mask & 0xFF)
-           fprintf (f, ", ");
+  /* Don't do this if thumb_expand_prologue wants to emit instructions
+     between the push and the stack frame allocation.  */
+  if (for_prologue
+      && ((flag_pic && arm_pic_register != INVALID_REGNUM)
+         || (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0)))
+    return 0;
 
-         asm_fprintf (f, "%r", PC_REGNUM);
-       }
+  reg_base = 0;
+  n_free = 0;
+  if (!for_prologue)
+    {
+      reg_base = arm_size_return_regs () / UNITS_PER_WORD;
+      live_regs_mask >>= reg_base;
     }
 
-  fprintf (f, "}\n");
-
-  if (push && pushed_words && dwarf2out_do_frame ())
+  while (reg_base + n_free < 8 && !(live_regs_mask & 1)
+        && (for_prologue || call_used_regs[reg_base + n_free]))
     {
-      char *l = dwarf2out_cfi_label (false);
-      int pushed_mask = real_regs;
+      live_regs_mask >>= 1;
+      n_free++;
+    }
 
-      *cfa_offset += pushed_words * 4;
-      dwarf2out_def_cfa (l, SP_REGNUM, *cfa_offset);
+  if (n_free == 0)
+    return 0;
+  gcc_assert (amount / 4 * 4 == amount);
 
-      pushed_words = 0;
-      pushed_mask = real_regs;
-      for (regno = 0; regno <= 14; regno++, pushed_mask >>= 1)
-       {
-         if (pushed_mask & 1)
-           dwarf2out_reg_save (l, regno, 4 * pushed_words++ - *cfa_offset);
-       }
-    }
+  if (amount >= 512 && (amount - n_free * 4) < 512)
+    return (amount - 508) / 4;
+  if (amount <= n_free * 4)
+    return amount / 4;
+  return 0;
 }
 
-/* Generate code to return from a thumb function.
-   If 'reg_containing_return_addr' is -1, then the return address is
-   actually on the stack, at the stack pointer.  */
-static void
-thumb_exit (FILE *f, int reg_containing_return_addr)
+/* The bits which aren't usefully expanded as rtl.  */
+const char *
+thumb_unexpanded_epilogue (void)
 {
-  unsigned regs_available_for_popping;
-  unsigned regs_to_pop;
-  int pops_needed;
-  unsigned available;
-  unsigned required;
-  int mode;
+  arm_stack_offsets *offsets;
+  int regno;
+  unsigned long live_regs_mask = 0;
+  int high_regs_pushed = 0;
+  int extra_pop;
+  int had_to_push_lr;
   int size;
-  int restore_a4 = FALSE;
 
-  /* Compute the registers we need to pop.  */
-  regs_to_pop = 0;
-  pops_needed = 0;
+  if (cfun->machine->return_used_this_function != 0)
+    return "";
 
-  if (reg_containing_return_addr == -1)
-    {
-      regs_to_pop |= 1 << LR_REGNUM;
-      ++pops_needed;
-    }
+  if (IS_NAKED (arm_current_func_type ()))
+    return "";
 
-  if (TARGET_BACKTRACE)
-    {
-      /* Restore the (ARM) frame pointer and stack pointer.  */
-      regs_to_pop |= (1 << ARM_HARD_FRAME_POINTER_REGNUM) | (1 << SP_REGNUM);
-      pops_needed += 2;
-    }
+  offsets = arm_get_frame_offsets ();
+  live_regs_mask = offsets->saved_regs_mask;
+  high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
 
-  /* If there is nothing to pop then just emit the BX instruction and
-     return.  */
-  if (pops_needed == 0)
-    {
-      if (crtl->calls_eh_return)
-       asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, ARM_EH_STACKADJ_REGNUM);
+  /* If we can deduce the registers used from the function's return value.
+     This is more reliable that examining df_regs_ever_live_p () because that
+     will be set if the register is ever used in the function, not just if
+     the register is used to hold a return value.  */
+  size = arm_size_return_regs ();
 
-      asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
-      return;
-    }
-  /* Otherwise if we are not supporting interworking and we have not created
-     a backtrace structure and the function was not entered in ARM mode then
-     just pop the return address straight into the PC.  */
-  else if (!TARGET_INTERWORK
-          && !TARGET_BACKTRACE
-          && !is_called_in_ARM_mode (current_function_decl)
-          && !crtl->calls_eh_return)
+  extra_pop = thumb1_extra_regs_pushed (offsets, false);
+  if (extra_pop > 0)
     {
-      asm_fprintf (f, "\tpop\t{%r}\n", PC_REGNUM);
-      return;
+      unsigned long extra_mask = (1 << extra_pop) - 1;
+      live_regs_mask |= extra_mask << ((size + UNITS_PER_WORD - 1) 
+                                      / UNITS_PER_WORD);
     }
 
-  /* Find out how many of the (return) argument registers we can corrupt.  */
-  regs_available_for_popping = 0;
+  /* The prolog may have pushed some high registers to use as
+     work registers.  e.g. the testsuite file:
+     gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c
+     compiles to produce:
+       push    {r4, r5, r6, r7, lr}
+       mov     r7, r9
+       mov     r6, r8
+       push    {r6, r7}
+     as part of the prolog.  We have to undo that pushing here.  */
 
-  /* If returning via __builtin_eh_return, the bottom three registers
-     all contain information needed for the return.  */
-  if (crtl->calls_eh_return)
-    size = 12;
-  else
+  if (high_regs_pushed)
     {
-      /* If we can deduce the registers used from the function's
-        return value.  This is more reliable that examining
-        df_regs_ever_live_p () because that will be set if the register is
-        ever used in the function, not just if the register is used
-        to hold a return value.  */
+      unsigned long mask = live_regs_mask & 0xff;
+      int next_hi_reg;
 
-      if (crtl->return_rtx != 0)
-       mode = GET_MODE (crtl->return_rtx);
-      else
-       mode = DECL_MODE (DECL_RESULT (current_function_decl));
+      /* The available low registers depend on the size of the value we are
+         returning.  */
+      if (size <= 12)
+       mask |=  1 << 3;
+      if (size <= 8)
+       mask |= 1 << 2;
 
-      size = GET_MODE_SIZE (mode);
+      if (mask == 0)
+       /* Oh dear!  We have no low registers into which we can pop
+           high registers!  */
+       internal_error
+         ("no low registers available for popping high registers");
 
-      if (size == 0)
+      for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
+       if (live_regs_mask & (1 << next_hi_reg))
+         break;
+
+      while (high_regs_pushed)
        {
-         /* In a void function we can use any argument register.
-            In a function that returns a structure on the stack
-            we can use the second and third argument registers.  */
-         if (mode == VOIDmode)
-           regs_available_for_popping =
-             (1 << ARG_REGISTER (1))
-             | (1 << ARG_REGISTER (2))
-             | (1 << ARG_REGISTER (3));
-         else
-           regs_available_for_popping =
-             (1 << ARG_REGISTER (2))
-             | (1 << ARG_REGISTER (3));
+         /* Find lo register(s) into which the high register(s) can
+             be popped.  */
+         for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
+           {
+             if (mask & (1 << regno))
+               high_regs_pushed--;
+             if (high_regs_pushed == 0)
+               break;
+           }
+
+         mask &= (2 << regno) - 1;     /* A noop if regno == 8 */
+
+         /* Pop the values into the low register(s).  */
+         thumb_pop (asm_out_file, mask);
+
+         /* Move the value(s) into the high registers.  */
+         for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
+           {
+             if (mask & (1 << regno))
+               {
+                 asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg,
+                              regno);
+
+                 for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
+                   if (live_regs_mask & (1 << next_hi_reg))
+                     break;
+               }
+           }
        }
-      else if (size <= 4)
-       regs_available_for_popping =
-         (1 << ARG_REGISTER (2))
-         | (1 << ARG_REGISTER (3));
-      else if (size <= 8)
-       regs_available_for_popping =
-         (1 << ARG_REGISTER (3));
+      live_regs_mask &= ~0x0f00;
     }
 
-  /* Match registers to be popped with registers into which we pop them.  */
-  for (available = regs_available_for_popping,
-       required  = regs_to_pop;
-       required != 0 && available != 0;
-       available &= ~(available & - available),
-       required  &= ~(required  & - required))
-    -- pops_needed;
+  had_to_push_lr = (live_regs_mask & (1 << LR_REGNUM)) != 0;
+  live_regs_mask &= 0xff;
 
-  /* If we have any popping registers left over, remove them.  */
-  if (available > 0)
-    regs_available_for_popping &= ~available;
+  if (crtl->args.pretend_args_size == 0 || TARGET_BACKTRACE)
+    {
+      /* Pop the return address into the PC.  */
+      if (had_to_push_lr)
+       live_regs_mask |= 1 << PC_REGNUM;
 
-  /* Otherwise if we need another popping register we can use
-     the fourth argument register.  */
-  else if (pops_needed)
+      /* Either no argument registers were pushed or a backtrace
+        structure was created which includes an adjusted stack
+        pointer, so just pop everything.  */
+      if (live_regs_mask)
+       thumb_pop (asm_out_file, live_regs_mask);
+
+      /* We have either just popped the return address into the
+        PC or it is was kept in LR for the entire function.
+        Note that thumb_pop has already called thumb_exit if the
+        PC was in the list.  */
+      if (!had_to_push_lr)
+       thumb_exit (asm_out_file, LR_REGNUM);
+    }
+  else
     {
-      /* If we have not found any free argument registers and
-        reg a4 contains the return address, we must move it.  */
-      if (regs_available_for_popping == 0
-         && reg_containing_return_addr == LAST_ARG_REGNUM)
-       {
-         asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM);
-         reg_containing_return_addr = LR_REGNUM;
-       }
-      else if (size > 12)
+      /* Pop everything but the return address.  */
+      if (live_regs_mask)
+       thumb_pop (asm_out_file, live_regs_mask);
+
+      if (had_to_push_lr)
        {
-         /* Register a4 is being used to hold part of the return value,
-            but we have dire need of a free, low register.  */
-         restore_a4 = TRUE;
+         if (size > 12)
+           {
+             /* We have no free low regs, so save one.  */
+             asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", IP_REGNUM,
+                          LAST_ARG_REGNUM);
+           }
 
-         asm_fprintf (f, "\tmov\t%r, %r\n",IP_REGNUM, LAST_ARG_REGNUM);
+         /* Get the return address into a temporary register.  */
+         thumb_pop (asm_out_file, 1 << LAST_ARG_REGNUM);
+
+         if (size > 12)
+           {
+             /* Move the return address to lr.  */
+             asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LR_REGNUM,
+                          LAST_ARG_REGNUM);
+             /* Restore the low register.  */
+             asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LAST_ARG_REGNUM,
+                          IP_REGNUM);
+             regno = LR_REGNUM;
+           }
+         else
+           regno = LAST_ARG_REGNUM;
        }
+      else
+       regno = LR_REGNUM;
 
-      if (reg_containing_return_addr != LAST_ARG_REGNUM)
-       {
-         /* The fourth argument register is available.  */
-         regs_available_for_popping |= 1 << LAST_ARG_REGNUM;
+      /* Remove the argument registers that were pushed onto the stack.  */
+      asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n",
+                  SP_REGNUM, SP_REGNUM,
+                  crtl->args.pretend_args_size);
 
-         --pops_needed;
-       }
+      thumb_exit (asm_out_file, regno);
     }
 
-  /* Pop as many registers as we can.  */
-  thumb_pushpop (f, regs_available_for_popping, FALSE, NULL,
-                regs_available_for_popping);
+  return "";
+}
 
-  /* Process the registers we popped.  */
-  if (reg_containing_return_addr == -1)
-    {
-      /* The return address was popped into the lowest numbered register.  */
-      regs_to_pop &= ~(1 << LR_REGNUM);
+/* Functions to save and restore machine-specific function data.  */
+static struct machine_function *
+arm_init_machine_status (void)
+{
+  struct machine_function *machine;
+  machine = ggc_alloc_cleared_machine_function ();
 
-      reg_containing_return_addr =
-       number_of_first_bit_set (regs_available_for_popping);
+#if ARM_FT_UNKNOWN != 0
+  machine->func_type = ARM_FT_UNKNOWN;
+#endif
+  return machine;
+}
 
-      /* Remove this register for the mask of available registers, so that
-         the return address will not be corrupted by further pops.  */
-      regs_available_for_popping &= ~(1 << reg_containing_return_addr);
-    }
+/* Return an RTX indicating where the return address to the
+   calling function can be found.  */
+rtx
+arm_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
+{
+  if (count != 0)
+    return NULL_RTX;
 
-  /* If we popped other registers then handle them here.  */
-  if (regs_available_for_popping)
-    {
-      int frame_pointer;
+  return get_hard_reg_initial_val (Pmode, LR_REGNUM);
+}
 
-      /* Work out which register currently contains the frame pointer.  */
-      frame_pointer = number_of_first_bit_set (regs_available_for_popping);
+/* Do anything needed before RTL is emitted for each function.  */
+void
+arm_init_expanders (void)
+{
+  /* Arrange to initialize and mark the machine per-function status.  */
+  init_machine_status = arm_init_machine_status;
 
-      /* Move it into the correct place.  */
-      asm_fprintf (f, "\tmov\t%r, %r\n",
-                  ARM_HARD_FRAME_POINTER_REGNUM, frame_pointer);
+  /* This is to stop the combine pass optimizing away the alignment
+     adjustment of va_arg.  */
+  /* ??? It is claimed that this should not be necessary.  */
+  if (cfun)
+    mark_reg_pointer (arg_pointer_rtx, PARM_BOUNDARY);
+}
 
-      /* (Temporarily) remove it from the mask of popped registers.  */
-      regs_available_for_popping &= ~(1 << frame_pointer);
-      regs_to_pop &= ~(1 << ARM_HARD_FRAME_POINTER_REGNUM);
 
-      if (regs_available_for_popping)
+/* Like arm_compute_initial_elimination offset.  Simpler because there
+   isn't an ABI specified frame pointer for Thumb.  Instead, we set it
+   to point at the base of the local variables after static stack
+   space for a function has been allocated.  */
+
+HOST_WIDE_INT
+thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to)
+{
+  arm_stack_offsets *offsets;
+
+  offsets = arm_get_frame_offsets ();
+
+  switch (from)
+    {
+    case ARG_POINTER_REGNUM:
+      switch (to)
        {
-         int stack_pointer;
+       case STACK_POINTER_REGNUM:
+         return offsets->outgoing_args - offsets->saved_args;
 
-         /* We popped the stack pointer as well,
-            find the register that contains it.  */
-         stack_pointer = number_of_first_bit_set (regs_available_for_popping);
+       case FRAME_POINTER_REGNUM:
+         return offsets->soft_frame - offsets->saved_args;
 
-         /* Move it into the stack register.  */
-         asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, stack_pointer);
+       case ARM_HARD_FRAME_POINTER_REGNUM:
+         return offsets->saved_regs - offsets->saved_args;
 
-         /* At this point we have popped all necessary registers, so
-            do not worry about restoring regs_available_for_popping
-            to its correct value:
+       case THUMB_HARD_FRAME_POINTER_REGNUM:
+         return offsets->locals_base - offsets->saved_args;
 
-            assert (pops_needed == 0)
-            assert (regs_available_for_popping == (1 << frame_pointer))
-            assert (regs_to_pop == (1 << STACK_POINTER))  */
+       default:
+         gcc_unreachable ();
        }
-      else
+      break;
+
+    case FRAME_POINTER_REGNUM:
+      switch (to)
        {
-         /* Since we have just move the popped value into the frame
-            pointer, the popping register is available for reuse, and
-            we know that we still have the stack pointer left to pop.  */
-         regs_available_for_popping |= (1 << frame_pointer);
-       }
-    }
+       case STACK_POINTER_REGNUM:
+         return offsets->outgoing_args - offsets->soft_frame;
 
-  /* If we still have registers left on the stack, but we no longer have
-     any registers into which we can pop them, then we must move the return
-     address into the link register and make available the register that
-     contained it.  */
-  if (regs_available_for_popping == 0 && pops_needed > 0)
-    {
-      regs_available_for_popping |= 1 << reg_containing_return_addr;
+       case ARM_HARD_FRAME_POINTER_REGNUM:
+         return offsets->saved_regs - offsets->soft_frame;
 
-      asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM,
-                  reg_containing_return_addr);
+       case THUMB_HARD_FRAME_POINTER_REGNUM:
+         return offsets->locals_base - offsets->soft_frame;
 
-      reg_containing_return_addr = LR_REGNUM;
+       default:
+         gcc_unreachable ();
+       }
+      break;
+
+    default:
+      gcc_unreachable ();
     }
+}
 
-  /* If we have registers left on the stack then pop some more.
-     We know that at most we will want to pop FP and SP.  */
-  if (pops_needed > 0)
-    {
-      int  popped_into;
-      int  move_to;
+/* Generate the function's prologue.  */
 
-      thumb_pushpop (f, regs_available_for_popping, FALSE, NULL,
-                    regs_available_for_popping);
+void
+thumb1_expand_prologue (void)
+{
+  rtx insn;
 
-      /* We have popped either FP or SP.
-        Move whichever one it is into the correct register.  */
-      popped_into = number_of_first_bit_set (regs_available_for_popping);
-      move_to     = number_of_first_bit_set (regs_to_pop);
+  HOST_WIDE_INT amount;
+  arm_stack_offsets *offsets;
+  unsigned long func_type;
+  int regno;
+  unsigned long live_regs_mask;
+  unsigned long l_mask;
+  unsigned high_regs_pushed = 0;
 
-      asm_fprintf (f, "\tmov\t%r, %r\n", move_to, popped_into);
+  func_type = arm_current_func_type ();
 
-      regs_to_pop &= ~(1 << move_to);
+  /* Naked functions don't have prologues.  */
+  if (IS_NAKED (func_type))
+    return;
 
-      --pops_needed;
+  if (IS_INTERRUPT (func_type))
+    {
+      error ("interrupt Service Routines cannot be coded in Thumb mode");
+      return;
     }
 
-  /* If we still have not popped everything then we must have only
-     had one register available to us and we are now popping the SP.  */
-  if (pops_needed > 0)
+  if (is_called_in_ARM_mode (current_function_decl))
+    emit_insn (gen_prologue_thumb1_interwork ());
+
+  offsets = arm_get_frame_offsets ();
+  live_regs_mask = offsets->saved_regs_mask;
+
+  /* Extract a mask of the ones we can give to the Thumb's push instruction.  */
+  l_mask = live_regs_mask & 0x40ff;
+  /* Then count how many other high registers will need to be pushed.  */
+  high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
+
+  if (crtl->args.pretend_args_size)
     {
-      int  popped_into;
+      rtx x = GEN_INT (-crtl->args.pretend_args_size);
 
-      thumb_pushpop (f, regs_available_for_popping, FALSE, NULL,
-                    regs_available_for_popping);
+      if (cfun->machine->uses_anonymous_args)
+       {
+         int num_pushes = ARM_NUM_INTS (crtl->args.pretend_args_size);
+         unsigned long mask;
 
-      popped_into = number_of_first_bit_set (regs_available_for_popping);
+         mask = 1ul << (LAST_ARG_REGNUM + 1);
+         mask -= 1ul << (LAST_ARG_REGNUM + 1 - num_pushes);
 
-      asm_fprintf (f, "\tmov\t%r, %r\n", SP_REGNUM, popped_into);
-      /*
-       assert (regs_to_pop == (1 << STACK_POINTER))
-       assert (pops_needed == 1)
-      */
+         insn = thumb1_emit_multi_reg_push (mask, 0);
+       }
+      else
+       {
+         insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
+                                       stack_pointer_rtx, x));
+       }
+      RTX_FRAME_RELATED_P (insn) = 1;
     }
 
-  /* If necessary restore the a4 register.  */
-  if (restore_a4)
+  if (TARGET_BACKTRACE)
     {
-      if (reg_containing_return_addr != LR_REGNUM)
+      HOST_WIDE_INT offset = 0;
+      unsigned work_register;
+      rtx work_reg, x, arm_hfp_rtx;
+
+      /* We have been asked to create a stack backtrace structure.
+         The code looks like this:
+
+        0   .align 2
+        0   func:
+         0     sub   SP, #16         Reserve space for 4 registers.
+        2     push  {R7}            Push low registers.
+         4     add   R7, SP, #20     Get the stack pointer before the push.
+         6     str   R7, [SP, #8]    Store the stack pointer
+                                       (before reserving the space).
+         8     mov   R7, PC          Get hold of the start of this code + 12.
+        10     str   R7, [SP, #16]   Store it.
+        12     mov   R7, FP          Get hold of the current frame pointer.
+        14     str   R7, [SP, #4]    Store it.
+        16     mov   R7, LR          Get hold of the current return address.
+        18     str   R7, [SP, #12]   Store it.
+        20     add   R7, SP, #16     Point at the start of the
+                                       backtrace structure.
+        22     mov   FP, R7          Put this value into the frame pointer.  */
+
+      work_register = thumb_find_work_register (live_regs_mask);
+      work_reg = gen_rtx_REG (SImode, work_register);
+      arm_hfp_rtx = gen_rtx_REG (SImode, ARM_HARD_FRAME_POINTER_REGNUM);
+
+      insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
+                                   stack_pointer_rtx, GEN_INT (-16)));
+      RTX_FRAME_RELATED_P (insn) = 1;
+
+      if (l_mask)
        {
-         asm_fprintf (f, "\tmov\t%r, %r\n", LR_REGNUM, LAST_ARG_REGNUM);
-         reg_containing_return_addr = LR_REGNUM;
-       }
+         insn = thumb1_emit_multi_reg_push (l_mask, l_mask);
+         RTX_FRAME_RELATED_P (insn) = 1;
 
-      asm_fprintf (f, "\tmov\t%r, %r\n", LAST_ARG_REGNUM, IP_REGNUM);
-    }
+         offset = bit_count (l_mask) * UNITS_PER_WORD;
+       }
 
-  if (crtl->calls_eh_return)
-    asm_fprintf (f, "\tadd\t%r, %r\n", SP_REGNUM, ARM_EH_STACKADJ_REGNUM);
+      x = GEN_INT (offset + 16 + crtl->args.pretend_args_size);
+      emit_insn (gen_addsi3 (work_reg, stack_pointer_rtx, x));
 
-  /* Return to caller.  */
-  asm_fprintf (f, "\tbx\t%r\n", reg_containing_return_addr);
-}
-\f
-/* Scan INSN just before assembler is output for it.
-   For Thumb-1, we track the status of the condition codes; this
-   information is used in the cbranchsi4_insn pattern.  */
-void
-thumb1_final_prescan_insn (rtx insn)
-{
-  if (flag_print_asm_name)
-    asm_fprintf (asm_out_file, "%@ 0x%04x\n",
-                INSN_ADDRESSES (INSN_UID (insn)));
-  /* Don't overwrite the previous setter when we get to a cbranch.  */
-  if (INSN_CODE (insn) != CODE_FOR_cbranchsi4_insn)
-    {
-      enum attr_conds conds;
+      x = plus_constant (stack_pointer_rtx, offset + 4);
+      x = gen_frame_mem (SImode, x);
+      emit_move_insn (x, work_reg);
 
-      if (cfun->machine->thumb1_cc_insn)
+      /* Make sure that the instruction fetching the PC is in the right place
+        to calculate "start of backtrace creation code + 12".  */
+      /* ??? The stores using the common WORK_REG ought to be enough to
+        prevent the scheduler from doing anything weird.  Failing that
+        we could always move all of the following into an UNSPEC_VOLATILE.  */
+      if (l_mask)
        {
-         if (modified_in_p (cfun->machine->thumb1_cc_op0, insn)
-             || modified_in_p (cfun->machine->thumb1_cc_op1, insn))
-           CC_STATUS_INIT;
+         x = gen_rtx_REG (SImode, PC_REGNUM);
+         emit_move_insn (work_reg, x);
+
+         x = plus_constant (stack_pointer_rtx, offset + 12);
+         x = gen_frame_mem (SImode, x);
+         emit_move_insn (x, work_reg);
+
+         emit_move_insn (work_reg, arm_hfp_rtx);
+
+         x = plus_constant (stack_pointer_rtx, offset);
+         x = gen_frame_mem (SImode, x);
+         emit_move_insn (x, work_reg);
        }
-      conds = get_attr_conds (insn);
-      if (conds == CONDS_SET)
+      else
        {
-         rtx set = single_set (insn);
-         cfun->machine->thumb1_cc_insn = insn;
-         cfun->machine->thumb1_cc_op0 = SET_DEST (set);
-         cfun->machine->thumb1_cc_op1 = const0_rtx;
-         cfun->machine->thumb1_cc_mode = CC_NOOVmode;
-         if (INSN_CODE (insn) == CODE_FOR_thumb1_subsi3_insn)
-           {
-             rtx src1 = XEXP (SET_SRC (set), 1);
-             if (src1 == const0_rtx)
-               cfun->machine->thumb1_cc_mode = CCmode;
-           }
-       }
-      else if (conds != CONDS_NOCOND)
-       cfun->machine->thumb1_cc_insn = NULL_RTX;
-    }
-}
+         emit_move_insn (work_reg, arm_hfp_rtx);
 
-int
-thumb_shiftable_const (unsigned HOST_WIDE_INT val)
-{
-  unsigned HOST_WIDE_INT mask = 0xff;
-  int i;
+         x = plus_constant (stack_pointer_rtx, offset);
+         x = gen_frame_mem (SImode, x);
+         emit_move_insn (x, work_reg);
 
-  val = val & (unsigned HOST_WIDE_INT)0xffffffffu;
-  if (val == 0) /* XXX */
-    return 0;
+         x = gen_rtx_REG (SImode, PC_REGNUM);
+         emit_move_insn (work_reg, x);
 
-  for (i = 0; i < 25; i++)
-    if ((val & (mask << i)) == val)
-      return 1;
+         x = plus_constant (stack_pointer_rtx, offset + 12);
+         x = gen_frame_mem (SImode, x);
+         emit_move_insn (x, work_reg);
+       }
 
-  return 0;
-}
+      x = gen_rtx_REG (SImode, LR_REGNUM);
+      emit_move_insn (work_reg, x);
 
-/* Returns nonzero if the current function contains,
-   or might contain a far jump.  */
-static int
-thumb_far_jump_used_p (void)
-{
-  rtx insn;
+      x = plus_constant (stack_pointer_rtx, offset + 8);
+      x = gen_frame_mem (SImode, x);
+      emit_move_insn (x, work_reg);
 
-  /* This test is only important for leaf functions.  */
-  /* assert (!leaf_function_p ()); */
+      x = GEN_INT (offset + 12);
+      emit_insn (gen_addsi3 (work_reg, stack_pointer_rtx, x));
 
-  /* If we have already decided that far jumps may be used,
-     do not bother checking again, and always return true even if
-     it turns out that they are not being used.  Once we have made
-     the decision that far jumps are present (and that hence the link
-     register will be pushed onto the stack) we cannot go back on it.  */
-  if (cfun->machine->far_jump_used)
-    return 1;
+      emit_move_insn (arm_hfp_rtx, work_reg);
+    }
+  /* Optimization:  If we are not pushing any low registers but we are going
+     to push some high registers then delay our first push.  This will just
+     be a push of LR and we can combine it with the push of the first high
+     register.  */
+  else if ((l_mask & 0xff) != 0
+          || (high_regs_pushed == 0 && l_mask))
+    {
+      unsigned long mask = l_mask;
+      mask |= (1 << thumb1_extra_regs_pushed (offsets, true)) - 1;
+      insn = thumb1_emit_multi_reg_push (mask, mask);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
 
-  /* If this function is not being called from the prologue/epilogue
-     generation code then it must be being called from the
-     INITIAL_ELIMINATION_OFFSET macro.  */
-  if (!(ARM_DOUBLEWORD_ALIGN || reload_completed))
+  if (high_regs_pushed)
     {
-      /* In this case we know that we are being asked about the elimination
-        of the arg pointer register.  If that register is not being used,
-        then there are no arguments on the stack, and we do not have to
-        worry that a far jump might force the prologue to push the link
-        register, changing the stack offsets.  In this case we can just
-        return false, since the presence of far jumps in the function will
-        not affect stack offsets.
+      unsigned pushable_regs;
+      unsigned next_hi_reg;
 
-        If the arg pointer is live (or if it was live, but has now been
-        eliminated and so set to dead) then we do have to test to see if
-        the function might contain a far jump.  This test can lead to some
-        false negatives, since before reload is completed, then length of
-        branch instructions is not known, so gcc defaults to returning their
-        longest length, which in turn sets the far jump attribute to true.
+      for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--)
+       if (live_regs_mask & (1 << next_hi_reg))
+         break;
 
-        A false negative will not result in bad code being generated, but it
-        will result in a needless push and pop of the link register.  We
-        hope that this does not occur too often.
+      pushable_regs = l_mask & 0xff;
 
-        If we need doubleword stack alignment this could affect the other
-        elimination offsets so we can't risk getting it wrong.  */
-      if (df_regs_ever_live_p (ARG_POINTER_REGNUM))
-       cfun->machine->arg_pointer_live = 1;
-      else if (!cfun->machine->arg_pointer_live)
-       return 0;
-    }
+      if (pushable_regs == 0)
+       pushable_regs = 1 << thumb_find_work_register (live_regs_mask);
 
-  /* Check to see if the function contains a branch
-     insn with the far jump attribute set.  */
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    {
-      if (GET_CODE (insn) == JUMP_INSN
-         /* Ignore tablejump patterns.  */
-         && GET_CODE (PATTERN (insn)) != ADDR_VEC
-         && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
-         && get_attr_far_jump (insn) == FAR_JUMP_YES
-         )
+      while (high_regs_pushed > 0)
        {
-         /* Record the fact that we have decided that
-            the function does use far jumps.  */
-         cfun->machine->far_jump_used = 1;
-         return 1;
-       }
-    }
+         unsigned long real_regs_mask = 0;
 
-  return 0;
-}
+         for (regno = LAST_LO_REGNUM; regno >= 0; regno --)
+           {
+             if (pushable_regs & (1 << regno))
+               {
+                 emit_move_insn (gen_rtx_REG (SImode, regno),
+                                 gen_rtx_REG (SImode, next_hi_reg));
 
-/* Return nonzero if FUNC must be entered in ARM mode.  */
-int
-is_called_in_ARM_mode (tree func)
-{
-  gcc_assert (TREE_CODE (func) == FUNCTION_DECL);
+                 high_regs_pushed --;
+                 real_regs_mask |= (1 << next_hi_reg);
 
-  /* Ignore the problem about functions whose address is taken.  */
-  if (TARGET_CALLEE_INTERWORKING && TREE_PUBLIC (func))
-    return TRUE;
+                 if (high_regs_pushed)
+                   {
+                     for (next_hi_reg --; next_hi_reg > LAST_LO_REGNUM;
+                          next_hi_reg --)
+                       if (live_regs_mask & (1 << next_hi_reg))
+                         break;
+                   }
+                 else
+                   {
+                     pushable_regs &= ~((1 << regno) - 1);
+                     break;
+                   }
+               }
+           }
 
-#ifdef ARM_PE
-  return lookup_attribute ("interfacearm", DECL_ATTRIBUTES (func)) != NULL_TREE;
-#else
-  return FALSE;
-#endif
-}
+         /* If we had to find a work register and we have not yet
+            saved the LR then add it to the list of regs to push.  */
+         if (l_mask == (1 << LR_REGNUM))
+           {
+             pushable_regs |= l_mask;
+             real_regs_mask |= l_mask;
+             l_mask = 0;
+           }
 
-/* Given the stack offsets and register mask in OFFSETS, decide how
-   many additional registers to push instead of subtracting a constant
-   from SP.  For epilogues the principle is the same except we use pop.
-   FOR_PROLOGUE indicates which we're generating.  */
-static int
-thumb1_extra_regs_pushed (arm_stack_offsets *offsets, bool for_prologue)
-{
-  HOST_WIDE_INT amount;
-  unsigned long live_regs_mask = offsets->saved_regs_mask;
-  /* Extract a mask of the ones we can give to the Thumb's push/pop
-     instruction.  */
-  unsigned long l_mask = live_regs_mask & (for_prologue ? 0x40ff : 0xff);
-  /* Then count how many other high registers will need to be pushed.  */
-  unsigned long high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
-  int n_free, reg_base;
+         insn = thumb1_emit_multi_reg_push (pushable_regs, real_regs_mask);
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
+    }
 
-  if (!for_prologue && frame_pointer_needed)
-    amount = offsets->locals_base - offsets->saved_regs;
-  else
-    amount = offsets->outgoing_args - offsets->saved_regs;
+  /* Load the pic register before setting the frame pointer,
+     so we can use r7 as a temporary work register.  */
+  if (flag_pic && arm_pic_register != INVALID_REGNUM)
+    arm_load_pic_register (live_regs_mask);
 
-  /* If the stack frame size is 512 exactly, we can save one load
-     instruction, which should make this a win even when optimizing
-     for speed.  */
-  if (!optimize_size && amount != 512)
-    return 0;
+  if (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0)
+    emit_move_insn (gen_rtx_REG (Pmode, ARM_HARD_FRAME_POINTER_REGNUM),
+                   stack_pointer_rtx);
+
+  if (flag_stack_usage_info)
+    current_function_static_stack_size
+      = offsets->outgoing_args - offsets->saved_args;
+
+  amount = offsets->outgoing_args - offsets->saved_regs;
+  amount -= 4 * thumb1_extra_regs_pushed (offsets, true);
+  if (amount)
+    {
+      if (amount < 512)
+       {
+         insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                                       GEN_INT (- amount)));
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
+      else
+       {
+         rtx reg, dwarf;
+
+         /* The stack decrement is too big for an immediate value in a single
+            insn.  In theory we could issue multiple subtracts, but after
+            three of them it becomes more space efficient to place the full
+            value in the constant pool and load into a register.  (Also the
+            ARM debugger really likes to see only one stack decrement per
+            function).  So instead we look for a scratch register into which
+            we can load the decrement, and then we subtract this from the
+            stack pointer.  Unfortunately on the thumb the only available
+            scratch registers are the argument registers, and we cannot use
+            these as they may hold arguments to the function.  Instead we
+            attempt to locate a call preserved register which is used by this
+            function.  If we can find one, then we know that it will have
+            been pushed at the start of the prologue and so we can corrupt
+            it now.  */
+         for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++)
+           if (live_regs_mask & (1 << regno))
+             break;
 
-  /* Can't do this if there are high registers to push.  */
-  if (high_regs_pushed != 0)
-    return 0;
+         gcc_assert(regno <= LAST_LO_REGNUM);
 
-  /* Shouldn't do it in the prologue if no registers would normally
-     be pushed at all.  In the epilogue, also allow it if we'll have
-     a pop insn for the PC.  */
-  if  (l_mask == 0
-       && (for_prologue
-          || TARGET_BACKTRACE
-          || (live_regs_mask & 1 << LR_REGNUM) == 0
-          || TARGET_INTERWORK
-          || crtl->args.pretend_args_size != 0))
-    return 0;
+         reg = gen_rtx_REG (SImode, regno);
 
-  /* Don't do this if thumb_expand_prologue wants to emit instructions
-     between the push and the stack frame allocation.  */
-  if (for_prologue
-      && ((flag_pic && arm_pic_register != INVALID_REGNUM)
-         || (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0)))
-    return 0;
+         emit_insn (gen_movsi (reg, GEN_INT (- amount)));
 
-  reg_base = 0;
-  n_free = 0;
-  if (!for_prologue)
-    {
-      reg_base = arm_size_return_regs () / UNITS_PER_WORD;
-      live_regs_mask >>= reg_base;
-    }
+         insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
+                                       stack_pointer_rtx, reg));
 
-  while (reg_base + n_free < 8 && !(live_regs_mask & 1)
-        && (for_prologue || call_used_regs[reg_base + n_free]))
-    {
-      live_regs_mask >>= 1;
-      n_free++;
+         dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                              plus_constant (stack_pointer_rtx,
+                                             -amount));
+         add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
+         RTX_FRAME_RELATED_P (insn) = 1;
+       }
     }
 
-  if (n_free == 0)
-    return 0;
-  gcc_assert (amount / 4 * 4 == amount);
+  if (frame_pointer_needed)
+    thumb_set_frame_pointer (offsets);
 
-  if (amount >= 512 && (amount - n_free * 4) < 512)
-    return (amount - 508) / 4;
-  if (amount <= n_free * 4)
-    return amount / 4;
-  return 0;
+  /* If we are profiling, make sure no instructions are scheduled before
+     the call to mcount.  Similarly if the user has requested no
+     scheduling in the prolog.  Similarly if we want non-call exceptions
+     using the EABI unwinder, to prevent faulting instructions from being
+     swapped with a stack adjustment.  */
+  if (crtl->profile || !TARGET_SCHED_PROLOG
+      || (arm_except_unwind_info (&global_options) == UI_TARGET
+         && cfun->can_throw_non_call_exceptions))
+    emit_insn (gen_blockage ());
+
+  cfun->machine->lr_save_eliminated = !thumb_force_lr_save ();
+  if (live_regs_mask & 0xff)
+    cfun->machine->lr_save_eliminated = 0;
 }
 
-/* The bits which aren't usefully expanded as rtl.  */
-const char *
-thumb_unexpanded_epilogue (void)
+
+void
+thumb1_expand_epilogue (void)
 {
+  HOST_WIDE_INT amount;
   arm_stack_offsets *offsets;
   int regno;
-  unsigned long live_regs_mask = 0;
-  int high_regs_pushed = 0;
-  int extra_pop;
-  int had_to_push_lr;
-  int size;
-
-  if (cfun->machine->return_used_this_function != 0)
-    return "";
 
+  /* Naked functions don't have prologues.  */
   if (IS_NAKED (arm_current_func_type ()))
-    return "";
+    return;
 
   offsets = arm_get_frame_offsets ();
-  live_regs_mask = offsets->saved_regs_mask;
-  high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
+  amount = offsets->outgoing_args - offsets->saved_regs;
 
-  /* If we can deduce the registers used from the function's return value.
-     This is more reliable that examining df_regs_ever_live_p () because that
-     will be set if the register is ever used in the function, not just if
-     the register is used to hold a return value.  */
-  size = arm_size_return_regs ();
+  if (frame_pointer_needed)
+    {
+      emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
+      amount = offsets->locals_base - offsets->saved_regs;
+    }
+  amount -= 4 * thumb1_extra_regs_pushed (offsets, false);
 
-  extra_pop = thumb1_extra_regs_pushed (offsets, false);
-  if (extra_pop > 0)
+  gcc_assert (amount >= 0);
+  if (amount)
     {
-      unsigned long extra_mask = (1 << extra_pop) - 1;
-      live_regs_mask |= extra_mask << (size / UNITS_PER_WORD);
+      emit_insn (gen_blockage ());
+
+      if (amount < 512)
+       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
+                              GEN_INT (amount)));
+      else
+       {
+         /* r3 is always free in the epilogue.  */
+         rtx reg = gen_rtx_REG (SImode, LAST_ARG_REGNUM);
+
+         emit_insn (gen_movsi (reg, GEN_INT (amount)));
+         emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
+       }
     }
 
-  /* The prolog may have pushed some high registers to use as
-     work registers.  e.g. the testsuite file:
-     gcc/testsuite/gcc/gcc.c-torture/execute/complex-2.c
-     compiles to produce:
-       push    {r4, r5, r6, r7, lr}
-       mov     r7, r9
-       mov     r6, r8
-       push    {r6, r7}
-     as part of the prolog.  We have to undo that pushing here.  */
+  /* Emit a USE (stack_pointer_rtx), so that
+     the stack adjustment will not be deleted.  */
+  emit_insn (gen_prologue_use (stack_pointer_rtx));
 
-  if (high_regs_pushed)
-    {
-      unsigned long mask = live_regs_mask & 0xff;
-      int next_hi_reg;
+  if (crtl->profile || !TARGET_SCHED_PROLOG)
+    emit_insn (gen_blockage ());
 
-      /* The available low registers depend on the size of the value we are
-         returning.  */
-      if (size <= 12)
-       mask |=  1 << 3;
-      if (size <= 8)
-       mask |= 1 << 2;
+  /* Emit a clobber for each insn that will be restored in the epilogue,
+     so that flow2 will get register lifetimes correct.  */
+  for (regno = 0; regno < 13; regno++)
+    if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
+      emit_clobber (gen_rtx_REG (SImode, regno));
 
-      if (mask == 0)
-       /* Oh dear!  We have no low registers into which we can pop
-           high registers!  */
-       internal_error
-         ("no low registers available for popping high registers");
+  if (! df_regs_ever_live_p (LR_REGNUM))
+    emit_use (gen_rtx_REG (SImode, LR_REGNUM));
+}
 
-      for (next_hi_reg = 8; next_hi_reg < 13; next_hi_reg++)
-       if (live_regs_mask & (1 << next_hi_reg))
-         break;
+/* Implementation of insn prologue_thumb1_interwork.  This is the first
+   "instruction" of a function called in ARM mode.  Swap to thumb mode.  */
 
-      while (high_regs_pushed)
-       {
-         /* Find lo register(s) into which the high register(s) can
-             be popped.  */
-         for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
-           {
-             if (mask & (1 << regno))
-               high_regs_pushed--;
-             if (high_regs_pushed == 0)
-               break;
-           }
+const char *
+thumb1_output_interwork (void)
+{
+  const char * name;
+  FILE *f = asm_out_file;
+
+  gcc_assert (GET_CODE (DECL_RTL (current_function_decl)) == MEM);
+  gcc_assert (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0))
+             == SYMBOL_REF);
+  name = XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0);
+
+  /* Generate code sequence to switch us into Thumb mode.  */
+  /* The .code 32 directive has already been emitted by
+     ASM_DECLARE_FUNCTION_NAME.  */
+  asm_fprintf (f, "\torr\t%r, %r, #1\n", IP_REGNUM, PC_REGNUM);
+  asm_fprintf (f, "\tbx\t%r\n", IP_REGNUM);
+
+  /* Generate a label, so that the debugger will notice the
+     change in instruction sets.  This label is also used by
+     the assembler to bypass the ARM code when this function
+     is called from a Thumb encoded function elsewhere in the
+     same file.  Hence the definition of STUB_NAME here must
+     agree with the definition in gas/config/tc-arm.c.  */
 
-         mask &= (2 << regno) - 1;     /* A noop if regno == 8 */
+#define STUB_NAME ".real_start_of"
 
-         /* Pop the values into the low register(s).  */
-         thumb_pushpop (asm_out_file, mask, 0, NULL, mask);
+  fprintf (f, "\t.code\t16\n");
+#ifdef ARM_PE
+  if (arm_dllexport_name_p (name))
+    name = arm_strip_name_encoding (name);
+#endif
+  asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name);
+  fprintf (f, "\t.thumb_func\n");
+  asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name);
 
-         /* Move the value(s) into the high registers.  */
-         for (regno = 0; regno <= LAST_LO_REGNUM; regno++)
-           {
-             if (mask & (1 << regno))
-               {
-                 asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", next_hi_reg,
-                              regno);
+  return "";
+}
 
-                 for (next_hi_reg++; next_hi_reg < 13; next_hi_reg++)
-                   if (live_regs_mask & (1 << next_hi_reg))
-                     break;
-               }
-           }
-       }
-      live_regs_mask &= ~0x0f00;
-    }
+/* Handle the case of a double word load into a low register from
+   a computed memory address.  The computed address may involve a
+   register which is overwritten by the load.  */
+const char *
+thumb_load_double_from_address (rtx *operands)
+{
+  rtx addr;
+  rtx base;
+  rtx offset;
+  rtx arg1;
+  rtx arg2;
 
-  had_to_push_lr = (live_regs_mask & (1 << LR_REGNUM)) != 0;
-  live_regs_mask &= 0xff;
+  gcc_assert (GET_CODE (operands[0]) == REG);
+  gcc_assert (GET_CODE (operands[1]) == MEM);
 
-  if (crtl->args.pretend_args_size == 0 || TARGET_BACKTRACE)
+  /* Get the memory address.  */
+  addr = XEXP (operands[1], 0);
+
+  /* Work out how the memory address is computed.  */
+  switch (GET_CODE (addr))
     {
-      /* Pop the return address into the PC.  */
-      if (had_to_push_lr)
-       live_regs_mask |= 1 << PC_REGNUM;
+    case REG:
+      operands[2] = adjust_address (operands[1], SImode, 4);
 
-      /* Either no argument registers were pushed or a backtrace
-        structure was created which includes an adjusted stack
-        pointer, so just pop everything.  */
-      if (live_regs_mask)
-       thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL,
-                      live_regs_mask);
+      if (REGNO (operands[0]) == REGNO (addr))
+       {
+         output_asm_insn ("ldr\t%H0, %2", operands);
+         output_asm_insn ("ldr\t%0, %1", operands);
+       }
+      else
+       {
+         output_asm_insn ("ldr\t%0, %1", operands);
+         output_asm_insn ("ldr\t%H0, %2", operands);
+       }
+      break;
 
-      /* We have either just popped the return address into the
-        PC or it is was kept in LR for the entire function.
-        Note that thumb_pushpop has already called thumb_exit if the
-        PC was in the list.  */
-      if (!had_to_push_lr)
-       thumb_exit (asm_out_file, LR_REGNUM);
-    }
-  else
-    {
-      /* Pop everything but the return address.  */
-      if (live_regs_mask)
-       thumb_pushpop (asm_out_file, live_regs_mask, FALSE, NULL,
-                      live_regs_mask);
+    case CONST:
+      /* Compute <address> + 4 for the high order load.  */
+      operands[2] = adjust_address (operands[1], SImode, 4);
+
+      output_asm_insn ("ldr\t%0, %1", operands);
+      output_asm_insn ("ldr\t%H0, %2", operands);
+      break;
+
+    case PLUS:
+      arg1   = XEXP (addr, 0);
+      arg2   = XEXP (addr, 1);
+
+      if (CONSTANT_P (arg1))
+       base = arg2, offset = arg1;
+      else
+       base = arg1, offset = arg2;
 
-      if (had_to_push_lr)
+      gcc_assert (GET_CODE (base) == REG);
+
+      /* Catch the case of <address> = <reg> + <reg> */
+      if (GET_CODE (offset) == REG)
        {
-         if (size > 12)
-           {
-             /* We have no free low regs, so save one.  */
-             asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", IP_REGNUM,
-                          LAST_ARG_REGNUM);
-           }
+         int reg_offset = REGNO (offset);
+         int reg_base   = REGNO (base);
+         int reg_dest   = REGNO (operands[0]);
 
-         /* Get the return address into a temporary register.  */
-         thumb_pushpop (asm_out_file, 1 << LAST_ARG_REGNUM, 0, NULL,
-                        1 << LAST_ARG_REGNUM);
+         /* Add the base and offset registers together into the
+             higher destination register.  */
+         asm_fprintf (asm_out_file, "\tadd\t%r, %r, %r",
+                      reg_dest + 1, reg_base, reg_offset);
 
-         if (size > 12)
+         /* Load the lower destination register from the address in
+             the higher destination register.  */
+         asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #0]",
+                      reg_dest, reg_dest + 1);
+
+         /* Load the higher destination register from its own address
+             plus 4.  */
+         asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #4]",
+                      reg_dest + 1, reg_dest + 1);
+       }
+      else
+       {
+         /* Compute <address> + 4 for the high order load.  */
+         operands[2] = adjust_address (operands[1], SImode, 4);
+
+         /* If the computed address is held in the low order register
+            then load the high order register first, otherwise always
+            load the low order register first.  */
+         if (REGNO (operands[0]) == REGNO (base))
            {
-             /* Move the return address to lr.  */
-             asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LR_REGNUM,
-                          LAST_ARG_REGNUM);
-             /* Restore the low register.  */
-             asm_fprintf (asm_out_file, "\tmov\t%r, %r\n", LAST_ARG_REGNUM,
-                          IP_REGNUM);
-             regno = LR_REGNUM;
+             output_asm_insn ("ldr\t%H0, %2", operands);
+             output_asm_insn ("ldr\t%0, %1", operands);
            }
          else
-           regno = LAST_ARG_REGNUM;
+           {
+             output_asm_insn ("ldr\t%0, %1", operands);
+             output_asm_insn ("ldr\t%H0, %2", operands);
+           }
        }
-      else
-       regno = LR_REGNUM;
+      break;
 
-      /* Remove the argument registers that were pushed onto the stack.  */
-      asm_fprintf (asm_out_file, "\tadd\t%r, %r, #%d\n",
-                  SP_REGNUM, SP_REGNUM,
-                  crtl->args.pretend_args_size);
+    case LABEL_REF:
+      /* With no registers to worry about we can just load the value
+         directly.  */
+      operands[2] = adjust_address (operands[1], SImode, 4);
 
-      thumb_exit (asm_out_file, regno);
+      output_asm_insn ("ldr\t%H0, %2", operands);
+      output_asm_insn ("ldr\t%0, %1", operands);
+      break;
+
+    default:
+      gcc_unreachable ();
     }
 
   return "";
 }
 
-/* Functions to save and restore machine-specific function data.  */
-static struct machine_function *
-arm_init_machine_status (void)
+const char *
+thumb_output_move_mem_multiple (int n, rtx *operands)
 {
-  struct machine_function *machine;
-  machine = ggc_alloc_cleared_machine_function ();
+  rtx tmp;
 
-#if ARM_FT_UNKNOWN != 0
-  machine->func_type = ARM_FT_UNKNOWN;
-#endif
-  return machine;
-}
+  switch (n)
+    {
+    case 2:
+      if (REGNO (operands[4]) > REGNO (operands[5]))
+       {
+         tmp = operands[4];
+         operands[4] = operands[5];
+         operands[5] = tmp;
+       }
+      output_asm_insn ("ldmia\t%1!, {%4, %5}", operands);
+      output_asm_insn ("stmia\t%0!, {%4, %5}", operands);
+      break;
 
-/* Return an RTX indicating where the return address to the
-   calling function can be found.  */
-rtx
-arm_return_addr (int count, rtx frame ATTRIBUTE_UNUSED)
-{
-  if (count != 0)
-    return NULL_RTX;
+    case 3:
+      if (REGNO (operands[4]) > REGNO (operands[5]))
+       {
+         tmp = operands[4];
+         operands[4] = operands[5];
+         operands[5] = tmp;
+       }
+      if (REGNO (operands[5]) > REGNO (operands[6]))
+       {
+         tmp = operands[5];
+         operands[5] = operands[6];
+         operands[6] = tmp;
+       }
+      if (REGNO (operands[4]) > REGNO (operands[5]))
+       {
+         tmp = operands[4];
+         operands[4] = operands[5];
+         operands[5] = tmp;
+       }
 
-  return get_hard_reg_initial_val (Pmode, LR_REGNUM);
-}
+      output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands);
+      output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands);
+      break;
 
-/* Do anything needed before RTL is emitted for each function.  */
-void
-arm_init_expanders (void)
-{
-  /* Arrange to initialize and mark the machine per-function status.  */
-  init_machine_status = arm_init_machine_status;
+    default:
+      gcc_unreachable ();
+    }
 
-  /* This is to stop the combine pass optimizing away the alignment
-     adjustment of va_arg.  */
-  /* ??? It is claimed that this should not be necessary.  */
-  if (cfun)
-    mark_reg_pointer (arg_pointer_rtx, PARM_BOUNDARY);
+  return "";
 }
 
-
-/* Like arm_compute_initial_elimination offset.  Simpler because there
-   isn't an ABI specified frame pointer for Thumb.  Instead, we set it
-   to point at the base of the local variables after static stack
-   space for a function has been allocated.  */
-
-HOST_WIDE_INT
-thumb_compute_initial_elimination_offset (unsigned int from, unsigned int to)
+/* Output a call-via instruction for thumb state.  */
+const char *
+thumb_call_via_reg (rtx reg)
 {
-  arm_stack_offsets *offsets;
+  int regno = REGNO (reg);
+  rtx *labelp;
 
-  offsets = arm_get_frame_offsets ();
+  gcc_assert (regno < LR_REGNUM);
 
-  switch (from)
+  /* If we are in the normal text section we can use a single instance
+     per compilation unit.  If we are doing function sections, then we need
+     an entry per section, since we can't rely on reachability.  */
+  if (in_section == text_section)
     {
-    case ARG_POINTER_REGNUM:
-      switch (to)
-       {
-       case STACK_POINTER_REGNUM:
-         return offsets->outgoing_args - offsets->saved_args;
+      thumb_call_reg_needed = 1;
 
-       case FRAME_POINTER_REGNUM:
-         return offsets->soft_frame - offsets->saved_args;
+      if (thumb_call_via_label[regno] == NULL)
+       thumb_call_via_label[regno] = gen_label_rtx ();
+      labelp = thumb_call_via_label + regno;
+    }
+  else
+    {
+      if (cfun->machine->call_via[regno] == NULL)
+       cfun->machine->call_via[regno] = gen_label_rtx ();
+      labelp = cfun->machine->call_via + regno;
+    }
 
-       case ARM_HARD_FRAME_POINTER_REGNUM:
-         return offsets->saved_regs - offsets->saved_args;
+  output_asm_insn ("bl\t%a0", labelp);
+  return "";
+}
 
-       case THUMB_HARD_FRAME_POINTER_REGNUM:
-         return offsets->locals_base - offsets->saved_args;
+/* Routines for generating rtl.  */
+void
+thumb_expand_movmemqi (rtx *operands)
+{
+  rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
+  rtx in  = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
+  HOST_WIDE_INT len = INTVAL (operands[2]);
+  HOST_WIDE_INT offset = 0;
 
-       default:
-         gcc_unreachable ();
-       }
-      break;
+  while (len >= 12)
+    {
+      emit_insn (gen_movmem12b (out, in, out, in));
+      len -= 12;
+    }
 
-    case FRAME_POINTER_REGNUM:
-      switch (to)
-       {
-       case STACK_POINTER_REGNUM:
-         return offsets->outgoing_args - offsets->soft_frame;
+  if (len >= 8)
+    {
+      emit_insn (gen_movmem8b (out, in, out, in));
+      len -= 8;
+    }
 
-       case ARM_HARD_FRAME_POINTER_REGNUM:
-         return offsets->saved_regs - offsets->soft_frame;
+  if (len >= 4)
+    {
+      rtx reg = gen_reg_rtx (SImode);
+      emit_insn (gen_movsi (reg, gen_rtx_MEM (SImode, in)));
+      emit_insn (gen_movsi (gen_rtx_MEM (SImode, out), reg));
+      len -= 4;
+      offset += 4;
+    }
 
-       case THUMB_HARD_FRAME_POINTER_REGNUM:
-         return offsets->locals_base - offsets->soft_frame;
+  if (len >= 2)
+    {
+      rtx reg = gen_reg_rtx (HImode);
+      emit_insn (gen_movhi (reg, gen_rtx_MEM (HImode,
+                                             plus_constant (in, offset))));
+      emit_insn (gen_movhi (gen_rtx_MEM (HImode, plus_constant (out, offset)),
+                           reg));
+      len -= 2;
+      offset += 2;
+    }
 
-       default:
-         gcc_unreachable ();
-       }
-      break;
+  if (len)
+    {
+      rtx reg = gen_reg_rtx (QImode);
+      emit_insn (gen_movqi (reg, gen_rtx_MEM (QImode,
+                                             plus_constant (in, offset))));
+      emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (out, offset)),
+                           reg));
+    }
+}
+
+void
+thumb_reload_out_hi (rtx *operands)
+{
+  emit_insn (gen_thumb_movhi_clobber (operands[0], operands[1], operands[2]));
+}
 
-    default:
-      gcc_unreachable ();
+/* Handle reading a half-word from memory during reload.  */
+void
+thumb_reload_in_hi (rtx *operands ATTRIBUTE_UNUSED)
+{
+  gcc_unreachable ();
+}
+
+/* Return the length of a function name prefix
+    that starts with the character 'c'.  */
+static int
+arm_get_strip_length (int c)
+{
+  switch (c)
+    {
+    ARM_NAME_ENCODING_LENGTHS
+      default: return 0;
     }
 }
 
-/* Generate the rest of a function's prologue.  */
-void
-thumb1_expand_prologue (void)
+/* Return a pointer to a function's name with any
+   and all prefix encodings stripped from it.  */
+const char *
+arm_strip_name_encoding (const char *name)
 {
-  rtx insn, dwarf;
+  int skip;
 
-  HOST_WIDE_INT amount;
-  arm_stack_offsets *offsets;
-  unsigned long func_type;
-  int regno;
-  unsigned long live_regs_mask;
+  while ((skip = arm_get_strip_length (* name)))
+    name += skip;
 
-  func_type = arm_current_func_type ();
+  return name;
+}
 
-  /* Naked functions don't have prologues.  */
-  if (IS_NAKED (func_type))
-    return;
+/* If there is a '*' anywhere in the name's prefix, then
+   emit the stripped name verbatim, otherwise prepend an
+   underscore if leading underscores are being used.  */
+void
+arm_asm_output_labelref (FILE *stream, const char *name)
+{
+  int skip;
+  int verbatim = 0;
 
-  if (IS_INTERRUPT (func_type))
+  while ((skip = arm_get_strip_length (* name)))
     {
-      error ("interrupt Service Routines cannot be coded in Thumb mode");
-      return;
+      verbatim |= (*name == '*');
+      name += skip;
     }
 
-  offsets = arm_get_frame_offsets ();
-  live_regs_mask = offsets->saved_regs_mask;
-  /* Load the pic register before setting the frame pointer,
-     so we can use r7 as a temporary work register.  */
-  if (flag_pic && arm_pic_register != INVALID_REGNUM)
-    arm_load_pic_register (live_regs_mask);
+  if (verbatim)
+    fputs (name, stream);
+  else
+    asm_fprintf (stream, "%U%s", name);
+}
 
-  if (!frame_pointer_needed && CALLER_INTERWORKING_SLOT_SIZE > 0)
-    emit_move_insn (gen_rtx_REG (Pmode, ARM_HARD_FRAME_POINTER_REGNUM),
-                   stack_pointer_rtx);
+static void
+arm_file_start (void)
+{
+  int val;
 
-  if (flag_stack_usage)
-    current_function_static_stack_size
-      = offsets->outgoing_args - offsets->saved_args;
+  if (TARGET_UNIFIED_ASM)
+    asm_fprintf (asm_out_file, "\t.syntax unified\n");
 
-  amount = offsets->outgoing_args - offsets->saved_regs;
-  amount -= 4 * thumb1_extra_regs_pushed (offsets, true);
-  if (amount)
+  if (TARGET_BPABI)
     {
-      if (amount < 512)
+      const char *fpu_name;
+      if (arm_selected_arch)
+       asm_fprintf (asm_out_file, "\t.arch %s\n", arm_selected_arch->name);
+      else if (strncmp (arm_selected_cpu->name, "generic", 7) == 0)
+       asm_fprintf (asm_out_file, "\t.arch %s\n", arm_selected_cpu->name + 8);
+      else
+       asm_fprintf (asm_out_file, "\t.cpu %s\n", arm_selected_cpu->name);
+
+      if (TARGET_SOFT_FLOAT)
        {
-         insn = emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                                       GEN_INT (- amount)));
-         RTX_FRAME_RELATED_P (insn) = 1;
+         if (TARGET_VFP)
+           fpu_name = "softvfp";
+         else
+           fpu_name = "softfpa";
        }
       else
        {
-         rtx reg;
+         fpu_name = arm_fpu_desc->name;
+         if (arm_fpu_desc->model == ARM_FP_MODEL_VFP)
+           {
+             if (TARGET_HARD_FLOAT)
+               EMIT_EABI_ATTRIBUTE (Tag_ABI_HardFP_use, 27, 3);
+             if (TARGET_HARD_FLOAT_ABI)
+               EMIT_EABI_ATTRIBUTE (Tag_ABI_VFP_args, 28, 1);
+           }
+       }
+      asm_fprintf (asm_out_file, "\t.fpu %s\n", fpu_name);
 
-         /* The stack decrement is too big for an immediate value in a single
-            insn.  In theory we could issue multiple subtracts, but after
-            three of them it becomes more space efficient to place the full
-            value in the constant pool and load into a register.  (Also the
-            ARM debugger really likes to see only one stack decrement per
-            function).  So instead we look for a scratch register into which
-            we can load the decrement, and then we subtract this from the
-            stack pointer.  Unfortunately on the thumb the only available
-            scratch registers are the argument registers, and we cannot use
-            these as they may hold arguments to the function.  Instead we
-            attempt to locate a call preserved register which is used by this
-            function.  If we can find one, then we know that it will have
-            been pushed at the start of the prologue and so we can corrupt
-            it now.  */
-         for (regno = LAST_ARG_REGNUM + 1; regno <= LAST_LO_REGNUM; regno++)
-           if (live_regs_mask & (1 << regno))
-             break;
+      /* Some of these attributes only apply when the corresponding features
+         are used.  However we don't have any easy way of figuring this out.
+        Conservatively record the setting that would have been used.  */
 
-         gcc_assert(regno <= LAST_LO_REGNUM);
+      if (flag_rounding_math)
+       EMIT_EABI_ATTRIBUTE (Tag_ABI_FP_rounding, 19, 1);
 
-         reg = gen_rtx_REG (SImode, regno);
+      if (!flag_unsafe_math_optimizations)
+       {
+         EMIT_EABI_ATTRIBUTE (Tag_ABI_FP_denormal, 20, 1);
+         EMIT_EABI_ATTRIBUTE (Tag_ABI_FP_exceptions, 21, 1);
+       }
+      if (flag_signaling_nans)
+       EMIT_EABI_ATTRIBUTE (Tag_ABI_FP_user_exceptions, 22, 1);
 
-         emit_insn (gen_movsi (reg, GEN_INT (- amount)));
+      EMIT_EABI_ATTRIBUTE (Tag_ABI_FP_number_model, 23,
+                          flag_finite_math_only ? 1 : 3);
 
-         insn = emit_insn (gen_addsi3 (stack_pointer_rtx,
-                                       stack_pointer_rtx, reg));
-         RTX_FRAME_RELATED_P (insn) = 1;
-         dwarf = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                              plus_constant (stack_pointer_rtx,
-                                             -amount));
-         RTX_FRAME_RELATED_P (dwarf) = 1;
-         add_reg_note (insn, REG_FRAME_RELATED_EXPR, dwarf);
-       }
-    }
+      EMIT_EABI_ATTRIBUTE (Tag_ABI_align8_needed, 24, 1);
+      EMIT_EABI_ATTRIBUTE (Tag_ABI_align8_preserved, 25, 1);
+      EMIT_EABI_ATTRIBUTE (Tag_ABI_enum_size, 26, flag_short_enums ? 1 : 2);
 
-  if (frame_pointer_needed)
-    thumb_set_frame_pointer (offsets);
+      /* Tag_ABI_optimization_goals.  */
+      if (optimize_size)
+       val = 4;
+      else if (optimize >= 2)
+       val = 2;
+      else if (optimize)
+       val = 1;
+      else
+       val = 6;
+      EMIT_EABI_ATTRIBUTE (Tag_ABI_optimization_goals, 30, val);
 
-  /* If we are profiling, make sure no instructions are scheduled before
-     the call to mcount.  Similarly if the user has requested no
-     scheduling in the prolog.  Similarly if we want non-call exceptions
-     using the EABI unwinder, to prevent faulting instructions from being
-     swapped with a stack adjustment.  */
-  if (crtl->profile || !TARGET_SCHED_PROLOG
-      || (arm_except_unwind_info (&global_options) == UI_TARGET
-         && cfun->can_throw_non_call_exceptions))
-    emit_insn (gen_blockage ());
+      EMIT_EABI_ATTRIBUTE (Tag_CPU_unaligned_access, 34, unaligned_access);
 
-  cfun->machine->lr_save_eliminated = !thumb_force_lr_save ();
-  if (live_regs_mask & 0xff)
-    cfun->machine->lr_save_eliminated = 0;
-}
+      if (arm_fp16_format)
+       EMIT_EABI_ATTRIBUTE (Tag_ABI_FP_16bit_format, 38, (int) arm_fp16_format);
+
+      if (arm_lang_output_object_attributes_hook)
+       arm_lang_output_object_attributes_hook();
+    }
 
+  default_file_start ();
+}
 
-void
-thumb1_expand_epilogue (void)
+static void
+arm_file_end (void)
 {
-  HOST_WIDE_INT amount;
-  arm_stack_offsets *offsets;
   int regno;
 
-  /* Naked functions don't have prologues.  */
-  if (IS_NAKED (arm_current_func_type ()))
+  if (NEED_INDICATE_EXEC_STACK)
+    /* Add .note.GNU-stack.  */
+    file_end_indicate_exec_stack ();
+
+  if (! thumb_call_reg_needed)
     return;
 
-  offsets = arm_get_frame_offsets ();
-  amount = offsets->outgoing_args - offsets->saved_regs;
+  switch_to_section (text_section);
+  asm_fprintf (asm_out_file, "\t.code 16\n");
+  ASM_OUTPUT_ALIGN (asm_out_file, 1);
 
-  if (frame_pointer_needed)
+  for (regno = 0; regno < LR_REGNUM; regno++)
     {
-      emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx));
-      amount = offsets->locals_base - offsets->saved_regs;
-    }
-  amount -= 4 * thumb1_extra_regs_pushed (offsets, false);
+      rtx label = thumb_call_via_label[regno];
 
-  gcc_assert (amount >= 0);
-  if (amount)
-    {
-      if (amount < 512)
-       emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx,
-                              GEN_INT (amount)));
-      else
+      if (label != 0)
        {
-         /* r3 is always free in the epilogue.  */
-         rtx reg = gen_rtx_REG (SImode, LAST_ARG_REGNUM);
-
-         emit_insn (gen_movsi (reg, GEN_INT (amount)));
-         emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, reg));
+         targetm.asm_out.internal_label (asm_out_file, "L",
+                                         CODE_LABEL_NUMBER (label));
+         asm_fprintf (asm_out_file, "\tbx\t%r\n", regno);
        }
     }
-
-  /* Emit a USE (stack_pointer_rtx), so that
-     the stack adjustment will not be deleted.  */
-  emit_insn (gen_prologue_use (stack_pointer_rtx));
-
-  if (crtl->profile || !TARGET_SCHED_PROLOG)
-    emit_insn (gen_blockage ());
-
-  /* Emit a clobber for each insn that will be restored in the epilogue,
-     so that flow2 will get register lifetimes correct.  */
-  for (regno = 0; regno < 13; regno++)
-    if (df_regs_ever_live_p (regno) && !call_used_regs[regno])
-      emit_clobber (gen_rtx_REG (SImode, regno));
-
-  if (! df_regs_ever_live_p (LR_REGNUM))
-    emit_use (gen_rtx_REG (SImode, LR_REGNUM));
 }
 
+#ifndef ARM_PE
+/* Symbols in the text segment can be accessed without indirecting via the
+   constant pool; it may take an extra binary operation, but this is still
+   faster than indirecting via memory.  Don't do this when not optimizing,
+   since we won't be calculating al of the offsets necessary to do this
+   simplification.  */
+
 static void
-thumb1_output_function_prologue (FILE *f, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+arm_encode_section_info (tree decl, rtx rtl, int first)
 {
-  arm_stack_offsets *offsets;
-  unsigned long live_regs_mask = 0;
-  unsigned long l_mask;
-  unsigned high_regs_pushed = 0;
-  int cfa_offset = 0;
-  int regno;
-
-  if (IS_NAKED (arm_current_func_type ()))
-    return;
-
-  if (is_called_in_ARM_mode (current_function_decl))
-    {
-      const char * name;
-
-      gcc_assert (GET_CODE (DECL_RTL (current_function_decl)) == MEM);
-      gcc_assert (GET_CODE (XEXP (DECL_RTL (current_function_decl), 0))
-                 == SYMBOL_REF);
-      name = XSTR  (XEXP (DECL_RTL (current_function_decl), 0), 0);
-
-      /* Generate code sequence to switch us into Thumb mode.  */
-      /* The .code 32 directive has already been emitted by
-        ASM_DECLARE_FUNCTION_NAME.  */
-      asm_fprintf (f, "\torr\t%r, %r, #1\n", IP_REGNUM, PC_REGNUM);
-      asm_fprintf (f, "\tbx\t%r\n", IP_REGNUM);
-
-      /* Generate a label, so that the debugger will notice the
-        change in instruction sets.  This label is also used by
-        the assembler to bypass the ARM code when this function
-        is called from a Thumb encoded function elsewhere in the
-        same file.  Hence the definition of STUB_NAME here must
-        agree with the definition in gas/config/tc-arm.c.  */
+  if (optimize > 0 && TREE_CONSTANT (decl))
+    SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
 
-#define STUB_NAME ".real_start_of"
+  default_encode_section_info (decl, rtl, first);
+}
+#endif /* !ARM_PE */
 
-      fprintf (f, "\t.code\t16\n");
-#ifdef ARM_PE
-      if (arm_dllexport_name_p (name))
-        name = arm_strip_name_encoding (name);
-#endif
-      asm_fprintf (f, "\t.globl %s%U%s\n", STUB_NAME, name);
-      fprintf (f, "\t.thumb_func\n");
-      asm_fprintf (f, "%s%U%s:\n", STUB_NAME, name);
+static void
+arm_internal_label (FILE *stream, const char *prefix, unsigned long labelno)
+{
+  if (arm_ccfsm_state == 3 && (unsigned) arm_target_label == labelno
+      && !strcmp (prefix, "L"))
+    {
+      arm_ccfsm_state = 0;
+      arm_target_insn = NULL;
     }
+  default_internal_label (stream, prefix, labelno);
+}
 
-  if (crtl->args.pretend_args_size)
-    {
-      /* Output unwind directive for the stack adjustment.  */
-      if (arm_except_unwind_info (&global_options) == UI_TARGET)
-       fprintf (f, "\t.pad #%d\n",
-                crtl->args.pretend_args_size);
+/* Output code to add DELTA to the first argument, and then jump
+   to FUNCTION.  Used for C++ multiple inheritance.  */
+static void
+arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
+                    HOST_WIDE_INT delta,
+                    HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
+                    tree function)
+{
+  static int thunk_label = 0;
+  char label[256];
+  char labelpc[256];
+  int mi_delta = delta;
+  const char *const mi_op = mi_delta < 0 ? "sub" : "add";
+  int shift = 0;
+  int this_regno = (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)
+                    ? 1 : 0);
+  if (mi_delta < 0)
+    mi_delta = - mi_delta;
 
-      if (cfun->machine->uses_anonymous_args)
+  if (TARGET_THUMB1)
+    {
+      int labelno = thunk_label++;
+      ASM_GENERATE_INTERNAL_LABEL (label, "LTHUMBFUNC", labelno);
+      /* Thunks are entered in arm mode when avaiable.  */
+      if (TARGET_THUMB1_ONLY)
        {
-         int num_pushes;
-
-         fprintf (f, "\tpush\t{");
-
-         num_pushes = ARM_NUM_INTS (crtl->args.pretend_args_size);
-
-         for (regno = LAST_ARG_REGNUM + 1 - num_pushes;
-              regno <= LAST_ARG_REGNUM;
-              regno++)
-           asm_fprintf (f, "%r%s", regno,
-                        regno == LAST_ARG_REGNUM ? "" : ", ");
-
-         fprintf (f, "}\n");
+         /* push r3 so we can use it as a temporary.  */
+         /* TODO: Omit this save if r3 is not used.  */
+         fputs ("\tpush {r3}\n", file);
+         fputs ("\tldr\tr3, ", file);
        }
       else
-       asm_fprintf (f, "\tsub\t%r, %r, #%d\n",
-                    SP_REGNUM, SP_REGNUM,
-                    crtl->args.pretend_args_size);
-
-      /* We don't need to record the stores for unwinding (would it
-        help the debugger any if we did?), but record the change in
-        the stack pointer.  */
-      if (dwarf2out_do_frame ())
        {
-         char *l = dwarf2out_cfi_label (false);
-
-         cfa_offset = cfa_offset + crtl->args.pretend_args_size;
-         dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
+         fputs ("\tldr\tr12, ", file);
        }
-    }
+      assemble_name (file, label);
+      fputc ('\n', file);
+      if (flag_pic)
+       {
+         /* If we are generating PIC, the ldr instruction below loads
+            "(target - 7) - .LTHUNKPCn" into r12.  The pc reads as
+            the address of the add + 8, so we have:
 
-  /* Get the registers we are going to push.  */
-  offsets = arm_get_frame_offsets ();
-  live_regs_mask = offsets->saved_regs_mask;
-  /* Extract a mask of the ones we can give to the Thumb's push instruction.  */
-  l_mask = live_regs_mask & 0x40ff;
-  /* Then count how many other high registers will need to be pushed.  */
-  high_regs_pushed = bit_count (live_regs_mask & 0x0f00);
+            r12 = (target - 7) - .LTHUNKPCn + (.LTHUNKPCn + 8)
+                = target + 1.
 
-  if (TARGET_BACKTRACE)
+            Note that we have "+ 1" because some versions of GNU ld
+            don't set the low bit of the result for R_ARM_REL32
+            relocations against thumb function symbols.
+            On ARMv6M this is +4, not +8.  */
+         ASM_GENERATE_INTERNAL_LABEL (labelpc, "LTHUNKPC", labelno);
+         assemble_name (file, labelpc);
+         fputs (":\n", file);
+         if (TARGET_THUMB1_ONLY)
+           {
+             /* This is 2 insns after the start of the thunk, so we know it
+                is 4-byte aligned.  */
+             fputs ("\tadd\tr3, pc, r3\n", file);
+             fputs ("\tmov r12, r3\n", file);
+           }
+         else
+           fputs ("\tadd\tr12, pc, r12\n", file);
+       }
+      else if (TARGET_THUMB1_ONLY)
+       fputs ("\tmov r12, r3\n", file);
+    }
+  if (TARGET_THUMB1_ONLY)
     {
-      unsigned offset;
-      unsigned work_register;
-
-      /* We have been asked to create a stack backtrace structure.
-         The code looks like this:
-
-        0   .align 2
-        0   func:
-         0     sub   SP, #16         Reserve space for 4 registers.
-        2     push  {R7}            Push low registers.
-         4     add   R7, SP, #20     Get the stack pointer before the push.
-         6     str   R7, [SP, #8]    Store the stack pointer (before reserving the space).
-         8     mov   R7, PC          Get hold of the start of this code plus 12.
-        10     str   R7, [SP, #16]   Store it.
-        12     mov   R7, FP          Get hold of the current frame pointer.
-        14     str   R7, [SP, #4]    Store it.
-        16     mov   R7, LR          Get hold of the current return address.
-        18     str   R7, [SP, #12]   Store it.
-        20     add   R7, SP, #16     Point at the start of the backtrace structure.
-        22     mov   FP, R7          Put this value into the frame pointer.  */
-
-      work_register = thumb_find_work_register (live_regs_mask);
-
-      if (arm_except_unwind_info (&global_options) == UI_TARGET)
-       asm_fprintf (f, "\t.pad #16\n");
-
-      asm_fprintf
-       (f, "\tsub\t%r, %r, #16\t%@ Create stack backtrace structure\n",
-        SP_REGNUM, SP_REGNUM);
-
-      if (dwarf2out_do_frame ())
+      if (mi_delta > 255)
        {
-         char *l = dwarf2out_cfi_label (false);
-
-         cfa_offset = cfa_offset + 16;
-         dwarf2out_def_cfa (l, SP_REGNUM, cfa_offset);
+         fputs ("\tldr\tr3, ", file);
+         assemble_name (file, label);
+         fputs ("+4\n", file);
+         asm_fprintf (file, "\t%s\t%r, %r, r3\n",
+                      mi_op, this_regno, this_regno);
        }
-
-      if (l_mask)
+      else if (mi_delta != 0)
        {
-         thumb_pushpop (f, l_mask, 1, &cfa_offset, l_mask);
-         offset = bit_count (l_mask) * UNITS_PER_WORD;
+         asm_fprintf (file, "\t%s\t%r, %r, #%d\n",
+                      mi_op, this_regno, this_regno,
+                      mi_delta);
        }
-      else
-       offset = 0;
-
-      asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM,
-                  offset + 16 + crtl->args.pretend_args_size);
-
-      asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
-                  offset + 4);
-
-      /* Make sure that the instruction fetching the PC is in the right place
-        to calculate "start of backtrace creation code + 12".  */
-      if (l_mask)
+    }
+  else
+    {
+      /* TODO: Use movw/movt for large constants when available.  */
+      while (mi_delta != 0)
        {
-         asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM);
-         asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
-                      offset + 12);
-         asm_fprintf (f, "\tmov\t%r, %r\n", work_register,
-                      ARM_HARD_FRAME_POINTER_REGNUM);
-         asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
-                      offset);
+         if ((mi_delta & (3 << shift)) == 0)
+           shift += 2;
+         else
+           {
+             asm_fprintf (file, "\t%s\t%r, %r, #%d\n",
+                          mi_op, this_regno, this_regno,
+                          mi_delta & (0xff << shift));
+             mi_delta &= ~(0xff << shift);
+             shift += 8;
+           }
        }
-      else
+    }
+  if (TARGET_THUMB1)
+    {
+      if (TARGET_THUMB1_ONLY)
+       fputs ("\tpop\t{r3}\n", file);
+
+      fprintf (file, "\tbx\tr12\n");
+      ASM_OUTPUT_ALIGN (file, 2);
+      assemble_name (file, label);
+      fputs (":\n", file);
+      if (flag_pic)
        {
-         asm_fprintf (f, "\tmov\t%r, %r\n", work_register,
-                      ARM_HARD_FRAME_POINTER_REGNUM);
-         asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
-                      offset);
-         asm_fprintf (f, "\tmov\t%r, %r\n", work_register, PC_REGNUM);
-         asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
-                      offset + 12);
+         /* Output ".word .LTHUNKn-7-.LTHUNKPCn".  */
+         rtx tem = XEXP (DECL_RTL (function), 0);
+         tem = gen_rtx_PLUS (GET_MODE (tem), tem, GEN_INT (-7));
+         tem = gen_rtx_MINUS (GET_MODE (tem),
+                              tem,
+                              gen_rtx_SYMBOL_REF (Pmode,
+                                                  ggc_strdup (labelpc)));
+         assemble_integer (tem, 4, BITS_PER_WORD, 1);
        }
+      else
+       /* Output ".word .LTHUNKn".  */
+       assemble_integer (XEXP (DECL_RTL (function), 0), 4, BITS_PER_WORD, 1);
 
-      asm_fprintf (f, "\tmov\t%r, %r\n", work_register, LR_REGNUM);
-      asm_fprintf (f, "\tstr\t%r, [%r, #%d]\n", work_register, SP_REGNUM,
-                  offset + 8);
-      asm_fprintf (f, "\tadd\t%r, %r, #%d\n", work_register, SP_REGNUM,
-                  offset + 12);
-      asm_fprintf (f, "\tmov\t%r, %r\t\t%@ Backtrace structure created\n",
-                  ARM_HARD_FRAME_POINTER_REGNUM, work_register);
+      if (TARGET_THUMB1_ONLY && mi_delta > 255)
+       assemble_integer (GEN_INT(mi_delta), 4, BITS_PER_WORD, 1);
     }
-  /* Optimization:  If we are not pushing any low registers but we are going
-     to push some high registers then delay our first push.  This will just
-     be a push of LR and we can combine it with the push of the first high
-     register.  */
-  else if ((l_mask & 0xff) != 0
-          || (high_regs_pushed == 0 && l_mask))
+  else
     {
-      unsigned long mask = l_mask;
-      mask |= (1 << thumb1_extra_regs_pushed (offsets, true)) - 1;
-      thumb_pushpop (f, mask, 1, &cfa_offset, mask);
+      fputs ("\tb\t", file);
+      assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
+      if (NEED_PLT_RELOC)
+        fputs ("(PLT)", file);
+      fputc ('\n', file);
     }
+}
 
-  if (high_regs_pushed)
-    {
-      unsigned pushable_regs;
-      unsigned next_hi_reg;
-
-      for (next_hi_reg = 12; next_hi_reg > LAST_LO_REGNUM; next_hi_reg--)
-       if (live_regs_mask & (1 << next_hi_reg))
-         break;
-
-      pushable_regs = l_mask & 0xff;
+int
+arm_emit_vector_const (FILE *file, rtx x)
+{
+  int i;
+  const char * pattern;
 
-      if (pushable_regs == 0)
-       pushable_regs = 1 << thumb_find_work_register (live_regs_mask);
+  gcc_assert (GET_CODE (x) == CONST_VECTOR);
 
-      while (high_regs_pushed > 0)
-       {
-         unsigned long real_regs_mask = 0;
+  switch (GET_MODE (x))
+    {
+    case V2SImode: pattern = "%08x"; break;
+    case V4HImode: pattern = "%04x"; break;
+    case V8QImode: pattern = "%02x"; break;
+    default:       gcc_unreachable ();
+    }
 
-         for (regno = LAST_LO_REGNUM; regno >= 0; regno --)
-           {
-             if (pushable_regs & (1 << regno))
-               {
-                 asm_fprintf (f, "\tmov\t%r, %r\n", regno, next_hi_reg);
+  fprintf (file, "0x");
+  for (i = CONST_VECTOR_NUNITS (x); i--;)
+    {
+      rtx element;
 
-                 high_regs_pushed --;
-                 real_regs_mask |= (1 << next_hi_reg);
+      element = CONST_VECTOR_ELT (x, i);
+      fprintf (file, pattern, INTVAL (element));
+    }
 
-                 if (high_regs_pushed)
-                   {
-                     for (next_hi_reg --; next_hi_reg > LAST_LO_REGNUM;
-                          next_hi_reg --)
-                       if (live_regs_mask & (1 << next_hi_reg))
-                         break;
-                   }
-                 else
-                   {
-                     pushable_regs &= ~((1 << regno) - 1);
-                     break;
-                   }
-               }
-           }
+  return 1;
+}
 
-         /* If we had to find a work register and we have not yet
-            saved the LR then add it to the list of regs to push.  */
-         if (l_mask == (1 << LR_REGNUM))
-           {
-             thumb_pushpop (f, pushable_regs | (1 << LR_REGNUM),
-                            1, &cfa_offset,
-                            real_regs_mask | (1 << LR_REGNUM));
-             l_mask = 0;
-           }
-         else
-           thumb_pushpop (f, pushable_regs, 1, &cfa_offset, real_regs_mask);
-       }
-    }
+/* 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);
 }
 
-/* Handle the case of a double word load into a low register from
-   a computed memory address.  The computed address may involve a
-   register which is overwritten by the load.  */
 const char *
-thumb_load_double_from_address (rtx *operands)
+arm_output_load_gr (rtx *operands)
 {
-  rtx addr;
-  rtx base;
+  rtx reg;
   rtx offset;
-  rtx arg1;
-  rtx arg2;
+  rtx wcgr;
+  rtx sum;
 
-  gcc_assert (GET_CODE (operands[0]) == REG);
-  gcc_assert (GET_CODE (operands[1]) == MEM);
+  if (GET_CODE (operands [1]) != MEM
+      || GET_CODE (sum = XEXP (operands [1], 0)) != PLUS
+      || GET_CODE (reg = XEXP (sum, 0)) != REG
+      || GET_CODE (offset = XEXP (sum, 1)) != CONST_INT
+      || ((INTVAL (offset) < 1024) && (INTVAL (offset) > -1024)))
+    return "wldrw%?\t%0, %1";
 
-  /* Get the memory address.  */
-  addr = XEXP (operands[1], 0);
+  /* Fix up an out-of-range load of a GR register.  */
+  output_asm_insn ("str%?\t%0, [sp, #-4]!\t@ Start of GR load expansion", & reg);
+  wcgr = operands[0];
+  operands[0] = reg;
+  output_asm_insn ("ldr%?\t%0, %1", operands);
 
-  /* Work out how the memory address is computed.  */
-  switch (GET_CODE (addr))
-    {
-    case REG:
-      operands[2] = adjust_address (operands[1], SImode, 4);
+  operands[0] = wcgr;
+  operands[1] = reg;
+  output_asm_insn ("tmcr%?\t%0, %1", operands);
+  output_asm_insn ("ldr%?\t%0, [sp], #4\t@ End of GR load expansion", & reg);
 
-      if (REGNO (operands[0]) == REGNO (addr))
-       {
-         output_asm_insn ("ldr\t%H0, %2", operands);
-         output_asm_insn ("ldr\t%0, %1", operands);
-       }
-      else
-       {
-         output_asm_insn ("ldr\t%0, %1", operands);
-         output_asm_insn ("ldr\t%H0, %2", operands);
-       }
-      break;
+  return "";
+}
 
-    case CONST:
-      /* Compute <address> + 4 for the high order load.  */
-      operands[2] = adjust_address (operands[1], SImode, 4);
+/* Worker function for TARGET_SETUP_INCOMING_VARARGS.
 
-      output_asm_insn ("ldr\t%0, %1", operands);
-      output_asm_insn ("ldr\t%H0, %2", operands);
-      break;
+   On the ARM, PRETEND_SIZE is set in order to have the prologue push the last
+   named arg and all anonymous args onto the stack.
+   XXX I know the prologue shouldn't be pushing registers, but it is faster
+   that way.  */
 
-    case PLUS:
-      arg1   = XEXP (addr, 0);
-      arg2   = XEXP (addr, 1);
+static void
+arm_setup_incoming_varargs (cumulative_args_t pcum_v,
+                           enum machine_mode mode,
+                           tree type,
+                           int *pretend_size,
+                           int second_time ATTRIBUTE_UNUSED)
+{
+  CUMULATIVE_ARGS *pcum = get_cumulative_args (pcum_v);
+  int nregs;
 
-      if (CONSTANT_P (arg1))
-       base = arg2, offset = arg1;
-      else
-       base = arg1, offset = arg2;
+  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;
 
-      gcc_assert (GET_CODE (base) == REG);
+  if (nregs < NUM_ARG_REGS)
+    *pretend_size = (NUM_ARG_REGS - nregs) * UNITS_PER_WORD;
+}
 
-      /* Catch the case of <address> = <reg> + <reg> */
-      if (GET_CODE (offset) == REG)
-       {
-         int reg_offset = REGNO (offset);
-         int reg_base   = REGNO (base);
-         int reg_dest   = REGNO (operands[0]);
+/* Return nonzero if the CONSUMER instruction (a store) does not need
+   PRODUCER's value to calculate the address.  */
 
-         /* Add the base and offset registers together into the
-             higher destination register.  */
-         asm_fprintf (asm_out_file, "\tadd\t%r, %r, %r",
-                      reg_dest + 1, reg_base, reg_offset);
+int
+arm_no_early_store_addr_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx addr = PATTERN (consumer);
 
-         /* Load the lower destination register from the address in
-             the higher destination register.  */
-         asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #0]",
-                      reg_dest, reg_dest + 1);
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (addr) == COND_EXEC)
+    addr = COND_EXEC_CODE (addr);
+  if (GET_CODE (addr) == PARALLEL)
+    addr = XVECEXP (addr, 0, 0);
+  addr = XEXP (addr, 0);
 
-         /* Load the higher destination register from its own address
-             plus 4.  */
-         asm_fprintf (asm_out_file, "\tldr\t%r, [%r, #4]",
-                      reg_dest + 1, reg_dest + 1);
-       }
-      else
-       {
-         /* Compute <address> + 4 for the high order load.  */
-         operands[2] = adjust_address (operands[1], SImode, 4);
+  return !reg_overlap_mentioned_p (value, addr);
+}
 
-         /* If the computed address is held in the low order register
-            then load the high order register first, otherwise always
-            load the low order register first.  */
-         if (REGNO (operands[0]) == REGNO (base))
-           {
-             output_asm_insn ("ldr\t%H0, %2", operands);
-             output_asm_insn ("ldr\t%0, %1", operands);
-           }
-         else
-           {
-             output_asm_insn ("ldr\t%0, %1", operands);
-             output_asm_insn ("ldr\t%H0, %2", operands);
-           }
-       }
-      break;
+/* Return nonzero if the CONSUMER instruction (a store) does need
+   PRODUCER's value to calculate the address.  */
 
-    case LABEL_REF:
-      /* With no registers to worry about we can just load the value
-         directly.  */
-      operands[2] = adjust_address (operands[1], SImode, 4);
+int
+arm_early_store_addr_dep (rtx producer, rtx consumer)
+{
+  return !arm_no_early_store_addr_dep (producer, consumer);
+}
 
-      output_asm_insn ("ldr\t%H0, %2", operands);
-      output_asm_insn ("ldr\t%0, %1", operands);
-      break;
+/* Return nonzero if the CONSUMER instruction (a load) does need
+   PRODUCER's value to calculate the address.  */
 
-    default:
-      gcc_unreachable ();
-    }
+int
+arm_early_load_addr_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx addr = PATTERN (consumer);
 
-  return "";
-}
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (addr) == COND_EXEC)
+    addr = COND_EXEC_CODE (addr);
+  if (GET_CODE (addr) == PARALLEL)
+    addr = XVECEXP (addr, 0, 0);
+  addr = XEXP (addr, 1);
 
-const char *
-thumb_output_move_mem_multiple (int n, rtx *operands)
-{
-  rtx tmp;
+  return reg_overlap_mentioned_p (value, addr);
+}
 
-  switch (n)
-    {
-    case 2:
-      if (REGNO (operands[4]) > REGNO (operands[5]))
-       {
-         tmp = operands[4];
-         operands[4] = operands[5];
-         operands[5] = tmp;
-       }
-      output_asm_insn ("ldmia\t%1!, {%4, %5}", operands);
-      output_asm_insn ("stmia\t%0!, {%4, %5}", operands);
-      break;
+/* Return nonzero if the CONSUMER instruction (an ALU op) does not
+   have an early register shift value or amount dependency on the
+   result of PRODUCER.  */
 
-    case 3:
-      if (REGNO (operands[4]) > REGNO (operands[5]))
-       {
-         tmp = operands[4];
-         operands[4] = operands[5];
-         operands[5] = tmp;
-       }
-      if (REGNO (operands[5]) > REGNO (operands[6]))
-       {
-         tmp = operands[5];
-         operands[5] = operands[6];
-         operands[6] = tmp;
-       }
-      if (REGNO (operands[4]) > REGNO (operands[5]))
-       {
-         tmp = operands[4];
-         operands[4] = operands[5];
-         operands[5] = tmp;
-       }
+int
+arm_no_early_alu_shift_dep (rtx producer, rtx consumer)
+{
+  rtx value = PATTERN (producer);
+  rtx op = PATTERN (consumer);
+  rtx early_op;
 
-      output_asm_insn ("ldmia\t%1!, {%4, %5, %6}", operands);
-      output_asm_insn ("stmia\t%0!, {%4, %5, %6}", operands);
-      break;
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (op) == COND_EXEC)
+    op = COND_EXEC_CODE (op);
+  if (GET_CODE (op) == PARALLEL)
+    op = XVECEXP (op, 0, 0);
+  op = XEXP (op, 1);
 
-    default:
-      gcc_unreachable ();
-    }
+  early_op = XEXP (op, 0);
+  /* This is either an actual independent shift, or a shift applied to
+     the first operand of another operation.  We want the whole shift
+     operation.  */
+  if (GET_CODE (early_op) == REG)
+    early_op = op;
 
-  return "";
+  return !reg_overlap_mentioned_p (value, early_op);
 }
 
-/* Output a call-via instruction for thumb state.  */
-const char *
-thumb_call_via_reg (rtx reg)
+/* Return nonzero if the CONSUMER instruction (an ALU op) does not
+   have an early register shift value dependency on the result of
+   PRODUCER.  */
+
+int
+arm_no_early_alu_shift_value_dep (rtx producer, rtx consumer)
 {
-  int regno = REGNO (reg);
-  rtx *labelp;
+  rtx value = PATTERN (producer);
+  rtx op = PATTERN (consumer);
+  rtx early_op;
 
-  gcc_assert (regno < LR_REGNUM);
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (op) == COND_EXEC)
+    op = COND_EXEC_CODE (op);
+  if (GET_CODE (op) == PARALLEL)
+    op = XVECEXP (op, 0, 0);
+  op = XEXP (op, 1);
 
-  /* If we are in the normal text section we can use a single instance
-     per compilation unit.  If we are doing function sections, then we need
-     an entry per section, since we can't rely on reachability.  */
-  if (in_section == text_section)
-    {
-      thumb_call_reg_needed = 1;
+  early_op = XEXP (op, 0);
 
-      if (thumb_call_via_label[regno] == NULL)
-       thumb_call_via_label[regno] = gen_label_rtx ();
-      labelp = thumb_call_via_label + regno;
-    }
-  else
-    {
-      if (cfun->machine->call_via[regno] == NULL)
-       cfun->machine->call_via[regno] = gen_label_rtx ();
-      labelp = cfun->machine->call_via + regno;
-    }
+  /* This is either an actual independent shift, or a shift applied to
+     the first operand of another operation.  We want the value being
+     shifted, in either case.  */
+  if (GET_CODE (early_op) != REG)
+    early_op = XEXP (early_op, 0);
 
-  output_asm_insn ("bl\t%a0", labelp);
-  return "";
+  return !reg_overlap_mentioned_p (value, early_op);
 }
 
-/* Routines for generating rtl.  */
-void
-thumb_expand_movmemqi (rtx *operands)
+/* Return nonzero if the CONSUMER (a mul or mac op) does not
+   have an early register mult dependency on the result of
+   PRODUCER.  */
+
+int
+arm_no_early_mul_dep (rtx producer, rtx consumer)
 {
-  rtx out = copy_to_mode_reg (SImode, XEXP (operands[0], 0));
-  rtx in  = copy_to_mode_reg (SImode, XEXP (operands[1], 0));
-  HOST_WIDE_INT len = INTVAL (operands[2]);
-  HOST_WIDE_INT offset = 0;
+  rtx value = PATTERN (producer);
+  rtx op = PATTERN (consumer);
 
-  while (len >= 12)
-    {
-      emit_insn (gen_movmem12b (out, in, out, in));
-      len -= 12;
-    }
+  if (GET_CODE (value) == COND_EXEC)
+    value = COND_EXEC_CODE (value);
+  if (GET_CODE (value) == PARALLEL)
+    value = XVECEXP (value, 0, 0);
+  value = XEXP (value, 0);
+  if (GET_CODE (op) == COND_EXEC)
+    op = COND_EXEC_CODE (op);
+  if (GET_CODE (op) == PARALLEL)
+    op = XVECEXP (op, 0, 0);
+  op = XEXP (op, 1);
 
-  if (len >= 8)
+  if (GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
     {
-      emit_insn (gen_movmem8b (out, in, out, in));
-      len -= 8;
+      if (GET_CODE (XEXP (op, 0)) == MULT)
+       return !reg_overlap_mentioned_p (value, XEXP (op, 0));
+      else
+       return !reg_overlap_mentioned_p (value, XEXP (op, 1));
     }
 
-  if (len >= 4)
-    {
-      rtx reg = gen_reg_rtx (SImode);
-      emit_insn (gen_movsi (reg, gen_rtx_MEM (SImode, in)));
-      emit_insn (gen_movsi (gen_rtx_MEM (SImode, out), reg));
-      len -= 4;
-      offset += 4;
-    }
+  return 0;
+}
 
-  if (len >= 2)
-    {
-      rtx reg = gen_reg_rtx (HImode);
-      emit_insn (gen_movhi (reg, gen_rtx_MEM (HImode,
-                                             plus_constant (in, offset))));
-      emit_insn (gen_movhi (gen_rtx_MEM (HImode, plus_constant (out, offset)),
-                           reg));
-      len -= 2;
-      offset += 2;
-    }
+/* We can't rely on the caller doing the proper promotion when
+   using APCS or ATPCS.  */
 
-  if (len)
-    {
-      rtx reg = gen_reg_rtx (QImode);
-      emit_insn (gen_movqi (reg, gen_rtx_MEM (QImode,
-                                             plus_constant (in, offset))));
-      emit_insn (gen_movqi (gen_rtx_MEM (QImode, plus_constant (out, offset)),
-                           reg));
-    }
+static bool
+arm_promote_prototypes (const_tree t ATTRIBUTE_UNUSED)
+{
+    return !TARGET_AAPCS_BASED;
 }
 
-void
-thumb_reload_out_hi (rtx *operands)
+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)
 {
-  emit_insn (gen_thumb_movhi_clobber (operands[0], operands[1], operands[2]));
+  if (GET_MODE_CLASS (mode) == MODE_INT
+      && GET_MODE_SIZE (mode) < 4)
+    return SImode;
+
+  return mode;
 }
 
-/* Handle reading a half-word from memory during reload.  */
-void
-thumb_reload_in_hi (rtx *operands ATTRIBUTE_UNUSED)
+/* AAPCS based ABIs use short enums by default.  */
+
+static bool
+arm_default_short_enums (void)
 {
-  gcc_unreachable ();
+  return TARGET_AAPCS_BASED && arm_abi != ARM_ABI_AAPCS_LINUX;
 }
 
-/* Return the length of a function name prefix
-    that starts with the character 'c'.  */
-static int
-arm_get_strip_length (int c)
+
+/* AAPCS requires that anonymous bitfields affect structure alignment.  */
+
+static bool
+arm_align_anon_bitfield (void)
 {
-  switch (c)
-    {
-    ARM_NAME_ENCODING_LENGTHS
-      default: return 0;
-    }
+  return TARGET_AAPCS_BASED;
 }
 
-/* Return a pointer to a function's name with any
-   and all prefix encodings stripped from it.  */
-const char *
-arm_strip_name_encoding (const char *name)
-{
-  int skip;
 
-  while ((skip = arm_get_strip_length (* name)))
-    name += skip;
+/* The generic C++ ABI says 64-bit (long long).  The EABI says 32-bit.  */
 
-  return name;
+static tree
+arm_cxx_guard_type (void)
+{
+  return TARGET_AAPCS_BASED ? integer_type_node : long_long_integer_type_node;
 }
 
-/* If there is a '*' anywhere in the name's prefix, then
-   emit the stripped name verbatim, otherwise prepend an
-   underscore if leading underscores are being used.  */
-void
-arm_asm_output_labelref (FILE *stream, const char *name)
+/* Return non-zero if the consumer (a multiply-accumulate instruction)
+   has an accumulator dependency on the result of the producer (a
+   multiplication instruction) and no other dependency on that result.  */
+int
+arm_mac_accumulator_is_mul_result (rtx producer, rtx consumer)
 {
-  int skip;
-  int verbatim = 0;
+  rtx mul = PATTERN (producer);
+  rtx mac = PATTERN (consumer);
+  rtx mul_result;
+  rtx mac_op0, mac_op1, mac_acc;
 
-  while ((skip = arm_get_strip_length (* name)))
-    {
-      verbatim |= (*name == '*');
-      name += skip;
-    }
+  if (GET_CODE (mul) == COND_EXEC)
+    mul = COND_EXEC_CODE (mul);
+  if (GET_CODE (mac) == COND_EXEC)
+    mac = COND_EXEC_CODE (mac);
 
-  if (verbatim)
-    fputs (name, stream);
-  else
-    asm_fprintf (stream, "%U%s", name);
-}
+  /* Check that mul is of the form (set (...) (mult ...))
+     and mla is of the form (set (...) (plus (mult ...) (...))).  */
+  if ((GET_CODE (mul) != SET || GET_CODE (XEXP (mul, 1)) != MULT)
+      || (GET_CODE (mac) != SET || GET_CODE (XEXP (mac, 1)) != PLUS
+          || GET_CODE (XEXP (XEXP (mac, 1), 0)) != MULT))
+    return 0;
 
-static void
-arm_file_start (void)
-{
-  int val;
+  mul_result = XEXP (mul, 0);
+  mac_op0 = XEXP (XEXP (XEXP (mac, 1), 0), 0);
+  mac_op1 = XEXP (XEXP (XEXP (mac, 1), 0), 1);
+  mac_acc = XEXP (XEXP (mac, 1), 1);
 
-  if (TARGET_UNIFIED_ASM)
-    asm_fprintf (asm_out_file, "\t.syntax unified\n");
+  return (reg_overlap_mentioned_p (mul_result, mac_acc)
+          && !reg_overlap_mentioned_p (mul_result, mac_op0)
+          && !reg_overlap_mentioned_p (mul_result, mac_op1));
+}
 
-  if (TARGET_BPABI)
-    {
-      const char *fpu_name;
-      if (arm_selected_arch)
-       asm_fprintf (asm_out_file, "\t.arch %s\n", arm_selected_arch->name);
-      else
-       asm_fprintf (asm_out_file, "\t.cpu %s\n", arm_selected_cpu->name);
 
-      if (TARGET_SOFT_FLOAT)
-       {
-         if (TARGET_VFP)
-           fpu_name = "softvfp";
-         else
-           fpu_name = "softfpa";
-       }
-      else
-       {
-         fpu_name = arm_fpu_desc->name;
-         if (arm_fpu_desc->model == ARM_FP_MODEL_VFP)
-           {
-             if (TARGET_HARD_FLOAT)
-               asm_fprintf (asm_out_file, "\t.eabi_attribute 27, 3\n");
-             if (TARGET_HARD_FLOAT_ABI)
-               asm_fprintf (asm_out_file, "\t.eabi_attribute 28, 1\n");
-           }
-       }
-      asm_fprintf (asm_out_file, "\t.fpu %s\n", fpu_name);
+/* The EABI says test the least significant bit of a guard variable.  */
 
-      /* Some of these attributes only apply when the corresponding features
-         are used.  However we don't have any easy way of figuring this out.
-        Conservatively record the setting that would have been used.  */
+static bool
+arm_cxx_guard_mask_bit (void)
+{
+  return TARGET_AAPCS_BASED;
+}
 
-      /* Tag_ABI_FP_rounding.  */
-      if (flag_rounding_math)
-       asm_fprintf (asm_out_file, "\t.eabi_attribute 19, 1\n");
-      if (!flag_unsafe_math_optimizations)
-       {
-         /* Tag_ABI_FP_denomal.  */
-         asm_fprintf (asm_out_file, "\t.eabi_attribute 20, 1\n");
-         /* Tag_ABI_FP_exceptions.  */
-         asm_fprintf (asm_out_file, "\t.eabi_attribute 21, 1\n");
-       }
-      /* Tag_ABI_FP_user_exceptions.  */
-      if (flag_signaling_nans)
-       asm_fprintf (asm_out_file, "\t.eabi_attribute 22, 1\n");
-      /* Tag_ABI_FP_number_model.  */
-      asm_fprintf (asm_out_file, "\t.eabi_attribute 23, %d\n", 
-                  flag_finite_math_only ? 1 : 3);
-
-      /* Tag_ABI_align8_needed.  */
-      asm_fprintf (asm_out_file, "\t.eabi_attribute 24, 1\n");
-      /* Tag_ABI_align8_preserved.  */
-      asm_fprintf (asm_out_file, "\t.eabi_attribute 25, 1\n");
-      /* Tag_ABI_enum_size.  */
-      asm_fprintf (asm_out_file, "\t.eabi_attribute 26, %d\n",
-                  flag_short_enums ? 1 : 2);
 
-      /* Tag_ABI_optimization_goals.  */
-      if (optimize_size)
-       val = 4;
-      else if (optimize >= 2)
-       val = 2;
-      else if (optimize)
-       val = 1;
-      else
-       val = 6;
-      asm_fprintf (asm_out_file, "\t.eabi_attribute 30, %d\n", val);
+/* The EABI specifies that all array cookies are 8 bytes long.  */
 
-      /* Tag_ABI_FP_16bit_format.  */
-      if (arm_fp16_format)
-       asm_fprintf (asm_out_file, "\t.eabi_attribute 38, %d\n",
-                    (int)arm_fp16_format);
+static tree
+arm_get_cookie_size (tree type)
+{
+  tree size;
 
-      if (arm_lang_output_object_attributes_hook)
-       arm_lang_output_object_attributes_hook();
-    }
-  default_file_start();
+  if (!TARGET_AAPCS_BASED)
+    return default_cxx_get_cookie_size (type);
+
+  size = build_int_cst (sizetype, 8);
+  return size;
 }
 
-static void
-arm_file_end (void)
-{
-  int regno;
 
-  if (NEED_INDICATE_EXEC_STACK)
-    /* Add .note.GNU-stack.  */
-    file_end_indicate_exec_stack ();
+/* The EABI says that array cookies should also contain the element size.  */
 
-  if (! thumb_call_reg_needed)
-    return;
+static bool
+arm_cookie_has_size (void)
+{
+  return TARGET_AAPCS_BASED;
+}
 
-  switch_to_section (text_section);
-  asm_fprintf (asm_out_file, "\t.code 16\n");
-  ASM_OUTPUT_ALIGN (asm_out_file, 1);
 
-  for (regno = 0; regno < LR_REGNUM; regno++)
-    {
-      rtx label = thumb_call_via_label[regno];
+/* The EABI says constructors and destructors should return a pointer to
+   the object constructed/destroyed.  */
 
-      if (label != 0)
-       {
-         targetm.asm_out.internal_label (asm_out_file, "L",
-                                         CODE_LABEL_NUMBER (label));
-         asm_fprintf (asm_out_file, "\tbx\t%r\n", regno);
-       }
-    }
+static bool
+arm_cxx_cdtor_returns_this (void)
+{
+  return TARGET_AAPCS_BASED;
 }
 
-#ifndef ARM_PE
-/* Symbols in the text segment can be accessed without indirecting via the
-   constant pool; it may take an extra binary operation, but this is still
-   faster than indirecting via memory.  Don't do this when not optimizing,
-   since we won't be calculating al of the offsets necessary to do this
-   simplification.  */
+/* The EABI says that an inline function may never be the key
+   method.  */
+
+static bool
+arm_cxx_key_method_may_be_inline (void)
+{
+  return !TARGET_AAPCS_BASED;
+}
 
 static void
-arm_encode_section_info (tree decl, rtx rtl, int first)
+arm_cxx_determine_class_data_visibility (tree decl)
 {
-  if (optimize > 0 && TREE_CONSTANT (decl))
-    SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
+  if (!TARGET_AAPCS_BASED
+      || !TARGET_DLLIMPORT_DECL_ATTRIBUTES)
+    return;
 
-  default_encode_section_info (decl, rtl, first);
+  /* In general, \S 3.2.5.5 of the ARM EABI requires that class data
+     is exported.  However, on systems without dynamic vague linkage,
+     \S 3.2.5.6 says that COMDAT class data has hidden linkage.  */
+  if (!TARGET_ARM_DYNAMIC_VAGUE_LINKAGE_P && DECL_COMDAT (decl))
+    DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN;
+  else
+    DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
+  DECL_VISIBILITY_SPECIFIED (decl) = 1;
 }
-#endif /* !ARM_PE */
 
-static void
-arm_internal_label (FILE *stream, const char *prefix, unsigned long labelno)
+static bool
+arm_cxx_class_data_always_comdat (void)
 {
-  if (arm_ccfsm_state == 3 && (unsigned) arm_target_label == labelno
-      && !strcmp (prefix, "L"))
-    {
-      arm_ccfsm_state = 0;
-      arm_target_insn = NULL;
-    }
-  default_internal_label (stream, prefix, labelno);
+  /* \S 3.2.5.4 of the ARM C++ ABI says that class data only have
+     vague linkage if the class has no key function.  */
+  return !TARGET_AAPCS_BASED;
 }
 
-/* Output code to add DELTA to the first argument, and then jump
-   to FUNCTION.  Used for C++ multiple inheritance.  */
-static void
-arm_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
-                    HOST_WIDE_INT delta,
-                    HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
-                    tree function)
+
+/* The EABI says __aeabi_atexit should be used to register static
+   destructors.  */
+
+static bool
+arm_cxx_use_aeabi_atexit (void)
 {
-  static int thunk_label = 0;
-  char label[256];
-  char labelpc[256];
-  int mi_delta = delta;
-  const char *const mi_op = mi_delta < 0 ? "sub" : "add";
-  int shift = 0;
-  int this_regno = (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function)
-                    ? 1 : 0);
-  if (mi_delta < 0)
-    mi_delta = - mi_delta;
+  return TARGET_AAPCS_BASED;
+}
 
-  if (TARGET_THUMB1)
+
+void
+arm_set_return_address (rtx source, rtx scratch)
+{
+  arm_stack_offsets *offsets;
+  HOST_WIDE_INT delta;
+  rtx addr;
+  unsigned long saved_regs;
+
+  offsets = arm_get_frame_offsets ();
+  saved_regs = offsets->saved_regs_mask;
+
+  if ((saved_regs & (1 << LR_REGNUM)) == 0)
+    emit_move_insn (gen_rtx_REG (Pmode, LR_REGNUM), source);
+  else
     {
-      int labelno = thunk_label++;
-      ASM_GENERATE_INTERNAL_LABEL (label, "LTHUMBFUNC", labelno);
-      /* Thunks are entered in arm mode when avaiable.  */
-      if (TARGET_THUMB1_ONLY)
-       {
-         /* push r3 so we can use it as a temporary.  */
-         /* TODO: Omit this save if r3 is not used.  */
-         fputs ("\tpush {r3}\n", file);
-         fputs ("\tldr\tr3, ", file);
-       }
+      if (frame_pointer_needed)
+       addr = plus_constant(hard_frame_pointer_rtx, -4);
       else
        {
-         fputs ("\tldr\tr12, ", file);
-       }
-      assemble_name (file, label);
-      fputc ('\n', file);
-      if (flag_pic)
-       {
-         /* If we are generating PIC, the ldr instruction below loads
-            "(target - 7) - .LTHUNKPCn" into r12.  The pc reads as
-            the address of the add + 8, so we have:
+         /* LR will be the first saved register.  */
+         delta = offsets->outgoing_args - (offsets->frame + 4);
 
-            r12 = (target - 7) - .LTHUNKPCn + (.LTHUNKPCn + 8)
-                = target + 1.
 
-            Note that we have "+ 1" because some versions of GNU ld
-            don't set the low bit of the result for R_ARM_REL32
-            relocations against thumb function symbols.
-            On ARMv6M this is +4, not +8.  */
-         ASM_GENERATE_INTERNAL_LABEL (labelpc, "LTHUNKPC", labelno);
-         assemble_name (file, labelpc);
-         fputs (":\n", file);
-         if (TARGET_THUMB1_ONLY)
+         if (delta >= 4096)
            {
-             /* This is 2 insns after the start of the thunk, so we know it
-                is 4-byte aligned.  */
-             fputs ("\tadd\tr3, pc, r3\n", file);
-             fputs ("\tmov r12, r3\n", file);
+             emit_insn (gen_addsi3 (scratch, stack_pointer_rtx,
+                                    GEN_INT (delta & ~4095)));
+             addr = scratch;
+             delta &= 4095;
            }
          else
-           fputs ("\tadd\tr12, pc, r12\n", file);
-       }
-      else if (TARGET_THUMB1_ONLY)
-       fputs ("\tmov r12, r3\n", file);
-    }
-  if (TARGET_THUMB1_ONLY)
-    {
-      if (mi_delta > 255)
-       {
-         fputs ("\tldr\tr3, ", file);
-         assemble_name (file, label);
-         fputs ("+4\n", file);
-         asm_fprintf (file, "\t%s\t%r, %r, r3\n",
-                      mi_op, this_regno, this_regno);
-       }
-      else if (mi_delta != 0)
-       {
-         asm_fprintf (file, "\t%s\t%r, %r, #%d\n",
-                      mi_op, this_regno, this_regno,
-                      mi_delta);
+           addr = stack_pointer_rtx;
+
+         addr = plus_constant (addr, delta);
        }
+      emit_move_insn (gen_frame_mem (Pmode, addr), source);
     }
-  else
+}
+
+
+void
+thumb_set_return_address (rtx source, rtx scratch)
+{
+  arm_stack_offsets *offsets;
+  HOST_WIDE_INT delta;
+  HOST_WIDE_INT limit;
+  int reg;
+  rtx addr;
+  unsigned long mask;
+
+  emit_use (source);
+
+  offsets = arm_get_frame_offsets ();
+  mask = offsets->saved_regs_mask;
+  if (mask & (1 << LR_REGNUM))
     {
-      /* TODO: Use movw/movt for large constants when available.  */
-      while (mi_delta != 0)
+      limit = 1024;
+      /* Find the saved regs.  */
+      if (frame_pointer_needed)
        {
-         if ((mi_delta & (3 << shift)) == 0)
-           shift += 2;
-         else
-           {
-             asm_fprintf (file, "\t%s\t%r, %r, #%d\n",
-                          mi_op, this_regno, this_regno,
-                          mi_delta & (0xff << shift));
-             mi_delta &= ~(0xff << shift);
-             shift += 8;
-           }
+         delta = offsets->soft_frame - offsets->saved_args;
+         reg = THUMB_HARD_FRAME_POINTER_REGNUM;
+         if (TARGET_THUMB1)
+           limit = 128;
        }
-    }
-  if (TARGET_THUMB1)
-    {
-      if (TARGET_THUMB1_ONLY)
-       fputs ("\tpop\t{r3}\n", file);
+      else
+       {
+         delta = offsets->outgoing_args - offsets->saved_args;
+         reg = SP_REGNUM;
+       }
+      /* Allow for the stack frame.  */
+      if (TARGET_THUMB1 && TARGET_BACKTRACE)
+       delta -= 16;
+      /* The link register is always the first saved register.  */
+      delta -= 4;
 
-      fprintf (file, "\tbx\tr12\n");
-      ASM_OUTPUT_ALIGN (file, 2);
-      assemble_name (file, label);
-      fputs (":\n", file);
-      if (flag_pic)
+      /* Construct the address.  */
+      addr = gen_rtx_REG (SImode, reg);
+      if (delta > limit)
        {
-         /* Output ".word .LTHUNKn-7-.LTHUNKPCn".  */
-         rtx tem = XEXP (DECL_RTL (function), 0);
-         tem = gen_rtx_PLUS (GET_MODE (tem), tem, GEN_INT (-7));
-         tem = gen_rtx_MINUS (GET_MODE (tem),
-                              tem,
-                              gen_rtx_SYMBOL_REF (Pmode,
-                                                  ggc_strdup (labelpc)));
-         assemble_integer (tem, 4, BITS_PER_WORD, 1);
+         emit_insn (gen_movsi (scratch, GEN_INT (delta)));
+         emit_insn (gen_addsi3 (scratch, scratch, stack_pointer_rtx));
+         addr = scratch;
        }
       else
-       /* Output ".word .LTHUNKn".  */
-       assemble_integer (XEXP (DECL_RTL (function), 0), 4, BITS_PER_WORD, 1);
+       addr = plus_constant (addr, delta);
 
-      if (TARGET_THUMB1_ONLY && mi_delta > 255)
-       assemble_integer (GEN_INT(mi_delta), 4, BITS_PER_WORD, 1);
+      emit_move_insn (gen_frame_mem (Pmode, addr), source);
     }
   else
-    {
-      fputs ("\tb\t", file);
-      assemble_name (file, XSTR (XEXP (DECL_RTL (function), 0), 0));
-      if (NEED_PLT_RELOC)
-        fputs ("(PLT)", file);
-      fputc ('\n', file);
-    }
+    emit_move_insn (gen_rtx_REG (Pmode, LR_REGNUM), source);
 }
 
-int
-arm_emit_vector_const (FILE *file, rtx x)
+/* Implements target hook vector_mode_supported_p.  */
+bool
+arm_vector_mode_supported_p (enum machine_mode mode)
 {
-  int i;
-  const char * pattern;
-
-  gcc_assert (GET_CODE (x) == CONST_VECTOR);
-
-  switch (GET_MODE (x))
-    {
-    case V2SImode: pattern = "%08x"; break;
-    case V4HImode: pattern = "%04x"; break;
-    case V8QImode: pattern = "%02x"; break;
-    default:       gcc_unreachable ();
-    }
+  /* Neon also supports V2SImode, etc. listed in the clause below.  */
+  if (TARGET_NEON && (mode == V2SFmode || mode == V4SImode || mode == V8HImode
+      || mode == V16QImode || mode == V4SFmode || mode == V2DImode))
+    return true;
 
-  fprintf (file, "0x");
-  for (i = CONST_VECTOR_NUNITS (x); i--;)
-    {
-      rtx element;
+  if ((TARGET_NEON || TARGET_IWMMXT)
+      && ((mode == V2SImode)
+         || (mode == V4HImode)
+         || (mode == V8QImode)))
+    return true;
 
-      element = CONST_VECTOR_ELT (x, i);
-      fprintf (file, pattern, INTVAL (element));
-    }
+  if (TARGET_INT_SIMD && (mode == V4UQQmode || mode == V4QQmode
+      || mode == V2UHQmode || mode == V2HQmode || mode == V2UHAmode
+      || mode == V2HAmode))
+    return true;
 
-  return 1;
+  return false;
 }
 
-/* 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)
+/* Implements target hook array_mode_supported_p.  */
+
+static bool
+arm_array_mode_supported_p (enum machine_mode mode,
+                           unsigned HOST_WIDE_INT nelems)
 {
-  REAL_VALUE_TYPE r;
-  long bits;
+  if (TARGET_NEON
+      && (VALID_NEON_DREG_MODE (mode) || VALID_NEON_QREG_MODE (mode))
+      && (nelems >= 2 && nelems <= 4))
+    return true;
 
-  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);
+  return false;
 }
 
-const char *
-arm_output_load_gr (rtx *operands)
+/* Use the option -mvectorize-with-neon-double to override the use of quardword
+   registers when autovectorizing for Neon, at least until multiple vector
+   widths are supported properly by the middle-end.  */
+
+static enum machine_mode
+arm_preferred_simd_mode (enum machine_mode mode)
 {
-  rtx reg;
-  rtx offset;
-  rtx wcgr;
-  rtx sum;
+  if (TARGET_NEON)
+    switch (mode)
+      {
+      case SFmode:
+       return TARGET_NEON_VECTORIZE_DOUBLE ? V2SFmode : V4SFmode;
+      case SImode:
+       return TARGET_NEON_VECTORIZE_DOUBLE ? V2SImode : V4SImode;
+      case HImode:
+       return TARGET_NEON_VECTORIZE_DOUBLE ? V4HImode : V8HImode;
+      case QImode:
+       return TARGET_NEON_VECTORIZE_DOUBLE ? V8QImode : V16QImode;
+      case DImode:
+       if (!TARGET_NEON_VECTORIZE_DOUBLE)
+         return V2DImode;
+       break;
 
-  if (GET_CODE (operands [1]) != MEM
-      || GET_CODE (sum = XEXP (operands [1], 0)) != PLUS
-      || GET_CODE (reg = XEXP (sum, 0)) != REG
-      || GET_CODE (offset = XEXP (sum, 1)) != CONST_INT
-      || ((INTVAL (offset) < 1024) && (INTVAL (offset) > -1024)))
-    return "wldrw%?\t%0, %1";
+      default:;
+      }
 
-  /* Fix up an out-of-range load of a GR register.  */
-  output_asm_insn ("str%?\t%0, [sp, #-4]!\t@ Start of GR load expansion", & reg);
-  wcgr = operands[0];
-  operands[0] = reg;
-  output_asm_insn ("ldr%?\t%0, %1", operands);
+  if (TARGET_REALLY_IWMMXT)
+    switch (mode)
+      {
+      case SImode:
+       return V2SImode;
+      case HImode:
+       return V4HImode;
+      case QImode:
+       return V8QImode;
 
-  operands[0] = wcgr;
-  operands[1] = reg;
-  output_asm_insn ("tmcr%?\t%0, %1", operands);
-  output_asm_insn ("ldr%?\t%0, [sp], #4\t@ End of GR load expansion", & reg);
+      default:;
+      }
 
-  return "";
+  return word_mode;
 }
 
-/* Worker function for TARGET_SETUP_INCOMING_VARARGS.
-
-   On the ARM, PRETEND_SIZE is set in order to have the prologue push the last
-   named arg and all anonymous args onto the stack.
-   XXX I know the prologue shouldn't be pushing registers, but it is faster
-   that way.  */
+/* Implement TARGET_CLASS_LIKELY_SPILLED_P.
 
-static void
-arm_setup_incoming_varargs (CUMULATIVE_ARGS *pcum,
-                           enum machine_mode mode,
-                           tree type,
-                           int *pretend_size,
-                           int second_time ATTRIBUTE_UNUSED)
+   We need to define this for LO_REGS on Thumb-1.  Otherwise we can end up
+   using r0-r4 for function arguments, r7 for the stack frame and don't have
+   enough left over to do doubleword arithmetic.  For Thumb-2 all the
+   potentially problematic instructions accept high registers so this is not
+   necessary.  Care needs to be taken to avoid adding new Thumb-2 patterns
+   that require many low registers.  */
+static bool
+arm_class_likely_spilled_p (reg_class_t rclass)
 {
-  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;
-}
+  if ((TARGET_THUMB1 && rclass == LO_REGS)
+      || rclass  == CC_REG)
+    return true;
 
-/* Return nonzero if the CONSUMER instruction (a store) does not need
-   PRODUCER's value to calculate the address.  */
+  return false;
+}
 
-int
-arm_no_early_store_addr_dep (rtx producer, rtx consumer)
+/* Implements target hook small_register_classes_for_mode_p.  */
+bool
+arm_small_register_classes_for_mode_p (enum machine_mode mode ATTRIBUTE_UNUSED)
 {
-  rtx value = PATTERN (producer);
-  rtx addr = PATTERN (consumer);
-
-  if (GET_CODE (value) == COND_EXEC)
-    value = COND_EXEC_CODE (value);
-  if (GET_CODE (value) == PARALLEL)
-    value = XVECEXP (value, 0, 0);
-  value = XEXP (value, 0);
-  if (GET_CODE (addr) == COND_EXEC)
-    addr = COND_EXEC_CODE (addr);
-  if (GET_CODE (addr) == PARALLEL)
-    addr = XVECEXP (addr, 0, 0);
-  addr = XEXP (addr, 0);
-
-  return !reg_overlap_mentioned_p (value, addr);
+  return TARGET_THUMB1;
 }
 
-/* Return nonzero if the CONSUMER instruction (a store) does need
-   PRODUCER's value to calculate the address.  */
+/* Implement TARGET_SHIFT_TRUNCATION_MASK.  SImode shifts use normal
+   ARM insns and therefore guarantee that the shift count is modulo 256.
+   DImode shifts (those implemented by lib1funcs.S or by optabs.c)
+   guarantee no particular behavior for out-of-range counts.  */
 
-int
-arm_early_store_addr_dep (rtx producer, rtx consumer)
+static unsigned HOST_WIDE_INT
+arm_shift_truncation_mask (enum machine_mode mode)
 {
-  return !arm_no_early_store_addr_dep (producer, consumer);
+  return mode == SImode ? 255 : 0;
 }
 
-/* Return nonzero if the CONSUMER instruction (a load) does need
-   PRODUCER's value to calculate the address.  */
-
-int
-arm_early_load_addr_dep (rtx producer, rtx consumer)
-{
-  rtx value = PATTERN (producer);
-  rtx addr = PATTERN (consumer);
 
-  if (GET_CODE (value) == COND_EXEC)
-    value = COND_EXEC_CODE (value);
-  if (GET_CODE (value) == PARALLEL)
-    value = XVECEXP (value, 0, 0);
-  value = XEXP (value, 0);
-  if (GET_CODE (addr) == COND_EXEC)
-    addr = COND_EXEC_CODE (addr);
-  if (GET_CODE (addr) == PARALLEL)
-    addr = XVECEXP (addr, 0, 0);
-  addr = XEXP (addr, 1);
+/* Map internal gcc register numbers to DWARF2 register numbers.  */
 
-  return reg_overlap_mentioned_p (value, addr);
-}
+unsigned int
+arm_dbx_register_number (unsigned int regno)
+{
+  if (regno < 16)
+    return regno;
 
-/* Return nonzero if the CONSUMER instruction (an ALU op) does not
-   have an early register shift value or amount dependency on the
-   result of PRODUCER.  */
+  /* TODO: Legacy targets output FPA regs as registers 16-23 for backwards
+     compatibility.  The EABI defines them as registers 96-103.  */
+  if (IS_FPA_REGNUM (regno))
+    return (TARGET_AAPCS_BASED ? 96 : 16) + regno - FIRST_FPA_REGNUM;
 
-int
-arm_no_early_alu_shift_dep (rtx producer, rtx consumer)
-{
-  rtx value = PATTERN (producer);
-  rtx op = PATTERN (consumer);
-  rtx early_op;
+  if (IS_VFP_REGNUM (regno))
+    {
+      /* See comment in arm_dwarf_register_span.  */
+      if (VFP_REGNO_OK_FOR_SINGLE (regno))
+       return 64 + regno - FIRST_VFP_REGNUM;
+      else
+       return 256 + (regno - FIRST_VFP_REGNUM) / 2;
+    }
 
-  if (GET_CODE (value) == COND_EXEC)
-    value = COND_EXEC_CODE (value);
-  if (GET_CODE (value) == PARALLEL)
-    value = XVECEXP (value, 0, 0);
-  value = XEXP (value, 0);
-  if (GET_CODE (op) == COND_EXEC)
-    op = COND_EXEC_CODE (op);
-  if (GET_CODE (op) == PARALLEL)
-    op = XVECEXP (op, 0, 0);
-  op = XEXP (op, 1);
+  if (IS_IWMMXT_GR_REGNUM (regno))
+    return 104 + regno - FIRST_IWMMXT_GR_REGNUM;
 
-  early_op = XEXP (op, 0);
-  /* This is either an actual independent shift, or a shift applied to
-     the first operand of another operation.  We want the whole shift
-     operation.  */
-  if (GET_CODE (early_op) == REG)
-    early_op = op;
+  if (IS_IWMMXT_REGNUM (regno))
+    return 112 + regno - FIRST_IWMMXT_REGNUM;
 
-  return !reg_overlap_mentioned_p (value, early_op);
+  gcc_unreachable ();
 }
 
-/* Return nonzero if the CONSUMER instruction (an ALU op) does not
-   have an early register shift value dependency on the result of
-   PRODUCER.  */
-
-int
-arm_no_early_alu_shift_value_dep (rtx producer, rtx consumer)
+/* Dwarf models VFPv3 registers as 32 64-bit registers.
+   GCC models tham as 64 32-bit registers, so we need to describe this to
+   the DWARF generation code.  Other registers can use the default.  */
+static rtx
+arm_dwarf_register_span (rtx rtl)
 {
-  rtx value = PATTERN (producer);
-  rtx op = PATTERN (consumer);
-  rtx early_op;
+  unsigned regno;
+  int nregs;
+  int i;
+  rtx p;
 
-  if (GET_CODE (value) == COND_EXEC)
-    value = COND_EXEC_CODE (value);
-  if (GET_CODE (value) == PARALLEL)
-    value = XVECEXP (value, 0, 0);
-  value = XEXP (value, 0);
-  if (GET_CODE (op) == COND_EXEC)
-    op = COND_EXEC_CODE (op);
-  if (GET_CODE (op) == PARALLEL)
-    op = XVECEXP (op, 0, 0);
-  op = XEXP (op, 1);
+  regno = REGNO (rtl);
+  if (!IS_VFP_REGNUM (regno))
+    return NULL_RTX;
 
-  early_op = XEXP (op, 0);
+  /* XXX FIXME: The EABI defines two VFP register ranges:
+       64-95: Legacy VFPv2 numbering for S0-S31 (obsolescent)
+       256-287: D0-D31
+     The recommended encoding for S0-S31 is a DW_OP_bit_piece of the
+     corresponding D register.  Until GDB supports this, we shall use the
+     legacy encodings.  We also use these encodings for D0-D15 for
+     compatibility with older debuggers.  */
+  if (VFP_REGNO_OK_FOR_SINGLE (regno))
+    return NULL_RTX;
 
-  /* This is either an actual independent shift, or a shift applied to
-     the first operand of another operation.  We want the value being
-     shifted, in either case.  */
-  if (GET_CODE (early_op) != REG)
-    early_op = XEXP (early_op, 0);
+  nregs = GET_MODE_SIZE (GET_MODE (rtl)) / 8;
+  p = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nregs));
+  regno = (regno - FIRST_VFP_REGNUM) / 2;
+  for (i = 0; i < nregs; i++)
+    XVECEXP (p, 0, i) = gen_rtx_REG (DImode, 256 + regno + i);
 
-  return !reg_overlap_mentioned_p (value, early_op);
+  return p;
 }
 
-/* Return nonzero if the CONSUMER (a mul or mac op) does not
-   have an early register mult dependency on the result of
-   PRODUCER.  */
+#if ARM_UNWIND_INFO
+/* Emit unwind directives for a store-multiple instruction or stack pointer
+   push during alignment.
+   These should only ever be generated by the function prologue code, so
+   expect them to have a particular form.  */
 
-int
-arm_no_early_mul_dep (rtx producer, rtx consumer)
+static void
+arm_unwind_emit_sequence (FILE * asm_out_file, rtx p)
 {
-  rtx value = PATTERN (producer);
-  rtx op = PATTERN (consumer);
+  int i;
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT nregs;
+  int reg_size;
+  unsigned reg;
+  unsigned lastreg;
+  rtx e;
 
-  if (GET_CODE (value) == COND_EXEC)
-    value = COND_EXEC_CODE (value);
-  if (GET_CODE (value) == PARALLEL)
-    value = XVECEXP (value, 0, 0);
-  value = XEXP (value, 0);
-  if (GET_CODE (op) == COND_EXEC)
-    op = COND_EXEC_CODE (op);
-  if (GET_CODE (op) == PARALLEL)
-    op = XVECEXP (op, 0, 0);
-  op = XEXP (op, 1);
+  e = XVECEXP (p, 0, 0);
+  if (GET_CODE (e) != SET)
+    abort ();
 
-  if (GET_CODE (op) == PLUS || GET_CODE (op) == MINUS)
+  /* First insn will adjust the stack pointer.  */
+  if (GET_CODE (e) != SET
+      || GET_CODE (XEXP (e, 0)) != REG
+      || REGNO (XEXP (e, 0)) != SP_REGNUM
+      || GET_CODE (XEXP (e, 1)) != PLUS)
+    abort ();
+
+  offset = -INTVAL (XEXP (XEXP (e, 1), 1));
+  nregs = XVECLEN (p, 0) - 1;
+
+  reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1));
+  if (reg < 16)
     {
-      if (GET_CODE (XEXP (op, 0)) == MULT)
-       return !reg_overlap_mentioned_p (value, XEXP (op, 0));
-      else
-       return !reg_overlap_mentioned_p (value, XEXP (op, 1));
+      /* The function prologue may also push pc, but not annotate it as it is
+        never restored.  We turn this into a stack pointer adjustment.  */
+      if (nregs * 4 == offset - 4)
+       {
+         fprintf (asm_out_file, "\t.pad #4\n");
+         offset -= 4;
+       }
+      reg_size = 4;
+      fprintf (asm_out_file, "\t.save {");
+    }
+  else if (IS_VFP_REGNUM (reg))
+    {
+      reg_size = 8;
+      fprintf (asm_out_file, "\t.vsave {");
+    }
+  else if (reg >= FIRST_FPA_REGNUM && reg <= LAST_FPA_REGNUM)
+    {
+      /* FPA registers are done differently.  */
+      asm_fprintf (asm_out_file, "\t.save %r, %wd\n", reg, nregs);
+      return;
     }
+  else
+    /* Unknown register type.  */
+    abort ();
 
-  return 0;
-}
+  /* If the stack increment doesn't match the size of the saved registers,
+     something has gone horribly wrong.  */
+  if (offset != nregs * reg_size)
+    abort ();
 
-/* We can't rely on the caller doing the proper promotion when
-   using APCS or ATPCS.  */
+  offset = 0;
+  lastreg = 0;
+  /* The remaining insns will describe the stores.  */
+  for (i = 1; i <= nregs; i++)
+    {
+      /* Expect (set (mem <addr>) (reg)).
+         Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)).  */
+      e = XVECEXP (p, 0, i);
+      if (GET_CODE (e) != SET
+         || GET_CODE (XEXP (e, 0)) != MEM
+         || GET_CODE (XEXP (e, 1)) != REG)
+       abort ();
 
-static bool
-arm_promote_prototypes (const_tree t ATTRIBUTE_UNUSED)
-{
-    return !TARGET_AAPCS_BASED;
+      reg = REGNO (XEXP (e, 1));
+      if (reg < lastreg)
+       abort ();
+
+      if (i != 1)
+       fprintf (asm_out_file, ", ");
+      /* We can't use %r for vfp because we need to use the
+        double precision register names.  */
+      if (IS_VFP_REGNUM (reg))
+       asm_fprintf (asm_out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
+      else
+       asm_fprintf (asm_out_file, "%r", reg);
+
+#ifdef ENABLE_CHECKING
+      /* Check that the addresses are consecutive.  */
+      e = XEXP (XEXP (e, 0), 0);
+      if (GET_CODE (e) == PLUS)
+       {
+         offset += reg_size;
+         if (GET_CODE (XEXP (e, 0)) != REG
+             || REGNO (XEXP (e, 0)) != SP_REGNUM
+             || GET_CODE (XEXP (e, 1)) != CONST_INT
+             || offset != INTVAL (XEXP (e, 1)))
+           abort ();
+       }
+      else if (i != 1
+              || GET_CODE (e) != REG
+              || REGNO (e) != SP_REGNUM)
+       abort ();
+#endif
+    }
+  fprintf (asm_out_file, "}\n");
 }
 
-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)
+/*  Emit unwind directives for a SET.  */
+
+static void
+arm_unwind_emit_set (FILE * asm_out_file, rtx p)
 {
-  if (GET_MODE_CLASS (mode) == MODE_INT
-      && GET_MODE_SIZE (mode) < 4)
-    return SImode;
+  rtx e0;
+  rtx e1;
+  unsigned reg;
 
-  return mode;
-}
+  e0 = XEXP (p, 0);
+  e1 = XEXP (p, 1);
+  switch (GET_CODE (e0))
+    {
+    case MEM:
+      /* Pushing a single register.  */
+      if (GET_CODE (XEXP (e0, 0)) != PRE_DEC
+         || GET_CODE (XEXP (XEXP (e0, 0), 0)) != REG
+         || REGNO (XEXP (XEXP (e0, 0), 0)) != SP_REGNUM)
+       abort ();
 
-/* AAPCS based ABIs use short enums by default.  */
+      asm_fprintf (asm_out_file, "\t.save ");
+      if (IS_VFP_REGNUM (REGNO (e1)))
+       asm_fprintf(asm_out_file, "{d%d}\n",
+                   (REGNO (e1) - FIRST_VFP_REGNUM) / 2);
+      else
+       asm_fprintf(asm_out_file, "{%r}\n", REGNO (e1));
+      break;
 
-static bool
-arm_default_short_enums (void)
-{
-  return TARGET_AAPCS_BASED && arm_abi != ARM_ABI_AAPCS_LINUX;
-}
+    case REG:
+      if (REGNO (e0) == SP_REGNUM)
+       {
+         /* A stack increment.  */
+         if (GET_CODE (e1) != PLUS
+             || GET_CODE (XEXP (e1, 0)) != REG
+             || REGNO (XEXP (e1, 0)) != SP_REGNUM
+             || GET_CODE (XEXP (e1, 1)) != CONST_INT)
+           abort ();
 
+         asm_fprintf (asm_out_file, "\t.pad #%wd\n",
+                      -INTVAL (XEXP (e1, 1)));
+       }
+      else if (REGNO (e0) == HARD_FRAME_POINTER_REGNUM)
+       {
+         HOST_WIDE_INT offset;
 
-/* AAPCS requires that anonymous bitfields affect structure alignment.  */
+         if (GET_CODE (e1) == PLUS)
+           {
+             if (GET_CODE (XEXP (e1, 0)) != REG
+                 || GET_CODE (XEXP (e1, 1)) != CONST_INT)
+               abort ();
+             reg = REGNO (XEXP (e1, 0));
+             offset = INTVAL (XEXP (e1, 1));
+             asm_fprintf (asm_out_file, "\t.setfp %r, %r, #%wd\n",
+                          HARD_FRAME_POINTER_REGNUM, reg,
+                          offset);
+           }
+         else if (GET_CODE (e1) == REG)
+           {
+             reg = REGNO (e1);
+             asm_fprintf (asm_out_file, "\t.setfp %r, %r\n",
+                          HARD_FRAME_POINTER_REGNUM, reg);
+           }
+         else
+           abort ();
+       }
+      else if (GET_CODE (e1) == REG && REGNO (e1) == SP_REGNUM)
+       {
+         /* Move from sp to reg.  */
+         asm_fprintf (asm_out_file, "\t.movsp %r\n", REGNO (e0));
+       }
+     else if (GET_CODE (e1) == PLUS
+             && GET_CODE (XEXP (e1, 0)) == REG
+             && REGNO (XEXP (e1, 0)) == SP_REGNUM
+             && GET_CODE (XEXP (e1, 1)) == CONST_INT)
+       {
+         /* Set reg to offset from sp.  */
+         asm_fprintf (asm_out_file, "\t.movsp %r, #%d\n",
+                      REGNO (e0), (int)INTVAL(XEXP (e1, 1)));
+       }
+      else
+       abort ();
+      break;
 
-static bool
-arm_align_anon_bitfield (void)
-{
-  return TARGET_AAPCS_BASED;
+    default:
+      abort ();
+    }
 }
 
 
-/* The generic C++ ABI says 64-bit (long long).  The EABI says 32-bit.  */
+/* Emit unwind directives for the given insn.  */
 
-static tree
-arm_cxx_guard_type (void)
+static void
+arm_unwind_emit (FILE * asm_out_file, rtx insn)
 {
-  return TARGET_AAPCS_BASED ? integer_type_node : long_long_integer_type_node;
-}
+  rtx note, pat;
+  bool handled_one = false;
 
-/* Return non-zero if the consumer (a multiply-accumulate instruction)
-   has an accumulator dependency on the result of the producer (a
-   multiplication instruction) and no other dependency on that result.  */
-int
-arm_mac_accumulator_is_mul_result (rtx producer, rtx consumer)
-{
-  rtx mul = PATTERN (producer);
-  rtx mac = PATTERN (consumer);
-  rtx mul_result;
-  rtx mac_op0, mac_op1, mac_acc;
+  if (arm_except_unwind_info (&global_options) != UI_TARGET)
+    return;
 
-  if (GET_CODE (mul) == COND_EXEC)
-    mul = COND_EXEC_CODE (mul);
-  if (GET_CODE (mac) == COND_EXEC)
-    mac = COND_EXEC_CODE (mac);
+  if (!(flag_unwind_tables || crtl->uses_eh_lsda)
+      && (TREE_NOTHROW (current_function_decl)
+         || crtl->all_throwers_are_sibcalls))
+    return;
 
-  /* Check that mul is of the form (set (...) (mult ...))
-     and mla is of the form (set (...) (plus (mult ...) (...))).  */
-  if ((GET_CODE (mul) != SET || GET_CODE (XEXP (mul, 1)) != MULT)
-      || (GET_CODE (mac) != SET || GET_CODE (XEXP (mac, 1)) != PLUS
-          || GET_CODE (XEXP (XEXP (mac, 1), 0)) != MULT))
-    return 0;
+  if (NOTE_P (insn) || !RTX_FRAME_RELATED_P (insn))
+    return;
 
-  mul_result = XEXP (mul, 0);
-  mac_op0 = XEXP (XEXP (XEXP (mac, 1), 0), 0);
-  mac_op1 = XEXP (XEXP (XEXP (mac, 1), 0), 1);
-  mac_acc = XEXP (XEXP (mac, 1), 1);
+  for (note = REG_NOTES (insn); note ; note = XEXP (note, 1))
+    {
+      pat = XEXP (note, 0);
+      switch (REG_NOTE_KIND (note))
+       {
+       case REG_FRAME_RELATED_EXPR:
+         goto found;
 
-  return (reg_overlap_mentioned_p (mul_result, mac_acc)
-          && !reg_overlap_mentioned_p (mul_result, mac_op0)
-          && !reg_overlap_mentioned_p (mul_result, mac_op1));
-}
+       case REG_CFA_REGISTER:
+         if (pat == NULL)
+           {
+             pat = PATTERN (insn);
+             if (GET_CODE (pat) == PARALLEL)
+               pat = XVECEXP (pat, 0, 0);
+           }
 
+         /* Only emitted for IS_STACKALIGN re-alignment.  */
+         {
+           rtx dest, src;
+           unsigned reg;
 
-/* The EABI says test the least significant bit of a guard variable.  */
+           src = SET_SRC (pat);
+           dest = SET_DEST (pat);
 
-static bool
-arm_cxx_guard_mask_bit (void)
-{
-  return TARGET_AAPCS_BASED;
-}
+           gcc_assert (src == stack_pointer_rtx);
+           reg = REGNO (dest);
+           asm_fprintf (asm_out_file, "\t.unwind_raw 0, 0x%x @ vsp = r%d\n",
+                        reg + 0x90, reg);
+         }
+         handled_one = true;
+         break;
 
+       case REG_CFA_DEF_CFA:
+       case REG_CFA_EXPRESSION:
+       case REG_CFA_ADJUST_CFA:
+       case REG_CFA_OFFSET:
+         /* ??? Only handling here what we actually emit.  */
+         gcc_unreachable ();
 
-/* The EABI specifies that all array cookies are 8 bytes long.  */
+       default:
+         break;
+       }
+    }
+  if (handled_one)
+    return;
+  pat = PATTERN (insn);
+ found:
 
-static tree
-arm_get_cookie_size (tree type)
-{
-  tree size;
+  switch (GET_CODE (pat))
+    {
+    case SET:
+      arm_unwind_emit_set (asm_out_file, pat);
+      break;
 
-  if (!TARGET_AAPCS_BASED)
-    return default_cxx_get_cookie_size (type);
+    case SEQUENCE:
+      /* Store multiple.  */
+      arm_unwind_emit_sequence (asm_out_file, pat);
+      break;
 
-  size = build_int_cst (sizetype, 8);
-  return size;
+    default:
+      abort();
+    }
 }
 
 
-/* The EABI says that array cookies should also contain the element size.  */
+/* Output a reference from a function exception table to the type_info
+   object X.  The EABI specifies that the symbol should be relocated by
+   an R_ARM_TARGET2 relocation.  */
 
 static bool
-arm_cookie_has_size (void)
+arm_output_ttype (rtx x)
 {
-  return TARGET_AAPCS_BASED;
-}
-
-
-/* The EABI says constructors and destructors should return a pointer to
-   the object constructed/destroyed.  */
+  fputs ("\t.word\t", asm_out_file);
+  output_addr_const (asm_out_file, x);
+  /* Use special relocations for symbol references.  */
+  if (GET_CODE (x) != CONST_INT)
+    fputs ("(TARGET2)", asm_out_file);
+  fputc ('\n', asm_out_file);
 
-static bool
-arm_cxx_cdtor_returns_this (void)
-{
-  return TARGET_AAPCS_BASED;
+  return TRUE;
 }
 
-/* The EABI says that an inline function may never be the key
-   method.  */
-
-static bool
-arm_cxx_key_method_may_be_inline (void)
-{
-  return !TARGET_AAPCS_BASED;
-}
+/* Implement TARGET_ASM_EMIT_EXCEPT_PERSONALITY.  */
 
 static void
-arm_cxx_determine_class_data_visibility (tree decl)
-{
-  if (!TARGET_AAPCS_BASED
-      || !TARGET_DLLIMPORT_DECL_ATTRIBUTES)
-    return;
-
-  /* In general, \S 3.2.5.5 of the ARM EABI requires that class data
-     is exported.  However, on systems without dynamic vague linkage,
-     \S 3.2.5.6 says that COMDAT class data has hidden linkage.  */
-  if (!TARGET_ARM_DYNAMIC_VAGUE_LINKAGE_P && DECL_COMDAT (decl))
-    DECL_VISIBILITY (decl) = VISIBILITY_HIDDEN;
-  else
-    DECL_VISIBILITY (decl) = VISIBILITY_DEFAULT;
-  DECL_VISIBILITY_SPECIFIED (decl) = 1;
-}
-
-static bool
-arm_cxx_class_data_always_comdat (void)
-{
-  /* \S 3.2.5.4 of the ARM C++ ABI says that class data only have
-     vague linkage if the class has no key function.  */
-  return !TARGET_AAPCS_BASED;
-}
-
-
-/* The EABI says __aeabi_atexit should be used to register static
-   destructors.  */
-
-static bool
-arm_cxx_use_aeabi_atexit (void)
+arm_asm_emit_except_personality (rtx personality)
 {
-  return TARGET_AAPCS_BASED;
+  fputs ("\t.personality\t", asm_out_file);
+  output_addr_const (asm_out_file, personality);
+  fputc ('\n', asm_out_file);
 }
 
+/* Implement TARGET_ASM_INITIALIZE_SECTIONS.  */
 
-void
-arm_set_return_address (rtx source, rtx scratch)
+static void
+arm_asm_init_sections (void)
 {
-  arm_stack_offsets *offsets;
-  HOST_WIDE_INT delta;
-  rtx addr;
-  unsigned long saved_regs;
-
-  offsets = arm_get_frame_offsets ();
-  saved_regs = offsets->saved_regs_mask;
-
-  if ((saved_regs & (1 << LR_REGNUM)) == 0)
-    emit_move_insn (gen_rtx_REG (Pmode, LR_REGNUM), source);
-  else
-    {
-      if (frame_pointer_needed)
-       addr = plus_constant(hard_frame_pointer_rtx, -4);
-      else
-       {
-         /* LR will be the first saved register.  */
-         delta = offsets->outgoing_args - (offsets->frame + 4);
-
-
-         if (delta >= 4096)
-           {
-             emit_insn (gen_addsi3 (scratch, stack_pointer_rtx,
-                                    GEN_INT (delta & ~4095)));
-             addr = scratch;
-             delta &= 4095;
-           }
-         else
-           addr = stack_pointer_rtx;
-
-         addr = plus_constant (addr, delta);
-       }
-      emit_move_insn (gen_frame_mem (Pmode, addr), source);
-    }
+  exception_section = get_unnamed_section (0, output_section_asm_op,
+                                          "\t.handlerdata");
 }
+#endif /* ARM_UNWIND_INFO */
 
+/* Output unwind directives for the start/end of a function.  */
 
 void
-thumb_set_return_address (rtx source, rtx scratch)
+arm_output_fn_unwind (FILE * f, bool prologue)
 {
-  arm_stack_offsets *offsets;
-  HOST_WIDE_INT delta;
-  HOST_WIDE_INT limit;
-  int reg;
-  rtx addr;
-  unsigned long mask;
-
-  emit_use (source);
+  if (arm_except_unwind_info (&global_options) != UI_TARGET)
+    return;
 
-  offsets = arm_get_frame_offsets ();
-  mask = offsets->saved_regs_mask;
-  if (mask & (1 << LR_REGNUM))
+  if (prologue)
+    fputs ("\t.fnstart\n", f);
+  else
     {
-      limit = 1024;
-      /* Find the saved regs.  */
-      if (frame_pointer_needed)
-       {
-         delta = offsets->soft_frame - offsets->saved_args;
-         reg = THUMB_HARD_FRAME_POINTER_REGNUM;
-         if (TARGET_THUMB1)
-           limit = 128;
-       }
-      else
-       {
-         delta = offsets->outgoing_args - offsets->saved_args;
-         reg = SP_REGNUM;
-       }
-      /* Allow for the stack frame.  */
-      if (TARGET_THUMB1 && TARGET_BACKTRACE)
-       delta -= 16;
-      /* The link register is always the first saved register.  */
-      delta -= 4;
-
-      /* Construct the address.  */
-      addr = gen_rtx_REG (SImode, reg);
-      if (delta > limit)
-       {
-         emit_insn (gen_movsi (scratch, GEN_INT (delta)));
-         emit_insn (gen_addsi3 (scratch, scratch, stack_pointer_rtx));
-         addr = scratch;
-       }
-      else
-       addr = plus_constant (addr, delta);
+      /* If this function will never be unwound, then mark it as such.
+         The came condition is used in arm_unwind_emit to suppress
+        the frame annotations.  */
+      if (!(flag_unwind_tables || crtl->uses_eh_lsda)
+         && (TREE_NOTHROW (current_function_decl)
+             || crtl->all_throwers_are_sibcalls))
+       fputs("\t.cantunwind\n", f);
 
-      emit_move_insn (gen_frame_mem (Pmode, addr), source);
+      fputs ("\t.fnend\n", f);
     }
-  else
-    emit_move_insn (gen_rtx_REG (Pmode, LR_REGNUM), source);
 }
 
-/* Implements target hook vector_mode_supported_p.  */
-bool
-arm_vector_mode_supported_p (enum machine_mode mode)
+static bool
+arm_emit_tls_decoration (FILE *fp, rtx x)
 {
-  /* Neon also supports V2SImode, etc. listed in the clause below.  */
-  if (TARGET_NEON && (mode == V2SFmode || mode == V4SImode || mode == V8HImode
-      || mode == V16QImode || mode == V4SFmode || mode == V2DImode))
-    return true;
-
-  if ((TARGET_NEON || TARGET_IWMMXT)
-      && ((mode == V2SImode)
-         || (mode == V4HImode)
-         || (mode == V8QImode)))
-    return true;
+  enum tls_reloc reloc;
+  rtx val;
 
-  return false;
-}
+  val = XVECEXP (x, 0, 0);
+  reloc = (enum tls_reloc) INTVAL (XVECEXP (x, 0, 1));
 
-/* Use the option -mvectorize-with-neon-quad to override the use of doubleword
-   registers when autovectorizing for Neon, at least until multiple vector
-   widths are supported properly by the middle-end.  */
+  output_addr_const (fp, val);
 
-static enum machine_mode
-arm_preferred_simd_mode (enum machine_mode mode)
-{
-  if (TARGET_NEON)
-    switch (mode)
-      {
-      case SFmode:
-       return TARGET_NEON_VECTORIZE_QUAD ? V4SFmode : V2SFmode;
-      case SImode:
-       return TARGET_NEON_VECTORIZE_QUAD ? V4SImode : V2SImode;
-      case HImode:
-       return TARGET_NEON_VECTORIZE_QUAD ? V8HImode : V4HImode;
-      case QImode:
-       return TARGET_NEON_VECTORIZE_QUAD ? V16QImode : V8QImode;
-      case DImode:
-       if (TARGET_NEON_VECTORIZE_QUAD)
-         return V2DImode;
-       break;
+  switch (reloc)
+    {
+    case TLS_GD32:
+      fputs ("(tlsgd)", fp);
+      break;
+    case TLS_LDM32:
+      fputs ("(tlsldm)", fp);
+      break;
+    case TLS_LDO32:
+      fputs ("(tlsldo)", fp);
+      break;
+    case TLS_IE32:
+      fputs ("(gottpoff)", fp);
+      break;
+    case TLS_LE32:
+      fputs ("(tpoff)", fp);
+      break;
+    case TLS_DESCSEQ:
+      fputs ("(tlsdesc)", fp);
+      break;
+    default:
+      gcc_unreachable ();
+    }
 
-      default:;
-      }
+  switch (reloc)
+    {
+    case TLS_GD32:
+    case TLS_LDM32:
+    case TLS_IE32:
+    case TLS_DESCSEQ:
+      fputs (" + (. - ", fp);
+      output_addr_const (fp, XVECEXP (x, 0, 2));
+      /* For DESCSEQ the 3rd operand encodes thumbness, and is added */
+      fputs (reloc == TLS_DESCSEQ ? " + " : " - ", fp);
+      output_addr_const (fp, XVECEXP (x, 0, 3));
+      fputc (')', fp);
+      break;
+    default:
+      break;
+    }
 
-  if (TARGET_REALLY_IWMMXT)
-    switch (mode)
-      {
-      case SImode:
-       return V2SImode;
-      case HImode:
-       return V4HImode;
-      case QImode:
-       return V8QImode;
+  return TRUE;
+}
 
-      default:;
-      }
+/* ARM implementation of TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
 
-  return word_mode;
+static void
+arm_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  gcc_assert (size == 4);
+  fputs ("\t.word\t", file);
+  output_addr_const (file, x);
+  fputs ("(tlsldo)", file);
 }
 
-/* Implement TARGET_CLASS_LIKELY_SPILLED_P.
-   We need to define this for LO_REGS on thumb.  Otherwise we can end up
-   using r0-r4 for function arguments, r7 for the stack frame and don't
-   have enough left over to do doubleword arithmetic.  */
+/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA.  */
 
 static bool
-arm_class_likely_spilled_p (reg_class_t rclass)
+arm_output_addr_const_extra (FILE *fp, rtx x)
 {
-  if ((TARGET_THUMB && rclass == LO_REGS)
-      || rclass  == CC_REG)
-    return true;
+  if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLS)
+    return arm_emit_tls_decoration (fp, x);
+  else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PIC_LABEL)
+    {
+      char label[256];
+      int labelno = INTVAL (XVECEXP (x, 0, 0));
 
-  return false;
+      ASM_GENERATE_INTERNAL_LABEL (label, "LPIC", labelno);
+      assemble_name_raw (fp, label);
+
+      return TRUE;
+    }
+  else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_GOTSYM_OFF)
+    {
+      assemble_name (fp, "_GLOBAL_OFFSET_TABLE_");
+      if (GOT_PCREL)
+       fputs ("+.", fp);
+      fputs ("-(", fp);
+      output_addr_const (fp, XVECEXP (x, 0, 0));
+      fputc (')', fp);
+      return TRUE;
+    }
+  else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_SYMBOL_OFFSET)
+    {
+      output_addr_const (fp, XVECEXP (x, 0, 0));
+      if (GOT_PCREL)
+        fputs ("+.", fp);
+      fputs ("-(", fp);
+      output_addr_const (fp, XVECEXP (x, 0, 1));
+      fputc (')', fp);
+      return TRUE;
+    }
+  else if (GET_CODE (x) == CONST_VECTOR)
+    return arm_emit_vector_const (fp, x);
+
+  return FALSE;
 }
 
-/* Implements target hook small_register_classes_for_mode_p.  */
-bool
-arm_small_register_classes_for_mode_p (enum machine_mode mode ATTRIBUTE_UNUSED)
+/* Output assembly for a shift instruction.
+   SET_FLAGS determines how the instruction modifies the condition codes.
+   0 - Do not set condition codes.
+   1 - Set condition codes.
+   2 - Use smallest instruction.  */
+const char *
+arm_output_shift(rtx * operands, int set_flags)
 {
-  return TARGET_THUMB1;
-}
+  char pattern[100];
+  static const char flag_chars[3] = {'?', '.', '!'};
+  const char *shift;
+  HOST_WIDE_INT val;
+  char c;
 
-/* Implement TARGET_SHIFT_TRUNCATION_MASK.  SImode shifts use normal
-   ARM insns and therefore guarantee that the shift count is modulo 256.
-   DImode shifts (those implemented by lib1funcs.asm or by optabs.c)
-   guarantee no particular behavior for out-of-range counts.  */
+  c = flag_chars[set_flags];
+  if (TARGET_UNIFIED_ASM)
+    {
+      shift = shift_op(operands[3], &val);
+      if (shift)
+       {
+         if (val != -1)
+           operands[2] = GEN_INT(val);
+         sprintf (pattern, "%s%%%c\t%%0, %%1, %%2", shift, c);
+       }
+      else
+       sprintf (pattern, "mov%%%c\t%%0, %%1", c);
+    }
+  else
+    sprintf (pattern, "mov%%%c\t%%0, %%1%%S3", c);
+  output_asm_insn (pattern, operands);
+  return "";
+}
 
-static unsigned HOST_WIDE_INT
-arm_shift_truncation_mask (enum machine_mode mode)
+/* Output a Thumb-1 casesi dispatch sequence.  */
+const char *
+thumb1_output_casesi (rtx *operands)
 {
-  return mode == SImode ? 255 : 0;
+  rtx diff_vec = PATTERN (next_real_insn (operands[0]));
+
+  gcc_assert (GET_CODE (diff_vec) == ADDR_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 ();
+    }
 }
 
-
-/* Map internal gcc register numbers to DWARF2 register numbers.  */
-
-unsigned int
-arm_dbx_register_number (unsigned int regno)
+/* Output a Thumb-2 casesi instruction.  */
+const char *
+thumb2_output_casesi (rtx *operands)
 {
-  if (regno < 16)
-    return regno;
+  rtx diff_vec = PATTERN (next_real_insn (operands[2]));
 
-  /* TODO: Legacy targets output FPA regs as registers 16-23 for backwards
-     compatibility.  The EABI defines them as registers 96-103.  */
-  if (IS_FPA_REGNUM (regno))
-    return (TARGET_AAPCS_BASED ? 96 : 16) + regno - FIRST_FPA_REGNUM;
+  gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
 
-  if (IS_VFP_REGNUM (regno))
+  output_asm_insn ("cmp\t%0, %1", operands);
+  output_asm_insn ("bhi\t%l3", operands);
+  switch (GET_MODE(diff_vec))
     {
-      /* See comment in arm_dwarf_register_span.  */
-      if (VFP_REGNO_OK_FOR_SINGLE (regno))
-       return 64 + regno - FIRST_VFP_REGNUM;
+    case QImode:
+      return "tbb\t[%|pc, %0]";
+    case HImode:
+      return "tbh\t[%|pc, %0, lsl #1]";
+    case SImode:
+      if (flag_pic)
+       {
+         output_asm_insn ("adr\t%4, %l2", operands);
+         output_asm_insn ("ldr\t%5, [%4, %0, lsl #2]", operands);
+         output_asm_insn ("add\t%4, %4, %5", operands);
+         return "bx\t%4";
+       }
       else
-       return 256 + (regno - FIRST_VFP_REGNUM) / 2;
+       {
+         output_asm_insn ("adr\t%4, %l2", operands);
+         return "ldr\t%|pc, [%4, %0, lsl #2]";
+       }
+    default:
+      gcc_unreachable ();
     }
-
-  if (IS_IWMMXT_GR_REGNUM (regno))
-    return 104 + regno - FIRST_IWMMXT_GR_REGNUM;
-
-  if (IS_IWMMXT_REGNUM (regno))
-    return 112 + regno - FIRST_IWMMXT_REGNUM;
-
-  gcc_unreachable ();
 }
 
-/* Dwarf models VFPv3 registers as 32 64-bit registers.
-   GCC models tham as 64 32-bit registers, so we need to describe this to
-   the DWARF generation code.  Other registers can use the default.  */
-static rtx
-arm_dwarf_register_span (rtx rtl)
+/* Most ARM cores are single issue, but some newer ones can dual issue.
+   The scheduler descriptions rely on this being correct.  */
+static int
+arm_issue_rate (void)
 {
-  unsigned regno;
-  int nregs;
-  int i;
-  rtx p;
-
-  regno = REGNO (rtl);
-  if (!IS_VFP_REGNUM (regno))
-    return NULL_RTX;
-
-  /* XXX FIXME: The EABI defines two VFP register ranges:
-       64-95: Legacy VFPv2 numbering for S0-S31 (obsolescent)
-       256-287: D0-D31
-     The recommended encoding for S0-S31 is a DW_OP_bit_piece of the
-     corresponding D register.  Until GDB supports this, we shall use the
-     legacy encodings.  We also use these encodings for D0-D15 for
-     compatibility with older debuggers.  */
-  if (VFP_REGNO_OK_FOR_SINGLE (regno))
-    return NULL_RTX;
+  switch (arm_tune)
+    {
+    case cortexa15:
+      return 3;
 
-  nregs = GET_MODE_SIZE (GET_MODE (rtl)) / 8;
-  p = gen_rtx_PARALLEL (VOIDmode, rtvec_alloc (nregs));
-  regno = (regno - FIRST_VFP_REGNUM) / 2;
-  for (i = 0; i < nregs; i++)
-    XVECEXP (p, 0, i) = gen_rtx_REG (DImode, 256 + regno + i);
+    case cortexr4:
+    case cortexr4f:
+    case cortexr5:
+    case genericv7a:
+    case cortexa5:
+    case cortexa8:
+    case cortexa9:
+    case fa726te:
+      return 2;
 
-  return p;
+    default:
+      return 1;
+    }
 }
 
-#if ARM_UNWIND_INFO
-/* Emit unwind directives for a store-multiple instruction or stack pointer
-   push during alignment.
-   These should only ever be generated by the function prologue code, so
-   expect them to have a particular form.  */
-
-static void
-arm_unwind_emit_sequence (FILE * asm_out_file, rtx p)
+/* A table and a function to perform ARM-specific name mangling for
+   NEON vector types in order to conform to the AAPCS (see "Procedure
+   Call Standard for the ARM Architecture", Appendix A).  To qualify
+   for emission with the mangled names defined in that document, a
+   vector type must not only be of the correct mode but also be
+   composed of NEON vector element types (e.g. __builtin_neon_qi).  */
+typedef struct
 {
-  int i;
-  HOST_WIDE_INT offset;
-  HOST_WIDE_INT nregs;
-  int reg_size;
-  unsigned reg;
-  unsigned lastreg;
-  rtx e;
-
-  e = XVECEXP (p, 0, 0);
-  if (GET_CODE (e) != SET)
-    abort ();
+  enum machine_mode mode;
+  const char *element_type_name;
+  const char *aapcs_name;
+} arm_mangle_map_entry;
 
-  /* First insn will adjust the stack pointer.  */
-  if (GET_CODE (e) != SET
-      || GET_CODE (XEXP (e, 0)) != REG
-      || REGNO (XEXP (e, 0)) != SP_REGNUM
-      || GET_CODE (XEXP (e, 1)) != PLUS)
-    abort ();
+static arm_mangle_map_entry arm_mangle_map[] = {
+  /* 64-bit containerized types.  */
+  { V8QImode,  "__builtin_neon_qi",     "15__simd64_int8_t" },
+  { V8QImode,  "__builtin_neon_uqi",    "16__simd64_uint8_t" },
+  { V4HImode,  "__builtin_neon_hi",     "16__simd64_int16_t" },
+  { V4HImode,  "__builtin_neon_uhi",    "17__simd64_uint16_t" },
+  { V2SImode,  "__builtin_neon_si",     "16__simd64_int32_t" },
+  { V2SImode,  "__builtin_neon_usi",    "17__simd64_uint32_t" },
+  { V2SFmode,  "__builtin_neon_sf",     "18__simd64_float32_t" },
+  { V8QImode,  "__builtin_neon_poly8",  "16__simd64_poly8_t" },
+  { V4HImode,  "__builtin_neon_poly16", "17__simd64_poly16_t" },
+  /* 128-bit containerized types.  */
+  { V16QImode, "__builtin_neon_qi",     "16__simd128_int8_t" },
+  { V16QImode, "__builtin_neon_uqi",    "17__simd128_uint8_t" },
+  { V8HImode,  "__builtin_neon_hi",     "17__simd128_int16_t" },
+  { V8HImode,  "__builtin_neon_uhi",    "18__simd128_uint16_t" },
+  { V4SImode,  "__builtin_neon_si",     "17__simd128_int32_t" },
+  { V4SImode,  "__builtin_neon_usi",    "18__simd128_uint32_t" },
+  { V4SFmode,  "__builtin_neon_sf",     "19__simd128_float32_t" },
+  { V16QImode, "__builtin_neon_poly8",  "17__simd128_poly8_t" },
+  { V8HImode,  "__builtin_neon_poly16", "18__simd128_poly16_t" },
+  { VOIDmode, NULL, NULL }
+};
 
-  offset = -INTVAL (XEXP (XEXP (e, 1), 1));
-  nregs = XVECLEN (p, 0) - 1;
+const char *
+arm_mangle_type (const_tree type)
+{
+  arm_mangle_map_entry *pos = arm_mangle_map;
 
-  reg = REGNO (XEXP (XVECEXP (p, 0, 1), 1));
-  if (reg < 16)
+  /* 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))
     {
-      /* The function prologue may also push pc, but not annotate it as it is
-        never restored.  We turn this into a stack pointer adjustment.  */
-      if (nregs * 4 == offset - 4)
+      static bool warned;
+      if (!warned && warn_psabi && !in_system_header)
        {
-         fprintf (asm_out_file, "\t.pad #4\n");
-         offset -= 4;
+         warned = true;
+         inform (input_location,
+                 "the mangling of %<va_list%> has changed in GCC 4.4");
        }
-      reg_size = 4;
-      fprintf (asm_out_file, "\t.save {");
-    }
-  else if (IS_VFP_REGNUM (reg))
-    {
-      reg_size = 8;
-      fprintf (asm_out_file, "\t.vsave {");
-    }
-  else if (reg >= FIRST_FPA_REGNUM && reg <= LAST_FPA_REGNUM)
-    {
-      /* FPA registers are done differently.  */
-      asm_fprintf (asm_out_file, "\t.save %r, %wd\n", reg, nregs);
-      return;
+      return "St9__va_list";
     }
-  else
-    /* Unknown register type.  */
-    abort ();
 
-  /* If the stack increment doesn't match the size of the saved registers,
-     something has gone horribly wrong.  */
-  if (offset != nregs * reg_size)
-    abort ();
+  /* Half-precision float.  */
+  if (TREE_CODE (type) == REAL_TYPE && TYPE_PRECISION (type) == 16)
+    return "Dh";
 
-  offset = 0;
-  lastreg = 0;
-  /* The remaining insns will describe the stores.  */
-  for (i = 1; i <= nregs; i++)
-    {
-      /* Expect (set (mem <addr>) (reg)).
-         Where <addr> is (reg:SP) or (plus (reg:SP) (const_int)).  */
-      e = XVECEXP (p, 0, i);
-      if (GET_CODE (e) != SET
-         || GET_CODE (XEXP (e, 0)) != MEM
-         || GET_CODE (XEXP (e, 1)) != REG)
-       abort ();
+  if (TREE_CODE (type) != VECTOR_TYPE)
+    return NULL;
 
-      reg = REGNO (XEXP (e, 1));
-      if (reg < lastreg)
-       abort ();
+  /* Check the mode of the vector type, and the name of the vector
+     element type, against the table.  */
+  while (pos->mode != VOIDmode)
+    {
+      tree elt_type = TREE_TYPE (type);
 
-      if (i != 1)
-       fprintf (asm_out_file, ", ");
-      /* We can't use %r for vfp because we need to use the
-        double precision register names.  */
-      if (IS_VFP_REGNUM (reg))
-       asm_fprintf (asm_out_file, "d%d", (reg - FIRST_VFP_REGNUM) / 2);
-      else
-       asm_fprintf (asm_out_file, "%r", reg);
+      if (pos->mode == TYPE_MODE (type)
+         && TREE_CODE (TYPE_NAME (elt_type)) == TYPE_DECL
+         && !strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (elt_type))),
+                     pos->element_type_name))
+        return pos->aapcs_name;
 
-#ifdef ENABLE_CHECKING
-      /* Check that the addresses are consecutive.  */
-      e = XEXP (XEXP (e, 0), 0);
-      if (GET_CODE (e) == PLUS)
-       {
-         offset += reg_size;
-         if (GET_CODE (XEXP (e, 0)) != REG
-             || REGNO (XEXP (e, 0)) != SP_REGNUM
-             || GET_CODE (XEXP (e, 1)) != CONST_INT
-             || offset != INTVAL (XEXP (e, 1)))
-           abort ();
-       }
-      else if (i != 1
-              || GET_CODE (e) != REG
-              || REGNO (e) != SP_REGNUM)
-       abort ();
-#endif
+      pos++;
     }
-  fprintf (asm_out_file, "}\n");
+
+  /* Use the default mangling for unrecognized (possibly user-defined)
+     vector types.  */
+  return NULL;
 }
 
-/*  Emit unwind directives for a SET.  */
+/* Order of allocation of core registers for Thumb: this allocation is
+   written over the corresponding initial entries of the array
+   initialized with REG_ALLOC_ORDER.  We allocate all low registers
+   first.  Saving and restoring a low register is usually cheaper than
+   using a call-clobbered high register.  */
 
-static void
-arm_unwind_emit_set (FILE * asm_out_file, rtx p)
+static const int thumb_core_reg_alloc_order[] =
 {
-  rtx e0;
-  rtx e1;
-  unsigned reg;
+   3,  2,  1,  0,  4,  5,  6,  7,
+  14, 12,  8,  9, 10, 11, 13, 15
+};
 
-  e0 = XEXP (p, 0);
-  e1 = XEXP (p, 1);
-  switch (GET_CODE (e0))
-    {
-    case MEM:
-      /* Pushing a single register.  */
-      if (GET_CODE (XEXP (e0, 0)) != PRE_DEC
-         || GET_CODE (XEXP (XEXP (e0, 0), 0)) != REG
-         || REGNO (XEXP (XEXP (e0, 0), 0)) != SP_REGNUM)
-       abort ();
+/* Adjust register allocation order when compiling for Thumb.  */
 
-      asm_fprintf (asm_out_file, "\t.save ");
-      if (IS_VFP_REGNUM (REGNO (e1)))
-       asm_fprintf(asm_out_file, "{d%d}\n",
-                   (REGNO (e1) - FIRST_VFP_REGNUM) / 2);
-      else
-       asm_fprintf(asm_out_file, "{%r}\n", REGNO (e1));
-      break;
+void
+arm_order_regs_for_local_alloc (void)
+{
+  const int arm_reg_alloc_order[] = REG_ALLOC_ORDER;
+  memcpy(reg_alloc_order, arm_reg_alloc_order, sizeof (reg_alloc_order));
+  if (TARGET_THUMB)
+    memcpy (reg_alloc_order, thumb_core_reg_alloc_order,
+            sizeof (thumb_core_reg_alloc_order));
+}
 
-    case REG:
-      if (REGNO (e0) == SP_REGNUM)
-       {
-         /* A stack increment.  */
-         if (GET_CODE (e1) != PLUS
-             || GET_CODE (XEXP (e1, 0)) != REG
-             || REGNO (XEXP (e1, 0)) != SP_REGNUM
-             || GET_CODE (XEXP (e1, 1)) != CONST_INT)
-           abort ();
+/* Implement TARGET_FRAME_POINTER_REQUIRED.  */
 
-         asm_fprintf (asm_out_file, "\t.pad #%wd\n",
-                      -INTVAL (XEXP (e1, 1)));
-       }
-      else if (REGNO (e0) == HARD_FRAME_POINTER_REGNUM)
-       {
-         HOST_WIDE_INT offset;
+bool
+arm_frame_pointer_required (void)
+{
+  return (cfun->has_nonlocal_label
+          || SUBTARGET_FRAME_POINTER_REQUIRED
+          || (TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ()));
+}
 
-         if (GET_CODE (e1) == PLUS)
-           {
-             if (GET_CODE (XEXP (e1, 0)) != REG
-                 || GET_CODE (XEXP (e1, 1)) != CONST_INT)
-               abort ();
-             reg = REGNO (XEXP (e1, 0));
-             offset = INTVAL (XEXP (e1, 1));
-             asm_fprintf (asm_out_file, "\t.setfp %r, %r, #%wd\n",
-                          HARD_FRAME_POINTER_REGNUM, reg,
-                          offset);
-           }
-         else if (GET_CODE (e1) == REG)
-           {
-             reg = REGNO (e1);
-             asm_fprintf (asm_out_file, "\t.setfp %r, %r\n",
-                          HARD_FRAME_POINTER_REGNUM, reg);
-           }
-         else
-           abort ();
-       }
-      else if (GET_CODE (e1) == REG && REGNO (e1) == SP_REGNUM)
-       {
-         /* Move from sp to reg.  */
-         asm_fprintf (asm_out_file, "\t.movsp %r\n", REGNO (e0));
-       }
-     else if (GET_CODE (e1) == PLUS
-             && GET_CODE (XEXP (e1, 0)) == REG
-             && REGNO (XEXP (e1, 0)) == SP_REGNUM
-             && GET_CODE (XEXP (e1, 1)) == CONST_INT)
-       {
-         /* Set reg to offset from sp.  */
-         asm_fprintf (asm_out_file, "\t.movsp %r, #%d\n",
-                      REGNO (e0), (int)INTVAL(XEXP (e1, 1)));
-       }
-      else if (GET_CODE (e1) == UNSPEC && XINT (e1, 1) == UNSPEC_STACK_ALIGN)
-       {
-         /* Stack pointer save before alignment.  */
-         reg = REGNO (e0);
-         asm_fprintf (asm_out_file, "\t.unwind_raw 0, 0x%x @ vsp = r%d\n",
-                      reg + 0x90, reg);
-       }
-      else
-       abort ();
-      break;
+/* Only thumb1 can't support conditional execution, so return true if
+   the target is not thumb1.  */
+static bool
+arm_have_conditional_execution (void)
+{
+  return !TARGET_THUMB1;
+}
 
-    default:
-      abort ();
-    }
+static unsigned int
+arm_autovectorize_vector_sizes (void)
+{
+  return TARGET_NEON_VECTORIZE_DOUBLE ? 0 : (16 | 8);
 }
 
+static bool
+arm_vector_alignment_reachable (const_tree type, bool is_packed)
+{
+  /* Vectors which aren't in packed structures will not be less aligned than
+     the natural alignment of their element type, so this is safe.  */
+  if (TARGET_NEON && !BYTES_BIG_ENDIAN)
+    return !is_packed;
 
-/* Emit unwind directives for the given insn.  */
+  return default_builtin_vector_alignment_reachable (type, is_packed);
+}
 
-static void
-arm_unwind_emit (FILE * asm_out_file, rtx insn)
+static bool
+arm_builtin_support_vector_misalignment (enum machine_mode mode,
+                                        const_tree type, int misalignment,
+                                        bool is_packed)
 {
-  rtx pat;
+  if (TARGET_NEON && !BYTES_BIG_ENDIAN)
+    {
+      HOST_WIDE_INT align = TYPE_ALIGN_UNIT (type);
 
-  if (arm_except_unwind_info (&global_options) != UI_TARGET)
-    return;
+      if (is_packed)
+        return align == 1;
 
-  if (!(flag_unwind_tables || crtl->uses_eh_lsda)
-      && (TREE_NOTHROW (current_function_decl)
-         || crtl->all_throwers_are_sibcalls))
-    return;
+      /* If the misalignment is unknown, we should be able to handle the access
+        so long as it is not to a member of a packed data structure.  */
+      if (misalignment == -1)
+        return true;
 
-  if (GET_CODE (insn) == NOTE || !RTX_FRAME_RELATED_P (insn))
-    return;
+      /* Return true if the misalignment is a multiple of the natural alignment
+         of the vector's element type.  This is probably always going to be
+        true in practice, since we've already established that this isn't a
+        packed access.  */
+      return ((misalignment % align) == 0);
+    }
 
-  pat = find_reg_note (insn, REG_FRAME_RELATED_EXPR, NULL_RTX);
-  if (pat)
-    pat = XEXP (pat, 0);
-  else
-    pat = PATTERN (insn);
+  return default_builtin_support_vector_misalignment (mode, type, misalignment,
+                                                     is_packed);
+}
 
-  switch (GET_CODE (pat))
-    {
-    case SET:
-      arm_unwind_emit_set (asm_out_file, pat);
-      break;
+static void
+arm_conditional_register_usage (void)
+{
+  int regno;
 
-    case SEQUENCE:
-      /* Store multiple.  */
-      arm_unwind_emit_sequence (asm_out_file, pat);
-      break;
+  if (TARGET_SOFT_FLOAT || TARGET_THUMB1 || !TARGET_FPA)
+    {
+      for (regno = FIRST_FPA_REGNUM;
+          regno <= LAST_FPA_REGNUM; ++regno)
+       fixed_regs[regno] = call_used_regs[regno] = 1;
+    }
 
-    default:
-      abort();
+  if (TARGET_THUMB1 && optimize_size)
+    {
+      /* When optimizing for size on Thumb-1, it's better not
+        to use the HI regs, because of the overhead of
+        stacking them.  */
+      for (regno = FIRST_HI_REGNUM;
+          regno <= LAST_HI_REGNUM; ++regno)
+       fixed_regs[regno] = call_used_regs[regno] = 1;
     }
-}
 
+  /* The link register can be clobbered by any branch insn,
+     but we have no way to track that at present, so mark
+     it as unavailable.  */
+  if (TARGET_THUMB1)
+    fixed_regs[LR_REGNUM] = call_used_regs[LR_REGNUM] = 1;
 
-/* Output a reference from a function exception table to the type_info
-   object X.  The EABI specifies that the symbol should be relocated by
-   an R_ARM_TARGET2 relocation.  */
+  if (TARGET_32BIT && TARGET_HARD_FLOAT)
+    {
+      if (TARGET_MAVERICK)
+       {
+         for (regno = FIRST_FPA_REGNUM;
+              regno <= LAST_FPA_REGNUM; ++ regno)
+           fixed_regs[regno] = call_used_regs[regno] = 1;
+         for (regno = FIRST_CIRRUS_FP_REGNUM;
+              regno <= LAST_CIRRUS_FP_REGNUM; ++ regno)
+           {
+             fixed_regs[regno] = 0;
+             call_used_regs[regno] = regno < FIRST_CIRRUS_FP_REGNUM + 4;
+           }
+       }
+      if (TARGET_VFP)
+       {
+         /* VFPv3 registers are disabled when earlier VFP
+            versions are selected due to the definition of
+            LAST_VFP_REGNUM.  */
+         for (regno = FIRST_VFP_REGNUM;
+              regno <= LAST_VFP_REGNUM; ++ regno)
+           {
+             fixed_regs[regno] = 0;
+             call_used_regs[regno] = regno < FIRST_VFP_REGNUM + 16
+               || regno >= FIRST_VFP_REGNUM + 32;
+           }
+       }
+    }
 
-static bool
-arm_output_ttype (rtx x)
-{
-  fputs ("\t.word\t", asm_out_file);
-  output_addr_const (asm_out_file, x);
-  /* Use special relocations for symbol references.  */
-  if (GET_CODE (x) != CONST_INT)
-    fputs ("(TARGET2)", asm_out_file);
-  fputc ('\n', asm_out_file);
+  if (TARGET_REALLY_IWMMXT)
+    {
+      regno = FIRST_IWMMXT_GR_REGNUM;
+      /* The 2002/10/09 revision of the XScale ABI has wCG0
+         and wCG1 as call-preserved registers.  The 2002/11/21
+         revision changed this so that all wCG registers are
+         scratch registers.  */
+      for (regno = FIRST_IWMMXT_GR_REGNUM;
+          regno <= LAST_IWMMXT_GR_REGNUM; ++ regno)
+       fixed_regs[regno] = 0;
+      /* The XScale ABI has wR0 - wR9 as scratch registers,
+        the rest as call-preserved registers.  */
+      for (regno = FIRST_IWMMXT_REGNUM;
+          regno <= LAST_IWMMXT_REGNUM; ++ regno)
+       {
+         fixed_regs[regno] = 0;
+         call_used_regs[regno] = regno < FIRST_IWMMXT_REGNUM + 10;
+       }
+    }
 
-  return TRUE;
+  if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
+    {
+      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+    }
+  else if (TARGET_APCS_STACK)
+    {
+      fixed_regs[10]     = 1;
+      call_used_regs[10] = 1;
+    }
+  /* -mcaller-super-interworking reserves r11 for calls to
+     _interwork_r11_call_via_rN().  Making the register global
+     is an easy way of ensuring that it remains valid for all
+     calls.  */
+  if (TARGET_APCS_FRAME || TARGET_CALLER_INTERWORKING
+      || TARGET_TPCS_FRAME || TARGET_TPCS_LEAF_FRAME)
+    {
+      fixed_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
+      call_used_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
+      if (TARGET_CALLER_INTERWORKING)
+       global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
+    }
+  SUBTARGET_CONDITIONAL_REGISTER_USAGE
 }
 
-/* Implement TARGET_ASM_EMIT_EXCEPT_PERSONALITY.  */
-
-static void
-arm_asm_emit_except_personality (rtx personality)
+static reg_class_t
+arm_preferred_rename_class (reg_class_t rclass)
 {
-  fputs ("\t.personality\t", asm_out_file);
-  output_addr_const (asm_out_file, personality);
-  fputc ('\n', asm_out_file);
+  /* Thumb-2 instructions using LO_REGS may be smaller than instructions
+     using GENERIC_REGS.  During register rename pass, we prefer LO_REGS,
+     and code size can be reduced.  */
+  if (TARGET_THUMB2 && rclass == GENERAL_REGS)
+    return LO_REGS;
+  else
+    return NO_REGS;
 }
 
-/* Implement TARGET_ASM_INITIALIZE_SECTIONS.  */
-
-static void
-arm_asm_init_sections (void)
+/* Compute the atrribute "length" of insn "*push_multi".
+   So this function MUST be kept in sync with that insn pattern.  */
+int
+arm_attr_length_push_multi(rtx parallel_op, rtx first_op)
 {
-  exception_section = get_unnamed_section (0, output_section_asm_op,
-                                          "\t.handlerdata");
-}
-#endif /* ARM_UNWIND_INFO */
+  int i, regno, hi_reg;
+  int num_saves = XVECLEN (parallel_op, 0);
 
-/* Implement TARGET_EXCEPT_UNWIND_INFO.  */
-
-static enum unwind_info_type
-arm_except_unwind_info (struct gcc_options *opts)
-{
-  /* Honor the --enable-sjlj-exceptions configure switch.  */
-#ifdef CONFIG_SJLJ_EXCEPTIONS
-  if (CONFIG_SJLJ_EXCEPTIONS)
-    return UI_SJLJ;
-#endif
+  /* ARM mode.  */
+  if (TARGET_ARM)
+    return 4;
+  /* Thumb1 mode.  */
+  if (TARGET_THUMB1)
+    return 2;
 
-  /* If not using ARM EABI unwind tables... */
-  if (ARM_UNWIND_INFO)
+  /* Thumb2 mode.  */
+  regno = REGNO (first_op);
+  hi_reg = (REGNO_REG_CLASS (regno) == HI_REGS) && (regno != LR_REGNUM);
+  for (i = 1; i < num_saves && !hi_reg; i++)
     {
-      /* For simplicity elsewhere in this file, indicate that all unwind
-        info is disabled if we're not emitting unwind tables.  */
-      if (!opts->x_flag_exceptions && !opts->x_flag_unwind_tables)
-       return UI_NONE;
-      else
-       return UI_TARGET;
+      regno = REGNO (XEXP (XVECEXP (parallel_op, 0, i), 0));
+      hi_reg |= (REGNO_REG_CLASS (regno) == HI_REGS) && (regno != LR_REGNUM);
     }
 
-  /* ... we use sjlj exceptions for backwards compatibility.  */
-  return UI_SJLJ;
+  if (!hi_reg)
+    return 2;
+  return 4;
 }
 
-
-/* Handle UNSPEC DWARF call frame instructions.  These are needed for dynamic
-   stack alignment.  */
-
-static void
-arm_dwarf_handle_frame_unspec (const char *label, rtx pattern, int index)
+/* Compute the number of instructions emitted by output_move_double.  */
+int
+arm_count_output_move_double_insns (rtx *operands)
 {
-  rtx unspec = SET_SRC (pattern);
-  gcc_assert (GET_CODE (unspec) == UNSPEC);
-
-  switch (index)
-    {
-    case UNSPEC_STACK_ALIGN:
-      /* ??? We should set the CFA = (SP & ~7).  At this point we haven't
-         put anything on the stack, so hopefully it won't matter.
-         CFA = SP will be correct after alignment.  */
-      dwarf2out_reg_save_reg (label, stack_pointer_rtx,
-                              SET_DEST (pattern));
-      break;
-    default:
-      gcc_unreachable ();
-    }
+  int count;
+  rtx ops[2];
+  /* output_move_double may modify the operands array, so call it
+     here on a copy of the array.  */
+  ops[0] = operands[0];
+  ops[1] = operands[1];
+  output_move_double (ops, false, &count);
+  return count;
 }
 
-
-/* Output unwind directives for the start/end of a function.  */
-
-void
-arm_output_fn_unwind (FILE * f, bool prologue)
+int
+vfp3_const_double_for_fract_bits (rtx operand)
 {
-  if (arm_except_unwind_info (&global_options) != UI_TARGET)
-    return;
-
-  if (prologue)
-    fputs ("\t.fnstart\n", f);
-  else
+  REAL_VALUE_TYPE r0;
+  
+  if (GET_CODE (operand) != CONST_DOUBLE)
+    return 0;
+  
+  REAL_VALUE_FROM_CONST_DOUBLE (r0, operand);
+  if (exact_real_inverse (DFmode, &r0))
     {
-      /* If this function will never be unwound, then mark it as such.
-         The came condition is used in arm_unwind_emit to suppress
-        the frame annotations.  */
-      if (!(flag_unwind_tables || crtl->uses_eh_lsda)
-         && (TREE_NOTHROW (current_function_decl)
-             || crtl->all_throwers_are_sibcalls))
-       fputs("\t.cantunwind\n", f);
-
-      fputs ("\t.fnend\n", f);
+      if (exact_real_truncate (DFmode, &r0))
+       {
+         HOST_WIDE_INT value = real_to_integer (&r0);
+         value = value & 0xffffffff;
+         if ((value != 0) && ( (value & (value - 1)) == 0))
+           return int_log2 (value);
+       }
     }
+  return 0;
 }
+\f
+/* Emit a memory barrier around an atomic sequence according to MODEL.  */
 
-static bool
-arm_emit_tls_decoration (FILE *fp, rtx x)
+static void
+arm_pre_atomic_barrier (enum memmodel model)
 {
-  enum tls_reloc reloc;
-  rtx val;
-
-  val = XVECEXP (x, 0, 0);
-  reloc = (enum tls_reloc) INTVAL (XVECEXP (x, 0, 1));
-
-  output_addr_const (fp, val);
-
-  switch (reloc)
+  switch (model)
     {
-    case TLS_GD32:
-      fputs ("(tlsgd)", fp);
-      break;
-    case TLS_LDM32:
-      fputs ("(tlsldm)", fp);
-      break;
-    case TLS_LDO32:
-      fputs ("(tlsldo)", fp);
-      break;
-    case TLS_IE32:
-      fputs ("(gottpoff)", fp);
+    case MEMMODEL_RELAXED:
+    case MEMMODEL_CONSUME:
+    case MEMMODEL_ACQUIRE:
       break;
-    case TLS_LE32:
-      fputs ("(tpoff)", fp);
+    case MEMMODEL_RELEASE:
+    case MEMMODEL_ACQ_REL:
+    case MEMMODEL_SEQ_CST:
+      emit_insn (gen_memory_barrier ());
       break;
     default:
       gcc_unreachable ();
     }
+}
 
-  switch (reloc)
+static void
+arm_post_atomic_barrier (enum memmodel model)
+{
+  switch (model)
     {
-    case TLS_GD32:
-    case TLS_LDM32:
-    case TLS_IE32:
-      fputs (" + (. - ", fp);
-      output_addr_const (fp, XVECEXP (x, 0, 2));
-      fputs (" - ", fp);
-      output_addr_const (fp, XVECEXP (x, 0, 3));
-      fputc (')', fp);
+    case MEMMODEL_RELAXED:
+    case MEMMODEL_CONSUME:
+    case MEMMODEL_RELEASE:
       break;
-    default:
+    case MEMMODEL_ACQUIRE:
+    case MEMMODEL_ACQ_REL:
+    case MEMMODEL_SEQ_CST:
+      emit_insn (gen_memory_barrier ());
       break;
+    default:
+      gcc_unreachable ();
     }
-
-  return TRUE;
 }
 
-/* ARM implementation of TARGET_ASM_OUTPUT_DWARF_DTPREL.  */
+/* Emit the load-exclusive and store-exclusive instructions.  */
 
 static void
-arm_output_dwarf_dtprel (FILE *file, int size, rtx x)
-{
-  gcc_assert (size == 4);
-  fputs ("\t.word\t", file);
-  output_addr_const (file, x);
-  fputs ("(tlsldo)", file);
-}
-
-/* Implement TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA.  */
-
-static bool
-arm_output_addr_const_extra (FILE *fp, rtx x)
+arm_emit_load_exclusive (enum machine_mode mode, rtx rval, rtx mem)
 {
-  if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLS)
-    return arm_emit_tls_decoration (fp, x);
-  else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_PIC_LABEL)
-    {
-      char label[256];
-      int labelno = INTVAL (XVECEXP (x, 0, 0));
+  rtx (*gen) (rtx, rtx);
 
-      ASM_GENERATE_INTERNAL_LABEL (label, "LPIC", labelno);
-      assemble_name_raw (fp, label);
-
-      return TRUE;
-    }
-  else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_GOTSYM_OFF)
-    {
-      assemble_name (fp, "_GLOBAL_OFFSET_TABLE_");
-      if (GOT_PCREL)
-       fputs ("+.", fp);
-      fputs ("-(", fp);
-      output_addr_const (fp, XVECEXP (x, 0, 0));
-      fputc (')', fp);
-      return TRUE;
-    }
-  else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_SYMBOL_OFFSET)
+  switch (mode)
     {
-      output_addr_const (fp, XVECEXP (x, 0, 0));
-      if (GOT_PCREL)
-        fputs ("+.", fp);
-      fputs ("-(", fp);
-      output_addr_const (fp, XVECEXP (x, 0, 1));
-      fputc (')', fp);
-      return TRUE;
+    case QImode: gen = gen_arm_load_exclusiveqi; break;
+    case HImode: gen = gen_arm_load_exclusivehi; break;
+    case SImode: gen = gen_arm_load_exclusivesi; break;
+    case DImode: gen = gen_arm_load_exclusivedi; break;
+    default:
+      gcc_unreachable ();
     }
-  else if (GET_CODE (x) == CONST_VECTOR)
-    return arm_emit_vector_const (fp, x);
 
-  return FALSE;
-}
-
-/* Output assembly for a shift instruction.
-   SET_FLAGS determines how the instruction modifies the condition codes.
-   0 - Do not set condition codes.
-   1 - Set condition codes.
-   2 - Use smallest instruction.  */
-const char *
-arm_output_shift(rtx * operands, int set_flags)
-{
-  char pattern[100];
-  static const char flag_chars[3] = {'?', '.', '!'};
-  const char *shift;
-  HOST_WIDE_INT val;
-  char c;
-  
-  c = flag_chars[set_flags];
-  if (TARGET_UNIFIED_ASM)
-    {
-      shift = shift_op(operands[3], &val);
-      if (shift)
-       {
-         if (val != -1)
-           operands[2] = GEN_INT(val);
-         sprintf (pattern, "%s%%%c\t%%0, %%1, %%2", shift, c);
-       }
-      else
-       sprintf (pattern, "mov%%%c\t%%0, %%1", c);
-    }
-  else
-    sprintf (pattern, "mov%%%c\t%%0, %%1%%S3", c);
-  output_asm_insn (pattern, operands);
-  return "";
+  emit_insn (gen (rval, mem));
 }
 
-/* Output a Thumb-1 casesi dispatch sequence.  */
-const char *
-thumb1_output_casesi (rtx *operands)
+static void
+arm_emit_store_exclusive (enum machine_mode mode, rtx bval, rtx rval, rtx mem)
 {
-  rtx diff_vec = PATTERN (next_real_insn (operands[0]));
-
-  gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+  rtx (*gen) (rtx, rtx, rtx);
 
-  switch (GET_MODE(diff_vec))
+  switch (mode)
     {
-    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";
+    case QImode: gen = gen_arm_store_exclusiveqi; break;
+    case HImode: gen = gen_arm_store_exclusivehi; break;
+    case SImode: gen = gen_arm_store_exclusivesi; break;
+    case DImode: gen = gen_arm_store_exclusivedi; break;
     default:
       gcc_unreachable ();
     }
+
+  emit_insn (gen (bval, rval, mem));
 }
 
-/* Output a Thumb-2 casesi instruction.  */
-const char *
-thumb2_output_casesi (rtx *operands)
+/* Mark the previous jump instruction as unlikely.  */
+
+static void
+emit_unlikely_jump (rtx insn)
 {
-  rtx diff_vec = PATTERN (next_real_insn (operands[2]));
+  rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
 
-  gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+  insn = emit_jump_insn (insn);
+  add_reg_note (insn, REG_BR_PROB, very_unlikely);
+}
 
-  output_asm_insn ("cmp\t%0, %1", operands);
-  output_asm_insn ("bhi\t%l3", operands);
-  switch (GET_MODE(diff_vec))
+/* Expand a compare and swap pattern.  */
+
+void
+arm_expand_compare_and_swap (rtx operands[])
+{
+  rtx bval, rval, mem, oldval, newval, is_weak, mod_s, mod_f, x;
+  enum machine_mode mode;
+  rtx (*gen) (rtx, rtx, rtx, rtx, rtx, rtx, rtx);
+
+  bval = operands[0];
+  rval = operands[1];
+  mem = operands[2];
+  oldval = operands[3];
+  newval = operands[4];
+  is_weak = operands[5];
+  mod_s = operands[6];
+  mod_f = operands[7];
+  mode = GET_MODE (mem);
+
+  switch (mode)
     {
     case QImode:
-      return "tbb\t[%|pc, %0]";
     case HImode:
-      return "tbh\t[%|pc, %0, lsl #1]";
+      /* For narrow modes, we're going to perform the comparison in SImode,
+        so do the zero-extension now.  */
+      rval = gen_reg_rtx (SImode);
+      oldval = convert_modes (SImode, mode, oldval, true);
+      /* FALLTHRU */
+
     case SImode:
-      if (flag_pic)
-       {
-         output_asm_insn ("adr\t%4, %l2", operands);
-         output_asm_insn ("ldr\t%5, [%4, %0, lsl #2]", operands);
-         output_asm_insn ("add\t%4, %4, %5", operands);
-         return "bx\t%4";
-       }
-      else
-       {
-         output_asm_insn ("adr\t%4, %l2", operands);
-         return "ldr\t%|pc, [%4, %0, lsl #2]";
-       }
+      /* Force the value into a register if needed.  We waited until after
+        the zero-extension above to do this properly.  */
+      if (!arm_add_operand (oldval, mode))
+       oldval = force_reg (mode, oldval);
+      break;
+
+    case DImode:
+      if (!cmpdi_operand (oldval, mode))
+       oldval = force_reg (mode, oldval);
+      break;
+
     default:
       gcc_unreachable ();
     }
-}
 
-/* Most ARM cores are single issue, but some newer ones can dual issue.
-   The scheduler descriptions rely on this being correct.  */
-static int
-arm_issue_rate (void)
-{
-  switch (arm_tune)
+  switch (mode)
     {
-    case cortexr4:
-    case cortexr4f:
-    case cortexa5:
-    case cortexa8:
-    case cortexa9:
-    case fa726te:
-      return 2;
-
+    case QImode: gen = gen_atomic_compare_and_swapqi_1; break;
+    case HImode: gen = gen_atomic_compare_and_swaphi_1; break;
+    case SImode: gen = gen_atomic_compare_and_swapsi_1; break;
+    case DImode: gen = gen_atomic_compare_and_swapdi_1; break;
     default:
-      return 1;
+      gcc_unreachable ();
     }
+
+  emit_insn (gen (rval, mem, oldval, newval, is_weak, mod_s, mod_f));
+
+  if (mode == QImode || mode == HImode)
+    emit_move_insn (operands[1], gen_lowpart (mode, rval));
+
+  /* In all cases, we arrange for success to be signaled by Z set.
+     This arrangement allows for the boolean result to be used directly
+     in a subsequent branch, post optimization.  */
+  x = gen_rtx_REG (CCmode, CC_REGNUM);
+  x = gen_rtx_EQ (SImode, x, const0_rtx);
+  emit_insn (gen_rtx_SET (VOIDmode, bval, x));
 }
 
-/* A table and a function to perform ARM-specific name mangling for
-   NEON vector types in order to conform to the AAPCS (see "Procedure
-   Call Standard for the ARM Architecture", Appendix A).  To qualify
-   for emission with the mangled names defined in that document, a
-   vector type must not only be of the correct mode but also be
-   composed of NEON vector element types (e.g. __builtin_neon_qi).  */
-typedef struct
+/* Split a compare and swap pattern.  It is IMPLEMENTATION DEFINED whether
+   another memory store between the load-exclusive and store-exclusive can
+   reset the monitor from Exclusive to Open state.  This means we must wait
+   until after reload to split the pattern, lest we get a register spill in
+   the middle of the atomic sequence.  */
+
+void
+arm_split_compare_and_swap (rtx operands[])
 {
+  rtx rval, mem, oldval, newval, scratch;
   enum machine_mode mode;
-  const char *element_type_name;
-  const char *aapcs_name;
-} arm_mangle_map_entry;
+  enum memmodel mod_s, mod_f;
+  bool is_weak;
+  rtx label1, label2, x, cond;
 
-static arm_mangle_map_entry arm_mangle_map[] = {
-  /* 64-bit containerized types.  */
-  { V8QImode,  "__builtin_neon_qi",     "15__simd64_int8_t" },
-  { V8QImode,  "__builtin_neon_uqi",    "16__simd64_uint8_t" },
-  { V4HImode,  "__builtin_neon_hi",     "16__simd64_int16_t" },
-  { V4HImode,  "__builtin_neon_uhi",    "17__simd64_uint16_t" },
-  { V2SImode,  "__builtin_neon_si",     "16__simd64_int32_t" },
-  { V2SImode,  "__builtin_neon_usi",    "17__simd64_uint32_t" },
-  { V2SFmode,  "__builtin_neon_sf",     "18__simd64_float32_t" },
-  { V8QImode,  "__builtin_neon_poly8",  "16__simd64_poly8_t" },
-  { V4HImode,  "__builtin_neon_poly16", "17__simd64_poly16_t" },
-  /* 128-bit containerized types.  */
-  { V16QImode, "__builtin_neon_qi",     "16__simd128_int8_t" },
-  { V16QImode, "__builtin_neon_uqi",    "17__simd128_uint8_t" },
-  { V8HImode,  "__builtin_neon_hi",     "17__simd128_int16_t" },
-  { V8HImode,  "__builtin_neon_uhi",    "18__simd128_uint16_t" },
-  { V4SImode,  "__builtin_neon_si",     "17__simd128_int32_t" },
-  { V4SImode,  "__builtin_neon_usi",    "18__simd128_uint32_t" },
-  { V4SFmode,  "__builtin_neon_sf",     "19__simd128_float32_t" },
-  { V16QImode, "__builtin_neon_poly8",  "17__simd128_poly8_t" },
-  { V8HImode,  "__builtin_neon_poly16", "18__simd128_poly16_t" },
-  { VOIDmode, NULL, NULL }
-};
+  rval = operands[0];
+  mem = operands[1];
+  oldval = operands[2];
+  newval = operands[3];
+  is_weak = (operands[4] != const0_rtx);
+  mod_s = (enum memmodel) INTVAL (operands[5]);
+  mod_f = (enum memmodel) INTVAL (operands[6]);
+  scratch = operands[7];
+  mode = GET_MODE (mem);
 
-const char *
-arm_mangle_type (const_tree type)
-{
-  arm_mangle_map_entry *pos = arm_mangle_map;
+  arm_pre_atomic_barrier (mod_s);
 
-  /* 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))
+  label1 = NULL_RTX;
+  if (!is_weak)
     {
-      static bool warned;
-      if (!warned && warn_psabi && !in_system_header)
-       {
-         warned = true;
-         inform (input_location,
-                 "the mangling of %<va_list%> has changed in GCC 4.4");
-       }
-      return "St9__va_list";
+      label1 = gen_label_rtx ();
+      emit_label (label1);
     }
+  label2 = gen_label_rtx ();
 
-  /* Half-precision float.  */
-  if (TREE_CODE (type) == REAL_TYPE && TYPE_PRECISION (type) == 16)
-    return "Dh";
+  arm_emit_load_exclusive (mode, rval, mem);
 
-  if (TREE_CODE (type) != VECTOR_TYPE)
-    return NULL;
+  cond = arm_gen_compare_reg (NE, rval, oldval, scratch);
+  x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+  x = gen_rtx_IF_THEN_ELSE (VOIDmode, x,
+                           gen_rtx_LABEL_REF (Pmode, label2), pc_rtx);
+  emit_unlikely_jump (gen_rtx_SET (VOIDmode, pc_rtx, x));
 
-  /* Check the mode of the vector type, and the name of the vector
-     element type, against the table.  */
-  while (pos->mode != VOIDmode)
-    {
-      tree elt_type = TREE_TYPE (type);
+  arm_emit_store_exclusive (mode, scratch, mem, newval);
 
-      if (pos->mode == TYPE_MODE (type)
-         && TREE_CODE (TYPE_NAME (elt_type)) == TYPE_DECL
-         && !strcmp (IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (elt_type))),
-                     pos->element_type_name))
-        return pos->aapcs_name;
+  /* Weak or strong, we want EQ to be true for success, so that we
+     match the flags that we got from the compare above.  */
+  cond = gen_rtx_REG (CCmode, CC_REGNUM);
+  x = gen_rtx_COMPARE (CCmode, scratch, const0_rtx);
+  emit_insn (gen_rtx_SET (VOIDmode, cond, x));
 
-      pos++;
+  if (!is_weak)
+    {
+      x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+      x = gen_rtx_IF_THEN_ELSE (VOIDmode, x,
+                               gen_rtx_LABEL_REF (Pmode, label1), pc_rtx);
+      emit_unlikely_jump (gen_rtx_SET (VOIDmode, pc_rtx, x));
     }
 
-  /* Use the default mangling for unrecognized (possibly user-defined)
-     vector types.  */
-  return NULL;
-}
-
-/* Order of allocation of core registers for Thumb: this allocation is
-   written over the corresponding initial entries of the array
-   initialized with REG_ALLOC_ORDER.  We allocate all low registers
-   first.  Saving and restoring a low register is usually cheaper than
-   using a call-clobbered high register.  */
+  if (mod_f != MEMMODEL_RELAXED)
+    emit_label (label2);
 
-static const int thumb_core_reg_alloc_order[] =
-{
-   3,  2,  1,  0,  4,  5,  6,  7,
-  14, 12,  8,  9, 10, 11, 13, 15
-};
+  arm_post_atomic_barrier (mod_s);
 
-/* Adjust register allocation order when compiling for Thumb.  */
+  if (mod_f == MEMMODEL_RELAXED)
+    emit_label (label2);
+}
 
 void
-arm_order_regs_for_local_alloc (void)
+arm_split_atomic_op (enum rtx_code code, rtx old_out, rtx new_out, rtx mem,
+                    rtx value, rtx model_rtx, rtx cond)
 {
-  const int arm_reg_alloc_order[] = REG_ALLOC_ORDER;
-  memcpy(reg_alloc_order, arm_reg_alloc_order, sizeof (reg_alloc_order));
-  if (TARGET_THUMB)
-    memcpy (reg_alloc_order, thumb_core_reg_alloc_order,
-            sizeof (thumb_core_reg_alloc_order));
-}
+  enum memmodel model = (enum memmodel) INTVAL (model_rtx);
+  enum machine_mode mode = GET_MODE (mem);
+  enum machine_mode wmode = (mode == DImode ? DImode : SImode);
+  rtx label, x;
 
-/* Implement TARGET_FRAME_POINTER_REQUIRED.  */
+  arm_pre_atomic_barrier (model);
 
-bool
-arm_frame_pointer_required (void)
-{
-  return (cfun->has_nonlocal_label
-          || SUBTARGET_FRAME_POINTER_REQUIRED
-          || (TARGET_ARM && TARGET_APCS_FRAME && ! leaf_function_p ()));
-}
+  label = gen_label_rtx ();
+  emit_label (label);
 
-/* Only thumb1 can't support conditional execution, so return true if
-   the target is not thumb1.  */
-static bool
-arm_have_conditional_execution (void)
-{
-  return !TARGET_THUMB1;
-}
+  if (new_out)
+    new_out = gen_lowpart (wmode, new_out);
+  if (old_out)
+    old_out = gen_lowpart (wmode, old_out);
+  else
+    old_out = new_out;
+  value = simplify_gen_subreg (wmode, value, mode, 0);
 
-/* Legitimize a memory reference for sync primitive implemented using
-   ldrex / strex.  We currently force the form of the reference to be
-   indirect without offset.  We do not yet support the indirect offset
-   addressing supported by some ARM targets for these
-   instructions.  */
-static rtx
-arm_legitimize_sync_memory (rtx memory)
-{
-  rtx addr = force_reg (Pmode, XEXP (memory, 0));
-  rtx legitimate_memory = gen_rtx_MEM (GET_MODE (memory), addr);
+  arm_emit_load_exclusive (mode, old_out, mem);
 
-  set_mem_alias_set (legitimate_memory, ALIAS_SET_MEMORY_BARRIER);
-  MEM_VOLATILE_P (legitimate_memory) = MEM_VOLATILE_P (memory);
-  return legitimate_memory;
-}
+  switch (code)
+    {
+    case SET:
+      new_out = value;
+      break;
 
-/* An instruction emitter. */
-typedef void (* emit_f) (int label, const char *, rtx *);
+    case NOT:
+      x = gen_rtx_AND (wmode, old_out, value);
+      emit_insn (gen_rtx_SET (VOIDmode, new_out, x));
+      x = gen_rtx_NOT (wmode, new_out);
+      emit_insn (gen_rtx_SET (VOIDmode, new_out, x));
+      break;
 
-/* An instruction emitter that emits via the conventional
-   output_asm_insn.  */
-static void
-arm_emit (int label ATTRIBUTE_UNUSED, const char *pattern, rtx *operands)
-{
-  output_asm_insn (pattern, operands);
-}
+    case MINUS:
+      if (CONST_INT_P (value))
+       {
+         value = GEN_INT (-INTVAL (value));
+         code = PLUS;
+       }
+      /* FALLTHRU */
+
+    case PLUS:
+      if (mode == DImode)
+       {
+         /* DImode plus/minus need to clobber flags.  */
+         /* The adddi3 and subdi3 patterns are incorrectly written so that
+            they require matching operands, even when we could easily support
+            three operands.  Thankfully, this can be fixed up post-splitting,
+            as the individual add+adc patterns do accept three operands and
+            post-reload cprop can make these moves go away.  */
+         emit_move_insn (new_out, old_out);
+         if (code == PLUS)
+           x = gen_adddi3 (new_out, new_out, value);
+         else
+           x = gen_subdi3 (new_out, new_out, value);
+         emit_insn (x);
+         break;
+       }
+      /* FALLTHRU */
+
+    default:
+      x = gen_rtx_fmt_ee (code, wmode, old_out, value);
+      emit_insn (gen_rtx_SET (VOIDmode, new_out, x));
+      break;
+    }
 
-/* Count the number of emitted synchronization instructions.  */
-static unsigned arm_insn_count;
+  arm_emit_store_exclusive (mode, cond, mem, gen_lowpart (mode, new_out));
 
-/* An emitter that counts emitted instructions but does not actually
-   emit instruction into the the instruction stream.  */
-static void
-arm_count (int label,
-          const char *pattern ATTRIBUTE_UNUSED,
-          rtx *operands ATTRIBUTE_UNUSED)
-{
-  if (! label)
-    ++ arm_insn_count;
+  x = gen_rtx_NE (VOIDmode, cond, const0_rtx);
+  emit_unlikely_jump (gen_cbranchsi4 (x, cond, const0_rtx, label));
+
+  arm_post_atomic_barrier (model);
 }
+\f
+#define MAX_VECT_LEN 16
 
-/* Construct a pattern using conventional output formatting and feed
-   it to output_asm_insn.  Provides a mechanism to construct the
-   output pattern on the fly.  Note the hard limit on the pattern
-   buffer size.  */
-static void ATTRIBUTE_PRINTF_4
-arm_output_asm_insn (emit_f emit, int label, rtx *operands,
-                    const char *pattern, ...)
+struct expand_vec_perm_d
 {
-  va_list ap;
-  char buffer[256];
+  rtx target, op0, op1;
+  unsigned char perm[MAX_VECT_LEN];
+  enum machine_mode vmode;
+  unsigned char nelt;
+  bool one_vector_p;
+  bool testing_p;
+};
 
-  va_start (ap, pattern);
-  vsprintf (buffer, pattern, ap);
-  va_end (ap);
-  emit (label, buffer, operands);
-}
+/* Generate a variable permutation.  */
 
-/* Emit the memory barrier instruction, if any, provided by this
-   target to a specified emitter.  */
 static void
-arm_process_output_memory_barrier (emit_f emit, rtx *operands)
+arm_expand_vec_perm_1 (rtx target, rtx op0, rtx op1, rtx sel)
 {
-  if (TARGET_HAVE_DMB)
-    {
-      /* Note we issue a system level barrier. We should consider
-         issuing a inner shareabilty zone barrier here instead, ie.
-         "DMB ISH".  */
-      emit (0, "dmb\tsy", operands);
-      return;
-    }
+  enum machine_mode vmode = GET_MODE (target);
+  bool one_vector_p = rtx_equal_p (op0, op1);
+
+  gcc_checking_assert (vmode == V8QImode || vmode == V16QImode);
+  gcc_checking_assert (GET_MODE (op0) == vmode);
+  gcc_checking_assert (GET_MODE (op1) == vmode);
+  gcc_checking_assert (GET_MODE (sel) == vmode);
+  gcc_checking_assert (TARGET_NEON);
 
-  if (TARGET_HAVE_DMB_MCR)
+  if (one_vector_p)
     {
-      emit (0, "mcr\tp15, 0, r0, c7, c10, 5", operands);
-      return;
+      if (vmode == V8QImode)
+       emit_insn (gen_neon_vtbl1v8qi (target, op0, sel));
+      else
+       emit_insn (gen_neon_vtbl1v16qi (target, op0, sel));
     }
+  else
+    {
+      rtx pair;
 
-  gcc_unreachable ();
+      if (vmode == V8QImode)
+       {
+         pair = gen_reg_rtx (V16QImode);
+         emit_insn (gen_neon_vcombinev8qi (pair, op0, op1));
+         pair = gen_lowpart (TImode, pair);
+         emit_insn (gen_neon_vtbl2v8qi (target, pair, sel));
+       }
+      else
+       {
+         pair = gen_reg_rtx (OImode);
+         emit_insn (gen_neon_vcombinev16qi (pair, op0, op1));
+         emit_insn (gen_neon_vtbl2v16qi (target, pair, sel));
+       }
+    }
 }
 
-/* Emit the memory barrier instruction, if any, provided by this
-   target.  */
-const char *
-arm_output_memory_barrier (rtx *operands)
+void
+arm_expand_vec_perm (rtx target, rtx op0, rtx op1, rtx sel)
 {
-  arm_process_output_memory_barrier (arm_emit, operands);
-  return "";
+  enum machine_mode vmode = GET_MODE (target);
+  unsigned int i, nelt = GET_MODE_NUNITS (vmode);
+  bool one_vector_p = rtx_equal_p (op0, op1);
+  rtx rmask[MAX_VECT_LEN], mask;
+
+  /* TODO: ARM's VTBL indexing is little-endian.  In order to handle GCC's
+     numbering of elements for big-endian, we must reverse the order.  */
+  gcc_checking_assert (!BYTES_BIG_ENDIAN);
+
+  /* The VTBL instruction does not use a modulo index, so we must take care
+     of that ourselves.  */
+  mask = GEN_INT (one_vector_p ? nelt - 1 : 2 * nelt - 1);
+  for (i = 0; i < nelt; ++i)
+    rmask[i] = mask;
+  mask = gen_rtx_CONST_VECTOR (vmode, gen_rtvec_v (nelt, rmask));
+  sel = expand_simple_binop (vmode, AND, sel, mask, NULL, 0, OPTAB_LIB_WIDEN);
+
+  arm_expand_vec_perm_1 (target, op0, op1, sel);
 }
 
-/* Helper to figure out the instruction suffix required on ldrex/strex
-   for operations on an object of the specified mode.  */
-static const char *
-arm_ldrex_suffix (enum machine_mode mode)
+/* Generate or test for an insn that supports a constant permutation.  */
+
+/* Recognize patterns for the VUZP insns.  */
+
+static bool
+arm_evpc_neon_vuzp (struct expand_vec_perm_d *d)
 {
-  switch (mode)
+  unsigned int i, odd, mask, nelt = d->nelt;
+  rtx out0, out1, in0, in1, x;
+  rtx (*gen)(rtx, rtx, rtx, rtx);
+
+  if (GET_MODE_UNIT_SIZE (d->vmode) >= 8)
+    return false;
+
+  /* Note that these are little-endian tests.  Adjust for big-endian later.  */
+  if (d->perm[0] == 0)
+    odd = 0;
+  else if (d->perm[0] == 1)
+    odd = 1;
+  else
+    return false;
+  mask = (d->one_vector_p ? nelt - 1 : 2 * nelt - 1);
+
+  for (i = 0; i < nelt; i++)
+    {
+      unsigned elt = (i * 2 + odd) & mask;
+      if (d->perm[i] != elt)
+       return false;
+    }
+
+  /* Success!  */
+  if (d->testing_p)
+    return true;
+
+  switch (d->vmode)
     {
-    case QImode: return "b";
-    case HImode: return "h";
-    case SImode: return "";
-    case DImode: return "d";
+    case V16QImode: gen = gen_neon_vuzpv16qi_internal; break;
+    case V8QImode:  gen = gen_neon_vuzpv8qi_internal;  break;
+    case V8HImode:  gen = gen_neon_vuzpv8hi_internal;  break;
+    case V4HImode:  gen = gen_neon_vuzpv4hi_internal;  break;
+    case V4SImode:  gen = gen_neon_vuzpv4si_internal;  break;
+    case V2SImode:  gen = gen_neon_vuzpv2si_internal;  break;
+    case V2SFmode:  gen = gen_neon_vuzpv2sf_internal;  break;
+    case V4SFmode:  gen = gen_neon_vuzpv4sf_internal;  break;
     default:
       gcc_unreachable ();
     }
-  return "";
-}
-
-/* Emit an ldrex{b,h,d, } instruction appropriate for the specified
-   mode.  */
-static void
-arm_output_ldrex (emit_f emit,
-                 enum machine_mode mode,
-                 rtx target,
-                 rtx memory)
-{
-  const char *suffix = arm_ldrex_suffix (mode);
-  rtx operands[2];
 
-  operands[0] = target;
-  operands[1] = memory;
-  arm_output_asm_insn (emit, 0, operands, "ldrex%s\t%%0, %%C1", suffix);
-}
+  in0 = d->op0;
+  in1 = d->op1;
+  if (BYTES_BIG_ENDIAN)
+    {
+      x = in0, in0 = in1, in1 = x;
+      odd = !odd;
+    }
 
-/* Emit a strex{b,h,d, } instruction appropriate for the specified
-   mode.  */
-static void
-arm_output_strex (emit_f emit,
-                 enum machine_mode mode,
-                 const char *cc,
-                 rtx result,
-                 rtx value,
-                 rtx memory)
-{
-  const char *suffix = arm_ldrex_suffix (mode);
-  rtx operands[3];
+  out0 = d->target;
+  out1 = gen_reg_rtx (d->vmode);
+  if (odd)
+    x = out0, out0 = out1, out1 = x;
 
-  operands[0] = result;
-  operands[1] = value;
-  operands[2] = memory;
-  arm_output_asm_insn (emit, 0, operands, "strex%s%s\t%%0, %%1, %%C2", suffix,
-                      cc);
+  emit_insn (gen (out0, in0, in1, out1));
+  return true;
 }
 
-/* Helper to emit a two operand instruction.  */
-static void
-arm_output_op2 (emit_f emit, const char *mnemonic, rtx d, rtx s)
-{
-  rtx operands[2];
-
-  operands[0] = d;
-  operands[1] = s;
-  arm_output_asm_insn (emit, 0, operands, "%s\t%%0, %%1", mnemonic);
-}
+/* Recognize patterns for the VZIP insns.  */
 
-/* Helper to emit a three operand instruction.  */
-static void
-arm_output_op3 (emit_f emit, const char *mnemonic, rtx d, rtx a, rtx b)
+static bool
+arm_evpc_neon_vzip (struct expand_vec_perm_d *d)
 {
-  rtx operands[3];
-
-  operands[0] = d;
-  operands[1] = a;
-  operands[2] = b;
-  arm_output_asm_insn (emit, 0, operands, "%s\t%%0, %%1, %%2", mnemonic);
-}
+  unsigned int i, high, mask, nelt = d->nelt;
+  rtx out0, out1, in0, in1, x;
+  rtx (*gen)(rtx, rtx, rtx, rtx);
 
-/* Emit a load store exclusive synchronization loop.
+  if (GET_MODE_UNIT_SIZE (d->vmode) >= 8)
+    return false;
 
-   do
-     old_value = [mem]
-     if old_value != required_value
-       break;
-     t1 = sync_op (old_value, new_value)
-     [mem] = t1, t2 = [0|1]
-   while ! t2
+  /* Note that these are little-endian tests.  Adjust for big-endian later.  */
+  high = nelt / 2;
+  if (d->perm[0] == high)
+    ;
+  else if (d->perm[0] == 0)
+    high = 0;
+  else
+    return false;
+  mask = (d->one_vector_p ? nelt - 1 : 2 * nelt - 1);
 
-   Note:
-     t1 == t2 is not permitted
-     t1 == old_value is permitted
+  for (i = 0; i < nelt / 2; i++)
+    {
+      unsigned elt = (i + high) & mask;
+      if (d->perm[i * 2] != elt)
+       return false;
+      elt = (elt + nelt) & mask;
+      if (d->perm[i * 2 + 1] != elt)
+       return false;
+    }
 
-   required_value:
+  /* Success!  */
+  if (d->testing_p)
+    return true;
 
-   RTX register or const_int representing the required old_value for
-   the modify to continue, if NULL no comparsion is performed.  */
-static void
-arm_output_sync_loop (emit_f emit,
-                     enum machine_mode mode,
-                     rtx old_value,
-                     rtx memory,
-                     rtx required_value,
-                     rtx new_value,
-                     rtx t1,
-                     rtx t2,
-                     enum attr_sync_op sync_op,
-                     int early_barrier_required)
-{
-  rtx operands[1];
+  switch (d->vmode)
+    {
+    case V16QImode: gen = gen_neon_vzipv16qi_internal; break;
+    case V8QImode:  gen = gen_neon_vzipv8qi_internal;  break;
+    case V8HImode:  gen = gen_neon_vzipv8hi_internal;  break;
+    case V4HImode:  gen = gen_neon_vzipv4hi_internal;  break;
+    case V4SImode:  gen = gen_neon_vzipv4si_internal;  break;
+    case V2SImode:  gen = gen_neon_vzipv2si_internal;  break;
+    case V2SFmode:  gen = gen_neon_vzipv2sf_internal;  break;
+    case V4SFmode:  gen = gen_neon_vzipv4sf_internal;  break;
+    default:
+      gcc_unreachable ();
+    }
 
-  gcc_assert (t1 != t2);
+  in0 = d->op0;
+  in1 = d->op1;
+  if (BYTES_BIG_ENDIAN)
+    {
+      x = in0, in0 = in1, in1 = x;
+      high = !high;
+    }
 
-  if (early_barrier_required)
-    arm_process_output_memory_barrier (emit, NULL);
+  out0 = d->target;
+  out1 = gen_reg_rtx (d->vmode);
+  if (high)
+    x = out0, out0 = out1, out1 = x;
 
-  arm_output_asm_insn (emit, 1, operands, "%sLSYT%%=:", LOCAL_LABEL_PREFIX);
+  emit_insn (gen (out0, in0, in1, out1));
+  return true;
+}
 
-  arm_output_ldrex (emit, mode, old_value, memory);
+/* Recognize patterns for the VREV insns.  */
 
-  if (required_value)
-    {
-      rtx operands[2];
+static bool
+arm_evpc_neon_vrev (struct expand_vec_perm_d *d)
+{
+  unsigned int i, j, diff, nelt = d->nelt;
+  rtx (*gen)(rtx, rtx, rtx);
 
-      operands[0] = old_value;
-      operands[1] = required_value;
-      arm_output_asm_insn (emit, 0, operands, "cmp\t%%0, %%1");
-      arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYB%%=", LOCAL_LABEL_PREFIX);
-    }
+  if (!d->one_vector_p)
+    return false;
 
-  switch (sync_op)
+  diff = d->perm[0];
+  switch (diff)
     {
-    case SYNC_OP_ADD:
-      arm_output_op3 (emit, "add", t1, old_value, new_value);
+    case 7:
+      switch (d->vmode)
+       {
+       case V16QImode: gen = gen_neon_vrev64v16qi; break;
+       case V8QImode:  gen = gen_neon_vrev64v8qi;  break;
+       default:
+         return false;
+       }
       break;
-
-    case SYNC_OP_SUB:
-      arm_output_op3 (emit, "sub", t1, old_value, new_value);
+    case 3:
+      switch (d->vmode)
+       {
+       case V16QImode: gen = gen_neon_vrev32v16qi; break;
+       case V8QImode:  gen = gen_neon_vrev32v8qi;  break;
+       case V8HImode:  gen = gen_neon_vrev64v8hi;  break;
+       case V4HImode:  gen = gen_neon_vrev64v4hi;  break;
+       default:
+         return false;
+       }
       break;
-
-    case SYNC_OP_IOR:
-      arm_output_op3 (emit, "orr", t1, old_value, new_value);
+    case 1:
+      switch (d->vmode)
+       {
+       case V16QImode: gen = gen_neon_vrev16v16qi; break;
+       case V8QImode:  gen = gen_neon_vrev16v8qi;  break;
+       case V8HImode:  gen = gen_neon_vrev32v8hi;  break;
+       case V4HImode:  gen = gen_neon_vrev32v4hi;  break;
+       case V4SImode:  gen = gen_neon_vrev64v4si;  break;
+       case V2SImode:  gen = gen_neon_vrev64v2si;  break;
+       case V4SFmode:  gen = gen_neon_vrev64v4sf;  break;
+       case V2SFmode:  gen = gen_neon_vrev64v2sf;  break;
+       default:
+         return false;
+       }
       break;
+    default:
+      return false;
+    }
 
-    case SYNC_OP_XOR:
-      arm_output_op3 (emit, "eor", t1, old_value, new_value);
-      break;
+  for (i = 0; i < nelt; i += diff)
+    for (j = 0; j <= diff; j += 1)
+      if (d->perm[i + j] != i + diff - j)
+       return false;
 
-    case SYNC_OP_AND:
-      arm_output_op3 (emit,"and", t1, old_value, new_value);
-      break;
+  /* Success! */
+  if (d->testing_p)
+    return true;
 
-    case SYNC_OP_NAND:
-      arm_output_op3 (emit, "and", t1, old_value, new_value);
-      arm_output_op2 (emit, "mvn", t1, t1);
-      break;
+  /* ??? The third operand is an artifact of the builtin infrastructure
+     and is ignored by the actual instruction.  */
+  emit_insn (gen (d->target, d->op0, const0_rtx));
+  return true;
+}
 
-    case SYNC_OP_NONE:
-      t1 = new_value;
-      break;
-    }
+/* Recognize patterns for the VTRN insns.  */
 
-  if (t2)
-    {
-       arm_output_strex (emit, mode, "", t2, t1, memory);
-       operands[0] = t2;
-       arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
-       arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=",
-                           LOCAL_LABEL_PREFIX);
-    }
-  else
-    {
-      /* Use old_value for the return value because for some operations
-        the old_value can easily be restored.  This saves one register.  */
-      arm_output_strex (emit, mode, "", old_value, t1, memory);
-      operands[0] = old_value;
-      arm_output_asm_insn (emit, 0, operands, "teq\t%%0, #0");
-      arm_output_asm_insn (emit, 0, operands, "bne\t%sLSYT%%=",
-                          LOCAL_LABEL_PREFIX);
+static bool
+arm_evpc_neon_vtrn (struct expand_vec_perm_d *d)
+{
+  unsigned int i, odd, mask, nelt = d->nelt;
+  rtx out0, out1, in0, in1, x;
+  rtx (*gen)(rtx, rtx, rtx, rtx);
 
-      switch (sync_op)
-       {
-       case SYNC_OP_ADD:
-         arm_output_op3 (emit, "sub", old_value, t1, new_value);
-         break;
+  if (GET_MODE_UNIT_SIZE (d->vmode) >= 8)
+    return false;
 
-       case SYNC_OP_SUB:
-         arm_output_op3 (emit, "add", old_value, t1, new_value);
-         break;
+  /* Note that these are little-endian tests.  Adjust for big-endian later.  */
+  if (d->perm[0] == 0)
+    odd = 0;
+  else if (d->perm[0] == 1)
+    odd = 1;
+  else
+    return false;
+  mask = (d->one_vector_p ? nelt - 1 : 2 * nelt - 1);
 
-       case SYNC_OP_XOR:
-         arm_output_op3 (emit, "eor", old_value, t1, new_value);
-         break;
+  for (i = 0; i < nelt; i += 2)
+    {
+      if (d->perm[i] != i + odd)
+       return false;
+      if (d->perm[i + 1] != ((i + nelt + odd) & mask))
+       return false;
+    }
 
-       case SYNC_OP_NONE:
-         arm_output_op2 (emit, "mov", old_value, required_value);
-         break;
+  /* Success!  */
+  if (d->testing_p)
+    return true;
 
-       default:
-         gcc_unreachable ();
-       }
+  switch (d->vmode)
+    {
+    case V16QImode: gen = gen_neon_vtrnv16qi_internal; break;
+    case V8QImode:  gen = gen_neon_vtrnv8qi_internal;  break;
+    case V8HImode:  gen = gen_neon_vtrnv8hi_internal;  break;
+    case V4HImode:  gen = gen_neon_vtrnv4hi_internal;  break;
+    case V4SImode:  gen = gen_neon_vtrnv4si_internal;  break;
+    case V2SImode:  gen = gen_neon_vtrnv2si_internal;  break;
+    case V2SFmode:  gen = gen_neon_vtrnv2sf_internal;  break;
+    case V4SFmode:  gen = gen_neon_vtrnv4sf_internal;  break;
+    default:
+      gcc_unreachable ();
     }
 
-  arm_process_output_memory_barrier (emit, NULL);
-  arm_output_asm_insn (emit, 1, operands, "%sLSYB%%=:", LOCAL_LABEL_PREFIX);
-}
+  in0 = d->op0;
+  in1 = d->op1;
+  if (BYTES_BIG_ENDIAN)
+    {
+      x = in0, in0 = in1, in1 = x;
+      odd = !odd;
+    }
 
-static rtx
-arm_get_sync_operand (rtx *operands, int index, rtx default_value)
-{
-  if (index > 0)
-    default_value = operands[index - 1];
+  out0 = d->target;
+  out1 = gen_reg_rtx (d->vmode);
+  if (odd)
+    x = out0, out0 = out1, out1 = x;
 
-  return default_value;
+  emit_insn (gen (out0, in0, in1, out1));
+  return true;
 }
 
-#define FETCH_SYNC_OPERAND(NAME, DEFAULT) \
-  arm_get_sync_operand (operands, (int) get_attr_sync_##NAME (insn), DEFAULT);
+/* The NEON VTBL instruction is a fully variable permuation that's even
+   stronger than what we expose via VEC_PERM_EXPR.  What it doesn't do
+   is mask the index operand as VEC_PERM_EXPR requires.  Therefore we
+   can do slightly better by expanding this as a constant where we don't
+   have to apply a mask.  */
 
-/* Extract the operands for a synchroniztion instruction from the
-   instructions attributes and emit the instruction.  */
-static void
-arm_process_output_sync_insn (emit_f emit, rtx insn, rtx *operands)
+static bool
+arm_evpc_neon_vtbl (struct expand_vec_perm_d *d)
 {
-  rtx result, memory, required_value, new_value, t1, t2;
-  int early_barrier;
-  enum machine_mode mode;
-  enum attr_sync_op sync_op;
-
-  result = FETCH_SYNC_OPERAND(result, 0);
-  memory = FETCH_SYNC_OPERAND(memory, 0);
-  required_value = FETCH_SYNC_OPERAND(required_value, 0);
-  new_value = FETCH_SYNC_OPERAND(new_value, 0);
-  t1 = FETCH_SYNC_OPERAND(t1, 0);
-  t2 = FETCH_SYNC_OPERAND(t2, 0);
-  early_barrier =
-    get_attr_sync_release_barrier (insn) == SYNC_RELEASE_BARRIER_YES;
-  sync_op = get_attr_sync_op (insn);
-  mode = GET_MODE (memory);
+  rtx rperm[MAX_VECT_LEN], sel;
+  enum machine_mode vmode = d->vmode;
+  unsigned int i, nelt = d->nelt;
 
-  arm_output_sync_loop (emit, mode, result, memory, required_value,
-                       new_value, t1, t2, sync_op, early_barrier);
-}
-
-/* Emit a synchronization instruction loop.  */
-const char *
-arm_output_sync_insn (rtx insn, rtx *operands)
-{
-  arm_process_output_sync_insn (arm_emit, insn, operands);
-  return "";
-}
+  /* TODO: ARM's VTBL indexing is little-endian.  In order to handle GCC's
+     numbering of elements for big-endian, we must reverse the order.  */
+  if (BYTES_BIG_ENDIAN)
+    return false;
 
-/* Count the number of machine instruction that will be emitted for a
-   synchronization instruction.  Note that the emitter used does not
-   emit instructions, it just counts instructions being carefull not
-   to count labels.  */
-unsigned int
-arm_sync_loop_insns (rtx insn, rtx *operands)
-{
-  arm_insn_count = 0;
-  arm_process_output_sync_insn (arm_count, insn, operands);
-  return arm_insn_count;
-}
+  if (d->testing_p)
+    return true;
 
-/* Helper to call a target sync instruction generator, dealing with
-   the variation in operands required by the different generators.  */
-static rtx
-arm_call_generator (struct arm_sync_generator *generator, rtx old_value,
-                   rtx memory, rtx required_value, rtx new_value)
-{
-  switch (generator->op)
-    {
-    case arm_sync_generator_omn:
-      gcc_assert (! required_value);
-      return generator->u.omn (old_value, memory, new_value);
+  /* Generic code will try constant permutation twice.  Once with the
+     original mode and again with the elements lowered to QImode.
+     So wait and don't do the selector expansion ourselves.  */
+  if (vmode != V8QImode && vmode != V16QImode)
+    return false;
 
-    case arm_sync_generator_omrn:
-      gcc_assert (required_value);
-      return generator->u.omrn (old_value, memory, required_value, new_value);
-    }
+  for (i = 0; i < nelt; ++i)
+    rperm[i] = GEN_INT (d->perm[i]);
+  sel = gen_rtx_CONST_VECTOR (vmode, gen_rtvec_v (nelt, rperm));
+  sel = force_reg (vmode, sel);
 
-  return NULL;
+  arm_expand_vec_perm_1 (d->target, d->op0, d->op1, sel);
+  return true;
 }
 
-/* Expand a synchronization loop. The synchronization loop is expanded
-   as an opaque block of instructions in order to ensure that we do
-   not subsequently get extraneous memory accesses inserted within the
-   critical region. The exclusive access property of ldrex/strex is
-   only guaranteed in there are no intervening memory accesses. */
-void
-arm_expand_sync (enum machine_mode mode,
-                struct arm_sync_generator *generator,
-                rtx target, rtx memory, rtx required_value, rtx new_value)
+static bool
+arm_expand_vec_perm_const_1 (struct expand_vec_perm_d *d)
 {
-  if (target == NULL)
-    target = gen_reg_rtx (mode);
-
-  memory = arm_legitimize_sync_memory (memory);
-  if (mode != SImode)
+  /* The pattern matching functions above are written to look for a small
+     number to begin the sequence (0, 1, N/2).  If we begin with an index
+     from the second operand, we can swap the operands.  */
+  if (d->perm[0] >= d->nelt)
     {
-      rtx load_temp = gen_reg_rtx (SImode);
+      unsigned i, nelt = d->nelt;
+      rtx x;
 
-      if (required_value)
-       required_value = convert_modes (SImode, mode, required_value, true);
+      for (i = 0; i < nelt; ++i)
+       d->perm[i] = (d->perm[i] + nelt) & (2 * nelt - 1);
 
-      new_value = convert_modes (SImode, mode, new_value, true);
-      emit_insn (arm_call_generator (generator, load_temp, memory,
-                                    required_value, new_value));
-      emit_move_insn (target, gen_lowpart (mode, load_temp));
+      x = d->op0;
+      d->op0 = d->op1;
+      d->op1 = x;
     }
-  else
+
+  if (TARGET_NEON)
     {
-      emit_insn (arm_call_generator (generator, target, memory, required_value,
-                                    new_value));
+      if (arm_evpc_neon_vuzp (d))
+       return true;
+      if (arm_evpc_neon_vzip (d))
+       return true;
+      if (arm_evpc_neon_vrev (d))
+       return true;
+      if (arm_evpc_neon_vtrn (d))
+       return true;
+      return arm_evpc_neon_vtbl (d);
     }
+  return false;
 }
 
-static unsigned int
-arm_autovectorize_vector_sizes (void)
-{
-  return TARGET_NEON_VECTORIZE_QUAD ? 16 | 8 : 0;
-}
+/* Expand a vec_perm_const pattern.  */
 
-static bool
-arm_vector_alignment_reachable (const_tree type, bool is_packed)
+bool
+arm_expand_vec_perm_const (rtx target, rtx op0, rtx op1, rtx sel)
 {
-  /* Vectors which aren't in packed structures will not be less aligned than
-     the natural alignment of their element type, so this is safe.  */
-  if (TARGET_NEON && !BYTES_BIG_ENDIAN)
-    return !is_packed;
+  struct expand_vec_perm_d d;
+  int i, nelt, which;
 
-  return default_builtin_vector_alignment_reachable (type, is_packed);
-}
+  d.target = target;
+  d.op0 = op0;
+  d.op1 = op1;
 
-static bool
-arm_builtin_support_vector_misalignment (enum machine_mode mode,
-                                        const_tree type, int misalignment,
-                                        bool is_packed)
-{
-  if (TARGET_NEON && !BYTES_BIG_ENDIAN)
+  d.vmode = GET_MODE (target);
+  gcc_assert (VECTOR_MODE_P (d.vmode));
+  d.nelt = nelt = GET_MODE_NUNITS (d.vmode);
+  d.testing_p = false;
+
+  for (i = which = 0; i < nelt; ++i)
     {
-      HOST_WIDE_INT align = TYPE_ALIGN_UNIT (type);
+      rtx e = XVECEXP (sel, 0, i);
+      int ei = INTVAL (e) & (2 * nelt - 1);
+      which |= (ei < nelt ? 1 : 2);
+      d.perm[i] = ei;
+    }
 
-      if (is_packed)
-        return align == 1;
+  switch (which)
+    {
+    default:
+      gcc_unreachable();
 
-      /* If the misalignment is unknown, we should be able to handle the access
-        so long as it is not to a member of a packed data structure.  */
-      if (misalignment == -1)
-        return true;
+    case 3:
+      d.one_vector_p = false;
+      if (!rtx_equal_p (op0, op1))
+       break;
 
-      /* Return true if the misalignment is a multiple of the natural alignment
-         of the vector's element type.  This is probably always going to be
-        true in practice, since we've already established that this isn't a
-        packed access.  */
-      return ((misalignment % align) == 0);
+      /* The elements of PERM do not suggest that only the first operand
+        is used, but both operands are identical.  Allow easier matching
+        of the permutation by folding the permutation into the single
+        input vector.  */
+      /* FALLTHRU */
+    case 2:
+      for (i = 0; i < nelt; ++i)
+        d.perm[i] &= nelt - 1;
+      d.op0 = op1;
+      d.one_vector_p = true;
+      break;
+
+    case 1:
+      d.op1 = op0;
+      d.one_vector_p = true;
+      break;
     }
-  
-  return default_builtin_support_vector_misalignment (mode, type, misalignment,
-                                                     is_packed);
+
+  return arm_expand_vec_perm_const_1 (&d);
 }
 
-static void
-arm_conditional_register_usage (void)
+/* Implement TARGET_VECTORIZE_VEC_PERM_CONST_OK.  */
+
+static bool
+arm_vectorize_vec_perm_const_ok (enum machine_mode vmode,
+                                const unsigned char *sel)
 {
-  int regno;
+  struct expand_vec_perm_d d;
+  unsigned int i, nelt, which;
+  bool ret;
 
-  if (TARGET_SOFT_FLOAT || TARGET_THUMB1 || !TARGET_FPA)
-    {
-      for (regno = FIRST_FPA_REGNUM;
-          regno <= LAST_FPA_REGNUM; ++regno)
-       fixed_regs[regno] = call_used_regs[regno] = 1;
-    }
+  d.vmode = vmode;
+  d.nelt = nelt = GET_MODE_NUNITS (d.vmode);
+  d.testing_p = true;
+  memcpy (d.perm, sel, nelt);
 
-  if (TARGET_THUMB1 && optimize_size)
+  /* Categorize the set of elements in the selector.  */
+  for (i = which = 0; i < nelt; ++i)
     {
-      /* When optimizing for size on Thumb-1, it's better not
-        to use the HI regs, because of the overhead of
-        stacking them.  */
-      for (regno = FIRST_HI_REGNUM;
-          regno <= LAST_HI_REGNUM; ++regno)
-       fixed_regs[regno] = call_used_regs[regno] = 1;
+      unsigned char e = d.perm[i];
+      gcc_assert (e < 2 * nelt);
+      which |= (e < nelt ? 1 : 2);
     }
 
-  /* The link register can be clobbered by any branch insn,
-     but we have no way to track that at present, so mark
-     it as unavailable.  */
-  if (TARGET_THUMB1)
-    fixed_regs[LR_REGNUM] = call_used_regs[LR_REGNUM] = 1;
+  /* For all elements from second vector, fold the elements to first.  */
+  if (which == 2)
+    for (i = 0; i < nelt; ++i)
+      d.perm[i] -= nelt;
 
-  if (TARGET_32BIT && TARGET_HARD_FLOAT)
-    {
-      if (TARGET_MAVERICK)
-       {
-         for (regno = FIRST_FPA_REGNUM;
-              regno <= LAST_FPA_REGNUM; ++ regno)
-           fixed_regs[regno] = call_used_regs[regno] = 1;
-         for (regno = FIRST_CIRRUS_FP_REGNUM;
-              regno <= LAST_CIRRUS_FP_REGNUM; ++ regno)
-           {
-             fixed_regs[regno] = 0;
-             call_used_regs[regno] = regno < FIRST_CIRRUS_FP_REGNUM + 4;
-           }
-       }
-      if (TARGET_VFP)
-       {
-         /* VFPv3 registers are disabled when earlier VFP
-            versions are selected due to the definition of
-            LAST_VFP_REGNUM.  */
-         for (regno = FIRST_VFP_REGNUM;
-              regno <= LAST_VFP_REGNUM; ++ regno)
-           {
-             fixed_regs[regno] = 0;
-             call_used_regs[regno] = regno < FIRST_VFP_REGNUM + 16
-               || regno >= FIRST_VFP_REGNUM + 32;
-           }
-       }
-    }
+  /* Check whether the mask can be applied to the vector type.  */
+  d.one_vector_p = (which != 3);
 
-  if (TARGET_REALLY_IWMMXT)
-    {
-      regno = FIRST_IWMMXT_GR_REGNUM;
-      /* The 2002/10/09 revision of the XScale ABI has wCG0
-         and wCG1 as call-preserved registers.  The 2002/11/21
-         revision changed this so that all wCG registers are
-         scratch registers.  */
-      for (regno = FIRST_IWMMXT_GR_REGNUM;
-          regno <= LAST_IWMMXT_GR_REGNUM; ++ regno)
-       fixed_regs[regno] = 0;
-      /* The XScale ABI has wR0 - wR9 as scratch registers,
-        the rest as call-preserved registers.  */
-      for (regno = FIRST_IWMMXT_REGNUM;
-          regno <= LAST_IWMMXT_REGNUM; ++ regno)
-       {
-         fixed_regs[regno] = 0;
-         call_used_regs[regno] = regno < FIRST_IWMMXT_REGNUM + 10;
-       }
-    }
+  d.target = gen_raw_REG (d.vmode, LAST_VIRTUAL_REGISTER + 1);
+  d.op1 = d.op0 = gen_raw_REG (d.vmode, LAST_VIRTUAL_REGISTER + 2);
+  if (!d.one_vector_p)
+    d.op1 = gen_raw_REG (d.vmode, LAST_VIRTUAL_REGISTER + 3);
 
-  if ((unsigned) PIC_OFFSET_TABLE_REGNUM != INVALID_REGNUM)
-    {
-      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
-      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
-    }
-  else if (TARGET_APCS_STACK)
-    {
-      fixed_regs[10]     = 1;
-      call_used_regs[10] = 1;
-    }
-  /* -mcaller-super-interworking reserves r11 for calls to
-     _interwork_r11_call_via_rN().  Making the register global
-     is an easy way of ensuring that it remains valid for all
-     calls.  */
-  if (TARGET_APCS_FRAME || TARGET_CALLER_INTERWORKING
-      || TARGET_TPCS_FRAME || TARGET_TPCS_LEAF_FRAME)
-    {
-      fixed_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
-      call_used_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
-      if (TARGET_CALLER_INTERWORKING)
-       global_regs[ARM_HARD_FRAME_POINTER_REGNUM] = 1;
-    }
-  SUBTARGET_CONDITIONAL_REGISTER_USAGE
-}
+  start_sequence ();
+  ret = arm_expand_vec_perm_const_1 (&d);
+  end_sequence ();
 
-static reg_class_t
-arm_preferred_rename_class (reg_class_t rclass)
-{
-  /* Thumb-2 instructions using LO_REGS may be smaller than instructions
-     using GENERIC_REGS.  During register rename pass, we prefer LO_REGS,
-     and code size can be reduced.  */
-  if (TARGET_THUMB2 && rclass == GENERAL_REGS)
-    return LO_REGS;
-  else
-    return NO_REGS;
+  return ret;
 }
 
+\f
 #include "gt-arm.h"