hooks.c (hook_int_rtx_mode_as_bool_0): New function.
[platform/upstream/gcc.git] / gcc / config / rs6000 / rs6000.c
index 39c0714..430125f 100644 (file)
@@ -1,8 +1,5 @@
 /* Subroutines used for code generation on IBM RS/6000.
-   Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
-   2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
-   2012
-   Free Software Foundation, Inc.
+   Copyright (C) 1991-2012 Free Software Foundation, Inc.
    Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
    This file is part of GCC.
@@ -40,8 +37,8 @@
 #include "except.h"
 #include "function.h"
 #include "output.h"
+#include "dbxout.h"
 #include "basic-block.h"
-#include "integrate.h"
 #include "diagnostic-core.h"
 #include "toplev.h"
 #include "ggc.h"
@@ -52,7 +49,6 @@
 #include "common/common-target.h"
 #include "langhooks.h"
 #include "reload.h"
-#include "cfglayout.h"
 #include "cfgloop.h"
 #include "sched-int.h"
 #include "gimple.h"
@@ -61,6 +57,7 @@
 #include "params.h"
 #include "tm-constrs.h"
 #include "opts.h"
+#include "tree-vectorizer.h"
 #if TARGET_XCOFF
 #include "xcoffout.h"  /* get declarations of xcoff_*_section_name */
 #endif
@@ -355,44 +352,6 @@ struct processor_costs size64_cost = {
   0,
 };
 
-/* Instruction costs on RIOS1 processors.  */
-static const
-struct processor_costs rios1_cost = {
-  COSTS_N_INSNS (5),    /* mulsi */
-  COSTS_N_INSNS (4),    /* mulsi_const */
-  COSTS_N_INSNS (3),    /* mulsi_const9 */
-  COSTS_N_INSNS (5),    /* muldi */
-  COSTS_N_INSNS (19),   /* divsi */
-  COSTS_N_INSNS (19),   /* divdi */
-  COSTS_N_INSNS (2),    /* fp */
-  COSTS_N_INSNS (2),    /* dmul */
-  COSTS_N_INSNS (19),   /* sdiv */
-  COSTS_N_INSNS (19),   /* ddiv */
-  128,                 /* cache line size */
-  64,                  /* l1 cache */
-  512,                 /* l2 cache */
-  0,                   /* streams */
-};
-
-/* Instruction costs on RIOS2 processors.  */
-static const
-struct processor_costs rios2_cost = {
-  COSTS_N_INSNS (2),    /* mulsi */
-  COSTS_N_INSNS (2),    /* mulsi_const */
-  COSTS_N_INSNS (2),    /* mulsi_const9 */
-  COSTS_N_INSNS (2),    /* muldi */
-  COSTS_N_INSNS (13),   /* divsi */
-  COSTS_N_INSNS (13),   /* divdi */
-  COSTS_N_INSNS (2),    /* fp */
-  COSTS_N_INSNS (2),    /* dmul */
-  COSTS_N_INSNS (17),   /* sdiv */
-  COSTS_N_INSNS (17),   /* ddiv */
-  256,                 /* cache line size */
-  256,                 /* l1 cache */
-  1024,                        /* l2 cache */
-  0,                   /* streams */
-};
-
 /* Instruction costs on RS64A processors.  */
 static const
 struct processor_costs rs64a_cost = {
@@ -755,6 +714,44 @@ struct processor_costs ppce500mc64_cost = {
   1,                   /* prefetch streams /*/
 };
 
+/* Instruction costs on PPCE5500 processors.  */
+static const
+struct processor_costs ppce5500_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (5),    /* mulsi_const */
+  COSTS_N_INSNS (4),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (14),   /* divsi */
+  COSTS_N_INSNS (14),   /* divdi */
+  COSTS_N_INSNS (7),    /* fp */
+  COSTS_N_INSNS (10),   /* dmul */
+  COSTS_N_INSNS (36),   /* sdiv */
+  COSTS_N_INSNS (66),   /* ddiv */
+  64,                  /* cache line size */
+  32,                  /* l1 cache */
+  128,                 /* l2 cache */
+  1,                   /* prefetch streams /*/
+};
+
+/* Instruction costs on PPCE6500 processors.  */
+static const
+struct processor_costs ppce6500_cost = {
+  COSTS_N_INSNS (5),    /* mulsi */
+  COSTS_N_INSNS (5),    /* mulsi_const */
+  COSTS_N_INSNS (4),    /* mulsi_const9 */
+  COSTS_N_INSNS (5),    /* muldi */
+  COSTS_N_INSNS (14),   /* divsi */
+  COSTS_N_INSNS (14),   /* divdi */
+  COSTS_N_INSNS (7),    /* fp */
+  COSTS_N_INSNS (10),   /* dmul */
+  COSTS_N_INSNS (36),   /* sdiv */
+  COSTS_N_INSNS (66),   /* ddiv */
+  64,                  /* cache line size */
+  32,                  /* l1 cache */
+  128,                 /* l2 cache */
+  1,                   /* prefetch streams /*/
+};
+
 /* Instruction costs on AppliedMicro Titan processors.  */
 static const
 struct processor_costs titan_cost = {
@@ -920,315 +917,103 @@ static const struct rs6000_builtin_info_type rs6000_builtin_info[] =
 static tree (*rs6000_veclib_handler) (tree, tree, tree);
 
 \f
-static bool rs6000_function_ok_for_sibcall (tree, tree);
-static const char *rs6000_invalid_within_doloop (const_rtx);
-static bool rs6000_legitimate_address_p (enum machine_mode, rtx, bool);
 static bool rs6000_debug_legitimate_address_p (enum machine_mode, rtx, bool);
-static rtx rs6000_generate_compare (rtx, enum machine_mode);
-static void rs6000_emit_stack_tie (void);
 static bool spe_func_has_64bit_regs_p (void);
-static rtx gen_frame_mem_offset (enum machine_mode, rtx, int);
-static unsigned rs6000_hash_constant (rtx);
-static unsigned toc_hash_function (const void *);
-static int toc_hash_eq (const void *, const void *);
-static bool reg_offset_addressing_ok_p (enum machine_mode);
-static bool virtual_stack_registers_memory_p (rtx);
-static bool constant_pool_expr_p (rtx);
-static bool legitimate_small_data_p (enum machine_mode, rtx);
-static bool legitimate_lo_sum_address_p (enum machine_mode, rtx, int);
 static struct machine_function * rs6000_init_machine_status (void);
-static bool rs6000_assemble_integer (rtx, unsigned int, int);
-static bool no_global_regs_above (int, bool);
-#if defined (HAVE_GAS_HIDDEN) && !TARGET_MACHO
-static void rs6000_assemble_visibility (tree, int);
-#endif
 static int rs6000_ra_ever_killed (void);
-static bool rs6000_attribute_takes_identifier_p (const_tree);
 static tree rs6000_handle_longcall_attribute (tree *, tree, tree, int, bool *);
 static tree rs6000_handle_altivec_attribute (tree *, tree, tree, int, bool *);
-static bool rs6000_ms_bitfield_layout_p (const_tree);
 static tree rs6000_handle_struct_attribute (tree *, tree, tree, int, bool *);
-static void rs6000_eliminate_indexed_memrefs (rtx operands[2]);
-static const char *rs6000_mangle_type (const_tree);
-static void rs6000_set_default_type_attributes (tree);
-static rtx rs6000_savres_routine_sym (rs6000_stack_t *, bool, bool, bool);
-static rtx rs6000_emit_stack_reset (rs6000_stack_t *, rtx, rtx, int, bool);
-static bool rs6000_reg_live_or_pic_offset_p (int);
 static tree rs6000_builtin_vectorized_libmass (tree, tree, tree);
-static tree rs6000_builtin_vectorized_function (tree, tree, tree);
-static void rs6000_restore_saved_cr (rtx, int);
-static bool rs6000_output_addr_const_extra (FILE *, rtx);
-static void rs6000_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void rs6000_output_function_epilogue (FILE *, HOST_WIDE_INT);
-static void rs6000_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT,
-                                   tree);
 static rtx rs6000_emit_set_long_const (rtx, HOST_WIDE_INT, HOST_WIDE_INT);
-static bool rs6000_return_in_memory (const_tree, const_tree);
-static rtx rs6000_function_value (const_tree, const_tree, bool);
-static void rs6000_file_start (void);
-#if TARGET_ELF
-static int rs6000_elf_reloc_rw_mask (void);
-static void rs6000_elf_asm_out_constructor (rtx, int) ATTRIBUTE_UNUSED;
-static void rs6000_elf_asm_out_destructor (rtx, int) ATTRIBUTE_UNUSED;
-static void rs6000_elf_file_end (void) ATTRIBUTE_UNUSED;
-static void rs6000_elf_asm_init_sections (void);
-static section *rs6000_elf_select_rtx_section (enum machine_mode, rtx,
-                                              unsigned HOST_WIDE_INT);
-static void rs6000_elf_encode_section_info (tree, rtx, int)
-     ATTRIBUTE_UNUSED;
-#endif
-static bool rs6000_use_blocks_for_constant_p (enum machine_mode, const_rtx);
-static void rs6000_alloc_sdmode_stack_slot (void);
-static void rs6000_instantiate_decls (void);
-#if TARGET_XCOFF
-static void rs6000_xcoff_asm_output_anchor (rtx);
-static void rs6000_xcoff_asm_globalize_label (FILE *, const char *);
-static void rs6000_xcoff_asm_init_sections (void);
-static int rs6000_xcoff_reloc_rw_mask (void);
-static void rs6000_xcoff_asm_named_section (const char *, unsigned int, tree);
-static section *rs6000_xcoff_select_section (tree, int,
-                                            unsigned HOST_WIDE_INT);
-static void rs6000_xcoff_unique_section (tree, int);
-static section *rs6000_xcoff_select_rtx_section
-  (enum machine_mode, rtx, unsigned HOST_WIDE_INT);
-static const char * rs6000_xcoff_strip_name_encoding (const char *);
-static unsigned int rs6000_xcoff_section_type_flags (tree, const char *, int);
-static void rs6000_xcoff_file_start (void);
-static void rs6000_xcoff_file_end (void);
-#endif
-static int rs6000_variable_issue (FILE *, int, rtx, int);
-static int rs6000_register_move_cost (enum machine_mode,
-                                     reg_class_t, reg_class_t);
 static int rs6000_memory_move_cost (enum machine_mode, reg_class_t, bool);
-static bool rs6000_rtx_costs (rtx, int, int, int, int *, bool);
 static bool rs6000_debug_rtx_costs (rtx, int, int, int, int *, bool);
-static int rs6000_debug_address_cost (rtx, bool);
-static int rs6000_adjust_cost (rtx, rtx, rtx, int);
+static int rs6000_debug_address_cost (rtx, enum machine_mode, addr_space_t,
+                                     bool);
 static int rs6000_debug_adjust_cost (rtx, rtx, rtx, int);
-static void rs6000_sched_init (FILE *, int, int);
 static bool is_microcoded_insn (rtx);
 static bool is_nonpipeline_insn (rtx);
 static bool is_cracked_insn (rtx);
-static bool is_branch_slot_insn (rtx);
-static bool is_load_insn (rtx);
-static rtx get_store_dest (rtx pat);
-static bool is_store_insn (rtx);
+static bool is_load_insn (rtx, rtx *);
+static bool is_store_insn (rtx, rtx *);
 static bool set_to_load_agen (rtx,rtx);
-static bool adjacent_mem_locations (rtx,rtx);
-static int rs6000_adjust_priority (rtx, int);
-static int rs6000_issue_rate (void);
-static bool rs6000_is_costly_dependence (dep_t, int, int);
-static rtx get_next_active_insn (rtx, rtx);
 static bool insn_terminates_group_p (rtx , enum group_termination);
 static bool insn_must_be_first_in_group (rtx);
 static bool insn_must_be_last_in_group (rtx);
-static bool is_costly_group (rtx *, rtx);
-static int force_new_group (int, FILE *, rtx *, rtx, bool *, int, int *);
-static int redefine_groups (FILE *, int, rtx, rtx);
-static int pad_groups (FILE *, int, rtx, rtx);
-static void rs6000_sched_finish (FILE *, int);
-static int rs6000_sched_reorder (FILE *, int, rtx *, int *, int);
-static int rs6000_sched_reorder2 (FILE *, int, rtx *, int *, int);
-static int rs6000_use_sched_lookahead (void);
-static int rs6000_use_sched_lookahead_guard (rtx);
-static void * rs6000_alloc_sched_context (void);
-static void rs6000_init_sched_context (void *, bool);
-static void rs6000_set_sched_context (void *);
-static void rs6000_free_sched_context (void *);
-static tree rs6000_builtin_reciprocal (unsigned int, bool, bool);
-static tree rs6000_builtin_mask_for_load (void);
-static tree rs6000_builtin_mul_widen_even (tree);
-static tree rs6000_builtin_mul_widen_odd (tree);
-static bool rs6000_builtin_support_vector_misalignment (enum
-                                                       machine_mode,
-                                                       const_tree,
-                                                       int, bool);
-static int rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt,
-                                              tree, int);
-static enum machine_mode rs6000_preferred_simd_mode (enum machine_mode);
-
-static void def_builtin (const char *, tree, enum rs6000_builtins);
-static bool rs6000_vector_alignment_reachable (const_tree, bool);
-static void rs6000_init_builtins (void);
-static tree rs6000_builtin_decl (unsigned, bool);
-
-static rtx rs6000_expand_unop_builtin (enum insn_code, tree, rtx);
-static rtx rs6000_expand_binop_builtin (enum insn_code, tree, rtx);
-static rtx rs6000_expand_ternop_builtin (enum insn_code, tree, rtx);
-static rtx rs6000_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
 static void altivec_init_builtins (void);
-static unsigned builtin_hash_function (const void *);
-static int builtin_hash_eq (const void *, const void *);
 static tree builtin_function_type (enum machine_mode, enum machine_mode,
                                   enum machine_mode, enum machine_mode,
                                   enum rs6000_builtins, const char *name);
 static void rs6000_common_init_builtins (void);
-static void rs6000_init_libfuncs (void);
-
 static void paired_init_builtins (void);
-static rtx paired_expand_builtin (tree, rtx, bool *);
-static rtx paired_expand_lv_builtin (enum insn_code, tree, rtx);
-static rtx paired_expand_stv_builtin (enum insn_code, tree);
 static rtx paired_expand_predicate_builtin (enum insn_code, tree, rtx);
-
 static void spe_init_builtins (void);
-static rtx spe_expand_builtin (tree, rtx, bool *);
-static rtx spe_expand_stv_builtin (enum insn_code, tree);
 static rtx spe_expand_predicate_builtin (enum insn_code, tree, rtx);
 static rtx spe_expand_evsel_builtin (enum insn_code, tree, rtx);
 static int rs6000_emit_int_cmove (rtx, rtx, rtx, rtx);
 static rs6000_stack_t *rs6000_stack_info (void);
-static void debug_stack_info (rs6000_stack_t *);
-
-static rtx altivec_expand_builtin (tree, rtx, bool *);
-static rtx altivec_expand_ld_builtin (tree, rtx, bool *);
-static rtx altivec_expand_st_builtin (tree, rtx, bool *);
-static rtx altivec_expand_dst_builtin (tree, rtx, bool *);
-static rtx altivec_expand_abs_builtin (enum insn_code, tree, rtx);
-static rtx altivec_expand_predicate_builtin (enum insn_code, tree, rtx);
-static rtx altivec_expand_stv_builtin (enum insn_code, tree);
-static rtx altivec_expand_vec_init_builtin (tree, tree, rtx);
-static rtx altivec_expand_vec_set_builtin (tree);
-static rtx altivec_expand_vec_ext_builtin (tree, rtx);
-static int get_element_number (tree, tree);
-static void rs6000_option_override (void);
-static int rs6000_loop_align_max_skip (rtx);
-static int first_altivec_reg_to_save (void);
-static unsigned int compute_vrsave_mask (void);
-static void compute_save_world_info (rs6000_stack_t *info_ptr);
 static void is_altivec_return_reg (rtx, void *);
-static rtx generate_set_vrsave (rtx, rs6000_stack_t *, int);
 int easy_vector_constant (rtx, enum machine_mode);
-static rtx rs6000_dwarf_register_span (rtx);
-static void rs6000_init_dwarf_reg_sizes_extra (tree);
-static rtx rs6000_legitimize_address (rtx, rtx, enum machine_mode);
 static rtx rs6000_debug_legitimize_address (rtx, rtx, enum machine_mode);
 static rtx rs6000_legitimize_tls_address (rtx, enum tls_model);
-static void rs6000_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
-static rtx rs6000_delegitimize_address (rtx);
-static bool rs6000_const_not_ok_for_debug_p (rtx);
-static rtx rs6000_tls_get_addr (void);
-static rtx rs6000_got_sym (void);
 static int rs6000_tls_symbol_ref_1 (rtx *, void *);
-static const char *rs6000_get_some_local_dynamic_name (void);
 static int rs6000_get_some_local_dynamic_name_1 (rtx *, void *);
-static rtx rs6000_complex_function_value (enum machine_mode);
-static rtx rs6000_spe_function_arg (const CUMULATIVE_ARGS *,
-                                   enum machine_mode, const_tree);
-static void rs6000_darwin64_record_arg_advance_flush (CUMULATIVE_ARGS *,
-                                                     HOST_WIDE_INT, int);
-static void rs6000_darwin64_record_arg_advance_recurse (CUMULATIVE_ARGS *,
-                                                       const_tree,
-                                                       HOST_WIDE_INT);
-static void rs6000_darwin64_record_arg_flush (CUMULATIVE_ARGS *,
-                                             HOST_WIDE_INT,
-                                             rtx[], int *);
-static void rs6000_darwin64_record_arg_recurse (CUMULATIVE_ARGS *,
-                                               const_tree, HOST_WIDE_INT,
-                                               rtx[], int *);
-static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, const_tree, bool, bool);
-static rtx rs6000_mixed_function_arg (enum machine_mode, const_tree, int);
-static void rs6000_function_arg_advance (cumulative_args_t, enum machine_mode,
-                                        const_tree, bool);
-static rtx rs6000_function_arg (cumulative_args_t, enum machine_mode,
-                               const_tree, bool);
-static unsigned int rs6000_function_arg_boundary (enum machine_mode,
-                                                 const_tree);
-static void rs6000_move_block_from_reg (int regno, rtx x, int nregs);
-static void setup_incoming_varargs (cumulative_args_t,
-                                   enum machine_mode, tree,
-                                   int *, int);
-static bool rs6000_pass_by_reference (cumulative_args_t, enum machine_mode,
-                                     const_tree, bool);
-static int rs6000_arg_partial_bytes (cumulative_args_t, enum machine_mode,
-                                    tree, bool);
-static const char *invalid_arg_for_unprototyped_fn (const_tree, const_tree, const_tree);
+static rtx rs6000_darwin64_record_arg (CUMULATIVE_ARGS *, const_tree,
+                                      bool, bool);
 #if TARGET_MACHO
 static void macho_branch_islands (void);
-static int no_previous_def (tree function_name);
-static tree get_prev_label (tree function_name);
-static void rs6000_darwin_file_start (void);
 #endif
-
-static tree rs6000_build_builtin_va_list (void);
-static void rs6000_va_start (tree, rtx);
-static tree rs6000_gimplify_va_arg (tree, tree, gimple_seq *, gimple_seq *);
-static bool rs6000_must_pass_in_stack (enum machine_mode, const_tree);
-static bool rs6000_scalar_mode_supported_p (enum machine_mode);
-static bool rs6000_vector_mode_supported_p (enum machine_mode);
-static rtx rs6000_emit_vector_compare_inner (enum rtx_code, rtx, rtx);
-static rtx rs6000_emit_vector_compare (enum rtx_code, rtx, rtx,
-                                      enum machine_mode);
-static tree rs6000_stack_protect_fail (void);
-
 static rtx rs6000_legitimize_reload_address (rtx, enum machine_mode, int, int,
                                             int, int *);
-
 static rtx rs6000_debug_legitimize_reload_address (rtx, enum machine_mode, int,
                                                   int, int, int *);
-
-rtx (*rs6000_legitimize_reload_address_ptr) (rtx, enum machine_mode, int, int,
-                                            int, int *)
-  = rs6000_legitimize_reload_address;
-
-static bool rs6000_mode_dependent_address_p (const_rtx);
 static bool rs6000_mode_dependent_address (const_rtx);
 static bool rs6000_debug_mode_dependent_address (const_rtx);
-static bool (*rs6000_mode_dependent_address_ptr) (const_rtx)
-  = rs6000_mode_dependent_address;
-
 static enum reg_class rs6000_secondary_reload_class (enum reg_class,
                                                     enum machine_mode, rtx);
 static enum reg_class rs6000_debug_secondary_reload_class (enum reg_class,
                                                           enum machine_mode,
                                                           rtx);
-enum reg_class (*rs6000_secondary_reload_class_ptr) (enum reg_class,
-                                                    enum machine_mode, rtx)
-  = rs6000_secondary_reload_class;
-
 static enum reg_class rs6000_preferred_reload_class (rtx, enum reg_class);
 static enum reg_class rs6000_debug_preferred_reload_class (rtx,
                                                           enum reg_class);
-enum reg_class (*rs6000_preferred_reload_class_ptr) (rtx, enum reg_class)
-  = rs6000_preferred_reload_class;
-
 static bool rs6000_secondary_memory_needed (enum reg_class, enum reg_class,
                                            enum machine_mode);
-
 static bool rs6000_debug_secondary_memory_needed (enum reg_class,
                                                  enum reg_class,
                                                  enum machine_mode);
-
-bool (*rs6000_secondary_memory_needed_ptr) (enum reg_class, enum reg_class,
-                                           enum machine_mode)
-  = rs6000_secondary_memory_needed;
-
 static bool rs6000_cannot_change_mode_class (enum machine_mode,
                                             enum machine_mode,
                                             enum reg_class);
 static bool rs6000_debug_cannot_change_mode_class (enum machine_mode,
                                                   enum machine_mode,
                                                   enum reg_class);
+static bool rs6000_save_toc_in_prologue_p (void);
+
+rtx (*rs6000_legitimize_reload_address_ptr) (rtx, enum machine_mode, int, int,
+                                            int, int *)
+  = rs6000_legitimize_reload_address;
+
+static bool (*rs6000_mode_dependent_address_ptr) (const_rtx)
+  = rs6000_mode_dependent_address;
+
+enum reg_class (*rs6000_secondary_reload_class_ptr) (enum reg_class,
+                                                    enum machine_mode, rtx)
+  = rs6000_secondary_reload_class;
+
+enum reg_class (*rs6000_preferred_reload_class_ptr) (rtx, enum reg_class)
+  = rs6000_preferred_reload_class;
+
+bool (*rs6000_secondary_memory_needed_ptr) (enum reg_class, enum reg_class,
+                                           enum machine_mode)
+  = rs6000_secondary_memory_needed;
 
 bool (*rs6000_cannot_change_mode_class_ptr) (enum machine_mode,
                                             enum machine_mode,
                                             enum reg_class)
   = rs6000_cannot_change_mode_class;
 
-static reg_class_t rs6000_secondary_reload (bool, rtx, reg_class_t,
-                                           enum machine_mode,
-                                           struct secondary_reload_info *);
-
 const int INSN_NOT_AVAILABLE = -1;
-static enum machine_mode rs6000_eh_return_filter_mode (void);
-static bool rs6000_can_eliminate (const int, const int);
-static void rs6000_conditional_register_usage (void);
-static void rs6000_trampoline_init (rtx, tree, rtx);
-static bool rs6000_cannot_force_const_mem (enum machine_mode, rtx);
-static bool rs6000_legitimate_constant_p (enum machine_mode, rtx);
-static bool rs6000_save_toc_in_prologue_p (void);
-static void rs6000_code_end (void) ATTRIBUTE_UNUSED;
-static void rs6000_set_up_by_prologue (struct hard_reg_set_container *);
 
 /* Hash table stuff for keeping track of TOC entries.  */
 
@@ -1254,14 +1039,6 @@ struct GTY(()) builtin_hash_struct
 
 static GTY ((param_is (struct builtin_hash_struct))) htab_t builtin_hash_table;
 
-static bool rs6000_valid_attribute_p (tree, tree, tree, int);
-static void rs6000_function_specific_save (struct cl_target_option *);
-static void rs6000_function_specific_restore (struct cl_target_option *);
-static void rs6000_function_specific_print (FILE *, int,
-                                           struct cl_target_option *);
-static bool rs6000_can_inline_p (tree, tree);
-static void rs6000_set_current_function (tree);
-
 \f
 /* Default register names.  */
 char rs6000_reg_names[][8] =
@@ -1457,10 +1234,6 @@ static const struct attribute_spec rs6000_attribute_table[] =
 
 #undef TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD
 #define TARGET_VECTORIZE_BUILTIN_MASK_FOR_LOAD rs6000_builtin_mask_for_load
-#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN
-#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_EVEN rs6000_builtin_mul_widen_even
-#undef TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD
-#define TARGET_VECTORIZE_BUILTIN_MUL_WIDEN_ODD rs6000_builtin_mul_widen_odd
 #undef TARGET_VECTORIZE_SUPPORT_VECTOR_MISALIGNMENT
 #define TARGET_VECTORIZE_SUPPORT_VECTOR_MISALIGNMENT           \
   rs6000_builtin_support_vector_misalignment
@@ -1472,6 +1245,14 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_VECTORIZE_PREFERRED_SIMD_MODE
 #define TARGET_VECTORIZE_PREFERRED_SIMD_MODE \
   rs6000_preferred_simd_mode
+#undef TARGET_VECTORIZE_INIT_COST
+#define TARGET_VECTORIZE_INIT_COST rs6000_init_cost
+#undef TARGET_VECTORIZE_ADD_STMT_COST
+#define TARGET_VECTORIZE_ADD_STMT_COST rs6000_add_stmt_cost
+#undef TARGET_VECTORIZE_FINISH_COST
+#define TARGET_VECTORIZE_FINISH_COST rs6000_finish_cost
+#undef TARGET_VECTORIZE_DESTROY_COST_DATA
+#define TARGET_VECTORIZE_DESTROY_COST_DATA rs6000_destroy_cost_data
 
 #undef TARGET_INIT_BUILTINS
 #define TARGET_INIT_BUILTINS rs6000_init_builtins
@@ -1514,7 +1295,7 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_RTX_COSTS
 #define TARGET_RTX_COSTS rs6000_rtx_costs
 #undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST hook_int_rtx_bool_0
+#define TARGET_ADDRESS_COST hook_int_rtx_mode_as_bool_0
 
 #undef TARGET_DWARF_REGISTER_SPAN
 #define TARGET_DWARF_REGISTER_SPAN rs6000_dwarf_register_span
@@ -1522,6 +1303,9 @@ static const struct attribute_spec rs6000_attribute_table[] =
 #undef TARGET_INIT_DWARF_REG_SIZES_EXTRA
 #define TARGET_INIT_DWARF_REG_SIZES_EXTRA rs6000_init_dwarf_reg_sizes_extra
 
+#undef TARGET_MEMBER_TYPE_FORCES_BLK
+#define TARGET_MEMBER_TYPE_FORCES_BLK rs6000_member_type_forces_blk
+
 /* On rs6000, function arguments are promoted, as are function return
    values.  */
 #undef TARGET_PROMOTE_FUNCTION_MODE
@@ -1677,8 +1461,7 @@ static const struct attribute_spec rs6000_attribute_table[] =
 /* Simplifications for entries below.  */
 
 enum {
-  POWERPC_BASE_MASK = MASK_POWERPC | MASK_NEW_MNEMONICS,
-  POWERPC_7400_MASK = POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_ALTIVEC
+  POWERPC_7400_MASK = MASK_PPC_GFXOPT | MASK_ALTIVEC
 };
 
 /* Some OSs don't support saving the high part of 64-bit registers on context
@@ -1688,8 +1471,7 @@ enum {
    the user's specification.  */
 
 enum {
-  POWER_MASKS = MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
-  POWERPC_MASKS = (POWERPC_BASE_MASK | MASK_PPC_GPOPT | MASK_STRICT_ALIGN
+  POWERPC_MASKS = (MASK_PPC_GPOPT | MASK_STRICT_ALIGN
                   | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC
                   | MASK_MFCRF | MASK_POPCNTB | MASK_FPRND | MASK_MULHW
                   | MASK_DLMZB | MASK_CMPB | MASK_MFPGPR | MASK_DFP
@@ -1972,7 +1754,6 @@ rs6000_debug_reg_global (void)
   rs6000_debug_reg_print (LR_REGNO, LR_REGNO, "lr");
   rs6000_debug_reg_print (CTR_REGNO, CTR_REGNO, "ctr");
   rs6000_debug_reg_print (CR0_REGNO, CR7_REGNO, "cr");
-  rs6000_debug_reg_print (MQ_REGNO, MQ_REGNO, "mq");
   rs6000_debug_reg_print (CA_REGNO, CA_REGNO, "ca");
   rs6000_debug_reg_print (VRSAVE_REGNO, VRSAVE_REGNO, "vrsave");
   rs6000_debug_reg_print (VSCR_REGNO, VSCR_REGNO, "vscr");
@@ -2195,7 +1976,6 @@ rs6000_init_hard_regno_mode_ok (bool global_init_p)
   for (r = CR1_REGNO; r <= CR7_REGNO; ++r)
     rs6000_regno_regclass[r] = CR_REGS;
 
-  rs6000_regno_regclass[MQ_REGNO] = MQ_REGS;
   rs6000_regno_regclass[LR_REGNO] = LINK_REGS;
   rs6000_regno_regclass[CTR_REGNO] = CTR_REGS;
   rs6000_regno_regclass[CA_REGNO] = CA_REGS;
@@ -2298,7 +2078,7 @@ rs6000_init_hard_regno_mode_ok (bool global_init_p)
 
   /* TODO add SPE and paired floating point vector support.  */
 
-  /* Register class constaints for the constraints that depend on compile
+  /* Register class constraints for the constraints that depend on compile
      switches.  */
   if (TARGET_HARD_FLOAT && TARGET_FPRS)
     rs6000_constraints[RS6000_CONSTRAINT_f] = FLOAT_REGS;
@@ -2549,7 +2329,7 @@ darwin_rs6000_override_options (void)
 
   /* Unless the user (not the configurer) has explicitly overridden
      it with -mcpu=G3 or -mno-altivec, then 10.5+ targets default to
-     G4 unless targetting the kernel.  */
+     G4 unless targeting the kernel.  */
   if (!flag_mkernel
       && !flag_apple_kext
       && strverscmp (darwin_macosx_version_min, "10.5") >= 0
@@ -2585,7 +2365,6 @@ rs6000_builtin_mask_calculate (void)
          | ((TARGET_FRSQRTE)               ? RS6000_BTM_FRSQRTE  : 0)
          | ((TARGET_FRSQRTES)              ? RS6000_BTM_FRSQRTES : 0)
          | ((TARGET_POPCNTD)               ? RS6000_BTM_POPCNTD  : 0)
-         | ((TARGET_POWERPC)               ? RS6000_BTM_POWERPC  : 0)
          | ((rs6000_cpu == PROCESSOR_CELL) ? RS6000_BTM_CELL     : 0));
 }
 
@@ -2596,7 +2375,11 @@ static bool
 rs6000_option_override_internal (bool global_init_p)
 {
   bool ret = true;
-  const char *default_cpu = OPTION_TARGET_CPU_DEFAULT;
+  bool have_cpu = false;
+
+  /* The default cpu requested at configure time, if any.  */
+  const char *implicit_cpu = OPTION_TARGET_CPU_DEFAULT;
+
   int set_masks;
   int cpu_index;
   int tune_index;
@@ -2615,11 +2398,6 @@ rs6000_option_override_internal (bool global_init_p)
     warning (0, "-malign-power is not supported for 64-bit Darwin;"
             " it is incompatible with the installed C and C++ libraries");
 
-  if (global_options_set.x_rs6000_spe_abi
-      && rs6000_spe_abi
-      && !TARGET_SPE_ABI)
-    error ("not configured for SPE ABI");
-
   /* Numerous experiment shows that IRA based loop pressure
      calculation works better for RTL loop invariant motion on targets
      with enough (>= 32) registers.  It is an expensive optimization.
@@ -2639,7 +2417,7 @@ rs6000_option_override_internal (bool global_init_p)
       rs6000_pointer_size = 32;
     }
 
-  set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT;
+  set_masks = POWERPC_MASKS | MASK_SOFT_FLOAT;
 #ifdef OS_MISSING_POWERPC64
   if (OS_MISSING_POWERPC64)
     set_masks &= ~MASK_POWERPC64;
@@ -2652,52 +2430,109 @@ rs6000_option_override_internal (bool global_init_p)
   /* Don't override by the processor default if given explicitly.  */
   set_masks &= ~target_flags_explicit;
 
-  /* Identify the processor type.  */
-  if (!default_cpu)
-    {
-      if (TARGET_POWERPC64)
-       default_cpu = "powerpc64";
-      else if (TARGET_POWERPC)
-       default_cpu = "powerpc";
-    }
-
   /* Process the -mcpu=<xxx> and -mtune=<xxx> argument.  If the user changed
      the cpu in a target attribute or pragma, but did not specify a tuning
      option, use the cpu for the tuning option rather than the option specified
-     with -mtune on the command line.  */
-  if (rs6000_cpu_index > 0)
-    cpu_index = rs6000_cpu_index;
-  else if (main_target_opt != NULL && main_target_opt->x_rs6000_cpu_index > 0)
-    rs6000_cpu_index = cpu_index = main_target_opt->x_rs6000_cpu_index;
+     with -mtune on the command line.  Process a '--with-cpu' configuration
+     request as an implicit --cpu.  */
+  if (rs6000_cpu_index >= 0)
+    {
+      cpu_index = rs6000_cpu_index;
+      have_cpu = true;
+    }
+  else if (main_target_opt != NULL && main_target_opt->x_rs6000_cpu_index >= 0)
+    {
+      rs6000_cpu_index = cpu_index = main_target_opt->x_rs6000_cpu_index;
+      have_cpu = true;
+    }
   else
-    rs6000_cpu_index = cpu_index = rs6000_cpu_name_lookup (default_cpu);
+    {
+      const char *default_cpu =
+        (implicit_cpu ? implicit_cpu
+         : (TARGET_POWERPC64 ? "powerpc64" : "powerpc"));
+
+      rs6000_cpu_index = cpu_index = rs6000_cpu_name_lookup (default_cpu);
+      have_cpu = implicit_cpu != 0;
+    }
+
+  gcc_assert (cpu_index >= 0);
 
-  if (rs6000_tune_index > 0)
+  target_flags &= ~set_masks;
+  target_flags |= (processor_target_table[cpu_index].target_enable
+                  & set_masks);
+
+  if (rs6000_tune_index >= 0)
     tune_index = rs6000_tune_index;
-  else
+  else if (have_cpu)
     rs6000_tune_index = tune_index = cpu_index;
-
-  if (cpu_index >= 0)
+  else
     {
-      target_flags &= ~set_masks;
-      target_flags |= (processor_target_table[cpu_index].target_enable
-                      & set_masks);
+      size_t i;
+      enum processor_type tune_proc
+       = (TARGET_POWERPC64 ? PROCESSOR_DEFAULT64 : PROCESSOR_DEFAULT);
+
+      tune_index = -1;
+      for (i = 0; i < ARRAY_SIZE (processor_target_table); i++)
+       if (processor_target_table[i].processor == tune_proc)
+         {
+           rs6000_tune_index = tune_index = i;
+           break;
+         }
     }
 
-  rs6000_cpu = ((tune_index >= 0)
-               ? processor_target_table[tune_index].processor
-               : (TARGET_POWERPC64
-                  ? PROCESSOR_DEFAULT64
-                  : PROCESSOR_DEFAULT));
+  gcc_assert (tune_index >= 0);
+  rs6000_cpu = processor_target_table[tune_index].processor;
+
+  /* Pick defaults for SPE related control flags.  Do this early to make sure
+     that the TARGET_ macros are representative ASAP.  */
+  {
+    int spe_capable_cpu =
+      (rs6000_cpu == PROCESSOR_PPC8540
+       || rs6000_cpu == PROCESSOR_PPC8548);
+
+    if (!global_options_set.x_rs6000_spe_abi)
+      rs6000_spe_abi = spe_capable_cpu;
+
+    if (!global_options_set.x_rs6000_spe)
+      rs6000_spe = spe_capable_cpu;
+
+    if (!global_options_set.x_rs6000_float_gprs)
+      rs6000_float_gprs =
+        (rs6000_cpu == PROCESSOR_PPC8540 ? 1
+         : rs6000_cpu == PROCESSOR_PPC8548 ? 2
+         : 0);
+  }
+
+  if (global_options_set.x_rs6000_spe_abi
+      && rs6000_spe_abi
+      && !TARGET_SPE_ABI)
+    error ("not configured for SPE ABI");
+
+  if (global_options_set.x_rs6000_spe
+      && rs6000_spe
+      && !TARGET_SPE)
+    error ("not configured for SPE instruction set");
+
+  if (main_target_opt != NULL
+      && ((main_target_opt->x_rs6000_spe_abi != rs6000_spe_abi)
+          || (main_target_opt->x_rs6000_spe != rs6000_spe)
+          || (main_target_opt->x_rs6000_float_gprs != rs6000_float_gprs)))
+    error ("target attribute or pragma changes SPE ABI");
 
   if (rs6000_cpu == PROCESSOR_PPCE300C2 || rs6000_cpu == PROCESSOR_PPCE300C3
-      || rs6000_cpu == PROCESSOR_PPCE500MC || rs6000_cpu == PROCESSOR_PPCE500MC64)
+      || rs6000_cpu == PROCESSOR_PPCE500MC || rs6000_cpu == PROCESSOR_PPCE500MC64
+      || rs6000_cpu == PROCESSOR_PPCE5500)
     {
       if (TARGET_ALTIVEC)
        error ("AltiVec not supported in this target");
       if (TARGET_SPE)
        error ("SPE not supported in this target");
     }
+  if (rs6000_cpu == PROCESSOR_PPCE6500)
+    {
+      if (TARGET_SPE)
+       error ("SPE not supported in this target");
+    }
 
   /* Disable Cell microcode if we are optimizing for the Cell
      and not optimizing for size.  */
@@ -2792,7 +2627,9 @@ rs6000_option_override_internal (bool global_init_p)
      user's opinion, though.  */
   if (rs6000_block_move_inline_limit == 0
       && (rs6000_cpu == PROCESSOR_PPCE500MC
-         || rs6000_cpu == PROCESSOR_PPCE500MC64))
+         || rs6000_cpu == PROCESSOR_PPCE500MC64
+         || rs6000_cpu == PROCESSOR_PPCE5500
+         || rs6000_cpu == PROCESSOR_PPCE6500))
     rs6000_block_move_inline_limit = 128;
 
   /* store_one_arg depends on expand_block_move to handle at least the
@@ -2853,7 +2690,7 @@ rs6000_option_override_internal (bool global_init_p)
        rs6000_long_double_type_size = RS6000_DEFAULT_LONG_DOUBLE_SIZE;
     }
 
-#ifndef POWERPC_LINUX
+#if !defined (POWERPC_LINUX) && !defined (POWERPC_FREEBSD)
   if (!global_options_set.x_rs6000_ieeequad)
     rs6000_ieeequad = 1;
 #endif
@@ -2925,35 +2762,46 @@ rs6000_option_override_internal (bool global_init_p)
   SUB3TARGET_OVERRIDE_OPTIONS;
 #endif
 
-  if (TARGET_E500 || rs6000_cpu == PROCESSOR_PPCE500MC
-      || rs6000_cpu == PROCESSOR_PPCE500MC64)
+  /* For the E500 family of cores, reset the single/double FP flags to let us
+     check that they remain constant across attributes or pragmas.  Also,
+     clear a possible request for string instructions, not supported and which
+     we might have silently queried above for -Os. 
+
+     For other families, clear ISEL in case it was set implicitly.
+  */
+
+  switch (rs6000_cpu)
     {
-      /* The e500 and e500mc do not have string instructions, and we set
-        MASK_STRING above when optimizing for size.  */
-      if ((target_flags & MASK_STRING) != 0)
-       target_flags = target_flags & ~MASK_STRING;
+    case PROCESSOR_PPC8540:
+    case PROCESSOR_PPC8548:
+    case PROCESSOR_PPCE500MC:
+    case PROCESSOR_PPCE500MC64:
+    case PROCESSOR_PPCE5500:
+    case PROCESSOR_PPCE6500:
+
+      rs6000_single_float = TARGET_E500_SINGLE || TARGET_E500_DOUBLE;
+      rs6000_double_float = TARGET_E500_DOUBLE;
+
+      target_flags &= ~MASK_STRING;
+
+      break;
+
+    default:
+
+      if (have_cpu && !(target_flags_explicit & MASK_ISEL))
+       target_flags &= ~MASK_ISEL;
+
+      break;
     }
