OSDN Git Service

* final.c (output_in_slot): New global variable.
[pf3gnuchains/gcc-fork.git] / gcc / config / sparc / sparc.c
index 278ca9a..cf7ac04 100644 (file)
@@ -47,13 +47,172 @@ Boston, MA 02111-1307, USA.  */
 #include "target.h"
 #include "target-def.h"
 #include "cfglayout.h"
+#include "tree-gimple.h"
+
+/* Processor costs */
+static const
+struct processor_costs cypress_costs = {
+  COSTS_N_INSNS (2), /* int load */
+  COSTS_N_INSNS (2), /* int signed load */
+  COSTS_N_INSNS (2), /* int zeroed load */
+  COSTS_N_INSNS (2), /* float load */
+  COSTS_N_INSNS (5), /* fmov, fneg, fabs */
+  COSTS_N_INSNS (5), /* fadd, fsub */
+  COSTS_N_INSNS (1), /* fcmp */
+  COSTS_N_INSNS (1), /* fmov, fmovr */
+  COSTS_N_INSNS (7), /* fmul */
+  COSTS_N_INSNS (37), /* fdivs */
+  COSTS_N_INSNS (37), /* fdivd */
+  COSTS_N_INSNS (63), /* fsqrts */
+  COSTS_N_INSNS (63), /* fsqrtd */
+  COSTS_N_INSNS (1), /* imul */
+  COSTS_N_INSNS (1), /* imulX */
+  0, /* imul bit factor */
+  COSTS_N_INSNS (1), /* idiv */
+  COSTS_N_INSNS (1), /* idivX */
+  COSTS_N_INSNS (1), /* movcc/movr */
+  0, /* shift penalty */
+};
+
+static const
+struct processor_costs supersparc_costs = {
+  COSTS_N_INSNS (1), /* int load */
+  COSTS_N_INSNS (1), /* int signed load */
+  COSTS_N_INSNS (1), /* int zeroed load */
+  COSTS_N_INSNS (0), /* float load */
+  COSTS_N_INSNS (3), /* fmov, fneg, fabs */
+  COSTS_N_INSNS (3), /* fadd, fsub */
+  COSTS_N_INSNS (3), /* fcmp */
+  COSTS_N_INSNS (1), /* fmov, fmovr */
+  COSTS_N_INSNS (3), /* fmul */
+  COSTS_N_INSNS (6), /* fdivs */
+  COSTS_N_INSNS (9), /* fdivd */
+  COSTS_N_INSNS (12), /* fsqrts */
+  COSTS_N_INSNS (12), /* fsqrtd */
+  COSTS_N_INSNS (4), /* imul */
+  COSTS_N_INSNS (4), /* imulX */
+  0, /* imul bit factor */
+  COSTS_N_INSNS (4), /* idiv */
+  COSTS_N_INSNS (4), /* idivX */
+  COSTS_N_INSNS (1), /* movcc/movr */
+  1, /* shift penalty */
+};
+
+static const
+struct processor_costs hypersparc_costs = {
+  COSTS_N_INSNS (1), /* int load */
+  COSTS_N_INSNS (1), /* int signed load */
+  COSTS_N_INSNS (1), /* int zeroed load */
+  COSTS_N_INSNS (1), /* float load */
+  COSTS_N_INSNS (1), /* fmov, fneg, fabs */
+  COSTS_N_INSNS (1), /* fadd, fsub */
+  COSTS_N_INSNS (1), /* fcmp */
+  COSTS_N_INSNS (1), /* fmov, fmovr */
+  COSTS_N_INSNS (1), /* fmul */
+  COSTS_N_INSNS (8), /* fdivs */
+  COSTS_N_INSNS (12), /* fdivd */
+  COSTS_N_INSNS (17), /* fsqrts */
+  COSTS_N_INSNS (17), /* fsqrtd */
+  COSTS_N_INSNS (17), /* imul */
+  COSTS_N_INSNS (17), /* imulX */
+  0, /* imul bit factor */
+  COSTS_N_INSNS (17), /* idiv */
+  COSTS_N_INSNS (17), /* idivX */
+  COSTS_N_INSNS (1), /* movcc/movr */
+  0, /* shift penalty */
+};
+
+static const
+struct processor_costs sparclet_costs = {
+  COSTS_N_INSNS (3), /* int load */
+  COSTS_N_INSNS (3), /* int signed load */
+  COSTS_N_INSNS (1), /* int zeroed load */
+  COSTS_N_INSNS (1), /* float load */
+  COSTS_N_INSNS (1), /* fmov, fneg, fabs */
+  COSTS_N_INSNS (1), /* fadd, fsub */
+  COSTS_N_INSNS (1), /* fcmp */
+  COSTS_N_INSNS (1), /* fmov, fmovr */
+  COSTS_N_INSNS (1), /* fmul */
+  COSTS_N_INSNS (1), /* fdivs */
+  COSTS_N_INSNS (1), /* fdivd */
+  COSTS_N_INSNS (1), /* fsqrts */
+  COSTS_N_INSNS (1), /* fsqrtd */
+  COSTS_N_INSNS (5), /* imul */
+  COSTS_N_INSNS (5), /* imulX */
+  0, /* imul bit factor */
+  COSTS_N_INSNS (5), /* idiv */
+  COSTS_N_INSNS (5), /* idivX */
+  COSTS_N_INSNS (1), /* movcc/movr */
+  0, /* shift penalty */
+};
+
+static const
+struct processor_costs ultrasparc_costs = {
+  COSTS_N_INSNS (2), /* int load */
+  COSTS_N_INSNS (3), /* int signed load */
+  COSTS_N_INSNS (2), /* int zeroed load */
+  COSTS_N_INSNS (2), /* float load */
+  COSTS_N_INSNS (1), /* fmov, fneg, fabs */
+  COSTS_N_INSNS (4), /* fadd, fsub */
+  COSTS_N_INSNS (1), /* fcmp */
+  COSTS_N_INSNS (2), /* fmov, fmovr */
+  COSTS_N_INSNS (4), /* fmul */
+  COSTS_N_INSNS (13), /* fdivs */
+  COSTS_N_INSNS (23), /* fdivd */
+  COSTS_N_INSNS (13), /* fsqrts */
+  COSTS_N_INSNS (23), /* fsqrtd */
+  COSTS_N_INSNS (4), /* imul */
+  COSTS_N_INSNS (4), /* imulX */
+  2, /* imul bit factor */
+  COSTS_N_INSNS (37), /* idiv */
+  COSTS_N_INSNS (68), /* idivX */
+  COSTS_N_INSNS (2), /* movcc/movr */
+  2, /* shift penalty */
+};
+
+static const
+struct processor_costs ultrasparc3_costs = {
+  COSTS_N_INSNS (2), /* int load */
+  COSTS_N_INSNS (3), /* int signed load */
+  COSTS_N_INSNS (3), /* int zeroed load */
+  COSTS_N_INSNS (2), /* float load */
+  COSTS_N_INSNS (3), /* fmov, fneg, fabs */
+  COSTS_N_INSNS (4), /* fadd, fsub */
+  COSTS_N_INSNS (5), /* fcmp */
+  COSTS_N_INSNS (3), /* fmov, fmovr */
+  COSTS_N_INSNS (4), /* fmul */
+  COSTS_N_INSNS (17), /* fdivs */
+  COSTS_N_INSNS (20), /* fdivd */
+  COSTS_N_INSNS (20), /* fsqrts */
+  COSTS_N_INSNS (29), /* fsqrtd */
+  COSTS_N_INSNS (6), /* imul */
+  COSTS_N_INSNS (6), /* imulX */
+  0, /* imul bit factor */
+  COSTS_N_INSNS (40), /* idiv */
+  COSTS_N_INSNS (71), /* idivX */
+  COSTS_N_INSNS (2), /* movcc/movr */
+  0, /* shift penalty */
+};
+
+const struct processor_costs *sparc_costs = &cypress_costs;
+
+#ifdef HAVE_AS_RELAX_OPTION
+/* If 'as' and 'ld' are relaxing tail call insns into branch always, use
+   "or %o7,%g0,X; call Y; or X,%g0,%o7" always, so that it can be optimized.
+   With sethi/jmp, neither 'as' nor 'ld' has an easy way how to find out if
+   somebody does not branch between the sethi and jmp.  */
+#define LEAF_SIBCALL_SLOT_RESERVED_P 1
+#else
+#define LEAF_SIBCALL_SLOT_RESERVED_P \
+  ((TARGET_ARCH64 && !TARGET_CM_MEDLOW) || flag_pic)
+#endif
 
 /* Global variables for machine-dependent things.  */
 
 /* Size of frame.  Need to know this to emit return insns from leaf procedures.
-   ACTUAL_FSIZE is set by compute_frame_size() which is called during the
-   reload pass.  This is important as the value is later used in insn
-   scheduling (to see what can go in a delay slot).
+   ACTUAL_FSIZE is set by sparc_compute_frame_size() which is called during the
+   reload pass.  This is important as the value is later used for scheduling
+   (to see what can go in a delay slot).
    APPARENT_FSIZE is the size of the stack less the register save area and less
    the outgoing argument area.  It is used when saving call preserved regs.  */
 static HOST_WIDE_INT apparent_fsize;
@@ -63,15 +222,13 @@ static HOST_WIDE_INT actual_fsize;
    saved (as 4-byte quantities).  */
 static int num_gfregs;
 
+/* The alias set for prologue/epilogue register save/restore.  */
+static GTY(()) int sparc_sr_alias_set;
+
 /* Save the operands last given to a compare for use when we
    generate a scc or bcc insn.  */
 rtx sparc_compare_op0, sparc_compare_op1;
 
-/* Coordinate with the md file wrt special insns created by
-   sparc_function_epilogue.  */
-bool sparc_emitting_epilogue;
-bool sparc_skip_caller_unimp;
-
 /* Vector to say how input registers are mapped to output registers.
    HARD_FRAME_POINTER_REGNUM cannot be remapped by this function to
    eliminate it.  You must use -fomit-frame-pointer to get that.  */
@@ -115,18 +272,15 @@ struct machine_function GTY(())
   const char *some_ld_name;
 };
 
-/* Name of where we pretend to think the frame pointer points.
-   Normally, this is "%fp", but if we are in a leaf procedure,
-   this is "%sp+something".  We record "something" separately as it may be
-   too big for reg+constant addressing.  */
+/* Register we pretend to think the frame pointer is allocated to.
+   Normally, this is %fp, but if we are in a leaf procedure, this
+   is %sp+"something".  We record "something" separately as it may
+   be too big for reg+constant addressing.  */
 
-static const char *frame_base_name;
+static rtx frame_base_reg;
 static HOST_WIDE_INT frame_base_offset;
 
 static void sparc_init_modes (void);
-static int save_regs (FILE *, int, int, const char *, int, int, HOST_WIDE_INT);
-static int restore_regs (FILE *, int, int, const char *, int, int);
-static void build_big_number (FILE *, HOST_WIDE_INT, const char *);
 static void scan_record_type (tree, int *, int *, int *);
 static int function_arg_slotno (const CUMULATIVE_ARGS *, enum machine_mode,
                                tree, int, int, int *, int *);
@@ -137,27 +291,23 @@ static int hypersparc_adjust_cost (rtx, rtx, rtx, int);
 static void sparc_output_addr_vec (rtx);
 static void sparc_output_addr_diff_vec (rtx);
 static void sparc_output_deferred_case_vectors (void);
-static int check_return_regs (rtx);
 static rtx sparc_builtin_saveregs (void);
 static int epilogue_renumber (rtx *, int);
 static bool sparc_assemble_integer (rtx, unsigned int, int);
 static int set_extends (rtx);
-static void output_restore_regs (FILE *, int);
-static void sparc_output_function_prologue (FILE *, HOST_WIDE_INT);
-static void sparc_output_function_epilogue (FILE *, HOST_WIDE_INT);
-static void sparc_function_epilogue (FILE *, HOST_WIDE_INT, int);
-static void sparc_function_prologue (FILE *, HOST_WIDE_INT, int);
+static void load_pic_register (void);
+static int save_or_restore_regs (int, int, rtx, int, int);
+static void emit_save_regs (void);
+static void emit_restore_regs (void);
+static void sparc_asm_function_prologue (FILE *, HOST_WIDE_INT);
+static void sparc_asm_function_epilogue (FILE *, HOST_WIDE_INT);
 #ifdef OBJECT_FORMAT_ELF
 static void sparc_elf_asm_named_section (const char *, unsigned int);
 #endif
-static void sparc_aout_select_rtx_section (enum machine_mode, rtx,
-                                          unsigned HOST_WIDE_INT)
-     ATTRIBUTE_UNUSED;
 
 static int sparc_adjust_cost (rtx, rtx, rtx, int);
 static int sparc_issue_rate (void);
 static void sparc_sched_init (FILE *, int, int);
-static int sparc_use_dfa_pipeline_interface (void);
 static int sparc_use_sched_lookahead (void);
 
 static void emit_soft_tfmode_libcall (const char *, int, rtx *);
@@ -181,6 +331,12 @@ static bool sparc_promote_prototypes (tree);
 static rtx sparc_struct_value_rtx (tree, int);
 static bool sparc_return_in_memory (tree, tree);
 static bool sparc_strict_argument_naming (CUMULATIVE_ARGS *);
+static tree sparc_gimplify_va_arg (tree, tree, tree *, tree *);
+static bool sparc_pass_by_reference (CUMULATIVE_ARGS *,
+                                    enum machine_mode, tree, bool);
+#ifdef SUBTARGET_ATTRIBUTE_TABLE
+const struct attribute_spec sparc_attribute_table[];
+#endif
 \f
 /* Option handling.  */
 
@@ -226,9 +382,9 @@ enum processor_type sparc_cpu;
 #define TARGET_ASM_INTEGER sparc_assemble_integer
 
 #undef TARGET_ASM_FUNCTION_PROLOGUE
-#define TARGET_ASM_FUNCTION_PROLOGUE sparc_output_function_prologue
+#define TARGET_ASM_FUNCTION_PROLOGUE sparc_asm_function_prologue
 #undef TARGET_ASM_FUNCTION_EPILOGUE
-#define TARGET_ASM_FUNCTION_EPILOGUE sparc_output_function_epilogue
+#define TARGET_ASM_FUNCTION_EPILOGUE sparc_asm_function_epilogue
 
 #undef TARGET_SCHED_ADJUST_COST
 #define TARGET_SCHED_ADJUST_COST sparc_adjust_cost
@@ -236,8 +392,6 @@ enum processor_type sparc_cpu;
 #define TARGET_SCHED_ISSUE_RATE sparc_issue_rate
 #undef TARGET_SCHED_INIT
 #define TARGET_SCHED_INIT sparc_sched_init
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE sparc_use_dfa_pipeline_interface
 #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD sparc_use_sched_lookahead
 
@@ -283,12 +437,32 @@ enum processor_type sparc_cpu;
 #define TARGET_STRUCT_VALUE_RTX sparc_struct_value_rtx
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY sparc_return_in_memory
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE sparc_pass_by_reference
 
 #undef TARGET_EXPAND_BUILTIN_SAVEREGS
 #define TARGET_EXPAND_BUILTIN_SAVEREGS sparc_builtin_saveregs
 #undef TARGET_STRICT_ARGUMENT_NAMING
 #define TARGET_STRICT_ARGUMENT_NAMING sparc_strict_argument_naming
 
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR sparc_gimplify_va_arg
+
+#undef TARGET_LATE_RTL_PROLOGUE_EPILOGUE
+#define TARGET_LATE_RTL_PROLOGUE_EPILOGUE true
+
+#ifdef SUBTARGET_INSERT_ATTRIBUTES
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES SUBTARGET_INSERT_ATTRIBUTES
+#endif
+
+#ifdef SUBTARGET_ATTRIBUTE_TABLE
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE sparc_attribute_table
+#endif
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Validate and override various options, and do some machine dependent
@@ -488,10 +662,53 @@ sparc_override_options (void)
   /* Do various machine dependent initializations.  */
   sparc_init_modes ();
 
+  /* Acquire a unique set number for our register saves and restores.  */
+  sparc_sr_alias_set = new_alias_set ();
+
   /* Set up function hooks.  */
   init_machine_status = sparc_init_machine_status;
+
+  switch (sparc_cpu)
+    {
+    case PROCESSOR_V7:
+    case PROCESSOR_CYPRESS:
+      sparc_costs = &cypress_costs;
+      break;
+    case PROCESSOR_V8:
+    case PROCESSOR_SPARCLITE:
+    case PROCESSOR_SUPERSPARC:
+      sparc_costs = &supersparc_costs;
+      break;
+    case PROCESSOR_F930:
+    case PROCESSOR_F934:
+    case PROCESSOR_HYPERSPARC:
+    case PROCESSOR_SPARCLITE86X:
+      sparc_costs = &hypersparc_costs;
+      break;
+    case PROCESSOR_SPARCLET:
+    case PROCESSOR_TSC701:
+      sparc_costs = &sparclet_costs;
+      break;
+    case PROCESSOR_V9:
+    case PROCESSOR_ULTRASPARC:
+      sparc_costs = &ultrasparc_costs;
+      break;
+    case PROCESSOR_ULTRASPARC3:
+      sparc_costs = &ultrasparc3_costs;
+      break;
+    };
 }
 \f
