rs6000.c (rs6000_emit_move): #if 0 splitting slow, unaligned loads and stores while...
[platform/upstream/gcc.git] / gcc / config / rs6000 / rs6000.c
index 19f1b28..4969cd1 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines used for code generation on IBM RS/6000.
    Copyright (C) 1991, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 
-   2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+   2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
    Contributed by Richard Kenner (kenner@vlsi1.ultra.nyu.edu)
 
    This file is part of GCC.
 #define min(A,B)       ((A) < (B) ? (A) : (B))
 #define max(A,B)       ((A) > (B) ? (A) : (B))
 
+/* Structure used to define the rs6000 stack */
+typedef struct rs6000_stack {
+  int first_gp_reg_save;       /* first callee saved GP register used */
+  int first_fp_reg_save;       /* first callee saved FP register used */
+  int first_altivec_reg_save;  /* first callee saved AltiVec register used */
+  int lr_save_p;               /* true if the link reg needs to be saved */
+  int cr_save_p;               /* true if the CR reg needs to be saved */
+  unsigned int vrsave_mask;    /* mask of vec registers to save */
+  int toc_save_p;              /* true if the TOC needs to be saved */
+  int push_p;                  /* true if we need to allocate stack space */
+  int calls_p;                 /* true if the function makes any calls */
+  enum rs6000_abi abi;         /* which ABI to use */
+  int gp_save_offset;          /* offset to save GP regs from initial SP */
+  int fp_save_offset;          /* offset to save FP regs from initial SP */
+  int altivec_save_offset;     /* offset to save AltiVec regs from initial SP */
+  int lr_save_offset;          /* offset to save LR from initial SP */
+  int cr_save_offset;          /* offset to save CR from initial SP */
+  int vrsave_save_offset;      /* offset to save VRSAVE from initial SP */
+  int spe_gp_save_offset;      /* offset to save spe 64-bit gprs  */
+  int toc_save_offset;         /* offset to save the TOC pointer */
+  int varargs_save_offset;     /* offset to save the varargs registers */
+  int ehrd_offset;             /* offset to EH return data */
+  int reg_size;                        /* register size (4 or 8) */
+  int varargs_size;            /* size to hold V.4 args passed in regs */
+  HOST_WIDE_INT vars_size;     /* variable save area size */
+  int parm_size;               /* outgoing parameter size */
+  int save_size;               /* save area size */
+  int fixed_size;              /* fixed size of stack frame */
+  int gp_size;                 /* size of saved GP registers */
+  int fp_size;                 /* size of saved FP registers */
+  int altivec_size;            /* size of saved AltiVec registers */
+  int cr_size;                 /* size to hold CR if not in save_size */
+  int lr_size;                 /* size to hold LR if not in save_size */
+  int vrsave_size;             /* size to hold VRSAVE if not in save_size */
+  int altivec_padding_size;    /* size of altivec alignment padding if
+                                  not in save_size */
+  int spe_gp_size;             /* size of 64-bit GPR save size for SPE */
+  int spe_padding_size;
+  int toc_size;                        /* size to hold TOC if not in save_size */
+  HOST_WIDE_INT total_size;    /* total bytes allocated for stack */
+  int spe_64bit_regs_used;
+} rs6000_stack_t;
+
 /* Target cpu type */
 
 enum processor_type rs6000_cpu;
@@ -90,6 +133,10 @@ int rs6000_sched_restricted_insns_priority;
 const char *rs6000_sched_costly_dep_str;
 enum rs6000_dependence_cost rs6000_sched_costly_dep;
 
+/* Support for -minsert-sched-nops option.  */
+const char *rs6000_sched_insert_nops_str;
+enum rs6000_nop_insertion rs6000_sched_insert_nops;
+
 /* Size of long double */
 const char *rs6000_long_double_size_string;
 int rs6000_long_double_type_size;
@@ -183,7 +230,7 @@ int toc_initialized;
 char toc_label_name[10];
 
 /* Alias set for saves and restores from the rs6000 stack.  */
-static int rs6000_sr_alias_set;
+static GTY(()) int rs6000_sr_alias_set;
 
 /* Call distance, overridden by -mlongcall and #pragma longcall(1).
    The only place that looks at this is rs6000_set_default_type_attributes;
@@ -192,8 +239,8 @@ static int rs6000_sr_alias_set;
 int rs6000_default_long_calls;
 const char *rs6000_longcall_switch;
 
-/* Control alignment for fields within structures. */
-/* String from -malign-XXXXX. */
+/* Control alignment for fields within structures.  */
+/* String from -malign-XXXXX.  */
 const char *rs6000_alignment_string;
 int rs6000_alignment_flags;
 
@@ -218,7 +265,7 @@ static void rs6000_frame_related (rtx, rtx, HOST_WIDE_INT, rtx, rtx);
 static rtx spe_synthesize_frame_save (rtx);
 static bool spe_func_has_64bit_regs_p (void);
 static void emit_frame_save (rtx, rtx, enum machine_mode, unsigned int,
-                            int, int);
+                            int, HOST_WIDE_INT);
 static rtx gen_frame_mem_offset (enum machine_mode, rtx, int);
 static void rs6000_emit_allocate_stack (HOST_WIDE_INT, int);
 static unsigned rs6000_hash_constant (rtx);
@@ -231,6 +278,7 @@ static bool legitimate_small_data_p (enum machine_mode, rtx);
 static bool legitimate_offset_address_p (enum machine_mode, rtx, int);
 static bool legitimate_indexed_address_p (rtx, int);
 static bool legitimate_indirect_address_p (rtx, int);
+static bool macho_lo_sum_memory_operand (rtx x, enum machine_mode mode);
 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);
@@ -279,10 +327,20 @@ static int rs6000_use_dfa_pipeline_interface (void);
 static int rs6000_variable_issue (FILE *, int, rtx, int);
 static bool rs6000_rtx_costs (rtx, int, int, int *);
 static int rs6000_adjust_cost (rtx, rtx, rtx, int);
+static bool is_microcoded_insn (rtx);
 static int is_dispatch_slot_restricted (rtx);
+static bool is_cracked_insn (rtx);
+static bool is_branch_slot_insn (rtx);
 static int rs6000_adjust_priority (rtx, int);
 static int rs6000_issue_rate (void);
 static bool rs6000_is_costly_dependence (rtx, rtx, rtx, int, int);
+static rtx get_next_active_insn (rtx, rtx);
+static bool insn_terminates_group_p (rtx , enum group_termination);
+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_use_sched_lookahead (void);
 
 static void rs6000_init_builtins (void);
@@ -302,6 +360,8 @@ static rtx spe_expand_builtin (tree, rtx, bool *);
 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 *);
@@ -310,6 +370,7 @@ 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, 
                                            const char *, tree, rtx);
+static rtx altivec_expand_lv_builtin (enum insn_code, tree, rtx);
 static rtx altivec_expand_stv_builtin (enum insn_code, tree);
 static void rs6000_parse_abi_options (void);
 static void rs6000_parse_alignment_option (void);
@@ -332,9 +393,20 @@ 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 (CUMULATIVE_ARGS *,
                                    enum machine_mode, tree);
+static rtx rs6000_mixed_function_arg (CUMULATIVE_ARGS *,
+                                     enum machine_mode, tree, int);
+static void rs6000_move_block_from_reg(int regno, rtx x, int nregs);
 static void setup_incoming_varargs (CUMULATIVE_ARGS *,
                                    enum machine_mode, tree,
                                    int *, int);
+#if TARGET_MACHO
+static void macho_branch_islands (void);
+static void add_compiler_branch_island (tree, tree, int);
+static int no_previous_def (tree function_name);
+static tree get_prev_label (tree function_name);
+#endif
+
+static tree rs6000_build_builtin_va_list (void);
 
 /* Hash table stuff for keeping track of TOC entries.  */
 
@@ -476,6 +548,8 @@ static const char alt_reg_names[][8] =
 #define TARGET_SCHED_ADJUST_PRIORITY rs6000_adjust_priority
 #undef TARGET_SCHED_IS_COSTLY_DEPENDENCE      
 #define TARGET_SCHED_IS_COSTLY_DEPENDENCE rs6000_is_costly_dependence
+#undef TARGET_SCHED_FINISH
+#define TARGET_SCHED_FINISH rs6000_sched_finish
 
 #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD rs6000_use_sched_lookahead
@@ -537,6 +611,9 @@ static const char alt_reg_names[][8] =
 #undef TARGET_PRETEND_OUTGOING_VARARGS_NAMED
 #define TARGET_PRETEND_OUTGOING_VARARGS_NAMED hook_bool_CUMULATIVE_ARGS_true
 
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST rs6000_build_builtin_va_list
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Override command line options.  Mostly we process the processor
@@ -547,138 +624,83 @@ rs6000_override_options (const char *default_cpu)
 {
   size_t i, j;
   struct rs6000_cpu_select *ptr;
+  int set_masks;
 
-  /* Simplify the entries below by making a mask for any POWER
-     variant and any PowerPC variant.  */
+  /* Simplifications for entries below.  */
 
-#define POWER_MASKS (MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING)
-#define POWERPC_MASKS (MASK_POWERPC | MASK_PPC_GPOPT \
-                      | MASK_PPC_GFXOPT | MASK_POWERPC64)
-#define POWERPC_OPT_MASKS (MASK_PPC_GPOPT | MASK_PPC_GFXOPT)
+  enum {
+    POWERPC_BASE_MASK = MASK_POWERPC | MASK_NEW_MNEMONICS,
+    POWERPC_7400_MASK = POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_ALTIVEC
+  };
 
+  /* This table occasionally claims that a processor does not support
+     a particular feature even though it does, but the feature is slower
+     than the alternative.  Thus, it shouldn't be relied on as a
+     complete description of the processor's support.  
+
+     Please keep this list in order, and don't forget to update the
+     documentation in invoke.texi when adding a new processor or
+     flag.  */
   static struct ptt
     {
       const char *const name;          /* Canonical processor name.  */
       const enum processor_type processor; /* Processor type enum value.  */
       const int target_enable; /* Target flags to enable.  */
-      const int target_disable;        /* Target flags to disable.  */
     } const processor_target_table[]