-  else if (global_options_set.x_rs6000_cpu_index)
+
+  if (main_target_opt)
     {
-      /* For the powerpc-eabispe configuration, we set all these by
-        default, so let's unset them if we manually set another
-        CPU that is not the E500.  */
-      if (main_target_opt != NULL
-         && ((main_target_opt->x_rs6000_spe_abi != rs6000_spe_abi)
-             || (main_target_opt->x_rs6000_spe != rs6000_spe)
-             || (main_target_opt->x_rs6000_float_gprs != rs6000_float_gprs)))
-       error ("target attribute or pragma changes SPE ABI");
-      else
-       {
-         if (!global_options_set.x_rs6000_spe_abi)
-           rs6000_spe_abi = 0;
-         if (!global_options_set.x_rs6000_spe)
-           rs6000_spe = 0;
-         if (!global_options_set.x_rs6000_float_gprs)
-           rs6000_float_gprs = 0;
-       }
-      if (!(target_flags_explicit & MASK_ISEL))
-       target_flags &= ~MASK_ISEL;
+      if (main_target_opt->x_rs6000_single_float != rs6000_single_float)
+       error ("target attribute or pragma changes single precision floating "
+              "point");
+      if (main_target_opt->x_rs6000_double_float != rs6000_double_float)
+       error ("target attribute or pragma changes double precision floating "
+              "point");
     }
 
   /* Detect invalid option combinations with E500.  */
@@ -2974,7 +2822,9 @@ rs6000_option_override_internal (bool global_init_p)
                                 || rs6000_cpu == PROCESSOR_POWER6
                                 || rs6000_cpu == PROCESSOR_POWER7
                                 || rs6000_cpu == PROCESSOR_PPCE500MC
-                                || rs6000_cpu == PROCESSOR_PPCE500MC64);
+                                || rs6000_cpu == PROCESSOR_PPCE500MC64
+                                || rs6000_cpu == PROCESSOR_PPCE5500
+                                || rs6000_cpu == PROCESSOR_PPCE6500);
 
   /* Allow debug switches to override the above settings.  These are set to -1
      in rs6000.opt to indicate the user hasn't directly set the switch.  */
@@ -2992,7 +2842,7 @@ rs6000_option_override_internal (bool global_init_p)
 
   /* Handle -msched-costly-dep option.  */
   rs6000_sched_costly_dep
-    = (rs6000_sched_groups ? store_to_load_dep_costly : no_dep_costly);
+    = (rs6000_sched_groups ? true_store_to_load_dep_costly : no_dep_costly);
 
   if (rs6000_sched_costly_dep_str)
     {
@@ -3110,14 +2960,6 @@ rs6000_option_override_internal (bool global_init_p)
   else
     switch (rs6000_cpu)
       {
-      case PROCESSOR_RIOS1:
-       rs6000_cost = &rios1_cost;
-       break;
-
-      case PROCESSOR_RIOS2:
-       rs6000_cost = &rios2_cost;
-       break;
-
       case PROCESSOR_RS64A:
        rs6000_cost = &rs64a_cost;
        break;
@@ -3180,6 +3022,7 @@ rs6000_option_override_internal (bool global_init_p)
        break;
 
       case PROCESSOR_PPC8540:
+      case PROCESSOR_PPC8548:
        rs6000_cost = &ppc8540_cost;
        break;
 
@@ -3196,6 +3039,14 @@ rs6000_option_override_internal (bool global_init_p)
        rs6000_cost = &ppce500mc64_cost;
        break;
 
+      case PROCESSOR_PPCE5500:
+       rs6000_cost = &ppce5500_cost;
+       break;
+
+      case PROCESSOR_PPCE6500:
+       rs6000_cost = &ppce6500_cost;
+       break;
+
       case PROCESSOR_TITAN:
        rs6000_cost = &titan_cost;
        break;
@@ -3252,26 +3103,6 @@ rs6000_option_override_internal (bool global_init_p)
       && rs6000_single_float == 0 && rs6000_double_float == 0)
     rs6000_single_float = rs6000_double_float = 1;
 
-  /* Reset single and double FP flags if target is E500. */
-  if (TARGET_E500) 
-  {
-    rs6000_single_float = rs6000_double_float = 0;
-    if (TARGET_E500_SINGLE)
-      rs6000_single_float = 1; 
-    if (TARGET_E500_DOUBLE)
-      rs6000_single_float = rs6000_double_float = 1;
-  }
-
-  if (main_target_opt)
-    {
-      if (main_target_opt->x_rs6000_single_float != rs6000_single_float)
-       error ("target attribute or pragma changes single precision floating "
-              "point");
-      if (main_target_opt->x_rs6000_double_float != rs6000_double_float)
-       error ("target attribute or pragma changes double precision floating "
-              "point");
-    }
-
   /* If not explicitly specified via option, decide whether to generate indexed
      load/store instructions.  */
   if (TARGET_AVOID_XFORM == -1)
@@ -3409,53 +3240,6 @@ rs6000_loop_align_max_skip (rtx label)
   return (1 << rs6000_loop_align (label)) - 1;
 }
 
-/* Implement targetm.vectorize.builtin_mul_widen_even.  */
-static tree
-rs6000_builtin_mul_widen_even (tree type)
-{
-  if (!TARGET_ALTIVEC)
-    return NULL_TREE;
-
-  switch (TYPE_MODE (type))
-    {
-    case V8HImode:
-      return TYPE_UNSIGNED (type)
-            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUH_UNS]
-            : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESH];
-
-    case V16QImode:
-      return TYPE_UNSIGNED (type)
-            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULEUB_UNS]
-            : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULESB];
-    default:
-      return NULL_TREE;
-    }
-}
-
-/* Implement targetm.vectorize.builtin_mul_widen_odd.  */
-static tree
-rs6000_builtin_mul_widen_odd (tree type)
-{
-  if (!TARGET_ALTIVEC)
-    return NULL_TREE;
-
-  switch (TYPE_MODE (type))
-    {
-    case V8HImode:
-      return TYPE_UNSIGNED (type)
-            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUH_UNS]
-            : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSH];
-
-    case V16QImode:
-      return TYPE_UNSIGNED (type)
-            ? rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOUB_UNS]
-            : rs6000_builtin_decls[ALTIVEC_BUILTIN_VMULOSB];
-    default:
-      return NULL_TREE;
-    }
-}
-
-
 /* Return true iff, data reference of TYPE can reach vector alignment (16)
    after applying N number of iterations.  This routine does not determine
    how may iterations are required to reach desired alignment.  */
@@ -3488,7 +3272,7 @@ rs6000_vector_alignment_reachable (const_tree type ATTRIBUTE_UNUSED, bool is_pac
 
 /* Return true if the vector misalignment factor is supported by the
    target.  */ 
-bool
+static bool
 rs6000_builtin_support_vector_misalignment (enum machine_mode mode,
                                            const_tree type,
                                            int misalignment,
@@ -3528,6 +3312,7 @@ rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost,
                                    tree vectype, int misalign)
 {
   unsigned elements;
+  tree elem_type;
 
   switch (type_of_cost)
     {
@@ -3543,9 +3328,16 @@ rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost,
         return 1;
 
       case vec_perm:
-       if (!TARGET_VSX)
+       if (TARGET_VSX)
+         return 3;
+       else
          return 1;
-       return 2;
+
+      case vec_promote_demote:
+        if (TARGET_VSX)
+          return 4;
+        else
+          return 1;
 
       case cond_branch_taken:
         return 3;
@@ -3620,6 +3412,18 @@ rs6000_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost,
 
         return 2;
 
+      case vec_construct:
+       elements = TYPE_VECTOR_SUBPARTS (vectype);
+       elem_type = TREE_TYPE (vectype);
+       /* 32-bit vectors loaded into registers are stored as double
+          precision, so we need n/2 converts in addition to the usual
+          n/2 merges to construct a vector of short floats from them.  */
+       if (SCALAR_FLOAT_TYPE_P (elem_type)
+           && TYPE_PRECISION (elem_type) == 32)
+         return elements + 1;
+       else
+         return elements / 2 + 1;
+
       default:
         gcc_unreachable ();
     }
@@ -3667,21 +3471,141 @@ rs6000_preferred_simd_mode (enum machine_mode mode)
   return word_mode;
 }
 
-/* Handler for the Mathematical Acceleration Subsystem (mass) interface to a
-   library with vectorized intrinsics.  */
-
-static tree
-rs6000_builtin_vectorized_libmass (tree fndecl, tree type_out, tree type_in)
+typedef struct _rs6000_cost_data
 {
-  char name[32];
-  const char *suffix = NULL;
-  tree fntype, new_fndecl, bdecl = NULL_TREE;
-  int n_args = 1;
-  const char *bname;
-  enum machine_mode el_mode, in_mode;
-  int n, in_n;
+  struct loop *loop_info;
+  unsigned cost[3];
+} rs6000_cost_data;
 
-  /* Libmass is suitable for unsafe math only as it does not correctly support
+/* Test for likely overcommitment of vector hardware resources.  If a
+   loop iteration is relatively large, and too large a percentage of
+   instructions in the loop are vectorized, the cost model may not
+   adequately reflect delays from unavailable vector resources.
+   Penalize the loop body cost for this case.  */
+
+static void
+rs6000_density_test (rs6000_cost_data *data)
+{
+  const int DENSITY_PCT_THRESHOLD = 85;
+  const int DENSITY_SIZE_THRESHOLD = 70;
+  const int DENSITY_PENALTY = 10;
+  struct loop *loop = data->loop_info;
+  basic_block *bbs = get_loop_body (loop);
+  int nbbs = loop->num_nodes;
+  int vec_cost = data->cost[vect_body], not_vec_cost = 0;
+  int i, density_pct;
+
+  for (i = 0; i < nbbs; i++)
+    {
+      basic_block bb = bbs[i];
+      gimple_stmt_iterator gsi;
+
+      for (gsi = gsi_start_bb (bb); !gsi_end_p (gsi); gsi_next (&gsi))
+       {
+         gimple stmt = gsi_stmt (gsi);
+         stmt_vec_info stmt_info = vinfo_for_stmt (stmt);
+
+         if (!STMT_VINFO_RELEVANT_P (stmt_info)
+             && !STMT_VINFO_IN_PATTERN_P (stmt_info))
+           not_vec_cost++;
+       }
+    }
+
+  free (bbs);
+  density_pct = (vec_cost * 100) / (vec_cost + not_vec_cost);
+
+  if (density_pct > DENSITY_PCT_THRESHOLD
+      && vec_cost + not_vec_cost > DENSITY_SIZE_THRESHOLD)
+    {
+      data->cost[vect_body] = vec_cost * (100 + DENSITY_PENALTY) / 100;
+      if (vect_print_dump_info (REPORT_DETAILS))
+       fprintf (vect_dump,
+                "density %d%%, cost %d exceeds threshold, penalizing "
+                "loop body cost by %d%%", density_pct, 
+                vec_cost + not_vec_cost, DENSITY_PENALTY);
+    }
+}
+
+/* Implement targetm.vectorize.init_cost.  */
+
+static void *
+rs6000_init_cost (struct loop *loop_info)
+{
+  rs6000_cost_data *data = XNEW (struct _rs6000_cost_data);
+  data->loop_info = loop_info;
+  data->cost[vect_prologue] = 0;
+  data->cost[vect_body]     = 0;
+  data->cost[vect_epilogue] = 0;
+  return data;
+}
+
+/* Implement targetm.vectorize.add_stmt_cost.  */
+
+static unsigned
+rs6000_add_stmt_cost (void *data, int count, enum vect_cost_for_stmt kind,
+                     struct _stmt_vec_info *stmt_info, int misalign,
+                     enum vect_cost_model_location where)
+{
+  rs6000_cost_data *cost_data = (rs6000_cost_data*) data;
+  unsigned retval = 0;
+
+  if (flag_vect_cost_model)
+    {
+      tree vectype = stmt_info ? stmt_vectype (stmt_info) : NULL_TREE;
+      int stmt_cost = rs6000_builtin_vectorization_cost (kind, vectype,
+                                                        misalign);
+      /* Statements in an inner loop relative to the loop being
+        vectorized are weighted more heavily.  The value here is
+        arbitrary and could potentially be improved with analysis.  */
+      if (where == vect_body && stmt_info && stmt_in_inner_loop_p (stmt_info))
+       count *= 50;  /* FIXME.  */
+
+      retval = (unsigned) (count * stmt_cost);
+      cost_data->cost[where] += retval;
+    }
+
+  return retval;
+}
+
+/* Implement targetm.vectorize.finish_cost.  */
+
+static void
+rs6000_finish_cost (void *data, unsigned *prologue_cost,
+                   unsigned *body_cost, unsigned *epilogue_cost)
+{
+  rs6000_cost_data *cost_data = (rs6000_cost_data*) data;
+
+  if (cost_data->loop_info)
+    rs6000_density_test (cost_data);
+
+  *prologue_cost = cost_data->cost[vect_prologue];
+  *body_cost     = cost_data->cost[vect_body];
+  *epilogue_cost = cost_data->cost[vect_epilogue];
+}
+
+/* Implement targetm.vectorize.destroy_cost_data.  */
+
+static void
+rs6000_destroy_cost_data (void *data)
+{
+  free (data);
+}
+
+/* Handler for the Mathematical Acceleration Subsystem (mass) interface to a
+   library with vectorized intrinsics.  */
+
+static tree
+rs6000_builtin_vectorized_libmass (tree fndecl, tree type_out, tree type_in)
+{
+  char name[32];
+  const char *suffix = NULL;
+  tree fntype, new_fndecl, bdecl = NULL_TREE;
+  int n_args = 1;
+  const char *bname;
+  enum machine_mode el_mode, in_mode;
+  int n, in_n;
+
+  /* Libmass is suitable for unsafe math only as it does not correctly support
      parts of IEEE with the required precision such as denormals.  Only support
      it if we have VSX to use the simd d2 or f4 functions.
      XXX: Add variable length support.  */
@@ -4016,11 +3940,6 @@ rs6000_file_start (void)
 
   default_file_start ();
 
-#ifdef TARGET_BI_ARCH
-  if ((TARGET_DEFAULT ^ target_flags) & MASK_64BIT)
-    rs6000_default_cpu = 0;
-#endif
-
   if (flag_verbose_asm)
     {
       sprintf (buffer, "\n%s rs6000/powerpc options:", ASM_COMMENT_START);
@@ -4108,11 +4027,11 @@ direct_return (void)
 int
 num_insns_constant_wide (HOST_WIDE_INT value)
 {
-  /* signed constant loadable with {cal|addi} */
+  /* signed constant loadable with addi */
   if ((unsigned HOST_WIDE_INT) (value + 0x8000) < 0x10000)
     return 1;
 
-  /* constant loadable with {cau|addis} */
+  /* constant loadable with addis */
   else if ((value & 0xffff) == 0
           && (value >> 31 == -1 || value >> 31 == 0))
     return 1;
@@ -4694,28 +4613,25 @@ rs6000_expand_vector_init (rtx target, rtx vals)
   /* Double word values on VSX can use xxpermdi or lxvdsx.  */
   if (VECTOR_MEM_VSX_P (mode) && (mode == V2DFmode || mode == V2DImode))
     {
+      rtx op0 = XVECEXP (vals, 0, 0);
+      rtx op1 = XVECEXP (vals, 0, 1);
       if (all_same)
        {
-         rtx element = XVECEXP (vals, 0, 0);
+         if (!MEM_P (op0) && !REG_P (op0))
+           op0 = force_reg (inner_mode, op0);
          if (mode == V2DFmode)
-           emit_insn (gen_vsx_splat_v2df (target, element));
+           emit_insn (gen_vsx_splat_v2df (target, op0));
          else
-           emit_insn (gen_vsx_splat_v2di (target, element));
+           emit_insn (gen_vsx_splat_v2di (target, op0));
        }
       else
        {
+         op0 = force_reg (inner_mode, op0);
+         op1 = force_reg (inner_mode, op1);
          if (mode == V2DFmode)
-           {
-             rtx op0 = copy_to_mode_reg (DFmode, XVECEXP (vals, 0, 0));
-             rtx op1 = copy_to_mode_reg (DFmode, XVECEXP (vals, 0, 1));
-             emit_insn (gen_vsx_concat_v2df (target, op0, op1));
-           }
+           emit_insn (gen_vsx_concat_v2df (target, op0, op1));
          else
-           {
-             rtx op0 = copy_to_mode_reg (DImode, XVECEXP (vals, 0, 0));
-             rtx op1 = copy_to_mode_reg (DImode, XVECEXP (vals, 0, 1));
-             emit_insn (gen_vsx_concat_v2di (target, op0, op1));
-           }
+           emit_insn (gen_vsx_concat_v2di (target, op0, op1));
        }
       return;
     }
@@ -4729,7 +4645,7 @@ rs6000_expand_vector_init (rtx target, rtx vals)
       if (all_same)
        {
          rtx freg = gen_reg_rtx (V4SFmode);
-         rtx sreg = copy_to_reg (XVECEXP (vals, 0, 0));
+         rtx sreg = force_reg (SFmode, XVECEXP (vals, 0, 0));
 
          emit_insn (gen_vsx_xscvdpsp_scalar (freg, sreg));
          emit_insn (gen_vsx_xxspltw_v4sf (target, freg, const0_rtx));
@@ -4740,13 +4656,13 @@ rs6000_expand_vector_init (rtx target, rtx vals)
          rtx dbl_odd  = gen_reg_rtx (V2DFmode);
          rtx flt_even = gen_reg_rtx (V4SFmode);
          rtx flt_odd  = gen_reg_rtx (V4SFmode);
+         rtx op0 = force_reg (SFmode, XVECEXP (vals, 0, 0));
+         rtx op1 = force_reg (SFmode, XVECEXP (vals, 0, 1));
+         rtx op2 = force_reg (SFmode, XVECEXP (vals, 0, 2));
+         rtx op3 = force_reg (SFmode, XVECEXP (vals, 0, 3));
 
-         emit_insn (gen_vsx_concat_v2sf (dbl_even,
-                                         copy_to_reg (XVECEXP (vals, 0, 0)),
-                                         copy_to_reg (XVECEXP (vals, 0, 1))));
-         emit_insn (gen_vsx_concat_v2sf (dbl_odd,
-                                         copy_to_reg (XVECEXP (vals, 0, 2)),
-                                         copy_to_reg (XVECEXP (vals, 0, 3))));
+         emit_insn (gen_vsx_concat_v2sf (dbl_even, op0, op1));
+         emit_insn (gen_vsx_concat_v2sf (dbl_odd, op2, op3));
          emit_insn (gen_vsx_xvcvdpsp (flt_even, dbl_even));
          emit_insn (gen_vsx_xvcvdpsp (flt_odd, dbl_odd));
          rs6000_expand_extract_even (target, flt_even, flt_odd);
@@ -4758,7 +4674,7 @@ rs6000_expand_vector_init (rtx target, rtx vals)
      of 64-bit items is not supported on Altivec.  */
   if (all_same && GET_MODE_SIZE (inner_mode) <= 4)
     {
-      mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode), 0);
+      mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode));
       emit_move_insn (adjust_address_nv (mem, inner_mode, 0),
                      XVECEXP (vals, 0, 0));
       x = gen_rtx_UNSPEC (VOIDmode,
@@ -4794,7 +4710,7 @@ rs6000_expand_vector_init (rtx target, rtx vals)
 
   /* Construct the vector in memory one field at a time
      and load the whole vector.  */
-  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode));
   for (i = 0; i < n_elts; i++)
     emit_move_insn (adjust_address_nv (mem, inner_mode,
                                    i * GET_MODE_SIZE (inner_mode)),
@@ -4823,7 +4739,7 @@ rs6000_expand_vector_set (rtx target, rtx val, int elt)
     }
 
   /* Load single variable value.  */
-  mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode), 0);
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (inner_mode));
   emit_move_insn (adjust_address_nv (mem, inner_mode, 0), val);
   x = gen_rtx_UNSPEC (VOIDmode,
                      gen_rtvec (1, const0_rtx), UNSPEC_LVE);
@@ -4878,7 +4794,7 @@ rs6000_expand_vector_extract (rtx target, rtx vec, int elt)
     }
 
   /* Allocate mode-sized buffer.  */
-  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+  mem = assign_stack_temp (mode, GET_MODE_SIZE (mode));
 
   emit_move_insn (mem, vec);
 
@@ -5119,6 +5035,78 @@ gpr_or_gpr_p (rtx op0, rtx op1)
          || (REG_P (op1) && INT_REGNO_P (REGNO (op1))));
 }
 
+/* Given an address, return a constant offset term if one exists.  */
+
+static rtx
+address_offset (rtx op)
+{
+  if (GET_CODE (op) == PRE_INC
+      || GET_CODE (op) == PRE_DEC)
+    op = XEXP (op, 0);
+  else if (GET_CODE (op) == PRE_MODIFY
+          || GET_CODE (op) == LO_SUM)
+    op = XEXP (op, 1);
+
+  if (GET_CODE (op) == CONST)
+    op = XEXP (op, 0);
+
+  if (GET_CODE (op) == PLUS)
+    op = XEXP (op, 1);
+
+  if (CONST_INT_P (op))
+    return op;
+
+  return NULL_RTX;
+}
+
+/* Return true if the MEM operand is a memory operand suitable for use
+   with a (full width, possibly multiple) gpr load/store.  On
+   powerpc64 this means the offset must be divisible by 4.
+   Implements 'Y' constraint.
+
+   Accept direct, indexed, offset, lo_sum and tocref.  Since this is
+   a constraint function we know the operand has satisfied a suitable
+   memory predicate.  Also accept some odd rtl generated by reload
+   (see rs6000_legitimize_reload_address for various forms).  It is
+   important that reload rtl be accepted by appropriate constraints
+   but not by the operand predicate.
+
+   Offsetting a lo_sum should not be allowed, except where we know by
+   alignment that a 32k boundary is not crossed, but see the ???
+   comment in rs6000_legitimize_reload_address.  Note that by
+   "offsetting" here we mean a further offset to access parts of the
+   MEM.  It's fine to have a lo_sum where the inner address is offset
+   from a sym, since the same sym+offset will appear in the high part
+   of the address calculation.  */
+
+bool
+mem_operand_gpr (rtx op, enum machine_mode mode)
+{
+  unsigned HOST_WIDE_INT offset;
+  int extra;
+  rtx addr = XEXP (op, 0);
+
+  op = address_offset (addr);
+  if (op == NULL_RTX)
+    return true;
+
+  offset = INTVAL (op);
+  if (TARGET_POWERPC64 && (offset & 3) != 0)
+    return false;
+
+  if (GET_CODE (addr) == LO_SUM)
+    /* We know by alignment that ABI_AIX medium/large model toc refs
+       will not cross a 32k boundary, since all entries in the
+       constant pool are naturally aligned and we check alignment for
+       other medium model toc-relative addresses.  For ABI_V4 and
+       ABI_DARWIN lo_sum addresses, we just check that 64-bit
+       offsets are 4-byte aligned.  */
+    return true;
+
+  extra = GET_MODE_SIZE (mode) - UNITS_PER_WORD;
+  gcc_assert (extra >= 0);
+  return offset + 0x8000 < 0x10000u - extra;
+}
 \f
 /* Subroutines of rs6000_legitimize_address and rs6000_legitimate_address_p.  */
 
@@ -5273,15 +5261,37 @@ constant_pool_expr_p (rtx op)
          && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (base), Pmode));
 }
 
-static rtx tocrel_base, tocrel_offset;
+static const_rtx tocrel_base, tocrel_offset;
+
+/* Return true if OP is a toc pointer relative address (the output
+   of create_TOC_reference).  If STRICT, do not match high part or
+   non-split -mcmodel=large/medium toc pointer relative addresses.  */
 
 bool
-toc_relative_expr_p (rtx op)
+toc_relative_expr_p (const_rtx op, bool strict)
 {
-  if (GET_CODE (op) != CONST)
+  if (!TARGET_TOC)
     return false;
 
-  split_const (op, &tocrel_base, &tocrel_offset);
+  if (TARGET_CMODEL != CMODEL_SMALL)
+    {
+      /* Only match the low part.  */
+      if (GET_CODE (op) == LO_SUM
+         && REG_P (XEXP (op, 0))
+         && INT_REG_OK_FOR_BASE_P (XEXP (op, 0), strict))
+       op = XEXP (op, 1);
+      else if (strict)
+       return false;
+    }
+
+  tocrel_base = op;
+  tocrel_offset = const0_rtx;
+  if (GET_CODE (op) == PLUS && CONST_INT_P (XEXP (op, 1)))
+    {
+      tocrel_base = XEXP (op, 0);
+      tocrel_offset = XEXP (op, 1);
+    }
+
   return (GET_CODE (tocrel_base) == UNSPEC
          && XINT (tocrel_base, 1) == UNSPEC_TOCREL);
 }
@@ -5293,14 +5303,7 @@ bool
 legitimate_constant_pool_address_p (const_rtx x, enum machine_mode mode,
                                    bool strict)
 {
-  return (TARGET_TOC
-         && (GET_CODE (x) == PLUS || GET_CODE (x) == LO_SUM)
-         && GET_CODE (XEXP (x, 0)) == REG
-         && (REGNO (XEXP (x, 0)) == TOC_REGISTER
-             || ((TARGET_MINIMAL_TOC
-                  || TARGET_CMODEL != CMODEL_SMALL)
-                 && INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict)))
-         && toc_relative_expr_p (XEXP (x, 1))
+  return (toc_relative_expr_p (x, strict)
          && (TARGET_CMODEL != CMODEL_MEDIUM
              || constant_pool_expr_p (XVECEXP (tocrel_base, 0, 0))
              || mode == QImode
@@ -5321,13 +5324,15 @@ legitimate_small_data_p (enum machine_mode mode, rtx x)
 #define SPE_CONST_OFFSET_OK(x) (((x) & ~0xf8) == 0)
 
 bool
-rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
+rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x,
+                                   bool strict, bool worst_case)
 {
-  unsigned HOST_WIDE_INT offset, extra;
+  unsigned HOST_WIDE_INT offset;
+  unsigned int extra;
 
   if (GET_CODE (x) != PLUS)
     return false;
-  if (GET_CODE (XEXP (x, 0)) != REG)
+  if (!REG_P (XEXP (x, 0)))
     return false;
   if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))
     return false;
@@ -5350,14 +5355,6 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
       return SPE_CONST_OFFSET_OK (offset);
 
     case DFmode:
-      if (TARGET_E500_DOUBLE)
-       return SPE_CONST_OFFSET_OK (offset);
-
-      /* If we are using VSX scalar loads, restrict ourselves to reg+reg
-        addressing.  */
-      if (VECTOR_MEM_VSX_P (DFmode))
-       return false;
-
     case DDmode:
     case DImode:
       /* On e500v2, we may have:
@@ -5368,25 +5365,33 @@ rs6000_legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
       if (TARGET_E500_DOUBLE)
        return SPE_CONST_OFFSET_OK (offset);
 
-      if (mode == DFmode || mode == DDmode || !TARGET_POWERPC64)
+      /* If we are using VSX scalar loads, restrict ourselves to reg+reg
+        addressing.  */
+      if (mode == DFmode && VECTOR_MEM_VSX_P (DFmode))
+       return false;
+
+      if (!worst_case)
+       break;
+      if (!TARGET_POWERPC64)
        extra = 4;
       else if (offset & 3)
        return false;
       break;
 
     case TFmode:
+    case TDmode:
+    case TImode:
       if (TARGET_E500_DOUBLE)
        return (SPE_CONST_OFFSET_OK (offset)
                && SPE_CONST_OFFSET_OK (offset + 8));
 
-    case TDmode:
-    case TImode:
-      if (mode == TFmode || mode == TDmode || !TARGET_POWERPC64)
+      extra = 8;
+      if (!worst_case)
+       break;
+      if (!TARGET_POWERPC64)
        extra = 12;
       else if (offset & 3)
        return false;
-      else
-       extra = 8;
       break;
 
     default:
@@ -5466,9 +5471,7 @@ legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
   if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), strict))
     return false;
   /* Restrict addressing for DI because of our SUBREG hackery.  */
-  if (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
-                            || mode == DDmode || mode == TDmode
-                            || mode == DImode))
+  if (TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD)
     return false;
   x = XEXP (x, 1);
 
@@ -5480,10 +5483,10 @@ legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
        return false;
       if (GET_MODE_NUNITS (mode) != 1)
        return false;
-      if (GET_MODE_BITSIZE (mode) > 64
-         || (GET_MODE_BITSIZE (mode) > 32 && !TARGET_POWERPC64
-             && !(TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
-                  && (mode == DFmode || mode == DDmode))))
+      if (GET_MODE_SIZE (mode) > UNITS_PER_WORD
+         && !(/* ??? Assume floating point reg based on mode?  */
+              TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT
+              && (mode == DFmode || mode == DDmode)))
        return false;
 
       return CONSTANT_P (x);
@@ -5518,7 +5521,7 @@ static rtx
 rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
                           enum machine_mode mode)
 {
-  unsigned int extra = 0;
+  unsigned int extra;
 
   if (!reg_offset_addressing_ok_p (mode))
     {
@@ -5545,22 +5548,18 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
        return rs6000_legitimize_tls_address (x, model);
     }
 
+  extra = 0;
   switch (mode)
     {
-    case DFmode:
-    case DDmode:
-      extra = 4;
-      break;
-    case DImode:
-      if (!TARGET_POWERPC64)
-       extra = 4;
-      break;
     case TFmode:
     case TDmode:
-      extra = 12;
-      break;
     case TImode:
-      extra = TARGET_POWERPC64 ? 8 : 12;
+      /* As in legitimate_offset_address_p we do not assume
+        worst-case.  The mode here is just a hint as to the registers
+        used.  A TImode is usually in gprs, but may actually be in
+        fprs.  Leave worst-case scenario for reload to handle via
+        insn constraints.  */
+      extra = 8;
       break;
     default:
       break;
@@ -5571,13 +5570,8 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
       && GET_CODE (XEXP (x, 1)) == CONST_INT
       && ((unsigned HOST_WIDE_INT) (INTVAL (XEXP (x, 1)) + 0x8000)
          >= 0x10000 - extra)
-      && !((TARGET_POWERPC64
-           && (mode == DImode || mode == TImode)
-           && (INTVAL (XEXP (x, 1)) & 3) != 0)
-          || SPE_VECTOR_MODE (mode)
-          || (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
-                                     || mode == DImode || mode == DDmode
-                                     || mode == TDmode))))
+      && !(SPE_VECTOR_MODE (mode)
+          || (TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD)))
     {
       HOST_WIDE_INT high_int, low_int;
       rtx sum;
@@ -5587,29 +5581,23 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
       high_int = INTVAL (XEXP (x, 1)) - low_int;
       sum = force_operand (gen_rtx_PLUS (Pmode, XEXP (x, 0),
                                         GEN_INT (high_int)), 0);
-      return plus_constant (sum, low_int);
+      return plus_constant (Pmode, sum, low_int);
     }
   else if (GET_CODE (x) == PLUS
           && GET_CODE (XEXP (x, 0)) == REG
           && GET_CODE (XEXP (x, 1)) != CONST_INT
           && GET_MODE_NUNITS (mode) == 1
-          && ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
-              || TARGET_POWERPC64
-              || ((mode != DImode && mode != DFmode && mode != DDmode)
-                  || (TARGET_E500_DOUBLE && mode != DDmode)))
-          && (TARGET_POWERPC64 || mode != DImode)
-          && !avoiding_indexed_address_p (mode)
-          && mode != TImode
-          && mode != TFmode
-          && mode != TDmode)
+          && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
+              || (/* ??? Assume floating point reg based on mode?  */
+                  (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
+                  && (mode == DFmode || mode == DDmode)))
+          && !avoiding_indexed_address_p (mode))
     {
       return gen_rtx_PLUS (Pmode, XEXP (x, 0),
                           force_reg (Pmode, force_operand (XEXP (x, 1), 0)));
     }
   else if (SPE_VECTOR_MODE (mode)
-          || (TARGET_E500_DOUBLE && (mode == DFmode || mode == TFmode
-                                     || mode == DDmode || mode == TDmode
-                                     || mode == DImode)))
+          || (TARGET_E500_DOUBLE && GET_MODE_SIZE (mode) > UNITS_PER_WORD))
     {
       if (mode == DImode)
        return x;
@@ -5642,7 +5630,11 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
 
       return force_reg (Pmode, x);
     }