+#ifdef SUBTARGET_ATTRIBUTE_TABLE
+/* Table of valid machine attributes.  */
+const struct attribute_spec sparc_attribute_table[] =
+{
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  SUBTARGET_ATTRIBUTE_TABLE,
+  { NULL,        0, 0, false, false, false, NULL }
+};
+#endif
+\f
 /* Miscellaneous utilities.  */
 
 /* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move
@@ -703,14 +920,6 @@ icc_or_fcc_reg_operand (rtx op, enum machine_mode mode)
   return fcc_reg_operand (op, mode);
 }
 
-/* Nonzero if OP can appear as the dest of a RESTORE insn.  */
-int
-restore_operand (rtx op, enum machine_mode mode)
-{
-  return (GET_CODE (op) == REG && GET_MODE (op) == mode
-         && (REGNO (op) < 8 || (REGNO (op) >= 24 && REGNO (op) < 32)));
-}
-
 /* Call insn on SPARC can take a PC-relative constant address, or any regular
    memory address.  */
 
@@ -1134,7 +1343,7 @@ arith10_operand (rtx op, enum machine_mode mode)
 /* Return true if OP is a register, is a CONST_INT that fits in a 13 bit
    immediate field, or is a CONST_DOUBLE whose both parts fit in a 13 bit
    immediate field.
-   v9: Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that
+   ARCH64: Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that
    can fit in a 13 bit immediate field.  This is an acceptable DImode operand
    for most 3 address instructions.  */
 
@@ -1348,6 +1557,32 @@ input_operand (rtx op, enum machine_mode mode)
   return 0;
 }
 
+/* Return 1 if OP is valid for the lhs of a compare insn.  */
+
+int
+compare_operand (rtx op, enum machine_mode mode)
+{
+  if (GET_CODE (op) == ZERO_EXTRACT)
+    return (register_operand (XEXP (op, 0), mode)
+           && small_int_or_double (XEXP (op, 1), mode)
+           && small_int_or_double (XEXP (op, 2), mode)
+           /* This matches cmp_zero_extract.  */
+           && ((mode == SImode
+                && ((GET_CODE (XEXP (op, 2)) == CONST_INT
+                     && INTVAL (XEXP (op, 2)) > 19)
+                    || (GET_CODE (XEXP (op, 2)) == CONST_DOUBLE
+                        && CONST_DOUBLE_LOW (XEXP (op, 2)) > 19)))
+               /* This matches cmp_zero_extract_sp64.  */
+               || (mode == DImode
+                   && TARGET_ARCH64
+                   && ((GET_CODE (XEXP (op, 2)) == CONST_INT
+                        && INTVAL (XEXP (op, 2)) > 51)
+                       || (GET_CODE (XEXP (op, 2)) == CONST_DOUBLE
+                           && CONST_DOUBLE_LOW (XEXP (op, 2)) > 51)))));
+  else
+    return register_operand (op, mode);
+}
+
 \f
 /* We know it can't be done in one insn when we get here,
    the movsi expander guarantees this.  */
@@ -1406,18 +1641,25 @@ sparc_emit_set_const32 (rtx op0, rtx op1)
 }
 
 \f
-/* SPARC-v9 code-model support.  */
+/* Load OP1, a symbolic 64-bit constant, into OP0, a DImode register.
+   If TEMP is non-zero, we are forbidden to use any other scratch
+   registers.  Otherwise, we are allowed to generate them as needed.
+
+   Note that TEMP may have TImode if the code model is TARGET_CM_MEDANY
+   or TARGET_CM_EMBMEDANY (see the reload_indi and reload_outdi patterns).  */
 void
-sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp1)
+sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp)
 {
-  rtx ti_temp1 = 0;
+  rtx temp1, temp2, temp3, temp4, temp5;
+  rtx ti_temp = 0;
 
-  if (temp1 && GET_MODE (temp1) == TImode)
+  if (temp && GET_MODE (temp) == TImode)
     {
-      ti_temp1 = temp1;
-      temp1 = gen_rtx_REG (DImode, REGNO (temp1));
+      ti_temp = temp;
+      temp = gen_rtx_REG (DImode, REGNO (temp));
     }
 
+  /* SPARC-V9 code-model support.  */
   switch (sparc_cmodel)
     {
     case CM_MEDLOW:
@@ -1429,8 +1671,13 @@ sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp1)
         The executable must be in the low 4TB of the virtual address
         space.
 
-        sethi  %hi(symbol), %temp
-        or     %temp, %lo(symbol), %reg  */
+        sethi  %hi(symbol), %temp1
+        or     %temp1, %lo(symbol), %reg  */
+      if (temp)
+       temp1 = temp;  /* op0 is allowed.  */
+      else
+       temp1 = gen_reg_rtx (DImode);
+
       emit_insn (gen_rtx_SET (VOIDmode, temp1, gen_rtx_HIGH (DImode, op1)));
       emit_insn (gen_rtx_SET (VOIDmode, op0, gen_rtx_LO_SUM (DImode, temp1, op1)));
       break;
@@ -1448,11 +1695,24 @@ sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp1)
         or     %temp1, %m44(symbol), %temp2
         sllx   %temp2, 12, %temp3
         or     %temp3, %l44(symbol), %reg  */
-      emit_insn (gen_seth44 (op0, op1));
-      emit_insn (gen_setm44 (op0, op0, op1));
-      emit_insn (gen_rtx_SET (VOIDmode, temp1,
-                             gen_rtx_ASHIFT (DImode, op0, GEN_INT (12))));
-      emit_insn (gen_setl44 (op0, temp1, op1));
+      if (temp)
+       {
+         temp1 = op0;
+         temp2 = op0;
+         temp3 = temp;  /* op0 is allowed.  */
+       }
+      else
+       {
+         temp1 = gen_reg_rtx (DImode);
+         temp2 = gen_reg_rtx (DImode);
+         temp3 = gen_reg_rtx (DImode);
+       }
+
+      emit_insn (gen_seth44 (temp1, op1));
+      emit_insn (gen_setm44 (temp2, temp1, op1));
+      emit_insn (gen_rtx_SET (VOIDmode, temp3,
+                             gen_rtx_ASHIFT (DImode, temp2, GEN_INT (12))));
+      emit_insn (gen_setl44 (op0, temp3, op1));
       break;
 
     case CM_MEDANY:
@@ -1467,29 +1727,44 @@ sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp1)
         sethi  %hh(symbol), %temp1
         sethi  %lm(symbol), %temp2
         or     %temp1, %hm(symbol), %temp3
-        or     %temp2, %lo(symbol), %temp4
-        sllx   %temp3, 32, %temp5
-        or     %temp4, %temp5, %reg  */
-
-      /* It is possible that one of the registers we got for operands[2]
-        might coincide with that of operands[0] (which is why we made
-        it TImode).  Pick the other one to use as our scratch.  */
-      if (rtx_equal_p (temp1, op0))
+        sllx   %temp3, 32, %temp4
+        or     %temp4, %temp2, %temp5
+        or     %temp5, %lo(symbol), %reg  */
+      if (temp)
        {
-         if (ti_temp1)
-           temp1 = gen_rtx_REG (DImode, REGNO (temp1) + 1);
-         else
-           abort();
+         /* It is possible that one of the registers we got for operands[2]
+            might coincide with that of operands[0] (which is why we made
+            it TImode).  Pick the other one to use as our scratch.  */
+         if (rtx_equal_p (temp, op0))
+           {
+             if (ti_temp)
+               temp = gen_rtx_REG (DImode, REGNO (temp) + 1);
+             else
+               abort();
+           }
+         temp1 = op0;
+         temp2 = temp;  /* op0 is _not_ allowed, see above.  */
+         temp3 = op0;
+         temp4 = op0;
+         temp5 = op0;
+       }
+      else
+       {
+         temp1 = gen_reg_rtx (DImode);
+         temp2 = gen_reg_rtx (DImode);
+         temp3 = gen_reg_rtx (DImode);
+         temp4 = gen_reg_rtx (DImode);
+         temp5 = gen_reg_rtx (DImode);
        }
 
-      emit_insn (gen_sethh (op0, op1));
-      emit_insn (gen_setlm (temp1, op1));
-      emit_insn (gen_sethm (op0, op0, op1));
-      emit_insn (gen_rtx_SET (VOIDmode, op0,
-                             gen_rtx_ASHIFT (DImode, op0, GEN_INT (32))));
-      emit_insn (gen_rtx_SET (VOIDmode, op0,
-                             gen_rtx_PLUS (DImode, op0, temp1)));
-      emit_insn (gen_setlo (op0, op0, op1));
+      emit_insn (gen_sethh (temp1, op1));
+      emit_insn (gen_setlm (temp2, op1));
+      emit_insn (gen_sethm (temp3, temp1, op1));
+      emit_insn (gen_rtx_SET (VOIDmode, temp4,
+                             gen_rtx_ASHIFT (DImode, temp3, GEN_INT (32))));
+      emit_insn (gen_rtx_SET (VOIDmode, temp5,
+                             gen_rtx_PLUS (DImode, temp4, temp2)));
+      emit_insn (gen_setlo (op0, temp5, op1));
       break;
 
     case CM_EMBMEDANY:
@@ -1501,42 +1776,69 @@ sparc_emit_set_symbolic_const64 (rtx op0, rtx op1, rtx temp1)
         look different.
 
         Data segment:  sethi   %hi(symbol), %temp1
-                       or      %temp1, %lo(symbol), %temp2
-                       add     %temp2, EMBMEDANY_BASE_REG, %reg
-
-        Text segment:  sethi   %uhi(symbol), %temp1
-                       sethi   %hi(symbol), %temp2
-                       or      %temp1, %ulo(symbol), %temp3
-                       or      %temp2, %lo(symbol), %temp4
-                       sllx    %temp3, 32, %temp5
-                       or      %temp4, %temp5, %reg  */
+                       add     %temp1, EMBMEDANY_BASE_REG, %temp2
+                       or      %temp2, %lo(symbol), %reg  */
       if (data_segment_operand (op1, GET_MODE (op1)))
        {
+         if (temp)
+           {
+             temp1 = temp;  /* op0 is allowed.  */
+             temp2 = op0;
+           }
+         else
+           {
+             temp1 = gen_reg_rtx (DImode);
+             temp2 = gen_reg_rtx (DImode);
+           }
+
          emit_insn (gen_embmedany_sethi (temp1, op1));
-         emit_insn (gen_embmedany_brsum (op0, temp1));
-         emit_insn (gen_embmedany_losum (op0, op0, op1));
+         emit_insn (gen_embmedany_brsum (temp2, temp1));
+         emit_insn (gen_embmedany_losum (op0, temp2, op1));
        }
+
+      /* Text segment: sethi   %uhi(symbol), %temp1
+                       sethi   %hi(symbol), %temp2
+                       or      %temp1, %ulo(symbol), %temp3
+                       sllx    %temp3, 32, %temp4
+                       or      %temp4, %temp2, %temp5
+                       or      %temp5, %lo(symbol), %reg  */
       else
        {
-         /* It is possible that one of the registers we got for operands[2]
-            might coincide with that of operands[0] (which is why we made
-            it TImode).  Pick the other one to use as our scratch.  */
-         if (rtx_equal_p (temp1, op0))
+         if (temp)
            {
-             if (ti_temp1)
-               temp1 = gen_rtx_REG (DImode, REGNO (temp1) + 1);
-             else
-               abort();
+             /* It is possible that one of the registers we got for operands[2]
+                might coincide with that of operands[0] (which is why we made
+                it TImode).  Pick the other one to use as our scratch.  */
+             if (rtx_equal_p (temp, op0))
+               {
+                 if (ti_temp)
+                   temp = gen_rtx_REG (DImode, REGNO (temp) + 1);
+                 else
+                   abort();
+               }
+             temp1 = op0;
+             temp2 = temp;  /* op0 is _not_ allowed, see above.  */
+             temp3 = op0;
+             temp4 = op0;
+             temp5 = op0;
+           }
+         else
+           {
+             temp1 = gen_reg_rtx (DImode);
+             temp2 = gen_reg_rtx (DImode);
+             temp3 = gen_reg_rtx (DImode);
+             temp4 = gen_reg_rtx (DImode);
+             temp5 = gen_reg_rtx (DImode);
            }
 
-         emit_insn (gen_embmedany_textuhi (op0, op1));
-         emit_insn (gen_embmedany_texthi  (temp1, op1));
-         emit_insn (gen_embmedany_textulo (op0, op0, op1));
-         emit_insn (gen_rtx_SET (VOIDmode, op0,
-                                 gen_rtx_ASHIFT (DImode, op0, GEN_INT (32))));
-         emit_insn (gen_rtx_SET (VOIDmode, op0,
-                                 gen_rtx_PLUS (DImode, op0, temp1)));
-         emit_insn (gen_embmedany_textlo  (op0, op0, op1));
+         emit_insn (gen_embmedany_textuhi (temp1, op1));
+         emit_insn (gen_embmedany_texthi  (temp2, op1));
+         emit_insn (gen_embmedany_textulo (temp3, temp1, op1));
+         emit_insn (gen_rtx_SET (VOIDmode, temp4,
+                                 gen_rtx_ASHIFT (DImode, temp3, GEN_INT (32))));
+         emit_insn (gen_rtx_SET (VOIDmode, temp5,
+                                 gen_rtx_PLUS (DImode, temp4, temp2)));
+         emit_insn (gen_embmedany_textlo  (op0, temp5, op1));
        }
       break;
 
@@ -1916,7 +2218,7 @@ sparc_emit_set_const64 (rtx op0, rtx op1)
   unsigned HOST_WIDE_INT high_bits, low_bits;
   int lowest_bit_set, highest_bit_set;
   int all_bits_between_are_set;
-  rtx temp;
+  rtx temp = 0;
 
   /* Sanity check that we know what we are working with.  */
   if (! TARGET_ARCH64)
@@ -1932,8 +2234,6 @@ sparc_emit_set_const64 (rtx op0, rtx op1)
 
   if (reload_in_progress || reload_completed)
     temp = op0;
-  else
-    temp = gen_reg_rtx (DImode);
 
   if (GET_CODE (op1) != CONST_DOUBLE
       && GET_CODE (op1) != CONST_INT)
@@ -1942,6 +2242,9 @@ sparc_emit_set_const64 (rtx op0, rtx op1)
       return;
     }
 
+  if (! temp)
+    temp = gen_reg_rtx (DImode);
+
   if (GET_CODE (op1) == CONST_DOUBLE)
     {
 #if HOST_BITS_PER_WIDE_INT == 64
@@ -2721,14 +3024,6 @@ emit_tfmode_cvt (enum rtx_code code, rtx *operands)
     emit_soft_tfmode_cvt (code, operands);
 }
 \f
-/* Return nonzero if a return peephole merging return with
-   setting of output register is ok.  */
-int
-leaf_return_peephole_ok (void)
-{
-  return (actual_fsize == 0);
-}
-
 /* Return nonzero if a branch/jump/call instruction will be emitting
    nop into its delay slot.  */
 
@@ -2748,69 +3043,48 @@ empty_delay_slot (rtx insn)
   return 1;
 }
 
-/* Return nonzero if TRIAL can go into the function epilogue's
-   delay slot.  SLOT is the slot we are trying to fill.  */
+/* Return nonzero if TRIAL can go into the call delay slot.  */
 
 int