-      = {{"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_MASKS},
-        {"power", PROCESSOR_POWER,
-           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
-           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"power2", PROCESSOR_POWER,
-           MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
-           POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"power3", PROCESSOR_PPC630,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS},
-        {"power4", PROCESSOR_POWER4,
-            MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-            POWER_MASKS},
-        {"powerpc", PROCESSOR_POWERPC,
-           MASK_POWERPC | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"powerpc64", PROCESSOR_POWERPC64,
-           MASK_POWERPC | MASK_POWERPC64 | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS},
-        {"rios", PROCESSOR_RIOS1,
-           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
-           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"rios1", PROCESSOR_RIOS1,
-           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
-           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"rsc", PROCESSOR_PPC601,
-           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
-           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"rsc1", PROCESSOR_PPC601,
-           MASK_POWER | MASK_MULTIPLE | MASK_STRING,
-           MASK_POWER2 | POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"rios2", PROCESSOR_RIOS2,
-           MASK_POWER | MASK_MULTIPLE | MASK_STRING | MASK_POWER2,
-           POWERPC_MASKS | MASK_NEW_MNEMONICS},
-        {"rs64a", PROCESSOR_RS64A,
-           MASK_POWERPC | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS},
-        {"401", PROCESSOR_PPC403,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+      = {{"401", PROCESSOR_PPC403, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
         {"403", PROCESSOR_PPC403,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS | MASK_STRICT_ALIGN,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"405", PROCESSOR_PPC405,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"405fp", PROCESSOR_PPC405,
-           MASK_POWERPC | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"440", PROCESSOR_PPC440,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"440fp", PROCESSOR_PPC440,
-           MASK_POWERPC | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"505", PROCESSOR_MPCCORE,
-           MASK_POWERPC | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+         POWERPC_BASE_MASK | MASK_SOFT_FLOAT | MASK_STRICT_ALIGN},
+        {"405", PROCESSOR_PPC405, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"405fp", PROCESSOR_PPC405, POWERPC_BASE_MASK},
+        {"440", PROCESSOR_PPC440, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"440fp", PROCESSOR_PPC440, POWERPC_BASE_MASK},
+        {"505", PROCESSOR_MPCCORE, POWERPC_BASE_MASK},
         {"601", PROCESSOR_PPC601,
-           MASK_POWER | MASK_POWERPC | MASK_NEW_MNEMONICS | MASK_MULTIPLE | MASK_STRING,
-           MASK_POWER2 | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"602", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"603", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"603e", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"ec603e", PROCESSOR_PPC603,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"604", PROCESSOR_PPC604,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"604e", PROCESSOR_PPC604e,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
+         MASK_POWER | POWERPC_BASE_MASK | MASK_MULTIPLE | MASK_STRING},
+        {"602", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"603", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"604", PROCESSOR_PPC604, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"604e", PROCESSOR_PPC604e, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
         {"620", PROCESSOR_PPC620,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS},
+         POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
         {"630", PROCESSOR_PPC630,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS},
-        {"740", PROCESSOR_PPC750,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"750", PROCESSOR_PPC750,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"7400", PROCESSOR_PPC7400,
-            MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-            POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"7450", PROCESSOR_PPC7450,
-            MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-            POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"8540", PROCESSOR_PPC8540,
-           MASK_POWERPC | MASK_PPC_GFXOPT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | MASK_PPC_GPOPT | MASK_POWERPC64},
-        {"801", PROCESSOR_MPCCORE,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"821", PROCESSOR_MPCCORE,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"823", PROCESSOR_MPCCORE,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
-        {"860", PROCESSOR_MPCCORE,
-           MASK_POWERPC | MASK_SOFT_FLOAT | MASK_NEW_MNEMONICS,
-           POWER_MASKS | POWERPC_OPT_MASKS | MASK_POWERPC64},
+         POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+        {"740", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"7400", PROCESSOR_PPC7400, POWERPC_7400_MASK},
+        {"7450", PROCESSOR_PPC7450, POWERPC_7400_MASK},
+        {"750", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"801", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"821", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"823", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"8540", PROCESSOR_PPC8540, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"860", PROCESSOR_MPCCORE, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
         {"970", PROCESSOR_POWER4,
-           MASK_POWERPC | POWERPC_OPT_MASKS | MASK_NEW_MNEMONICS,
-           POWER_MASKS}};
+         POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
+        {"common", PROCESSOR_COMMON, MASK_NEW_MNEMONICS},
+        {"ec603e", PROCESSOR_PPC603, POWERPC_BASE_MASK | MASK_SOFT_FLOAT},
+        {"G3", PROCESSOR_PPC750, POWERPC_BASE_MASK | MASK_PPC_GFXOPT},
+        {"G4",  PROCESSOR_PPC7450, POWERPC_7400_MASK},
+        {"G5", PROCESSOR_POWER4,
+         POWERPC_7400_MASK | MASK_PPC_GPOPT | MASK_MFCRF | MASK_POWERPC64},
+        {"power", PROCESSOR_POWER, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+        {"power2", PROCESSOR_POWER,
+         MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING},
+        {"power3", PROCESSOR_PPC630,
+         POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+        {"power4", PROCESSOR_POWER4,
+         POWERPC_BASE_MASK | MASK_PPC_GFXOPT | MASK_POWERPC64},
+        {"powerpc", PROCESSOR_POWERPC, POWERPC_BASE_MASK},
+        {"powerpc64", PROCESSOR_POWERPC64,
+         POWERPC_BASE_MASK | MASK_POWERPC64},
+        {"rios", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+        {"rios1", PROCESSOR_RIOS1, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+        {"rios2", PROCESSOR_RIOS2,
+         MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING},
+        {"rsc", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+        {"rsc1", PROCESSOR_PPC601, MASK_POWER | MASK_MULTIPLE | MASK_STRING},
+        {"rs64a", PROCESSOR_RS64A, POWERPC_BASE_MASK | MASK_POWERPC64},
+      };
 
   const size_t ptt_size = ARRAY_SIZE (processor_target_table);
 
@@ -687,6 +709,28 @@ rs6000_override_options (const char *default_cpu)
   /* Save current -mstring/-mno-string status.  */
   int string = TARGET_STRING;
 
+  /* Some OSs don't support saving the high part of 64-bit registers on
+     context switch.  Other OSs don't support saving Altivec registers.
+     On those OSs, we don't touch the MASK_POWERPC64 or MASK_ALTIVEC
+     settings; if the user wants either, the user must explicitly specify
+     them and we won't interfere with the user's specification.  */
+
+  enum {
+    POWER_MASKS = MASK_POWER | MASK_POWER2 | MASK_MULTIPLE | MASK_STRING,
+    POWERPC_MASKS = (POWERPC_BASE_MASK | MASK_PPC_GPOPT 
+                    | MASK_PPC_GFXOPT | MASK_POWERPC64 | MASK_ALTIVEC
+                    | MASK_MFCRF)
+  };
+ set_masks = POWER_MASKS | POWERPC_MASKS | MASK_SOFT_FLOAT;
+#ifdef OS_MISSING_POWERPC64
+  if (OS_MISSING_POWERPC64)
+    set_masks &= ~MASK_POWERPC64;
+#endif
+#ifdef OS_MISSING_ALTIVEC
+  if (OS_MISSING_ALTIVEC)
+    set_masks &= ~MASK_ALTIVEC;
+#endif
+
   /* Identify the processor type.  */
   rs6000_select[0].string = default_cpu;
   rs6000_cpu = TARGET_POWERPC64 ? PROCESSOR_DEFAULT64 : PROCESSOR_DEFAULT;
@@ -704,8 +748,9 @@ rs6000_override_options (const char *default_cpu)
 
                if (ptr->set_arch_p)
                  {
-                   target_flags |= processor_target_table[j].target_enable;
-                   target_flags &= ~processor_target_table[j].target_disable;
+                   target_flags &= ~set_masks;
+                   target_flags |= (processor_target_table[j].target_enable
+                                    & set_masks);
                  }
                break;
              }
@@ -865,7 +910,7 @@ rs6000_override_options (const char *default_cpu)
       rs6000_default_long_calls = (base[0] != 'n');
     }
 
-  /* Handle -mprioritize-restrcted-insns option.  */
+  /* Handle -mprioritize-restricted-insns option.  */
   rs6000_sched_restricted_insns_priority = DEFAULT_RESTRICTED_INSNS_PRIORITY;
   if (rs6000_sched_restricted_insns_priority_str)
     rs6000_sched_restricted_insns_priority =
@@ -883,7 +928,22 @@ rs6000_override_options (const char *default_cpu)
         rs6000_sched_costly_dep = true_store_to_load_dep_costly;
       else if (! strcmp (rs6000_sched_costly_dep_str, "store_to_load"))
         rs6000_sched_costly_dep = store_to_load_dep_costly;
-      else rs6000_sched_costly_dep = atoi (rs6000_sched_costly_dep_str);
+      else 
+        rs6000_sched_costly_dep = atoi (rs6000_sched_costly_dep_str);
+    }
+
+  /* Handle -minsert-sched-nops option.  */
+  rs6000_sched_insert_nops = DEFAULT_SCHED_FINISH_NOP_INSERTION_SCHEME;
+  if (rs6000_sched_insert_nops_str)
+    {
+      if (! strcmp (rs6000_sched_insert_nops_str, "no"))
+        rs6000_sched_insert_nops = sched_finish_none;
+      else if (! strcmp (rs6000_sched_insert_nops_str, "pad"))
+        rs6000_sched_insert_nops = sched_finish_pad_groups;
+      else if (! strcmp (rs6000_sched_insert_nops_str, "regroup_exact"))
+        rs6000_sched_insert_nops = sched_finish_regroup_exact;
+      else
+        rs6000_sched_insert_nops = atoi (rs6000_sched_insert_nops_str);
     }
 
 #ifdef TARGET_REGNAMES
@@ -1620,8 +1680,27 @@ easy_vector_constant (rtx op, enum machine_mode mode)
       && cst2 >= -0x7fff && cst2 <= 0x7fff)
     return 1;
 
-  if (TARGET_ALTIVEC && EASY_VECTOR_15 (cst, op, mode))
-    return 1;
+  if (TARGET_ALTIVEC)
+    switch (mode) 
+      {
+      case V4SImode:
+       if (EASY_VECTOR_15 (cst, op, mode))
+         return 1;
+       if ((cst & 0xffff) != ((cst >> 16) & 0xffff))
+         break;
+       cst = cst >> 16;
+      case V8HImode:
+       if (EASY_VECTOR_15 (cst, op, mode))
+         return 1;
+       if ((cst & 0xff) != ((cst >> 8) & 0xff))
+         break;
+       cst = cst >> 8;
+      case V16QImode:
+       if (EASY_VECTOR_15 (cst, op, mode))
+         return 1;
+      default: 
+       break;
+      }
 
   if (TARGET_ALTIVEC && EASY_VECTOR_15_ADD_SELF (cst, op, mode))
     return 1;
@@ -1662,23 +1741,37 @@ output_vec_const_move (rtx *operands)
     {
       if (zero_constant (vec, mode))
        return "vxor %0,%0,%0";
-      else if (EASY_VECTOR_15 (cst, vec, mode))
+      else if (EASY_VECTOR_15_ADD_SELF (cst, vec, mode))
+       return "#";
+      else if (easy_vector_constant (vec, mode))
        {
          operands[1] = GEN_INT (cst);
          switch (mode)
            {
            case V4SImode:
-             return "vspltisw %0,%1";
+             if (EASY_VECTOR_15 (cst, vec, mode))
+               {
+                 operands[1] = GEN_INT (cst);
+                 return "vspltisw %0,%1";
+               }
+             cst = cst >> 16;
            case V8HImode:
-             return "vspltish %0,%1";
+             if (EASY_VECTOR_15 (cst, vec, mode))
+               {
+                 operands[1] = GEN_INT (cst);
+                 return "vspltish %0,%1";
+               }
+             cst = cst >> 8;
            case V16QImode:
-             return "vspltisb %0,%1";
+             if (EASY_VECTOR_15 (cst, vec, mode))
+               {
+                 operands[1] = GEN_INT (cst);
+                 return "vspltisb %0,%1";
+               }
            default:
              abort ();
            }
        }
-      else if (EASY_VECTOR_15_ADD_SELF (cst, vec, mode))
-       return "#";
       else
        abort ();
     }
@@ -2073,7 +2166,7 @@ and64_operand (rtx op, enum machine_mode mode)
 int
 and64_2_operand (rtx op, enum machine_mode mode)
 {
-  if (fixed_regs[CR0_REGNO])   /* CR0 not available, don't do andi./andis. */
+  if (fixed_regs[CR0_REGNO])   /* CR0 not available, don't do andi./andis.  */
     return gpc_reg_operand (op, mode) || mask64_2_operand (op, mode);
 
   return logical_operand (op, mode) || mask64_2_operand (op, mode);
@@ -2098,6 +2191,7 @@ reg_or_mem_operand (rtx op, enum machine_mode mode)
 {
   return (gpc_reg_operand (op, mode)
          || memory_operand (op, mode)
+         || macho_lo_sum_memory_operand (op, mode)
          || volatile_mem_operand (op, mode));
 }
 
@@ -2223,6 +2317,26 @@ input_operand (rtx op, enum machine_mode mode)
   return 0;
 }
 
+
+/* Darwin, AIX increases natural record alignment to doubleword if the first
+   field is an FP double while the FP fields remain word aligned.  */
+
+unsigned int
+rs6000_special_round_type_align (tree type, int computed, int specified)
+{
+  tree field = TYPE_FIELDS (type);
+
+  /* Skip all the static variables only if ABI is greater than
+     1 or equal to 0.   */
+  while (field != NULL && TREE_CODE (field) == VAR_DECL)
+    field = TREE_CHAIN (field);
+
+  if (field == NULL || field == type || DECL_MODE (field) != DFmode)
+    return MAX (computed, specified);
+
+  return MAX (MAX (computed, specified), 64);
+}
+
 /* Return 1 for an operand in small memory on V.4/eabi.  */
 
 int
@@ -2267,6 +2381,27 @@ small_data_operand (rtx op ATTRIBUTE_UNUSED,
 #endif
 }
 
+/* Return true, if operand is a memory operand and has a
+   displacement divisible by 4.  */
+
+int
+word_offset_memref_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  rtx addr;
+  int off = 0;
+
+  if (!memory_operand (op, mode))
+    return 0;
+
+  addr = XEXP (op, 0);
+  if (GET_CODE (addr) == PLUS
+      && GET_CODE (XEXP (addr, 0)) == REG
+      && GET_CODE (XEXP (addr, 1)) == CONST_INT)
+    off = INTVAL (XEXP (addr, 1));
+
+  return (off % 4) == 0;
+}
+
 /* Return true if either operand is a general purpose register.  */
 
 bool
@@ -2391,7 +2526,7 @@ legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
 
     case DFmode:
     case DImode:
-      if (TARGET_32BIT)
+      if (mode == DFmode || !TARGET_POWERPC64)
        extra = 4;
       else if (offset & 3)
        return false;
@@ -2399,7 +2534,7 @@ legitimate_offset_address_p (enum machine_mode mode, rtx x, int strict)
 
     case TFmode:
     case TImode:
-      if (TARGET_32BIT)
+      if (mode == TFmode || !TARGET_POWERPC64)
        extra = 12;
       else if (offset & 3)
        return false;
@@ -2440,6 +2575,25 @@ legitimate_indirect_address_p (rtx x, int strict)
 }
 
 static bool
+macho_lo_sum_memory_operand (rtx x, enum machine_mode mode)
+{
+    if (!TARGET_MACHO || !flag_pic
+        || mode != SImode || GET_CODE(x) != MEM)
+      return false;
+    x = XEXP (x, 0);
+
+  if (GET_CODE (x) != LO_SUM)
+    return false;
+  if (GET_CODE (XEXP (x, 0)) != REG)
+    return false;
+  if (!INT_REG_OK_FOR_BASE_P (XEXP (x, 0), 0))
+    return false;
+  x = XEXP (x, 1);
+
+  return CONSTANT_P (x);
+}
+
+static bool
 legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
 {
   if (GET_CODE (x) != LO_SUM)
@@ -2450,9 +2604,9 @@ legitimate_lo_sum_address_p (enum machine_mode mode, rtx x, int strict)
     return false;
   x = XEXP (x, 1);
 
-  if (TARGET_ELF)
+  if (TARGET_ELF || TARGET_MACHO)
     {
-      if (DEFAULT_ABI != ABI_AIX && flag_pic)
+      if (DEFAULT_ABI != ABI_AIX && DEFAULT_ABI != ABI_DARWIN && flag_pic)
        return false;
       if (TARGET_TOC)
        return false;
@@ -2574,8 +2728,8 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
               || ((TARGET_HARD_FLOAT && TARGET_FPRS) && mode == DFmode)))
     {
       rtx reg = gen_reg_rtx (Pmode);
-      emit_insn (gen_elf_high (reg, (x)));
-      return gen_rtx_LO_SUM (Pmode, reg, (x));
+      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
@@ -2590,8 +2744,8 @@ rs6000_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
           && mode != TImode)
     {
       rtx reg = gen_reg_rtx (Pmode);
-      emit_insn (gen_macho_high (reg, (x)));
-      return gen_rtx_LO_SUM (Pmode, reg, (x));
+      emit_insn (gen_macho_high (reg, x));
+      return gen_rtx_LO_SUM (Pmode, reg, x);
     }
   else if (TARGET_TOC 
           && constant_pool_expr_p (x)
@@ -2831,6 +2985,9 @@ rs6000_tls_symbol_ref (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED)
 bool
 rs6000_tls_referenced_p (rtx x)
 {
+  if (! TARGET_HAVE_TLS)
+    return false;
+
   return for_each_rtx (&x, &rs6000_tls_symbol_ref_1, 0);
 }
 
@@ -2940,36 +3097,30 @@ rs6000_legitimize_reload_address (rtx x, enum machine_mode mode,
   if (GET_CODE (x) == SYMBOL_REF
       && DEFAULT_ABI == ABI_DARWIN
       && !ALTIVEC_VECTOR_MODE (mode)
-      && flag_pic)
-    {
-      /* Darwin load of floating point constant.  */
-      rtx offset = gen_rtx (CONST, Pmode,
-                   gen_rtx (MINUS, Pmode, x,
-                   gen_rtx (SYMBOL_REF, Pmode,
-                       machopic_function_base_name ())));
-      x = gen_rtx (LO_SUM, GET_MODE (x),
-           gen_rtx (PLUS, Pmode, pic_offset_table_rtx,
-               gen_rtx (HIGH, Pmode, offset)), offset);
+      && (flag_pic || MACHO_DYNAMIC_NO_PIC_P)
+      /* Don't do this for TFmode, since the result isn't offsettable.  */
+      && mode != TFmode)
+    {
+      if (flag_pic)
+       {
+         rtx offset = gen_rtx_CONST (Pmode,
+                        gen_rtx_MINUS (Pmode, x,
+                          gen_rtx_SYMBOL_REF (Pmode,
+                            machopic_function_base_name ())));
+         x = gen_rtx_LO_SUM (GET_MODE (x),
+               gen_rtx_PLUS (Pmode, pic_offset_table_rtx,
+                 gen_rtx_HIGH (Pmode, offset)), offset);
+       }
+      else
+       x = gen_rtx_LO_SUM (GET_MODE (x),
+              gen_rtx_HIGH (Pmode, x), x);
+
       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
-               BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-               opnum, (enum reload_type)type);
+                  BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
+                  opnum, (enum reload_type)type);
       *win = 1;
       return x;
     }
-   if (GET_CODE (x) == SYMBOL_REF
-       && DEFAULT_ABI == ABI_DARWIN
-       && !ALTIVEC_VECTOR_MODE (mode)
-       && MACHO_DYNAMIC_NO_PIC_P)
-     {
-       /* Darwin load of floating point constant.  */
-       x = gen_rtx (LO_SUM, GET_MODE (x),
-               gen_rtx (HIGH, Pmode, x), x);
-       push_reload (XEXP (x, 0), NULL_RTX, &XEXP (x, 0), NULL,
-               BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
-               opnum, (enum reload_type)type);
-       *win = 1;
-       return x;
-     }
 #endif
   if (TARGET_TOC
       && constant_pool_expr_p (x)
@@ -3175,7 +3326,7 @@ rs6000_emit_set_long_const (rtx dest, HOST_WIDE_INT c1, HOST_WIDE_INT c2)
          || (ud4 == 0 && ud3 == 0 && ud2 == 0 && ! (ud1 & 0x8000)))
        {
          if (ud1 & 0x8000)
-           emit_move_insn (dest, GEN_INT (((ud1  ^ 0x8000) -  0x8000)));
+           emit_move_insn (dest, GEN_INT (((ud1 ^ 0x8000) -  0x8000)));
          else
            emit_move_insn (dest, GEN_INT (ud1));
        }
@@ -3275,6 +3426,47 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
                      adjust_address (operands[1], SImode, 4));
       return;
     }
+#if 0
+    else if (mode == DImode && TARGET_POWERPC64
+               && GET_CODE (operands[0]) == REG
+               && GET_CODE (operands[1]) == MEM && optimize > 0
+               && SLOW_UNALIGNED_ACCESS (DImode,
+                                         MEM_ALIGN (operands[1]) > 32
+                                         ? 32
+                                         : MEM_ALIGN (operands[1]))
+               && !no_new_pseudos)
+      {
+        rtx reg = gen_reg_rtx (SImode);
+       emit_insn (gen_rtx_SET (SImode, reg,
+                               adjust_address (operands[1], SImode, 0)));
+        reg = simplify_gen_subreg (DImode, reg, SImode, 0);
+        emit_insn (gen_insvdi (operands[0], GEN_INT (32), const0_rtx, reg));
+        reg = gen_reg_rtx (SImode);
+       emit_insn (gen_rtx_SET (SImode, reg,
+                               adjust_address (operands[1], SImode, 4)));
+        reg = simplify_gen_subreg (DImode, reg, SImode, 0);
+        emit_insn (gen_insvdi (operands[0], GEN_INT (32), GEN_INT (32), reg));
+        return;
+      }
+      else if (mode == DImode && TARGET_POWERPC64
+               && GET_CODE (operands[1]) == REG
+               && GET_CODE (operands[0]) == MEM && optimize > 0
+               && SLOW_UNALIGNED_ACCESS (DImode,
+                                         MEM_ALIGN (operands[0]) > 32
+                                         ? 32
+                                         : MEM_ALIGN (operands[0]))
+               && !no_new_pseudos)
+      {
+        rtx reg = gen_reg_rtx (DImode);
+        emit_move_insn (reg,
+                       gen_rtx_LSHIFTRT (DImode, operands[1], GEN_INT (32)));
+        emit_move_insn (adjust_address (operands[0], SImode, 0),
+                       simplify_gen_subreg (SImode, reg, DImode, 0));
+        emit_move_insn (adjust_address (operands[0], SImode, 4),
+                       simplify_gen_subreg (SImode, operands[1], DImode, 0));
+        return;
+      }
+#endif
   
   if (!no_new_pseudos)
     {
@@ -3336,7 +3528,26 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
   /* Handle the case of CONSTANT_P_RTX.  */
   if (GET_CODE (operands[1]) == CONSTANT_P_RTX)
     goto emit_set;
-  
+
+  /* 128-bit constant floating-point values on Darwin should really be
+     loaded as two parts.  */
+  if ((DEFAULT_ABI == ABI_AIX || DEFAULT_ABI == ABI_DARWIN)
+      && TARGET_HARD_FLOAT && TARGET_FPRS && TARGET_LONG_DOUBLE_128
+      && mode == TFmode && GET_CODE (operands[1]) == CONST_DOUBLE)
+    {
+      /* DImode is used, not DFmode, because simplify_gen_subreg doesn't
+        know how to get a DFmode SUBREG of a TFmode.  */
+      rs6000_emit_move (simplify_gen_subreg (DImode, operands[0], mode, 0),
+                       simplify_gen_subreg (DImode, operands[1], mode, 0),
+                       DImode);
+      rs6000_emit_move (simplify_gen_subreg (DImode, operands[0], mode,
+                                            GET_MODE_SIZE (DImode)),
+                       simplify_gen_subreg (DImode, operands[1], mode,
+                                            GET_MODE_SIZE (DImode)),
+                       DImode);
+      return;
+    }
+
   /* FIXME:  In the long term, this switch statement should go away
      and be replaced by a sequence of tests based on things like
      mode == Pmode.  */
@@ -3571,6 +3782,19 @@ rs6000_emit_move (rtx dest, rtx source, enum machine_mode mode)
   emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[1]));
 }
 \f
+/* Nonzero if we can use a floating-point register to pass this arg.  */
+#define USE_FP_FOR_ARG_P(CUM,MODE,TYPE)                \
+  (GET_MODE_CLASS (MODE) == MODE_FLOAT         \
+   && (CUM)->fregno <= FP_ARG_MAX_REG          \
+   && TARGET_HARD_FLOAT && TARGET_FPRS)
+
+/* Nonzero if we can use an AltiVec register to pass this arg.  */
+#define USE_ALTIVEC_FOR_ARG_P(CUM,MODE,TYPE,NAMED)     \
+  (ALTIVEC_VECTOR_MODE (MODE)                          \
+   && (CUM)->vregno <= ALTIVEC_ARG_MAX_REG             \
+   && TARGET_ALTIVEC_ABI                               \
+   && (NAMED))
+
 /* Return a nonzero value to say to return the function value in
    memory, just as large structures are always returned.  TYPE will be
    the data type of the value, and FNTYPE will be the type of the
@@ -3755,23 +3979,37 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
   if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
     {
-      if (named && cum->vregno <= ALTIVEC_ARG_MAX_REG)
+      if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named))
        cum->vregno++;
-      else
-       {
+      
+      /* PowerPC64 Linux and AIX allocates GPRs for a vector argument
+        even if it is going to be passed in a vector register.  
+        Darwin does the same for variable-argument functions.  */
+      if ((DEFAULT_ABI == ABI_AIX && TARGET_64BIT)
+                  || (cum->stdarg && DEFAULT_ABI != ABI_V4))
+        {
          int align;
          
-         /* Vector parameters must be 16-byte aligned.  This places them at
-            2 mod 4 in terms of words (on both ABIs).  */
-         align = ((6 - (cum->words & 3)) & 3);
+         /* Vector parameters must be 16-byte aligned.  This places
+            them at 2 mod 4 in terms of words in 32-bit mode, since
+            the parameter save area starts at offset 24 from the
+            stack.  In 64-bit mode, they just have to start on an
+            even word, since the parameter save area is 16-byte
+            aligned.  Space for GPRs is reserved even if the argument
+            will be passed in memory.  */
+         if (TARGET_32BIT)
+           align = ((6 - (cum->words & 3)) & 3);
+         else
+           align = cum->words & 1;
          cum->words += align + RS6000_ARG_SIZE (mode, type);
-
+         
          if (TARGET_DEBUG_ARG)
            {
              fprintf (stderr, "function_adv: words = %2d, align=%d, ", 
                       cum->words, align);
              fprintf (stderr, "nargs = %4d, proto = %d, mode = %4s\n",
-                      cum->nargs_prototype, cum->prototype, GET_MODE_NAME (mode));
+                      cum->nargs_prototype, cum->prototype, 
+                      GET_MODE_NAME (mode));
            }
        }
     }