-  else if (TARGET_ELF
+  else if ((TARGET_ELF
+#if TARGET_MACHO
+           || !MACHO_DYNAMIC_NO_PIC_P
+#endif
+           )
           && TARGET_32BIT
           && TARGET_NO_TOC
           && ! flag_pic
@@ -5650,40 +5642,23 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && GET_CODE (x) != CONST_DOUBLE
           && CONSTANT_P (x)
           && GET_MODE_NUNITS (mode) == 1
-          && (GET_MODE_BITSIZE (mode) <= 32
-              || ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
+          && (GET_MODE_SIZE (mode) <= UNITS_PER_WORD
+              || (/* ??? Assume floating point reg based on mode?  */
+                  (TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
                   && (mode == DFmode || mode == DDmode))))
     {
       rtx reg = gen_reg_rtx (Pmode);
-      emit_insn (gen_elf_high (reg, x));
-      return gen_rtx_LO_SUM (Pmode, reg, x);
-    }
-  else if (TARGET_MACHO && TARGET_32BIT && TARGET_NO_TOC
-          && ! flag_pic
-#if TARGET_MACHO
-          && ! MACHO_DYNAMIC_NO_PIC_P
-#endif
-          && GET_CODE (x) != CONST_INT
-          && GET_CODE (x) != CONST_DOUBLE
-          && CONSTANT_P (x)
-          && GET_MODE_NUNITS (mode) == 1
-          && ((TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT)
-              || (mode != DFmode && mode != DDmode))
-          && mode != DImode
-          && mode != TImode)
-    {
-      rtx reg = gen_reg_rtx (Pmode);
-      emit_insn (gen_macho_high (reg, x));
+      if (TARGET_ELF)
+       emit_insn (gen_elf_high (reg, x));
+      else
+       emit_insn (gen_macho_high (reg, x));
       return gen_rtx_LO_SUM (Pmode, reg, x);
     }
   else if (TARGET_TOC
           && GET_CODE (x) == SYMBOL_REF
           && constant_pool_expr_p (x)
           && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), Pmode))
-    {
-      rtx reg = TARGET_CMODEL != CMODEL_SMALL ? gen_reg_rtx (Pmode) : NULL_RTX;
-      return create_TOC_reference (x, reg);
-    }
+    return create_TOC_reference (x, NULL_RTX);
   else
     return x;
 }
@@ -5741,6 +5716,7 @@ rs6000_debug_legitimize_address (rtx x, rtx oldx, enum machine_mode mode)
 /* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
    We need to emit DTP-relative relocations.  */
 
+static void rs6000_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
 static void
 rs6000_output_dwarf_dtprel (FILE *file, int size, rtx x)
 {
@@ -5766,49 +5742,55 @@ rs6000_output_dwarf_dtprel (FILE *file, int size, rtx x)
 static rtx
 rs6000_delegitimize_address (rtx orig_x)
 {
-  rtx x, y;
+  rtx x, y, offset;
 
   orig_x = delegitimize_mem_from_attrs (orig_x);
   x = orig_x;
   if (MEM_P (x))
     x = XEXP (x, 0);
 
-  if (GET_CODE (x) == (TARGET_CMODEL != CMODEL_SMALL ? LO_SUM : PLUS)
-      && GET_CODE (XEXP (x, 1)) == CONST)
+  y = x;
+  if (TARGET_CMODEL != CMODEL_SMALL
+      && GET_CODE (y) == LO_SUM)
+    y = XEXP (y, 1);
+
+  offset = NULL_RTX;
+  if (GET_CODE (y) == PLUS
+      && GET_MODE (y) == Pmode
+      && CONST_INT_P (XEXP (y, 1)))
     {
-      rtx offset = NULL_RTX;
+      offset = XEXP (y, 1);
+      y = XEXP (y, 0);
+    }
 
-      y = XEXP (XEXP (x, 1), 0);
-      if (GET_CODE (y) == PLUS
-         && GET_MODE (y) == Pmode
-         && CONST_INT_P (XEXP (y, 1)))
+  if (GET_CODE (y) == UNSPEC
+      && XINT (y, 1) == UNSPEC_TOCREL)
+    {
+#ifdef ENABLE_CHECKING
+      if (REG_P (XVECEXP (y, 0, 1))
+         && REGNO (XVECEXP (y, 0, 1)) == TOC_REGISTER)
        {
-         offset = XEXP (y, 1);
-         y = XEXP (y, 0);
+         /* All good.  */
        }
-      if (GET_CODE (y) == UNSPEC
-          && XINT (y, 1) == UNSPEC_TOCREL
-         && ((GET_CODE (XEXP (x, 0)) == REG
-              && (REGNO (XEXP (x, 0)) == TOC_REGISTER
-                  || TARGET_MINIMAL_TOC
-                  || TARGET_CMODEL != CMODEL_SMALL))
-             || (TARGET_CMODEL != CMODEL_SMALL
-                 && GET_CODE (XEXP (x, 0)) == CONST
-                 && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS
-                 && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 0)) == REG
-                 && REGNO (XEXP (XEXP (XEXP (x, 0), 0), 0)) == TOC_REGISTER
-                 && GET_CODE (XEXP (XEXP (XEXP (x, 0), 0), 1)) == HIGH
-                 && rtx_equal_p (XEXP (x, 1),
-                                 XEXP (XEXP (XEXP (XEXP (x, 0), 0), 1), 0)))))
-       {
-         y = XVECEXP (y, 0, 0);
-         if (offset != NULL_RTX)
-           y = gen_rtx_PLUS (Pmode, y, offset);
-         if (!MEM_P (orig_x))
-           return y;
-         else
-           return replace_equiv_address_nv (orig_x, y);
+      else if (GET_CODE (XVECEXP (y, 0, 1)) == DEBUG_EXPR)
+       {
+         /* Weirdness alert.  df_note_compute can replace r2 with a
+            debug_expr when this unspec is in a debug_insn.
+            Seen in gcc.dg/pr51957-1.c  */
        }
+      else
+       {
+         debug_rtx (orig_x);
+         abort ();
+       }
+#endif
+      y = XVECEXP (y, 0, 0);
+      if (offset != NULL_RTX)
+       y = gen_rtx_PLUS (Pmode, y, offset);
+      if (!MEM_P (orig_x))
+       return y;
+      else
+       return replace_equiv_address_nv (orig_x, y);
     }
 
   if (TARGET_MACHO
@@ -6070,9 +6052,8 @@ rs6000_tls_referenced_p (rtx x)
 static bool
 rs6000_cannot_force_const_mem (enum machine_mode mode ATTRIBUTE_UNUSED, rtx x)
 {
-  if (GET_CODE (x) == CONST
-      && GET_CODE (XEXP (x, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (x, 0), 1)) == HIGH)
+  if (GET_CODE (x) == HIGH
+      && GET_CODE (XEXP (x, 0)) == UNSPEC)
     return true;
 
   return rs6000_tls_referenced_p (x);
@@ -6087,6 +6068,21 @@ rs6000_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
   return RS6000_SYMBOL_REF_TLS_P (*x);
 }
 
+/* Return true iff the given SYMBOL_REF refers to a constant pool entry
+   that we have put in the TOC, or for cmodel=medium, if the SYMBOL_REF
+   can be addressed relative to the toc pointer.  */
+
+static bool
+use_toc_relative_ref (rtx sym)
+{
+  return ((constant_pool_expr_p (sym)
+          && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (sym),
+                                              get_pool_mode (sym)))
+         || (TARGET_CMODEL == CMODEL_MEDIUM
+             && !CONSTANT_POOL_ADDRESS_P (sym)
+             && SYMBOL_REF_LOCAL_P (sym)));
+}
+
 /* Our implementation of LEGITIMIZE_RELOAD_ADDRESS.  Returns a value to
    replace the input X, or the original X if no replacement is called for.
    The output parameter *WIN is 1 if the calling macro should goto WIN,
@@ -6124,7 +6120,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
     {
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
-                  opnum, (enum reload_type)type);
+                  opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6135,7 +6131,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
     {
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-                  opnum, (enum reload_type)type);
+                  opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6153,24 +6149,18 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
         floating point constant.  */
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-                  opnum, (enum reload_type)type);
+                  opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
 #endif
 
   if (TARGET_CMODEL != CMODEL_SMALL
-      && GET_CODE (x) == LO_SUM
-      && GET_CODE (XEXP (x, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
-      && REGNO (XEXP (XEXP (x, 0), 0)) == TOC_REGISTER
-      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST
-      && GET_CODE (XEXP (XEXP (XEXP (x, 0), 1), 0)) == HIGH
-      && GET_CODE (XEXP (x, 1)) == CONST
-      && GET_CODE (XEXP (XEXP (x, 1), 0)) == UNSPEC
-      && XINT (XEXP (XEXP (x, 1), 0), 1) == UNSPEC_TOCREL
-      && rtx_equal_p (XEXP (XEXP (XEXP (XEXP (x, 0), 1), 0), 0), XEXP (x, 1)))
+      && reg_offset_p
+      && small_toc_ref (x, VOIDmode))
     {
+      rtx hi = gen_rtx_HIGH (Pmode, copy_rtx (x));
+      x = gen_rtx_LO_SUM (Pmode, hi, x);
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
                   opnum, (enum reload_type) type);
@@ -6233,7 +6223,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
 
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, GET_MODE (x), VOIDmode, 0, 0,
-                  opnum, (enum reload_type)type);
+                  opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6252,7 +6242,18 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
 #endif
       /* Don't do this for TFmode or TDmode, since the result isn't offsettable.
         The same goes for DImode without 64-bit gprs and DFmode and DDmode
-        without fprs.  */
+        without fprs.
+        ??? Assume floating point reg based on mode?  This assumption is
+        violated by eg. powerpc-linux -m32 compile of gcc.dg/pr28796-2.c
+        where reload ends up doing a DFmode load of a constant from
+        mem using two gprs.  Unfortunately, at this point reload
+        hasn't yet selected regs so poking around in reload data
+        won't help and even if we could figure out the regs reliably,
+        we'd still want to allow this transformation when the mem is
+        naturally aligned.  Since we say the address is good here, we
+        can't disable offsets from LO_SUMs in mem_operand_gpr.
+        FIXME: Allow offset from lo_sum for other modes too, when
+        mem is sufficiently aligned.  */
       && mode != TFmode
       && mode != TDmode
       && (mode != DImode || TARGET_POWERPC64)
@@ -6274,7 +6275,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
 
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
                   BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-                  opnum, (enum reload_type)type);
+                  opnum, (enum reload_type) type);
       *win = 1;
       return x;
     }
@@ -6301,8 +6302,7 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
   if (TARGET_TOC
       && reg_offset_p
       && GET_CODE (x) == SYMBOL_REF
-      && constant_pool_expr_p (x)
-      && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (x), mode))
+      && use_toc_relative_ref (x))
     {
       x = create_TOC_reference (x, NULL_RTX);
       if (TARGET_CMODEL != CMODEL_SMALL)
@@ -6360,7 +6360,7 @@ rs6000_debug_legitimize_reload_address (rtx x, enum machine_mode mode,
    32-bit DImode, TImode, TFmode, TDmode), indexed addressing cannot be used
    because adjacent memory cells are accessed by adding word-sized offsets
    during assembly output.  */
-bool
+static bool
 rs6000_legitimate_address_p (enum machine_mode mode, rtx x, bool reg_ok_strict)
 {
   bool reg_offset_p = reg_offset_addressing_ok_p (mode);
@@ -6403,7 +6403,7 @@ rs6000_legitimate_address_p (enum machine_mode mode, rtx x, bool reg_ok_strict)
          || XEXP (x, 0) == arg_pointer_rtx)
       && GET_CODE (XEXP (x, 1)) == CONST_INT)
     return 1;
-  if (rs6000_legitimate_offset_address_p (mode, x, reg_ok_strict))
+  if (rs6000_legitimate_offset_address_p (mode, x, reg_ok_strict, false))
     return 1;
   if (mode != TImode
       && mode != TFmode
@@ -6431,7 +6431,8 @@ rs6000_legitimate_address_p (enum machine_mode mode, rtx x, bool reg_ok_strict)
           && (mode == DFmode || mode == DDmode || mode == DImode))
       && TARGET_UPDATE
       && legitimate_indirect_address_p (XEXP (x, 0), reg_ok_strict)
-      && (rs6000_legitimate_offset_address_p (mode, XEXP (x, 1), reg_ok_strict)
+      && (rs6000_legitimate_offset_address_p (mode, XEXP (x, 1),
+                                             reg_ok_strict, false)
          || (!avoiding_indexed_address_p (mode)
              && legitimate_indexed_address_p (XEXP (x, 1), reg_ok_strict)))
       && rtx_equal_p (XEXP (XEXP (x, 1), 0), XEXP (x, 0)))
@@ -6495,7 +6496,7 @@ rs6000_mode_dependent_address (const_rtx addr)
          && GET_CODE (XEXP (addr, 1)) == CONST_INT)
        {
          unsigned HOST_WIDE_INT val = INTVAL (XEXP (addr, 1));
-         return val + 12 + 0x8000 >= 0x10000;
+         return val + 0x8000 >= 0x10000 - (TARGET_POWERPC64 ? 8 : 12);
        }
       break;
 
@@ -6537,9 +6538,13 @@ rs6000_debug_mode_dependent_address (const_rtx addr)
 rtx
 rs6000_find_base_term (rtx op)
 {
-  rtx base, offset;
+  rtx base;
 
-  split_const (op, &base, &offset);
+  base = op;
+  if (GET_CODE (base) == CONST)
+    base = XEXP (base, 0);
+  if (GET_CODE (base) == PLUS)
+    base = XEXP (base, 0);
   if (GET_CODE (base) == UNSPEC)
     switch (XINT (base, 1))
       {
@@ -6561,14 +6566,16 @@ rs6000_find_base_term (rtx op)
 
    in 32-bit mode, that the recog predicate rejects.  */
 
-bool
-rs6000_offsettable_memref_p (rtx op)
+static bool
+rs6000_offsettable_memref_p (rtx op, enum machine_mode reg_mode)
 {
+  bool worst_case;
+
   if (!MEM_P (op))
     return false;
 
   /* First mimic offsettable_memref_p.  */
-  if (offsettable_address_p (1, GET_MODE (op), XEXP (op, 0)))
+  if (offsettable_address_p (true, GET_MODE (op), XEXP (op, 0)))
     return true;
 
   /* offsettable_address_p invokes rs6000_mode_dependent_address, but
@@ -6576,8 +6583,13 @@ rs6000_offsettable_memref_p (rtx op)
      reference and, therefore, assumes that it is the largest supported
      mode (TFmode).  As a consequence, legitimate offsettable memory
      references are rejected.  rs6000_legitimate_offset_address_p contains
-     the correct logic for the PLUS case of rs6000_mode_dependent_address.  */
-  return rs6000_legitimate_offset_address_p (GET_MODE (op), XEXP (op, 0), 1);
+     the correct logic for the PLUS case of rs6000_mode_dependent_address,
+     at least with a little bit of help here given that we know the
+     actual registers used.  */
+  worst_case = ((TARGET_POWERPC64 && GET_MODE_CLASS (reg_mode) == MODE_INT)
+               || GET_MODE_SIZE (reg_mode) == 4);
+  return rs6000_legitimate_offset_address_p (GET_MODE (op), XEXP (op, 0),
+                                            true, worst_case);
 }
 
 /* Change register usage conditional on target flags.  */
@@ -6589,11 +6601,9 @@ rs6000_conditional_register_usage (void)
   if (TARGET_DEBUG_TARGET)
     fprintf (stderr, "rs6000_conditional_register_usage called\n");
 
-  /* Set MQ register fixed (already call_used) if not POWER
-     architecture (RIOS1, RIOS2, RSC, and PPC601) so that it will not
-     be allocated.  */
-  if (! TARGET_POWER)
-    fixed_regs[64] = 1;
+  /* Set MQ register fixed (already call_used) so that it will not be
+     allocated.  */
+  fixed_regs[64] = 1;
 
   /* 64-bit AIX and Linux reserve GPR13 for thread-private data.  */
   if (TARGET_64BIT)
@@ -6946,32 +6956,6 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
       && !gpc_reg_operand (operands[1], mode))
     operands[1] = force_reg (mode, operands[1]);
 
-  if (mode == SFmode && ! TARGET_POWERPC
-      && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_DOUBLE_FLOAT 
-      && GET_CODE (operands[0]) == MEM)
-    {
-      int regnum;
-
-      if (reload_in_progress || reload_completed)
-       regnum = true_regnum (operands[1]);
-      else if (GET_CODE (operands[1]) == REG)
-       regnum = REGNO (operands[1]);
-      else
-       regnum = -1;
-
-      /* If operands[1] is a register, on POWER it may have
-        double-precision data in it, so truncate it to single
-        precision.  */
-      if (FP_REGNO_P (regnum) || regnum >= FIRST_PSEUDO_REGISTER)
-       {
-         rtx newreg;
-         newreg = (!can_create_pseudo_p () ? copy_rtx (operands[1])
-                   : gen_reg_rtx (mode));
-         emit_insn (gen_aux_truncdfsf2 (newreg, operands[1]));
-         operands[1] = newreg;
-       }
-    }
-
   /* Recognize the case where operand[1] is a reference to thread-local
      data and load its address to a register.  */
   if (rs6000_tls_referenced_p (operands[1]))
@@ -7191,33 +7175,13 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
       /* If this is a SYMBOL_REF that refers to a constant pool entry,
         and we have put it in the TOC, we just need to make a TOC-relative
         reference to it.  */
-      if ((TARGET_TOC
-          && GET_CODE (operands[1]) == SYMBOL_REF
-          && constant_pool_expr_p (operands[1])
-          && ASM_OUTPUT_SPECIAL_POOL_ENTRY_P (get_pool_constant (operands[1]),
-                                              get_pool_mode (operands[1])))
-         || (TARGET_CMODEL == CMODEL_MEDIUM
-             && GET_CODE (operands[1]) == SYMBOL_REF
-             && !CONSTANT_POOL_ADDRESS_P (operands[1])
-             && SYMBOL_REF_LOCAL_P (operands[1])))
-       {
-         rtx reg = NULL_RTX;
-         if (TARGET_CMODEL != CMODEL_SMALL)
-           {
-             if (can_create_pseudo_p ())
-               reg = gen_reg_rtx (Pmode);
-             else
-               reg = operands[0];
-           }
-         operands[1] = create_TOC_reference (operands[1], reg);
-       }
+      if (TARGET_TOC
+         && GET_CODE (operands[1]) == SYMBOL_REF
+         && use_toc_relative_ref (operands[1]))
+       operands[1] = create_TOC_reference (operands[1], operands[0]);
       else if (mode == Pmode
               && CONSTANT_P (operands[1])
               && GET_CODE (operands[1]) != HIGH
-              && !(TARGET_CMODEL != CMODEL_SMALL
-                   && GET_CODE (operands[1]) == CONST
-                   && GET_CODE (XEXP (operands[1], 0)) == PLUS
-                   && GET_CODE (XEXP (XEXP (operands[1], 0), 1)) == HIGH)
               && ((GET_CODE (operands[1]) != CONST_INT
                    && ! easy_fp_constant (operands[1], mode))
                   || (GET_CODE (operands[1]) == CONST_INT
@@ -7225,9 +7189,7 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                           > (TARGET_CMODEL != CMODEL_SMALL ? 3 : 2)))
                   || (GET_CODE (operands[0]) == REG
                       && FP_REGNO_P (REGNO (operands[0]))))
-              && ! legitimate_constant_pool_address_p (operands[1], mode,
-                                                       false)
-              && ! toc_relative_expr_p (operands[1])
+              && !toc_relative_expr_p (operands[1], false)
               && (TARGET_CMODEL == CMODEL_SMALL
                   || can_create_pseudo_p ()
                   || (REG_P (operands[0])
@@ -7277,16 +7239,8 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                        get_pool_constant (XEXP (operands[1], 0)),
                        get_pool_mode (XEXP (operands[1], 0))))
            {
-             rtx tocref;
-             rtx reg = NULL_RTX;
-             if (TARGET_CMODEL != CMODEL_SMALL)
-               {
-                 if (can_create_pseudo_p ())
-                   reg = gen_reg_rtx (Pmode);
-                 else
-                   reg = operands[0];
-               }
-             tocref = create_TOC_reference (XEXP (operands[1], 0), reg);
+             rtx tocref = create_TOC_reference (XEXP (operands[1], 0),
+                                                operands[0]);
              operands[1] = gen_const_mem (mode, tocref);
              set_mem_alias_set (operands[1], get_TOC_alias_set ());
            }
@@ -7295,17 +7249,6 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
 
     case TImode:
       rs6000_eliminate_indexed_memrefs (operands);
-
-      if (TARGET_POWER)
-       {
-         emit_insn (gen_rtx_PARALLEL (VOIDmode,
-                      gen_rtvec (2,
-                                 gen_rtx_SET (VOIDmode,
-                                              operands[0], operands[1]),
-                                 gen_rtx_CLOBBER (VOIDmode,
-                                                  gen_rtx_SCRATCH (SImode)))));
-         return;
-       }
       break;
 
     default:
@@ -7321,6 +7264,26 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
  emit_set:
   emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
 }
+
+/* Return true if a structure, union or array containing FIELD should be
+   accessed using `BLKMODE'.
+
+   For the SPE, simd types are V2SI, and gcc can be tempted to put the
+   entire thing in a DI and use subregs to access the internals.
+   store_bit_field() will force (subreg:DI (reg:V2SI x))'s to the
+   back-end.  Because a single GPR can hold a V2SI, but not a DI, the
+   best thing to do is set structs to BLKmode and avoid Severe Tire
+   Damage.
+
+   On e500 v2, DF and DI modes suffer from the same anomaly.  DF can
+   fit into 1, whereas DI still needs two.  */
+
+static bool
+rs6000_member_type_forces_blk (const_tree field, enum machine_mode mode)
+{
+  return ((TARGET_SPE && TREE_CODE (TREE_TYPE (field)) == VECTOR_TYPE)
+         || (TARGET_E500_DOUBLE && mode == DFmode));
+}
 \f
 /* Nonzero if we can use a floating-point register to pass this arg.  */
 #define USE_FP_FOR_ARG_P(CUM,MODE,TYPE)                \
@@ -8944,7 +8907,7 @@ setup_incoming_varargs (cumulative_args_t cum, enum machine_mode mode,
            }
 
          cfun->machine->varargs_save_offset = offset;
-         save_area = plus_constant (virtual_stack_vars_rtx, offset);
+         save_area = plus_constant (Pmode, virtual_stack_vars_rtx, offset);
        }
     }
   else
@@ -8976,7 +8939,7 @@ setup_incoming_varargs (cumulative_args_t cum, enum machine_mode mode,
        }
 
       mem = gen_rtx_MEM (BLKmode,
-                        plus_constant (save_area,
+                        plus_constant (Pmode, save_area,
                                        first_reg_offset * reg_size));
       MEM_NOTRAP_P (mem) = 1;
       set_mem_alias_set (mem, set);
@@ -9014,7 +8977,7 @@ setup_incoming_varargs (cumulative_args_t cum, enum machine_mode mode,
        {
          mem = gen_rtx_MEM ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
                              ? DFmode : SFmode, 
-                             plus_constant (save_area, off));
+                             plus_constant (Pmode, save_area, off));
          MEM_NOTRAP_P (mem) = 1;
          set_mem_alias_set (mem, set);
          set_mem_align (mem, GET_MODE_ALIGNMENT (
@@ -9174,7 +9137,7 @@ rs6000_va_start (tree valist, rtx nextarg)
 
 /* Implement va_arg.  */
 
-tree
+static tree
 rs6000_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
                        gimple_seq *post_p)
 {
@@ -11369,9 +11332,6 @@ rs6000_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
     case RS6000_BUILTIN_RSQRT:
       return rs6000_expand_unop_builtin (CODE_FOR_rsqrtdf2, exp, target);
 
-    case RS6000_BUILTIN_BSWAP_HI:
-      return rs6000_expand_unop_builtin (CODE_FOR_bswaphi2, exp, target);
-
     case POWER7_BUILTIN_BPERMD:
       return rs6000_expand_binop_builtin (((TARGET_64BIT)
                                           ? CODE_FOR_bpermd_di
@@ -11563,25 +11523,17 @@ rs6000_init_builtins (void)
   builtin_mode_to_type[V16QImode][0] = V16QI_type_node;
   builtin_mode_to_type[V16QImode][1] = unsigned_V16QI_type_node;
 
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                     get_identifier ("__bool char"),
-                     bool_char_type_node);
+  tdecl = add_builtin_type ("__bool char", bool_char_type_node);
   TYPE_NAME (bool_char_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                     get_identifier ("__bool short"),
-                     bool_short_type_node);
+
+  tdecl = add_builtin_type ("__bool short", bool_short_type_node);
   TYPE_NAME (bool_short_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                     get_identifier ("__bool int"),
-                     bool_int_type_node);
+
+  tdecl = add_builtin_type ("__bool int", bool_int_type_node);
   TYPE_NAME (bool_int_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL, get_identifier ("__pixel"),
-                     pixel_type_node);
+
+  tdecl = add_builtin_type ("__pixel", pixel_type_node);
   TYPE_NAME (pixel_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
   bool_V16QI_type_node = build_vector_type (bool_char_type_node, 16);
   bool_V8HI_type_node = build_vector_type (bool_short_type_node, 8);
@@ -11589,88 +11541,50 @@ rs6000_init_builtins (void)
   bool_V2DI_type_node = build_vector_type (bool_long_type_node, 2);
   pixel_V8HI_type_node = build_vector_type (pixel_type_node, 8);
 
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                     get_identifier ("__vector unsigned char"),
-                     unsigned_V16QI_type_node);
+  tdecl = add_builtin_type ("__vector unsigned char", unsigned_V16QI_type_node);
   TYPE_NAME (unsigned_V16QI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector signed char"),
-                     V16QI_type_node);
+
+  tdecl = add_builtin_type ("__vector signed char", V16QI_type_node);
   TYPE_NAME (V16QI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector __bool char"),
-                     bool_V16QI_type_node);
+
+  tdecl = add_builtin_type ("__vector __bool char", bool_V16QI_type_node);
   TYPE_NAME ( bool_V16QI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector unsigned short"),
-                     unsigned_V8HI_type_node);
+  tdecl = add_builtin_type ("__vector unsigned short", unsigned_V8HI_type_node);
   TYPE_NAME (unsigned_V8HI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector signed short"),
-                     V8HI_type_node);
+
+  tdecl = add_builtin_type ("__vector signed short", V8HI_type_node);
   TYPE_NAME (V8HI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                     get_identifier ("__vector __bool short"),
-                     bool_V8HI_type_node);
+
+  tdecl = add_builtin_type ("__vector __bool short", bool_V8HI_type_node);
   TYPE_NAME (bool_V8HI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                     get_identifier ("__vector unsigned int"),
-                     unsigned_V4SI_type_node);
+  tdecl = add_builtin_type ("__vector unsigned int", unsigned_V4SI_type_node);
   TYPE_NAME (unsigned_V4SI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector signed int"),
-                     V4SI_type_node);
+
+  tdecl = add_builtin_type ("__vector signed int", V4SI_type_node);
   TYPE_NAME (V4SI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector __bool int"),
-                     bool_V4SI_type_node);
+
+  tdecl = add_builtin_type ("__vector __bool int", bool_V4SI_type_node);
   TYPE_NAME (bool_V4SI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector float"),
-                     V4SF_type_node);
+  tdecl = add_builtin_type ("__vector float", V4SF_type_node);
   TYPE_NAME (V4SF_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector __pixel"),
-                     pixel_V8HI_type_node);
+
+  tdecl = add_builtin_type ("__vector __pixel", pixel_V8HI_type_node);
   TYPE_NAME (pixel_V8HI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector double"),
-                     V2DF_type_node);
+  tdecl = add_builtin_type ("__vector double", V2DF_type_node);
   TYPE_NAME (V2DF_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector long"),
-                     V2DI_type_node);
+  tdecl = add_builtin_type ("__vector long", V2DI_type_node);
   TYPE_NAME (V2DI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector unsigned long"),
-                     unsigned_V2DI_type_node);
+  tdecl = add_builtin_type ("__vector unsigned long", unsigned_V2DI_type_node);
   TYPE_NAME (unsigned_V2DI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
-  tdecl = build_decl (BUILTINS_LOCATION,
-                     TYPE_DECL, get_identifier ("__vector __bool long"),
-                     bool_V2DI_type_node);
+  tdecl = add_builtin_type ("__vector __bool long", bool_V2DI_type_node);
   TYPE_NAME (bool_V2DI_type_node) = tdecl;
-  (*lang_hooks.decls.pushdecl) (tdecl);
 
   /* Paired and SPE builtins are only available if you build a compiler with
      the appropriate options, so only create those builtins with the
@@ -11707,12 +11621,6 @@ rs6000_init_builtins (void)
                                 POWER7_BUILTIN_BPERMD, "__builtin_bpermd");
   def_builtin ("__builtin_bpermd", ftype, POWER7_BUILTIN_BPERMD);
 
-      /* Don't use builtin_function_type here, as it maps HI/QI to SI.  */
-  ftype = build_function_type_list (unsigned_intHI_type_node,
-                                   unsigned_intHI_type_node,
-                                   NULL_TREE);
-  def_builtin ("__builtin_bswap16", ftype, RS6000_BUILTIN_BSWAP_HI);
-
 #if TARGET_XCOFF
   /* AIX libm provides clog as __clog.  */
   if ((tdecl = builtin_decl_explicit (BUILT_IN_CLOG)) != NULL_TREE)
@@ -11839,10 +11747,7 @@ spe_init_builtins (void)
                                 signed_char_type_node,
                                 NULL_TREE);
 
-  (*lang_hooks.decls.pushdecl)
-    (build_decl (BUILTINS_LOCATION, TYPE_DECL,
-                get_identifier ("__ev64_opaque__"),
-                opaque_V2SI_type_node));
+  add_builtin_type ("__ev64_opaque__", opaque_V2SI_type_node);
 
   /* Initialize irregular SPE builtins.  */
 
@@ -12777,16 +12682,6 @@ rs6000_common_init_builtins (void)
 static void
 rs6000_init_libfuncs (void)
 {
-  if (DEFAULT_ABI != ABI_V4 && TARGET_XCOFF
-      && !TARGET_POWER2 && !TARGET_POWERPC)
-    {
-      /* AIX library routines for float->int conversion.  */
-      set_conv_libfunc (sfix_optab, SImode, DFmode, "__itrunc");
-      set_conv_libfunc (ufix_optab, SImode, DFmode, "__uitrunc");
-      set_conv_libfunc (sfix_optab, SImode, TFmode, "_qitrunc");
-      set_conv_libfunc (ufix_optab, SImode, TFmode, "_quitrunc");
-    }
-
   if (!TARGET_IEEEQUAD)
       /* AIX/Darwin/64-bit Linux quad floating point routines.  */
     if (!TARGET_XL_COMPAT)
@@ -12835,7 +12730,7 @@ rs6000_init_libfuncs (void)
       set_optab_libfunc (neg_optab, TFmode, "_q_neg");
       set_optab_libfunc (smul_optab, TFmode, "_q_mul");
       set_optab_libfunc (sdiv_optab, TFmode, "_q_div");
-      if (TARGET_PPC_GPOPT || TARGET_POWER2)
+      if (TARGET_PPC_GPOPT)
        set_optab_libfunc (sqrt_optab, TFmode, "_q_sqrt");
 
       set_optab_libfunc (eq_optab, TFmode, "_q_feq");
@@ -13156,7 +13051,7 @@ rs6000_output_load_multiple (rtx operands[3])
   rtx xop[10];
 
   if (XVECLEN (operands[0], 0) == 1)
-    return "{l|lwz} %2,0(%1)";
+    return "lwz %2,0(%1)";
 
   for (i = 0; i < words; i++)
     if (refers_to_regno_p (REGNO (operands[2]) + i,
@@ -13167,7 +13062,7 @@ rs6000_output_load_multiple (rtx operands[3])
            xop[0] = GEN_INT (4 * (words-1));
            xop[1] = operands[1];
            xop[2] = operands[2];
-           output_asm_insn ("{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,%0(%1)", xop);
+           output_asm_insn ("lswi %2,%1,%0\n\tlwz %1,%0(%1)", xop);
            return "";
          }
        else if (i == 0)
@@ -13175,7 +13070,7 @@ rs6000_output_load_multiple (rtx operands[3])
            xop[0] = GEN_INT (4 * (words-1));
            xop[1] = operands[1];
            xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + 1);
-           output_asm_insn ("{cal %1,4(%1)|addi %1,%1,4}\n\t{lsi|lswi} %2,%1,%0\n\t{l|lwz} %1,-4(%1)", xop);
+           output_asm_insn ("addi %1,%1,4\n\tlswi %2,%1,%0\n\tlwz %1,-4(%1)", xop);
            return "";
          }
        else
@@ -13186,16 +13081,16 @@ rs6000_output_load_multiple (rtx operands[3])
                  xop[0] = GEN_INT (j * 4);
                  xop[1] = operands[1];
                  xop[2] = gen_rtx_REG (SImode, REGNO (operands[2]) + j);
-                 output_asm_insn ("{l|lwz} %2,%0(%1)", xop);
+                 output_asm_insn ("lwz %2,%0(%1)", xop);
                }
            xop[0] = GEN_INT (i * 4);
            xop[1] = operands[1];
-           output_asm_insn ("{l|lwz} %1,%0(%1)", xop);
+           output_asm_insn ("lwz %1,%0(%1)", xop);
            return "";
          }
       }
 
-  return "{lsi|lswi} %2,%1,%N0";
+  return "lswi %2,%1,%N0";
 }
 
 \f