-eligible_for_epilogue_delay (rtx trial, int slot)
+tls_call_delay (rtx trial)
 {
-  rtx pat, src;
-
-  if (slot >= 1)
-    return 0;
-
-  if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
-    return 0;
-
-  if (get_attr_length (trial) != 1)
-    return 0;
-
-  /* If there are any call-saved registers, we should scan TRIAL if it
-     does not reference them.  For now just make it easy.  */
-  if (num_gfregs)
-    return 0;
-
-  /* If the function uses __builtin_eh_return, the eh_return machinery
-     occupies the delay slot.  */
-  if (current_function_calls_eh_return)
-    return 0;
+  rtx pat, unspec;
 
-  /* In the case of a true leaf function, anything can go into the delay slot.
-     A delay slot only exists however if the frame size is zero, otherwise
-     we will put an insn to adjust the stack after the return.  */
-  if (current_function_uses_only_leaf_regs)
-    {
-      if (leaf_return_peephole_ok ())
-       return ((get_attr_in_uncond_branch_delay (trial)
-                == IN_BRANCH_DELAY_TRUE));
-      return 0;
-    }
+  /* Binutils allows
+     call __tls_get_addr, %tgd_call (foo)
+      add %l7, %o0, %o0, %tgd_add (foo)
+     while Sun as/ld does not.  */
+  if (TARGET_GNU_TLS || !TARGET_TLS)
+    return 1;
 
   pat = PATTERN (trial);
+  if (GET_CODE (pat) != SET || GET_CODE (SET_DEST (pat)) != PLUS)
+    return 1;
 
-  /* Otherwise, only operations which can be done in tandem with
-     a `restore' or `return' insn can go into the delay slot.  */
-  if (GET_CODE (SET_DEST (pat)) != REG
-      || REGNO (SET_DEST (pat)) < 24)
-    return 0;
+  unspec = XEXP (SET_DEST (pat), 1);
+  if (GET_CODE (unspec) != UNSPEC
+      || (XINT (unspec, 1) != UNSPEC_TLSGD
+         && XINT (unspec, 1) != UNSPEC_TLSLDM))
+    return 1;
 
-  /* If this instruction sets up floating point register and we have a return
-     instruction, it can probably go in.  But restore will not work
-     with FP_REGS.  */
-  if (REGNO (SET_DEST (pat)) >= 32)
-    {
-      if (TARGET_V9 && ! epilogue_renumber (&pat, 1)
-         && (get_attr_in_uncond_branch_delay (trial) == IN_BRANCH_DELAY_TRUE))
-       return 1;
-      return 0;
-    }
+  return 0;
+}
+
+/* Return nonzero if TRIAL, an insn, can be combined with a 'restore'
+   instruction.  RETURN_P is true if the v9 variant 'return' is to be
+   considered in the test too.
 
-  /* The set of insns matched here must agree precisely with the set of
-     patterns paired with a RETURN in sparc.md.  */
+   TRIAL must be a SET whose destination is a REG appropriate for the
+   'restore' instruction or, if RETURN_P is true, for the 'return'
+   instruction.  */
 
-  src = SET_SRC (pat);
+static int
+eligible_for_restore_insn (rtx trial, bool return_p)
+{
+  rtx pat = PATTERN (trial);
+  rtx src = SET_SRC (pat);
 
-  /* This matches "*return_[qhs]i" or even "*return_di" on TARGET_ARCH64.  */
+  /* The 'restore src,%g0,dest' pattern for word mode and below.  */
   if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
       && arith_operand (src, GET_MODE (src)))
     {
@@ -2820,41 +3094,39 @@ eligible_for_epilogue_delay (rtx trial, int slot)
         return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
     }
 
-  /* This matches "*return_di".  */
+  /* The 'restore src,%g0,dest' pattern for double-word mode.  */
   else if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
           && arith_double_operand (src, GET_MODE (src)))
     return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
 
-  /* This matches "*return_sf_no_fpu".  */
-  else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode)
-          && register_operand (src, SFmode))
+  /* The 'restore src,%g0,dest' pattern for float if no FPU.  */
+  else if (! TARGET_FPU && register_operand (src, SFmode))
+    return 1;
+
+  /* The 'restore src,%g0,dest' pattern for double if no FPU.  */
+  else if (! TARGET_FPU && TARGET_ARCH64 && register_operand (src, DFmode))
     return 1;
 
-  /* If we have return instruction, anything that does not use
+  /* If we have the 'return' instruction, anything that does not use
      local or output registers and can go into a delay slot wins.  */
-  else if (TARGET_V9 && ! epilogue_renumber (&pat, 1)
-          && (get_attr_in_uncond_branch_delay (trial) == IN_BRANCH_DELAY_TRUE))
+  else if (return_p && TARGET_V9 && ! epilogue_renumber (&pat, 1)
+          && (get_attr_in_uncond_branch_delay (trial)
+              == IN_UNCOND_BRANCH_DELAY_TRUE))
     return 1;
 
-  /* This matches "*return_addsi".  */
+  /* The 'restore src1,src2,dest' pattern for SImode.  */
   else if (GET_CODE (src) == PLUS
-          && arith_operand (XEXP (src, 0), SImode)
-          && arith_operand (XEXP (src, 1), SImode)
-          && (register_operand (XEXP (src, 0), SImode)
-              || register_operand (XEXP (src, 1), SImode)))
+          && register_operand (XEXP (src, 0), SImode)
+          && arith_operand (XEXP (src, 1), SImode))
     return 1;
 
-  /* This matches "*return_adddi".  */
+  /* The 'restore src1,src2,dest' pattern for DImode.  */
   else if (GET_CODE (src) == PLUS
-          && arith_double_operand (XEXP (src, 0), DImode)
-          && arith_double_operand (XEXP (src, 1), DImode)
-          && (register_operand (XEXP (src, 0), DImode)
-              || register_operand (XEXP (src, 1), DImode)))
+          && register_operand (XEXP (src, 0), DImode)
+          && arith_double_operand (XEXP (src, 1), DImode))
     return 1;
 
-  /* This can match "*return_losum_[sd]i".
-     Catch only some cases, so that return_losum* don't have
-     to be too big.  */
+  /* The 'restore src1,%lo(src2),dest' pattern.  */
   else if (GET_CODE (src) == LO_SUM
           && ! TARGET_CM_MEDMID
           && ((register_operand (XEXP (src, 0), SImode)
@@ -2864,7 +3136,7 @@ eligible_for_epilogue_delay (rtx trial, int slot)
                   && immediate_operand (XEXP (src, 1), DImode))))
     return 1;
 
-  /* sll{,x} reg,1,reg2 is add reg,reg,reg2 as well.  */
+  /* The 'restore src,src,dest' pattern.  */
   else if (GET_CODE (src) == ASHIFT
           && (register_operand (XEXP (src, 0), SImode)
               || register_operand (XEXP (src, 0), DImode))
@@ -2874,39 +3146,64 @@ eligible_for_epilogue_delay (rtx trial, int slot)
   return 0;
 }
 
-/* Return nonzero if TRIAL can go into the call delay slot.  */
+/* Return nonzero if TRIAL can go into the function return's
+   delay slot.  */
+
 int
-tls_call_delay (rtx trial)
+eligible_for_return_delay (rtx trial)
 {
-  rtx pat, unspec;
+  int leaf_function_p = current_function_uses_only_leaf_regs;
+  rtx pat;
 
-  /* Binutils allows
-     call __tls_get_addr, %tgd_call (foo)
-      add %l7, %o0, %o0, %tgd_add (foo)
-     while Sun as/ld does not.  */
-  if (TARGET_GNU_TLS || !TARGET_TLS)
-    return 1;
+  if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
+    return 0;
+
+  if (get_attr_length (trial) != 1)
+    return 0;
+
+  /* If there are any call-saved registers, we should scan TRIAL if it
+     does not reference them.  For now just make it easy.  */
+  if (num_gfregs)
+    return 0;
+
+  /* If the function uses __builtin_eh_return, the eh_return machinery
+     occupies the delay slot.  */
+  if (current_function_calls_eh_return)
+    return 0;
+
+  /* In the case of a true leaf function, anything can go into the slot.  */
+  if (leaf_function_p)
+    return get_attr_in_uncond_branch_delay (trial)
+          == IN_UNCOND_BRANCH_DELAY_TRUE;
 
   pat = PATTERN (trial);
-  if (GET_CODE (pat) != SET || GET_CODE (SET_DEST (pat)) != PLUS)
-    return 1;
 
-  unspec = XEXP (SET_DEST (pat), 1);
-  if (GET_CODE (unspec) != UNSPEC
-      || (XINT (unspec, 1) != UNSPEC_TLSGD
-         && XINT (unspec, 1) != UNSPEC_TLSLDM))
-    return 1;
+  /* Otherwise, only operations which can be done in tandem with
+     a `restore' or `return' insn can go into the delay slot.  */
+  if (GET_CODE (SET_DEST (pat)) != REG
+      || (REGNO (SET_DEST (pat)) >= 8 && REGNO (SET_DEST (pat)) < 24))
+    return 0;
 
-  return 0;
+  /* If this instruction sets up floating point register and we have a return
+     instruction, it can probably go in.  But restore will not work
+     with FP_REGS.  */
+  if (REGNO (SET_DEST (pat)) >= 32)
+    return (TARGET_V9
+           && ! epilogue_renumber (&pat, 1)
+           && (get_attr_in_uncond_branch_delay (trial)
+               == IN_UNCOND_BRANCH_DELAY_TRUE));
+
+  return eligible_for_restore_insn (trial, true);
 }
 
-/* Return nonzero if TRIAL can go into the sibling call
+/* Return nonzero if TRIAL can go into the sibling call's
    delay slot.  */
 
 int
 eligible_for_sibcall_delay (rtx trial)
 {
-  rtx pat, src;
+  int leaf_function_p = current_function_uses_only_leaf_regs;
+  rtx pat;
 
   if (GET_CODE (trial) != INSN || GET_CODE (PATTERN (trial)) != SET)
     return 0;
@@ -2916,11 +3213,11 @@ eligible_for_sibcall_delay (rtx trial)
 
   pat = PATTERN (trial);
 
-  if (current_function_uses_only_leaf_regs)
+  if (leaf_function_p)
     {
       /* If the tail call is done using the call instruction,
         we have to restore %o7 in the delay slot.  */
-      if ((TARGET_ARCH64 && ! TARGET_CM_MEDLOW) || flag_pic)
+      if (LEAF_SIBCALL_SLOT_RESERVED_P)
        return 0;
 
       /* %g1 is used to build the function address */
@@ -2933,7 +3230,7 @@ eligible_for_sibcall_delay (rtx trial)
   /* Otherwise, only operations which can be done in tandem with
      a `restore' insn can go into the delay slot.  */
   if (GET_CODE (SET_DEST (pat)) != REG
-      || REGNO (SET_DEST (pat)) < 24
+      || (REGNO (SET_DEST (pat)) >= 8 && REGNO (SET_DEST (pat)) < 24)
       || REGNO (SET_DEST (pat)) >= 32)
     return 0;
 
@@ -2942,98 +3239,16 @@ eligible_for_sibcall_delay (rtx trial)
   if (reg_mentioned_p (gen_rtx_REG (Pmode, 15), pat))
     return 0;
 
-  src = SET_SRC (pat);
-
-  if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
-      && arith_operand (src, GET_MODE (src)))
-    {
-      if (TARGET_ARCH64)
-        return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
-      else
-        return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (SImode);
-    }
-
-  else if (GET_MODE_CLASS (GET_MODE (src)) != MODE_FLOAT
-          && arith_double_operand (src, GET_MODE (src)))
-    return GET_MODE_SIZE (GET_MODE (src)) <= GET_MODE_SIZE (DImode);
-
-  else if (! TARGET_FPU && restore_operand (SET_DEST (pat), SFmode)
-          && register_operand (src, SFmode))
-    return 1;
-
-  else if (GET_CODE (src) == PLUS
-          && arith_operand (XEXP (src, 0), SImode)
-          && arith_operand (XEXP (src, 1), SImode)
-          && (register_operand (XEXP (src, 0), SImode)
-              || register_operand (XEXP (src, 1), SImode)))
-    return 1;
-
-  else if (GET_CODE (src) == PLUS
-          && arith_double_operand (XEXP (src, 0), DImode)
-          && arith_double_operand (XEXP (src, 1), DImode)
-          && (register_operand (XEXP (src, 0), DImode)
-              || register_operand (XEXP (src, 1), DImode)))
-    return 1;
+  return eligible_for_restore_insn (trial, false);
+}
 
-  else if (GET_CODE (src) == LO_SUM
-          && ! TARGET_CM_MEDMID
-          && ((register_operand (XEXP (src, 0), SImode)
-               && immediate_operand (XEXP (src, 1), SImode))
-              || (TARGET_ARCH64
-                  && register_operand (XEXP (src, 0), DImode)
-                  && immediate_operand (XEXP (src, 1), DImode))))
-    return 1;
+int
+short_branch (int uid1, int uid2)
+{
+  int delta = INSN_ADDRESSES (uid1) - INSN_ADDRESSES (uid2);
 
-  else if (GET_CODE (src) == ASHIFT
-          && (register_operand (XEXP (src, 0), SImode)
-              || register_operand (XEXP (src, 0), DImode))
-          && XEXP (src, 1) == const1_rtx)
-    return 1;
-
-  return 0;
-}
-
-static int
-check_return_regs (rtx x)
-{
-  switch (GET_CODE (x))
-    {
-    case REG:
-      return IN_OR_GLOBAL_P (x);
-
-    case CONST_INT:
-    case CONST_DOUBLE:
-    case CONST:
-    case SYMBOL_REF:
-    case LABEL_REF:
-    return 1;
-
-    case SET:
-    case IOR:
-    case AND:
-    case XOR:
-    case PLUS:
-    case MINUS:
-      if (check_return_regs (XEXP (x, 1)) == 0)
-  return 0;
-    case NOT:
-    case NEG:
-    case MEM:
-      return check_return_regs (XEXP (x, 0));
-      
-    default:
-      return 0;
-    }
-
-}
-
-int
-short_branch (int uid1, int uid2)
-{
-  int delta = INSN_ADDRESSES (uid1) - INSN_ADDRESSES (uid2);
-
-  /* Leave a few words of "slop".  */
-  if (delta >= -1023 && delta <= 1022)
+  /* Leave a few words of "slop".  */
+  if (delta >= -1023 && delta <= 1022)
     return 1;
 
   return 0;
@@ -3115,8 +3330,8 @@ sparc_cannot_force_const_mem (rtx x)
 static GTY(()) rtx global_offset_table;
 
 /* The function we use to get at it.  */
-static GTY(()) rtx get_pc_symbol;
-static GTY(()) char get_pc_symbol_name[256];
+static GTY(()) rtx add_pc_to_pic_symbol;
+static GTY(()) char add_pc_to_pic_symbol_name[256];
 
 /* Ensure that we are not using patterns that are not OK with PIC.  */
 
@@ -3590,11 +3805,10 @@ legitimize_pic_address (rtx orig, enum machine_mode mode ATTRIBUTE_UNUSED,
       else
        address = orig;
 
-      pic_ref = gen_rtx_MEM (Pmode,
-                            gen_rtx_PLUS (Pmode,
-                                          pic_offset_table_rtx, address));
+      pic_ref = gen_const_mem (Pmode,
+                              gen_rtx_PLUS (Pmode,
+                                            pic_offset_table_rtx, address));
       current_function_uses_pic_offset_table = 1;
-      RTX_UNCHANGING_P (pic_ref) = 1;
       insn = emit_move_insn (reg, pic_ref);
       /* Put a REG_EQUAL note on this insn, so that it can be optimized
         by loop.  */
@@ -3694,41 +3908,42 @@ legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED, enum machine_mode mode)
   return x;
 }
 
-/* Emit special PIC prologues.  */
+/* Emit the special PIC prologue.  */
 