@@ -3858,6 +4096,7 @@ function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 }
 
 /* Determine where to put a SIMD argument on the SPE.  */
+
 static rtx
 rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
                         tree type)
@@ -3883,17 +4122,121 @@ rs6000_spe_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
          return gen_rtx_PARALLEL (mode, gen_rtvec (2, r1, r2));
        }
       else
-       return NULL;
+       return NULL_RTX;
     }
   else
     {
       if (cum->sysv_gregno <= GP_ARG_MAX_REG)
        return gen_rtx_REG (mode, cum->sysv_gregno);
       else
-       return NULL;
+       return NULL_RTX;
     }
 }
 
+/* Determine where to place an argument in 64-bit mode with 32-bit ABI.  */
+
+static rtx
+rs6000_mixed_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
+                          tree type, int align_words)
+{
+  if (mode == DFmode)
+    {
+      /* -mpowerpc64 with 32bit ABI splits up a DFmode argument
+        in vararg list into zero, one or two GPRs */
+      if (align_words >= GP_ARG_NUM_REG)
+       return gen_rtx_PARALLEL (DFmode,
+                gen_rtvec (2,
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              NULL_RTX, const0_rtx), 
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              gen_rtx_REG (mode,
+                                                           cum->fregno),
+                                              const0_rtx)));
+      else if (align_words + RS6000_ARG_SIZE (mode, type)
+              > GP_ARG_NUM_REG)
+       /* If this is partially on the stack, then we only
+          include the portion actually in registers here.  */
+       return gen_rtx_PARALLEL (DFmode,
+                gen_rtvec (2,   
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              gen_rtx_REG (SImode,
+                                                           GP_ARG_MIN_REG
+                                                           + align_words),
+                                              const0_rtx),
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              gen_rtx_REG (mode,
+                                                           cum->fregno),
+                                              const0_rtx)));
+
+      /* split a DFmode arg into two GPRs */
+      return gen_rtx_PARALLEL (DFmode,
+              gen_rtvec (3,
+                         gen_rtx_EXPR_LIST (VOIDmode,       
+                                            gen_rtx_REG (SImode,
+                                                         GP_ARG_MIN_REG
+                                                         + align_words),
+                                            const0_rtx),
+                         gen_rtx_EXPR_LIST (VOIDmode,
+                                            gen_rtx_REG (SImode,
+                                                         GP_ARG_MIN_REG
+                                                         + align_words + 1),
+                                            GEN_INT (4)),
+                         gen_rtx_EXPR_LIST (VOIDmode,
+                                            gen_rtx_REG (mode, cum->fregno),
+                                            const0_rtx)));
+    }
+  /* -mpowerpc64 with 32bit ABI splits up a DImode argument into one
+     or two GPRs */
+  else if (mode == DImode)
+    {
+      if (align_words < GP_ARG_NUM_REG - 1)
+       return gen_rtx_PARALLEL (DImode,
+                gen_rtvec (2,
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              gen_rtx_REG (SImode,
+                                                           GP_ARG_MIN_REG
+                                                           + align_words),
+                                              const0_rtx),
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              gen_rtx_REG (SImode,
+                                                           GP_ARG_MIN_REG
+                                                           + align_words + 1),
+                                              GEN_INT (4))));
+      else if (align_words == GP_ARG_NUM_REG - 1)
+         return gen_rtx_PARALLEL (DImode,
+                  gen_rtvec (2,
+                             gen_rtx_EXPR_LIST (VOIDmode,
+                                                NULL_RTX, const0_rtx),
+                             gen_rtx_EXPR_LIST (VOIDmode,
+                                                gen_rtx_REG (SImode,
+                                                             GP_ARG_MIN_REG
+                                                             + align_words),
+                                                const0_rtx)));
+    }
+  else if (mode == BLKmode && align_words <= (GP_ARG_NUM_REG - 1))
+    {
+      int k;
+      int size = int_size_in_bytes (type);
+      int no_units = ((size - 1) / 4) + 1;
+      int max_no_words = GP_ARG_NUM_REG - align_words;
+      int rtlvec_len = no_units < max_no_words ? no_units : max_no_words;
+      rtx *rtlvec = (rtx *) alloca (rtlvec_len * sizeof (rtx));
+
+      memset ((char *) rtlvec, 0, rtlvec_len * sizeof (rtx));
+
+      for (k=0; k < rtlvec_len; k++)
+       rtlvec[k] = gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (SImode,
+                                                   GP_ARG_MIN_REG
+                                                   + align_words + k),
+                                      k == 0 ? const0_rtx : GEN_INT (k*4));
+
+      return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (k, rtlvec));
+  }
+
+  return NULL_RTX;
+}
+
 /* Determine where to put an argument to a function.
    Value is zero to push the argument on the stack,
    or a hard register in which to store the argument.
@@ -3947,40 +4290,68 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       return GEN_INT (cum->call_cookie);
     }
 
-  if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
+  if (USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named))
+    if (TARGET_64BIT && ! cum->prototype)
+      {
+       /* Vector parameters get passed in vector register
+          and also in GPRs or memory, in absence of prototype.  */
+       int align_words;
+       rtx slot;
+       align_words = (cum->words + 1) & ~1;
+
+       if (align_words >= GP_ARG_NUM_REG)
+         {
+           slot = NULL_RTX;
+         }
+       else
+         {
+           slot = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
+         }
+       return gen_rtx_PARALLEL (mode,
+                gen_rtvec (2,
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              slot, const0_rtx),
+                           gen_rtx_EXPR_LIST (VOIDmode,
+                                              gen_rtx_REG (mode, cum->vregno),
+                                              const0_rtx)));
+      }
+    else
+      return gen_rtx_REG (mode, cum->vregno);
+  else if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (mode))
     {
-      if (named && cum->vregno <= ALTIVEC_ARG_MAX_REG)
-       return gen_rtx_REG (mode, cum->vregno);
-      else if (named || abi == ABI_V4)
+      if (named || abi == ABI_V4)
        return NULL_RTX;
       else
        {
          /* Vector parameters to varargs functions under AIX or Darwin
             get passed in memory and possibly also in GPRs.  */
          int align, align_words;
-         rtx reg;
+         enum machine_mode part_mode = mode;
 
          /* Vector parameters must be 16-byte aligned.  This places them at
-            2 mod 4 in terms of words.  */
-         align = ((6 - (cum->words & 3)) & 3);
+            2 mod 4 in terms of words in 32-bit mode, since the parameter
+            save area starts at offset 24 from the stack.  In 64-bit mode,
+            they just have to start on an even word, since the parameter
+            save area is 16-byte aligned.  */
+         if (TARGET_32BIT)
+           align = ((6 - (cum->words & 3)) & 3);
+         else
+           align = cum->words & 1;
          align_words = cum->words + align;
 
          /* Out of registers?  Memory, then.  */
          if (align_words >= GP_ARG_NUM_REG)
            return NULL_RTX;
          
-         /* The vector value goes in both memory and GPRs.  Varargs
-            vector regs will always be saved in R5-R8 or R9-R12.  */
-         reg = gen_rtx_REG (mode, GP_ARG_MIN_REG + align_words);
-
-         return gen_rtx_PARALLEL (mode,
-                                  gen_rtvec (2,
-                                             gen_rtx_EXPR_LIST (VOIDmode,
-                                                                NULL_RTX, 
-                                                                const0_rtx),
-                                             gen_rtx_EXPR_LIST (VOIDmode,
-                                                                reg, 
-                                                                const0_rtx)));
+         /* The vector value goes in GPRs.  Only the part of the
+            value in GPRs is reported here.  */
+         if (align_words + CLASS_MAX_NREGS (mode, GENERAL_REGS)
+             > GP_ARG_NUM_REG)
+           /* Fortunately, there are only two possibilities, the value
+              is either wholly in GPRs or half in GPRs and half not.  */
+           part_mode = DImode;
+         
+         return gen_rtx_REG (part_mode, GP_ARG_MIN_REG + align_words);
        }
     }
   else if (TARGET_SPE_ABI && TARGET_SPE && SPE_VECTOR_MODE (mode))