@@ -13676,7 +13571,8 @@ rs6000_secondary_reload (bool in_p,
          if (rclass == GENERAL_REGS || rclass == BASE_REGS)
            {
              if (!legitimate_indirect_address_p (addr, false)
-                 && !rs6000_legitimate_offset_address_p (TImode, addr, false))
+                 && !rs6000_legitimate_offset_address_p (TImode, addr,
+                                                         false, true))
                {
                  sri->icode = icode;
                  /* account for splitting the loads, and converting the
@@ -13749,18 +13645,12 @@ rs6000_secondary_reload (bool in_p,
           && MEM_P (x)
           && GET_MODE_SIZE (GET_MODE (x)) >= UNITS_PER_WORD)
     {
-      rtx addr = XEXP (x, 0);
-
-      if (GET_CODE (addr) == PRE_MODIFY)
-       addr = XEXP (addr, 1);
-      else if (GET_CODE (addr) == LO_SUM
-              && GET_CODE (XEXP (addr, 0)) == REG
-              && GET_CODE (XEXP (addr, 1)) == CONST)
-       addr = XEXP (XEXP (addr, 1), 0);
+      rtx off = address_offset (XEXP (x, 0));
+      unsigned int extra = GET_MODE_SIZE (GET_MODE (x)) - UNITS_PER_WORD;
 
-      if (GET_CODE (addr) == PLUS
-         && GET_CODE (XEXP (addr, 1)) == CONST_INT
-         && (INTVAL (XEXP (addr, 1)) & 3) != 0)
+      if (off != NULL_RTX
+         && (INTVAL (off) & 3) != 0
+         && (unsigned HOST_WIDE_INT) INTVAL (off) + 0x8000 < 0x10000 - extra)
        {
          if (in_p)
            sri->icode = CODE_FOR_reload_di_load;
@@ -13772,6 +13662,34 @@ rs6000_secondary_reload (bool in_p,
       else
        default_p = true;
     }
+  else if (!TARGET_POWERPC64
+          && rs6000_reload_register_type (rclass) == GPR_REGISTER_TYPE
+          && MEM_P (x)
+          && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+    {
+      rtx off = address_offset (XEXP (x, 0));
+      unsigned int extra = GET_MODE_SIZE (GET_MODE (x)) - UNITS_PER_WORD;
+
+      /* We need a secondary reload only when our legitimate_address_p
+        says the address is good (as otherwise the entire address
+        will be reloaded).  So for mode sizes of 8 and 16 this will
+        be when the offset is in the ranges [0x7ffc,0x7fff] and
+        [0x7ff4,0x7ff7] respectively.  Note that the address we see
+        here may have been manipulated by legitimize_reload_address.  */
+      if (off != NULL_RTX
+         && ((unsigned HOST_WIDE_INT) INTVAL (off) - (0x8000 - extra)
+             < UNITS_PER_WORD))
+       {
+         if (in_p)
+           sri->icode = CODE_FOR_reload_si_load;
+         else
+           sri->icode = CODE_FOR_reload_si_store;
+         sri->extra_cost = 2;
+         ret = NO_REGS;
+       }
+      else
+       default_p = true;
+    }
   else
     default_p = true;
 
@@ -13860,8 +13778,9 @@ rs6000_secondary_reload_inner (rtx reg, rtx mem, rtx scratch, bool store_p)
        }
 
       if (GET_CODE (addr) == PLUS
-         && (!rs6000_legitimate_offset_address_p (TImode, addr, false)
-             || and_op2 != NULL_RTX))
+         && (and_op2 != NULL_RTX
+             || !rs6000_legitimate_offset_address_p (TImode, addr,
+                                                     false, true)))
        {
          addr_op1 = XEXP (addr, 0);
          addr_op2 = XEXP (addr, 1);
@@ -13893,7 +13812,8 @@ rs6000_secondary_reload_inner (rtx reg, rtx mem, rtx scratch, bool store_p)
          scratch_or_premodify = scratch;
        }
       else if (!legitimate_indirect_address_p (addr, false)
-              && !rs6000_legitimate_offset_address_p (TImode, addr, false))
+              && !rs6000_legitimate_offset_address_p (TImode, addr,
+                                                      false, true))
        {
          if (TARGET_DEBUG_ADDR)
            {
@@ -13952,7 +13872,7 @@ rs6000_secondary_reload_inner (rtx reg, rtx mem, rtx scratch, bool store_p)
              && GET_MODE_SIZE (mode) == 8
              && and_op2 == NULL_RTX
              && scratch_or_premodify == scratch
-             && rs6000_legitimate_offset_address_p (mode, addr, false)))
+             && rs6000_legitimate_offset_address_p (mode, addr, false, false)))
        ;
 
       else if (GET_CODE (addr) == PLUS)
@@ -14061,10 +13981,11 @@ rs6000_secondary_reload_inner (rtx reg, rtx mem, rtx scratch, bool store_p)
 }
 
 /* Convert reloads involving 64-bit gprs and misaligned offset
-   addressing to use indirect addressing.  */
+   addressing, or multiple 32-bit gprs and offsets that are too large,
+   to use indirect addressing.  */
 
 void
-rs6000_secondary_reload_ppc64 (rtx reg, rtx mem, rtx scratch, bool store_p)
+rs6000_secondary_reload_gpr (rtx reg, rtx mem, rtx scratch, bool store_p)
 {
   int regno = true_regnum (reg);
   enum reg_class rclass;
@@ -14073,7 +13994,7 @@ rs6000_secondary_reload_ppc64 (rtx reg, rtx mem, rtx scratch, bool store_p)
 
   if (TARGET_DEBUG_ADDR)
     {
-      fprintf (stderr, "\nrs6000_secondary_reload_ppc64, type = %s\n",
+      fprintf (stderr, "\nrs6000_secondary_reload_gpr, type = %s\n",
               store_p ? "store" : "load");
       fprintf (stderr, "reg:\n");
       debug_rtx (reg);
@@ -14703,7 +14624,6 @@ void
 print_operand (FILE *file, rtx x, int code)
 {
   int i;
-  HOST_WIDE_INT val;
   unsigned HOST_WIDE_INT uval;
 
   switch (code)
@@ -14755,7 +14675,7 @@ print_operand (FILE *file, rtx x, int code)
 
     case 'D':
       /* Like 'J' but get to the GT bit only.  */
-      gcc_assert (GET_CODE (x) == REG);
+      gcc_assert (REG_P (x));
 
       /* Bit 1 is GT bit.  */
       i = 4 * (REGNO (x) - CR0_REGNO) + 1;
@@ -14875,18 +14795,18 @@ print_operand (FILE *file, rtx x, int code)
     case 'L':
       /* Write second word of DImode or DFmode reference.  Works on register
         or non-indexed memory only.  */
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
        fputs (reg_names[REGNO (x) + 1], file);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
        {
          /* Handle possible auto-increment.  Since it is pre-increment and
             we have already done it, we can just use an offset of word.  */
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
-           output_address (plus_constant (XEXP (XEXP (x, 0), 0),
+           output_address (plus_constant (Pmode, XEXP (XEXP (x, 0), 0),
                                           UNITS_PER_WORD));
          else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY)
-           output_address (plus_constant (XEXP (XEXP (x, 0), 0),
+           output_address (plus_constant (Pmode, XEXP (XEXP (x, 0), 0),
                                           UNITS_PER_WORD));
          else
            output_address (XEXP (adjust_address_nv (x, SImode,
@@ -15046,7 +14966,7 @@ print_operand (FILE *file, rtx x, int code)
 
     case 't':
       /* Like 'J' but get to the OVERFLOW/UNORDERED bit.  */
-      gcc_assert (GET_CODE (x) == REG && GET_MODE (x) == CCmode);
+      gcc_assert (REG_P (x) && GET_MODE (x) == CCmode);
 
       /* Bit 3 is OV bit.  */
       i = 4 * (REGNO (x) - CR0_REGNO) + 3;
@@ -15061,7 +14981,7 @@ print_operand (FILE *file, rtx x, int code)
                                  && REGNO (x) != CTR_REGNO))
        output_operand_lossage ("invalid %%T value");
       else if (REGNO (x) == LR_REGNO)
-       fputs (TARGET_NEW_MNEMONICS ? "lr" : "r", file);
+       fputs ("lr", file);
       else
        fputs ("ctr", file);
       return;
@@ -15086,7 +15006,7 @@ print_operand (FILE *file, rtx x, int code)
 
     case 'U':
       /* Print `u' if this has an auto-increment or auto-decrement.  */
-      if (GET_CODE (x) == MEM
+      if (MEM_P (x)
          && (GET_CODE (XEXP (x, 0)) == PRE_INC
              || GET_CODE (XEXP (x, 0)) == PRE_DEC
              || GET_CODE (XEXP (x, 0)) == PRE_MODIFY))
@@ -15144,34 +15064,17 @@ print_operand (FILE *file, rtx x, int code)
 
     case 'W':
       /* MB value for a PowerPC64 rldic operand.  */
-      val = (GET_CODE (x) == CONST_INT
-            ? INTVAL (x) : CONST_DOUBLE_HIGH (x));
-
-      if (val < 0)
-       i = -1;
-      else
-       for (i = 0; i < HOST_BITS_PER_WIDE_INT; i++)
-         if ((val <<= 1) < 0)
-           break;
+      i = clz_hwi (GET_CODE (x) == CONST_INT
+                  ? INTVAL (x) : CONST_DOUBLE_HIGH (x));
 
 #if HOST_BITS_PER_WIDE_INT == 32
-      if (GET_CODE (x) == CONST_INT && i >= 0)
+      if (GET_CODE (x) == CONST_INT && i > 0)
        i += 32;  /* zero-extend high-part was all 0's */
       else if (GET_CODE (x) == CONST_DOUBLE && i == 32)
-       {
-         val = CONST_DOUBLE_LOW (x);
-
-         gcc_assert (val);
-         if (val < 0)
-           --i;
-         else
-           for ( ; i < 64; i++)
-             if ((val <<= 1) < 0)
-               break;
-       }
+       i = clz_hwi (CONST_DOUBLE_LOW (x)) + 32;
 #endif
 
-      fprintf (file, "%d", i + 1);
+      fprintf (file, "%d", i);
       return;
 
     case 'x':
@@ -15195,7 +15098,7 @@ print_operand (FILE *file, rtx x, int code)
       return;
 
     case 'X':
-      if (GET_CODE (x) == MEM
+      if (MEM_P (x)
          && (legitimate_indexed_address_p (XEXP (x, 0), 0)
              || (GET_CODE (XEXP (x, 0)) == PRE_MODIFY
                  && legitimate_indexed_address_p (XEXP (XEXP (x, 0), 1), 0))))
@@ -15204,15 +15107,15 @@ print_operand (FILE *file, rtx x, int code)
 
     case 'Y':
       /* Like 'L', for third word of TImode  */
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
        fputs (reg_names[REGNO (x) + 2], file);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
        {
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
-           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8));
+           output_address (plus_constant (Pmode, XEXP (XEXP (x, 0), 0), 8));
          else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY)
-           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 8));
+           output_address (plus_constant (Pmode, XEXP (XEXP (x, 0), 0), 8));
          else
            output_address (XEXP (adjust_address_nv (x, SImode, 8), 0));
          if (small_data_operand (x, GET_MODE (x)))
@@ -15254,15 +15157,15 @@ print_operand (FILE *file, rtx x, int code)
 
     case 'Z':
       /* Like 'L', for last word of TImode.  */
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
        fputs (reg_names[REGNO (x) + 3], file);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
        {
          if (GET_CODE (XEXP (x, 0)) == PRE_INC
              || GET_CODE (XEXP (x, 0)) == PRE_DEC)
-           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12));
+           output_address (plus_constant (Pmode, XEXP (XEXP (x, 0), 0), 12));
          else if (GET_CODE (XEXP (x, 0)) == PRE_MODIFY)
-           output_address (plus_constant (XEXP (XEXP (x, 0), 0), 12));
+           output_address (plus_constant (Pmode, XEXP (XEXP (x, 0), 0), 12));
          else
            output_address (XEXP (adjust_address_nv (x, SImode, 12), 0));
          if (small_data_operand (x, GET_MODE (x)))
@@ -15276,7 +15179,7 @@ print_operand (FILE *file, rtx x, int code)
       {
        rtx tmp;
 
-       gcc_assert (GET_CODE (x) == MEM);
+       gcc_assert (MEM_P (x));
 
        tmp = XEXP (x, 0);
 
@@ -15287,7 +15190,7 @@ print_operand (FILE *file, rtx x, int code)
                || GET_MODE (x) == TImode))
          {
            /* Handle [reg].  */
-           if (GET_CODE (tmp) == REG)
+           if (REG_P (tmp))
              {
                fprintf (file, "0(%s)", reg_names[REGNO (tmp)]);
                break;
@@ -15298,7 +15201,7 @@ print_operand (FILE *file, rtx x, int code)
              {
                int x;
 
-               gcc_assert (GET_CODE (XEXP (tmp, 0)) == REG);
+               gcc_assert (REG_P (XEXP (tmp, 0)));
 
                x = INTVAL (XEXP (tmp, 1));
                fprintf (file, "%d(%s)", x, reg_names[REGNO (XEXP (tmp, 0))]);
@@ -15315,7 +15218,7 @@ print_operand (FILE *file, rtx x, int code)
        else if (VECTOR_MEM_VSX_P (GET_MODE (x))
                 && GET_CODE (tmp) == PRE_MODIFY)
          tmp = XEXP (tmp, 1);
-       if (GET_CODE (tmp) == REG)
+       if (REG_P (tmp))
          fprintf (file, "0,%s", reg_names[REGNO (tmp)]);
        else
          {
@@ -15338,9 +15241,9 @@ print_operand (FILE *file, rtx x, int code)
       }
 
     case 0:
-      if (GET_CODE (x) == REG)
+      if (REG_P (x))
        fprintf (file, "%s", reg_names[REGNO (x)]);
-      else if (GET_CODE (x) == MEM)
+      else if (MEM_P (x))
        {
          /* We need to handle PRE_INC and PRE_DEC here, since we need to
             know the width from the mode.  */
@@ -15357,14 +15260,14 @@ print_operand (FILE *file, rtx x, int code)
        }
       else
        {
-         if (toc_relative_expr_p (x))
+         if (toc_relative_expr_p (x, false))
            /* This hack along with a corresponding hack in
               rs6000_output_addr_const_extra arranges to output addends
               where the assembler expects to find them.  eg.
-              (const (plus (unspec [symbol_ref ("x") tocrel]) 4))
+              (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 4)
               without this hack would be output as "x@toc+4".  We
               want "x+4@toc".  */
-           output_addr_const (file, tocrel_base);
+           output_addr_const (file, CONST_CAST_RTX (tocrel_base));
          else
            output_addr_const (file, x);
        }
@@ -15384,7 +15287,7 @@ print_operand (FILE *file, rtx x, int code)
 void
 print_operand_address (FILE *file, rtx x)
 {
-  if (GET_CODE (x) == REG)
+  if (REG_P (x))
     fprintf (file, "0(%s)", reg_names[ REGNO (x) ]);
   else if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == CONST
           || GET_CODE (x) == LABEL_REF)
@@ -15396,9 +15299,9 @@ print_operand_address (FILE *file, rtx x)
       else
        gcc_assert (!TARGET_TOC);
     }
-  else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == REG)
+  else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
+          && REG_P (XEXP (x, 1)))
     {
-      gcc_assert (REG_P (XEXP (x, 0)));
       if (REGNO (XEXP (x, 0)) == 0)
        fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 1)) ],
                 reg_names[ REGNO (XEXP (x, 0)) ]);
@@ -15406,11 +15309,12 @@ print_operand_address (FILE *file, rtx x)
        fprintf (file, "%s,%s", reg_names[ REGNO (XEXP (x, 0)) ],
                 reg_names[ REGNO (XEXP (x, 1)) ]);
     }
-  else if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT)
+  else if (GET_CODE (x) == PLUS && REG_P (XEXP (x, 0))
+          && GET_CODE (XEXP (x, 1)) == CONST_INT)
     fprintf (file, HOST_WIDE_INT_PRINT_DEC "(%s)",
             INTVAL (XEXP (x, 1)), reg_names[ REGNO (XEXP (x, 0)) ]);
 #if TARGET_MACHO
-  else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
+  else if (GET_CODE (x) == LO_SUM && REG_P (XEXP (x, 0))
           && CONSTANT_P (XEXP (x, 1)))
     {
       fprintf (file, "lo16(");
@@ -15418,29 +15322,29 @@ print_operand_address (FILE *file, rtx x)
       fprintf (file, ")(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
     }
 #endif
-  else if (legitimate_constant_pool_address_p (x, QImode, true))
+#if TARGET_ELF
+  else if (GET_CODE (x) == LO_SUM && REG_P (XEXP (x, 0))
+          && CONSTANT_P (XEXP (x, 1)))
+    {
+      output_addr_const (file, XEXP (x, 1));
+      fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+    }
+#endif
+  else if (toc_relative_expr_p (x, false))
     {
       /* This hack along with a corresponding hack in
         rs6000_output_addr_const_extra arranges to output addends
         where the assembler expects to find them.  eg.
         (lo_sum (reg 9)
-        .       (const (plus (unspec [symbol_ref ("x") tocrel]) 8)))
+        .       (plus (unspec [(symbol_ref ("x")) (reg 2)] tocrel) 8))
         without this hack would be output as "x@toc+8@l(9)".  We
         want "x+8@toc@l(9)".  */
-      output_addr_const (file, tocrel_base);
+      output_addr_const (file, CONST_CAST_RTX (tocrel_base));
       if (GET_CODE (x) == LO_SUM)
-       fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+       fprintf (file, "@l(%s)", reg_names[REGNO (XEXP (x, 0))]);
       else
-       fprintf (file, "(%s)", reg_names[REGNO (XEXP (x, 0))]);
-    }
-#if TARGET_ELF
-  else if (GET_CODE (x) == LO_SUM && GET_CODE (XEXP (x, 0)) == REG
-          && CONSTANT_P (XEXP (x, 1)))
-    {
-      output_addr_const (file, XEXP (x, 1));
-      fprintf (file, "@l(%s)", reg_names[ REGNO (XEXP (x, 0)) ]);
+       fprintf (file, "(%s)", reg_names[REGNO (XVECEXP (tocrel_base, 0, 1))]);
     }
-#endif
   else
     gcc_unreachable ();
 }
@@ -15454,13 +15358,15 @@ rs6000_output_addr_const_extra (FILE *file, rtx x)
     switch (XINT (x, 1))
       {
       case UNSPEC_TOCREL:
-       gcc_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF);
+       gcc_checking_assert (GET_CODE (XVECEXP (x, 0, 0)) == SYMBOL_REF
+                            && REG_P (XVECEXP (x, 0, 1))
+                            && REGNO (XVECEXP (x, 0, 1)) == TOC_REGISTER);
        output_addr_const (file, XVECEXP (x, 0, 0));
        if (x == tocrel_base && tocrel_offset != const0_rtx)
          {
            if (INTVAL (tocrel_offset) >= 0)
              fprintf (file, "+");
-           output_addr_const (file, tocrel_offset);
+           output_addr_const (file, CONST_CAST_RTX (tocrel_offset));
          }
        if (!TARGET_AIX || (TARGET_ELF && TARGET_MINIMAL_TOC))
          {
@@ -15607,18 +15513,25 @@ rs6000_generate_compare (rtx cmp, enum machine_mode mode)
           || code == GEU || code == LEU)
     comp_mode = CCUNSmode;
   else if ((code == EQ || code == NE)
-          && GET_CODE (op0) == SUBREG
-          && GET_CODE (op1) == SUBREG
-          && SUBREG_PROMOTED_UNSIGNED_P (op0)
-          && SUBREG_PROMOTED_UNSIGNED_P (op1))
+          && unsigned_reg_p (op0)
+          && (unsigned_reg_p (op1)
+              || (CONST_INT_P (op1) && INTVAL (op1) != 0)))
     /* These are unsigned values, perhaps there will be a later
-       ordering compare that can be shared with this one.
-       Unfortunately we cannot detect the signedness of the operands
-       for non-subregs.  */
+       ordering compare that can be shared with this one.  */
     comp_mode = CCUNSmode;
   else
     comp_mode = CCmode;
 
+  /* If we have an unsigned compare, make sure we don't have a signed value as
+     an immediate.  */
+  if (comp_mode == CCUNSmode && GET_CODE (op1) == CONST_INT
+      && INTVAL (op1) < 0)
+    {
+      op0 = copy_rtx_if_shared (op0);
+      op1 = force_reg (GET_MODE (op0), op1);
+      cmp = gen_rtx_fmt_ee (code, GET_MODE (cmp), op0, op1);
+    }
+
   /* First, the compare.  */
   compare_result = gen_reg_rtx (comp_mode);
 
@@ -16033,8 +15946,7 @@ output_cbranch (rtx op, const char *label, int reversed, rtx insn)
       gcc_unreachable ();
     }
 
-  /* Maybe we have a guess as to how likely the branch is.
-     The old mnemonics don't have a way to specify this information.  */
+  /* Maybe we have a guess as to how likely the branch is.  */
   pred = "";
   note = find_reg_note (insn, REG_BR_PROB, NULL_RTX);
   if (note != NULL_RTX)
@@ -16061,9 +15973,9 @@ output_cbranch (rtx op, const char *label, int reversed, rtx insn)
     }
 
   if (label == NULL)
-    s += sprintf (s, "{b%sr|b%slr%s} ", ccode, ccode, pred);
+    s += sprintf (s, "b%slr%s ", ccode, pred);
   else
-    s += sprintf (s, "{b%s|b%s%s} ", ccode, ccode, pred);
+    s += sprintf (s, "b%s%s ", ccode, pred);
 
   /* We need to escape any '%' characters in the reg_names string.
      Assume they'd only be the first character....  */
@@ -16122,6 +16034,10 @@ rs6000_emit_vector_compare_inner (enum rtx_code code, rtx op0, rtx op1)
     case EQ:
     case GT:
     case GTU:
+    case ORDERED:
+    case UNORDERED:
+    case UNEQ:
+    case LTGT:
       mask = gen_reg_rtx (mode);
       emit_insn (gen_rtx_SET (VOIDmode,
                              mask,
@@ -16368,6 +16284,11 @@ rs6000_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond)
   if (GET_MODE (false_cond) != result_mode)
     return 0;
 
+  /* Don't allow using floating point comparisons for integer results for
+     now.  */
+  if (FLOAT_MODE_P (compare_mode) && !FLOAT_MODE_P (result_mode))
+    return 0;
+
   /* First, work out if the hardware can do this at all, or
      if it's too slow....  */
   if (!FLOAT_MODE_P (compare_mode))
@@ -16696,9 +16617,19 @@ emit_store_conditional (enum machine_mode mode, rtx res, rtx mem, rtx val)
 
 /* Expand barriers before and after a load_locked/store_cond sequence.  */
 
-static void
-rs6000_pre_atomic_barrier (enum memmodel model)
+static rtx
+rs6000_pre_atomic_barrier (rtx mem, enum memmodel model)
 {
+  rtx addr = XEXP (mem, 0);
+  int strict_p = (reload_in_progress || reload_completed);
+
+  if (!legitimate_indirect_address_p (addr, strict_p)
+      && !legitimate_indexed_address_p (addr, strict_p))
+    {
+      addr = force_reg (Pmode, addr);
+      mem = replace_equiv_address_nv (mem, addr);
+    }
+
   switch (model)
     {
     case MEMMODEL_RELAXED:
@@ -16715,6 +16646,7 @@ rs6000_pre_atomic_barrier (enum memmodel model)
     default:
       gcc_unreachable ();
     }
+  return mem;
 }
 
 static void
@@ -16853,7 +16785,7 @@ rs6000_expand_atomic_compare_and_swap (rtx operands[])
   else if (reg_overlap_mentioned_p (retval, oldval))
     oldval = copy_to_reg (oldval);
 
-  rs6000_pre_atomic_barrier (mod_s);
+  mem = rs6000_pre_atomic_barrier (mem, mod_s);
 
   label1 = NULL_RTX;
   if (!is_weak)
@@ -16938,7 +16870,7 @@ rs6000_expand_atomic_exchange (rtx operands[])
       mode = SImode;
     }
 
-  rs6000_pre_atomic_barrier (model);
+  mem = rs6000_pre_atomic_barrier (mem, model);
 
   label = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
   emit_label (XEXP (label, 0));
@@ -17022,7 +16954,7 @@ rs6000_expand_atomic_op (enum rtx_code code, rtx mem, rtx val,
       mode = SImode;
     }
 
-  rs6000_pre_atomic_barrier (model);
+  mem = rs6000_pre_atomic_barrier (mem, model);
 
   label = gen_label_rtx ();
   emit_label (label);
@@ -17141,7 +17073,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
              emit_insn (gen_add3_insn (breg, breg, delta_rtx));
              src = replace_equiv_address (src, breg);
            }
-         else if (! rs6000_offsettable_memref_p (src))
+         else if (! rs6000_offsettable_memref_p (src, reg_mode))
            {
              if (GET_CODE (XEXP (src, 0)) == PRE_MODIFY)
                {
@@ -17207,7 +17139,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
                emit_insn (gen_add3_insn (breg, breg, delta_rtx));
              dst = replace_equiv_address (dst, breg);
            }
-         else if (!rs6000_offsettable_memref_p (dst)
+         else if (!rs6000_offsettable_memref_p (dst, reg_mode)
                   && GET_CODE (XEXP (dst, 0)) != LO_SUM)
            {
              if (GET_CODE (XEXP (dst, 0)) == PRE_MODIFY)
@@ -17245,7 +17177,7 @@ rs6000_split_multireg_move (rtx dst, rtx src)
                }
            }
          else if (GET_CODE (XEXP (dst, 0)) != LO_SUM)
-           gcc_assert (rs6000_offsettable_memref_p (dst));
+           gcc_assert (rs6000_offsettable_memref_p (dst, reg_mode));
        }
 
       for (i = 0; i < nregs; i++)
@@ -17275,6 +17207,12 @@ rs6000_split_multireg_move (rtx dst, rtx src)
 /* This page contains routines that are used to determine what the
    function prologue and epilogue code will do and write them out.  */
 
+static inline bool
+save_reg_p (int r)
+{
+  return !call_used_regs[r] && df_regs_ever_live_p (r);
+}
+
 /* Return the first fixed-point register that is required to be
    saved. 32 if none.  */
 
@@ -17285,14 +17223,16 @@ first_reg_to_save (void)
 
   /* Find lowest numbered live register.  */
   for (first_reg = 13; first_reg <= 31; first_reg++)
-    if (df_regs_ever_live_p (first_reg)
-       && (! call_used_regs[first_reg]
-           || (first_reg == RS6000_PIC_OFFSET_TABLE_REGNUM
-               && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
-                   || (DEFAULT_ABI == ABI_DARWIN && flag_pic)
-                   || (TARGET_TOC && TARGET_MINIMAL_TOC)))))
+    if (save_reg_p (first_reg))
       break;
 
+  if (first_reg > RS6000_PIC_OFFSET_TABLE_REGNUM
+      && ((DEFAULT_ABI == ABI_V4 && flag_pic != 0)
+         || (DEFAULT_ABI == ABI_DARWIN && flag_pic)
+         || (TARGET_TOC && TARGET_MINIMAL_TOC))
+      && df_regs_ever_live_p (RS6000_PIC_OFFSET_TABLE_REGNUM))
+    first_reg = RS6000_PIC_OFFSET_TABLE_REGNUM;
+
 #if TARGET_MACHO
   if (flag_pic
       && crtl->uses_pic_offset_table
@@ -17312,7 +17252,7 @@ first_fp_reg_to_save (void)
 
   /* Find lowest numbered live register.  */
   for (first_reg = 14 + 32; first_reg <= 63; first_reg++)
-    if (df_regs_ever_live_p (first_reg))
+    if (save_reg_p (first_reg))
       break;
 
   return first_reg;
@@ -17338,7 +17278,7 @@ first_altivec_reg_to_save (void)
 
   /* Find lowest numbered live register.  */
   for (i = FIRST_ALTIVEC_REGNO + 20; i <= LAST_ALTIVEC_REGNO; ++i)
-    if (df_regs_ever_live_p (i))
+    if (save_reg_p (i))
       break;
 
   return i;
@@ -17452,6 +17392,21 @@ is_altivec_return_reg (rtx reg, void *xyes)
 }
 
 \f
+/* Look for user-defined global regs in the range FIRST to LAST-1.
+   We should not restore these, and so cannot use lmw or out-of-line
+   restore functions if there are any.  We also can't save them
+   (well, emit frame notes for them), because frame unwinding during
+   exception handling will restore saved registers.  */
+
+static bool
+global_regs_p (unsigned first, unsigned last)
+{
+  while (first < last)
+    if (global_regs[first++])
+      return true;
+  return false;
+}
+
 /* Determine the strategy for savings/restoring registers.  */
 
 enum {
@@ -17462,7 +17417,9 @@ enum {
   REST_INLINE_GPRS = 0x10,
   SAVE_NOINLINE_GPRS_SAVES_LR = 0x20,
   SAVE_NOINLINE_FPRS_SAVES_LR = 0x40,
-  REST_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x80
+  REST_NOINLINE_FPRS_DOESNT_RESTORE_LR = 0x80,
+  SAVE_INLINE_VRS = 0x100,
+  REST_INLINE_VRS = 0x200
 };
 
 static int
@@ -17470,41 +17427,102 @@ rs6000_savres_strategy (rs6000_stack_t *info,
                        bool using_static_chain_p)
 {
   int strategy = 0;
+  bool lr_save_p;
 
   if (TARGET_MULTIPLE
       && !TARGET_POWERPC64
       && !(TARGET_SPE_ABI && info->spe_64bit_regs_used)
       && info->first_gp_reg_save < 31
-      && no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true))
+      && !global_regs_p (info->first_gp_reg_save, 32))
     strategy |= SAVRES_MULTIPLE;
 
   if (crtl->calls_eh_return
-      || cfun->machine->ra_need_lr
-      || info->total_size > 32767)
+      || cfun->machine->ra_need_lr)
     strategy |= (SAVE_INLINE_FPRS | REST_INLINE_FPRS
-                | SAVE_INLINE_GPRS | REST_INLINE_GPRS);
+                | SAVE_INLINE_GPRS | REST_INLINE_GPRS
+                | SAVE_INLINE_VRS | REST_INLINE_VRS);
 
   if (info->first_fp_reg_save == 64
-      || FP_SAVE_INLINE (info->first_fp_reg_save)
       /* The out-of-line FP routines use double-precision stores;
         we can't use those routines if we don't have such stores.  */
       || (TARGET_HARD_FLOAT && !TARGET_DOUBLE_FLOAT)
-      || !no_global_regs_above (info->first_fp_reg_save, /*gpr=*/false))
+      || global_regs_p (info->first_fp_reg_save, 64))
     strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
 
   if (info->first_gp_reg_save == 32
-      || GP_SAVE_INLINE (info->first_gp_reg_save)
-      || !((strategy & SAVRES_MULTIPLE)
-          || no_global_regs_above (info->first_gp_reg_save, /*gpr=*/true)))
+      || (!(strategy & SAVRES_MULTIPLE)
+         && global_regs_p (info->first_gp_reg_save, 32)))
     strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
 
+  if (info->first_altivec_reg_save == LAST_ALTIVEC_REGNO + 1
+      || global_regs_p (info->first_altivec_reg_save, LAST_ALTIVEC_REGNO + 1))
+    strategy |= SAVE_INLINE_VRS | REST_INLINE_VRS;
+
+  /* Define cutoff for using out-of-line functions to save registers.  */
+  if (DEFAULT_ABI == ABI_V4 || TARGET_ELF)
+    {
+      if (!optimize_size)
+       {
+         strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
+         strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
+         strategy |= SAVE_INLINE_VRS | REST_INLINE_VRS;
+       }
+      else
+       {
+         /* Prefer out-of-line restore if it will exit.  */
+         if (info->first_fp_reg_save > 61)
+           strategy |= SAVE_INLINE_FPRS;
+         if (info->first_gp_reg_save > 29)
+           {
+             if (info->first_fp_reg_save == 64)
+               strategy |= SAVE_INLINE_GPRS;
+             else
+               strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
+           }
+         if (info->first_altivec_reg_save == LAST_ALTIVEC_REGNO)
+           strategy |= SAVE_INLINE_VRS | REST_INLINE_VRS;
+       }
+    }
+  else if (DEFAULT_ABI == ABI_DARWIN)
+    {
+      if (info->first_fp_reg_save > 60)
+       strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
+      if (info->first_gp_reg_save > 29)
+       strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
+      strategy |= SAVE_INLINE_VRS | REST_INLINE_VRS;
+    }
+  else
+    {
+      gcc_checking_assert (DEFAULT_ABI == ABI_AIX);
+      if (info->first_fp_reg_save > 61)
+       strategy |= SAVE_INLINE_FPRS | REST_INLINE_FPRS;
+      strategy |= SAVE_INLINE_GPRS | REST_INLINE_GPRS;
+      strategy |= SAVE_INLINE_VRS | REST_INLINE_VRS;
+    }
+
   /* Don't bother to try to save things out-of-line if r11 is occupied
      by the static chain.  It would require too much fiddling and the
      static chain is rarely used anyway.  FPRs are saved w.r.t the stack
-     pointer on Darwin.  */
-  if (using_static_chain_p)
-    strategy |= (DEFAULT_ABI == ABI_DARWIN ? 0 : SAVE_INLINE_FPRS)
-               | SAVE_INLINE_GPRS;
+     pointer on Darwin, and AIX uses r1 or r12.  */
+  if (using_static_chain_p && DEFAULT_ABI != ABI_AIX)
+    strategy |= ((DEFAULT_ABI == ABI_DARWIN ? 0 : SAVE_INLINE_FPRS)
+                | SAVE_INLINE_GPRS
+                | SAVE_INLINE_VRS | REST_INLINE_VRS);
+
+  /* We can only use the out-of-line routines to restore if we've
+     saved all the registers from first_fp_reg_save in the prologue.
+     Otherwise, we risk loading garbage.  */
+  if ((strategy & (SAVE_INLINE_FPRS | REST_INLINE_FPRS)) == SAVE_INLINE_FPRS)
+    {
+      int i;
+
+      for (i = info->first_fp_reg_save; i < 64; i++)
+       if (!save_reg_p (i))
+         {
+           strategy |= REST_INLINE_FPRS;
+           break;
+         }
+    }
 
   /* If we are going to use store multiple, then don't even bother
      with the out-of-line routines, since the store-multiple
@@ -17512,6 +17530,16 @@ rs6000_savres_strategy (rs6000_stack_t *info,
   if ((strategy & SAVRES_MULTIPLE))
     strategy |= SAVE_INLINE_GPRS;
 
+  /* info->lr_save_p isn't yet set if the only reason lr needs to be
+     saved is an out-of-line save or restore.  Set up the value for
+     the next test (excluding out-of-line gpr restore).  */
+  lr_save_p = (info->lr_save_p
+              || !(strategy & SAVE_INLINE_GPRS)
+              || !(strategy & SAVE_INLINE_FPRS)
+              || !(strategy & SAVE_INLINE_VRS)
+              || !(strategy & REST_INLINE_FPRS)
+              || !(strategy & REST_INLINE_VRS));
+
   /* The situation is more complicated with load multiple.  We'd
      prefer to use the out-of-line routines for restores, since the
      "exit" out-of-line routines can handle the restore of LR and the
@@ -17521,17 +17549,10 @@ rs6000_savres_strategy (rs6000_stack_t *info,
      have saved some fprs; In those cases it is advantageous to use
      load multiple when available.  */
   if ((strategy & SAVRES_MULTIPLE)
-      && (!info->lr_save_p
+      && (!lr_save_p
          || info->first_fp_reg_save != 64))
     strategy |= REST_INLINE_GPRS;
 
-  /* We can only use load multiple or the out-of-line routines to
-     restore if we've used store multiple or out-of-line routines
-     in the prologue, i.e. if we've saved all the registers from
-     first_gp_reg_save.  Otherwise, we risk loading garbage.  */
-  if ((strategy & (SAVE_INLINE_GPRS | SAVRES_MULTIPLE)) == SAVE_INLINE_GPRS)
-    strategy |= REST_INLINE_GPRS;
-
   /* Saving CR interferes with the exit routines used on the SPE, so
      just punt here.  */
   if (TARGET_SPE_ABI
@@ -17539,8 +17560,24 @@ rs6000_savres_strategy (rs6000_stack_t *info,
       && info->cr_save_p)
     strategy |= REST_INLINE_GPRS;
 
-#ifdef POWERPC_LINUX
-  if (TARGET_64BIT)
+  /* We can only use load multiple or the out-of-line routines to
+     restore if we've used store multiple or out-of-line routines
+     in the prologue, i.e. if we've saved all the registers from
+     first_gp_reg_save.  Otherwise, we risk loading garbage.  */
+  if ((strategy & (SAVE_INLINE_GPRS | REST_INLINE_GPRS | SAVRES_MULTIPLE))
+      == SAVE_INLINE_GPRS)
+    {
+      int i;
+
+      for (i = info->first_gp_reg_save; i < 32; i++)
+       if (!save_reg_p (i))
+         {
+           strategy |= REST_INLINE_GPRS;
+           break;
+         }
+    }
+
+  if (TARGET_ELF && TARGET_64BIT)
     {
       if (!(strategy & SAVE_INLINE_FPRS))
        strategy |= SAVE_NOINLINE_FPRS_SAVES_LR;
@@ -17548,10 +17585,9 @@ rs6000_savres_strategy (rs6000_stack_t *info,
               && info->first_fp_reg_save == 64)
        strategy |= SAVE_NOINLINE_GPRS_SAVES_LR;
     }
-#else
-  if (TARGET_AIX && !(strategy & REST_INLINE_FPRS))
+  else if (TARGET_AIX && !(strategy & REST_INLINE_FPRS))
     strategy |= REST_NOINLINE_FPRS_DOESNT_RESTORE_LR;
-#endif
+
   if (TARGET_MACHO && !(strategy & SAVE_INLINE_FPRS))
     strategy |= SAVE_NOINLINE_FPRS_SAVES_LR;
 
@@ -17724,7 +17760,7 @@ rs6000_stack_info (void)
                                 - info_ptr->first_altivec_reg_save);
 
   /* Does this function call anything?  */
-  info_ptr->calls_p = (! current_function_is_leaf
+  info_ptr->calls_p = (! crtl->is_leaf 
                       || cfun->machine->ra_needs_full_frame);
 
   /* Determine if we need to save the condition code registers.  */
@@ -17915,8 +17951,10 @@ rs6000_stack_info (void)
 
   if (!(info_ptr->savres_strategy & SAVE_INLINE_GPRS)
       || !(info_ptr->savres_strategy & SAVE_INLINE_FPRS)
+      || !(info_ptr->savres_strategy & SAVE_INLINE_VRS)
       || !(info_ptr->savres_strategy & REST_INLINE_GPRS)
-      || !(info_ptr->savres_strategy & REST_INLINE_FPRS))
+      || !(info_ptr->savres_strategy & REST_INLINE_FPRS)
+      || !(info_ptr->savres_strategy & REST_INLINE_VRS))
     info_ptr->lr_save_p = 1;
 
   if (info_ptr->lr_save_p)
@@ -18170,7 +18208,8 @@ rs6000_return_addr (int count, rtx frame)
          (Pmode,
           memory_address
           (Pmode,
-           plus_constant (copy_to_reg
+           plus_constant (Pmode,
+                          copy_to_reg
                           (gen_rtx_MEM (Pmode,
                                         memory_address (Pmode, frame))),
                           RETURN_ADDRESS_OFFSET)));
@@ -18440,7 +18479,8 @@ rs6000_emit_eh_reg_restore (rtx source, rtx scratch)
       else if (info->push_p)
        sp_offset = info->total_size;
 
-      tmp = plus_constant (frame_rtx, info->lr_save_offset + sp_offset);
+      tmp = plus_constant (Pmode, frame_rtx,
+                          info->lr_save_offset + sp_offset);
       tmp = gen_frame_mem (Pmode, tmp);
       emit_move_insn (tmp, operands[0]);
     }
@@ -18499,7 +18539,7 @@ uses_TOC (void)
 rtx
 create_TOC_reference (rtx symbol, rtx largetoc_reg)
 {
-  rtx tocrel, tocreg;
+  rtx tocrel, tocreg, hi;
 
   if (TARGET_DEBUG_ADDR)
     {
@@ -18517,24 +18557,18 @@ create_TOC_reference (rtx symbol, rtx largetoc_reg)
   if (!can_create_pseudo_p ())
     df_set_regs_ever_live (TOC_REGISTER, true);
 
-  tocrel = gen_rtx_CONST (Pmode,
-                         gen_rtx_UNSPEC (Pmode, gen_rtvec (1, symbol),
-                                         UNSPEC_TOCREL));
   tocreg = gen_rtx_REG (Pmode, TOC_REGISTER);
-  if (TARGET_CMODEL != CMODEL_SMALL)
+  tocrel = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, symbol, tocreg), UNSPEC_TOCREL);
+  if (TARGET_CMODEL == CMODEL_SMALL || can_create_pseudo_p ())
+    return tocrel;
+
+  hi = gen_rtx_HIGH (Pmode, copy_rtx (tocrel));
+  if (largetoc_reg != NULL)
     {
-      rtx hi = gen_rtx_CONST (Pmode,
-                             gen_rtx_PLUS (Pmode, tocreg, 
-                                           gen_rtx_HIGH (Pmode, tocrel)));
-      if (largetoc_reg != NULL)
-       {
-         emit_move_insn (largetoc_reg, hi);
-         hi = largetoc_reg;
-       }
-      return gen_rtx_LO_SUM (Pmode, hi, copy_rtx (tocrel));
+      emit_move_insn (largetoc_reg, hi);
+      hi = largetoc_reg;
     }
-  else
-    return gen_rtx_PLUS (Pmode, tocreg, tocrel);
+  return gen_rtx_LO_SUM (Pmode, hi, tocrel);
 }
 
 /* Issue assembly directives that create a reference to the given DWARF
@@ -18543,19 +18577,36 @@ void
 rs6000_aix_asm_output_dwarf_table_ref (char * frame_table_label)
 {
   fprintf (asm_out_file, "\t.ref %s\n",
-          TARGET_STRIP_NAME_ENCODING (frame_table_label));
+          (* targetm.strip_name_encoding) (frame_table_label));
 }
 \f
 /* This ties together stack memory (MEM with an alias set of frame_alias_set)
    and the change to the stack pointer.  */
 
 static void