-void
+static void
 load_pic_register (void)
 {
-  /* Labels to get the PC in the prologue of this function.  */
   int orig_flag_pic = flag_pic;
 
-  if (! flag_pic)
-    abort ();
-
-  /* If we haven't emitted the special get_pc helper function, do so now.  */
-  if (get_pc_symbol_name[0] == 0)
+  /* If we haven't emitted the special helper function, do so now.  */
+  if (add_pc_to_pic_symbol_name[0] == 0)
     {
+      const char *pic_name = reg_names[REGNO (pic_offset_table_rtx)];
       int align;
 
-      ASM_GENERATE_INTERNAL_LABEL (get_pc_symbol_name, "LGETPC", 0);
+      ASM_GENERATE_INTERNAL_LABEL (add_pc_to_pic_symbol_name, "LADDPC", 0);
       text_section ();
 
       align = floor_log2 (FUNCTION_BOUNDARY / BITS_PER_UNIT);
       if (align > 0)
        ASM_OUTPUT_ALIGN (asm_out_file, align);
-      (*targetm.asm_out.internal_label) (asm_out_file, "LGETPC", 0);
-      fputs ("\tretl\n\tadd\t%o7, %l7, %l7\n", asm_out_file);
+      ASM_OUTPUT_LABEL (asm_out_file, add_pc_to_pic_symbol_name);
+      if (flag_delayed_branch)
+       fprintf (asm_out_file, "\tjmp %%o7+8\n\t add\t%%o7, %s, %s\n",
+                pic_name, pic_name);
+      else
+       fprintf (asm_out_file, "\tadd\t%%o7, %s, %s\n\tjmp %%o7+8\n\t nop\n",
+                pic_name, pic_name);
     }
 
   /* Initialize every time through, since we can't easily
      know this to be permanent.  */
   global_offset_table = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
-  get_pc_symbol = gen_rtx_SYMBOL_REF (Pmode, get_pc_symbol_name);
-  flag_pic = 0;
-
-  emit_insn (gen_get_pc (pic_offset_table_rtx, global_offset_table,
-                        get_pc_symbol));
+  add_pc_to_pic_symbol = gen_rtx_SYMBOL_REF (Pmode, add_pc_to_pic_symbol_name);
 
+  flag_pic = 0;
+  emit_insn (gen_load_pcrel_sym (pic_offset_table_rtx, global_offset_table,
+                                add_pc_to_pic_symbol));
   flag_pic = orig_flag_pic;
 
   /* Need to emit this whether or not we obey regdecls,
@@ -4005,128 +4220,16 @@ sparc_init_modes (void)
     }
 }
 \f
-/* Save non call used registers from LOW to HIGH at BASE+OFFSET.
-   N_REGS is the number of 4-byte regs saved thus far.  This applies even to
-   v9 int regs as it simplifies the code.  */
-
-static int
-save_regs (FILE *file, int low, int high, const char *base,
-          int offset, int n_regs, HOST_WIDE_INT real_offset)
-{
-  int i;
-
-  if (TARGET_ARCH64 && high <= 32)
-    {
-      for (i = low; i < high; i++)
-       {
-         if (regs_ever_live[i] && ! call_used_regs[i])
-           {
-             fprintf (file, "\tstx\t%s, [%s+%d]\n",
-                      reg_names[i], base, offset + 4 * n_regs);
-             if (dwarf2out_do_frame ())
-               dwarf2out_reg_save ("", i, real_offset + 4 * n_regs);
-             n_regs += 2;
-           }
-       }
-    }
-  else
-    {
-      for (i = low; i < high; i += 2)
-       {
-         if (regs_ever_live[i] && ! call_used_regs[i])
-           {
-             if (regs_ever_live[i+1] && ! call_used_regs[i+1])
-               {
-                 fprintf (file, "\tstd\t%s, [%s+%d]\n",
-                          reg_names[i], base, offset + 4 * n_regs);
-                 if (dwarf2out_do_frame ())
-                   {
-                     char *l = dwarf2out_cfi_label ();
-                     dwarf2out_reg_save (l, i, real_offset + 4 * n_regs);
-                     dwarf2out_reg_save (l, i+1, real_offset + 4 * n_regs + 4);
-                   }
-                 n_regs += 2;
-               }
-             else
-               {
-                 fprintf (file, "\tst\t%s, [%s+%d]\n",
-                          reg_names[i], base, offset + 4 * n_regs);
-                 if (dwarf2out_do_frame ())
-                   dwarf2out_reg_save ("", i, real_offset + 4 * n_regs);
-                 n_regs += 2;
-               }
-           }
-         else
-           {
-             if (regs_ever_live[i+1] && ! call_used_regs[i+1])
-               {
-                 fprintf (file, "\tst\t%s, [%s+%d]\n",
-                          reg_names[i+1], base, offset + 4 * n_regs + 4);
-                 if (dwarf2out_do_frame ())
-                   dwarf2out_reg_save ("", i + 1, real_offset + 4 * n_regs + 4);
-                 n_regs += 2;
-               }
-           }
-       }
-    }
-  return n_regs;
-}
-
-/* Restore non call used registers from LOW to HIGH at BASE+OFFSET.
-
-   N_REGS is the number of 4-byte regs saved thus far.  This applies even to
-   v9 int regs as it simplifies the code.  */
-
-static int
-restore_regs (FILE *file, int low, int high, const char *base,
-             int offset, int n_regs)
-{
-  int i;
-
-  if (TARGET_ARCH64 && high <= 32)
-    {
-      for (i = low; i < high; i++)
-       {
-         if (regs_ever_live[i] && ! call_used_regs[i])
-           fprintf (file, "\tldx\t[%s+%d], %s\n",
-             base, offset + 4 * n_regs, reg_names[i]),
-           n_regs += 2;
-       }
-    }
-  else
-    {
-      for (i = low; i < high; i += 2)
-       {
-         if (regs_ever_live[i] && ! call_used_regs[i])
-           if (regs_ever_live[i+1] && ! call_used_regs[i+1])
-             fprintf (file, "\tldd\t[%s+%d], %s\n",
-                      base, offset + 4 * n_regs, reg_names[i]),
-             n_regs += 2;
-           else
-             fprintf (file, "\tld\t[%s+%d], %s\n",
-                      base, offset + 4 * n_regs, reg_names[i]),
-             n_regs += 2;
-         else if (regs_ever_live[i+1] && ! call_used_regs[i+1])
-           fprintf (file, "\tld\t[%s+%d], %s\n",
-                    base, offset + 4 * n_regs + 4, reg_names[i+1]),
-           n_regs += 2;
-       }
-    }
-  return n_regs;
-}
-
 /* Compute the frame size required by the function.  This function is called
-   during the reload pass and also by output_function_prologue().  */
+   during the reload pass and also by sparc_expand_prologue.  */
 
 HOST_WIDE_INT
-compute_frame_size (HOST_WIDE_INT size, int leaf_function)
+sparc_compute_frame_size (HOST_WIDE_INT size, int leaf_function_p)
 {
-  int n_regs = 0, i;
   int outgoing_args_size = (current_function_outgoing_args_size
                            + REG_PARM_STACK_SPACE (current_function_decl));
-
-  /* N_REGS is the number of 4-byte regs saved thus far.  This applies
-     even to v9 int regs to be consistent with save_regs/restore_regs.  */
+  int n_regs = 0;  /* N_REGS is the number of 4-byte regs saved thus far.  */
+  int i;
 
   if (TARGET_ARCH64)
     {
@@ -4147,14 +4250,14 @@ compute_frame_size (HOST_WIDE_INT size, int leaf_function)
        || (regs_ever_live[i+1] && ! call_used_regs[i+1]))
       n_regs += 2;
 
-  /* Set up values for use in `function_epilogue'.  */
+  /* Set up values for use in prologue and epilogue.  */
   num_gfregs = n_regs;
 
-  if (leaf_function && n_regs == 0
-      && size == 0 && current_function_outgoing_args_size == 0)
-    {
-      actual_fsize = apparent_fsize = 0;
-    }
+  if (leaf_function_p
+      && n_regs == 0
+      && size == 0
+      && current_function_outgoing_args_size == 0)
+    actual_fsize = apparent_fsize = 0;
   else
     {
       /* We subtract STARTING_FRAME_OFFSET, remember it's negative.  */
@@ -4167,106 +4270,14 @@ compute_frame_size (HOST_WIDE_INT size, int leaf_function)
      If a SAVE must be done, or there is a stack-local variable,
      the register window area must be allocated.
      ??? For v8 we apparently need an additional 8 bytes of reserved space.  */
-  if (leaf_function == 0 || size > 0)
+  if (! leaf_function_p || size > 0)
     actual_fsize += (16 * UNITS_PER_WORD) + (TARGET_ARCH64 ? 0 : 8);
 
   return SPARC_STACK_ALIGN (actual_fsize);
 }
 
-/* Build big number NUM in register REG and output the result to FILE.
-   REG is guaranteed to be the only clobbered register.  The function
-   will very likely emit several instructions, so it must not be called
-   from within a delay slot.  */
-
-static void
-build_big_number (FILE *file, HOST_WIDE_INT num, const char *reg)
-{
-#if HOST_BITS_PER_WIDE_INT == 64
-  HOST_WIDE_INT high_bits = (num >> 32) & 0xffffffff;
-
-  if (high_bits == 0
-#else
-  if (num >= 0
-#endif
-      || ! TARGET_ARCH64)
-    {
-      /* We don't use the 'set' macro because it appears to be broken
-        in the Solaris 7 assembler.  */
-      fprintf (file, "\tsethi\t%%hi("HOST_WIDE_INT_PRINT_DEC"), %s\n",
-              num, reg);
-      if ((num & 0x3ff) != 0)
-       fprintf (file, "\tor\t%s, %%lo("HOST_WIDE_INT_PRINT_DEC"), %s\n",
-                reg, num, reg);
-    }
-#if HOST_BITS_PER_WIDE_INT == 64
-  else if (high_bits == 0xffffffff) /* && TARGET_ARCH64 */
-#else
-  else /* num < 0 && TARGET_ARCH64 */
-#endif
-    {
-      /* Sethi does not sign extend, so we must use a little trickery
-        to use it for negative numbers.  Invert the constant before
-        loading it in, then use xor immediate to invert the loaded bits
-        (along with the upper 32 bits) to the desired constant.  This
-        works because the sethi and immediate fields overlap.  */
-      HOST_WIDE_INT inv = ~num;
-      HOST_WIDE_INT low = -0x400 + (num & 0x3ff);
-         
-      fprintf (file, "\tsethi\t%%hi("HOST_WIDE_INT_PRINT_DEC"), %s\n",
-              inv, reg);
-      fprintf (file, "\txor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
-              reg, low, reg);
-    }
-#if HOST_BITS_PER_WIDE_INT == 64
-  else /* TARGET_ARCH64 */
-    {
-      /* We don't use the 'setx' macro because if requires a scratch register.
-         This is the translation of sparc_emit_set_const64_longway into asm.
-         Hopefully we will soon have prologue/epilogue emitted as RTL.  */
-      HOST_WIDE_INT low1 = (num >> (32 - 12))          & 0xfff;
-      HOST_WIDE_INT low2 = (num >> (32 - 12 - 12))     & 0xfff;
-      HOST_WIDE_INT low3 = (num >> (32 - 12 - 12 - 8)) & 0x0ff;
-      int to_shift = 12;
-
-      /* We don't use the 'set' macro because it appears to be broken
-        in the Solaris 7 assembler.  */
-      fprintf (file, "\tsethi\t%%hi("HOST_WIDE_INT_PRINT_DEC"), %s\n",
-              high_bits, reg);
-      if ((high_bits & 0x3ff) != 0)
-       fprintf (file, "\tor\t%s, %%lo("HOST_WIDE_INT_PRINT_DEC"), %s\n",
-                reg, high_bits, reg);
-
-      if (low1 != 0)
-       {
-         fprintf (file, "\tsllx\t%s, %d, %s\n", reg, to_shift, reg);
-         fprintf (file, "\tor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
-                  reg, low1, reg);
-         to_shift = 12;
-       }
-      else
-       {
-         to_shift += 12;
-       }
-      if (low2 != 0)
-       {
-         fprintf (file, "\tsllx\t%s, %d, %s\n", reg, to_shift, reg);
-         fprintf (file, "\tor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
-                  reg, low2, reg);
-         to_shift = 8;
-       }
-      else
-       {
-         to_shift += 8;
-       }
-      fprintf (file, "\tsllx\t%s, %d, %s\n", reg, to_shift, reg);
-      if (low3 != 0)
-       fprintf (file, "\tor\t%s, "HOST_WIDE_INT_PRINT_DEC", %s\n",
-                reg, low3, reg);
-    }
-#endif
-}
-
 /* Output any necessary .register pseudo-ops.  */
+
 void
 sparc_output_scratch_registers (FILE *file ATTRIBUTE_UNUSED)
 {
@@ -4291,101 +4302,260 @@ sparc_output_scratch_registers (FILE *file ATTRIBUTE_UNUSED)
 #endif
 }
 
-/* This function generates the assembly code for function entry.
-   FILE is a stdio stream to output the code to.
-   SIZE is an int: how many units of temporary storage to allocate.
-   Refer to the array `regs_ever_live' to determine which registers
-   to save; `regs_ever_live[I]' is nonzero if register number I
-   is ever used in the function.  This macro is responsible for
-   knowing which registers should not be saved even if used.  */
+/* Save/restore call-saved registers from LOW to HIGH at BASE+OFFSET
+   as needed.  LOW should be double-word aligned for 32-bit registers.
+   Return the new OFFSET.  */
 
-/* On SPARC, move-double insns between fpu and cpu need an 8-byte block
-   of memory.  If any fpu reg is used in the function, we allocate
-   such a block here, at the bottom of the frame, just in case it's needed.
+#define SORR_SAVE    0
+#define SORR_RESTORE 1
 
-   If this function is a leaf procedure, then we may choose not
-   to do a "save" insn.  The decision about whether or not
-   to do this is made in regclass.c.  */
+static int
+save_or_restore_regs (int low, int high, rtx base, int offset, int action)
+{
+  rtx mem, insn;
+  int i;
+
+  if (TARGET_ARCH64 && high <= 32)
+    {
+      for (i = low; i < high; i++)
+       {
+         if (regs_ever_live[i] && ! call_used_regs[i])
+           {
+             mem = gen_rtx_MEM (DImode, plus_constant (base, offset));
+             set_mem_alias_set (mem, sparc_sr_alias_set);
+             if (action == SORR_SAVE)
+               {
+                 insn = emit_move_insn (mem, gen_rtx_REG (DImode, i));
+                 RTX_FRAME_RELATED_P (insn) = 1;
+               }
+             else  /* action == SORR_RESTORE */
+               emit_move_insn (gen_rtx_REG (DImode, i), mem);
+             offset += 8;
+           }
+       }
+    }
+  else
+    {
+      for (i = low; i < high; i += 2)
+       {
+         bool reg0 = regs_ever_live[i] && ! call_used_regs[i];
+         bool reg1 = regs_ever_live[i+1] && ! call_used_regs[i+1];
+         enum machine_mode mode;
+         int regno;
+
+         if (reg0 && reg1)
+           {
+             mode = i < 32 ? DImode : DFmode;
+             regno = i;
+           }
+         else if (reg0)
+           {
+             mode = i < 32 ? SImode : SFmode;
+             regno = i;
+           }
+         else if (reg1)
+           {
+             mode = i < 32 ? SImode : SFmode;
+             regno = i + 1;
+             offset += 4;
+           }
+         else
+           continue;
+
+         mem = gen_rtx_MEM (mode, plus_constant (base, offset));
+         set_mem_alias_set (mem, sparc_sr_alias_set);
+         if (action == SORR_SAVE)
+           {
+             insn = emit_move_insn (mem, gen_rtx_REG (mode, regno));
+             RTX_FRAME_RELATED_P (insn) = 1;
+           }
+         else  /* action == SORR_RESTORE */
+           emit_move_insn (gen_rtx_REG (mode, regno), mem);
+
+         /* Always preserve double-word alignment.  */
+         offset = (offset + 7) & -8;
+       }
+    }
+
+  return offset;
+}
+
+/* Emit code to save call-saved registers.  */
+
+static void
+emit_save_regs (void)
+{
+  HOST_WIDE_INT offset;
+  rtx base;
+
+  offset = frame_base_offset - apparent_fsize;
+
+  if (offset < -4096 || offset + num_gfregs * 4 > 4096)
+    {
+      /* ??? This might be optimized a little as %g1 might already have a
+        value close enough that a single add insn will do.  */
+      /* ??? Although, all of this is probably only a temporary fix
+        because if %g1 can hold a function result, then
+        sparc_expand_epilogue will lose (the result will be
+        clobbered).  */
+      base = gen_rtx_REG (Pmode, 1);
+      emit_move_insn (base, GEN_INT (offset));
+      emit_insn (gen_rtx_SET (VOIDmode,
+                             base,
+                             gen_rtx_PLUS (Pmode, frame_base_reg, base)));
+      offset = 0;
+    }
+  else
+    base = frame_base_reg;
+
+  offset = save_or_restore_regs (0, 8, base, offset, SORR_SAVE);
+  save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, SORR_SAVE);
+}
+
+/* Emit code to restore call-saved registers.  */
 
 static void
-sparc_output_function_prologue (FILE *file, HOST_WIDE_INT size)
+emit_restore_regs (void)
 {
-  sparc_function_prologue (file, size,
-                          current_function_uses_only_leaf_regs);
+  HOST_WIDE_INT offset;
+  rtx base;
+
+  offset = frame_base_offset - apparent_fsize;
+
+  if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/)
+    {
+      base = gen_rtx_REG (Pmode, 1);
+      emit_move_insn (base, GEN_INT (offset));
+      emit_insn (gen_rtx_SET (VOIDmode,
+                             base,
+                             gen_rtx_PLUS (Pmode, frame_base_reg, base)));
+      offset = 0;
+    }
+  else
+    base = frame_base_reg;
+
+  offset = save_or_restore_regs (0, 8, base, offset, SORR_RESTORE);
+  save_or_restore_regs (32, TARGET_V9 ? 96 : 64, base, offset, SORR_RESTORE);
 }
 
-/* Output code for the function prologue.  */
+/* Emit an increment for the stack pointer.  */
 
 static void
-sparc_function_prologue (FILE *file, HOST_WIDE_INT size, int leaf_function)
+emit_stack_pointer_increment (rtx increment)
 {
-  sparc_output_scratch_registers (file);
+  if (TARGET_ARCH64)
+    emit_insn (gen_adddi3 (stack_pointer_rtx, stack_pointer_rtx, increment));
+  else
+    emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, increment));
+}
+
+/* Emit a decrement for the stack pointer.  */
+
+static void
+emit_stack_pointer_decrement (rtx decrement)
+{
+  if (TARGET_ARCH64)
+    emit_insn (gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx, decrement));
+  else
+    emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, decrement));
+}
+
+/* Expand the function prologue.  The prologue is responsible for reserving
+   storage for the frame, saving the call-saved registers and loading the
+   PIC register if needed.  */
+
+void
+sparc_expand_prologue (void)
+{
+  int leaf_function_p = current_function_uses_only_leaf_regs;
 
   /* Need to use actual_fsize, since we are also allocating
      space for our callee (and our own register save area).  */
-  actual_fsize = compute_frame_size (size, leaf_function);
+  actual_fsize = sparc_compute_frame_size (get_frame_size(), leaf_function_p);
 
-  if (leaf_function)
+  if (leaf_function_p)
     {
-      frame_base_name = "%sp";
+      frame_base_reg = stack_pointer_rtx;
       frame_base_offset = actual_fsize + SPARC_STACK_BIAS;
     }
   else
     {
-      frame_base_name = "%fp";
+      frame_base_reg = hard_frame_pointer_rtx;
       frame_base_offset = SPARC_STACK_BIAS;
     }
 
-  /* This is only for the human reader.  */
-  fprintf (file, "\t%s#PROLOGUE# 0\n", ASM_COMMENT_START);
-
   if (actual_fsize == 0)
     /* do nothing.  */ ;
-  else if (! leaf_function)
+  else if (leaf_function_p)
     {
       if (actual_fsize <= 4096)
-       fprintf (file, "\tsave\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
-                actual_fsize);
+       emit_stack_pointer_increment (GEN_INT (- actual_fsize));
       else if (actual_fsize <= 8192)
        {
-         fprintf (file, "\tsave\t%%sp, -4096, %%sp\n");
-         fprintf (file, "\tadd\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
-                  actual_fsize - 4096);
+         emit_stack_pointer_increment (GEN_INT (-4096));
+         emit_stack_pointer_increment (GEN_INT (4096 - actual_fsize));
        }
       else
        {
-         build_big_number (file, -actual_fsize, "%g1");
-         fprintf (file, "\tsave\t%%sp, %%g1, %%sp\n");
+         rtx reg = gen_rtx_REG (Pmode, 1);
+         emit_move_insn (reg, GEN_INT (-actual_fsize));
+         emit_stack_pointer_increment (reg);
        }
     }
-  else /* leaf function */
+  else
     {
       if (actual_fsize <= 4096)
-       fprintf (file, "\tadd\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
-                actual_fsize);
+        emit_insn (gen_save_register_window (GEN_INT (-actual_fsize)));
       else if (actual_fsize <= 8192)
        {
-         fprintf (file, "\tadd\t%%sp, -4096, %%sp\n");
-         fprintf (file, "\tadd\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
-                  actual_fsize - 4096);
+         emit_insn (gen_save_register_window (GEN_INT (-4096)));
+         emit_stack_pointer_increment (GEN_INT (4096 - actual_fsize));
        }
       else
        {
-         build_big_number (file, -actual_fsize, "%g1");
-         fprintf (file, "\tadd\t%%sp, %%g1, %%sp\n");
+         rtx reg = gen_rtx_REG (Pmode, 1);
+         emit_move_insn (reg, GEN_INT (-actual_fsize));
+         emit_insn (gen_save_register_window (reg));
        }
     }
 
+  /* Call-saved registers are saved just above the outgoing argument area.  */
+  if (num_gfregs)
+    emit_save_regs ();
+
+  /* Load the PIC register if needed.  */
+  if (flag_pic && current_function_uses_pic_offset_table)
+    load_pic_register ();
+}
+/* This function generates the assembly code for function entry, which boils
+   down to emitting the necessary .register directives.  It also informs the
+   DWARF-2 back-end on the layout of the frame.
+
+   ??? Historical cruft: "On SPARC, move-double insns between fpu and cpu need
+   an 8-byte block of memory.  If any fpu reg is used in the function, we
+   allocate such a block here, at the bottom of the frame, just in case it's
+   needed."  Could this explain the -8 in emit_restore_regs?  */
+
+static void
+sparc_asm_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+  int leaf_function_p = current_function_uses_only_leaf_regs;
+
+  sparc_output_scratch_registers (file);
+
   if (dwarf2out_do_frame () && actual_fsize)
     {
       char *label = dwarf2out_cfi_label ();
 
       /* The canonical frame address refers to the top of the frame.  */
-      dwarf2out_def_cfa (label, (leaf_function ? STACK_POINTER_REGNUM
-                                : HARD_FRAME_POINTER_REGNUM),
+      dwarf2out_def_cfa (label,
+                        leaf_function_p
+                        ? STACK_POINTER_REGNUM
+                        : HARD_FRAME_POINTER_REGNUM,
                         frame_base_offset);
 
-      if (! leaf_function)
+      if (! leaf_function_p)
        {
          /* Note the register window save.  This tells the unwinder that
             it needs to restore the window registers from the previous
@@ -4396,233 +4566,224 @@ sparc_function_prologue (FILE *file, HOST_WIDE_INT size, int leaf_function)
          dwarf2out_return_reg (label, 31);
        }
     }
+}
 
-  /* If doing anything with PIC, do it now.  */
-  if (! flag_pic)
-    fprintf (file, "\t%s#PROLOGUE# 1\n", ASM_COMMENT_START);
+/* Expand the function epilogue, either normal or part of a sibcall.
+   We emit all the instructions except the return or the call.  */
+
+void
+sparc_expand_epilogue (void)
+{
+  int leaf_function_p = current_function_uses_only_leaf_regs;
 
-  /* Call saved registers are saved just above the outgoing argument area.  */
   if (num_gfregs)
-    {
-      HOST_WIDE_INT offset, real_offset;
-      int n_regs;
-      const char *base;
+    emit_restore_regs ();
 
-      real_offset = -apparent_fsize;
-      offset = -apparent_fsize + frame_base_offset;
-      if (offset < -4096 || offset + num_gfregs * 4 > 4096)
+  if (actual_fsize == 0)
+    /* do nothing.  */ ;
+  else if (leaf_function_p)
+    {
+      if (actual_fsize <= 4096)
+       emit_stack_pointer_decrement (GEN_INT (- actual_fsize));
+      else if (actual_fsize <= 8192)
        {
-         /* ??? This might be optimized a little as %g1 might already have a
-            value close enough that a single add insn will do.  */
-         /* ??? Although, all of this is probably only a temporary fix
-            because if %g1 can hold a function result, then
-            output_function_epilogue will lose (the result will get
-            clobbered).  */
-         build_big_number (file, offset, "%g1");
-         fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
-         base = "%g1";
-         offset = 0;
+         emit_stack_pointer_decrement (GEN_INT (-4096));
+         emit_stack_pointer_decrement (GEN_INT (4096 - actual_fsize));
        }
       else
        {
-         base = frame_base_name;
+         rtx reg = gen_rtx_REG (Pmode, 1);
+         emit_move_insn (reg, GEN_INT (-actual_fsize));
+         emit_stack_pointer_decrement (reg);
        }
-
-      n_regs = save_regs (file, 0, 8, base, offset, 0, real_offset);
-      save_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs,
-                real_offset);
     }
 }
+  
+/* This function generates the assembly code for function exit.  */
+  
+static void
+sparc_asm_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
+{
+  /* If code does not drop into the epilogue, we have to still output
+     a dummy nop for the sake of sane backtraces.  Otherwise, if the
+     last two instructions of a function were "call foo; dslot;" this
+     can make the return PC of foo (ie. address of call instruction
+     plus 8) point to the first instruction in the next function.  */
+
+  rtx insn, last_real_insn;
 
-/* Output code to restore any call saved registers.  */
+  insn = get_last_insn ();
 
+  last_real_insn = prev_real_insn (insn);
+  if (last_real_insn
+      && GET_CODE (last_real_insn) == INSN
+      && GET_CODE (PATTERN (last_real_insn)) == SEQUENCE)
+    last_real_insn = XVECEXP (PATTERN (last_real_insn), 0, 0);
+
+  if (last_real_insn && GET_CODE (last_real_insn) == CALL_INSN)
+    fputs("\tnop\n", file);
+
+  sparc_output_deferred_case_vectors ();
+}
+  
+/* Output a 'restore' instruction.  */
 static void
-output_restore_regs (FILE *file, int leaf_function ATTRIBUTE_UNUSED)
+output_restore (rtx pat)
 {
-  HOST_WIDE_INT offset;
-  int n_regs;
-  const char *base;
+  rtx operands[3];
 
-  offset = -apparent_fsize + frame_base_offset;
-  if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/)
+  if (! pat)
     {
-      build_big_number (file, offset, "%g1");
-      fprintf (file, "\tadd\t%s, %%g1, %%g1\n", frame_base_name);
-      base = "%g1";
-      offset = 0;
-    }
-  else
-    {
-      base = frame_base_name;
+      fputs ("\t restore\n", asm_out_file);
+      return;
     }
 
-  n_regs = restore_regs (file, 0, 8, base, offset, 0);
-  restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs);
-}
-
-/* This function generates the assembly code for function exit,
-   on machines that need it.
+  if (GET_CODE (pat) != SET)
+    abort ();
 
-   The function epilogue should not depend on the current stack pointer!
-   It should use the frame pointer only.  This is mandatory because
-   of alloca; we also take advantage of it to omit stack adjustments
-   before returning.  */
+  operands[0] = SET_DEST (pat);
+  pat = SET_SRC (pat);
 
-static void
-sparc_output_function_epilogue (FILE *file, HOST_WIDE_INT size)
-{
-  sparc_function_epilogue (file, size,
-                          current_function_uses_only_leaf_regs);
+  switch (GET_CODE (pat))
+    {
+      case PLUS:
+       operands[1] = XEXP (pat, 0);
+       operands[2] = XEXP (pat, 1);
+       output_asm_insn (" restore %r1, %2, %Y0", operands);
+       break;
+      case LO_SUM:
+       operands[1] = XEXP (pat, 0);
+       operands[2] = XEXP (pat, 1);
+       output_asm_insn (" restore %r1, %%lo(%a2), %Y0", operands);
+       break;
+      case ASHIFT:
+       operands[1] = XEXP (pat, 0);
+       if (XEXP (pat, 1) != const1_rtx)
+         abort();
+       output_asm_insn (" restore %r1, %r1, %Y0", operands);
+       break;
+      default:
+       operands[1] = pat;
+       output_asm_insn (" restore %%g0, %1, %Y0", operands);
+       break;
+    }
 }
+  
+/* Output a return.  */
 
-/* Output code for the function epilogue.  */
-
-static void
-sparc_function_epilogue (FILE *file,
-                        HOST_WIDE_INT size ATTRIBUTE_UNUSED,
-                        int leaf_function)
+const char *
+output_return (rtx insn)
 {
-  const char *ret;
-
+  int leaf_function_p = current_function_uses_only_leaf_regs;
+  bool delay_slot_filled_p = dbr_sequence_length () > 0;
   /* True if the caller has placed an "unimp" insn immediately after the call.
      This insn is used in the 32-bit ABI when calling a function that returns
      a non zero-sized structure. The 64-bit ABI doesn't have it.  Be careful
      to have this test be the same as that used on the call.  */
-  sparc_skip_caller_unimp =
-    ! TARGET_ARCH64
-    && current_function_returns_struct
-    && (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
-       == INTEGER_CST)
-    && ! integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl)));
+  bool sparc_skip_caller_unimp
+    ! TARGET_ARCH64
+      && current_function_returns_struct
+      && (TREE_CODE (DECL_SIZE (DECL_RESULT (current_function_decl)))
+         == INTEGER_CST)
+      && ! integer_zerop (DECL_SIZE (DECL_RESULT (current_function_decl)));
 