@@ -3993,7 +4364,7 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
          if (cum->fregno <= FP_ARG_V4_MAX_REG)
            return gen_rtx_REG (mode, cum->fregno);
          else
-           return NULL;
+           return NULL_RTX;
        }
       else
        {
@@ -4015,7 +4386,7 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
          if (gregno + n_words - 1 <= GP_ARG_MAX_REG)
            return gen_rtx_REG (mode, gregno);
          else
-           return NULL;
+           return NULL_RTX;
        }
     }
   else
@@ -4027,7 +4398,11 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       if (type && TREE_CODE (TYPE_SIZE (type)) != INTEGER_CST)
         return NULL_RTX;
 
-      if (USE_FP_FOR_ARG_P (*cum, mode, type))
+      if (TARGET_32BIT && TARGET_POWERPC64
+         && (mode == DFmode || mode == DImode || mode == BLKmode))
+       return rs6000_mixed_function_arg (cum, mode, type, align_words);
+
+      if (USE_FP_FOR_ARG_P (cum, mode, type))
        {
          if (! type
              || ((cum->nargs_prototype > 0)
@@ -4072,13 +4447,13 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
 int
 function_arg_partial_nregs (CUMULATIVE_ARGS *cum, enum machine_mode mode, 
-                           tree type, int named ATTRIBUTE_UNUSED)
+                           tree type, int named)
 {
   if (DEFAULT_ABI == ABI_V4)
     return 0;
 
-  if (USE_FP_FOR_ARG_P (*cum, mode, type)
-      || USE_ALTIVEC_FOR_ARG_P (*cum, mode, type))
+  if (USE_FP_FOR_ARG_P (cum, mode, type)
+      || USE_ALTIVEC_FOR_ARG_P (cum, mode, type, named))
     {
       if (cum->nargs_prototype >= 0)
        return 0;
@@ -4124,6 +4499,37 @@ function_arg_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
     }
   return type && int_size_in_bytes (type) < 0;
 }
+
+static void
+rs6000_move_block_from_reg (int regno, rtx x, int nregs)
+{
+  int i;
+  enum machine_mode reg_mode = TARGET_32BIT ? SImode : DImode;
+
+  if (nregs == 0)
+    return;
+
+    for (i = 0; i < nregs; i++)
+    {
+      rtx tem = adjust_address_nv (x, reg_mode, i*GET_MODE_SIZE(reg_mode));
+      if (reload_completed)
+      {
+       if (! strict_memory_address_p (reg_mode, XEXP (tem, 0)))
+         tem = NULL_RTX;
+       else
+         tem = simplify_gen_subreg (reg_mode, x, BLKmode, 
+                                    i * GET_MODE_SIZE(reg_mode));
+      }
+      else
+       tem = replace_equiv_address (tem, XEXP (tem, 0));
+
+      if (tem == NULL_RTX)
+        abort ();
+
+      emit_move_insn (tem, gen_rtx_REG (reg_mode, regno + i));
+    }
+}
+
 \f
 /* Perform any needed actions needed for a function that is receiving a
    variable number of arguments. 
@@ -4147,19 +4553,10 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   int reg_size = TARGET_32BIT ? 4 : 8;
   rtx save_area = NULL_RTX, mem;
   int first_reg_offset, set;
-  tree fntype;
-  int stdarg_p;
 
-  fntype = TREE_TYPE (current_function_decl);
-  stdarg_p = (TYPE_ARG_TYPES (fntype) != 0
-             && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype)))
-                 != void_type_node));
-
-  /* For varargs, we do not want to skip the dummy va_dcl argument.
-     For stdargs, we do want to skip the last named argument.  */
+  /* Skip the last named argument.  */
   next_cum = *cum;
-  if (stdarg_p)
-    function_arg_advance (&next_cum, mode, type, 1);
+  function_arg_advance (&next_cum, mode, type, 1);
 
   if (DEFAULT_ABI == ABI_V4)
     {
@@ -4190,8 +4587,8 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       set_mem_alias_set (mem, set);
       set_mem_align (mem, BITS_PER_WORD);
 
-      move_block_from_reg (GP_ARG_MIN_REG + first_reg_offset, mem,
-                          GP_ARG_NUM_REG - first_reg_offset);
+      rs6000_move_block_from_reg (GP_ARG_MIN_REG + first_reg_offset, mem, 
+                                 GP_ARG_NUM_REG - first_reg_offset);
     }
 
   /* Save FP registers if needed.  */
@@ -4228,8 +4625,8 @@ setup_incoming_varargs (CUMULATIVE_ARGS *cum, enum machine_mode mode,
 
 /* Create the va_list data type.  */
 
-tree
-rs6000_build_va_list (void)
+static tree
+rs6000_build_builtin_va_list (void)
 {
   tree f_gpr, f_fpr, f_res, f_ovf, f_sav, record, type_decl;
 
@@ -4369,29 +4766,7 @@ rs6000_va_arg (tree valist, tree type)
          return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL);
        }
       else
-       {
-         /* Altivec arguments must be aligned to a 128-bit boundary.  */
-         if (TARGET_ALTIVEC_ABI && ALTIVEC_VECTOR_MODE (TYPE_MODE (type)))
-           {
-             tree vtype = TREE_TYPE (valist);
-             tree new_valist, modify;
-             
-             /* Round address up to multiple of 16.  Computes
-                (addr+15)&~0xf.  */
-             new_valist = fold (build (BIT_AND_EXPR, vtype,
-                                       fold (build (PLUS_EXPR, vtype, valist,
-                                                    build_int_2 (15, 0))),
-                                       build_int_2 (~15, -1)));
-
-             /* Update valist.  */
-             modify = build (MODIFY_EXPR, TREE_TYPE (valist), valist,
-                             new_valist);
-             TREE_SIDE_EFFECTS (modify) = 1;
-             expand_expr (modify, const0_rtx, VOIDmode, EXPAND_NORMAL);
-           }
-         
-         return std_expand_builtin_va_arg (valist, type);
-       }
+       return std_expand_builtin_va_arg (valist, type);
     }
 
   f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
@@ -4439,7 +4814,7 @@ rs6000_va_arg (tree valist, tree type)
       sav_scale = 4;
     }
 
-  /* Pull the value out of the saved registers ...  */
+  /* Pull the value out of the saved registers....  */
 
   lab_false = gen_label_rtx ();
   lab_over = gen_label_rtx ();
@@ -5225,6 +5600,52 @@ altivec_expand_predicate_builtin (enum insn_code icode, const char *opcode,
 }
 
 static rtx
+altivec_expand_lv_builtin (enum insn_code icode, tree arglist, rtx target)
+{
+  rtx pat, addr;
+  tree arg0 = TREE_VALUE (arglist);
+  tree arg1 = TREE_VALUE (TREE_CHAIN (arglist));
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode0 = Pmode;
+  enum machine_mode mode1 = Pmode;
+  rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
+  rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
+
+  if (icode == CODE_FOR_nothing)
+    /* Builtin not supported on this processor.  */
+    return 0;
+
+  /* If we got invalid arguments bail out before generating bad rtl.  */
+  if (arg0 == error_mark_node || arg1 == error_mark_node)
+    return const0_rtx;
+
+  if (target == 0
+      || GET_MODE (target) != tmode
+      || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+    target = gen_reg_rtx (tmode);
+
+  op1 = copy_to_mode_reg (mode1, op1); 
+
+  if (op0 == const0_rtx)
+    {
+      addr = gen_rtx_MEM (tmode, op1);
+    }
+  else
+    {
+      op0 = copy_to_mode_reg (mode0, op0);
+      addr = gen_rtx_MEM (tmode, gen_rtx_PLUS (Pmode, op0, op1));
+    }
+
+  pat = GEN_FCN (icode) (target, addr);
+
+  if (! pat)
+    return 0;
+  emit_insn (pat);
+
+  return target;
+}
+
+static rtx
 altivec_expand_stv_builtin (enum insn_code icode, tree arglist)
 {
   tree arg0 = TREE_VALUE (arglist);
@@ -5233,10 +5654,10 @@ altivec_expand_stv_builtin (enum insn_code icode, tree arglist)
   rtx op0 = expand_expr (arg0, NULL_RTX, VOIDmode, 0);
   rtx op1 = expand_expr (arg1, NULL_RTX, VOIDmode, 0);
   rtx op2 = expand_expr (arg2, NULL_RTX, VOIDmode, 0);
-  rtx pat;
-  enum machine_mode mode0 = insn_data[icode].operand[0].mode;
-  enum machine_mode mode1 = insn_data[icode].operand[1].mode;
-  enum machine_mode mode2 = insn_data[icode].operand[2].mode;
+  rtx pat, addr;
+  enum machine_mode tmode = insn_data[icode].operand[0].mode;
+  enum machine_mode mode1 = Pmode;
+  enum machine_mode mode2 = Pmode;
 
   /* Invalid arguments.  Bail before doing anything stoopid!  */
   if (arg0 == error_mark_node
@@ -5244,14 +5665,22 @@ altivec_expand_stv_builtin (enum insn_code icode, tree arglist)
       || arg2 == error_mark_node)
     return const0_rtx;
 
-  if (! (*insn_data[icode].operand[2].predicate) (op0, mode2))
-    op0 = copy_to_mode_reg (mode2, op0);
-  if (! (*insn_data[icode].operand[0].predicate) (op1, mode0))
-    op1 = copy_to_mode_reg (mode0, op1);
-  if (! (*insn_data[icode].operand[1].predicate) (op2, mode1))
-    op2 = copy_to_mode_reg (mode1, op2);
+  if (! (*insn_data[icode].operand[1].predicate) (op0, tmode))
+    op0 = copy_to_mode_reg (tmode, op0);
+
+  op2 = copy_to_mode_reg (mode2, op2); 
+
+  if (op1 == const0_rtx)
+    {
+      addr = gen_rtx_MEM (tmode, op2);
+    }
+  else
+    {
+      op1 = copy_to_mode_reg (mode1, op1);
+      addr = gen_rtx_MEM (tmode, gen_rtx_PLUS (Pmode, op1, op2));
+    }
 
-  pat = GEN_FCN (icode) (op1, op2, op0);
+  pat = GEN_FCN (icode) (addr, op0);
   if (pat)
     emit_insn (pat);
   return NULL_RTX;
@@ -5466,7 +5895,7 @@ altivec_expand_dst_builtin (tree exp, rtx target ATTRIBUTE_UNUSED,
          }
 
        if (! (*insn_data[d->icode].operand[0].predicate) (op0, mode0))
-         op0 = copy_to_mode_reg (mode0, op0);
+         op0 = gen_rtx_MEM (mode0, copy_to_mode_reg (Pmode, op0));
        if (! (*insn_data[d->icode].operand[1].predicate) (op1, mode1))
          op1 = copy_to_mode_reg (mode1, op1);
 
@@ -5601,25 +6030,25 @@ altivec_expand_builtin (tree exp, rtx target, bool *expandedp)
   switch (fcode)
     {
     case ALTIVEC_BUILTIN_LVSL:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvsl,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsl,
                                           arglist, target);
     case ALTIVEC_BUILTIN_LVSR:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvsr,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvsr,
                                          arglist, target);
     case ALTIVEC_BUILTIN_LVEBX:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvebx,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvebx,
                                          arglist, target);
     case ALTIVEC_BUILTIN_LVEHX:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvehx,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvehx,
                                          arglist, target);
     case ALTIVEC_BUILTIN_LVEWX:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvewx,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvewx,
                                          arglist, target);
     case ALTIVEC_BUILTIN_LVXL:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvxl,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvxl,
                                          arglist, target);
     case ALTIVEC_BUILTIN_LVX:
-      return rs6000_expand_binop_builtin (CODE_FOR_altivec_lvx,
+      return altivec_expand_lv_builtin (CODE_FOR_altivec_lvx,
                                          arglist, target);
     default:
       break;
@@ -6330,27 +6759,27 @@ altivec_init_builtins (void)
   tree void_ftype_qi
     = build_function_type_list (void_type_node, char_type_node, NULL_TREE);
 
-  tree v16qi_ftype_int_pcvoid
+  tree v16qi_ftype_long_pcvoid
     = build_function_type_list (V16QI_type_node,
-                               integer_type_node, pcvoid_type_node, NULL_TREE);
-  tree v8hi_ftype_int_pcvoid
+                               long_integer_type_node, pcvoid_type_node, NULL_TREE);
+  tree v8hi_ftype_long_pcvoid
     = build_function_type_list (V8HI_type_node,
-                               integer_type_node, pcvoid_type_node, NULL_TREE);
-  tree v4si_ftype_int_pcvoid
+                               long_integer_type_node, pcvoid_type_node, NULL_TREE);
+  tree v4si_ftype_long_pcvoid
     = build_function_type_list (V4SI_type_node,
-                               integer_type_node, pcvoid_type_node, NULL_TREE);
+                               long_integer_type_node, pcvoid_type_node, NULL_TREE);
 
-  tree void_ftype_v4si_int_pvoid
+  tree void_ftype_v4si_long_pvoid
     = build_function_type_list (void_type_node,
-                               V4SI_type_node, integer_type_node,
+                               V4SI_type_node, long_integer_type_node,
                                pvoid_type_node, NULL_TREE);
-  tree void_ftype_v16qi_int_pvoid
+  tree void_ftype_v16qi_long_pvoid
     = build_function_type_list (void_type_node,
-                               V16QI_type_node, integer_type_node,
+                               V16QI_type_node, long_integer_type_node,
                                pvoid_type_node, NULL_TREE);
-  tree void_ftype_v8hi_int_pvoid
+  tree void_ftype_v8hi_long_pvoid
     = build_function_type_list (void_type_node,
-                               V8HI_type_node, integer_type_node,
+                               V8HI_type_node, long_integer_type_node,
                                pvoid_type_node, NULL_TREE);
   tree int_ftype_int_v8hi_v8hi
     = build_function_type_list (integer_type_node,
@@ -6397,18 +6826,18 @@ altivec_init_builtins (void)
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_mfvscr", v8hi_ftype_void, ALTIVEC_BUILTIN_MFVSCR);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_dssall", void_ftype_void, ALTIVEC_BUILTIN_DSSALL);
   def_builtin (MASK_ALTIVEC, "__builtin_altivec_dss", void_ftype_qi, ALTIVEC_BUILTIN_DSS);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsl", v16qi_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVSL);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsr", v16qi_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVSR);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvebx", v16qi_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVEBX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvehx", v8hi_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVEHX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvewx", v4si_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVEWX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvxl", v4si_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVXL);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvx", v4si_ftype_int_pcvoid, ALTIVEC_BUILTIN_LVX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvx", void_ftype_v4si_int_pvoid, ALTIVEC_BUILTIN_STVX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvewx", void_ftype_v4si_int_pvoid, ALTIVEC_BUILTIN_STVEWX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvxl", void_ftype_v4si_int_pvoid, ALTIVEC_BUILTIN_STVXL);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvebx", void_ftype_v16qi_int_pvoid, ALTIVEC_BUILTIN_STVEBX);