-rs6000_emit_stack_tie (void)
+rs6000_emit_stack_tie (rtx fp, bool hard_frame_needed)
 {
-  rtx mem = gen_frame_mem (BLKmode,
-                          gen_rtx_REG (Pmode, STACK_POINTER_REGNUM));
+  rtvec p;
+  int i;
+  rtx regs[3];
+
+  i = 0;
+  regs[i++] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+  if (hard_frame_needed)
+    regs[i++] = gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM);
+  if (!(REGNO (fp) == STACK_POINTER_REGNUM
+       || (hard_frame_needed
+           && REGNO (fp) == HARD_FRAME_POINTER_REGNUM)))
+    regs[i++] = fp;
+
+  p = rtvec_alloc (i);
+  while (--i >= 0)
+    {
+      rtx mem = gen_frame_mem (BLKmode, regs[i]);
+      RTVEC_ELT (p, i) = gen_rtx_SET (VOIDmode, mem, const0_rtx);
+    }
 
-  emit_insn (gen_stack_tie (mem));
+  emit_insn (gen_stack_tie (gen_rtx_PARALLEL (VOIDmode, p)));
 }
 
 /* Emit the correct code for allocating stack space, as insns.
@@ -18563,7 +18614,7 @@ rs6000_emit_stack_tie (void)
    The generated code may use hard register 0 as a temporary.  */
 
 static void
-rs6000_emit_allocate_stack (HOST_WIDE_INT size, rtx copy_reg)
+rs6000_emit_allocate_stack (HOST_WIDE_INT size, rtx copy_reg, int copy_off)
 {
   rtx insn;
   rtx stack_reg = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
@@ -18607,7 +18658,12 @@ rs6000_emit_allocate_stack (HOST_WIDE_INT size, rtx copy_reg)
     }
 
   if (copy_reg)
-    emit_move_insn (copy_reg, stack_reg);
+    {
+      if (copy_off != 0)
+       emit_insn (gen_add3_insn (copy_reg, stack_reg, GEN_INT (copy_off)));
+      else
+       emit_move_insn (copy_reg, stack_reg);
+    }
 
   if (size > 32767)
     {
@@ -18666,9 +18722,11 @@ rs6000_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
         it exceeds SIZE.  If only one probe is needed, this will not
         generate any code.  Then probe at FIRST + SIZE.  */
       for (i = PROBE_INTERVAL; i < size; i += PROBE_INTERVAL)
-       emit_stack_probe (plus_constant (stack_pointer_rtx, -(first + i)));
+       emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+                                        -(first + i)));
 
-      emit_stack_probe (plus_constant (stack_pointer_rtx, -(first + size)));
+      emit_stack_probe (plus_constant (Pmode, stack_pointer_rtx,
+                                      -(first + size)));
     }
 
   /* Otherwise, do the same as above, but in a loop.  Note that we must be
@@ -18694,7 +18752,8 @@ rs6000_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
 
       /* TEST_ADDR = SP + FIRST.  */
       emit_insn (gen_rtx_SET (VOIDmode, r12,
-                             plus_constant (stack_pointer_rtx, -first)));
+                             plus_constant (Pmode, stack_pointer_rtx,
+                                            -first)));
 
       /* LAST_ADDR = SP + FIRST + ROUNDED_SIZE.  */
       if (rounded_size > 32768)
@@ -18705,7 +18764,7 @@ rs6000_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
        }
       else
        emit_insn (gen_rtx_SET (VOIDmode, r0,
-                               plus_constant (r12, -rounded_size)));
+                               plus_constant (Pmode, r12, -rounded_size)));
 
 
       /* Step 3: the loop
@@ -18729,7 +18788,7 @@ rs6000_emit_probe_stack_range (HOST_WIDE_INT first, HOST_WIDE_INT size)
         that SIZE is equal to ROUNDED_SIZE.  */
 
       if (size != rounded_size)
-       emit_stack_probe (plus_constant (r12, rounded_size - size));
+       emit_stack_probe (plus_constant (Pmode, r12, rounded_size - size));
     }
 }
 
@@ -18752,9 +18811,9 @@ output_probe_stack_range (rtx reg1, rtx reg2)
   xops[0] = reg1;
   xops[1] = reg2;
   if (TARGET_64BIT)
-    output_asm_insn ("{cmp|cmpd} 0,%0,%1", xops);
+    output_asm_insn ("cmpd 0,%0,%1", xops);
   else
-    output_asm_insn ("{cmp|cmpw} 0,%0,%1", xops);
+    output_asm_insn ("cmpw 0,%0,%1", xops);
 
   fputs ("\tbeq 0,", asm_out_file);
   assemble_name_raw (asm_out_file, end_lab);
@@ -18762,11 +18821,11 @@ output_probe_stack_range (rtx reg1, rtx reg2)
 
   /* TEST_ADDR = TEST_ADDR + PROBE_INTERVAL.  */
   xops[1] = GEN_INT (-PROBE_INTERVAL);
-  output_asm_insn ("{cal %0,%1(%0)|addi %0,%0,%1}", xops);
+  output_asm_insn ("addi %0,%0,%1", xops);
 
   /* Probe at TEST_ADDR and branch.  */
   xops[1] = gen_rtx_REG (Pmode, 0);
-  output_asm_insn ("{st|stw} %1,0(%0)", xops);
+  output_asm_insn ("stw %1,0(%0)", xops);
   fprintf (asm_out_file, "\tb ");
   assemble_name_raw (asm_out_file, loop_lab);
   fputc ('\n', asm_out_file);
@@ -18780,7 +18839,10 @@ output_probe_stack_range (rtx reg1, rtx reg2)
    with (plus:P (reg 1) VAL), and with REG2 replaced with RREG if REG2
    is not NULL.  It would be nice if dwarf2out_frame_debug_expr could
    deduce these equivalences by itself so it wasn't necessary to hold
-   its hand so much.  */
+   its hand so much.  Don't be tempted to always supply d2_f_d_e with
+   the actual cfa register, ie. r31 when we are using a hard frame
+   pointer.  That fails when saving regs off r1, and sched moves the
+   r31 setup past the reg saves.  */
 
 static rtx
 rs6000_frame_related (rtx insn, rtx reg, HOST_WIDE_INT val,
@@ -18788,6 +18850,25 @@ rs6000_frame_related (rtx insn, rtx reg, HOST_WIDE_INT val,
 {
   rtx real, temp;
 
+  if (REGNO (reg) == STACK_POINTER_REGNUM && reg2 == NULL_RTX)
+    {
+      /* No need for any replacement.  Just set RTX_FRAME_RELATED_P.  */
+      int i;
+
+      gcc_checking_assert (val == 0);
+      real = PATTERN (insn);
+      if (GET_CODE (real) == PARALLEL)
+       for (i = 0; i < XVECLEN (real, 0); i++)
+         if (GET_CODE (XVECEXP (real, 0, i)) == SET)
+           {
+             rtx set = XVECEXP (real, 0, i);
+
+             RTX_FRAME_RELATED_P (set) = 1;
+           }
+      RTX_FRAME_RELATED_P (insn) = 1;
+      return insn;
+    }
+
   /* copy_rtx will not make unique copies of registers, so we need to
      ensure we don't have unwanted sharing here.  */
   if (reg == reg2)
@@ -18801,10 +18882,13 @@ rs6000_frame_related (rtx insn, rtx reg, HOST_WIDE_INT val,
   if (reg2 != NULL_RTX)
     real = replace_rtx (real, reg2, rreg);
 
-  real = replace_rtx (real, reg,
-                     gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode,
-                                                       STACK_POINTER_REGNUM),
-                                   GEN_INT (val)));
+  if (REGNO (reg) == STACK_POINTER_REGNUM)
+    gcc_checking_assert (val == 0);
+  else
+    real = replace_rtx (real, reg,
+                       gen_rtx_PLUS (Pmode, gen_rtx_REG (Pmode,
+                                                         STACK_POINTER_REGNUM),
+                                     GEN_INT (val)));
 
   /* We expect that 'real' is either a SET or a PARALLEL containing
      SETs (and possibly other stuff).  In a PARALLEL, all the SETs
@@ -18918,48 +19002,49 @@ generate_set_vrsave (rtx reg, rs6000_stack_t *info, int epiloguep)
   return insn;
 }
 
+static rtx
+gen_frame_set (rtx reg, rtx frame_reg, int offset, bool store)
+{
+  rtx addr, mem;
+
+  addr = gen_rtx_PLUS (Pmode, frame_reg, GEN_INT (offset));
+  mem = gen_frame_mem (GET_MODE (reg), addr);
+  return gen_rtx_SET (VOIDmode, store ? mem : reg, store ? reg : mem);
+}
+
+static rtx
+gen_frame_load (rtx reg, rtx frame_reg, int offset)
+{
+  return gen_frame_set (reg, frame_reg, offset, false);
+}
+
+static rtx
+gen_frame_store (rtx reg, rtx frame_reg, int offset)
+{
+  return gen_frame_set (reg, frame_reg, offset, true);
+}
+
 /* Save a register into the frame, and emit RTX_FRAME_RELATED_P notes.
    Save REGNO into [FRAME_REG + OFFSET] in mode MODE.  */
 
 static rtx
-emit_frame_save (rtx frame_reg, rtx frame_ptr, enum machine_mode mode,
-                unsigned int regno, int offset, HOST_WIDE_INT total_size)
+emit_frame_save (rtx frame_reg, enum machine_mode mode,
+                unsigned int regno, int offset, HOST_WIDE_INT frame_reg_to_sp)
 {
-  rtx reg, offset_rtx, insn, mem, addr, int_rtx;
-  rtx replacea, replaceb;
-
-  int_rtx = GEN_INT (offset);
+  rtx reg, insn;
 
   /* Some cases that need register indexed addressing.  */
-  if ((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
-      || (TARGET_VSX && ALTIVEC_OR_VSX_VECTOR_MODE (mode))
-      || (TARGET_E500_DOUBLE && mode == DFmode)
-      || (TARGET_SPE_ABI
-         && SPE_VECTOR_MODE (mode)
-         && !SPE_CONST_OFFSET_OK (offset)))
-    {
-      /* Whomever calls us must make sure r11 is available in the
-        flow path of instructions in the prologue.  */
-      offset_rtx = gen_rtx_REG (Pmode, 11);
-      emit_move_insn (offset_rtx, int_rtx);
-
-      replacea = offset_rtx;
-      replaceb = int_rtx;
-    }
-  else
-    {
-      offset_rtx = int_rtx;
-      replacea = NULL_RTX;
-      replaceb = NULL_RTX;
-    }
+  gcc_checking_assert (!((TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+                        || (TARGET_VSX && ALTIVEC_OR_VSX_VECTOR_MODE (mode))
+                        || (TARGET_E500_DOUBLE && mode == DFmode)
+                        || (TARGET_SPE_ABI
+                            && SPE_VECTOR_MODE (mode)
+                            && !SPE_CONST_OFFSET_OK (offset))));
 
   reg = gen_rtx_REG (mode, regno);
-  addr = gen_rtx_PLUS (Pmode, frame_reg, offset_rtx);
-  mem = gen_frame_mem (mode, addr);
-
-  insn = emit_move_insn (mem, reg);
-
-  return rs6000_frame_related (insn, frame_ptr, total_size, replacea, replaceb);
+  insn = emit_insn (gen_frame_store (reg, frame_reg, offset));
+  return rs6000_frame_related (insn, frame_reg, frame_reg_to_sp,
+                              NULL_RTX, NULL_RTX);
 }
 
 /* Emit an offset memory reference suitable for a frame store, while
@@ -18972,7 +19057,7 @@ gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset)
 
   int_rtx = GEN_INT (offset);
 
-  if ((TARGET_SPE_ABI && SPE_VECTOR_MODE (mode))
+  if ((TARGET_SPE_ABI && SPE_VECTOR_MODE (mode) && !SPE_CONST_OFFSET_OK (offset))
       || (TARGET_E500_DOUBLE && mode == DFmode))
     {
       offset_rtx = gen_rtx_REG (Pmode, FIXED_SCRATCH);
@@ -18984,30 +19069,25 @@ gen_frame_mem_offset (enum machine_mode mode, rtx reg, int offset)
   return gen_frame_mem (mode, gen_rtx_PLUS (Pmode, reg, offset_rtx));
 }
 
-/* Look for user-defined global regs.  We should not save and restore these,
-   and cannot use stmw/lmw if there are any in its range.  */
-
-static bool
-no_global_regs_above (int first, bool gpr)
-{
-  int i;
-  int last = gpr ? 32 : 64;
-  for (i = first; i < last; i++)
-    if (global_regs[i])
-      return false;
-  return true;
-}
-
 #ifndef TARGET_FIX_AND_CONTINUE
 #define TARGET_FIX_AND_CONTINUE 0
 #endif
 
-/* It's really GPR 13 and FPR 14, but we need the smaller of the two.  */
+/* It's really GPR 13 or 14, FPR 14 and VR 20.  We need the smallest.  */
 #define FIRST_SAVRES_REGISTER FIRST_SAVED_GP_REGNO
 #define LAST_SAVRES_REGISTER 31
 #define N_SAVRES_REGISTERS (LAST_SAVRES_REGISTER - FIRST_SAVRES_REGISTER + 1)
 
-static GTY(()) rtx savres_routine_syms[N_SAVRES_REGISTERS][8];
+enum {
+  SAVRES_LR = 0x1,
+  SAVRES_SAVE = 0x2,
+  SAVRES_REG = 0x0c,
+  SAVRES_GPR = 0,
+  SAVRES_FPR = 4,
+  SAVRES_VR  = 8
+};
+
+static GTY(()) rtx savres_routine_syms[N_SAVRES_REGISTERS][12];
 
 /* Temporary holding space for an out-of-line register save/restore
    routine name.  */
@@ -19017,8 +19097,7 @@ static char savres_routine_name[30];
    We are saving/restoring GPRs if GPR is true.  */
 
 static char *
-rs6000_savres_routine_name (rs6000_stack_t *info, int regno,
-                           bool savep, bool gpr, bool lr)
+rs6000_savres_routine_name (rs6000_stack_t *info, int regno, int sel)
 {
   const char *prefix = "";
   const char *suffix = "";
@@ -19054,14 +19133,14 @@ rs6000_savres_routine_name (rs6000_stack_t *info, int regno,
   if (TARGET_SPE)
     {
       /* No floating point saves on the SPE.  */
-      gcc_assert (gpr);
+      gcc_assert ((sel & SAVRES_REG) == SAVRES_GPR);
 
-      if (savep)
+      if ((sel & SAVRES_SAVE))
        prefix = info->spe_64bit_regs_used ? "_save64gpr_" : "_save32gpr_";
       else
        prefix = info->spe_64bit_regs_used ? "_rest64gpr_" : "_rest32gpr_";
 
-      if (lr)
+      if ((sel & SAVRES_LR))
        suffix = "_x";
     }
   else if (DEFAULT_ABI == ABI_V4)
@@ -19069,35 +19148,46 @@ rs6000_savres_routine_name (rs6000_stack_t *info, int regno,
       if (TARGET_64BIT)
        goto aix_names;
 
-      if (gpr)
-       prefix = savep ? "_savegpr_" : "_restgpr_";
+      if ((sel & SAVRES_REG) == SAVRES_GPR)
+       prefix = (sel & SAVRES_SAVE) ? "_savegpr_" : "_restgpr_";
+      else if ((sel & SAVRES_REG) == SAVRES_FPR)
+       prefix = (sel & SAVRES_SAVE) ? "_savefpr_" : "_restfpr_";
+      else if ((sel & SAVRES_REG) == SAVRES_VR)
+       prefix = (sel & SAVRES_SAVE) ? "_savevr_" : "_restvr_";
       else
-       prefix = savep ? "_savefpr_" : "_restfpr_";
+       abort ();
 
-      if (lr)
+      if ((sel & SAVRES_LR))
        suffix = "_x";
     }
   else if (DEFAULT_ABI == ABI_AIX)
     {
-#ifndef POWERPC_LINUX
+#if !defined (POWERPC_LINUX) && !defined (POWERPC_FREEBSD)
       /* No out-of-line save/restore routines for GPRs on AIX.  */
-      gcc_assert (!TARGET_AIX || !gpr);
+      gcc_assert (!TARGET_AIX || (sel & SAVRES_REG) != SAVRES_GPR);
 #endif
 
     aix_names:
-      if (gpr)
-       prefix = (savep
-                 ? (lr ? "_savegpr0_" : "_savegpr1_")
-                 : (lr ? "_restgpr0_" : "_restgpr1_"));
-#ifdef POWERPC_LINUX
-      else if (lr)
-       prefix = (savep ? "_savefpr_" : "_restfpr_");
+      if ((sel & SAVRES_REG) == SAVRES_GPR)
+       prefix = ((sel & SAVRES_SAVE)
+                 ? ((sel & SAVRES_LR) ? "_savegpr0_" : "_savegpr1_")
+                 : ((sel & SAVRES_LR) ? "_restgpr0_" : "_restgpr1_"));
+      else if ((sel & SAVRES_REG) == SAVRES_FPR)
+       {
+#if defined (POWERPC_LINUX) || defined (POWERPC_FREEBSD)
+         if ((sel & SAVRES_LR))
+           prefix = ((sel & SAVRES_SAVE) ? "_savefpr_" : "_restfpr_");
+         else
 #endif
-      else
-       {
-         prefix = savep ? SAVE_FP_PREFIX : RESTORE_FP_PREFIX;
-         suffix = savep ? SAVE_FP_SUFFIX : RESTORE_FP_SUFFIX;
+           {
+             prefix = (sel & SAVRES_SAVE) ? SAVE_FP_PREFIX : RESTORE_FP_PREFIX;
+             suffix = (sel & SAVRES_SAVE) ? SAVE_FP_SUFFIX : RESTORE_FP_SUFFIX;
+           }
        }
+      else if ((sel & SAVRES_REG) == SAVRES_VR)
+       prefix = (sel & SAVRES_SAVE) ? "_savevr_" : "_restvr_";
+      else
+       abort ();
     }
 
    if (DEFAULT_ABI == ABI_DARWIN)
@@ -19107,14 +19197,19 @@ rs6000_savres_routine_name (rs6000_stack_t *info, int regno,
         single symbol for the start of save sequence, and the code here
         embeds an offset into that code on the basis of the first register
         to be saved.  */
-      prefix = savep ? "save" : "rest" ;
-      if (gpr)
-       sprintf (savres_routine_name, "*%sGPR%s%s%.0d ; %s r%d-r31",
-              prefix, (lr ? "x" : ""), (regno == 13 ? "" : "+"),
-              (regno-13) * 4, prefix, regno);
+      prefix = (sel & SAVRES_SAVE) ? "save" : "rest" ;
+      if ((sel & SAVRES_REG) == SAVRES_GPR)
+       sprintf (savres_routine_name, "*%sGPR%s%s%.0d ; %s r%d-r31", prefix,
+                ((sel & SAVRES_LR) ? "x" : ""), (regno == 13 ? "" : "+"),
+                (regno - 13) * 4, prefix, regno);
+      else if ((sel & SAVRES_REG) == SAVRES_FPR)
+       sprintf (savres_routine_name, "*%sFP%s%.0d ; %s f%d-f31", prefix,
+                (regno == 14 ? "" : "+"), (regno - 14) * 4, prefix, regno);
+      else if ((sel & SAVRES_REG) == SAVRES_VR)
+       sprintf (savres_routine_name, "*%sVEC%s%.0d ; %s v%d-v31", prefix,
+                (regno == 20 ? "" : "+"), (regno - 20) * 8, prefix, regno);
       else
-       sprintf (savres_routine_name, "*%sFP%s%.0d ; %s f%d-f31",
-              prefix, (regno == 14 ? "" : "+"),  (regno-14) * 4, prefix, regno);
+       abort ();
     }
   else
     sprintf (savres_routine_name, "%s%d%s", prefix, regno, suffix);
@@ -19126,22 +19221,28 @@ rs6000_savres_routine_name (rs6000_stack_t *info, int regno,
    We are saving/restoring GPRs if GPR is true.  */
 
 static rtx
-rs6000_savres_routine_sym (rs6000_stack_t *info, bool savep,
-                          bool gpr, bool lr)
+rs6000_savres_routine_sym (rs6000_stack_t *info, int sel)
 {
-  int regno = gpr ? info->first_gp_reg_save : (info->first_fp_reg_save - 32);
+  int regno = ((sel & SAVRES_REG) == SAVRES_GPR
+              ? info->first_gp_reg_save
+              : (sel & SAVRES_REG) == SAVRES_FPR
+              ? info->first_fp_reg_save - 32
+              : (sel & SAVRES_REG) == SAVRES_VR
+              ? info->first_altivec_reg_save - FIRST_ALTIVEC_REGNO
+              : -1);
   rtx sym;
-  int select = ((savep ? 1 : 0) << 2
-               | ((TARGET_SPE_ABI
-                   /* On the SPE, we never have any FPRs, but we do have
-                      32/64-bit versions of the routines.  */
-                   ? (info->spe_64bit_regs_used ? 1 : 0)
-                   : (gpr ? 1 : 0)) << 1)
-               | (lr ? 1: 0));
+  int select = sel;
+
+  /* On the SPE, we never have any FPRs, but we do have 32/64-bit
+     versions of the gpr routines.  */
+  if (TARGET_SPE_ABI && (sel & SAVRES_REG) == SAVRES_GPR
+      && info->spe_64bit_regs_used)
+    select ^= SAVRES_FPR ^ SAVRES_GPR;
 
   /* Don't generate bogus routine names.  */
   gcc_assert (FIRST_SAVRES_REGISTER <= regno
-             && regno <= LAST_SAVRES_REGISTER);
+             && regno <= LAST_SAVRES_REGISTER
+             && select >= 0 && select <= 12);
 
   sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select];
 
@@ -19149,7 +19250,7 @@ rs6000_savres_routine_sym (rs6000_stack_t *info, bool savep,
     {
       char *name;
 
-      name = rs6000_savres_routine_name (info, regno, savep, gpr, lr);
+      name = rs6000_savres_routine_name (info, regno, sel);
 
       sym = savres_routine_syms[regno-FIRST_SAVRES_REGISTER][select]
        = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (name));
@@ -19160,63 +19261,59 @@ rs6000_savres_routine_sym (rs6000_stack_t *info, bool savep,
 }
 
 /* Emit a sequence of insns, including a stack tie if needed, for
-   resetting the stack pointer.  If SAVRES is true, then don't reset the
-   stack pointer, but move the base of the frame into r11 for use by
-   out-of-line register restore routines.  */
+   resetting the stack pointer.  If UPDT_REGNO is not 1, then don't
+   reset the stack pointer, but move the base of the frame into
+   reg UPDT_REGNO for use by out-of-line register restore routines.  */
 
 static rtx
 rs6000_emit_stack_reset (rs6000_stack_t *info,
-                        rtx sp_reg_rtx, rtx frame_reg_rtx,
-                        int sp_offset, bool savres)
+                        rtx frame_reg_rtx, HOST_WIDE_INT frame_off,
+                        unsigned updt_regno)
 {
+  rtx updt_reg_rtx;
+
   /* This blockage is needed so that sched doesn't decide to move
      the sp change before the register restores.  */
   if (DEFAULT_ABI == ABI_V4
       || (TARGET_SPE_ABI
          && info->spe_64bit_regs_used != 0
          && info->first_gp_reg_save != 32))
-    rs6000_emit_stack_tie ();
-  
-  if (frame_reg_rtx != sp_reg_rtx)
-    {
-      if (sp_offset != 0)
-       {
-         rtx dest_reg = savres ? gen_rtx_REG (Pmode, 11) : sp_reg_rtx;
-         rtx insn = emit_insn (gen_add3_insn (dest_reg, frame_reg_rtx,
-                                              GEN_INT (sp_offset)));
-         if (!savres)
-           return insn;
-       }
-      else if (!savres)
-       return emit_move_insn (sp_reg_rtx, frame_reg_rtx);
-    }
-  else if (sp_offset != 0)
-    {
-      /* If we are restoring registers out-of-line, we will be using the
-        "exit" variants of the restore routines, which will reset the
-        stack for us.  But we do need to point r11 into the right place
-        for those routines.  */
-      rtx dest_reg = (savres
-                     ? gen_rtx_REG (Pmode, 11)
-                     : sp_reg_rtx);
+    rs6000_emit_stack_tie (frame_reg_rtx, frame_pointer_needed);
+
+  /* If we are restoring registers out-of-line, we will be using the
+     "exit" variants of the restore routines, which will reset the
+     stack for us.  But we do need to point updt_reg into the
+     right place for those routines.  */
+  updt_reg_rtx = gen_rtx_REG (Pmode, updt_regno);
+
+  if (frame_off != 0)
+    return emit_insn (gen_add3_insn (updt_reg_rtx,
+                                    frame_reg_rtx, GEN_INT (frame_off)));
+  else if (REGNO (frame_reg_rtx) != updt_regno)
+    return emit_move_insn (updt_reg_rtx, frame_reg_rtx);
 
-      rtx insn = emit_insn (gen_add3_insn (dest_reg, sp_reg_rtx,
-                                          GEN_INT (sp_offset)));
-      if (!savres)
-       return insn;
-    }
   return NULL_RTX;
 }
 
+/* Return the register number used as a pointer by out-of-line
+   save/restore functions.  */
+
+static inline unsigned
+ptr_regno_for_savres (int sel)
+{
+  if (DEFAULT_ABI == ABI_AIX)
+    return (sel & SAVRES_REG) == SAVRES_FPR || (sel & SAVRES_LR) ? 1 : 12;
+  return DEFAULT_ABI == ABI_DARWIN && (sel & SAVRES_REG) == SAVRES_FPR ? 1 : 11;
+}
+
 /* Construct a parallel rtx describing the effect of a call to an
    out-of-line register save/restore routine, and emit the insn
    or jump_insn as appropriate.  */
 
 static rtx
 rs6000_emit_savres_rtx (rs6000_stack_t *info,
-                       rtx frame_reg_rtx, int save_area_offset,
-                       enum machine_mode reg_mode,
-                       bool savep, bool gpr, bool lr)
+                       rtx frame_reg_rtx, int save_area_offset, int lr_offset,
+                       enum machine_mode reg_mode, int sel)
 {
   int i;
   int offset, start_reg, end_reg, n_regs, use_reg;
@@ -19226,54 +19323,60 @@ rs6000_emit_savres_rtx (rs6000_stack_t *info,
   rtx par, insn;
 
   offset = 0;
-  start_reg = (gpr
+  start_reg = ((sel & SAVRES_REG) == SAVRES_GPR
               ? info->first_gp_reg_save
-              : info->first_fp_reg_save);
-  end_reg = gpr ? 32 : 64;
+              : (sel & SAVRES_REG) == SAVRES_FPR
+              ? info->first_fp_reg_save
+              : (sel & SAVRES_REG) == SAVRES_VR
+              ? info->first_altivec_reg_save
+              : -1);
+  end_reg = ((sel & SAVRES_REG) == SAVRES_GPR
+            ? 32
+            : (sel & SAVRES_REG) == SAVRES_FPR
+            ? 64
+            : (sel & SAVRES_REG) == SAVRES_VR
+            ? LAST_ALTIVEC_REGNO + 1
+            : -1);
   n_regs = end_reg - start_reg;
-  p = rtvec_alloc ((lr ? 4 : 3) + n_regs);
+  p = rtvec_alloc (3 + ((sel & SAVRES_LR) ? 1 : 0)
+                  + ((sel & SAVRES_REG) == SAVRES_VR ? 1 : 0)
+                  + n_regs);
 
-  if (!savep && lr)
+  if (!(sel & SAVRES_SAVE) && (sel & SAVRES_LR))
     RTVEC_ELT (p, offset++) = ret_rtx;
 
   RTVEC_ELT (p, offset++)
     = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, LR_REGNO));
 
-  sym = rs6000_savres_routine_sym (info, savep, gpr, lr);
+  sym = rs6000_savres_routine_sym (info, sel);
   RTVEC_ELT (p, offset++) = gen_rtx_USE (VOIDmode, sym);
-  use_reg = DEFAULT_ABI == ABI_AIX ? (gpr && !lr ? 12 : 1)
-                                  : DEFAULT_ABI == ABI_DARWIN && !gpr ? 1
-                                                                      : 11;
-  RTVEC_ELT (p, offset++)
-    = gen_rtx_USE (VOIDmode,
-                  gen_rtx_REG (Pmode, use_reg));
 
-  for (i = 0; i < end_reg - start_reg; i++)
+  use_reg = ptr_regno_for_savres (sel);
+  if ((sel & SAVRES_REG) == SAVRES_VR)
     {
-      rtx addr, reg, mem;
-      reg = gen_rtx_REG (reg_mode, start_reg + i);
-      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                          GEN_INT (save_area_offset + reg_size*i));
-      mem = gen_frame_mem (reg_mode, addr);
-
-      RTVEC_ELT (p, i + offset) = gen_rtx_SET (VOIDmode,
-                                              savep ? mem : reg,
-                                              savep ? reg : mem);
+      /* Vector regs are saved/restored using [reg+reg] addressing.  */
+      RTVEC_ELT (p, offset++)
+       = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (Pmode, use_reg));
+      RTVEC_ELT (p, offset++)
+       = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, 0));
     }
+  else
+    RTVEC_ELT (p, offset++)
+      = gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, use_reg));
 
-  if (savep && lr)
-    {
-      rtx addr, reg, mem;
-      reg = gen_rtx_REG (Pmode, 0);
-      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                          GEN_INT (info->lr_save_offset));
-      mem = gen_frame_mem (Pmode, addr);
-      RTVEC_ELT (p, i + offset) = gen_rtx_SET (VOIDmode, mem, reg);
-    }
+  for (i = 0; i < end_reg - start_reg; i++)
+    RTVEC_ELT (p, i + offset)
+      = gen_frame_set (gen_rtx_REG (reg_mode, start_reg + i),
+                      frame_reg_rtx, save_area_offset + reg_size * i,
+                      (sel & SAVRES_SAVE) != 0);
+
+  if ((sel & SAVRES_SAVE) && (sel & SAVRES_LR))
+    RTVEC_ELT (p, i + offset)
+      = gen_frame_store (gen_rtx_REG (Pmode, 0), frame_reg_rtx, lr_offset);
 
   par = gen_rtx_PARALLEL (VOIDmode, p);
 