-  if (current_function_epilogue_delay_list == 0)
+  if (leaf_function_p)
     {
-      /* If code does not drop into the epilogue, we need
-        do nothing except output pending case vectors.
+      /* This is a leaf function so we don't have to bother restoring the
+        register window, which frees us from dealing with the convoluted
+        semantics of restore/return.  We simply output the jump to the
+        return address and the insn in the delay slot, which usually is
+        the substraction restoring the stack pointer %sp.  */
 
-        We have to still output a dummy nop for the sake of
-        sane backtraces.  Otherwise, if the last two instructions
-        of a function were call foo; dslot; this can make the return
-        PC of foo (ie. address of call instruction plus 8) point to
-        the first instruction in the next function.  */
-      rtx insn, last_real_insn;
-
-      insn = get_last_insn ();
+      if (current_function_calls_eh_return)
+       abort ();
 
-      last_real_insn = prev_real_insn (insn);
-      if (last_real_insn
-         && GET_CODE (last_real_insn) == INSN
-         && GET_CODE (PATTERN (last_real_insn)) == SEQUENCE)
-       last_real_insn = XVECEXP (PATTERN (last_real_insn), 0, 0);
+      fprintf (asm_out_file, "\tjmp\t%%o7+%d\n", sparc_skip_caller_unimp ? 12 : 8);
 
-      if (last_real_insn && GET_CODE (last_real_insn) == CALL_INSN)
-       fputs("\tnop\n", file);
+      if (delay_slot_filled_p)
+       {
+         rtx delay = NEXT_INSN (insn);
+         if (! delay)
+           abort ();
 
-      if (GET_CODE (insn) == NOTE)
-             insn = prev_nonnote_insn (insn);
-      if (insn && GET_CODE (insn) == BARRIER)
-             goto output_vectors;
+         final_scan_insn (delay, asm_out_file, 1, 0, 2, NULL);
+         PATTERN (delay) = gen_blockage ();
+         INSN_CODE (delay) = -1;
+       }
+      else
+       fputs ("\t nop\n", asm_out_file);
     }
-
-  if (num_gfregs)
-    output_restore_regs (file, leaf_function);
-
-  /* Work out how to skip the caller's unimp instruction if required.  */
-  if (leaf_function)
-    ret = (sparc_skip_caller_unimp ? "jmp\t%o7+12" : "retl");
   else
-    ret = (sparc_skip_caller_unimp ? "jmp\t%i7+12" : "ret");
-
-  if (! leaf_function)
     {
+      /* This is a regular function so we have to restore the register window.
+        We may have a pending insn for the delay slot, which will be either
+        combined with the 'restore' instruction or put in the delay slot of
+        the 'return' instruction.  */
+
       if (current_function_calls_eh_return)
        {
-         if (current_function_epilogue_delay_list)
-           abort ();
-         if (sparc_skip_caller_unimp)
+         /* If the function uses __builtin_eh_return, the eh_return
+            machinery occupies the delay slot.  */
+         if (delay_slot_filled_p || sparc_skip_caller_unimp)
            abort ();
 
-         fputs ("\trestore\n\tretl\n\tadd\t%sp, %g1, %sp\n", file);
+         if (! flag_delayed_branch)
+           fputs ("\tadd\t%fp, %g1, %fp\n", asm_out_file);
+
+         if (TARGET_V9)
+           fputs ("\treturn\t%i7+8\n", asm_out_file);
+         else
+           fputs ("\trestore\n\tjmp\t%o7+8\n", asm_out_file);
+
+         if (flag_delayed_branch)
+           fputs ("\t add\t%sp, %g1, %sp\n", asm_out_file);
+         else
+           fputs ("\t nop\n", asm_out_file);
        }
-      /* If we wound up with things in our delay slot, flush them here.  */
-      else if (current_function_epilogue_delay_list)
+      else if (delay_slot_filled_p)
        {
-         rtx delay = PATTERN (XEXP (current_function_epilogue_delay_list, 0));
+         rtx delay, pat;
+
+         delay = NEXT_INSN (insn);
+         if (! delay)
+           abort ();
 
-         if (TARGET_V9 && ! epilogue_renumber (&delay, 1))
+         pat = PATTERN (delay);
+
+         if (TARGET_V9 && ! epilogue_renumber (&pat, 1))
            {
-             epilogue_renumber (&delay, 0);
-             fputs (sparc_skip_caller_unimp
-                    ? "\treturn\t%i7+12\n"
-                    : "\treturn\t%i7+8\n", file);
-             final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
-                              file, 1, 0, 0, NULL);
+             epilogue_renumber (&pat, 0);
+             fprintf (asm_out_file, "\treturn\t%%i7+%d\n",
+                      sparc_skip_caller_unimp ? 12 : 8);
+             final_scan_insn (delay, asm_out_file, 1, 0, 2, NULL);
            }
          else
            {
-             rtx insn, src;
-
-             if (GET_CODE (delay) != SET)
-               abort();
-
-             src = SET_SRC (delay);
-             if (GET_CODE (src) == ASHIFT)
-               {
-                 if (XEXP (src, 1) != const1_rtx)
-                   abort();
-                 SET_SRC (delay)
-                   = gen_rtx_PLUS (GET_MODE (src), XEXP (src, 0),
-                                   XEXP (src, 0));
-               }
-
-             insn = gen_rtx_PARALLEL (VOIDmode,
-                                      gen_rtvec (2, delay,
-                                                 gen_rtx_RETURN (VOIDmode)));
-             insn = emit_jump_insn (insn);
-
-             sparc_emitting_epilogue = true;
-             final_scan_insn (insn, file, 1, 0, 1, NULL);
-             sparc_emitting_epilogue = false;
+             fprintf (asm_out_file, "\tjmp\t%%i7+%d\n",
+                      sparc_skip_caller_unimp ? 12 : 8);
+             output_restore (pat);
            }
+
+         PATTERN (delay) = gen_blockage ();
+         INSN_CODE (delay) = -1;
        }
-      else if (TARGET_V9 && ! sparc_skip_caller_unimp)
-       fputs ("\treturn\t%i7+8\n\tnop\n", file);
       else