-  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvehx", void_ftype_v8hi_int_pvoid, ALTIVEC_BUILTIN_STVEHX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsl", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSL);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvsr", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVSR);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvebx", v16qi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEBX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvehx", v8hi_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEHX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvewx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVEWX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvxl", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVXL);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_lvx", v4si_ftype_long_pcvoid, ALTIVEC_BUILTIN_LVX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvx", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvewx", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVEWX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvxl", void_ftype_v4si_long_pvoid, ALTIVEC_BUILTIN_STVXL);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvebx", void_ftype_v16qi_long_pvoid, ALTIVEC_BUILTIN_STVEBX);
+  def_builtin (MASK_ALTIVEC, "__builtin_altivec_stvehx", void_ftype_v8hi_long_pvoid, ALTIVEC_BUILTIN_STVEHX);
 
   /* Add the DST variants.  */
   d = (struct builtin_description *) bdesc_dst;
@@ -6948,6 +7377,8 @@ rs6000_init_libfuncs (void)
          /* 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");
        }
 
       /* Standard AIX/Darwin/64-bit SVR4 quad floating point routines.  */
@@ -7968,7 +8399,8 @@ addrs_ok_for_quad_peep (rtx addr1, rtx addr2)
 
 enum reg_class
 secondary_reload_class (enum reg_class class, 
-               enum machine_mode mode ATTRIBUTE_UNUSED, rtx in)
+                       enum machine_mode mode ATTRIBUTE_UNUSED,
+                       rtx in)
 {
   int regno;
 
@@ -9422,7 +9854,7 @@ output_cbranch (rtx op, const char *label, int reversed, rtx insn)
     s += sprintf (s, "{b%s|b%s%s} ", ccode, ccode, pred);
 
   /* We need to escape any '%' characters in the reg_names string.
-     Assume they'd only be the first character...  */
+     Assume they'd only be the first character....  */
   if (reg_names[cc_regno + CR0_REGNO][0] == '%')
     *s++ = '%';
   s += sprintf (s, "%s", reg_names[cc_regno + CR0_REGNO]);
@@ -9455,7 +9887,7 @@ rs6000_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond)
   enum machine_mode result_mode = GET_MODE (dest);
   rtx temp;
 
-  /* These modes should always match. */
+  /* These modes should always match.  */
   if (GET_MODE (op1) != compare_mode
       /* In the isel case however, we can use a compare immediate, so
         op1 may be a small constant.  */
@@ -9467,7 +9899,7 @@ rs6000_emit_cmove (rtx dest, rtx op, rtx true_cond, rtx false_cond)
     return 0;
 
   /* First, work out if the hardware can do this at all, or
-     if it's too slow...  */
+     if it's too slow....  */
   if (! rs6000_compare_fp_p)
     {
       if (TARGET_ISEL)
@@ -9673,73 +10105,72 @@ rs6000_emit_minmax (rtx dest, enum rtx_code code, rtx op0, rtx op1)
     emit_move_insn (dest, target);
 }
 
-/* Called by splitter for multireg moves.
-   Input: 
-          operands[0] : Destination of move
-          operands[1] : Source of move
-
-   Output:
-         operands[2-n] : Destination slots
-         operands[n-m] : Source slots
-   where n = 2 + HARD_REGNO_NREGS (reg, GET_MODE (operands[0]))
-         m = 2 + 2 * HARD_REGNO_NREGS (reg, GET_MODE (operands[0])) - 1
+/* Emit instructions to move SRC to DST.  Called by splitters for
+   multi-register moves.  It will emit at most one instruction for
+   each register that is accessed; that is, it won't emit li/lis pairs
+   (or equivalent for 64-bit code).  One of SRC or DST must be a hard
+   register.  */
 
-   Splits the move of operands[1] to operands[0].
-   This is done, if GPRs are one of the operands.  In this case
-   a sequence of simple move insns has to be issued.  The sequence of these
-   move insns has to be done in correct order to avoid early clobber of the
-   base register or destructive overlap of registers. 
-*/
-         
 void
-rs6000_split_multireg_move (rtx *operands)
+rs6000_split_multireg_move (rtx dst, rtx src)
 {
-  int nregs, reg, i, j, used_update = 0;
-  enum machine_mode mode; 
-  rtx dst = operands[0];
-  rtx src = operands[1];
-  rtx insn = 0;
-
-  /* Calculate number to move (2/4 for 32/64 bit mode).  */ 
-
-  reg = REG_P (operands[0]) ? REGNO (operands[0]) : REGNO (operands[1]); 
-  mode = GET_MODE (operands[0]);
-  nregs = HARD_REGNO_NREGS (reg, mode);                                  
+  /* The register number of the first register being moved.  */
+  int reg;
+  /* The mode that is to be moved.  */
+  enum machine_mode mode;
+  /* The mode that the move is being done in, and its size.  */
+  enum machine_mode reg_mode;
+  int reg_mode_size;
+  /* The number of registers that will be moved.  */
+  int nregs;
+
+  reg = REG_P (dst) ? REGNO (dst) : REGNO (src);
+  mode = GET_MODE (dst);
+  nregs = HARD_REGNO_NREGS (reg, mode);
+  if (FP_REGNO_P (reg))
+    reg_mode = DFmode;
+  else if (ALTIVEC_REGNO_P (reg))
+    reg_mode = V16QImode;
+  else
+    reg_mode = word_mode;
+  reg_mode_size = GET_MODE_SIZE (reg_mode);
   
-  if (REG_P (operands[1]) 
-      && REG_P (operands[0]) 
-      && (REGNO (operands[1]) < REGNO (operands[0])))
-    {  
-      /* Move register range backwards, if we have destructive overlap.  */
-
-      j = nregs;
-      for (i = 0; i < nregs; i++)
-        {
-          j--;
-          operands[i+2] = operand_subword (operands[0], j, 0, mode);
-          operands[i+2+nregs] = 
-            operand_subword (operands[1], j, 0, mode);   
-        }
-    }     
+  if (reg_mode_size * nregs != GET_MODE_SIZE (mode))
+    abort ();
+  
+  if (REG_P (src) && REG_P (dst) && (REGNO (src) < REGNO (dst)))
+    {
+      /* Move register range backwards, if we might have destructive
+        overlap.  */
+      int i;
+      for (i = nregs - 1; i >= 0; i--)
+       emit_insn (gen_rtx_SET (VOIDmode, 
+                               simplify_gen_subreg (reg_mode, dst, mode,
+                                                    i * reg_mode_size),
+                               simplify_gen_subreg (reg_mode, src, mode,
+                                                    i * reg_mode_size)));
+    }
   else
     {
-      j = -1;
+      int i;
+      int j = -1;
+      bool used_update = false;
 
-      if (GET_CODE (operands[1]) == MEM)
+      if (GET_CODE (src) == MEM && INT_REGNO_P (reg))
         {
           rtx breg;
 
-         if (GET_CODE (XEXP (operands[1], 0)) == PRE_INC
-             || GET_CODE (XEXP (operands[1], 0)) == PRE_DEC)
+         if (GET_CODE (XEXP (src, 0)) == PRE_INC
+             || GET_CODE (XEXP (src, 0)) == PRE_DEC)
            {
              rtx delta_rtx;
-             breg = XEXP (XEXP (operands[1], 0), 0);
-             delta_rtx =  GET_CODE (XEXP (operands[1], 0)) == PRE_INC 
-                 ? GEN_INT (GET_MODE_SIZE (GET_MODE (operands[1]))) 
-                 : GEN_INT (-GET_MODE_SIZE (GET_MODE (operands[1]))); 
-             insn = emit_insn (TARGET_32BIT
-                               ? gen_addsi3 (breg, breg, delta_rtx)
-                               : gen_adddi3 (breg, breg, delta_rtx));
+             breg = XEXP (XEXP (src, 0), 0);
+             delta_rtx =  GET_CODE (XEXP (src, 0)) == PRE_INC 
+                 ? GEN_INT (GET_MODE_SIZE (GET_MODE (src))) 
+                 : GEN_INT (-GET_MODE_SIZE (GET_MODE (src))); 
+             emit_insn (TARGET_32BIT
+                        ? gen_addsi3 (breg, breg, delta_rtx)
+                        : gen_adddi3 (breg, breg, delta_rtx));
              src = gen_rtx_MEM (mode, breg);
            }
 
@@ -9759,35 +10190,34 @@ rs6000_split_multireg_move (rtx *operands)
            j = REGNO (breg) - REGNO (dst);
         }
 
-      if (GET_CODE (operands[0]) == MEM)
+      if (GET_CODE (dst) == MEM && INT_REGNO_P (reg))
        {
          rtx breg;
 
-         if (GET_CODE (XEXP (operands[0], 0)) == PRE_INC
-             || GET_CODE (XEXP (operands[0], 0)) == PRE_DEC)
+         if (GET_CODE (XEXP (dst, 0)) == PRE_INC
+             || GET_CODE (XEXP (dst, 0)) == PRE_DEC)
            {
              rtx delta_rtx;
-             breg = XEXP (XEXP (operands[0], 0), 0);
-             delta_rtx = GET_CODE (XEXP (operands[0], 0)) == PRE_INC 
-               ? GEN_INT (GET_MODE_SIZE (GET_MODE (operands[0]))) 
-               : GEN_INT (-GET_MODE_SIZE (GET_MODE (operands[0]))); 
+             breg = XEXP (XEXP (dst, 0), 0);
+             delta_rtx = GET_CODE (XEXP (dst, 0)) == PRE_INC 
+               ? GEN_INT (GET_MODE_SIZE (GET_MODE (dst))) 
+               : GEN_INT (-GET_MODE_SIZE (GET_MODE (dst))); 
 
              /* We have to update the breg before doing the store.
                 Use store with update, if available.  */
 
              if (TARGET_UPDATE)
                {
-                 insn = emit_insn (TARGET_32BIT
-                                   ? gen_movsi_update (breg, breg, delta_rtx, 
-                                       operand_subword (src, 0, 0, mode))
-                                   : gen_movdi_update (breg, breg, delta_rtx,
-                                       operand_subword (src, 0, 0, mode)));
-                 used_update = 1;
+                 rtx nsrc = simplify_gen_subreg (reg_mode, src, mode, 0);
+                 emit_insn (TARGET_32BIT
+                            ? gen_movsi_update (breg, breg, delta_rtx, nsrc)
+                            : gen_movdi_update (breg, breg, delta_rtx, nsrc));
+                 used_update = true;
                }
              else
-                 insn = emit_insn (TARGET_32BIT
-                                   ? gen_addsi3 (breg, breg, delta_rtx)
-                                   : gen_adddi3 (breg, breg, delta_rtx));
+               emit_insn (TARGET_32BIT
+                          ? gen_addsi3 (breg, breg, delta_rtx)
+                          : gen_adddi3 (breg, breg, delta_rtx));
              dst = gen_rtx_MEM (mode, breg);
            }
        }
@@ -9799,15 +10229,16 @@ rs6000_split_multireg_move (rtx *operands)
          if (j == nregs) 
            j = 0;
 
-         operands[i+2] = operand_subword (dst, j, 0, mode);
-         operands[i+2+nregs] = operand_subword (src, j, 0, mode);
-
+         /* If compiler already emited move of first word by 
+            store with update, no need to do anything.  */
          if (j == 0 && used_update)
-           {
-             /* Already emited move of first word by 
-                store with update -> emit dead insn instead (r := r).  */
-             operands[i+2] = operands[i+2+nregs];
-           }
+           continue;
+         
+         emit_insn (gen_rtx_SET (VOIDmode,
+                                 simplify_gen_subreg (reg_mode, dst, mode,
+                                                      j * reg_mode_size),
+                                 simplify_gen_subreg (reg_mode, src, mode,
+                                                      j * reg_mode_size)));
        }
     }
 }
@@ -9899,7 +10330,7 @@ compute_vrsave_mask (void)
      them in again.  More importantly, the mask we compute here is
      used to generate CLOBBERs in the set_vrsave insn, and we do not
      wish the argument registers to die.  */
-  for (i = cfun->args_info.vregno; i >= ALTIVEC_ARG_MIN_REG; --i)
+  for (i = cfun->args_info.vregno - 1; i >= ALTIVEC_ARG_MIN_REG; --i)
     mask &= ~ALTIVEC_REG_BIT (i);
 
   /* Similarly, remove the return value from the set.  */
@@ -10019,14 +10450,14 @@ is_altivec_return_reg (rtx reg, void *xyes)
 #define ABI_STACK_BOUNDARY STACK_BOUNDARY
 #endif
 
-rs6000_stack_t *
+static rs6000_stack_t *
 rs6000_stack_info (void)
 {
   static rs6000_stack_t info, zero_info;
   rs6000_stack_t *info_ptr = &info;
   int reg_size = TARGET_POWERPC64 ? 8 : 4;
   int ehrd_size;
-  int total_raw_size;
+  HOST_WIDE_INT total_raw_size;
 
   /* Zero all fields portably.  */
   info = zero_info;
@@ -10143,16 +10574,15 @@ rs6000_stack_info (void)
   else
     info_ptr->spe_gp_size = 0;
 
-  if (TARGET_ALTIVEC_ABI && TARGET_ALTIVEC_VRSAVE)
-    {
-      info_ptr->vrsave_mask = compute_vrsave_mask ();
-      info_ptr->vrsave_size  = info_ptr->vrsave_mask ? 4 : 0;
-    }
+  if (TARGET_ALTIVEC_ABI)
+    info_ptr->vrsave_mask = compute_vrsave_mask ();
   else
-    {
-      info_ptr->vrsave_mask = 0;
-      info_ptr->vrsave_size = 0;
-    }
+    info_ptr->vrsave_mask = 0;
+
+  if (TARGET_ALTIVEC_VRSAVE && info_ptr->vrsave_mask)
+    info_ptr->vrsave_size  = 4;
+  else
+    info_ptr->vrsave_size  = 0;
 
   /* Calculate the offsets.  */
   switch (DEFAULT_ABI)
@@ -10358,7 +10788,7 @@ spe_func_has_64bit_regs_p (void)
   return false;
 }
 