-  if (!savep && lr)
+  if (!(sel & SAVRES_SAVE) && (sel & SAVRES_LR))
     {
       insn = emit_jump_insn (par);
       JUMP_LABEL (insn) = ret_rtx;
@@ -19313,18 +19416,40 @@ rs6000_emit_prologue (void)
   enum machine_mode reg_mode = Pmode;
   int reg_size = TARGET_32BIT ? 4 : 8;
   rtx sp_reg_rtx = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
-  rtx frame_ptr_rtx = gen_rtx_REG (Pmode, 12);
   rtx frame_reg_rtx = sp_reg_rtx;
+  unsigned int cr_save_regno;
   rtx cr_save_rtx = NULL_RTX;
   rtx insn;
   int strategy;
-  int saving_FPRs_inline;
-  int saving_GPRs_inline;
-  int using_store_multiple;
   int using_static_chain_p = (cfun->static_chain_decl != NULL_TREE
-                              && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
+                             && df_regs_ever_live_p (STATIC_CHAIN_REGNUM)
                              && call_used_regs[STATIC_CHAIN_REGNUM]);
-  HOST_WIDE_INT sp_offset = 0;
+  /* Offset to top of frame for frame_reg and sp respectively.  */
+  HOST_WIDE_INT frame_off = 0;
+  HOST_WIDE_INT sp_off = 0;
+
+#ifdef ENABLE_CHECKING
+  /* Track and check usage of r0, r11, r12.  */
+  int reg_inuse = using_static_chain_p ? 1 << 11 : 0;
+#define START_USE(R) do \
+  {                                            \
+    gcc_assert ((reg_inuse & (1 << (R))) == 0);        \
+    reg_inuse |= 1 << (R);                     \
+  } while (0)
+#define END_USE(R) do \
+  {                                            \
+    gcc_assert ((reg_inuse & (1 << (R))) != 0);        \
+    reg_inuse &= ~(1 << (R));                  \
+  } while (0)
+#define NOT_INUSE(R) do \
+  {                                            \
+    gcc_assert ((reg_inuse & (1 << (R))) == 0);        \
+  } while (0)
+#else
+#define START_USE(R) do {} while (0)
+#define END_USE(R) do {} while (0)
+#define NOT_INUSE(R) do {} while (0)
+#endif
 
   if (flag_stack_usage_info)
     current_function_static_stack_size = info->total_size;
@@ -19352,52 +19477,6 @@ rs6000_emit_prologue (void)
       reg_size = 8;
     }
 
-  strategy = info->savres_strategy;
-  using_store_multiple = strategy & SAVRES_MULTIPLE;
-  saving_FPRs_inline = strategy & SAVE_INLINE_FPRS;
-  saving_GPRs_inline = strategy & SAVE_INLINE_GPRS;
-
-  /* For V.4, update stack before we do any saving and set back pointer.  */
-  if (! WORLD_SAVE_P (info)
-      && info->push_p
-      && (DEFAULT_ABI == ABI_V4
-         || crtl->calls_eh_return))
-    {
-      bool need_r11 = (TARGET_SPE
-                      ? (!saving_GPRs_inline
-                         && info->spe_64bit_regs_used == 0)
-                      : (!saving_FPRs_inline || !saving_GPRs_inline));
-      rtx copy_reg = need_r11 ? gen_rtx_REG (Pmode, 11) : NULL;
-
-      if (info->total_size < 32767)
-       sp_offset = info->total_size;
-      else if (need_r11)
-       frame_reg_rtx = copy_reg;
-      else if (info->cr_save_p
-              || info->lr_save_p
-              || info->first_fp_reg_save < 64
-              || info->first_gp_reg_save < 32
-              || info->altivec_size != 0
-              || info->vrsave_mask != 0
-              || crtl->calls_eh_return)
-       {
-         copy_reg = frame_ptr_rtx;
-         frame_reg_rtx = copy_reg;
-       }
-      else
-       {
-         /* The prologue won't be saving any regs so there is no need
-            to set up a frame register to access any frame save area.
-            We also won't be using sp_offset anywhere below, but set
-            the correct value anyway to protect against future
-            changes to this function.  */
-         sp_offset = info->total_size;
-       }
-      rs6000_emit_allocate_stack (info->total_size, copy_reg);
-      if (frame_reg_rtx != sp_reg_rtx)
-       rs6000_emit_stack_tie ();
-    }
-
   /* Handle world saves specially here.  */
   if (WORLD_SAVE_P (info))
     {
@@ -19425,7 +19504,7 @@ rs6000_emit_prologue (void)
                  && info->push_p
                  && info->lr_save_p
                  && (!crtl->calls_eh_return
-                      || info->ehrd_offset == -432)
+                     || info->ehrd_offset == -432)
                  && info->vrsave_save_offset == -224
                  && info->altivec_save_offset == -416);
 
@@ -19451,59 +19530,33 @@ rs6000_emit_prologue (void)
       /* We do floats first so that the instruction pattern matches
         properly.  */
       for (i = 0; i < 64 - info->first_fp_reg_save; i++)
-       {
-         rtx reg = gen_rtx_REG (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                                  ? DFmode : SFmode), 
-                                info->first_fp_reg_save + i);
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->fp_save_offset
-                                           + sp_offset + 8 * i));
-         rtx mem = gen_frame_mem (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                                    ? DFmode : SFmode), addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
-       }
+       RTVEC_ELT (p, j++)
+         = gen_frame_store (gen_rtx_REG (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT
+                                         ? DFmode : SFmode,
+                                         info->first_fp_reg_save + i),
+                            frame_reg_rtx,
+                            info->fp_save_offset + frame_off + 8 * i);
       for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
-       {
-         rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->altivec_save_offset
-                                           + sp_offset + 16 * i));
-         rtx mem = gen_frame_mem (V4SImode, addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
-       }
+       RTVEC_ELT (p, j++)
+         = gen_frame_store (gen_rtx_REG (V4SImode,
+                                         info->first_altivec_reg_save + i),
+                            frame_reg_rtx,
+                            info->altivec_save_offset + frame_off + 16 * i);
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-       {
-         rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->gp_save_offset
-                                           + sp_offset + reg_size * i));
-         rtx mem = gen_frame_mem (reg_mode, addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
-       }
+       RTVEC_ELT (p, j++)
+         = gen_frame_store (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+                            frame_reg_rtx,
+                            info->gp_save_offset + frame_off + reg_size * i);
 
-      {
-       /* CR register traditionally saved as CR2.  */
-       rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
-       rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                GEN_INT (info->cr_save_offset
-                                         + sp_offset));
-       rtx mem = gen_frame_mem (reg_mode, addr);
-
-       RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg);
-      }
+      /* CR register traditionally saved as CR2.  */
+      RTVEC_ELT (p, j++)
+       = gen_frame_store (gen_rtx_REG (SImode, CR2_REGNO),
+                          frame_reg_rtx, info->cr_save_offset + frame_off);
       /* Explain about use of R0.  */
       if (info->lr_save_p)
-       {
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->lr_save_offset
-                                           + sp_offset));
-         rtx mem = gen_frame_mem (reg_mode, addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, mem, reg0);
-       }
+       RTVEC_ELT (p, j++)
+         = gen_frame_store (reg0,
+                            frame_reg_rtx, info->lr_save_offset + frame_off);
       /* Explain what happens to the stack pointer.  */
       {
        rtx newval = gen_rtx_PLUS (Pmode, sp_reg_rtx, treg);
@@ -19511,9 +19564,69 @@ rs6000_emit_prologue (void)
       }
 
       insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
-      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+      rs6000_frame_related (insn, frame_reg_rtx, sp_off - frame_off,
                            treg, GEN_INT (-info->total_size));
-      sp_offset = info->total_size;
+      sp_off = frame_off = info->total_size;
+    }
+
+  strategy = info->savres_strategy;
+
+  /* For V.4, update stack before we do any saving and set back pointer.  */
+  if (! WORLD_SAVE_P (info)
+      && info->push_p
+      && (DEFAULT_ABI == ABI_V4
+         || crtl->calls_eh_return))
+    {
+      bool need_r11 = (TARGET_SPE
+                      ? (!(strategy & SAVE_INLINE_GPRS)
+                         && info->spe_64bit_regs_used == 0)
+                      : (!(strategy & SAVE_INLINE_FPRS)
+                         || !(strategy & SAVE_INLINE_GPRS)
+                         || !(strategy & SAVE_INLINE_VRS)));
+      int ptr_regno = -1;
+      rtx ptr_reg = NULL_RTX;
+      int ptr_off = 0;
+
+      if (info->total_size < 32767)
+       frame_off = info->total_size;
+      else if (need_r11)
+       ptr_regno = 11;
+      else if (info->cr_save_p
+              || info->lr_save_p
+              || info->first_fp_reg_save < 64
+              || info->first_gp_reg_save < 32
+              || info->altivec_size != 0
+              || info->vrsave_mask != 0
+              || crtl->calls_eh_return)
+       ptr_regno = 12;
+      else
+       {
+         /* The prologue won't be saving any regs so there is no need
+            to set up a frame register to access any frame save area.
+            We also won't be using frame_off anywhere below, but set
+            the correct value anyway to protect against future
+            changes to this function.  */
+         frame_off = info->total_size;
+       }
+      if (ptr_regno != -1)
+       {
+         /* Set up the frame offset to that needed by the first
+            out-of-line save function.  */
+         START_USE (ptr_regno);
+         ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
+         frame_reg_rtx = ptr_reg;
+         if (!(strategy & SAVE_INLINE_FPRS) && info->fp_size != 0)
+           gcc_checking_assert (info->fp_save_offset + info->fp_size == 0);
+         else if (!(strategy & SAVE_INLINE_GPRS) && info->first_gp_reg_save < 32)
+           ptr_off = info->gp_save_offset + info->gp_size;
+         else if (!(strategy & SAVE_INLINE_VRS) && info->altivec_size != 0)
+           ptr_off = info->altivec_save_offset + info->altivec_size;
+         frame_off = -ptr_off;
+       }
+      rs6000_emit_allocate_stack (info->total_size, ptr_reg, ptr_off);
+      sp_off = info->total_size;
+      if (frame_reg_rtx != sp_reg_rtx)
+       rs6000_emit_stack_tie (frame_reg_rtx, false);
     }
 
   /* If we use the link register, get it into r0.  */
@@ -19521,34 +19634,42 @@ rs6000_emit_prologue (void)
     {
       rtx addr, reg, mem;
 
-      insn = emit_move_insn (gen_rtx_REG (Pmode, 0),
-                            gen_rtx_REG (Pmode, LR_REGNO));
+      reg = gen_rtx_REG (Pmode, 0);
+      START_USE (0);
+      insn = emit_move_insn (reg, gen_rtx_REG (Pmode, LR_REGNO));
       RTX_FRAME_RELATED_P (insn) = 1;
 
       if (!(strategy & (SAVE_NOINLINE_GPRS_SAVES_LR
                        | SAVE_NOINLINE_FPRS_SAVES_LR)))
        {
          addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                              GEN_INT (info->lr_save_offset + sp_offset));
-         reg = gen_rtx_REG (Pmode, 0);
+                              GEN_INT (info->lr_save_offset + frame_off));
          mem = gen_rtx_MEM (Pmode, addr);
          /* This should not be of rs6000_sr_alias_set, because of
             __builtin_return_address.  */
 
          insn = emit_move_insn (mem, reg);
-         rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+         rs6000_frame_related (insn, frame_reg_rtx, sp_off - frame_off,
                                NULL_RTX, NULL_RTX);
+         END_USE (0);
        }
     }
 
-  /* If we need to save CR, put it into r12 or r11.  */
-  if (!WORLD_SAVE_P (info) && info->cr_save_p && frame_reg_rtx != frame_ptr_rtx)
+  /* If we need to save CR, put it into r12 or r11.  Choose r12 except when
+     r12 will be needed by out-of-line gpr restore.  */
+  cr_save_regno = (DEFAULT_ABI == ABI_AIX
+                  && !(strategy & (SAVE_INLINE_GPRS
+                                   | SAVE_NOINLINE_GPRS_SAVES_LR))
+                  ? 11 : 12);
+  if (!WORLD_SAVE_P (info)
+      && info->cr_save_p
+      && REGNO (frame_reg_rtx) != cr_save_regno
+      && !(using_static_chain_p && cr_save_regno == 11))
     {
       rtx set;
 
-      cr_save_rtx
-       = gen_rtx_REG (SImode, DEFAULT_ABI == ABI_AIX && !saving_GPRs_inline
-                      ? 11 : 12);
+      cr_save_rtx = gen_rtx_REG (SImode, cr_save_regno);
+      START_USE (cr_save_regno);
       insn = emit_insn (gen_movesi_from_cr (cr_save_rtx));
       RTX_FRAME_RELATED_P (insn) = 1;
       /* Now, there's no way that dwarf2out_frame_debug_expr is going
@@ -19564,30 +19685,42 @@ rs6000_emit_prologue (void)
 
   /* Do any required saving of fpr's.  If only one or two to save, do
      it ourselves.  Otherwise, call function.  */
-  if (!WORLD_SAVE_P (info) && saving_FPRs_inline)
+  if (!WORLD_SAVE_P (info) && (strategy & SAVE_INLINE_FPRS))
     {
       int i;
       for (i = 0; i < 64 - info->first_fp_reg_save; i++)
-       if ((df_regs_ever_live_p (info->first_fp_reg_save+i)
-            && ! call_used_regs[info->first_fp_reg_save+i]))
-         emit_frame_save (frame_reg_rtx, frame_ptr_rtx, 
-                          (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                           ? DFmode : SFmode,
+       if (save_reg_p (info->first_fp_reg_save + i))
+         emit_frame_save (frame_reg_rtx,
+                          (TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT
+                           ? DFmode : SFmode),
                           info->first_fp_reg_save + i,
-                          info->fp_save_offset + sp_offset + 8 * i,
-                          info->total_size);
+                          info->fp_save_offset + frame_off + 8 * i,
+                          sp_off - frame_off);
     }
   else if (!WORLD_SAVE_P (info) && info->first_fp_reg_save != 64)
     {
-      insn = rs6000_emit_savres_rtx (info, frame_reg_rtx,
-                                    info->fp_save_offset + sp_offset,
-                                    DFmode,
-                                    /*savep=*/true, /*gpr=*/false,
-                                    /*lr=*/((strategy
-                                             & SAVE_NOINLINE_FPRS_SAVES_LR)
-                                            != 0));
-      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+      bool lr = (strategy & SAVE_NOINLINE_FPRS_SAVES_LR) != 0;
+      int sel = SAVRES_SAVE | SAVRES_FPR | (lr ? SAVRES_LR : 0);
+      unsigned ptr_regno = ptr_regno_for_savres (sel);
+      rtx ptr_reg = frame_reg_rtx;
+
+      if (REGNO (frame_reg_rtx) == ptr_regno)
+       gcc_checking_assert (frame_off == 0);
+      else
+       {
+         ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
+         NOT_INUSE (ptr_regno);
+         emit_insn (gen_add3_insn (ptr_reg,
+                                   frame_reg_rtx, GEN_INT (frame_off)));
+       }
+      insn = rs6000_emit_savres_rtx (info, ptr_reg,
+                                    info->fp_save_offset,
+                                    info->lr_save_offset,
+                                    DFmode, sel);
+      rs6000_frame_related (insn, ptr_reg, sp_off,
                            NULL_RTX, NULL_RTX);
+      if (lr)
+       END_USE (0);
     }
 
   /* Save GPRs.  This is done as a PARALLEL if we are using
@@ -19599,152 +19732,138 @@ rs6000_emit_prologue (void)
     {
       int i;
       rtx spe_save_area_ptr;
-      int save_ptr_to_sp;
+      HOST_WIDE_INT save_off;
       int ool_adjust = 0;
 
       /* Determine whether we can address all of the registers that need
         to be saved with an offset from frame_reg_rtx that fits in
         the small const field for SPE memory instructions.  */
       int spe_regs_addressable
-       = (SPE_CONST_OFFSET_OK (info->spe_gp_save_offset + sp_offset
+       = (SPE_CONST_OFFSET_OK (info->spe_gp_save_offset + frame_off
                                + reg_size * (32 - info->first_gp_reg_save - 1))
-          && saving_GPRs_inline);
-      int spe_offset;
+          && (strategy & SAVE_INLINE_GPRS));
 
       if (spe_regs_addressable)
        {
          spe_save_area_ptr = frame_reg_rtx;
-         save_ptr_to_sp = info->total_size - sp_offset;
-         spe_offset = info->spe_gp_save_offset + sp_offset;
+         save_off = frame_off;
        }
       else
        {
          /* Make r11 point to the start of the SPE save area.  We need
             to be careful here if r11 is holding the static chain.  If
             it is, then temporarily save it in r0.  */
-         int offset;
+         HOST_WIDE_INT offset;
 
-         if (!saving_GPRs_inline)
+         if (!(strategy & SAVE_INLINE_GPRS))
            ool_adjust = 8 * (info->first_gp_reg_save
                              - (FIRST_SAVRES_REGISTER + 1));
-         offset = info->spe_gp_save_offset + sp_offset - ool_adjust;
+         offset = info->spe_gp_save_offset + frame_off - ool_adjust;
          spe_save_area_ptr = gen_rtx_REG (Pmode, 11);
-         save_ptr_to_sp = info->total_size - sp_offset + offset;
-         spe_offset = 0;
+         save_off = frame_off - offset;
 
          if (using_static_chain_p)
            {
              rtx r0 = gen_rtx_REG (Pmode, 0);
+
+             START_USE (0);
              gcc_assert (info->first_gp_reg_save > 11);
 
              emit_move_insn (r0, spe_save_area_ptr);
            }
+         else if (REGNO (frame_reg_rtx) != 11)
+           START_USE (11);
+
          emit_insn (gen_addsi3 (spe_save_area_ptr,
                                 frame_reg_rtx, GEN_INT (offset)));
-         if (REGNO (frame_reg_rtx) == 11)
-           sp_offset = -info->spe_gp_save_offset + ool_adjust;
+         if (!using_static_chain_p && REGNO (frame_reg_rtx) == 11)
+           frame_off = -info->spe_gp_save_offset + ool_adjust;
        }
 
-      if (saving_GPRs_inline)
+      if ((strategy & SAVE_INLINE_GPRS))
        {
          for (i = 0; i < 32 - info->first_gp_reg_save; i++)
            if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
-             {
-               rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-               rtx offset, addr, mem;
-
-               /* We're doing all this to ensure that the offset fits into
-                  the immediate offset of 'evstdd'.  */
-               gcc_assert (SPE_CONST_OFFSET_OK (reg_size * i + spe_offset));
-
-               offset = GEN_INT (reg_size * i + spe_offset);
-               addr = gen_rtx_PLUS (Pmode, spe_save_area_ptr, offset);
-               mem = gen_rtx_MEM (V2SImode, addr);
-
-               insn = emit_move_insn (mem, reg);
-
-               rs6000_frame_related (insn,
-                                     spe_save_area_ptr, save_ptr_to_sp,
-                                     NULL_RTX, NULL_RTX);
-             }
+             emit_frame_save (spe_save_area_ptr, reg_mode,
+                              info->first_gp_reg_save + i,
+                              (info->spe_gp_save_offset + save_off
+                               + reg_size * i),
+                              sp_off - save_off);
        }
       else
        {
          insn = rs6000_emit_savres_rtx (info, spe_save_area_ptr,
-                                        ool_adjust, reg_mode,
-                                        /*savep=*/true, /*gpr=*/true,
-                                        /*lr=*/false);
+                                        info->spe_gp_save_offset + save_off,
+                                        0, reg_mode,
+                                        SAVRES_SAVE | SAVRES_GPR);
 
-         rs6000_frame_related (insn, spe_save_area_ptr, save_ptr_to_sp,
+         rs6000_frame_related (insn, spe_save_area_ptr, sp_off - save_off,
                                NULL_RTX, NULL_RTX);
        }
 
       /* Move the static chain pointer back.  */
-      if (using_static_chain_p && !spe_regs_addressable)
-       emit_move_insn (spe_save_area_ptr, gen_rtx_REG (Pmode, 0));
-    }
-  else if (!WORLD_SAVE_P (info) && !saving_GPRs_inline)
-    {
-      if (DEFAULT_ABI == ABI_DARWIN)
+      if (!spe_regs_addressable)
        {
-         rtx dest_reg = gen_rtx_REG (Pmode, 11);
-         if (info->first_fp_reg_save == 64)
-           {
-             /* we only need a copy, no fprs were saved.  */
-             if (dest_reg != frame_reg_rtx)
-               emit_move_insn (dest_reg, frame_reg_rtx);
-           }
-         else
+         if (using_static_chain_p)
            {
-             int save_off = 8 * (64 - info->first_fp_reg_save);
-             rtx offset = GEN_INT (sp_offset - save_off);
-
-             if (REGNO (dest_reg) == REGNO (frame_reg_rtx))
-               sp_offset = save_off;
-             emit_insn (gen_add3_insn (dest_reg, frame_reg_rtx, offset));
+             emit_move_insn (spe_save_area_ptr, gen_rtx_REG (Pmode, 0));
+             END_USE (0);
            }
+         else if (REGNO (frame_reg_rtx) != 11)
+           END_USE (11);
        }
+    }
+  else if (!WORLD_SAVE_P (info) && !(strategy & SAVE_INLINE_GPRS))
+    {
+      bool lr = (strategy & SAVE_NOINLINE_GPRS_SAVES_LR) != 0;
+      int sel = SAVRES_SAVE | SAVRES_GPR | (lr ? SAVRES_LR : 0);
+      unsigned ptr_regno = ptr_regno_for_savres (sel);
+      rtx ptr_reg = frame_reg_rtx;
+      bool ptr_set_up = REGNO (ptr_reg) == ptr_regno;
+      int end_save = info->gp_save_offset + info->gp_size;
+      int ptr_off;
+
+      if (!ptr_set_up)
+       ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
+
       /* Need to adjust r11 (r12) if we saved any FPRs.  */
-      else if (info->first_fp_reg_save != 64)
+      if (end_save + frame_off != 0)
        {
-         rtx dest_reg = gen_rtx_REG (Pmode, DEFAULT_ABI == ABI_AIX ? 12 : 11);
-         int save_off = 8 * (64 - info->first_fp_reg_save);
-         rtx offset = GEN_INT (sp_offset - save_off);
+         rtx offset = GEN_INT (end_save + frame_off);
 
-         if (REGNO (dest_reg) == REGNO (frame_reg_rtx))
-           sp_offset = save_off;
-         emit_insn (gen_add3_insn (dest_reg, frame_reg_rtx, offset));
+         if (ptr_set_up)
+           frame_off = -end_save;
+         else
+           NOT_INUSE (ptr_regno);
+         emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
        }
-
-      insn = rs6000_emit_savres_rtx (info, frame_reg_rtx,
-                                    info->gp_save_offset + sp_offset,
-                                    reg_mode,
-                                    /*savep=*/true, /*gpr=*/true,
-                                    /*lr=*/((strategy
-                                             & SAVE_NOINLINE_GPRS_SAVES_LR)
-                                            != 0));
-      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+      else if (!ptr_set_up)
+       {
+         NOT_INUSE (ptr_regno);
+         emit_move_insn (ptr_reg, frame_reg_rtx);
+       }
+      ptr_off = -end_save;
+      insn = rs6000_emit_savres_rtx (info, ptr_reg,
+                                    info->gp_save_offset + ptr_off,
+                                    info->lr_save_offset + ptr_off,
+                                    reg_mode, sel);
+      rs6000_frame_related (insn, ptr_reg, sp_off - ptr_off,
                            NULL_RTX, NULL_RTX);
+      if (lr)
+       END_USE (0);
     }
-  else if (!WORLD_SAVE_P (info) && using_store_multiple)
+  else if (!WORLD_SAVE_P (info) && (strategy & SAVRES_MULTIPLE))
     {
       rtvec p;
       int i;
       p = rtvec_alloc (32 - info->first_gp_reg_save);
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-       {
-         rtx addr, reg, mem;
-         reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-         addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                              GEN_INT (info->gp_save_offset
-                                       + sp_offset
-                                       + reg_size * i));
-         mem = gen_frame_mem (reg_mode, addr);
-
-         RTVEC_ELT (p, i) = gen_rtx_SET (VOIDmode, mem, reg);
-       }
+       RTVEC_ELT (p, i)
+         = gen_frame_store (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+                            frame_reg_rtx,
+                            info->gp_save_offset + frame_off + reg_size * i);
       insn = emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
-      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+      rs6000_frame_related (insn, frame_reg_rtx, sp_off - frame_off,
                            NULL_RTX, NULL_RTX);
     }
   else if (!WORLD_SAVE_P (info))
@@ -19752,39 +19871,43 @@ rs6000_emit_prologue (void)
       int i;
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
        if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
-          {
-            rtx addr, reg, mem;
-            reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-
-            addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                 GEN_INT (info->gp_save_offset
-                                          + sp_offset
-                                          + reg_size * i));
-            mem = gen_frame_mem (reg_mode, addr);
-
-            insn = emit_move_insn (mem, reg);
-            rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
-                                  NULL_RTX, NULL_RTX);
-          }
+         emit_frame_save (frame_reg_rtx, reg_mode,
+                          info->first_gp_reg_save + i,
+                          info->gp_save_offset + frame_off + reg_size * i,
+                          sp_off - frame_off);
     }
 
-  /* ??? There's no need to emit actual instructions here, but it's the
-     easiest way to get the frame unwind information emitted.  */
   if (crtl->calls_eh_return)
     {
-      unsigned int i, regno;
+      unsigned int i;
+      rtvec p;
 
       for (i = 0; ; ++i)
        {
-         regno = EH_RETURN_DATA_REGNO (i);
+         unsigned int regno = EH_RETURN_DATA_REGNO (i);
+         if (regno == INVALID_REGNUM)
+           break;
+       }
+
+      p = rtvec_alloc (i);
+
+      for (i = 0; ; ++i)
+       {
+         unsigned int regno = EH_RETURN_DATA_REGNO (i);
          if (regno == INVALID_REGNUM)
            break;
 
-         emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode, regno,
-                          info->ehrd_offset + sp_offset
-                          + reg_size * (int) i,
-                          info->total_size);
+         insn
+           = gen_frame_store (gen_rtx_REG (reg_mode, regno),
+                              sp_reg_rtx,
+                              info->ehrd_offset + sp_off + reg_size * (int) i);
+         RTVEC_ELT (p, i) = insn;
+         RTX_FRAME_RELATED_P (insn) = 1;
        }
+
+      insn = emit_insn (gen_blockage ());
+      RTX_FRAME_RELATED_P (insn) = 1;
+      add_reg_note (insn, REG_FRAME_RELATED_EXPR, gen_rtx_PARALLEL (VOIDmode, p));
     }
 
   /* In AIX ABI we need to make sure r2 is really saved.  */
@@ -19794,13 +19917,15 @@ rs6000_emit_prologue (void)
       rtx save_insn, join_insn, note;
       long toc_restore_insn;
 
-      gcc_assert (frame_reg_rtx == frame_ptr_rtx
-                 || frame_reg_rtx == sp_reg_rtx);
       tmp_reg = gen_rtx_REG (Pmode, 11);
       tmp_reg_si = gen_rtx_REG (SImode, 11);
       if (using_static_chain_p)
-       emit_move_insn (gen_rtx_REG (Pmode, 0), tmp_reg);
-      gcc_assert (saving_GPRs_inline && saving_FPRs_inline);
+       {
+         START_USE (0);
+         emit_move_insn (gen_rtx_REG (Pmode, 0), tmp_reg);
+       }
+      else
+       START_USE (11);
       emit_move_insn (tmp_reg, gen_rtx_REG (Pmode, LR_REGNO));
       /* Peek at instruction to which this function returns.  If it's
         restoring r2, then we know we've already saved r2.  We can't
@@ -19826,9 +19951,9 @@ rs6000_emit_prologue (void)
       JUMP_LABEL (jump) = toc_save_done;
       LABEL_NUSES (toc_save_done) += 1;
 
-      save_insn = emit_frame_save (frame_reg_rtx, frame_ptr_rtx, reg_mode,
-                                  TOC_REGNUM, sp_offset + 5 * reg_size,
-                                  info->total_size);
+      save_insn = emit_frame_save (frame_reg_rtx, reg_mode,
+                                  TOC_REGNUM, frame_off + 5 * reg_size,
+                                  sp_off - frame_off);
 
       emit_label (toc_save_done);
 
@@ -19841,8 +19966,11 @@ rs6000_emit_prologue (void)
         code that minimizes the number of DW_CFA_advance opcodes better
         freedom in placing the annotations.  */
       note = find_reg_note (save_insn, REG_FRAME_RELATED_EXPR, NULL);
-      gcc_assert (note);
-      remove_note (save_insn, note);
+      if (note)
+       remove_note (save_insn, note);
+      else
+       note = alloc_reg_note (REG_FRAME_RELATED_EXPR,
+                              copy_rtx (PATTERN (save_insn)), NULL_RTX);
       RTX_FRAME_RELATED_P (save_insn) = 0;
 
       join_insn = emit_insn (gen_blockage ());
@@ -19850,24 +19978,29 @@ rs6000_emit_prologue (void)
       RTX_FRAME_RELATED_P (join_insn) = 1;
 
       if (using_static_chain_p)
-       emit_move_insn (tmp_reg, gen_rtx_REG (Pmode, 0));
+       {
+         emit_move_insn (tmp_reg, gen_rtx_REG (Pmode, 0));
+         END_USE (0);
+       }
+      else
+       END_USE (11);
     }
 
   /* Save CR if we use any that must be preserved.  */
   if (!WORLD_SAVE_P (info) && info->cr_save_p)
     {
       rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                              GEN_INT (info->cr_save_offset + sp_offset));
+                              GEN_INT (info->cr_save_offset + frame_off));
       rtx mem = gen_frame_mem (SImode, addr);
       /* See the large comment above about why CR2_REGNO is used.  */
       rtx magic_eh_cr_reg = gen_rtx_REG (SImode, CR2_REGNO);
 
-      /* If r12 was used to hold the original sp, copy cr into r0 now
-        that it's free.  */
-      if (REGNO (frame_reg_rtx) == 12)
+      /* If we didn't copy cr before, do so now using r0.  */
+      if (cr_save_rtx == NULL_RTX)
        {
          rtx set;
 
+         START_USE (0);
          cr_save_rtx = gen_rtx_REG (SImode, 0);
          insn = emit_insn (gen_movesi_from_cr (cr_save_rtx));
          RTX_FRAME_RELATED_P (insn) = 1;
@@ -19875,8 +20008,9 @@ rs6000_emit_prologue (void)
          add_reg_note (insn, REG_FRAME_RELATED_EXPR, set);
        }
       insn = emit_move_insn (mem, cr_save_rtx);
+      END_USE (REGNO (cr_save_rtx));
 
-      rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
+      rs6000_frame_related (insn, frame_reg_rtx, sp_off - frame_off,
                            NULL_RTX, NULL_RTX);
     }
 
@@ -19885,21 +20019,38 @@ rs6000_emit_prologue (void)
   if (!WORLD_SAVE_P (info) && info->push_p
       && !(DEFAULT_ABI == ABI_V4 || crtl->calls_eh_return))
     {
-      rtx copy_reg = NULL;
-
-      if (info->total_size < 32767)
-       sp_offset = info->total_size;
-      else if (info->altivec_size != 0
-              || info->vrsave_mask != 0)
-       {
-         copy_reg = frame_ptr_rtx;
-         frame_reg_rtx = copy_reg;
-       }
-      else
-       sp_offset = info->total_size;
-      rs6000_emit_allocate_stack (info->total_size, copy_reg);
+      rtx ptr_reg = NULL;
+      int ptr_off = 0;
+
+      /* If saving altivec regs we need to be able to address all save
+        locations using a 16-bit offset.  */
+      if ((strategy & SAVE_INLINE_VRS) == 0
+         || (info->altivec_size != 0
+             && (info->altivec_save_offset + info->altivec_size - 16
+                 + info->total_size - frame_off) > 32767)
+         || (info->vrsave_mask != 0
+             && (info->vrsave_save_offset
+                 + info->total_size - frame_off) > 32767))
+       {
+         int sel = SAVRES_SAVE | SAVRES_VR;
+         unsigned ptr_regno = ptr_regno_for_savres (sel);
+
+         if (using_static_chain_p
+             && ptr_regno == STATIC_CHAIN_REGNUM)
+           ptr_regno = 12;
+         if (REGNO (frame_reg_rtx) != ptr_regno)
+           START_USE (ptr_regno);
+         ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
+         frame_reg_rtx = ptr_reg;
+         ptr_off = info->altivec_save_offset + info->altivec_size;
+         frame_off = -ptr_off;
+       }
+      else if (REGNO (frame_reg_rtx) == 1)
+       frame_off = info->total_size;
+      rs6000_emit_allocate_stack (info->total_size, ptr_reg, ptr_off);
+      sp_off = info->total_size;
       if (frame_reg_rtx != sp_reg_rtx)
-       rs6000_emit_stack_tie ();
+       rs6000_emit_stack_tie (frame_reg_rtx, false);
     }
 
   /* Set frame pointer, if needed.  */
@@ -19911,36 +20062,72 @@ rs6000_emit_prologue (void)
     }
 
   /* Save AltiVec registers if needed.  Save here because the red zone does
-     not include AltiVec registers.  */
-  if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI && info->altivec_size != 0)
+     not always include AltiVec registers.  */
+  if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI
+      && info->altivec_size != 0 && (strategy & SAVE_INLINE_VRS) == 0)
+    {
+      int end_save = info->altivec_save_offset + info->altivec_size;
+      int ptr_off;
+      /* Oddly, the vector save/restore functions point r0 at the end
+        of the save area, then use r11 or r12 to load offsets for
+        [reg+reg] addressing.  */
+      rtx ptr_reg = gen_rtx_REG (Pmode, 0);
+      int scratch_regno = ptr_regno_for_savres (SAVRES_SAVE | SAVRES_VR);
+      rtx scratch_reg = gen_rtx_REG (Pmode, scratch_regno);
+
+      gcc_checking_assert (scratch_regno == 11 || scratch_regno == 12);
+      NOT_INUSE (0);
+      if (end_save + frame_off != 0)
+       {
+         rtx offset = GEN_INT (end_save + frame_off);
+
+         emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
+       }
+      else
+       emit_move_insn (ptr_reg, frame_reg_rtx);
+
+      ptr_off = -end_save;
+      insn = rs6000_emit_savres_rtx (info, scratch_reg,
+                                    info->altivec_save_offset + ptr_off,
+                                    0, V4SImode, SAVRES_SAVE | SAVRES_VR);
+      rs6000_frame_related (insn, scratch_reg, sp_off - ptr_off,
+                           NULL_RTX, NULL_RTX);
+      if (REGNO (frame_reg_rtx) == REGNO (scratch_reg))
+       {
+         /* The oddity mentioned above clobbered our frame reg.  */
+         emit_move_insn (frame_reg_rtx, ptr_reg);
+         frame_off = ptr_off;
+       }
+    }
+  else if (!WORLD_SAVE_P (info) && TARGET_ALTIVEC_ABI
+          && info->altivec_size != 0)
     {
       int i;
 
-      /* There should be a non inline version of this, for when we
-         are saving lots of vector registers.  */
       for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
-        if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
-          {
-            rtx areg, savereg, mem;
-            int offset;
+       if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
+         {
+           rtx areg, savereg, mem;
+           int offset;
 
-            offset = info->altivec_save_offset + sp_offset
-              + 16 * (i - info->first_altivec_reg_save);
+           offset = (info->altivec_save_offset + frame_off
+                     + 16 * (i - info->first_altivec_reg_save));
 
-            savereg = gen_rtx_REG (V4SImode, i);
+           savereg = gen_rtx_REG (V4SImode, i);
 
-            areg = gen_rtx_REG (Pmode, 0);
-            emit_move_insn (areg, GEN_INT (offset));
+           NOT_INUSE (0);
+           areg = gen_rtx_REG (Pmode, 0);
+           emit_move_insn (areg, GEN_INT (offset));
 
-            /* AltiVec addressing mode is [reg+reg].  */
-            mem = gen_frame_mem (V4SImode,
-                                 gen_rtx_PLUS (Pmode, frame_reg_rtx, areg));
+           /* AltiVec addressing mode is [reg+reg].  */
+           mem = gen_frame_mem (V4SImode,
+                                gen_rtx_PLUS (Pmode, frame_reg_rtx, areg));
 
-            insn = emit_move_insn (mem, savereg);
+           insn = emit_move_insn (mem, savereg);
 
-            rs6000_frame_related (insn, frame_ptr_rtx, info->total_size,
-                                  areg, GEN_INT (offset));
-          }
+           rs6000_frame_related (insn, frame_reg_rtx, sp_off - frame_off,
+                                 areg, GEN_INT (offset));
+         }
     }
 
   /* VRSAVE is a bit vector representing which AltiVec registers
@@ -19955,25 +20142,34 @@ rs6000_emit_prologue (void)
       && TARGET_ALTIVEC_VRSAVE
       && info->vrsave_mask != 0)
     {
-      rtx reg, mem, vrsave;
+      rtx reg, vrsave;
       int offset;
+      int save_regno;
 
-      /* Get VRSAVE onto a GPR.  Note that ABI_V4 might be using r12
-         as frame_reg_rtx and r11 as the static chain pointer for
-         nested functions.  */
-      reg = gen_rtx_REG (SImode, 0);
+      /* Get VRSAVE onto a GPR.  Note that ABI_V4 and ABI_DARWIN might
+        be using r12 as frame_reg_rtx and r11 as the static chain
+        pointer for nested functions.  */
+      save_regno = 12;
+      if (DEFAULT_ABI == ABI_AIX && !using_static_chain_p)
+       save_regno = 11;
+      else if (REGNO (frame_reg_rtx) == 12)
+       {
+         save_regno = 11;
+         if (using_static_chain_p)
+           save_regno = 0;
+       }
+
+      NOT_INUSE (save_regno);
+      reg = gen_rtx_REG (SImode, save_regno);
       vrsave = gen_rtx_REG (SImode, VRSAVE_REGNO);
       if (TARGET_MACHO)