-       fprintf (file, "\t%s\n\trestore\n", ret);
-    }
-  /* All of the following cases are for leaf functions.  */
-  else if (current_function_calls_eh_return)
-    abort ();
-  else if (current_function_epilogue_delay_list)
-    {
-      /* eligible_for_epilogue_delay_slot ensures that if this is a
-        leaf function, then we will only have insn in the delay slot
-        if the frame size is zero, thus no adjust for the stack is
-        needed here.  */
-      if (actual_fsize != 0)
-       abort ();
-      fprintf (file, "\t%s\n", ret);
-      final_scan_insn (XEXP (current_function_epilogue_delay_list, 0),
-                      file, 1, 0, 1, NULL);
-    }
-  /* Output 'nop' instead of 'sub %sp,-0,%sp' when no frame, so as to
-        avoid generating confusing assembly language output.  */
-  else if (actual_fsize == 0)
-    fprintf (file, "\t%s\n\tnop\n", ret);
-  else if (actual_fsize <= 4096)
-    fprintf (file, "\t%s\n\tsub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
-            ret, actual_fsize);
-  else if (actual_fsize <= 8192)
-    fprintf (file, "\tsub\t%%sp, -4096, %%sp\n\t%s\n\tsub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n",
-            ret, actual_fsize - 4096);
-  else
-    {
-      build_big_number (file, actual_fsize, "%g1");
-      fprintf (file, "\t%s\n\tadd\t%%sp, %%g1, %%sp\n", ret);
+        {
+         /* The delay slot is empty.  */
+         if (TARGET_V9)
+           fprintf (asm_out_file, "\treturn\t%%i7+%d\n\t nop\n",
+                    sparc_skip_caller_unimp ? 12 : 8);
+         else if (flag_delayed_branch)
+           fprintf (asm_out_file, "\tjmp\t%%i7+%d\n\t restore\n",
+                    sparc_skip_caller_unimp ? 12 : 8);
+         else
+           fprintf (asm_out_file, "\trestore\n\tjmp\t%%o7+%d\n\t nop\n",
+                    sparc_skip_caller_unimp ? 12 : 8);
+       }
     }
 
- output_vectors:
-  sparc_output_deferred_case_vectors ();
+  return "";
 }
 
 /* Output a sibling call.  */