-void
+static void
 debug_stack_info (rs6000_stack_t *info)
 {
   const char *abi_string;
@@ -10447,13 +10877,15 @@ debug_stack_info (rs6000_stack_t *info)
     fprintf (stderr, "\tvarargs_save_offset = %5d\n", info->varargs_save_offset);
 
   if (info->total_size)
-    fprintf (stderr, "\ttotal_size          = %5d\n", info->total_size);
+    fprintf (stderr, "\ttotal_size          = "HOST_WIDE_INT_PRINT_DEC"\n",
+            info->total_size);
 
   if (info->varargs_size)
     fprintf (stderr, "\tvarargs_size        = %5d\n", info->varargs_size);
 
   if (info->vars_size)
-    fprintf (stderr, "\tvars_size           = %5d\n", info->vars_size);
+    fprintf (stderr, "\tvars_size           = "HOST_WIDE_INT_PRINT_DEC"\n",
+            info->vars_size);
 
   if (info->parm_size)
     fprintf (stderr, "\tparm_size           = %5d\n", info->parm_size);
@@ -10579,7 +11011,7 @@ rs6000_ra_ever_killed (void)
   /* regs_ever_live has LR marked as used if any sibcalls are present,
      but this should not force saving and restoring in the
      pro/epilogue.  Likewise, reg_set_between_p thinks a sibcall
-     clobbers LR, so that is inappropriate. */
+     clobbers LR, so that is inappropriate.  */
 
   /* Also, the prologue can generate a store into LR that
      doesn't really count, like this:
@@ -10726,22 +11158,58 @@ rs6000_emit_load_toc_table (int fromprolog)
     abort ();
 }
 
+/* Emit instructions to restore the link register after determining where
+   its value has been stored.  */
+
+void
+rs6000_emit_eh_reg_restore (rtx source, rtx scratch)
+{
+  rs6000_stack_t *info = rs6000_stack_info ();
+  rtx operands[2];
+
+  operands[0] = source;
+  operands[1] = scratch;
+
+  if (info->lr_save_p)
+    {
+      rtx frame_rtx = stack_pointer_rtx;
+      HOST_WIDE_INT sp_offset = 0;
+      rtx tmp;
+
+      if (frame_pointer_needed
+         || current_function_calls_alloca
+         || info->total_size > 32767)
+       {
+         emit_move_insn (operands[1], gen_rtx_MEM (Pmode, frame_rtx));
+         frame_rtx = operands[1];
+       }
+      else if (info->push_p)
+       sp_offset = info->total_size;
+
+      tmp = plus_constant (frame_rtx, info->lr_save_offset + sp_offset);
+      tmp = gen_rtx_MEM (Pmode, tmp);
+      emit_move_insn (tmp, operands[0]);
+    }
+  else
+    emit_move_insn (gen_rtx_REG (Pmode, LINK_REGISTER_REGNUM), operands[0]);
+}
+
+static GTY(()) int set = -1;
+
 int   
 get_TOC_alias_set (void)
 {
-    static int set = -1;
-    if (set == -1)
-      set = new_alias_set ();
-    return set;
+  if (set == -1)
+    set = new_alias_set ();
+  return set;
 }   
 
 /* This returns nonzero if the current function uses the TOC.  This is
-   determined by the presence of (unspec ... UNSPEC_TOC) or
-   use (unspec ... UNSPEC_TOC), which are generated by the various
-   load_toc_* patterns.  */
-
-int
-uses_TOC () 
+   determined by the presence of (use (unspec ... UNSPEC_TOC)), which
+   is generated by the ABI_V4 load_toc_* patterns.  */
+#if TARGET_ELF
+static int
+uses_TOC (void) 
 {
   rtx insn;
 
@@ -10766,6 +11234,7 @@ uses_TOC ()
       }
   return 0;
 }
+#endif
 
 rtx
 create_TOC_reference (rtx symbol) 