-        emit_insn (gen_get_vrsave_internal (reg));
+       emit_insn (gen_get_vrsave_internal (reg));
       else
-        emit_insn (gen_rtx_SET (VOIDmode, reg, vrsave));
+       emit_insn (gen_rtx_SET (VOIDmode, reg, vrsave));
 
       /* Save VRSAVE.  */
-      offset = info->vrsave_save_offset + sp_offset;
-      mem = gen_frame_mem (SImode,
-                          gen_rtx_PLUS (Pmode, frame_reg_rtx, 
-                                        GEN_INT (offset)));
-      insn = emit_move_insn (mem, reg);
+      offset = info->vrsave_save_offset + frame_off;
+      insn = emit_insn (gen_frame_store (reg, frame_reg_rtx, offset));
 
       /* Include the registers in the mask.  */
       emit_insn (gen_iorsi3 (reg, reg, GEN_INT ((int) info->vrsave_mask)));
@@ -20000,13 +20196,14 @@ rs6000_emit_prologue (void)
       if (save_LR_around_toc_setup)
        {
          rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
+         rtx tmp = gen_rtx_REG (Pmode, 12);
 
-         insn = emit_move_insn (frame_ptr_rtx, lr);
+         insn = emit_move_insn (tmp, lr);
          RTX_FRAME_RELATED_P (insn) = 1;
 
          rs6000_emit_load_toc_table (TRUE);
 
-         insn = emit_move_insn (lr, frame_ptr_rtx);
+         insn = emit_move_insn (lr, tmp);
          add_reg_note (insn, REG_CFA_RESTORE, lr);
          RTX_FRAME_RELATED_P (insn) = 1;
        }
@@ -20050,12 +20247,11 @@ rs6000_emit_prologue (void)
      both in terms of the size of DWARF info and the time taken in the
      unwinder to interpret it.  R2 changes, apart from the
      calls_eh_return case earlier in this function, are handled by
-     linux-unwind.h frob_update_context.  */ 
+     linux-unwind.h frob_update_context.  */
   if (rs6000_save_toc_in_prologue_p ())
     {
-      rtx addr = gen_rtx_PLUS (Pmode, sp_reg_rtx, GEN_INT (5 * reg_size));
-      rtx mem = gen_frame_mem (reg_mode, addr);
-      emit_move_insn (mem, gen_rtx_REG (reg_mode, TOC_REGNUM));
+      rtx reg = gen_rtx_REG (reg_mode, TOC_REGNUM);
+      emit_insn (gen_frame_store (reg, sp_reg_rtx, 5 * reg_size));
     }
 }
 
@@ -20073,37 +20269,29 @@ rs6000_output_function_prologue (FILE *file,
   /* Write .extern for any function we will call to save and restore
      fp values.  */
   if (info->first_fp_reg_save < 64
-      && !TARGET_MACHO)
+      && !TARGET_MACHO
+      && !TARGET_ELF)
     {
       char *name;
       int regno = info->first_fp_reg_save - 32;
 
       if ((info->savres_strategy & SAVE_INLINE_FPRS) == 0)
        {
-         name = rs6000_savres_routine_name (info, regno, /*savep=*/true,
-                                            /*gpr=*/false, /*lr=*/false);
+         bool lr = (info->savres_strategy & SAVE_NOINLINE_FPRS_SAVES_LR) != 0;
+         int sel = SAVRES_SAVE | SAVRES_FPR | (lr ? SAVRES_LR : 0);
+         name = rs6000_savres_routine_name (info, regno, sel);
          fprintf (file, "\t.extern %s\n", name);
        }
       if ((info->savres_strategy & REST_INLINE_FPRS) == 0)
        {
-         name = rs6000_savres_routine_name (info, regno, /*savep=*/false,
-                                            /*gpr=*/false, /*lr=*/true);
+         bool lr = (info->savres_strategy
+                    & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
+         int sel = SAVRES_FPR | (lr ? SAVRES_LR : 0);
+         name = rs6000_savres_routine_name (info, regno, sel);
          fprintf (file, "\t.extern %s\n", name);
        }
     }
 
-  /* Write .extern for AIX common mode routines, if needed.  */
-  if (! TARGET_POWER && ! TARGET_POWERPC && ! common_mode_defined)
-    {
-      fputs ("\t.extern __mulh\n", file);
-      fputs ("\t.extern __mull\n", file);
-      fputs ("\t.extern __divss\n", file);
-      fputs ("\t.extern __divus\n", file);
-      fputs ("\t.extern __quoss\n", file);
-      fputs ("\t.extern __quous\n", file);
-      common_mode_defined = 1;
-    }
-
   rs6000_pic_labelno++;
 }
 
@@ -20111,10 +20299,37 @@ rs6000_output_function_prologue (FILE *file,
    we restore after the pop when possible.  */
 #define ALWAYS_RESTORE_ALTIVEC_BEFORE_POP 0
 
+/* Restoring cr is a two step process: loading a reg from the frame
+   save, then moving the reg to cr.  For ABI_V4 we must let the
+   unwinder know that the stack location is no longer valid at or
+   before the stack deallocation, but we can't emit a cfa_restore for
+   cr at the stack deallocation like we do for other registers.
+   The trouble is that it is possible for the move to cr to be
+   scheduled after the stack deallocation.  So say exactly where cr
+   is located on each of the two insns.  */
+
+static rtx
+load_cr_save (int regno, rtx frame_reg_rtx, int offset, bool exit_func)
+{
+  rtx mem = gen_frame_mem_offset (SImode, frame_reg_rtx, offset);
+  rtx reg = gen_rtx_REG (SImode, regno);
+  rtx insn = emit_move_insn (reg, mem);
+
+  if (!exit_func && DEFAULT_ABI == ABI_V4)
+    {
+      rtx cr = gen_rtx_REG (SImode, CR2_REGNO);
+      rtx set = gen_rtx_SET (VOIDmode, reg, cr);
+
+      add_reg_note (insn, REG_CFA_REGISTER, set);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+  return reg;
+}
+
 /* Reload CR from REG.  */
 
 static void
-rs6000_restore_saved_cr (rtx reg, int using_mfcr_multiple)
+restore_saved_cr (rtx reg, int using_mfcr_multiple, bool exit_func)
 {
   int count = 0;
   int i;
@@ -20122,7 +20337,7 @@ rs6000_restore_saved_cr (rtx reg, int using_mfcr_multiple)
   if (using_mfcr_multiple)
     {
       for (i = 0; i < 8; i++)
-       if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+       if (save_reg_p (CR0_REGNO + i))
          count++;
       gcc_assert (count);
     }
@@ -20136,13 +20351,13 @@ rs6000_restore_saved_cr (rtx reg, int using_mfcr_multiple)
 
       ndx = 0;
       for (i = 0; i < 8; i++)
-       if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
+       if (save_reg_p (CR0_REGNO + i))
          {
            rtvec r = rtvec_alloc (2);
            RTVEC_ELT (r, 0) = reg;
            RTVEC_ELT (r, 1) = GEN_INT (1 << (7-i));
            RTVEC_ELT (p, ndx) =
-             gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO+i),
+             gen_rtx_SET (VOIDmode, gen_rtx_REG (CCmode, CR0_REGNO + i),
                           gen_rtx_UNSPEC (CCmode, r, UNSPEC_MOVESI_TO_CR));
            ndx++;
          }
@@ -20151,12 +20366,60 @@ rs6000_restore_saved_cr (rtx reg, int using_mfcr_multiple)
     }
   else
     for (i = 0; i < 8; i++)
-      if (df_regs_ever_live_p (CR0_REGNO+i) && ! call_used_regs[CR0_REGNO+i])
-       {
-         emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode,
-                                                      CR0_REGNO+i),
-                                         reg));
-       }
+      if (save_reg_p (CR0_REGNO + i))
+       emit_insn (gen_movsi_to_cr_one (gen_rtx_REG (CCmode, CR0_REGNO + i),
+                                       reg));
+
+  if (!exit_func && (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap))
+    {
+      rtx insn = get_last_insn ();
+      rtx cr = gen_rtx_REG (SImode, CR2_REGNO);
+
+      add_reg_note (insn, REG_CFA_RESTORE, cr);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+}
+
+/* Like cr, the move to lr instruction can be scheduled after the
+   stack deallocation, but unlike cr, its stack frame save is still
+   valid.  So we only need to emit the cfa_restore on the correct
+   instruction.  */
+
+static void
+load_lr_save (int regno, rtx frame_reg_rtx, int offset)
+{
+  rtx mem = gen_frame_mem_offset (Pmode, frame_reg_rtx, offset);
+  rtx reg = gen_rtx_REG (Pmode, regno);
+
+  emit_move_insn (reg, mem);
+}
+
+static void
+restore_saved_lr (int regno, bool exit_func)
+{
+  rtx reg = gen_rtx_REG (Pmode, regno);
+  rtx lr = gen_rtx_REG (Pmode, LR_REGNO);
+  rtx insn = emit_move_insn (lr, reg);
+
+  if (!exit_func && flag_shrink_wrap)
+    {
+      add_reg_note (insn, REG_CFA_RESTORE, lr);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+}
+
+static rtx
+add_crlr_cfa_restore (const rs6000_stack_t *info, rtx cfa_restores)
+{
+  if (info->cr_save_p)
+    cfa_restores = alloc_reg_note (REG_CFA_RESTORE,
+                                  gen_rtx_REG (SImode, CR2_REGNO),
+                                  cfa_restores);
+  if (info->lr_save_p)
+    cfa_restores = alloc_reg_note (REG_CFA_RESTORE,
+                                  gen_rtx_REG (Pmode, LR_REGNO),
+                                  cfa_restores);
+  return cfa_restores;
 }
 
 /* Return true if OFFSET from stack pointer can be clobbered by signals.
@@ -20198,7 +20461,7 @@ rs6000_emit_epilogue (int sibcall)
   int use_backchain_to_restore_sp;
   int restore_lr;
   int strategy;
-  int sp_offset = 0;
+  HOST_WIDE_INT frame_off = 0;
   rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1);
   rtx frame_reg_rtx = sp_reg_rtx;
   rtx cfa_restores = NULL_RTX;
@@ -20207,6 +20470,8 @@ rs6000_emit_epilogue (int sibcall)
   enum machine_mode reg_mode = Pmode;
   int reg_size = TARGET_32BIT ? 4 : 8;
   int i;
+  bool exit_func;
+  unsigned ptr_regno;
 
   info = rs6000_stack_info ();
 
@@ -20276,13 +20541,9 @@ rs6000_emit_epilogue (int sibcall)
 
       {
        /* CR register traditionally saved as CR2.  */
-       rtx reg = gen_rtx_REG (reg_mode, CR2_REGNO);
-       rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                GEN_INT (info->cr_save_offset));
-       rtx mem = gen_frame_mem (reg_mode, addr);
-
-       RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
-
+       rtx reg = gen_rtx_REG (SImode, CR2_REGNO);
+       RTVEC_ELT (p, j++)
+         = gen_frame_load (reg, frame_reg_rtx, info->cr_save_offset);
        if (flag_shrink_wrap)
          {
            cfa_restores = alloc_reg_note (REG_CFA_RESTORE,
@@ -20295,39 +20556,28 @@ rs6000_emit_epilogue (int sibcall)
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
        {
          rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->gp_save_offset
-                                           + reg_size * i));
-         rtx mem = gen_frame_mem (reg_mode, addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+         RTVEC_ELT (p, j++)
+           = gen_frame_load (reg,
+                             frame_reg_rtx, info->gp_save_offset + reg_size * i);
          if (flag_shrink_wrap)
            cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
        }
       for (i = 0; info->first_altivec_reg_save + i <= LAST_ALTIVEC_REGNO; i++)
        {
          rtx reg = gen_rtx_REG (V4SImode, info->first_altivec_reg_save + i);
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->altivec_save_offset
-                                           + 16 * i));
-         rtx mem = gen_frame_mem (V4SImode, addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+         RTVEC_ELT (p, j++)
+           = gen_frame_load (reg,
+                             frame_reg_rtx, info->altivec_save_offset + 16 * i);
          if (flag_shrink_wrap)
            cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
        }
       for (i = 0; info->first_fp_reg_save + i <= 63; i++)
        {
-         rtx reg = gen_rtx_REG (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                                  ? DFmode : SFmode), 
-                                info->first_fp_reg_save + i);
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->fp_save_offset
-                                           + 8 * i));
-         rtx mem = gen_frame_mem (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                                    ? DFmode : SFmode), addr);
-
-         RTVEC_ELT (p, j++) = gen_rtx_SET (VOIDmode, reg, mem);
+         rtx reg = gen_rtx_REG ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT
+                                 ? DFmode : SFmode),
+                                info->first_fp_reg_save + i);
+         RTVEC_ELT (p, j++)
+           = gen_frame_load (reg, frame_reg_rtx, info->fp_save_offset + 8 * i);
          if (flag_shrink_wrap)
            cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
        }
@@ -20352,9 +20602,9 @@ rs6000_emit_epilogue (int sibcall)
       return;
     }
 
-  /* frame_reg_rtx + sp_offset points to the top of this stack frame.  */
+  /* frame_reg_rtx + frame_off points to the top of this stack frame.  */
   if (info->push_p)
-    sp_offset = info->total_size;
+    frame_off = info->total_size;
 
   /* Restore AltiVec registers if we must do so before adjusting the
      stack.  */
@@ -20365,40 +20615,80 @@ rs6000_emit_epilogue (int sibcall)
              && offset_below_red_zone_p (info->altivec_save_offset))))
     {
       int i;
+      int scratch_regno = ptr_regno_for_savres (SAVRES_VR);
 
+      gcc_checking_assert (scratch_regno == 11 || scratch_regno == 12);
       if (use_backchain_to_restore_sp)
        {
-         frame_reg_rtx = gen_rtx_REG (Pmode, 11);
+         int frame_regno = 11;
+
+         if ((strategy & REST_INLINE_VRS) == 0)
+           {
+             /* Of r11 and r12, select the one not clobbered by an
+                out-of-line restore function for the frame register.  */
+             frame_regno = 11 + 12 - scratch_regno;
+           }
+         frame_reg_rtx = gen_rtx_REG (Pmode, frame_regno);
          emit_move_insn (frame_reg_rtx,
                          gen_rtx_MEM (Pmode, sp_reg_rtx));
-         sp_offset = 0;
+         frame_off = 0;
        }
       else if (frame_pointer_needed)
        frame_reg_rtx = hard_frame_pointer_rtx;
 
-      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
-       if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
-         {
-           rtx addr, areg, mem, reg;
+      if ((strategy & REST_INLINE_VRS) == 0)
+       {
+         int end_save = info->altivec_save_offset + info->altivec_size;
+         int ptr_off;
+         rtx ptr_reg = gen_rtx_REG (Pmode, 0);
+         rtx scratch_reg = gen_rtx_REG (Pmode, scratch_regno);
 
-           areg = gen_rtx_REG (Pmode, 0);
-           emit_move_insn
-             (areg, GEN_INT (info->altivec_save_offset
-                             + sp_offset
-                             + 16 * (i - info->first_altivec_reg_save)));
+         if (end_save + frame_off != 0)
+           {
+             rtx offset = GEN_INT (end_save + frame_off);
 
-           /* AltiVec addressing mode is [reg+reg].  */
-           addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
-           mem = gen_frame_mem (V4SImode, addr);
-
-           reg = gen_rtx_REG (V4SImode, i);
-           emit_move_insn (reg, mem);
-           if (flag_shrink_wrap
-               || offset_below_red_zone_p (info->altivec_save_offset
-                                           + (i - info->first_altivec_reg_save)
-                                           * 16))
-             cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg,
-                                            cfa_restores);
+             emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
+           }
+         else
+           emit_move_insn (ptr_reg, frame_reg_rtx);
+
+         ptr_off = -end_save;
+         insn = rs6000_emit_savres_rtx (info, scratch_reg,
+                                        info->altivec_save_offset + ptr_off,
+                                        0, V4SImode, SAVRES_VR);
+       }
+      else
+       {
+         for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+           if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
+             {
+               rtx addr, areg, mem, reg;
+
+               areg = gen_rtx_REG (Pmode, 0);
+               emit_move_insn
+                 (areg, GEN_INT (info->altivec_save_offset
+                                 + frame_off
+                                 + 16 * (i - info->first_altivec_reg_save)));
+
+               /* AltiVec addressing mode is [reg+reg].  */
+               addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+               mem = gen_frame_mem (V4SImode, addr);
+
+               reg = gen_rtx_REG (V4SImode, i);
+               emit_move_insn (reg, mem);
+             }
+       }
+
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+       if (((strategy & REST_INLINE_VRS) == 0
+            || (info->vrsave_mask & ALTIVEC_REG_BIT (i)) != 0)
+           && (flag_shrink_wrap
+               || (offset_below_red_zone_p
+                   (info->altivec_save_offset
+                    + 16 * (i - info->first_altivec_reg_save)))))
+         {
+           rtx reg = gen_rtx_REG (V4SImode, i);
+           cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
          }
     }
 
@@ -20410,7 +20700,7 @@ rs6000_emit_epilogue (int sibcall)
          || (DEFAULT_ABI != ABI_V4
              && offset_below_red_zone_p (info->vrsave_save_offset))))
     {
-      rtx addr, mem, reg;
+      rtx reg;
 
       if (frame_reg_rtx == sp_reg_rtx)
        {
@@ -20419,17 +20709,15 @@ rs6000_emit_epilogue (int sibcall)
              frame_reg_rtx = gen_rtx_REG (Pmode, 11);
              emit_move_insn (frame_reg_rtx,
                              gen_rtx_MEM (Pmode, sp_reg_rtx));
-             sp_offset = 0;
+             frame_off = 0;
            }
          else if (frame_pointer_needed)
            frame_reg_rtx = hard_frame_pointer_rtx;
        }
 
-      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                          GEN_INT (info->vrsave_save_offset + sp_offset));
-      mem = gen_frame_mem (SImode, addr);
       reg = gen_rtx_REG (SImode, 12);
-      emit_move_insn (reg, mem);
+      emit_insn (gen_frame_load (reg, frame_reg_rtx,
+                                info->vrsave_save_offset + frame_off));
 
       emit_insn (generate_set_vrsave (reg, info, 1));
     }
@@ -20448,7 +20736,7 @@ rs6000_emit_epilogue (int sibcall)
 
          insn = emit_move_insn (frame_reg_rtx,
                                 gen_rtx_MEM (Pmode, sp_reg_rtx));
-         sp_offset = 0;
+         frame_off = 0;
        }
       else if (ALWAYS_RESTORE_ALTIVEC_BEFORE_POP
               && DEFAULT_ABI == ABI_V4)
@@ -20470,17 +20758,11 @@ rs6000_emit_epilogue (int sibcall)
       /* Prevent reordering memory accesses against stack pointer restore.  */
       else if (cfun->calls_alloca
               || offset_below_red_zone_p (-info->total_size))
-       {
-         rtx mem1 = gen_rtx_MEM (BLKmode, hard_frame_pointer_rtx);
-         rtx mem2 = gen_rtx_MEM (BLKmode, sp_reg_rtx);
-         MEM_NOTRAP_P (mem1) = 1;
-         MEM_NOTRAP_P (mem2) = 1;
-         emit_insn (gen_frame_tie (mem1, mem2));
-       }
+       rs6000_emit_stack_tie (frame_reg_rtx, true);
 
       insn = emit_insn (gen_add3_insn (frame_reg_rtx, hard_frame_pointer_rtx,
                                       GEN_INT (info->total_size)));
-      sp_offset = 0;
+      frame_off = 0;
     }
   else if (info->push_p
           && DEFAULT_ABI != ABI_V4
@@ -20489,14 +20771,10 @@ rs6000_emit_epilogue (int sibcall)
       /* Prevent reordering memory accesses against stack pointer restore.  */
       if (cfun->calls_alloca
          || offset_below_red_zone_p (-info->total_size))
-       {
-         rtx mem = gen_rtx_MEM (BLKmode, sp_reg_rtx);
-         MEM_NOTRAP_P (mem) = 1;
-         emit_insn (gen_stack_tie (mem));
-       }
+       rs6000_emit_stack_tie (frame_reg_rtx, false);
       insn = emit_insn (gen_add3_insn (sp_reg_rtx, sp_reg_rtx,
                                       GEN_INT (info->total_size)));
-      sp_offset = 0;
+      frame_off = 0;
     }
   if (insn && frame_reg_rtx == sp_reg_rtx)
     {
@@ -20518,26 +20796,94 @@ rs6000_emit_epilogue (int sibcall)
     {
       int i;
 
-      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
-       if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
-         {
-           rtx addr, areg, mem, reg;
+      if ((strategy & REST_INLINE_VRS) == 0)
+       {
+         int end_save = info->altivec_save_offset + info->altivec_size;
+         int ptr_off;
+         rtx ptr_reg = gen_rtx_REG (Pmode, 0);
+         int scratch_regno = ptr_regno_for_savres (SAVRES_VR);
+         rtx scratch_reg = gen_rtx_REG (Pmode, scratch_regno);
 
-           areg = gen_rtx_REG (Pmode, 0);
-           emit_move_insn
-             (areg, GEN_INT (info->altivec_save_offset
-                             + sp_offset
-                             + 16 * (i - info->first_altivec_reg_save)));
+         if (end_save + frame_off != 0)
+           {
+             rtx offset = GEN_INT (end_save + frame_off);
 
-           /* AltiVec addressing mode is [reg+reg].  */
-           addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
-           mem = gen_frame_mem (V4SImode, addr);
-
-           reg = gen_rtx_REG (V4SImode, i);
-           emit_move_insn (reg, mem);
-           if (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap)
-             cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg,
-                                            cfa_restores);
+             emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx, offset));
+           }
+         else
+           emit_move_insn (ptr_reg, frame_reg_rtx);
+
+         ptr_off = -end_save;
+         insn = rs6000_emit_savres_rtx (info, scratch_reg,
+                                        info->altivec_save_offset + ptr_off,
+                                        0, V4SImode, SAVRES_VR);
+         if (REGNO (frame_reg_rtx) == REGNO (scratch_reg))
+           {
+             /* Frame reg was clobbered by out-of-line save.  Restore it
+                from ptr_reg, and if we are calling out-of-line gpr or
+                fpr restore set up the correct pointer and offset.  */
+             unsigned newptr_regno = 1;
+             if (!restoring_GPRs_inline)
+               {
+                 bool lr = info->gp_save_offset + info->gp_size == 0;
+                 int sel = SAVRES_GPR | (lr ? SAVRES_LR : 0);
+                 newptr_regno = ptr_regno_for_savres (sel);
+                 end_save = info->gp_save_offset + info->gp_size;
+               }
+             else if (!restoring_FPRs_inline)
+               {
+                 bool lr = !(strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR);
+                 int sel = SAVRES_FPR | (lr ? SAVRES_LR : 0);
+                 newptr_regno = ptr_regno_for_savres (sel);
+                 end_save = info->gp_save_offset + info->gp_size;
+               }
+
+             if (newptr_regno != 1 && REGNO (frame_reg_rtx) != newptr_regno)
+               frame_reg_rtx = gen_rtx_REG (Pmode, newptr_regno);
+               
+             if (end_save + ptr_off != 0)
+               {
+                 rtx offset = GEN_INT (end_save + ptr_off);
+
+                 frame_off = -end_save;
+                 emit_insn (gen_add3_insn (frame_reg_rtx, ptr_reg, offset));
+               }
+             else
+               {
+                 frame_off = ptr_off;
+                 emit_move_insn (frame_reg_rtx, ptr_reg);
+               }
+           }
+       }
+      else
+       {
+         for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+           if (info->vrsave_mask & ALTIVEC_REG_BIT (i))
+             {
+               rtx addr, areg, mem, reg;
+
+               areg = gen_rtx_REG (Pmode, 0);
+               emit_move_insn
+                 (areg, GEN_INT (info->altivec_save_offset
+                                 + frame_off
+                                 + 16 * (i - info->first_altivec_reg_save)));
+
+               /* AltiVec addressing mode is [reg+reg].  */
+               addr = gen_rtx_PLUS (Pmode, frame_reg_rtx, areg);
+               mem = gen_frame_mem (V4SImode, addr);
+
+               reg = gen_rtx_REG (V4SImode, i);
+               emit_move_insn (reg, mem);
+             }
+       }
+
+      for (i = info->first_altivec_reg_save; i <= LAST_ALTIVEC_REGNO; ++i)
+       if (((strategy & REST_INLINE_VRS) == 0
+            || (info->vrsave_mask & ALTIVEC_REG_BIT (i)) != 0)
+           && (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap))
+         {
+           rtx reg = gen_rtx_REG (V4SImode, i);
+           cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
          }
     }
 
@@ -20549,46 +20895,56 @@ rs6000_emit_epilogue (int sibcall)
       && (DEFAULT_ABI == ABI_V4
          || !offset_below_red_zone_p (info->vrsave_save_offset)))
     {
-      rtx addr, mem, reg;
+      rtx reg;
 
-      addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                          GEN_INT (info->vrsave_save_offset + sp_offset));
-      mem = gen_frame_mem (SImode, addr);
       reg = gen_rtx_REG (SImode, 12);
-      emit_move_insn (reg, mem);
+      emit_insn (gen_frame_load (reg, frame_reg_rtx,
+                                info->vrsave_save_offset + frame_off));
 
       emit_insn (generate_set_vrsave (reg, info, 1));
     }
 
+  /* If we exit by an out-of-line restore function on ABI_V4 then that
+     function will deallocate the stack, so we don't need to worry
+     about the unwinder restoring cr from an invalid stack frame
+     location.  */
+  exit_func = (!restoring_FPRs_inline
+              || (!restoring_GPRs_inline
+                  && info->first_fp_reg_save == 64));
+
   /* Get the old lr if we saved it.  If we are restoring registers
      out-of-line, then the out-of-line routines can do this for us.  */
   if (restore_lr && restoring_GPRs_inline)
-    {
-      rtx mem = gen_frame_mem_offset (Pmode, frame_reg_rtx,
-                                     info->lr_save_offset + sp_offset);
-
-      emit_move_insn (gen_rtx_REG (Pmode, 0), mem);
-    }
+    load_lr_save (0, frame_reg_rtx, info->lr_save_offset + frame_off);
 
   /* Get the old cr if we saved it.  */
   if (info->cr_save_p)
     {
-      rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                              GEN_INT (info->cr_save_offset + sp_offset));
-      rtx mem = gen_frame_mem (SImode, addr);
+      unsigned cr_save_regno = 12;
+
+      if (!restoring_GPRs_inline)
+       {
+         /* Ensure we don't use the register used by the out-of-line
+            gpr register restore below.  */
+         bool lr = info->gp_save_offset + info->gp_size == 0;
+         int sel = SAVRES_GPR | (lr ? SAVRES_LR : 0);
+         int gpr_ptr_regno = ptr_regno_for_savres (sel);
+
+         if (gpr_ptr_regno == 12)
+           cr_save_regno = 11;
+         gcc_checking_assert (REGNO (frame_reg_rtx) != cr_save_regno);
+       }
+      else if (REGNO (frame_reg_rtx) == 12)
+       cr_save_regno = 11;
 
-      cr_save_reg = gen_rtx_REG (SImode,
-                                DEFAULT_ABI == ABI_AIX
-                                && !restoring_GPRs_inline
-                                && info->first_fp_reg_save < 64
-                                ? 11 : 12);
-      emit_move_insn (cr_save_reg, mem);
+      cr_save_reg = load_cr_save (cr_save_regno, frame_reg_rtx,
+                                 info->cr_save_offset + frame_off,
+                                 exit_func);
     }
 
   /* Set LR here to try to overlap restores below.  */
   if (restore_lr && restoring_GPRs_inline)
-    emit_move_insn (gen_rtx_REG (Pmode, LR_REGNO),
-                   gen_rtx_REG (Pmode, 0));
+    restore_saved_lr (0, exit_func);
 
   /* Load exception handler data registers, if needed.  */
   if (crtl->calls_eh_return)
@@ -20597,11 +20953,9 @@ rs6000_emit_epilogue (int sibcall)
 
       if (TARGET_AIX)
        {
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (sp_offset + 5 * reg_size));
-         rtx mem = gen_frame_mem (reg_mode, addr);
-
-         emit_move_insn (gen_rtx_REG (reg_mode, 2), mem);
+         rtx reg = gen_rtx_REG (reg_mode, 2);
+         emit_insn (gen_frame_load (reg, frame_reg_rtx,
+                                    frame_off + 5 * reg_size));
        }
 
       for (i = 0; ; ++i)
@@ -20612,8 +20966,9 @@ rs6000_emit_epilogue (int sibcall)
          if (regno == INVALID_REGNUM)
            break;
 
+         /* Note: possible use of r0 here to address SPE regs.  */
          mem = gen_frame_mem_offset (reg_mode, frame_reg_rtx,
-                                     info->ehrd_offset + sp_offset
+                                     info->ehrd_offset + frame_off
                                      + reg_size * (int) i);
 
          emit_move_insn (gen_rtx_REG (reg_mode, regno), mem);
@@ -20630,16 +20985,13 @@ rs6000_emit_epilogue (int sibcall)
         to be saved with an offset from frame_reg_rtx that fits in
         the small const field for SPE memory instructions.  */
       int spe_regs_addressable
-       = (SPE_CONST_OFFSET_OK (info->spe_gp_save_offset + sp_offset
+       = (SPE_CONST_OFFSET_OK (info->spe_gp_save_offset + frame_off
                                + reg_size * (32 - info->first_gp_reg_save - 1))
           && restoring_GPRs_inline);
-      int spe_offset;
-      int ool_adjust = 0;
 
-      if (spe_regs_addressable)
-       spe_offset = info->spe_gp_save_offset + sp_offset;
-      else
+      if (!spe_regs_addressable)
        {
+         int ool_adjust = 0;
          rtx old_frame_reg_rtx = frame_reg_rtx;
          /* Make r11 point to the start of the SPE save area.  We worried about
             not clobbering it when we were saving registers in the prologue.
@@ -20652,17 +21004,17 @@ rs6000_emit_epilogue (int sibcall)
          frame_reg_rtx = gen_rtx_REG (Pmode, 11);
          emit_insn (gen_addsi3 (frame_reg_rtx, old_frame_reg_rtx,
                                 GEN_INT (info->spe_gp_save_offset
-                                         + sp_offset
+                                         + frame_off
                                          - ool_adjust)));
-         /* Keep the invariant that frame_reg_rtx + sp_offset points
+         /* Keep the invariant that frame_reg_rtx + frame_off points
             at the top of the stack frame.  */
-         sp_offset = -info->spe_gp_save_offset + ool_adjust;
-
-         spe_offset = 0;
+         frame_off = -info->spe_gp_save_offset + ool_adjust;
        }
 
       if (restoring_GPRs_inline)
        {
+         HOST_WIDE_INT spe_offset = info->spe_gp_save_offset + frame_off;
+
          for (i = 0; i < 32 - info->first_gp_reg_save; i++)
            if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
              {
@@ -20682,73 +21034,61 @@ rs6000_emit_epilogue (int sibcall)
        }
       else
        rs6000_emit_savres_rtx (info, frame_reg_rtx,
-                               ool_adjust, reg_mode,
-                               /*savep=*/false, /*gpr=*/true,
-                               /*lr=*/true);
+                               info->spe_gp_save_offset + frame_off,
+                               info->lr_save_offset + frame_off,
+                               reg_mode,
+                               SAVRES_GPR | SAVRES_LR);
     }
   else if (!restoring_GPRs_inline)
     {
       /* We are jumping to an out-of-line function.  */
-      bool can_use_exit = info->first_fp_reg_save == 64;
+      rtx ptr_reg;
+      int end_save = info->gp_save_offset + info->gp_size;
+      bool can_use_exit = end_save == 0;
+      int sel = SAVRES_GPR | (can_use_exit ? SAVRES_LR : 0);
+      int ptr_off;
 
       /* Emit stack reset code if we need it.  */
+      ptr_regno = ptr_regno_for_savres (sel);
+      ptr_reg = gen_rtx_REG (Pmode, ptr_regno);
       if (can_use_exit)
-       {
-         rs6000_emit_stack_reset (info, sp_reg_rtx, frame_reg_rtx,
-                                  sp_offset, can_use_exit);
-         if (DEFAULT_ABI == ABI_DARWIN)
-           /* we only need a copy, no fprs were saved.  */
-           emit_move_insn (gen_rtx_REG (Pmode, 11), frame_reg_rtx);
-
-         if (info->cr_save_p)
-           rs6000_restore_saved_cr (cr_save_reg, using_mtcr_multiple);
-       }
-      else
-       {
-         rtx src_reg = gen_rtx_REG (Pmode, DEFAULT_ABI == ABI_AIX ? 12 : 11);
-
-         emit_insn (gen_add3_insn (src_reg, frame_reg_rtx,
-                                   GEN_INT (sp_offset - info->fp_size)));
-         if (REGNO (frame_reg_rtx) == REGNO (src_reg))
-           sp_offset = info->fp_size;
-       }
-
-      rs6000_emit_savres_rtx (info, frame_reg_rtx,
-                             info->gp_save_offset, reg_mode,
-                             /*savep=*/false, /*gpr=*/true,
-                             /*lr=*/can_use_exit);
+       rs6000_emit_stack_reset (info, frame_reg_rtx, frame_off, ptr_regno);
+      else if (end_save + frame_off != 0)
+       emit_insn (gen_add3_insn (ptr_reg, frame_reg_rtx,
+                                 GEN_INT (end_save + frame_off)));
+      else if (REGNO (frame_reg_rtx) != ptr_regno)
+       emit_move_insn (ptr_reg, frame_reg_rtx);
+      if (REGNO (frame_reg_rtx) == ptr_regno)
+       frame_off = -end_save;
+
+      if (can_use_exit && info->cr_save_p)
+       restore_saved_cr (cr_save_reg, using_mtcr_multiple, true);
+
+      ptr_off = -end_save;
+      rs6000_emit_savres_rtx (info, ptr_reg,
+                             info->gp_save_offset + ptr_off,
+                             info->lr_save_offset + ptr_off,
+                             reg_mode, sel);
     }
   else if (using_load_multiple)
     {
       rtvec p;
       p = rtvec_alloc (32 - info->first_gp_reg_save);
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-       {
-         rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                  GEN_INT (info->gp_save_offset
-                                           + sp_offset
-                                           + reg_size * i));
-         rtx mem = gen_frame_mem (reg_mode, addr);
-         rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-
-         RTVEC_ELT (p, i) = gen_rtx_SET (VOIDmode, reg, mem);
-       }
+       RTVEC_ELT (p, i)
+         = gen_frame_load (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+                           frame_reg_rtx,
+                           info->gp_save_offset + frame_off + reg_size * i);
       emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
     }
   else
     {
       for (i = 0; i < 32 - info->first_gp_reg_save; i++)
-        if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
-         {
-            rtx addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                                     GEN_INT (info->gp_save_offset
-                                              + sp_offset
-                                              + reg_size * i));
-            rtx mem = gen_frame_mem (reg_mode, addr);
-           rtx reg = gen_rtx_REG (reg_mode, info->first_gp_reg_save + i);
-
-           emit_move_insn (reg, mem);
-          }
+       if (rs6000_reg_live_or_pic_offset_p (info->first_gp_reg_save + i))
+         emit_insn (gen_frame_load
+                    (gen_rtx_REG (reg_mode, info->first_gp_reg_save + i),
+                     frame_reg_rtx,
+                     info->gp_save_offset + frame_off + reg_size * i));
     }
 
   if (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap)