@@ -4630,134 +4791,69 @@ sparc_function_epilogue (FILE *file,
 const char *
 output_sibcall (rtx insn, rtx call_operand)
 {
-  int leaf_regs = current_function_uses_only_leaf_regs;
-  rtx operands[3];
-  int delay_slot = dbr_sequence_length () > 0;
-
-  if (num_gfregs)
-    {
-      /* Call to restore global regs might clobber
-        the delay slot. Instead of checking for this
-        output the delay slot now.  */
-      if (delay_slot)
-       {
-         rtx delay = NEXT_INSN (insn);
-
-         if (! delay)
-           abort ();
+  int leaf_function_p = current_function_uses_only_leaf_regs;
+  bool delay_slot_filled_p = dbr_sequence_length () > 0;
+  rtx operands[1];
 
-         final_scan_insn (delay, asm_out_file, 1, 0, 1, NULL);
-         PATTERN (delay) = gen_blockage ();
-         INSN_CODE (delay) = -1;
-         delay_slot = 0;
-       }
-      output_restore_regs (asm_out_file, leaf_regs);
-    }
+  if (! flag_delayed_branch)
+    abort();
 
   operands[0] = call_operand;
 
-  if (leaf_regs)
+  if (leaf_function_p)
     {
-#ifdef HAVE_AS_RELAX_OPTION
-      /* If as and ld are relaxing tail call insns into branch always,
-        use or %o7,%g0,X; call Y; or X,%g0,%o7 always, so that it can
-        be optimized.  With sethi/jmpl as nor ld has no easy way how to
-        find out if somebody does not branch between the sethi and jmpl.  */
-      int spare_slot = 0;
-#else
-      int spare_slot = ((TARGET_ARCH32 || TARGET_CM_MEDLOW) && ! flag_pic);
-#endif
-      HOST_WIDE_INT size = 0;
+      /* This is a leaf function so we don't have to bother restoring the
+        register window.  We simply output the jump to the function and
+        the insn in the delay slot (if any).  */
 
-      if ((actual_fsize || ! spare_slot) && delay_slot)
+      if (LEAF_SIBCALL_SLOT_RESERVED_P && delay_slot_filled_p)
+       abort();
+
+      if (delay_slot_filled_p)
        {
          rtx delay = NEXT_INSN (insn);
-
          if (! delay)
            abort ();
 
-         final_scan_insn (delay, asm_out_file, 1, 0, 1, NULL);
+         output_asm_insn ("sethi\t%%hi(%a0), %%g1", operands);
+         output_asm_insn ("jmp\t%%g1 + %%lo(%a0)", operands);
+         final_scan_insn (delay, asm_out_file, 1, 0, 2, NULL);
+
          PATTERN (delay) = gen_blockage ();
          INSN_CODE (delay) = -1;
-         delay_slot = 0;
-       }
-      if (actual_fsize)
-       {
-         if (actual_fsize <= 4096)
-           size = actual_fsize;
-         else if (actual_fsize <= 8192)
-           {
-             fputs ("\tsub\t%sp, -4096, %sp\n", asm_out_file);
-             size = actual_fsize - 4096;
-           }
-         else
-           {
-             build_big_number (asm_out_file, actual_fsize, "%g1");
-             fputs ("\tadd\t%%sp, %%g1, %%sp\n", asm_out_file);
-           }
-       }
-      if (spare_slot)
-       {
-         output_asm_insn ("sethi\t%%hi(%a0), %%g1", operands);
-         output_asm_insn ("jmpl\t%%g1 + %%lo(%a0), %%g0", operands);
-         if (size)
-           fprintf (asm_out_file, "\t sub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n", size);
-         else if (! delay_slot)
-           fputs ("\t nop\n", asm_out_file);
        }
       else
        {
-         if (size)
-           fprintf (asm_out_file, "\tsub\t%%sp, -"HOST_WIDE_INT_PRINT_DEC", %%sp\n", size);
          /* Use or with rs2 %%g0 instead of mov, so that as/ld can optimize
             it into branch if possible.  */
          output_asm_insn ("or\t%%o7, %%g0, %%g1", operands);
          output_asm_insn ("call\t%a0, 0", operands);
          output_asm_insn (" or\t%%g1, %%g0, %%o7", operands);
        }
-      return "";
     }
-
-  output_asm_insn ("call\t%a0, 0", operands);
-  if (delay_slot)
+  else
     {
-      rtx delay = NEXT_INSN (insn), pat;
-
-      if (! delay)
-       abort ();
+      /* This is a regular function so we have to restore the register window.
+        We may have a pending insn for the delay slot, which will be combined
+        with the 'restore' instruction.  */
 
-      pat = PATTERN (delay);
-      if (GET_CODE (pat) != SET)
-       abort ();
+      output_asm_insn ("call\t%a0, 0", operands);
 
-      operands[0] = SET_DEST (pat);
-      pat = SET_SRC (pat);
-      switch (GET_CODE (pat))
+      if (delay_slot_filled_p)
        {
-       case PLUS:
-         operands[1] = XEXP (pat, 0);
-         operands[2] = XEXP (pat, 1);
-         output_asm_insn (" restore %r1, %2, %Y0", operands);
-         break;
-       case LO_SUM:
-         operands[1] = XEXP (pat, 0);
-         operands[2] = XEXP (pat, 1);
-         output_asm_insn (" restore %r1, %%lo(%a2), %Y0", operands);
-         break;
-       case ASHIFT:
-         operands[1] = XEXP (pat, 0);
-         output_asm_insn (" restore %r1, %r1, %Y0", operands);
-         break;
-       default:
-         operands[1] = pat;
-         output_asm_insn (" restore %%g0, %1, %Y0", operands);
-         break;
+         rtx delay = NEXT_INSN (insn);
+         if (! delay)
+           abort ();
+
+         output_restore (PATTERN (delay));
+
+         PATTERN (delay) = gen_blockage ();
+         INSN_CODE (delay) = -1;
        }
-      PATTERN (delay) = gen_blockage ();
-      INSN_CODE (delay) = -1;
+      else
+       output_restore (NULL_RTX);
     }
-  else
-    fputs ("\t restore\n", asm_out_file);
+
   return "";
 }
 \f
@@ -5103,7 +5199,7 @@ static void function_arg_record_value_2
 static void function_arg_record_value_1
  (tree, HOST_WIDE_INT, struct function_arg_record_value_parms *, bool);
 static rtx function_arg_record_value (tree, enum machine_mode, int, int, int);
-static rtx function_arg_union_value (int, int);
+static rtx function_arg_union_value (int, enum machine_mode, int);
 
 /* A subroutine of function_arg_record_value.  Traverse the structure
    recursively and determine how many registers will be required.  */
@@ -5445,26 +5541,25 @@ function_arg_record_value (tree type, enum machine_mode mode,
    FUNCTION_ARG and FUNCTION_VALUE.
 
    SIZE is the size in bytes of the union.
+   MODE is the argument's machine mode.
    REGNO is the hard register the union will be passed in.  */
 
 static rtx
-function_arg_union_value (int size, int regno)
+function_arg_union_value (int size, enum machine_mode mode, int regno)
 {
-  enum machine_mode mode;
-  rtx reg;
+  int nwords = ROUND_ADVANCE (size), i;
+  rtx regs;
 
-  if (size <= UNITS_PER_WORD)
-    mode = word_mode;
-  else
-    mode = mode_for_size (size * BITS_PER_UNIT, MODE_INT, 0);
+  /* Unions are passed left-justified.  */
+  regs = gen_rtx_PARALLEL (mode, rtvec_alloc (nwords));
 
-  reg = gen_rtx_REG (mode, regno);
+  for (i = 0; i < nwords; i++)
+    XVECEXP (regs, 0, i)
+      = gen_rtx_EXPR_LIST (VOIDmode,
+                          gen_rtx_REG (word_mode, regno + i),
+                          GEN_INT (UNITS_PER_WORD * i));
 
-  /* Unions are passed left-justified.  */
-  return gen_rtx_PARALLEL (mode,
-                          gen_rtvec (1, gen_rtx_EXPR_LIST (VOIDmode,
-                                                           reg,
-                                                           const0_rtx)));
+  return regs;
 }
 
 /* Handle the FUNCTION_ARG macro.
@@ -5521,7 +5616,7 @@ function_arg (const struct sparc_args *cum, enum machine_mode mode,
       if (size > 16)
        abort (); /* shouldn't get here */
 
-      return function_arg_union_value (size, regno);
+      return function_arg_union_value (size, mode, regno);
     }
   /* v9 fp args in reg slots beyond the int reg slots get passed in regs
      but also have the slot allocated for them.
@@ -5636,8 +5731,8 @@ function_arg_partial_nregs (const struct sparc_args *cum,
     }
   else
     {
-      /* We are guaranteed by function_arg_pass_by_reference that the size
-        of the argument is not greater than 16 bytes, so we only need to
+      /* We are guaranteed by pass_by_reference that the size of the
+        argument is not greater than 16 bytes, so we only need to
         return 1 if the argument is partially passed in registers.  */
 
       if (type && AGGREGATE_TYPE_P (type))
@@ -5668,16 +5763,16 @@ function_arg_partial_nregs (const struct sparc_args *cum,
   return 0;
 }
 
-/* Handle the FUNCTION_ARG_PASS_BY_REFERENCE macro.
+/* Return true if the argument should be passed by reference.
    !v9: The SPARC ABI stipulates passing struct arguments (of any size) and
    quad-precision floats by invisible reference.
    v9: Aggregates greater than 16 bytes are passed by reference.
    For Pascal, also pass arrays by reference.  */
 
-int
-function_arg_pass_by_reference (const struct sparc_args *cum ATTRIBUTE_UNUSED,
-                               enum machine_mode mode, tree type,
-                               int named ATTRIBUTE_UNUSED)
+static bool
+sparc_pass_by_reference (CUMULATIVE_ARGS *cum ATTRIBUTE_UNUSED,
+                        enum machine_mode mode, tree type,
+                        bool named ATTRIBUTE_UNUSED)
 {
   if (TARGET_ARCH32)
     {
@@ -5846,7 +5941,7 @@ function_value (tree type, enum machine_mode mode, int incoming_p)
          if (size > 32)
            abort (); /* shouldn't get here */
 
-         return function_arg_union_value (size, regbase);
+         return function_arg_union_value (size, mode, regbase);
        }
       else if (AGGREGATE_TYPE_P (type))
        {
@@ -5858,6 +5953,18 @@ function_value (tree type, enum machine_mode mode, int incoming_p)
            abort (); /* shouldn't get here */
 
          mode = mode_for_size (bytes * BITS_PER_UNIT, MODE_INT, 0);
+
+         /* ??? We probably should have made the same ABI change in
+            3.4.0 as the one we made for unions.   The latter was
+            required by the SCD though, while the former is not
+            specified, so we favored compatibility and efficiency.
+
+            Now we're stuck for aggregates larger than 16 bytes,
+            because OImode vanished in the meantime.  Let's not
+            try to be unduly clever, and simply follow the ABI
+            for unions in that case.  */
+         if (mode == BLKmode)
+           return function_arg_union_value (bytes, mode, regbase);
        }
       else if (GET_MODE_CLASS (mode) == MODE_INT
               && GET_MODE_SIZE (mode) < UNITS_PER_WORD)
@@ -5912,15 +6019,15 @@ sparc_va_start (tree valist, rtx nextarg)
 
 /* Implement `va_arg' for stdarg.  */
 
-rtx
-sparc_va_arg (tree valist, tree type)
+static tree
+sparc_gimplify_va_arg (tree valist, tree type, tree *pre_p, tree *post_p)
 {
   HOST_WIDE_INT size, rsize, align;
   tree addr, incr;
-  rtx addr_rtx;
   bool indirect;
+  tree ptrtype = build_pointer_type (type);
 
-  if (function_arg_pass_by_reference (0, TYPE_MODE (type), type, 0))
+  if (pass_by_reference (NULL, TYPE_MODE (type), type, 0))
     {
       indirect = true;
       size = rsize = UNITS_PER_WORD;
@@ -5954,87 +6061,131 @@ sparc_va_arg (tree valist, tree type)
   incr = valist;
   if (align)
     {
-      incr = fold (build (PLUS_EXPR, ptr_type_node, incr,
-                        build_int_2 (align - 1, 0)));
-      incr = fold (build (BIT_AND_EXPR, ptr_type_node, incr,
-                         build_int_2 (-align, -1)));
+      incr = fold (build2 (PLUS_EXPR, ptr_type_node, incr,
+                          ssize_int (align - 1)));
+      incr = fold (build2 (BIT_AND_EXPR, ptr_type_node, incr,
+                          ssize_int (-align)));
     }
 
-  addr = incr = save_expr (incr);
+  gimplify_expr (&incr, pre_p, post_p, is_gimple_val, fb_rvalue);
+  addr = incr;
+
   if (BYTES_BIG_ENDIAN && size < rsize)
+    addr = fold (build2 (PLUS_EXPR, ptr_type_node, incr,
+                        ssize_int (rsize - size)));
+
+  if (indirect)
     {
-      addr = fold (build (PLUS_EXPR, ptr_type_node, incr,
-                         build_int_2 (rsize - size, 0)));
+      addr = fold_convert (build_pointer_type (ptrtype), addr);
+      addr = build_fold_indirect_ref (addr);
     }
-  incr = fold (build (PLUS_EXPR, ptr_type_node, incr,
-                     build_int_2 (rsize, 0)));
-
-  incr = build (MODIFY_EXPR, ptr_type_node, valist, incr);
-  TREE_SIDE_EFFECTS (incr) = 1;
-  expand_expr (incr, const0_rtx, VOIDmode, EXPAND_NORMAL);
-
-  addr_rtx = expand_expr (addr, NULL, Pmode, EXPAND_NORMAL);
-
   /* If the address isn't aligned properly for the type,
      we may need to copy to a temporary.  
      FIXME: This is inefficient.  Usually we can do this
      in registers.  */
-  if (align == 0
-      && TYPE_ALIGN (type) > BITS_PER_WORD
-      && !indirect)
-    {
-      /* FIXME: We really need to specify that the temporary is live
-        for the whole function because expand_builtin_va_arg wants
-        the alias set to be get_varargs_alias_set (), but in this
-        case the alias set is that for TYPE and if the memory gets
-        reused it will be reused with alias set TYPE.  */
-      rtx tmp = assign_temp (type, 0, 1, 0);
-      rtx dest_addr;
-
-      addr_rtx = force_reg (Pmode, addr_rtx);
-      addr_rtx = gen_rtx_MEM (BLKmode, addr_rtx);
-      set_mem_alias_set (addr_rtx, get_varargs_alias_set ());
-      set_mem_align (addr_rtx, BITS_PER_WORD);
-      tmp = shallow_copy_rtx (tmp);
-      PUT_MODE (tmp, BLKmode);
-      set_mem_alias_set (tmp, 0);
-      
-      dest_addr = emit_block_move (tmp, addr_rtx, GEN_INT (rsize),
-                                  BLOCK_OP_NORMAL);
-      if (dest_addr != NULL_RTX)
-       addr_rtx = dest_addr;
-      else
-       addr_rtx = XCEXP (tmp, 0, MEM);
+  else if (align == 0
+          && TYPE_ALIGN (type) > BITS_PER_WORD)
+    {
+      tree tmp = create_tmp_var (type, "va_arg_tmp");
+      tree dest_addr = build_fold_addr_expr (tmp);
+
+      tree copy = build_function_call_expr
+       (implicit_built_in_decls[BUILT_IN_MEMCPY],
+        tree_cons (NULL_TREE, dest_addr,
+                   tree_cons (NULL_TREE, addr,
+                              tree_cons (NULL_TREE, size_int (rsize),
+                                         NULL_TREE))));
+
+      gimplify_and_add (copy, pre_p);
+      addr = dest_addr;
     }
+  else
+    addr = fold_convert (ptrtype, addr);
 
-  if (indirect)
+  incr = fold (build2 (PLUS_EXPR, ptr_type_node, incr, ssize_int (rsize)));
+  incr = build2 (MODIFY_EXPR, ptr_type_node, valist, incr);
+  gimplify_and_add (incr, post_p);
+
+  return build_fold_indirect_ref (addr);
+}
+\f
+/* Return the string to output an unconditional branch to LABEL, which is
+   the operand number of the label.
+
+   DEST is the destination insn (i.e. the label), INSN is the source.  */
+
+const char *
+output_ubranch (rtx dest, int label, rtx insn)
+{
+  static char string[64];
+  bool noop = false;
+  char *p;
+
+  /* TurboSPARC is reported to have problems with
+     with
+       foo: b,a foo
+     i.e. an empty loop with the annul bit set.  The workaround is to use 
+        foo: b foo; nop
+     instead.  */
+
+  if (! TARGET_V9 && flag_delayed_branch
+      && (INSN_ADDRESSES (INSN_UID (dest))
+         == INSN_ADDRESSES (INSN_UID (insn))))
     {
-      addr_rtx = force_reg (Pmode, addr_rtx);
-      addr_rtx = gen_rtx_MEM (Pmode, addr_rtx);
-      set_mem_alias_set (addr_rtx, get_varargs_alias_set ());
+      strcpy (string, "b\t");
+      noop = true;
     }
+  else
+    {
+      bool v9_form = false;
+
+      if (TARGET_V9 && INSN_ADDRESSES_SET_P ())
+       {
+         int delta = (INSN_ADDRESSES (INSN_UID (dest))
+                      - INSN_ADDRESSES (INSN_UID (insn)));
+         /* Leave some instructions for "slop".  */
+         if (delta >= -260000 && delta < 260000)
+           v9_form = true;
+       }
 
-  return addr_rtx;
+      if (v9_form)
+       strcpy (string, "ba%*,pt\t%%xcc, ");
+      else
+       strcpy (string, "b%*\t");
+    }
+
+  p = strchr (string, '\0');
+  *p++ = '%';
+  *p++ = 'l';
+  *p++ = '0' + label;
+  *p++ = '%';
+  if (noop)
+    *p++ = '#';
+  else
+    *p++ = '(';
+  *p = '\0';
+
+  return string;
 }
-\f
+
 /* Return the string to output a conditional branch to LABEL, which is
    the operand number of the label.  OP is the conditional expression.
    XEXP (OP, 0) is assumed to be a condition code register (integer or
    floating point) and its mode specifies what kind of comparison we made.
 
+   DEST is the destination insn (i.e. the label), INSN is the source.
+
    REVERSED is nonzero if we should reverse the sense of the comparison.
 
    ANNUL is nonzero if we should generate an annulling branch.
 
-   NOOP is nonzero if we have to follow this branch by a noop.
-
-   INSN, if set, is the insn.  */
+   NOOP is nonzero if we have to follow this branch by a noop.  */
 
-char *
+const char *
 output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
                int noop, rtx insn)
 {
-  static char string[50];
+  static char string[64];
   enum rtx_code code = GET_CODE (op);
   rtx cc_reg = XEXP (op, 0);
   enum machine_mode mode = GET_MODE (cc_reg);
@@ -6050,7 +6201,7 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
      to
 
      be,pn %xcc, .+12
-     nop
+      nop
      ba .LC30
 
      and
@@ -6060,10 +6211,10 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
      to
 
      fbe,pt %fcc2, .+16
-     nop
+      nop
      ba .LC29  */
 
-  far = get_attr_length (insn) >= 3;
+  far = TARGET_V9 && (get_attr_length (insn) >= 3);
   if (reversed ^ far)
     {
       /* Reversal of FP compares takes care -- an ordered compare
@@ -6193,9 +6344,7 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
       spaces -= 2;
     }
 
-  if (! TARGET_V9)
-    labelno = "";
-  else
+  if (TARGET_V9)
     {
       rtx note;
       int v8 = 0;
@@ -6245,6 +6394,9 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
          spaces -= 3;
        }
     }
+  else
+    labelno = "";
+
   if (spaces > 0)
     *p++ = '\t';
   else
@@ -6253,10 +6405,10 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
   p = strchr (p, '\0');
   if (far)
     {
-      strcpy (p, ".+12\n\tnop\n\tb\t");
+      strcpy (p, ".+12\n\t nop\n\tb\t");
       if (annul || noop)
         p[3] = '6';
-      p += 13;
+      p += 14;
     }
   *p++ = '%';
   *p++ = 'l';
@@ -6265,7 +6417,7 @@ output_cbranch (rtx op, rtx dest, int label, int reversed, int annul,
   *p++ = label + '0';
   *p = '\0';
   if (noop)
-    strcpy (p, "\n\tnop");
+    strcpy (p, "\n\t nop");
 
   return string;
 }
@@ -6488,17 +6640,19 @@ sparc_emit_fixunsdi (rtx *operands, enum machine_mode mode)
    operand number of the reg.  OP is the conditional expression.  The mode
    of REG says what kind of comparison we made.
 
+   DEST is the destination insn (i.e. the label), INSN is the source.
+
    REVERSED is nonzero if we should reverse the sense of the comparison.
 
    ANNUL is nonzero if we should generate an annulling branch.
 
    NOOP is nonzero if we have to follow this branch by a noop.  */
 
-char *
+const char *
 output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
                 int annul, int noop, rtx insn)
 {
-  static char string[50];
+  static char string[64];
   enum rtx_code code = GET_CODE (op);
   enum machine_mode mode = GET_MODE (XEXP (op, 0));
   rtx note;
@@ -6513,7 +6667,7 @@ output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
      to
      
      brz,pn %g1, .+12
-     nop
+      nop
      ba,pt %xcc, .LC30
      
      and
@@ -6523,7 +6677,7 @@ output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
      to
      
      brlz,pt %o1, .+16
-     nop
+      nop
      ba,pt %xcc, .LC29  */
 
   far = get_attr_length (insn) >= 3;
@@ -6604,10 +6758,10 @@ output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
            veryfar = 0;
        }
 
-      strcpy (p, ".+12\n\tnop\n\t");
+      strcpy (p, ".+12\n\t nop\n\t");
       if (annul || noop)
         p[3] = '6';
-      p += 11;
+      p += 12;
       if (veryfar)
        {
          strcpy (p, "b\t");
@@ -6625,7 +6779,7 @@ output_v9branch (rtx op, rtx dest, int reg, int label, int reversed,
   *p = '\0';
 
   if (noop)
-    strcpy (p, "\n\tnop");
+    strcpy (p, "\n\t nop");
 
   return string;
 }
@@ -6953,7 +7107,8 @@ print_operand (FILE *file, rtx x, int code)
       /* Print out what we are using as the frame pointer.  This might
         be %fp, or might be %sp+offset.  */
       /* ??? What if offset is too big? Perhaps the caller knows it isn't? */
-      fprintf (file, "%s+"HOST_WIDE_INT_PRINT_DEC, frame_base_name, frame_base_offset);
+      fprintf (file, "%s+"HOST_WIDE_INT_PRINT_DEC,
+              reg_names[REGNO (frame_base_reg)], frame_base_offset);
       return;
     case '&':
       /* Print some local dynamic TLS name.  */
@@ -7440,7 +7595,7 @@ sparc_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
 
   /* Call __enable_execute_stack after writing onto the stack to make sure
      the stack address is accessible.  */
-#ifdef TRANSFER_FROM_TRAMPOLINE
+#ifdef ENABLE_EXECUTE_STACK
   emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
                      LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
 #endif
@@ -7481,7 +7636,7 @@ sparc64_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
 
   /* Call __enable_execute_stack after writing onto the stack to make sure
      the stack address is accessible.  */
-#ifdef TRANSFER_FROM_TRAMPOLINE
+#ifdef ENABLE_EXECUTE_STACK
   emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__enable_execute_stack"),
                      LCT_NORMAL, VOIDmode, 1, tramp, Pmode);
 #endif
@@ -7654,18 +7809,6 @@ sparc_sched_init (FILE *dump ATTRIBUTE_UNUSED,
 }
   
 static int
-sparc_use_dfa_pipeline_interface (void)
-{
-  if ((1 << sparc_cpu) &
-      ((1 << PROCESSOR_ULTRASPARC) | (1 << PROCESSOR_CYPRESS) |
-       (1 << PROCESSOR_SUPERSPARC) | (1 << PROCESSOR_HYPERSPARC) |
-       (1 << PROCESSOR_SPARCLITE86X) | (1 << PROCESSOR_TSC701) |
-       (1 << PROCESSOR_ULTRASPARC3)))
-    return 1;
-  return 0;
-}
-
-static int
 sparc_use_sched_lookahead (void)
 {
   if (sparc_cpu == PROCESSOR_ULTRASPARC
@@ -7925,8 +8068,8 @@ sparc_check_64 (rtx x, rtx insn)
 
 /* Returns assembly code to perform a DImode shift using
    a 64-bit global or out register on SPARC-V8+.  */
-char *
-sparc_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
+const char *
+output_v8plus_shift (rtx *operands, rtx insn, const char *opcode)
 {
   static char asm_code[60];
 
@@ -8005,7 +8148,10 @@ sparc_elf_asm_named_section (const char *name, unsigned int flags)
 #endif /* OBJECT_FORMAT_ELF */
 
 /* We do not allow indirect calls to be optimized into sibling calls.
-   
+
+   We cannot use sibling calls when delayed branches are disabled
+   because they will likely require the call delay slot to be filled.
+
    Also, on SPARC 32-bit we cannot emit a sibling call when the
    current function returns a structure.  This is because the "unimp
    after call" convention would cause the callee to return to the
@@ -8015,14 +8161,16 @@ sparc_elf_asm_named_section (const char *name, unsigned int flags)
    It may seem strange how this last case could occur.  Usually there
    is code after the call which jumps to epilogue code which dumps the
    return value into the struct return area.  That ought to invalidate
-   the sibling call right?  Well, in the c++ case we can end up passing
+   the sibling call right?  Well, in the C++ case we can end up passing
    the pointer to the struct return area to a constructor (which returns
    void) and then nothing else happens.  Such a sibling call would look
    valid without the added check here.  */
 static bool
 sparc_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
 {
-  return (decl && (TARGET_ARCH64 || ! current_function_returns_struct));
+  return (decl
+         && flag_delayed_branch
+         && (TARGET_ARCH64 || ! current_function_returns_struct));
 }
 \f
 /* libfunc renaming.  */
@@ -8122,20 +8270,6 @@ sparc_init_libfuncs (void)
   gofast_maybe_init_libfuncs ();
 }
 \f
-/* Use text section for a constant unless we need more alignment than
-   that offers.  */
-
-static void
-sparc_aout_select_rtx_section (enum machine_mode mode, rtx x,
-                              unsigned HOST_WIDE_INT align)
-{
-  if (align <= MAX_TEXT_ALIGN
-      && ! (flag_pic && symbolic_operand (x, mode)))
-    readonly_data_section ();
-  else
-    data_section ();
-}
-
 int
 sparc_extra_constraint_check (rtx op, int c, int strict)
 {
@@ -8202,368 +8336,205 @@ sparc_extra_constraint_check (rtx op, int c, int strict)
 static bool
 sparc_rtx_costs (rtx x, int code, int outer_code, int *total)
 {
+  enum machine_mode mode = GET_MODE (x);
+  bool float_mode_p = FLOAT_MODE_P (mode);
+
   switch (code)
     {
-    case PLUS: case MINUS: case ABS: case NEG:
-    case FLOAT: case UNSIGNED_FLOAT:
-    case FIX: case UNSIGNED_FIX:
-    case FLOAT_EXTEND: case FLOAT_TRUNCATE:
-      if (FLOAT_MODE_P (GET_MODE (x)))
-       {
-         switch (sparc_cpu)
-           {
-           case PROCESSOR_ULTRASPARC:
-           case PROCESSOR_ULTRASPARC3:
-             *total = COSTS_N_INSNS (4);
-             return true;
-
-           case PROCESSOR_SUPERSPARC:
-             *total = COSTS_N_INSNS (3);
-             return true;
-
-           case PROCESSOR_CYPRESS:
-             *total = COSTS_N_INSNS (5);
-             return true;
-
-           case PROCESSOR_HYPERSPARC:
-           case PROCESSOR_SPARCLITE86X:
-           default:
-             *total = COSTS_N_INSNS (1);
-             return true;
-           }
-       }
-
-      *total = COSTS_N_INSNS (1);
-      return true;
-
-    case SQRT:
-      switch (sparc_cpu)
+    case CONST_INT:
+      if (INTVAL (x) < 0x1000 && INTVAL (x) >= -0x1000)
        {
-       case PROCESSOR_ULTRASPARC:
-         if (GET_MODE (x) == SFmode)
-           *total = COSTS_N_INSNS (13);
-         else
-           *total = COSTS_N_INSNS (23);
-         return true;
-
-       case PROCESSOR_ULTRASPARC3:
-         if (GET_MODE (x) == SFmode)
-           *total = COSTS_N_INSNS (20);
-         else
-           *total = COSTS_N_INSNS (29);
-         return true;
-
-       case PROCESSOR_SUPERSPARC:
-         *total = COSTS_N_INSNS (12);
-         return true;
-
-       case PROCESSOR_CYPRESS:
-         *total = COSTS_N_INSNS (63);
-         return true;
-
-       case PROCESSOR_HYPERSPARC:
-       case PROCESSOR_SPARCLITE86X:
-         *total = COSTS_N_INSNS (17);
-         return true;
-
-       default:
-         *total = COSTS_N_INSNS (30);
+         *total = 0;
          return true;
        }
+      /* FALLTHRU */
 
-    case COMPARE:
-      if (FLOAT_MODE_P (GET_MODE (x)))
-       {
-         switch (sparc_cpu)
-           {
-           case PROCESSOR_ULTRASPARC:
-           case PROCESSOR_ULTRASPARC3:
-             *total = COSTS_N_INSNS (1);
-             return true;
-
-           case PROCESSOR_SUPERSPARC:
-             *total = COSTS_N_INSNS (3);
-             return true;
-
-           case PROCESSOR_CYPRESS:
-             *total = COSTS_N_INSNS (5);
-             return true;
-
-           case PROCESSOR_HYPERSPARC:
-           case PROCESSOR_SPARCLITE86X:
-           default:
-             *total = COSTS_N_INSNS (1);
-             return true;
-           }
-       }
+    case HIGH:
+      *total = 2;
+      return true;
 
-      /* ??? Maybe mark integer compares as zero cost on
-        ??? all UltraSPARC processors because the result
-        ??? can be bypassed to a branch in the same group.  */
+    case CONST:
+    case LABEL_REF:
+    case SYMBOL_REF:
+      *total = 4;
+      return true;
 
-      *total = COSTS_N_INSNS (1);
+    case CONST_DOUBLE:
+      if (GET_MODE (x) == DImode
+         && ((XINT (x, 3) == 0
+              && (unsigned HOST_WIDE_INT) XINT (x, 2) < 0x1000)
+             || (XINT (x, 3) == -1
+                 && XINT (x, 2) < 0
+                 && XINT (x, 2) >= -0x1000)))
+       *total = 0;
+      else
+       *total = 8;
       return true;
 
-    case MULT:
-      if (FLOAT_MODE_P (GET_MODE (x)))
+    case MEM:
+      /* If outer-code was a sign or zero extension, a cost
+        of COSTS_N_INSNS (1) was already added in.  This is
+        why we are subtracting it back out.  */
+      if (outer_code == ZERO_EXTEND)
        {
-         switch (sparc_cpu)
-           {
-           case PROCESSOR_ULTRASPARC:
-           case PROCESSOR_ULTRASPARC3:
-             *total = COSTS_N_INSNS (4);
-             return true;
-
-           case PROCESSOR_SUPERSPARC:
-             *total = COSTS_N_INSNS (3);
-             return true;
-
-           case PROCESSOR_CYPRESS:
-             *total = COSTS_N_INSNS (7);
-             return true;
-
-           case PROCESSOR_HYPERSPARC:
-           case PROCESSOR_SPARCLITE86X:
-             *total = COSTS_N_INSNS (1);
-             return true;
-
-           default:
-             *total = COSTS_N_INSNS (5);
-             return true;
-           }
+         *total = sparc_costs->int_zload - COSTS_N_INSNS (1);
        }
-
-      /* The latency is actually variable for Ultra-I/II
-        And if one of the inputs have a known constant
-        value, we could calculate this precisely.
-
-        However, for that to be useful we would need to
-        add some machine description changes which would
-        make sure small constants ended up in rs1 of the
-        multiply instruction.  This is because the multiply
-        latency is determined by the number of clear (or
-        set if the value is negative) bits starting from
-        the most significant bit of the first input.
-
-        The algorithm for computing num_cycles of a multiply
-        on Ultra-I/II is:
-
-               if (rs1 < 0)
-                       highest_bit = highest_clear_bit(rs1);
-               else
-                       highest_bit = highest_set_bit(rs1);
-               if (num_bits < 3)
-                       highest_bit = 3;
-               num_cycles = 4 + ((highest_bit - 3) / 2);
-
-        If we did that we would have to also consider register
-        allocation issues that would result from forcing such
-        a value into a register.
-
-        There are other similar tricks we could play if we
-        knew, for example, that one input was an array index.
-
-        Since we do not play any such tricks currently the
-        safest thing to do is report the worst case latency.  */
-      if (sparc_cpu == PROCESSOR_ULTRASPARC)
+      else if (outer_code == SIGN_EXTEND)
        {
-         *total = (GET_MODE (x) == DImode
-                   ? COSTS_N_INSNS (34) : COSTS_N_INSNS (19));
-         return true;
+         *total = sparc_costs->int_sload - COSTS_N_INSNS (1);
        }
-
-      /* Multiply latency on Ultra-III, fortunately, is constant.  */
-      if (sparc_cpu == PROCESSOR_ULTRASPARC3)
+      else if (float_mode_p)
        {
-         *total = COSTS_N_INSNS (6);
-         return true;
+         *total = sparc_costs->float_load;
        }
-
-      if (sparc_cpu == PROCESSOR_HYPERSPARC
-         || sparc_cpu == PROCESSOR_SPARCLITE86X)
+      else
        {
-         *total = COSTS_N_INSNS (17);
-         return true;
+         *total = sparc_costs->int_load;
        }
 
-      *total = (TARGET_HARD_MUL ? COSTS_N_INSNS (5) : COSTS_N_INSNS (25));
       return true;
 
-    case DIV:
-    case UDIV:
-    case MOD:
-    case UMOD:
-      if (FLOAT_MODE_P (GET_MODE (x)))
-       {
-         switch (sparc_cpu)
-           {
-           case PROCESSOR_ULTRASPARC:
-             if (GET_MODE (x) == SFmode)
-               *total = COSTS_N_INSNS (13);
-             else
-               *total = COSTS_N_INSNS (23);
-             return true;
+    case PLUS:
+    case MINUS:
+      if (float_mode_p)
+       *total = sparc_costs->float_plusminus;
+      else
+       *total = COSTS_N_INSNS (1);
+      return false;
 
-           case PROCESSOR_ULTRASPARC3:
-             if (GET_MODE (x) == SFmode)
-               *total = COSTS_N_INSNS (17);
-             else
-               *total = COSTS_N_INSNS (20);
-             return true;
+    case MULT:
+      if (float_mode_p)
+       *total = sparc_costs->float_mul;
+      else if (! TARGET_HARD_MUL)
+       *total = COSTS_N_INSNS (25);
+      else
+       {
+         int bit_cost;
 
-           case PROCESSOR_SUPERSPARC:
-             if (GET_MODE (x) == SFmode)
-               *total = COSTS_N_INSNS (6);
-             else
-               *total = COSTS_N_INSNS (9);
-             return true;
+         bit_cost = 0;
+         if (sparc_costs->int_mul_bit_factor)
+           {
+             int nbits;
 
-           case PROCESSOR_HYPERSPARC:
-           case PROCESSOR_SPARCLITE86X:
-             if (GET_MODE (x) == SFmode)
-               *total = COSTS_N_INSNS (8);
+             if (GET_CODE (XEXP (x, 1)) == CONST_INT)
+               {
+                 unsigned HOST_WIDE_INT value = INTVAL (XEXP (x, 1));
+                 for (nbits = 0; value != 0; value &= value - 1)
+                   nbits++;
+               }
+             else if (GET_CODE (XEXP (x, 1)) == CONST_DOUBLE
+                      && GET_MODE (XEXP (x, 1)) == DImode)
+               {
+                 rtx x1 = XEXP (x, 1);
+                 unsigned HOST_WIDE_INT value1 = XINT (x1, 2);
+                 unsigned HOST_WIDE_INT value2 = XINT (x1, 3);
+
+                 for (nbits = 0; value1 != 0; value1 &= value1 - 1)
+                   nbits++;
+                 for (; value2 != 0; value2 &= value2 - 1)
+                   nbits++;
+               }
              else
-               *total = COSTS_N_INSNS (12);
-             return true;
+               nbits = 7;
 
-           default:
-             *total = COSTS_N_INSNS (7);
-             return true;
+             if (nbits < 3)
+               nbits = 3;
+             bit_cost = (nbits - 3) / sparc_costs->int_mul_bit_factor;
+             bit_cost = COSTS_N_INSNS (bit_cost);
            }
+
+         if (mode == DImode)
+           *total = sparc_costs->int_mulX + bit_cost;
+         else
+           *total = sparc_costs->int_mul + bit_cost;
        }
+      return false;
 
-      if (sparc_cpu == PROCESSOR_ULTRASPARC)
-       *total = (GET_MODE (x) == DImode
-                 ? COSTS_N_INSNS (68) : COSTS_N_INSNS (37));
-      else if (sparc_cpu == PROCESSOR_ULTRASPARC3)
-       *total = (GET_MODE (x) == DImode
-                 ? COSTS_N_INSNS (71) : COSTS_N_INSNS (40));
-      else
-       *total = COSTS_N_INSNS (25);
-      return true;
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      *total = COSTS_N_INSNS (1) + sparc_costs->shift_penalty;
+      return false;
 
-    case IF_THEN_ELSE:
-      /* Conditional moves.  */
-      switch (sparc_cpu)
+    case DIV:
+    case UDIV:
+    case MOD:
+    case UMOD:
+      if (float_mode_p)
        {
-       case PROCESSOR_ULTRASPARC:
-         *total = COSTS_N_INSNS (2);
-         return true;
-
-       case PROCESSOR_ULTRASPARC3:
-         if (FLOAT_MODE_P (GET_MODE (x)))
-           *total = COSTS_N_INSNS (3);
+         if (mode == DFmode)
+           *total = sparc_costs->float_div_df;
          else
-           *total = COSTS_N_INSNS (2);
-         return true;
-
-       default:
-         *total = COSTS_N_INSNS (1);
-         return true;
+           *total = sparc_costs->float_div_sf;
        }
-
-    case MEM:
-      /* If outer-code is SIGN/ZERO extension we have to subtract
-        out COSTS_N_INSNS (1) from whatever we return in determining
-        the cost.  */
-      switch (sparc_cpu)
+      else
        {
-       case PROCESSOR_ULTRASPARC:
-         if (outer_code == ZERO_EXTEND)
-           *total = COSTS_N_INSNS (1);
-         else
-           *total = COSTS_N_INSNS (2);
-         return true;
-
-       case PROCESSOR_ULTRASPARC3:
-         if (outer_code == ZERO_EXTEND)
-           {
-             if (GET_MODE (x) == QImode
-                 || GET_MODE (x) == HImode
-                 || outer_code == SIGN_EXTEND)
-               *total = COSTS_N_INSNS (2);
-             else
-               *total = COSTS_N_INSNS (1);
-           }
-         else
-           {
-             /* This handles sign extension (3 cycles)
-                and everything else (2 cycles).  */
-             *total = COSTS_N_INSNS (2);
-           }
-         return true;
-
-       case PROCESSOR_SUPERSPARC:
-         if (FLOAT_MODE_P (GET_MODE (x))
-             || outer_code == ZERO_EXTEND
-             || outer_code == SIGN_EXTEND)
-           *total = COSTS_N_INSNS (0);
-         else
-           *total = COSTS_N_INSNS (1);
-         return true;
-
-       case PROCESSOR_TSC701:
-         if (outer_code == ZERO_EXTEND
-             || outer_code == SIGN_EXTEND)
-           *total = COSTS_N_INSNS (2);
-         else
-           *total = COSTS_N_INSNS (3);
-         return true;
-         
-       case PROCESSOR_CYPRESS:
-         if (outer_code == ZERO_EXTEND
-             || outer_code == SIGN_EXTEND)
-           *total = COSTS_N_INSNS (1);
+         if (mode == DImode)
+           *total = sparc_costs->int_divX;
          else
-           *total = COSTS_N_INSNS (2);
-         return true;
-         
-       case PROCESSOR_HYPERSPARC:
-       case PROCESSOR_SPARCLITE86X:
-       default:
-         if (outer_code == ZERO_EXTEND
-             || outer_code == SIGN_EXTEND)
-           *total = COSTS_N_INSNS (0);
-         else
-           *total = COSTS_N_INSNS (1);
-         return true;
+           *total = sparc_costs->int_div;
        }
+      return false;
 
-    case CONST_INT:
-      if (INTVAL (x) < 0x1000 && INTVAL (x) >= -0x1000)
+    case NEG:
+      if (! float_mode_p)
        {
-         *total = 0;
-         return true;
+         *total = COSTS_N_INSNS (1);
+         return false;
        }
       /* FALLTHRU */
 
-    case HIGH:
-      *total = 2;
-      return true;
+    case ABS:
+    case FLOAT:
+    case UNSIGNED_FLOAT:
+    case FIX:
+    case UNSIGNED_FIX:
+    case FLOAT_EXTEND:
+    case FLOAT_TRUNCATE:
+      *total = sparc_costs->float_move;
+      return false;
 
-    case CONST:
-    case LABEL_REF:
-    case SYMBOL_REF:
-      *total = 4;
-      return true;
+    case SQRT:
+      if (mode == DFmode)
+       *total = sparc_costs->float_sqrt_df;
+      else
+       *total = sparc_costs->float_sqrt_sf;
+      return false;
 
-    case CONST_DOUBLE:
-      if (GET_MODE (x) == DImode
-         && ((XINT (x, 3) == 0
-              && (unsigned HOST_WIDE_INT) XINT (x, 2) < 0x1000)
-             || (XINT (x, 3) == -1
-                 && XINT (x, 2) < 0
-                 && XINT (x, 2) >= -0x1000)))
-       *total = 0;
+    case COMPARE:
+      if (float_mode_p)
+       *total = sparc_costs->float_cmp;
       else
-       *total = 8;
-      return true;
+       *total = COSTS_N_INSNS (1);
+      return false;
+
+    case IF_THEN_ELSE:
+      if (float_mode_p)
+       *total = sparc_costs->float_cmove;
+      else
+       *total = sparc_costs->int_cmove;
+      return false;
 
     default:
       return false;
     }
 }
 
+/* Emit the sequence of insns SEQ while preserving the register REG.  */
+
+static void
+emit_and_preserve (rtx seq, rtx reg)
+{
+  rtx slot = gen_rtx_MEM (word_mode,
+                         plus_constant (stack_pointer_rtx, SPARC_STACK_BIAS));
+
+  emit_stack_pointer_decrement (GEN_INT (UNITS_PER_WORD));
+  emit_insn (gen_rtx_SET (VOIDmode, slot, reg));
+  emit_insn (seq);
+  emit_insn (gen_rtx_SET (VOIDmode, reg, slot));
+  emit_stack_pointer_increment (GEN_INT (UNITS_PER_WORD));
+}
+
 /* Output code to add DELTA to the first argument, and then jump to FUNCTION.
    Used for C++ multiple inheritance.  */
 
@@ -8573,21 +8544,42 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
                       HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
                       tree function)
 {
-  rtx this, insn, funexp, delta_rtx, tmp;
+  rtx this, insn, funexp, delta_rtx;
+  unsigned int int_arg_first;
 
   reload_completed = 1;
   epilogue_completed = 1;
   no_new_pseudos = 1;
-  current_function_uses_only_leaf_regs = 1;
+  reset_block_changes ();
 
   emit_note (NOTE_INSN_PROLOGUE_END);
 
+  if (flag_delayed_branch)
+    {
+      /* We will emit a regular sibcall below, so we need to instruct
+        output_sibcall that we are in a leaf function.  */
+      current_function_uses_only_leaf_regs = 1;
+
+      /* This will cause final.c to invoke leaf_renumber_regs so we
+        must behave as if we were in a not-yet-leafified function.  */
+      int_arg_first = SPARC_INCOMING_INT_ARG_FIRST;
+    }
+  else
+    {
+      /* We will emit the sibcall manually below, so we will need to
+        manually spill non-leaf registers.  */
+      current_function_uses_only_leaf_regs = 0;
+
+      /* We really are in a leaf function.  */
+      int_arg_first = SPARC_OUTGOING_INT_ARG_FIRST;
+    }
+
   /* Find the "this" pointer.  Normally in %o0, but in ARCH64 if the function
      returns a structure, the structure return pointer is there instead.  */
   if (TARGET_ARCH64 && aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
-    this = gen_rtx_REG (Pmode, SPARC_INCOMING_INT_ARG_FIRST + 1);
+    this = gen_rtx_REG (Pmode, int_arg_first + 1);
   else
-    this = gen_rtx_REG (Pmode, SPARC_INCOMING_INT_ARG_FIRST);
+    this = gen_rtx_REG (Pmode, int_arg_first);
 
   /* Add DELTA.  When possible use a plain add, otherwise load it into
      a register first.  */
@@ -8609,8 +8601,9 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       delta_rtx = scratch;
     }
 
-  tmp = gen_rtx_PLUS (Pmode, this, delta_rtx);
-  emit_insn (gen_rtx_SET (VOIDmode, this, tmp));
+  emit_insn (gen_rtx_SET (VOIDmode,
+                         this,
+                         gen_rtx_PLUS (Pmode, this, delta_rtx)));
 
   /* Generate a tail call to the target function.  */
   if (! TREE_USED (function))
@@ -8619,9 +8612,67 @@ sparc_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       TREE_USED (function) = 1;
     }
   funexp = XEXP (DECL_RTL (function), 0);
-  funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
-  insn = emit_call_insn (gen_sibcall (funexp));
-  SIBLING_CALL_P (insn) = 1;
+
+  if (flag_delayed_branch)
+    {
+      funexp = gen_rtx_MEM (FUNCTION_MODE, funexp);
+      insn = emit_call_insn (gen_sibcall (funexp));
+      SIBLING_CALL_P (insn) = 1;
+    }
+  else
+    {
+      /* The hoops we have to jump through in order to generate a sibcall
+        without using delay slots...  */
+      rtx spill_reg, seq, scratch = gen_rtx_REG (Pmode, 1);
+
+      if (flag_pic)
+        {
+         spill_reg = gen_rtx_REG (word_mode, 15);  /* %o7 */
+         start_sequence ();
+         load_pic_register ();  /* clobbers %o7 */
+         scratch = legitimize_pic_address (funexp, Pmode, scratch);
+         seq = get_insns ();
+         end_sequence ();
+         emit_and_preserve (seq, spill_reg);
+       }
+      else if (TARGET_ARCH32)
+       {
+         emit_insn (gen_rtx_SET (VOIDmode,
+                                 scratch,
+                                 gen_rtx_HIGH (SImode, funexp)));
+         emit_insn (gen_rtx_SET (VOIDmode,
+                                 scratch,
+                                 gen_rtx_LO_SUM (SImode, scratch, funexp)));
+       }
+      else  /* TARGET_ARCH64 */
+        {
+         switch (sparc_cmodel)
+           {
+           case CM_MEDLOW:
+           case CM_MEDMID:
+             /* The destination can serve as a temporary.  */
+             sparc_emit_set_symbolic_const64 (scratch, funexp, scratch);
+             break;
+
+           case CM_MEDANY:
+           case CM_EMBMEDANY:
+             /* The destination cannot serve as a temporary.  */
+             spill_reg = gen_rtx_REG (DImode, 15);  /* %o7 */
+             start_sequence ();
+             sparc_emit_set_symbolic_const64 (scratch, funexp, spill_reg);
+             seq = get_insns ();
+             end_sequence ();
+             emit_and_preserve (seq, spill_reg);
+             break;
+
+           default:
+             abort();
+           }
+       }
+
+      emit_jump_insn (gen_indirect_jump (scratch));
+    }
+
   emit_barrier ();
 
   /* Run just enough of rest_of_compilation to get the insns emitted.