@@ -11124,7 +11593,7 @@ generate_set_vrsave (rtx reg, rs6000_stack_t *info, int epiloguep)
 
 static void
 emit_frame_save (rtx frame_reg, rtx frame_ptr, enum machine_mode mode, 
-                unsigned int regno, int offset, int total_size)
+                unsigned int regno, int offset, HOST_WIDE_INT total_size)
 {
   rtx reg, offset_rtx, insn, mem, addr, int_rtx;
   rtx replacea, replaceb;
@@ -11189,12 +11658,12 @@ void
 rs6000_emit_prologue (void)
 {
   rs6000_stack_t *info = rs6000_stack_info ();
-  enum machine_mode reg_mode = TARGET_POWERPC64 ? DImode : SImode;
-  int reg_size = TARGET_POWERPC64 ? 8 : 4;
+  enum machine_mode reg_mode = Pmode;
+  int reg_size = UNITS_PER_WORD;
   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;
-  rtx cr_save_rtx = NULL;
+  rtx cr_save_rtx = NULL_RTX;
   rtx insn;
   int saving_FPRs_inline;
   int using_store_multiple;
@@ -11276,7 +11745,8 @@ rs6000_emit_prologue (void)
      used in this function, and do the corresponding magic in the
      epilogue.  */
 
-  if (TARGET_ALTIVEC && info->vrsave_mask != 0)
+  if (TARGET_ALTIVEC && TARGET_ALTIVEC_VRSAVE
+      && info->vrsave_mask != 0)
     {
       rtx reg, mem, vrsave;
       int offset;
@@ -11617,7 +12087,7 @@ rs6000_output_function_prologue (FILE *file,
       rs6000_emit_prologue ();
       emit_note (NOTE_INSN_DELETED);
 
-      /* Expand INSN_ADDRESSES so final() doesn't crash. */
+      /* Expand INSN_ADDRESSES so final() doesn't crash.  */
       {
        rtx insn;
        unsigned addr = 0;
@@ -11655,8 +12125,8 @@ rs6000_emit_epilogue (int sibcall)
   int sp_offset = 0;
   rtx sp_reg_rtx = gen_rtx_REG (Pmode, 1);
   rtx frame_reg_rtx = sp_reg_rtx;
-  enum machine_mode reg_mode = TARGET_POWERPC64 ? DImode : SImode;
-  int reg_size = TARGET_POWERPC64 ? 8 : 4;
+  enum machine_mode reg_mode = Pmode;
+  int reg_size = UNITS_PER_WORD;
   int i;
 
   info = rs6000_stack_info ();
@@ -11738,7 +12208,8 @@ rs6000_emit_epilogue (int sibcall)
     }
 
   /* Restore VRSAVE if needed.  */
-  if (TARGET_ALTIVEC_ABI && info->vrsave_mask != 0)
+  if (TARGET_ALTIVEC_ABI && TARGET_ALTIVEC_VRSAVE 
+      && info->vrsave_mask != 0)
     {
       rtx addr, mem, reg;
 
@@ -12049,7 +12520,7 @@ rs6000_output_function_epilogue (FILE *file,
          rs6000_emit_epilogue (FALSE);
          emit_note (NOTE_INSN_DELETED);
 
-         /* Expand INSN_ADDRESSES so final() doesn't crash. */
+         /* Expand INSN_ADDRESSES so final() doesn't crash.  */
          {
            rtx insn;
            unsigned addr = 0;
@@ -12067,7 +12538,8 @@ rs6000_output_function_epilogue (FILE *file,
        }
     }
 
-#if TARGET_OBJECT_FORMAT == OBJECT_MACHO
+#if TARGET_MACHO
+  macho_branch_islands ();
   /* Mach-O doesn't support labels at the end of objects, so if
      it looks like we might want one, insert a NOP.  */
   {
@@ -12142,27 +12614,25 @@ rs6000_output_function_epilogue (FILE *file,
       /* Tbtab format type.  Use format type 0.  */
       fputs ("\t.byte 0,", file);
 
-      /* Language type.  Unfortunately, there doesn't seem to be any
-        official way to get this info, so we use language_string.  C
-        is 0.  C++ is 9.  No number defined for Obj-C, so use the
-        value for C for now.  There is no official value for Java,
-         although IBM appears to be using 13.  There is no official value
-        for Chill, so we've chosen 44 pseudo-randomly.  */
-      if (! strcmp (language_string, "GNU C")
-         || ! strcmp (language_string, "GNU Objective-C"))
+      /* Language type.  Unfortunately, there does not seem to be any
+        official way to discover the language being compiled, so we
+        use language_string.
+        C is 0.  Fortran is 1.  Pascal is 2.  Ada is 3.  C++ is 9.
+        Java is 13.  Objective-C is 14.  */
+      if (! strcmp (language_string, "GNU C"))
        i = 0;
       else if (! strcmp (language_string, "GNU F77"))
        i = 1;
-      else if (! strcmp (language_string, "GNU Ada"))
-       i = 3;
       else if (! strcmp (language_string, "GNU Pascal"))
        i = 2;
+      else if (! strcmp (language_string, "GNU Ada"))
+       i = 3;
       else if (! strcmp (language_string, "GNU C++"))
        i = 9;
       else if (! strcmp (language_string, "GNU Java"))
        i = 13;
-      else if (! strcmp (language_string, "GNU CHILL"))
-       i = 44;
+      else if (! strcmp (language_string, "GNU Objective-C"))
+       i = 14;
       else
        abort ();
       fprintf (file, "%d,", i);
@@ -13134,7 +13604,7 @@ output_function_profiler (FILE *file, int labelno)
     case ABI_DARWIN:
       if (!TARGET_PROFILE_KERNEL)
        {
-         /* Don't do anything, done in output_profile_hook (). */
+         /* Don't do anything, done in output_profile_hook ().  */
        }
       else
        {
@@ -13183,20 +13653,10 @@ rs6000_variable_issue (FILE *stream ATTRIBUTE_UNUSED,
 
   if (rs6000_cpu == PROCESSOR_POWER4)
     {
-      enum attr_type type = get_attr_type (insn);
-      if (type == TYPE_LOAD_EXT_U || type == TYPE_LOAD_EXT_UX
-         || type == TYPE_LOAD_UX || type == TYPE_STORE_UX
-         || type == TYPE_MFCR)
-       return 0;
-      else if (type == TYPE_LOAD_U || type == TYPE_STORE_U
-              || type == TYPE_FPLOAD_U || type == TYPE_FPSTORE_U
-              || type == TYPE_FPLOAD_UX || type == TYPE_FPSTORE_UX
-              || type == TYPE_LOAD_EXT || type == TYPE_DELAYED_CR
-              || type == TYPE_COMPARE || type == TYPE_DELAYED_COMPARE
-              || type == TYPE_IMUL_COMPARE || type == TYPE_LMUL_COMPARE
-              || type == TYPE_IDIV || type == TYPE_LDIV
-              || type == TYPE_INSERT_WORD)
-       return more > 2 ? more - 2 : 0;
+      if (is_microcoded_insn (insn))
+        return 0;
+      else if (is_cracked_insn (insn))
+        return more > 2 ? more - 2 : 0;
     }
 
   return more - 1;
@@ -13260,13 +13720,38 @@ rs6000_adjust_cost (rtx insn, rtx link, rtx dep_insn ATTRIBUTE_UNUSED,
   return cost;
 }
 
-/* The function returns a non-zero value if INSN can be scheduled only
-   as the first insn in a dispatch group ("dispatch-slot restricted"). 
-   In this case, the returned value indicates how many dispatch slots 
-   the insn occupies (at the beginning of the group). 
+/* The function returns a true if INSN is microcoded.
+   Return false otherwise.  */
+
+static bool
+is_microcoded_insn (rtx insn)
+{
+  if (!insn || !INSN_P (insn)
+      || GET_CODE (PATTERN (insn)) == USE
+      || GET_CODE (PATTERN (insn)) == CLOBBER)
+    return false;
+
+  if (rs6000_cpu == PROCESSOR_POWER4)
+    {
+      enum attr_type type = get_attr_type (insn);
+      if (type == TYPE_LOAD_EXT_U
+         || type == TYPE_LOAD_EXT_UX
+         || type == TYPE_LOAD_UX
+         || type == TYPE_STORE_UX
+         || type == TYPE_MFCR)
+        return true;
+    }
+
+  return false;
+}
+
+/* The function returns a nonzero value if INSN can be scheduled only
+   as the first insn in a dispatch group ("dispatch-slot restricted").
+   In this case, the returned value indicates how many dispatch slots
+   the insn occupies (at the beginning of the group).
    Return 0 otherwise.  */
 
-static int 
+static int
 is_dispatch_slot_restricted (rtx insn)
 {
   enum attr_type type;
@@ -13300,6 +13785,55 @@ is_dispatch_slot_restricted (rtx insn)
   }
 }
 
+/* The function returns true if INSN is cracked into 2 instructions
+   by the processor (and therefore occupies 2 issue slots).  */
+
+static bool
+is_cracked_insn (rtx insn)
+{
+  if (!insn || !INSN_P (insn)
+      || GET_CODE (PATTERN (insn)) == USE
+      || GET_CODE (PATTERN (insn)) == CLOBBER)
+    return false;
+
+  if (rs6000_cpu == PROCESSOR_POWER4)
+    {
+      enum attr_type type = get_attr_type (insn);
+      if (type == TYPE_LOAD_U || type == TYPE_STORE_U
+              || type == TYPE_FPLOAD_U || type == TYPE_FPSTORE_U
+              || type == TYPE_FPLOAD_UX || type == TYPE_FPSTORE_UX
+              || type == TYPE_LOAD_EXT || type == TYPE_DELAYED_CR
+              || type == TYPE_COMPARE || type == TYPE_DELAYED_COMPARE
+              || type == TYPE_IMUL_COMPARE || type == TYPE_LMUL_COMPARE
+              || type == TYPE_IDIV || type == TYPE_LDIV
+              || type == TYPE_INSERT_WORD)
+        return true;
+    }
+
+  return false;
+}
+
+/* The function returns true if INSN can be issued only from
+   the branch slot.  */
+
+static bool
+is_branch_slot_insn (rtx insn)
+{
+  if (!insn || !INSN_P (insn)
+      || GET_CODE (PATTERN (insn)) == USE
+      || GET_CODE (PATTERN (insn)) == CLOBBER)
+    return false;
+
+  if (rs6000_cpu == PROCESSOR_POWER4)
+    {
+      enum attr_type type = get_attr_type (insn);
+      if (type == TYPE_BRANCH || type == TYPE_JMPREG)
+       return true;     
+      return false;
+    }
+
+  return false;
+}
 
 /* A C statement (sans semicolon) to update the integer scheduling
    priority INSN_PRIORITY (INSN). Increase the priority to execute the
@@ -13388,8 +13922,9 @@ rs6000_issue_rate (void)
   case CPU_PPC604E:
   case CPU_PPC620:
   case CPU_PPC630:
-  case CPU_POWER4:
     return 4;
+  case CPU_POWER4:
+    return 5;
   default:
     return 1;
   }
@@ -13544,7 +14079,426 @@ rs6000_is_costly_dependence (rtx insn, rtx next, rtx link, int cost, int distanc
   return false;
 }
 
+/* Return the next insn after INSN that is found before TAIL is reached, 
+   skipping any "non-active" insns - insns that will not actually occupy
+   an issue slot.  Return NULL_RTX if such an insn is not found.  */
+
+static rtx
+get_next_active_insn (rtx insn, rtx tail)
+{
+  rtx next_insn;
+
+  if (!insn || insn == tail)
+    return NULL_RTX;
+
+  next_insn = NEXT_INSN (insn);
+
+  while (next_insn
+        && next_insn != tail
+        && (GET_CODE(next_insn) == NOTE
+            || GET_CODE (PATTERN (next_insn)) == USE
+            || GET_CODE (PATTERN (next_insn)) == CLOBBER))
+    {
+      next_insn = NEXT_INSN (next_insn);
+    }
+
+  if (!next_insn || next_insn == tail)
+    return NULL_RTX;
+
+  return next_insn;
+}
+
+/* Return whether the presence of INSN causes a dispatch group termination
+   of group WHICH_GROUP.
+
+   If WHICH_GROUP == current_group, this function will return true if INSN
+   causes the termination of the current group (i.e, the dispatch group to
+   which INSN belongs). This means that INSN will be the last insn in the
+   group it belongs to.
+
+   If WHICH_GROUP == previous_group, this function will return true if INSN
+   causes the termination of the previous group (i.e, the dispatch group that
+   precedes the group to which INSN belongs).  This means that INSN will be
+   the first insn in the group it belongs to).  */
+
+static bool
+insn_terminates_group_p (rtx insn, enum group_termination which_group)
+{
+  enum attr_type type;
+
+  if (! insn)
+    return false;
+
+  type = get_attr_type (insn);
+
+  if (is_microcoded_insn (insn))
+    return true;
+
+  if (which_group == current_group)
+    {
+      if (is_branch_slot_insn (insn))
+        return true;
+      return false;
+    }
+  else if (which_group == previous_group)
+    {
+      if (is_dispatch_slot_restricted (insn))
+        return true;
+      return false;
+    }
+
+  return false;
+}
+
+/* Return true if it is recommended to keep NEXT_INSN "far" (in a separate
+   dispatch group) from the insns in GROUP_INSNS.  Return false otherwise.  */
+
+static bool
+is_costly_group (rtx *group_insns, rtx next_insn)
+{
+  int i;
+  rtx link;
+  int cost;
+  int issue_rate = rs6000_issue_rate ();
+
+  for (i = 0; i < issue_rate; i++)
+    {
+      rtx insn = group_insns[i];
+      if (!insn)
+        continue;
+      for (link = INSN_DEPEND (insn); link != 0; link = XEXP (link, 1))
+        {
+          rtx next = XEXP (link, 0);
+          if (next == next_insn)
+            {
+              cost = insn_cost (insn, link, next_insn);
+              if (rs6000_is_costly_dependence (insn, next_insn, link, cost, 0))
+                return true;
+            }
+        }
+    }
+
+  return false;
+}
+
+/* Utility of the function redefine_groups. 
+   Check if it is too costly to schedule NEXT_INSN together with GROUP_INSNS
+   in the same dispatch group.  If so, insert nops before NEXT_INSN, in order
+   to keep it "far" (in a separate group) from GROUP_INSNS, following
+   one of the following schemes, depending on the value of the flag
+   -minsert_sched_nops = X:
+   (1) X == sched_finish_regroup_exact: insert exactly as many nops as needed
+       in order to force NEXT_INSN into a separate group.
+   (2) X < sched_finish_regroup_exact: insert exactly X nops.  
+   GROUP_END, CAN_ISSUE_MORE and GROUP_COUNT record the state after nop 
+   insertion (has a group just ended, how many vacant issue slots remain in the
+   last group, and how many dispatch groups were encountered so far).  */
+
+static int 
+force_new_group (int sched_verbose, FILE *dump, rtx *group_insns, rtx next_insn,
+                bool *group_end, int can_issue_more, int *group_count)
+{
+  rtx nop;
+  bool force;
+  int issue_rate = rs6000_issue_rate ();
+  bool end = *group_end;
+  int i;
+
+  if (next_insn == NULL_RTX)
+    return can_issue_more;
+
+  if (rs6000_sched_insert_nops > sched_finish_regroup_exact)
+    return can_issue_more;
+
+  force = is_costly_group (group_insns, next_insn);
+  if (!force)
+    return can_issue_more;
+
+  if (sched_verbose > 6)
+    fprintf (dump,"force: group count = %d, can_issue_more = %d\n",
+                       *group_count ,can_issue_more);
+
+  if (rs6000_sched_insert_nops == sched_finish_regroup_exact)
+    {
+      if (*group_end)
+        can_issue_more = 0;
+
+      /* Since only a branch can be issued in the last issue_slot, it is
+        sufficient to insert 'can_issue_more - 1' nops if next_insn is not
+        a branch. If next_insn is a branch, we insert 'can_issue_more' nops;
+        in this case the last nop will start a new group and the branch will be
+        forced to the new group.  */
+      if (can_issue_more && !is_branch_slot_insn (next_insn))
+        can_issue_more--;
+
+      while (can_issue_more > 0)
+        {
+          nop = gen_nop();
+          emit_insn_before (nop, next_insn);
+          can_issue_more--;
+        }
+
+      *group_end = true;
+      return 0;
+    } 
+
+  if (rs6000_sched_insert_nops < sched_finish_regroup_exact)
+    {
+      int n_nops = rs6000_sched_insert_nops;
+
+      /* Nops can't be issued from the branch slot, so the effective 
+         issue_rate for nops is 'issue_rate - 1'.  */
+      if (can_issue_more == 0)
+        can_issue_more = issue_rate;
+      can_issue_more--;
+      if (can_issue_more == 0)
+        {
+          can_issue_more = issue_rate - 1;
+          (*group_count)++;
+          end = true;
+          for (i = 0; i < issue_rate; i++)
+            {
+              group_insns[i] = 0;
+            }
+        }
+
+      while (n_nops > 0)
+        {
+          nop = gen_nop ();
+          emit_insn_before (nop, next_insn);
+          if (can_issue_more == issue_rate - 1) /* new group begins */
+            end = false;
+          can_issue_more--;
+          if (can_issue_more == 0)
+            {
+              can_issue_more = issue_rate - 1;
+              (*group_count)++;
+              end = true;
+              for (i = 0; i < issue_rate; i++)
+                {
+                  group_insns[i] = 0;
+                } 
+            }  
+          n_nops--;
+        }
+
+      /* Scale back relative to 'issue_rate' (instead of 'issue_rate - 1').  */
+      can_issue_more++; 
+
+      *group_end = /* Is next_insn going to start a new group?  */
+         (end 
+          || (can_issue_more == 1 && !is_branch_slot_insn (next_insn))
+          || (can_issue_more <= 2 && is_cracked_insn (next_insn))
+          || (can_issue_more < issue_rate &&
+             insn_terminates_group_p (next_insn, previous_group)));
+      if (*group_end && end)
+        (*group_count)--;
+
+      if (sched_verbose > 6)
+        fprintf (dump, "done force: group count = %d, can_issue_more = %d\n",
+                       *group_count, can_issue_more);
+      return can_issue_more;   
+    } 
+
+  return can_issue_more;
+}
+
+/* This function tries to synch the dispatch groups that the compiler "sees"
+   with the dispatch groups that the processor dispatcher is expected to 
+   form in practice.  It tries to achieve this synchronization by forcing the
+   estimated processor grouping on the compiler (as opposed to the function
+   'pad_goups' which tries to force the scheduler's grouping on the processor).
+
+   The function scans the insn sequence between PREV_HEAD_INSN and TAIL and
+   examines the (estimated) dispatch groups that will be formed by the processor
+   dispatcher.  It marks these group boundaries to reflect the estimated
+   processor grouping, overriding the grouping that the scheduler had marked.
+   Depending on the value of the flag '-minsert-sched-nops' this function can
+   force certain insns into separate groups or force a certain distance between
+   them by inserting nops, for example, if there exists a "costly dependence"
+   between the insns.
+
+   The function estimates the group boundaries that the processor will form as
+   folllows:  It keeps track of how many vacant issue slots are available after
+   each insn.  A subsequent insn will start a new group if one of the following
+   4 cases applies:
+   - no more vacant issue slots remain in the current dispatch group.
+   - only the last issue slot, which is the branch slot, is vacant, but the next
+     insn is not a branch.
+   - only the last 2 or less issue slots, including the branch slot, are vacant,
+     which means that a cracked insn (which occupies two issue slots) can't be
+     issued in this group.
+   - less than 'issue_rate' slots are vacant, and the next insn always needs to 
+     start a new group.  */
+
+static int
+redefine_groups (FILE *dump, int sched_verbose, rtx prev_head_insn, rtx tail)
+{
+  rtx insn, next_insn;
+  int issue_rate;
+  int can_issue_more;
+  int slot, i;
+  bool group_end;
+  int group_count = 0;
+  rtx *group_insns;
+
+  /* Initialize.  */
+  issue_rate = rs6000_issue_rate ();
+  group_insns = alloca (issue_rate * sizeof (rtx));
+  for (i = 0; i < issue_rate; i++) 
+    {
+      group_insns[i] = 0;
+    }
+  can_issue_more = issue_rate;
+  slot = 0;
+  insn = get_next_active_insn (prev_head_insn, tail);
+  group_end = false;
+
+  while (insn != NULL_RTX)
+    {
+      slot = (issue_rate - can_issue_more);
+      group_insns[slot] = insn;
+      can_issue_more =
+        rs6000_variable_issue (dump, sched_verbose, insn, can_issue_more);
+      if (insn_terminates_group_p (insn, current_group))
+        can_issue_more = 0;
+
+      next_insn = get_next_active_insn (insn, tail);
+      if (next_insn == NULL_RTX)
+        return group_count + 1;
+
+      group_end = /* Is next_insn going to start a new group?  */
+        (can_issue_more == 0
+         || (can_issue_more == 1 && !is_branch_slot_insn (next_insn))
+         || (can_issue_more <= 2 && is_cracked_insn (next_insn))
+         || (can_issue_more < issue_rate &&
+             insn_terminates_group_p (next_insn, previous_group)));
+
+      can_issue_more = force_new_group (sched_verbose, dump, group_insns, 
+                       next_insn, &group_end, can_issue_more, &group_count);
+
+      if (group_end)
+        {
+          group_count++;
+          can_issue_more = 0;
+          for (i = 0; i < issue_rate; i++)
+            {
+              group_insns[i] = 0;
+            }
+        }
+
+      if (GET_MODE (next_insn) == TImode && can_issue_more)
+        PUT_MODE(next_insn, VOIDmode);
+      else if (!can_issue_more && GET_MODE (next_insn) != TImode)
+        PUT_MODE (next_insn, TImode);
+
+      insn = next_insn;
+      if (can_issue_more == 0)
+        can_issue_more = issue_rate;
+   } /* while */
+
+  return group_count;
+}
+
+/* Scan the insn sequence between PREV_HEAD_INSN and TAIL and examine the
+   dispatch group boundaries that the scheduler had marked.  Pad with nops
+   any dispatch groups which have vacant issue slots, in order to force the
+   scheduler's grouping on the processor dispatcher.  The function
+   returns the number of dispatch groups found.  */
+
+static int
+pad_groups (FILE *dump, int sched_verbose, rtx prev_head_insn, rtx tail)
+{
+  rtx insn, next_insn;
+  rtx nop;
+  int issue_rate;
+  int can_issue_more;
+  int group_end;
+  int group_count = 0;
+
+  /* Initialize issue_rate.  */
+  issue_rate = rs6000_issue_rate ();
+  can_issue_more = issue_rate;
+
+  insn = get_next_active_insn (prev_head_insn, tail);
+  next_insn = get_next_active_insn (insn, tail);
+
+  while (insn != NULL_RTX)
+    {
+      can_issue_more =
+       rs6000_variable_issue (dump, sched_verbose, insn, can_issue_more);
+
+      group_end = (next_insn == NULL_RTX || GET_MODE (next_insn) == TImode);
+
+      if (next_insn == NULL_RTX)
+        break;
+
+      if (group_end)
+        {
+          /* If the scheduler had marked group termination at this location
+             (between insn and next_indn), and neither insn nor next_insn will
+             force group termination, pad the group with nops to force group
+             termination.  */
+          if (can_issue_more
+              && (rs6000_sched_insert_nops == sched_finish_pad_groups)
+              && !insn_terminates_group_p (insn, current_group)
+              && !insn_terminates_group_p (next_insn, previous_group))
+            {
+              if (!is_branch_slot_insn(next_insn))
+                can_issue_more--;
+
+              while (can_issue_more)
+                {
+                  nop = gen_nop ();
+                  emit_insn_before (nop, next_insn);
+                  can_issue_more--;
+                }
+            }
+
+          can_issue_more = issue_rate;
+          group_count++;
+        }
+
+      insn = next_insn;
+      next_insn = get_next_active_insn (insn, tail);
+    }
+
+  return group_count;
+}
+
+/* The following function is called at the end of scheduling BB.
+   After reload, it inserts nops at insn group bundling.  */
+
+static void
+rs6000_sched_finish (FILE *dump, int sched_verbose)
+{
+  int n_groups;
+
+  if (sched_verbose)
+    fprintf (dump, "=== Finishing schedule.\n");
+
+  if (reload_completed && rs6000_cpu == PROCESSOR_POWER4)
+    {
+      if (rs6000_sched_insert_nops == sched_finish_none)
+        return;
+
+      if (rs6000_sched_insert_nops == sched_finish_pad_groups)
+        n_groups = pad_groups (dump, sched_verbose,
+                               current_sched_info->prev_head,
+                               current_sched_info->next_tail);
+      else
+        n_groups = redefine_groups (dump, sched_verbose,
+                               current_sched_info->prev_head,
+                               current_sched_info->next_tail);
 
+      if (sched_verbose >= 6)
+       {
+         fprintf (dump, "ngroups = %d\n", n_groups);
+         print_rtl (dump, current_sched_info->prev_head);
+         fprintf (dump, "Done finish_sched\n");
+       }
+    }
+}
 \f
 /* Length in units of the trampoline for entering a nested function.  */
 
@@ -13870,83 +14824,118 @@ symbolic_operand (rtx op)
 }
 #endif
 
-#ifdef RS6000_LONG_BRANCH
+#if TARGET_MACHO
 
-static tree stub_list = 0;
+static tree branch_island_list = 0;
 
-/* ADD_COMPILER_STUB adds the compiler generated stub for handling 
-   procedure calls to the linked list.  */
+/* Remember to generate a branch island for far calls to the given
+   function.  */
 
-void 
-add_compiler_stub (tree label_name, tree function_name, int line_number)
+static void 
+add_compiler_branch_island (tree label_name, tree function_name, int line_number)
 {
-  tree stub = build_tree_list (function_name, label_name);
-  TREE_TYPE (stub) = build_int_2 (line_number, 0);
-  TREE_CHAIN (stub) = stub_list;
-  stub_list = stub;
+  tree branch_island = build_tree_list (function_name, label_name);
+  TREE_TYPE (branch_island) = build_int_2 (line_number, 0);
+  TREE_CHAIN (branch_island) = branch_island_list;
+  branch_island_list = branch_island;
 }
 
-#define STUB_LABEL_NAME(STUB)     TREE_VALUE (STUB)
-#define STUB_FUNCTION_NAME(STUB)  TREE_PURPOSE (STUB)
-#define STUB_LINE_NUMBER(STUB)    TREE_INT_CST_LOW (TREE_TYPE (STUB))
-
-/* OUTPUT_COMPILER_STUB outputs the compiler generated stub for
-   handling procedure calls from the linked list and initializes the
-   linked list.  */
+#define BRANCH_ISLAND_LABEL_NAME(BRANCH_ISLAND)     TREE_VALUE (BRANCH_ISLAND)
+#define BRANCH_ISLAND_FUNCTION_NAME(BRANCH_ISLAND)  TREE_PURPOSE (BRANCH_ISLAND)
+#define BRANCH_ISLAND_LINE_NUMBER(BRANCH_ISLAND)    \
+               TREE_INT_CST_LOW (TREE_TYPE (BRANCH_ISLAND))
 
-void
-output_compiler_stub (void)
-{
-  char tmp_buf[256];
-  char label_buf[256];
-  tree stub;
-
-  if (!flag_pic)
-    for (stub = stub_list; stub; stub = TREE_CHAIN (stub))
-      {
-       fprintf (asm_out_file,
-                "%s:\n", IDENTIFIER_POINTER(STUB_LABEL_NAME(stub)));
+/* Generate far-jump branch islands for everything on the
+   branch_island_list.  Invoked immediately after the last instruction
+   of the epilogue has been emitted; the branch-islands must be
+   appended to, and contiguous with, the function body.  Mach-O stubs
+   are generated in machopic_output_stub().  */
 
+static void
+macho_branch_islands (void)
+{
+  char tmp_buf[512];
+  tree branch_island;
+
+  for (branch_island = branch_island_list;
+       branch_island;
+       branch_island = TREE_CHAIN (branch_island))
+    {
+      const char *label =
+       IDENTIFIER_POINTER (BRANCH_ISLAND_LABEL_NAME (branch_island));
+      const char *name  =
+       darwin_strip_name_encoding (
+         IDENTIFIER_POINTER (BRANCH_ISLAND_FUNCTION_NAME (branch_island)));
+      char name_buf[512];
+      /* Cheap copy of the details from the Darwin ASM_OUTPUT_LABELREF().  */
+      if (name[0] == '*' || name[0] == '&')
+       strcpy (name_buf, name+1);
+      else
+       {
+         name_buf[0] = '_';
+         strcpy (name_buf+1, name);
+       }
+      strcpy (tmp_buf, "\n");
+      strcat (tmp_buf, label);
 #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
-       if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
-         fprintf (asm_out_file, "\t.stabd 68,0,%d\n", STUB_LINE_NUMBER(stub));
+      if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+       fprintf (asm_out_file, "\t.stabd 68,0," HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+                BRANCH_ISLAND_LINE_NUMBER(branch_island));
 #endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
-
-       if (IDENTIFIER_POINTER (STUB_FUNCTION_NAME (stub))[0] == '*')
-         strcpy (label_buf,
-                 IDENTIFIER_POINTER (STUB_FUNCTION_NAME (stub))+1);
-       else
-         {
-           label_buf[0] = '_';
-           strcpy (label_buf+1,
-                   IDENTIFIER_POINTER (STUB_FUNCTION_NAME (stub)));
-         }
-
-       strcpy (tmp_buf, "lis r12,hi16(");
-       strcat (tmp_buf, label_buf);
-       strcat (tmp_buf, ")\n\tori r12,r12,lo16(");
-       strcat (tmp_buf, label_buf);
-       strcat (tmp_buf, ")\n\tmtctr r12\n\tbctr");
-       output_asm_insn (tmp_buf, 0);
-
+      if (flag_pic)
+       {
+         strcat (tmp_buf, ":\n\tmflr r0\n\tbcl 20,31,");
+         strcat (tmp_buf, label);
+         strcat (tmp_buf, "_pic\n");
+         strcat (tmp_buf, label);
+         strcat (tmp_buf, "_pic:\n\tmflr r11\n");
+         strcat (tmp_buf, "\taddis r11,r11,ha16(");
+         strcat (tmp_buf, name_buf);
+         strcat (tmp_buf, " - ");
+         strcat (tmp_buf, label);
+         strcat (tmp_buf, "_pic)\n");
+                  
+         strcat (tmp_buf, "\tmtlr r0\n");
+  
+         strcat (tmp_buf, "\taddi r12,r11,lo16(");
+         strcat (tmp_buf, name_buf);
+         strcat (tmp_buf, " - ");
+         strcat (tmp_buf, label);
+         strcat (tmp_buf, "_pic)\n");
+         strcat (tmp_buf, "\tmtctr r12\n\tbctr\n");
+       }
+      else
+       {
+         strcat (tmp_buf, ":\nlis r12,hi16(");
+         strcat (tmp_buf, name_buf);
+         strcat (tmp_buf, ")\n\tori r12,r12,lo16(");
+         strcat (tmp_buf, name_buf);
+         strcat (tmp_buf, ")\n\tmtctr r12\n\tbctr");
+       }
+      output_asm_insn (tmp_buf, 0);
 #if defined (DBX_DEBUGGING_INFO) || defined (XCOFF_DEBUGGING_INFO)
-       if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
-         fprintf(asm_out_file, "\t.stabd 68,0,%d\n", STUB_LINE_NUMBER (stub));
+      if (write_symbols == DBX_DEBUG || write_symbols == XCOFF_DEBUG)
+       fprintf(asm_out_file, "\t.stabd 68,0," HOST_WIDE_INT_PRINT_UNSIGNED "\n",
+               BRANCH_ISLAND_LINE_NUMBER (branch_island));
 #endif /* DBX_DEBUGGING_INFO || XCOFF_DEBUGGING_INFO */
-      }
+    }
 
-  stub_list = 0;
+  branch_island_list = 0;
 }
 
 /* NO_PREVIOUS_DEF checks in the link list whether the function name is
    already there or not.  */
 
-int
+static int
 no_previous_def (tree function_name)
 {
-  tree stub;
-  for (stub = stub_list; stub; stub = TREE_CHAIN (stub))
-    if (function_name == STUB_FUNCTION_NAME (stub))
+  tree branch_island;
+  for (branch_island = branch_island_list;
+       branch_island;
+       branch_island = TREE_CHAIN (branch_island))
+    if (function_name == BRANCH_ISLAND_FUNCTION_NAME (branch_island))
       return 0;
   return 1;
 }
@@ -13954,13 +14943,15 @@ no_previous_def (tree function_name)
 /* GET_PREV_LABEL gets the label name from the previous definition of
    the function.  */
 
-tree
+static tree
 get_prev_label (tree function_name)
 {
-  tree stub;
-  for (stub = stub_list; stub; stub = TREE_CHAIN (stub))
-    if (function_name == STUB_FUNCTION_NAME (stub))
-      return STUB_LABEL_NAME (stub);
+  tree branch_island;
+  for (branch_island = branch_island_list;
+       branch_island;
+       branch_island = TREE_CHAIN (branch_island))
+    if (function_name == BRANCH_ISLAND_FUNCTION_NAME (branch_island))
+      return BRANCH_ISLAND_LABEL_NAME (branch_island);
   return 0;
 }
 
@@ -13970,13 +14961,14 @@ get_prev_label (tree function_name)
    CALL_DEST is the routine we are calling.  */
 
 char *
-output_call (rtx insn, rtx call_dest, int operand_number)
+output_call (rtx insn, rtx *operands, int dest_operand_number, int cookie_operand_number)
 {
   static char buf[256];
-  if (GET_CODE (call_dest) == SYMBOL_REF && TARGET_LONG_BRANCH && !flag_pic)
+  if (GET_CODE (operands[dest_operand_number]) == SYMBOL_REF
+      && (INTVAL (operands[cookie_operand_number]) & CALL_LONG))
     {
       tree labelname;
-      tree funname = get_identifier (XSTR (call_dest, 0));
+      tree funname = get_identifier (XSTR (operands[dest_operand_number], 0));
       
       if (no_previous_def (funname))
        {
@@ -13990,23 +14982,25 @@ output_call (rtx insn, rtx call_dest, int operand_number)
          for (; insn && GET_CODE (insn) != NOTE; insn = PREV_INSN (insn));
          if (insn)
            line_number = NOTE_LINE_NUMBER (insn);
-         add_compiler_stub (labelname, funname, line_number);
+         add_compiler_branch_island (labelname, funname, line_number);
        }
       else
        labelname = get_prev_label (funname);
 
+      /* "jbsr foo, L42" is Mach-O for "Link as 'bl foo' if a 'bl'
+        instruction will reach 'foo', otherwise link as 'bl L42'".
+        "L42" should be a 'branch island', that will do a far jump to
+        'foo'.  Branch islands are generated in
+        macho_branch_islands().  */
       sprintf (buf, "jbsr %%z%d,%.246s",
-              operand_number, IDENTIFIER_POINTER (labelname));
-      return buf;
+              dest_operand_number, IDENTIFIER_POINTER (labelname));
     }
   else
-    {
-      sprintf (buf, "bl %%z%d", operand_number);
-      return buf;
-    }
+    sprintf (buf, "bl %%z%d", dest_operand_number);
+  return buf;
 }
 
-#endif /* RS6000_LONG_BRANCH */
+#endif /* TARGET_MACHO */
 
 /* Generate PIC and indirect symbol stubs.  */
 
@@ -14093,9 +15087,13 @@ rs6000_machopic_legitimize_pic_address (rtx orig, enum machine_mode mode,
 
       if (GET_CODE (XEXP (orig, 0)) == PLUS)
        {
+         /* Use a different reg for the intermediate value, as
+            it will be marked UNCHANGING.  */
+         rtx reg_temp = no_new_pseudos ? reg : gen_reg_rtx (Pmode);
+
          base =
            rs6000_machopic_legitimize_pic_address (XEXP (XEXP (orig, 0), 0),
-                                                   Pmode, reg);
+                                                   Pmode, reg_temp);
          offset =
            rs6000_machopic_legitimize_pic_address (XEXP (XEXP (orig, 0), 1),
                                                    Pmode, reg);
@@ -14138,14 +15136,8 @@ toc_section (void)
 static unsigned int
 rs6000_elf_section_type_flags (tree decl, const char *name, int reloc)
 {
-  unsigned int flags
-    = default_section_type_flags_1 (decl, name, reloc,
-                                   flag_pic || DEFAULT_ABI == ABI_AIX);
-
-  if (TARGET_RELOCATABLE)
-    flags |= SECTION_WRITE;
-
-  return flags;
+  return default_section_type_flags_1 (decl, name, reloc,
+                                      flag_pic || DEFAULT_ABI == ABI_AIX);
 }
 
 /* Record an element in the table of global constructors.  SYMBOL is
@@ -14242,7 +15234,7 @@ rs6000_elf_declare_function_name (FILE *file, const char *name, tree decl)
 
   if (TARGET_RELOCATABLE
       && (get_pool_size () != 0 || current_function_profile)
-      && uses_TOC())
+      && uses_TOC ())
     {
       char buf[256];
 
@@ -14675,7 +15667,7 @@ rs6000_rtx_costs (rtx x, int code, int outer_code ATTRIBUTE_UNUSED,
       return true;
 
     case MEM:
-      /* MEM should be slightly more expensive than (plus (reg) (const)) */
+      /* MEM should be slightly more expensive than (plus (reg) (const)) */
       *total = 5;
       return true;
 
@@ -14702,7 +15694,7 @@ rs6000_register_move_cost (enum machine_mode mode,
        return (rs6000_memory_move_cost (mode, from, 0)
                + rs6000_memory_move_cost (mode, GENERAL_REGS, 0));
 
-/* It's more expensive to move CR_REGS than CR0_REGS because of the shift...*/
+/* It's more expensive to move CR_REGS than CR0_REGS because of the shift....  */
       else if (from == CR_REGS)
        return 4;
 
@@ -14782,10 +15774,24 @@ rs6000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
   enum machine_mode mode;
   unsigned int regno;
 
+  if (TARGET_32BIT && TARGET_POWERPC64 && TYPE_MODE (valtype) == DImode)
+    {
+      /* Long long return value need be split in -mpowerpc64, 32bit ABI.  */
+      return gen_rtx_PARALLEL (DImode,
+       gen_rtvec (2,
+                  gen_rtx_EXPR_LIST (VOIDmode,
+                                     gen_rtx_REG (SImode, GP_ARG_RETURN),
+                                     const0_rtx),
+                  gen_rtx_EXPR_LIST (VOIDmode,
+                                     gen_rtx_REG (SImode,
+                                                  GP_ARG_RETURN + 1),
+                                     GEN_INT (4))));
+    }
+
   if ((INTEGRAL_TYPE_P (valtype)
        && TYPE_PRECISION (valtype) < BITS_PER_WORD)
       || POINTER_TYPE_P (valtype))
-    mode = word_mode;
+    mode = TARGET_32BIT ? SImode : DImode;
   else
     mode = TYPE_MODE (valtype);
 
@@ -14823,6 +15829,28 @@ rs6000_libcall_value (enum machine_mode mode)
   return gen_rtx_REG (mode, regno);
 }
 
+/* Define the offset between two registers, FROM to be eliminated and its
+   replacement TO, at the start of a routine.  */
+HOST_WIDE_INT
+rs6000_initial_elimination_offset (int from, int to)
+{
+  rs6000_stack_t *info = rs6000_stack_info ();
+  HOST_WIDE_INT offset;
+
+  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+    offset = info->push_p ? 0 : -info->total_size;
+  else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+    offset = info->total_size;
+  else if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+    offset = info->push_p ? info->total_size : 0;
+  else if (from == RS6000_PIC_OFFSET_TABLE_REGNUM)
+    offset = 0;
+  else
+    abort ();
+
+  return offset;
+}
+
 /* Return true if TYPE is of type __ev64_opaque__.  */
 
 static bool
@@ -14858,4 +15886,39 @@ rs6000_dwarf_register_span (rtx reg)
                                   gen_rtx_REG (SImode, regno + 1200)));
 }
 
+/* Map internal gcc register numbers to DWARF2 register numbers.  */
+
+unsigned int
+rs6000_dbx_register_number (unsigned int regno)
+{
+  if (regno <= 63 || write_symbols != DWARF2_DEBUG)
+    return regno;
+  if (regno == MQ_REGNO)
+    return 100;
+  if (regno == LINK_REGISTER_REGNUM)
+    return 108;
+  if (regno == COUNT_REGISTER_REGNUM)
+    return 109;
+  if (CR_REGNO_P (regno))
+    return regno - CR0_REGNO + 86;
+  if (regno == XER_REGNO)
+    return 101;
+  if (ALTIVEC_REGNO_P (regno))
+    return regno - FIRST_ALTIVEC_REGNO + 1124;
+  if (regno == VRSAVE_REGNO)
+    return 356;
+  if (regno == VSCR_REGNO)
+    return 67;
+  if (regno == SPE_ACC_REGNO)
+    return 99;
+  if (regno == SPEFSCR_REGNO)
+    return 612;
+  /* SPE high reg number.  We get these values of regno from
+     rs6000_dwarf_register_span.  */
+  if (regno >= 1200 && regno < 1232)
+    return regno;
+
+  abort ();
+}
+
 #include "gt-rs6000.h"