@@ -20763,7 +21103,7 @@ rs6000_emit_epilogue (int sibcall)
        {
          insn = get_last_insn ();
          add_reg_note (insn, REG_CFA_DEF_CFA,
-                       plus_constant (frame_reg_rtx, sp_offset));
+                       plus_constant (Pmode, frame_reg_rtx, frame_off));
          RTX_FRAME_RELATED_P (insn) = 1;
        }
 
@@ -20773,23 +21113,14 @@ rs6000_emit_epilogue (int sibcall)
         The cfa_restores must be emitted on or before the insn that
         invalidates the stack, and of course must not be emitted
         before the insn that actually does the restore.  The latter
-        is why the LR cfa_restore condition below is a little
-        complicated.  It's also why it is a bad idea to emit the
-        cfa_restores as a group on the last instruction here that
-        actually does a restore: That insn may be reordered with
-        respect to others doing restores.  */
-      if (info->cr_save_p)
-       cfa_restores = alloc_reg_note (REG_CFA_RESTORE,
-                                      gen_rtx_REG (SImode, CR2_REGNO),
-                                      cfa_restores);
+        is why it is a bad idea to emit the cfa_restores as a group
+        on the last instruction here that actually does a restore:
+        That insn may be reordered with respect to others doing
+        restores.  */
       if (flag_shrink_wrap
-         && (restore_lr
-             || (info->lr_save_p
-                 && !restoring_GPRs_inline
-                 && info->first_fp_reg_save == 64)))
-       cfa_restores = alloc_reg_note (REG_CFA_RESTORE,
-                                      gen_rtx_REG (Pmode, LR_REGNO),
-                                      cfa_restores);
+         && !restoring_GPRs_inline
+         && info->first_fp_reg_save == 64)
+       cfa_restores = add_crlr_cfa_restore (info, cfa_restores);
 
       for (i = info->first_gp_reg_save; i < 32; i++)
        if (!restoring_GPRs_inline
@@ -20813,45 +21144,43 @@ rs6000_emit_epilogue (int sibcall)
 
   if (restore_lr && !restoring_GPRs_inline)
     {
-      rtx mem = gen_frame_mem_offset (Pmode, frame_reg_rtx,
-                                    info->lr_save_offset + sp_offset);
-
-      emit_move_insn (gen_rtx_REG (Pmode, 0), mem);
-      emit_move_insn (gen_rtx_REG (Pmode, LR_REGNO),
-                     gen_rtx_REG (Pmode, 0));
+      load_lr_save (0, frame_reg_rtx, info->lr_save_offset + frame_off);
+      restore_saved_lr (0, exit_func);
     }
 
   /* Restore fpr's if we need to do it without calling a function.  */
   if (restoring_FPRs_inline)
     for (i = 0; i < 64 - info->first_fp_reg_save; i++)
-      if ((df_regs_ever_live_p (info->first_fp_reg_save + i)
-          && !call_used_regs[info->first_fp_reg_save + i]))
+      if (save_reg_p (info->first_fp_reg_save + i))
        {
-         rtx addr, mem, reg;
-         addr = gen_rtx_PLUS (Pmode, frame_reg_rtx,
-                              GEN_INT (info->fp_save_offset
-                                       + sp_offset
-                                       + 8 * i));
-         mem = gen_frame_mem (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                                ? DFmode : SFmode), addr);
-         reg = gen_rtx_REG (((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT)
-                              ? DFmode : SFmode),
-                            info->first_fp_reg_save + i);
-
-         emit_move_insn (reg, mem);
+         rtx reg = gen_rtx_REG ((TARGET_HARD_FLOAT && TARGET_DOUBLE_FLOAT
+                                 ? DFmode : SFmode),
+                                info->first_fp_reg_save + i);
+         emit_insn (gen_frame_load (reg, frame_reg_rtx,
+                                    info->fp_save_offset + frame_off + 8 * i));
          if (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap)
            cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg, cfa_restores);
        }
 
   /* If we saved cr, restore it here.  Just those that were used.  */
   if (info->cr_save_p)
-    rs6000_restore_saved_cr (cr_save_reg, using_mtcr_multiple);
+    restore_saved_cr (cr_save_reg, using_mtcr_multiple, exit_func);
 
   /* If this is V.4, unwind the stack pointer after all of the loads
-     have been done.  */
-  insn = rs6000_emit_stack_reset (info, sp_reg_rtx, frame_reg_rtx,
-                                 sp_offset, !restoring_FPRs_inline);
-  if (insn)
+     have been done, or set up r11 if we are restoring fp out of line.  */
+  ptr_regno = 1;
+  if (!restoring_FPRs_inline)
+    {
+      bool lr = (strategy & REST_NOINLINE_FPRS_DOESNT_RESTORE_LR) == 0;
+      int sel = SAVRES_FPR | (lr ? SAVRES_LR : 0);
+      ptr_regno = ptr_regno_for_savres (sel);
+    }
+
+  insn = rs6000_emit_stack_reset (info, frame_reg_rtx, frame_off, ptr_regno);
+  if (REGNO (frame_reg_rtx) == ptr_regno)
+    frame_off = 0;
+
+  if (insn && restoring_FPRs_inline)
     {
       if (cfa_restores)
        {
@@ -20915,16 +21244,11 @@ rs6000_emit_epilogue (int sibcall)
          int i;
          rtx sym;
 
-         if ((DEFAULT_ABI == ABI_V4 || flag_shrink_wrap)
-             && lr)
-           cfa_restores = alloc_reg_note (REG_CFA_RESTORE,
-                                          gen_rtx_REG (Pmode, LR_REGNO),
-                                          cfa_restores);
+         if (flag_shrink_wrap)
+           cfa_restores = add_crlr_cfa_restore (info, cfa_restores);
 
          sym = rs6000_savres_routine_sym (info,
-                                          /*savep=*/false,
-                                          /*gpr=*/false,
-                                          /*lr=*/lr);
+                                          SAVRES_FPR | (lr ? SAVRES_LR : 0));
          RTVEC_ELT (p, 2) = gen_rtx_USE (VOIDmode, sym);
          RTVEC_ELT (p, 3) = gen_rtx_USE (VOIDmode,
                                          gen_rtx_REG (Pmode,
@@ -20932,15 +21256,11 @@ rs6000_emit_epilogue (int sibcall)
                                                       ? 1 : 11));
          for (i = 0; i < 64 - info->first_fp_reg_save; i++)
            {
-             rtx addr, mem, reg;
+             rtx reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
 
-             addr = gen_rtx_PLUS (Pmode, sp_reg_rtx,
-                                  GEN_INT (info->fp_save_offset + 8 * i));
-             mem = gen_frame_mem (DFmode, addr);
-             reg = gen_rtx_REG (DFmode, info->first_fp_reg_save + i);
-
-             RTVEC_ELT (p, i + 4) = gen_rtx_SET (VOIDmode, reg, mem);
-             if (DEFAULT_ABI == ABI_V4 || flag_shrink_wrap)
+             RTVEC_ELT (p, i + 4)
+               = gen_frame_load (reg, sp_reg_rtx, info->fp_save_offset + 8 * i);
+             if (flag_shrink_wrap)
                cfa_restores = alloc_reg_note (REG_CFA_RESTORE, reg,
                                               cfa_restores);
            }
@@ -22054,7 +22374,7 @@ output_function_profiler (FILE *file, int labelno)
       fprintf (file, "\tmflr %s\n", reg_names[0]);
       if (NO_PROFILE_COUNTERS)
        {
-         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+         asm_fprintf (file, "\tstw %s,4(%s)\n",
                       reg_names[0], reg_names[1]);
        }
       else if (TARGET_SECURE_PLT && flag_pic)
@@ -22067,29 +22387,29 @@ output_function_profiler (FILE *file, int labelno)
            }
          else
            asm_fprintf (file, "\tbcl 20,31,1f\n1:\n");
-         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+         asm_fprintf (file, "\tstw %s,4(%s)\n",
                       reg_names[0], reg_names[1]);
          asm_fprintf (file, "\tmflr %s\n", reg_names[12]);
-         asm_fprintf (file, "\t{cau|addis} %s,%s,",
+         asm_fprintf (file, "\taddis %s,%s,",
                       reg_names[12], reg_names[12]);
          assemble_name (file, buf);
-         asm_fprintf (file, "-1b@ha\n\t{cal|la} %s,", reg_names[0]);
+         asm_fprintf (file, "-1b@ha\n\tla %s,", reg_names[0]);
          assemble_name (file, buf);
          asm_fprintf (file, "-1b@l(%s)\n", reg_names[12]);
        }
       else if (flag_pic == 1)
        {
          fputs ("\tbl _GLOBAL_OFFSET_TABLE_@local-4\n", file);
-         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+         asm_fprintf (file, "\tstw %s,4(%s)\n",
                       reg_names[0], reg_names[1]);
          asm_fprintf (file, "\tmflr %s\n", reg_names[12]);
-         asm_fprintf (file, "\t{l|lwz} %s,", reg_names[0]);
+         asm_fprintf (file, "\tlwz %s,", reg_names[0]);
          assemble_name (file, buf);
          asm_fprintf (file, "@got(%s)\n", reg_names[12]);
        }
       else if (flag_pic > 1)
        {
-         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+         asm_fprintf (file, "\tstw %s,4(%s)\n",
                       reg_names[0], reg_names[1]);
          /* Now, we need to get the address of the label.  */
          if (TARGET_LINK_STACK)
@@ -22110,19 +22430,19 @@ output_function_profiler (FILE *file, int labelno)
              fputs ("-.\n1:", file);
              asm_fprintf (file, "\tmflr %s\n", reg_names[11]);
            }
-         asm_fprintf (file, "\t{l|lwz} %s,0(%s)\n",
+         asm_fprintf (file, "\tlwz %s,0(%s)\n",
                       reg_names[0], reg_names[11]);
-         asm_fprintf (file, "\t{cax|add} %s,%s,%s\n",
+         asm_fprintf (file, "\tadd %s,%s,%s\n",
                       reg_names[0], reg_names[0], reg_names[11]);
        }
       else
        {
-         asm_fprintf (file, "\t{liu|lis} %s,", reg_names[12]);
+         asm_fprintf (file, "\tlis %s,", reg_names[12]);
          assemble_name (file, buf);
          fputs ("@ha\n", file);
-         asm_fprintf (file, "\t{st|stw} %s,4(%s)\n",
+         asm_fprintf (file, "\tstw %s,4(%s)\n",
                       reg_names[0], reg_names[1]);
-         asm_fprintf (file, "\t{cal|la} %s,", reg_names[0]);
+         asm_fprintf (file, "\tla %s,", reg_names[0]);
          assemble_name (file, buf);
          asm_fprintf (file, "@l(%s)\n", reg_names[12]);
        }
@@ -22264,7 +22584,7 @@ rs6000_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
                scheduling pass will not know about this latency since
                the mtctr instruction, which has the latency associated
                to it, will be generated by reload.  */
-            return TARGET_POWER ? 5 : 4;
+            return 4;
           case TYPE_BRANCH:
             /* Leave some extra cycles between a compare and its
                dependent branch, to inhibit expensive mispredicts.  */
@@ -22276,6 +22596,8 @@ rs6000_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
                  || rs6000_cpu_attr == CPU_PPC750
                  || rs6000_cpu_attr == CPU_PPC7400
                  || rs6000_cpu_attr == CPU_PPC7450
+                 || rs6000_cpu_attr == CPU_PPCE5500
+                 || rs6000_cpu_attr == CPU_PPCE6500
                  || rs6000_cpu_attr == CPU_POWER4
                  || rs6000_cpu_attr == CPU_POWER5
                 || rs6000_cpu_attr == CPU_POWER7
@@ -22645,49 +22967,78 @@ set_to_load_agen (rtx out_insn, rtx in_insn)
   return false;
 }
 
-/* The function returns true if the target storage location of
-   out_insn is adjacent to the target storage location of in_insn */
-/* Return 1 if memory locations are adjacent.  */
+/* Try to determine base/offset/size parts of the given MEM.
+   Return true if successful, false if all the values couldn't
+   be determined.
+
+   This function only looks for REG or REG+CONST address forms.
+   REG+REG address form will return false. */
 
 static bool
-adjacent_mem_locations (rtx insn1, rtx insn2)
+get_memref_parts (rtx mem, rtx *base, HOST_WIDE_INT *offset,
+                 HOST_WIDE_INT *size)
 {
+  rtx addr_rtx;
+  if MEM_SIZE_KNOWN_P (mem)
+    *size = MEM_SIZE (mem);
+  else
+    return false;
 
-  rtx a = get_store_dest (PATTERN (insn1));
-  rtx b = get_store_dest (PATTERN (insn2));
+  if (GET_CODE (XEXP (mem, 0)) == PRE_MODIFY)
+    addr_rtx = XEXP (XEXP (mem, 0), 1);
+  else
+    addr_rtx = (XEXP (mem, 0));
 
-  if ((GET_CODE (XEXP (a, 0)) == REG
-       || (GET_CODE (XEXP (a, 0)) == PLUS
-          && GET_CODE (XEXP (XEXP (a, 0), 1)) == CONST_INT))
-      && (GET_CODE (XEXP (b, 0)) == REG
-         || (GET_CODE (XEXP (b, 0)) == PLUS
-             && GET_CODE (XEXP (XEXP (b, 0), 1)) == CONST_INT)))
+  if (GET_CODE (addr_rtx) == REG)
+    {
+      *base = addr_rtx;
+      *offset = 0;
+    }
+  else if (GET_CODE (addr_rtx) == PLUS
+          && CONST_INT_P (XEXP (addr_rtx, 1)))
     {
-      HOST_WIDE_INT val0 = 0, val1 = 0, val_diff;
-      rtx reg0, reg1;
+      *base = XEXP (addr_rtx, 0);
+      *offset = INTVAL (XEXP (addr_rtx, 1));
+    }
+  else
+    return false;
 
-      if (GET_CODE (XEXP (a, 0)) == PLUS)
-        {
-         reg0 = XEXP (XEXP (a, 0), 0);
-         val0 = INTVAL (XEXP (XEXP (a, 0), 1));
-        }
-      else
-       reg0 = XEXP (a, 0);
+  return true;
+}
 
-      if (GET_CODE (XEXP (b, 0)) == PLUS)
-        {
-         reg1 = XEXP (XEXP (b, 0), 0);
-         val1 = INTVAL (XEXP (XEXP (b, 0), 1));
-        }
-      else
-       reg1 = XEXP (b, 0);
+/* The function returns true if the target storage location of
+   mem1 is adjacent to the target storage location of mem2 */
+/* Return 1 if memory locations are adjacent.  */
+
+static bool
+adjacent_mem_locations (rtx mem1, rtx mem2)
+{
+  rtx reg1, reg2;
+  HOST_WIDE_INT off1, size1, off2, size2;
 
-      val_diff = val1 - val0;
+  if (get_memref_parts (mem1, &reg1, &off1, &size1)
+      && get_memref_parts (mem2, &reg2, &off2, &size2))
+    return ((REGNO (reg1) == REGNO (reg2))
+           && ((off1 + size1 == off2)
+               || (off2 + size2 == off1)));
 
-      return ((REGNO (reg0) == REGNO (reg1))
-             && ((MEM_SIZE_KNOWN_P (a) && val_diff == MEM_SIZE (a))
-                 || (MEM_SIZE_KNOWN_P (b) && val_diff == -MEM_SIZE (b))));
-    }
+  return false;
+}
+
+/* This function returns true if it can be determined that the two MEM
+   locations overlap by at least 1 byte based on base reg/offset/size. */
+
+static bool
+mem_locations_overlap (rtx mem1, rtx mem2)
+{
+  rtx reg1, reg2;
+  HOST_WIDE_INT off1, size1, off2, size2;
+
+  if (get_memref_parts (mem1, &reg1, &off1, &size1)
+      && get_memref_parts (mem2, &reg2, &off2, &size2))
+    return ((REGNO (reg1) == REGNO (reg2))
+           && (((off1 <= off2) && (off1 + size1 > off2))
+               || ((off2 <= off1) && (off2 + size2 > off1))));
 
   return false;
 }
@@ -22701,6 +23052,7 @@ adjacent_mem_locations (rtx insn1, rtx insn2)
 static int
 rs6000_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
 {
+  rtx load_mem, str_mem;
   /* On machines (like the 750) which have asymmetric integer units,
      where one integer unit can do multiply and divides and the other
      can't, reduce the priority of multiply/divide so it is scheduled
@@ -22752,8 +23104,8 @@ rs6000_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
     }
 
   if (rs6000_cpu == PROCESSOR_POWER6
-      && ((load_store_pendulum == -2 && is_load_insn (insn))
-          || (load_store_pendulum == 2 && is_store_insn (insn))))
+      && ((load_store_pendulum == -2 && is_load_insn (insn, &load_mem))
+          || (load_store_pendulum == 2 && is_store_insn (insn, &str_mem))))
     /* Attach highest priority to insn if the scheduler has just issued two
        stores and this instruction is a load, or two loads and this instruction
        is a store. Power6 wants loads and stores scheduled alternately
@@ -22805,7 +23157,6 @@ rs6000_issue_rate (void)
     return 1;
 
   switch (rs6000_cpu_attr) {
-  case CPU_RIOS1:  /* ? */
   case CPU_RS64A:
   case CPU_PPC601: /* ? */
   case CPU_PPC7450:
@@ -22815,14 +23166,16 @@ rs6000_issue_rate (void)
   case CPU_PPC750:
   case CPU_PPC7400:
   case CPU_PPC8540:
+  case CPU_PPC8548:
   case CPU_CELL:
   case CPU_PPCE300C2:
   case CPU_PPCE300C3:
   case CPU_PPCE500MC:
   case CPU_PPCE500MC64:
+  case CPU_PPCE5500:
+  case CPU_PPCE6500:
   case CPU_TITAN:
     return 2;
-  case CPU_RIOS2:
   case CPU_PPC476:
   case CPU_PPC604:
   case CPU_PPC604E:
@@ -22845,11 +23198,18 @@ rs6000_issue_rate (void)
 static int
 rs6000_use_sched_lookahead (void)
 {
-  if (rs6000_cpu_attr == CPU_PPC8540)
-    return 4;
-  if (rs6000_cpu_attr == CPU_CELL)
-    return (reload_completed ? 8 : 0);
-  return 0;
+  switch (rs6000_cpu_attr)
+    {
+    case CPU_PPC8540:
+    case CPU_PPC8548:
+      return 4;
+
+    case CPU_CELL:
+      return (reload_completed ? 8 : 0);
+
+    default:
+      return 0;
+    }
 }
 
 /* We are choosing insn from the ready queue.  Return nonzero if INSN can be chosen.  */
@@ -22870,55 +23230,63 @@ rs6000_use_sched_lookahead_guard (rtx insn)
   return 1;
 }
 
-/* Determine is PAT refers to memory.  */
+/* Determine if PAT refers to memory. If so, set MEM_REF to the MEM rtx
+   and return true.  */
 
 static bool
-is_mem_ref (rtx pat)
+find_mem_ref (rtx pat, rtx *mem_ref)
 {
   const char * fmt;
   int i, j;
-  bool ret = false;
 
   /* stack_tie does not produce any real memory traffic.  */
-  if (GET_CODE (pat) == UNSPEC
-      && XINT (pat, 1) == UNSPEC_TIE)
+  if (tie_operand (pat, VOIDmode))
     return false;
 
   if (GET_CODE (pat) == MEM)
-    return true;
+    {
+      *mem_ref = pat;
+      return true;
+    }
 
   /* Recursively process the pattern.  */
   fmt = GET_RTX_FORMAT (GET_CODE (pat));
 
-  for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0 && !ret; i--)
+  for (i = GET_RTX_LENGTH (GET_CODE (pat)) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
-       ret |= is_mem_ref (XEXP (pat, i));
+       {
+         if (find_mem_ref (XEXP (pat, i), mem_ref))
+           return true;
+       }
       else if (fmt[i] == 'E')
        for (j = XVECLEN (pat, i) - 1; j >= 0; j--)
-         ret |= is_mem_ref (XVECEXP (pat, i, j));
+         {
+           if (find_mem_ref (XVECEXP (pat, i, j), mem_ref))
+             return true;
+         }
     }
 
-  return ret;
+  return false;
 }
 
 /* Determine if PAT is a PATTERN of a load insn.  */
 
 static bool
-is_load_insn1 (rtx pat)
+is_load_insn1 (rtx pat, rtx *load_mem)
 {
   if (!pat || pat == NULL_RTX)
     return false;
 
   if (GET_CODE (pat) == SET)
-    return is_mem_ref (SET_SRC (pat));
+    return find_mem_ref (SET_SRC (pat), load_mem);
 
   if (GET_CODE (pat) == PARALLEL)
     {
       int i;
 
       for (i = 0; i < XVECLEN (pat, 0); i++)
-       if (is_load_insn1 (XVECEXP (pat, 0, i)))
+       if (is_load_insn1 (XVECEXP (pat, 0, i), load_mem))
          return true;
     }
 
@@ -22928,7 +23296,7 @@ is_load_insn1 (rtx pat)
 /* Determine if INSN loads from memory.  */
 
 static bool
-is_load_insn (rtx insn)
+is_load_insn (rtx insn, rtx *load_mem)
 {
   if (!insn || !INSN_P (insn))
     return false;
@@ -22936,26 +23304,26 @@ is_load_insn (rtx insn)
   if (GET_CODE (insn) == CALL_INSN)
     return false;
 
-  return is_load_insn1 (PATTERN (insn));
+  return is_load_insn1 (PATTERN (insn), load_mem);
 }
 
 /* Determine if PAT is a PATTERN of a store insn.  */
 
 static bool
-is_store_insn1 (rtx pat)
+is_store_insn1 (rtx pat, rtx *str_mem)
 {
   if (!pat || pat == NULL_RTX)
     return false;
 
   if (GET_CODE (pat) == SET)
-    return is_mem_ref (SET_DEST (pat));
+    return find_mem_ref (SET_DEST (pat), str_mem);
 
   if (GET_CODE (pat) == PARALLEL)
     {
       int i;
 
       for (i = 0; i < XVECLEN (pat, 0); i++)
-       if (is_store_insn1 (XVECEXP (pat, 0, i)))
+       if (is_store_insn1 (XVECEXP (pat, 0, i), str_mem))
          return true;
     }
 
@@ -22965,38 +23333,12 @@ is_store_insn1 (rtx pat)
 /* Determine if INSN stores to memory.  */
 
 static bool
-is_store_insn (rtx insn)
+is_store_insn (rtx insn, rtx *str_mem)
 {
   if (!insn || !INSN_P (insn))
     return false;
 
-  return is_store_insn1 (PATTERN (insn));
-}
-
-/* Return the dest of a store insn.  */
-
-static rtx
-get_store_dest (rtx pat)
-{
-  gcc_assert (is_store_insn1 (pat));
-
-  if (GET_CODE (pat) == SET)
-    return SET_DEST (pat);
-  else if (GET_CODE (pat) == PARALLEL)
-    {
-      int i;
-
-      for (i = 0; i < XVECLEN (pat, 0); i++)
-       {
-         rtx inner_pat = XVECEXP (pat, 0, i);
-         if (GET_CODE (inner_pat) == SET
-             && is_mem_ref (SET_DEST (inner_pat)))
-           return inner_pat;
-       }
-    }
-  /* We shouldn't get here, because we should have either a simple
-     store insn or a store with update which are covered above.  */
-  gcc_unreachable();
+  return is_store_insn1 (PATTERN (insn), str_mem);
 }
 
 /* Returns whether the dependence between INSN and NEXT is considered
@@ -23007,6 +23349,7 @@ rs6000_is_costly_dependence (dep_t dep, int cost, int distance)
 {
   rtx insn;
   rtx next;
+  rtx load_mem, str_mem;
 
   /* If the flag is not enabled - no dependence is considered costly;
      allow all dependent insns in the same group.
@@ -23024,15 +23367,16 @@ rs6000_is_costly_dependence (dep_t dep, int cost, int distance)
   next = DEP_CON (dep);
 
   if (rs6000_sched_costly_dep == store_to_load_dep_costly
-      && is_load_insn (next)
-      && is_store_insn (insn))
+      && is_load_insn (next, &load_mem)
+      && is_store_insn (insn, &str_mem))
     /* Prevent load after store in the same group.  */
     return true;
 
   if (rs6000_sched_costly_dep == true_store_to_load_dep_costly
-      && is_load_insn (next)
-      && is_store_insn (insn)
-      && DEP_TYPE (dep) == REG_DEP_TRUE)
+      && is_load_insn (next, &load_mem)
+      && is_store_insn (insn, &str_mem)
+      && DEP_TYPE (dep) == REG_DEP_TRUE
+      && mem_locations_overlap(str_mem, load_mem))
      /* Prevent load after store in the same group if it is a true
        dependence.  */
      return true;
@@ -23159,12 +23503,12 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
     {
       int pos;
       int i;
-      rtx tmp;
+      rtx tmp, load_mem, str_mem;
 
-      if (is_store_insn (last_scheduled_insn))
+      if (is_store_insn (last_scheduled_insn, &str_mem))
         /* Issuing a store, swing the load_store_pendulum to the left */
         load_store_pendulum--;
-      else if (is_load_insn (last_scheduled_insn))
+      else if (is_load_insn (last_scheduled_insn, &load_mem))
         /* Issuing a load, swing the load_store_pendulum to the right */
         load_store_pendulum++;
       else
@@ -23183,7 +23527,7 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
 
           while (pos >= 0)
             {
-              if (is_load_insn (ready[pos]))
+              if (is_load_insn (ready[pos], &load_mem))
                 {
                   /* Found a load.  Move it to the head of the ready list,
                      and adjust it's priority so that it is more likely to
@@ -23209,7 +23553,7 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
 
           while (pos >= 0)
             {
-              if (is_load_insn (ready[pos])
+              if (is_load_insn (ready[pos], &load_mem)
                   && !sel_sched_p ()
                  && INSN_PRIORITY_KNOWN (ready[pos]))
                 {
@@ -23236,15 +23580,16 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
 
           while (pos >= 0)
             {
-              if (is_store_insn (ready[pos]))
+              if (is_store_insn (ready[pos], &str_mem))
                 {
+                 rtx str_mem2;
                   /* Maintain the index of the first store found on the
                      list */
                   if (first_store_pos == -1)
                     first_store_pos = pos;
 
-                  if (is_store_insn (last_scheduled_insn)
-                      && adjacent_mem_locations (last_scheduled_insn,ready[pos]))
+                  if (is_store_insn (last_scheduled_insn, &str_mem2)
+                      && adjacent_mem_locations (str_mem, str_mem2))
                     {
                       /* Found an adjacent store.  Move it to the head of the
                          ready list, and adjust it's priority so that it is
@@ -23288,7 +23633,7 @@ rs6000_sched_reorder2 (FILE *dump, int sched_verbose, rtx *ready,
 
           while (pos >= 0)
             {
-              if (is_store_insn (ready[pos])
+              if (is_store_insn (ready[pos], &str_mem)
                   && !sel_sched_p ()
                  && INSN_PRIORITY_KNOWN (ready[pos]))
                 {
@@ -23572,7 +23917,7 @@ is_costly_group (rtx *group_insns, rtx next_insn)
       if (!insn)
        continue;
 
-      FOR_EACH_DEP (insn, SD_LIST_FORW, sd_it, dep)
+      FOR_EACH_DEP (insn, SD_LIST_RES_FORW, sd_it, dep)
        {
          rtx next = DEP_CON (dep);
 
@@ -23636,12 +23981,20 @@ force_new_group (int sched_verbose, FILE *dump, rtx *group_insns,
       if (can_issue_more && !is_branch_slot_insn (next_insn))
        can_issue_more--;
 
-      while (can_issue_more > 0)
+      /* Power6 and Power7 have special group ending nop. */
+      if (rs6000_cpu_attr == CPU_POWER6 || rs6000_cpu_attr == CPU_POWER7)
        {
-         nop = gen_nop ();
+         nop = gen_group_ending_nop ();
          emit_insn_before (nop, next_insn);
-         can_issue_more--;
+         can_issue_more = 0;
        }
+      else
+       while (can_issue_more > 0)
+         {
+           nop = gen_nop ();
+           emit_insn_before (nop, next_insn);
+           can_issue_more--;
+         }
 
       *group_end = true;
       return 0;
@@ -24032,7 +24385,8 @@ rs6000_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
        rtx fnmem, fn_reg, toc_reg;
 
        if (!TARGET_POINTERS_TO_NESTED_FUNCTIONS)
-         error ("-mno-r11 must not be used if you have trampolines");
+         error ("You cannot take the address of a nested function if you use "
+                "the -mno-pointers-to-nested-functions option.");
 
        fnmem = gen_const_mem (Pmode, force_reg (Pmode, fnaddr));
        fn_reg = gen_reg_rtx (Pmode);
@@ -24416,6 +24770,7 @@ rs6000_elf_select_rtx_section (enum machine_mode mode, rtx x,
    function descriptor name.  This saves a lot of overriding code to
    read the prefixes.  */
 
+static void rs6000_elf_encode_section_info (tree, rtx, int) ATTRIBUTE_UNUSED;
 static void
 rs6000_elf_encode_section_info (tree decl, rtx rtl, int first)
 {
@@ -24572,7 +24927,7 @@ macho_branch_islands (void)
 
   while (!VEC_empty (branch_island, branch_islands))
     {
-      branch_island *bi = VEC_last (branch_island, branch_islands);
+      branch_island *bi = &VEC_last (branch_island, branch_islands);
       const char *label = IDENTIFIER_POINTER (bi->label_name);
       const char *name = IDENTIFIER_POINTER (bi->function_name);
       char name_buf[512];
@@ -24836,7 +25191,7 @@ rs6000_machopic_legitimize_pic_address (rtx orig, enum machine_mode mode,
       if (GET_CODE (offset) == CONST_INT)
        {
          if (SMALL_INT (offset))
-           return plus_constant (base, INTVAL (offset));
+           return plus_constant (Pmode, base, INTVAL (offset));
          else if (! reload_in_progress && ! reload_completed)
            offset = force_reg (Pmode, offset);
          else
@@ -24928,6 +25283,7 @@ rs6000_elf_reloc_rw_mask (void)
    This differs from default_named_section_asm_out_constructor in
    that we have special handling for -mrelocatable.  */
 
+static void rs6000_elf_asm_out_constructor (rtx, int) ATTRIBUTE_UNUSED;
 static void
 rs6000_elf_asm_out_constructor (rtx symbol, int priority)
 {
@@ -24957,6 +25313,7 @@ rs6000_elf_asm_out_constructor (rtx symbol, int priority)
     assemble_integer (symbol, POINTER_SIZE / BITS_PER_UNIT, POINTER_SIZE, 1);
 }
 
+static void rs6000_elf_asm_out_destructor (rtx, int) ATTRIBUTE_UNUSED;
 static void
 rs6000_elf_asm_out_destructor (rtx symbol, int priority)
 {
@@ -25062,6 +25419,7 @@ rs6000_elf_declare_function_name (FILE *file, const char *name, tree decl)
   ASM_OUTPUT_LABEL (file, name);
 }
 
+static void rs6000_elf_file_end (void) ATTRIBUTE_UNUSED;
 static void
 rs6000_elf_file_end (void)
 {
@@ -25083,7 +25441,7 @@ rs6000_elf_file_end (void)
                 aix_struct_return ? 2 : 1);
     }
 #endif
-#ifdef POWERPC_LINUX
+#if defined (POWERPC_LINUX) || defined (POWERPC_FREEBSD)
   if (TARGET_32BIT)
     file_end_indicate_exec_stack ();
 #endif
@@ -25713,7 +26071,8 @@ rs6000_debug_rtx_costs (rtx x, int code, int outer_code, int opno, int *total,
 /* Debug form of ADDRESS_COST that is selected if -mdebug=cost.  */
 
 static int
-rs6000_debug_address_cost (rtx x, bool speed)
+rs6000_debug_address_cost (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED,
+                          addr_space_t as ATTRIBUTE_UNUSED, bool speed)
 {
   int ret = TARGET_ADDRESS_COST (x, speed);
 
@@ -26607,7 +26966,7 @@ rs6000_complex_function_value (enum machine_mode mode)
    On RS/6000 an integer value is in r3 and a floating-point value is in
    fp1, unless -msoft-float.  */
 
-rtx
+static rtx
 rs6000_function_value (const_tree valtype,
                       const_tree fn_decl_or_type ATTRIBUTE_UNUSED,
                       bool outgoing ATTRIBUTE_UNUSED)
@@ -26754,7 +27113,7 @@ rs6000_libcall_value (enum machine_mode mode)
    We need r30 if -mminimal-toc was specified, and there are constant pool
    references.  */
 
-bool
+static bool
 rs6000_can_eliminate (const int from, const int to)
 {
   return (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM
@@ -26868,8 +27227,6 @@ rs6000_dbx_register_number (unsigned int regno)
 {
   if (regno <= 63 || write_symbols != DWARF2_DEBUG)
     return regno;
-  if (regno == MQ_REGNO)
-    return 100;
   if (regno == LR_REGNO)
     return 108;
   if (regno == CTR_REGNO)
@@ -27038,9 +27395,6 @@ static struct rs6000_opt_mask const rs6000_opt_masks[] =
 #ifdef MASK_STRICT_ALIGN
   { "strict-align",    MASK_STRICT_ALIGN,      false, false },
 #endif
-  { "power",           MASK_POWER,             false, false },
-  { "power2",          MASK_POWER2,            false, false },
-  { "powerpc",         MASK_POWERPC,           false, false },
   { "soft-float",      MASK_SOFT_FLOAT,        false, false },
   { "string",          MASK_STRING,            false, false },
 };
@@ -27057,7 +27411,6 @@ static struct rs6000_opt_mask const rs6000_builtin_mask_names[] =
   { "frsqrte",          RS6000_BTM_FRSQRTE,    false, false },
   { "frsqrtes",                 RS6000_BTM_FRSQRTES,   false, false },
   { "popcntd",          RS6000_BTM_POPCNTD,    false, false },
-  { "powerpc",          RS6000_BTM_POWERPC,    false, false },
   { "cell",             RS6000_BTM_CELL,       false, false },
 };
 
@@ -27636,14 +27989,14 @@ rs6000_allocate_stack_temp (enum machine_mode mode,
                            bool offsettable_p,
                            bool reg_reg_p)
 {
-  rtx stack = assign_stack_temp (mode, GET_MODE_SIZE (mode), 0);
+  rtx stack = assign_stack_temp (mode, GET_MODE_SIZE (mode));
   rtx addr = XEXP (stack, 0);
   int strict_p = (reload_in_progress || reload_completed);
 
   if (!legitimate_indirect_address_p (addr, strict_p))
     {
       if (offsettable_p
-         && !rs6000_legitimate_offset_address_p (mode, addr, strict_p))
+         && !rs6000_legitimate_offset_address_p (mode, addr, strict_p, true))
        stack = replace_equiv_address (stack, copy_addr_to_reg (addr));
 
       else if (reg_reg_p && !legitimate_indexed_address_p (addr, strict_p))
@@ -27881,6 +28234,7 @@ get_ppc476_thunk_name (char name[32])
 /* This function emits the simple thunk routine that is used to preserve
    the link stack on the 476 cpu.  */
 
+static void rs6000_code_end (void) ATTRIBUTE_UNUSED;
 static void
 rs6000_code_end (void)
 {