OSDN Git Service

* sh.c (calc_live_regs): If the return address pointer is live,
[pf3gnuchains/gcc-fork.git] / gcc / config / sh / sh.c
index 6cad651..d1b203b 100644 (file)
@@ -1,6 +1,6 @@
-/* Output routines for GCC for Hitachi / SuperH SH.
-   Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002, 2003
-   Free Software Foundation, Inc.
+/* Output routines for GCC for Renesas / SuperH SH.
+   Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002,
+   2003 Free Software Foundation, Inc.
    Contributed by Steve Chamberlain (sac@cygnus.com).
    Improved by Jim Wilson (wilson@cygnus.com). 
 
@@ -47,6 +47,7 @@ Boston, MA 02111-1307, USA.  */
 #include "langhooks.h"
 #include "basic-block.h"
 #include "ra.h"
+#include "cfglayout.h"
 
 int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
 
@@ -55,7 +56,7 @@ int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
 
 /* These are some macros to abstract register modes.  */
 #define CONST_OK_FOR_ADD(size) \
-  (TARGET_SHMEDIA ? CONST_OK_FOR_P (size) : CONST_OK_FOR_I (size))
+  (TARGET_SHMEDIA ? CONST_OK_FOR_I10 (size) : CONST_OK_FOR_I08 (size))
 #define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))
 #define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))
 #define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))
@@ -107,7 +108,7 @@ rtx sh_compare_op1;
 /* Provides the class number of the smallest class containing
    reg number.  */
 
-int regno_reg_class[FIRST_PSEUDO_REGISTER] =
+enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
 {
   R0_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
   GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
@@ -174,6 +175,8 @@ enum reg_class reg_class_from_letter[] =
 
 int assembler_dialect;
 
+static bool shmedia_space_reserved_for_target_registers;
+
 static void split_branches PARAMS ((rtx));
 static int branch_dest PARAMS ((rtx));
 static void force_into PARAMS ((rtx, rtx));
@@ -186,7 +189,8 @@ static int mova_p PARAMS ((rtx));
 static rtx find_barrier PARAMS ((int, rtx, rtx));
 static int noncall_uses_reg PARAMS ((rtx, rtx, rtx *));
 static rtx gen_block_redirect PARAMS ((rtx, int, int));
-static void output_stack_adjust PARAMS ((int, rtx, int, rtx (*) (rtx)));
+static void sh_reorg PARAMS ((void));
+static void output_stack_adjust (int, rtx, int, HARD_REG_SET *);
 static rtx frame_insn PARAMS ((rtx));
 static rtx push PARAMS ((int));
 static void pop PARAMS ((int));
@@ -207,15 +211,16 @@ static int sh_issue_rate PARAMS ((void));
 static bool sh_function_ok_for_sibcall PARAMS ((tree, tree));
 
 static bool sh_cannot_modify_jumps_p PARAMS ((void));
+static int sh_target_reg_class (void);
+static bool sh_optimize_target_register_callee_saved (bool);
 static bool sh_ms_bitfield_layout_p PARAMS ((tree));
 
-static void sh_encode_section_info PARAMS ((tree, int));
-static const char *sh_strip_name_encoding PARAMS ((const char *));
 static void sh_init_builtins PARAMS ((void));
 static void sh_media_init_builtins PARAMS ((void));
 static rtx sh_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
 static void sh_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
                                        HOST_WIDE_INT, tree));
+static void sh_file_start PARAMS ((void));
 static int flow_dependent_p PARAMS ((rtx, rtx));
 static void flow_dependent_p_1 PARAMS ((rtx, rtx, void *));
 static int shiftcosts PARAMS ((rtx));
@@ -226,6 +231,13 @@ static bool unspec_caller_rtx_p PARAMS ((rtx));
 static bool sh_cannot_copy_insn_p PARAMS ((rtx));
 static bool sh_rtx_costs PARAMS ((rtx, int, int, int *));
 static int sh_address_cost PARAMS ((rtx));
+static int shmedia_target_regs_stack_space (HARD_REG_SET *);
+static int shmedia_reserve_space_for_target_registers_p (int, HARD_REG_SET *);
+static int shmedia_target_regs_stack_adjust (HARD_REG_SET *);
+static int scavenge_reg (HARD_REG_SET *s);
+struct save_schedule_s;
+static struct save_entry_s *sh5_schedule_saves (HARD_REG_SET *,
+                                               struct save_schedule_s *, int);
 \f
 /* Initialize the GCC target structure.  */
 #undef TARGET_ATTRIBUTE_TABLE
@@ -252,6 +264,11 @@ static int sh_address_cost PARAMS ((rtx));
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
 
+#undef TARGET_ASM_FILE_START
+#define TARGET_ASM_FILE_START sh_file_start
+#undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
+#define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
+
 #undef TARGET_INSERT_ATTRIBUTES
 #define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
 
@@ -266,15 +283,15 @@ static int sh_address_cost PARAMS ((rtx));
 
 #undef TARGET_CANNOT_MODIFY_JUMPS_P
 #define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p
+#undef TARGET_BRANCH_TARGET_REGISTER_CLASS
+#define TARGET_BRANCH_TARGET_REGISTER_CLASS sh_target_reg_class
+#undef TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED
+#define TARGET_BRANCH_TARGET_REGISTER_CALLEE_SAVED \
+ sh_optimize_target_register_callee_saved
 
 #undef TARGET_MS_BITFIELD_LAYOUT_P
 #define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
 
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO sh_encode_section_info
-#undef TARGET_STRIP_NAME_ENCODING
-#define TARGET_STRIP_NAME_ENCODING sh_strip_name_encoding
-
 #undef TARGET_INIT_BUILTINS
 #define TARGET_INIT_BUILTINS sh_init_builtins
 #undef TARGET_EXPAND_BUILTIN
@@ -290,6 +307,9 @@ static int sh_address_cost PARAMS ((rtx));
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST sh_address_cost
 
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG sh_reorg
+
 #ifdef HAVE_AS_TLS
 #undef TARGET_HAVE_TLS
 #define TARGET_HAVE_TLS true
@@ -363,7 +383,7 @@ print_operand_address (stream, x)
    ','  print LOCAL_LABEL_PREFIX
    '@'  print trap, rte or rts depending upon pragma interruptness
    '#'  output a nop if there is nothing to put in the delay slot
-   '''  print likelyhood suffix (/u for unlikely).
+   '''  print likelihood suffix (/u for unlikely).
    'O'  print a constant without the #
    'R'  print the LSW of a dp value - changes if in little endian
    'S'  print the MSW of a dp value - changes if in little endian
@@ -629,7 +649,7 @@ expand_block_move (operands)
 
          entry_name = get_identifier ("__movstrSI12_i4");
 
-         sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name));
+         sym = function_symbol (IDENTIFIER_POINTER (entry_name));
          func_addr_rtx = copy_to_mode_reg (Pmode, sym);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[1], 0), r5);
@@ -649,7 +669,7 @@ expand_block_move (operands)
          entry_name = get_identifier (bytes & 4
                                       ? "__movstr_i4_odd"
                                       : "__movstr_i4_even");
-         sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name));
+         sym = function_symbol (IDENTIFIER_POINTER (entry_name));
          func_addr_rtx = copy_to_mode_reg (Pmode, sym);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[1], 0), r5);
@@ -673,7 +693,7 @@ expand_block_move (operands)
 
       sprintf (entry, "__movstrSI%d", bytes);
       entry_name = get_identifier (entry);
-      sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name));
+      sym = function_symbol (IDENTIFIER_POINTER (entry_name));
       func_addr_rtx = copy_to_mode_reg (Pmode, sym);
       force_into (XEXP (operands[0], 0), r4);
       force_into (XEXP (operands[1], 0), r5);
@@ -694,7 +714,7 @@ expand_block_move (operands)
       rtx r6 = gen_rtx_REG (SImode, 6);
 
       entry_name = get_identifier ("__movstr");
-      sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (entry_name));
+      sym = function_symbol (IDENTIFIER_POINTER (entry_name));
       func_addr_rtx = copy_to_mode_reg (Pmode, sym);
       force_into (XEXP (operands[0], 0), r4);
       force_into (XEXP (operands[1], 0), r5);
@@ -765,11 +785,20 @@ prepare_move_operands (operands, mode)
          && ! sh_register_operand (operands[1], mode))
        operands[1] = copy_to_mode_reg (mode, operands[1]);
 
+      if (GET_CODE (operands[0]) == MEM && ! memory_operand (operands[0], mode))
+       {
+         /* This is like change_address_1 (operands[0], mode, 0, 1) ,
+            except that we can't use that function because it is static.  */
+         rtx new = change_address (operands[0], mode, 0);
+         MEM_COPY_ATTRIBUTES (new, operands[0]);
+         operands[0] = new;
+       }
+
       /* This case can happen while generating code to move the result
         of a library call to the target.  Reject `st r0,@(rX,rY)' because
         reload will fail to find a spill register for rX, since r0 is already
         being used for the source.  */
-      else if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == 0
+      else if (refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0)
               && GET_CODE (operands[0]) == MEM
               && GET_CODE (XEXP (operands[0], 0)) == PLUS
               && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG)
@@ -1283,26 +1312,38 @@ output_ieee_ccmpeq (insn, operands)
   return output_branchy_insn (NE, "bt\t%l9\\;fcmp/eq\t%1,%0", insn, operands);
 }
 \f
-/* Output to FILE the start of the assembler file.  */
-
-void
-output_file_start (file)
-     FILE *file;
-{
-  output_file_directive (file, main_input_filename);
+/* Output the start of the assembler file.  */
 
-  /* Switch to the data section so that the coffsem symbol
-     isn't in the text section.  */
-  data_section ();
+static void
+sh_file_start ()
+{
+  default_file_start ();
+
+  if (TARGET_ELF)
+    /* We need to show the text section with the proper
+       attributes as in TEXT_SECTION_ASM_OP, before dwarf2out
+       emits it without attributes in TEXT_SECTION, else GAS
+       will complain.  We can teach GAS specifically about the
+       default attributes for our choice of text section, but
+       then we would have to change GAS again if/when we change
+       the text section name.  */
+    fprintf (asm_out_file, "%s\n", TEXT_SECTION_ASM_OP);
+  else
+    /* Switch to the data section so that the coffsem symbol
+       isn't in the text section.  */
+    data_section ();
 
   if (TARGET_LITTLE_ENDIAN)
-    fprintf (file, "\t.little\n");
+    fputs ("\t.little\n", asm_out_file);
 
-  if (TARGET_SHCOMPACT)
-    fprintf (file, "\t.mode\tSHcompact\n");
-  else if (TARGET_SHMEDIA)
-    fprintf (file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
-            TARGET_SHMEDIA64 ? 64 : 32);
+  if (!TARGET_ELF)
+    {
+      if (TARGET_SHCOMPACT)
+       fputs ("\t.mode\tSHcompact\n", asm_out_file);
+      else if (TARGET_SHMEDIA)
+       fprintf (asm_out_file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
+                TARGET_SHMEDIA64 ? 64 : 32);
+    }
 }
 \f
 /* Check if PAT includes UNSPEC_CALLER unspec pattern.  */
@@ -1482,8 +1523,8 @@ andcosts (x)
   if (TARGET_SHMEDIA)
     {
       if ((GET_CODE (XEXP (x, 1)) == CONST_INT
-          && CONST_OK_FOR_J (INTVAL (XEXP (x, 1))))
-         || EXTRA_CONSTRAINT_S (XEXP (x, 1)))
+          && CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1))))
+         || EXTRA_CONSTRAINT_C16 (XEXP (x, 1)))
        return 1;
       else
        return 2;
@@ -1492,13 +1533,13 @@ andcosts (x)
   /* These constants are single cycle extu.[bw] instructions.  */
   if (i == 0xff || i == 0xffff)
     return 1;
-  /* Constants that can be used in an and immediate instruction is a single
+  /* Constants that can be used in an and immediate instruction in a single
      cycle, but this requires r0, so make it a little more expensive.  */
-  if (CONST_OK_FOR_L (i))
+  if (CONST_OK_FOR_K08 (i))
     return 2;
   /* Constants that can be loaded with a mov immediate and an and.
      This case is probably unnecessary.  */
-  if (CONST_OK_FOR_I (i))
+  if (CONST_OK_FOR_I08 (i))
     return 2;
   /* Any other constants requires a 2 cycle pc-relative load plus an and.
      This case is probably unnecessary.  */
@@ -1530,11 +1571,11 @@ addsubcosts (x)
        return TARGET_SHMEDIA64 ? 5 : 3;
 
       case CONST_INT:
-       if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1))))
+       if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1))))
           return 2;
-       else if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1)) >> 16))
+       else if (CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1)) >> 16))
          return 3;
-       else if (CONST_OK_FOR_J ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
+       else if (CONST_OK_FOR_I16 ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
          return 4;
 
        /* Fall through.  */
@@ -1594,22 +1635,22 @@ sh_rtx_costs (x, code, outer_code, total)
            *total = 0;
          else if ((outer_code == IOR || outer_code == XOR
                    || outer_code == PLUS)
-                  && CONST_OK_FOR_P (INTVAL (x)))
+                  && CONST_OK_FOR_I10 (INTVAL (x)))
            *total = 0;
-         else if (CONST_OK_FOR_J (INTVAL (x)))
+         else if (CONST_OK_FOR_I16 (INTVAL (x)))
             *total = COSTS_N_INSNS (outer_code != SET);
-         else if (CONST_OK_FOR_J (INTVAL (x) >> 16))
+         else if (CONST_OK_FOR_I16 (INTVAL (x) >> 16))
            *total = COSTS_N_INSNS (2);
-         else if (CONST_OK_FOR_J ((INTVAL (x) >> 16) >> 16))
+         else if (CONST_OK_FOR_I16 ((INTVAL (x) >> 16) >> 16))
            *total = COSTS_N_INSNS (3);
           else
            *total = COSTS_N_INSNS (4);
          return true;
         }
-      if (CONST_OK_FOR_I (INTVAL (x)))
+      if (CONST_OK_FOR_I08 (INTVAL (x)))
         *total = 0;
       else if ((outer_code == AND || outer_code == IOR || outer_code == XOR)
-              && CONST_OK_FOR_L (INTVAL (x)))
+              && CONST_OK_FOR_K08 (INTVAL (x)))
         *total = 1;
       else
         *total = 8;
@@ -1911,7 +1952,7 @@ expand_ashiftrt (operands)
   emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
   sprintf (func, "__ashiftrt_r4_%d", value);
   func_name = get_identifier (func);
-  sym = gen_rtx_SYMBOL_REF (Pmode, IDENTIFIER_POINTER (func_name));
+  sym = function_symbol (IDENTIFIER_POINTER (func_name));
   emit_move_insn (wrk, sym);
   emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk));
   emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
@@ -2027,7 +2068,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp)
     {
       if (i > right)
        break;
-      if (! CONST_OK_FOR_L (mask >> i))
+      if (! CONST_OK_FOR_K08 (mask >> i))
        continue;
       cost = (i != 0) + 2 + ext_shift_insns[left + i];
       if (cost < best_cost)
@@ -2044,14 +2085,14 @@ shl_and_kind (left_rtx, mask_rtx, attrp)
     {
       if (i > right)
        break;
-      cost = (i != 0) + (CONST_OK_FOR_I (mask >> i) ? 2 : 3)
+      cost = (i != 0) + (CONST_OK_FOR_I08 (mask >> i) ? 2 : 3)
        + (can_ext ? ext_shift_insns : shift_insns)[left + i];
       if (cost < best_cost)
        {
          best = 4 - can_ext;
          best_cost = cost;
          best_right = i;
-         best_len = cost - 1 - ! CONST_OK_FOR_I (mask >> i);
+         best_len = cost - 1 - ! CONST_OK_FOR_I08 (mask >> i);
        }
     }
 
@@ -2479,8 +2520,6 @@ gen_datalabel_ref (sym)
   if (GET_CODE (sym) != SYMBOL_REF)
     abort ();
 
-  XSTR (sym, 0) = concat (SH_DATALABEL_ENCODING, XSTR (sym, 0), NULL);
-
   return sym;
 }
 
@@ -2831,8 +2870,8 @@ hi_const (src)
 /* Nonzero if the insn is a move instruction which needs to be fixed.  */
 
 /* ??? For a DImode/DFmode moves, we don't need to fix it if each half of the
-   CONST_DOUBLE input value is CONST_OK_FOR_I.  For a SFmode move, we don't
-   need to fix it if the input value is CONST_OK_FOR_I.  */
+   CONST_DOUBLE input value is CONST_OK_FOR_I08.  For a SFmode move, we don't
+   need to fix it if the input value is CONST_OK_FOR_I08.  */
 
 static int
 broken_move (insn)
@@ -2869,7 +2908,7 @@ broken_move (insn)
                && GET_CODE (SET_DEST (pat)) == REG
                && FP_REGISTER_P (REGNO (SET_DEST (pat))))
          && (GET_CODE (SET_SRC (pat)) != CONST_INT
-             || ! CONST_OK_FOR_I (INTVAL (SET_SRC (pat)))))
+             || ! CONST_OK_FOR_I08 (INTVAL (SET_SRC (pat)))))
        return 1;
     }
 
@@ -3441,7 +3480,7 @@ gen_block_redirect (jump, addr, need_block)
       rtx next = next_active_insn (next_active_insn (dest));
       if (next && GET_CODE (next) == JUMP_INSN
          && GET_CODE (PATTERN (next)) == SET
-         && recog_memoized (next) == CODE_FOR_jump)
+         && recog_memoized (next) == CODE_FOR_jump_compact)
        {
          dest = JUMP_LABEL (next);
          if (dest
@@ -3471,7 +3510,7 @@ gen_block_redirect (jump, addr, need_block)
         NOTE_INSN_BLOCK_END notes between the indirect_jump_scratch and
         the jump.  */
         
-      INSN_SCOPE (insn) = INSN_SCOPE (jump);
+      INSN_LOCATOR (insn) = INSN_LOCATOR (jump);
       INSN_CODE (insn) = CODE_FOR_indirect_jump_scratch;
       return insn;
     }
@@ -3522,7 +3561,7 @@ gen_far_branch (bp)
     jump = emit_jump_insn_after (gen_return (), insn);
   /* Emit a barrier so that reorg knows that any following instructions
      are not reachable via a fall-through path.
-     But don't do this when not optimizing, since we wouldn't supress the
+     But don't do this when not optimizing, since we wouldn't suppress the
      alignment for the barrier then, and could end up with out-of-range
      pc-relative loads.  */
   if (optimize)
@@ -3686,7 +3725,8 @@ barrier_align (barrier_or_label)
              || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))),           
                  (INSN_P (x) 
                   && (INSN_CODE (x) == CODE_FOR_block_branch_redirect
-                      || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch))))
+                      || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch
+                      || INSN_CODE (x) == CODE_FOR_stuff_delay_slot))))
            {
              rtx pat = PATTERN (prev);
              if (GET_CODE (pat) == PARALLEL)
@@ -3725,20 +3765,19 @@ sh_loop_align (label)
   return align_loops_log;
 }
 
-/* Exported to toplev.c.
-
-   Do a final pass over the function, just before delayed branch
+/* Do a final pass over the function, just before delayed branch
    scheduling.  */
 
-void
-machine_dependent_reorg (first)
-     rtx first;
+static void
+sh_reorg ()
 {
-  rtx insn, mova = NULL_RTX;
+  rtx first, insn, mova = NULL_RTX;
   int num_mova;
   rtx r0_rtx = gen_rtx_REG (Pmode, 0);
   rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx);
 
+  first = get_insns ();
+
   /* We must split call insns before introducing `mova's.  If we're
      optimizing, they'll have already been split.  Otherwise, make
      sure we don't split them too late.  */
@@ -4330,7 +4369,7 @@ split_branches (first)
                        || ((beyond = next_active_insn (beyond))
                            && GET_CODE (beyond) == JUMP_INSN))
                    && GET_CODE (PATTERN (beyond)) == SET
-                   && recog_memoized (beyond) == CODE_FOR_jump
+                   && recog_memoized (beyond) == CODE_FOR_jump_compact
                    && ((INSN_ADDRESSES
                         (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0)))
                         - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
@@ -4344,7 +4383,7 @@ split_branches (first)
            if ((GET_CODE (next) == JUMP_INSN
                 || GET_CODE (next = next_active_insn (next)) == JUMP_INSN)
                && GET_CODE (PATTERN (next)) == SET
-               && recog_memoized (next) == CODE_FOR_jump
+               && recog_memoized (next) == CODE_FOR_jump_compact
                && ((INSN_ADDRESSES
                     (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0)))
                     - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
@@ -4523,17 +4562,16 @@ output_jump_label_table ()
 
 static int extra_push;
 
-/* Adjust the stack by SIZE bytes.  REG holds the rtl of the register
-  to be adjusted, and TEMP, if nonnegative, holds the register number
-  of a general register that we may clobber.  */
+/* Adjust the stack by SIZE bytes.  REG holds the rtl of the register to be
+   adjusted.  If epilogue_p is zero, this is for a prologue; otherwise, it's
+   for an epilogue.  If LIVE_REGS_MASK is nonzero, it points to a HARD_REG_SET
+   of all the registers that are about to be restored, and hence dead.  */
 
 static void
-output_stack_adjust (size, reg, temp, emit_fn)
-     int size;
-     rtx reg;
-     int temp;
-     rtx (*emit_fn) PARAMS ((rtx));
+output_stack_adjust (int size, rtx reg, int epilogue_p,
+                    HARD_REG_SET *live_regs_mask)
 {
+  rtx (*emit_fn) (rtx) = epilogue_p ? &emit_insn : &frame_insn;
   if (size)
     {
       HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
@@ -4556,10 +4594,43 @@ output_stack_adjust (size, reg, temp, emit_fn)
        {
          rtx const_reg;
          rtx insn;
+         int temp = epilogue_p ? 7 : (TARGET_SH5 ? 0 : 1);
+         int i;
 
          /* If TEMP is invalid, we could temporarily save a general
             register to MACL.  However, there is currently no need
             to handle this case, so just abort when we see it.  */
+         if (current_function_interrupt
+             || ! call_used_regs[temp] || fixed_regs[temp])
+           temp = -1;
+         if (temp < 0 && ! current_function_interrupt)
+           {
+             HARD_REG_SET temps;
+             COPY_HARD_REG_SET (temps, call_used_reg_set);
+             AND_COMPL_HARD_REG_SET (temps, call_fixed_reg_set);
+             if (epilogue_p)
+               {
+                 for (i = 0; i < HARD_REGNO_NREGS (FIRST_RET_REG, DImode); i++)
+                   CLEAR_HARD_REG_BIT (temps, FIRST_RET_REG + i);
+                 if (current_function_calls_eh_return)
+                   {
+                     CLEAR_HARD_REG_BIT (temps, EH_RETURN_STACKADJ_REGNO);
+                     for (i = 0; i <= 3; i++)
+                       CLEAR_HARD_REG_BIT (temps, EH_RETURN_DATA_REGNO (i));
+                   }
+               }
+             else
+               {
+                 for (i = FIRST_PARM_REG;
+                      i < FIRST_PARM_REG + NPARM_REGS (SImode); i++)
+                   CLEAR_HARD_REG_BIT (temps, i);
+                 if (current_function_needs_context)
+                   CLEAR_HARD_REG_BIT (temps, STATIC_CHAIN_REGNUM);
+               }
+             temp = scavenge_reg (&temps);
+           }
+         if (temp < 0 && live_regs_mask)
+           temp = scavenge_reg (live_regs_mask);
          if (temp < 0)
            abort ();
          const_reg = gen_rtx_REG (GET_MODE (reg), temp);
@@ -4577,7 +4648,7 @@ output_stack_adjust (size, reg, temp, emit_fn)
              emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
              insn = emit_fn (GEN_ADD3 (reg, reg, const_reg));
            }
-         if (emit_fn == frame_insn)
+         if (! epilogue_p)
            REG_NOTES (insn)
              = (gen_rtx_EXPR_LIST
                 (REG_FRAME_RELATED_EXPR,
@@ -4693,6 +4764,53 @@ push_regs (mask, interrupt_handler)
     push (PR_REG);
 }
 
+/* Calculate how much extra space is needed to save all callee-saved
+   target registers.
+   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
+
+static int
+shmedia_target_regs_stack_space (HARD_REG_SET *live_regs_mask)
+{
+  int reg;
+  int stack_space = 0;
+  int interrupt_handler = sh_cfun_interrupt_handler_p ();
+
+  for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
+    if ((! call_used_regs[reg] || interrupt_handler)
+        && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
+      /* Leave space to save this target register on the stack,
+        in case target register allocation wants to use it. */
+      stack_space += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+  return stack_space;
+}
+   
+/* Decide whether we should reserve space for callee-save target registers,
+   in case target register allocation wants to use them.  REGS_SAVED is
+   the space, in bytes, that is already required for register saves.
+   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
+
+static int
+shmedia_reserve_space_for_target_registers_p (int regs_saved,
+                                             HARD_REG_SET *live_regs_mask)
+{
+  if (optimize_size)
+    return 0;
+  return shmedia_target_regs_stack_space (live_regs_mask) <= regs_saved;
+}
+
+/* Decide how much space to reserve for callee-save target registers
+   in case target register allocation wants to use them.
+   LIVE_REGS_MASK is the register mask calculated by calc_live_regs.  */
+
+static int
+shmedia_target_regs_stack_adjust (HARD_REG_SET *live_regs_mask)
+{
+  if (shmedia_space_reserved_for_target_registers)
+    return shmedia_target_regs_stack_space (live_regs_mask);
+  else
+    return 0;
+}
+
 /* Work out the registers which need to be saved, both as a mask and a
    count of saved words.  Return the count.
 
@@ -4707,12 +4825,11 @@ calc_live_regs (live_regs_mask)
   int reg;
   int count;
   int interrupt_handler;
-  int pr_live;
+  int pr_live, has_call;
 
   interrupt_handler = sh_cfun_interrupt_handler_p ();
 
-  for (count = 0; 32 * count < FIRST_PSEUDO_REGISTER; count++)
-    CLEAR_HARD_REG_SET (*live_regs_mask);
+  CLEAR_HARD_REG_SET (*live_regs_mask);
   if (TARGET_SH4 && TARGET_FMOVD && interrupt_handler
       && regs_ever_live[FPSCR_REG])
     target_flags &= ~FPU_SINGLE_BIT;
@@ -4731,7 +4848,9 @@ calc_live_regs (live_regs_mask)
      the initial value can become the PR_MEDIA_REG hard register, as seen for
      execute/20010122-1.c:test9.  */
   if (TARGET_SHMEDIA)
-    pr_live = regs_ever_live[PR_MEDIA_REG];
+    /* ??? this function is called from initial_elimination_offset, hence we
+       can't use the result of sh_media_register_for_return here.  */
+    pr_live = sh_pr_n_sets ();
   else
     {
       rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
@@ -4739,6 +4858,10 @@ calc_live_regs (live_regs_mask)
                 ? (GET_CODE (pr_initial) != REG
                    || REGNO (pr_initial) != (PR_REG))
                 : regs_ever_live[PR_REG]);
+      /* For Shcompact, if not optimizing, we end up with a memory reference
+        using the return address pointer for __builtin_return_address even
+        though there is no actual need to put the PR register on the stack.  */
+      pr_live |= regs_ever_live[RETURN_ADDRESS_POINTER_REGNUM];
     }
   /* Force PR to be live if the prologue has to call the SHmedia
      argument decoder or register saver.  */
@@ -4747,6 +4870,7 @@ calc_live_regs (live_regs_mask)
           & ~ CALL_COOKIE_RET_TRAMP (1))
          || current_function_has_nonlocal_label))
     pr_live = 1;
+  has_call = TARGET_SHMEDIA ? ! leaf_function_p () : pr_live;
   for (count = 0, reg = FIRST_PSEUDO_REGISTER - 1; reg >= 0; reg--)
     {
       if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
@@ -4756,7 +4880,9 @@ calc_live_regs (live_regs_mask)
             (regs_ever_live[reg]
              || (call_used_regs[reg]
                  && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG)
-                 && pr_live))
+                 && has_call)
+             || (has_call && REGISTER_NATURAL_MODE (reg) == SImode
+                 && (GENERAL_REGISTER_P (reg) || TARGET_REGISTER_P (reg))))
             && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
             && reg != RETURN_ADDRESS_POINTER_REGNUM
             && reg != T_REG && reg != GBR_REG
@@ -4766,13 +4892,13 @@ calc_live_regs (live_regs_mask)
             (TARGET_SHCOMPACT
              && flag_pic
              && current_function_args_info.call_cookie
-             && reg == PIC_OFFSET_TABLE_REGNUM)
+             && reg == (int) PIC_OFFSET_TABLE_REGNUM)
             || (regs_ever_live[reg] && ! call_used_regs[reg])
             || (current_function_calls_eh_return
-                && (reg == EH_RETURN_DATA_REGNO (0)
-                    || reg == EH_RETURN_DATA_REGNO (1)
-                    || reg == EH_RETURN_DATA_REGNO (2)
-                    || reg == EH_RETURN_DATA_REGNO (3)))))
+                && (reg == (int) EH_RETURN_DATA_REGNO (0)
+                    || reg == (int) EH_RETURN_DATA_REGNO (1)
+                    || reg == (int) EH_RETURN_DATA_REGNO (2)
+                    || reg == (int) EH_RETURN_DATA_REGNO (3)))))
        {
          SET_HARD_REG_BIT (*live_regs_mask, reg);
          count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
@@ -4796,6 +4922,32 @@ calc_live_regs (live_regs_mask)
            }
        }
     }
+  /* If we have a target register optimization pass after prologue / epilogue
+     threading, we need to assume all target registers will be live even if
+     they aren't now.  */
+  if (flag_branch_target_load_optimize2
+      && TARGET_SAVE_ALL_TARGET_REGS
+      && shmedia_space_reserved_for_target_registers)
+    for (reg = LAST_TARGET_REG; reg >= FIRST_TARGET_REG; reg--)
+      if ((! call_used_regs[reg] || interrupt_handler)
+         && ! TEST_HARD_REG_BIT (*live_regs_mask, reg))
+       {
+         SET_HARD_REG_BIT (*live_regs_mask, reg);
+         count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+       }
+  /* If this is an interrupt handler, we don't have any call-clobbered
+     registers we can conveniently use for target register save/restore.
+     Make sure we save at least one general purpose register when we need
+     to save target registers.  */
+  if (interrupt_handler
+      && hard_regs_intersect_p (live_regs_mask,
+                               &reg_class_contents[TARGET_REGS])
+      && ! hard_regs_intersect_p (live_regs_mask,
+                                 &reg_class_contents[GENERAL_REGS]))
+    {
+      SET_HARD_REG_BIT (*live_regs_mask, R0_REG);
+      count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (R0_REG));
+    }
 
   return count;
 }
@@ -4826,6 +4978,9 @@ sh_media_register_for_return ()
 
   if (! current_function_is_leaf)
     return -1;
+  if (lookup_attribute ("interrupt_handler",
+                       DECL_ATTRIBUTES (current_function_decl)))
+    return -1;
 
   tr0_used = flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
 
@@ -4836,6 +4991,130 @@ sh_media_register_for_return ()
   return -1;
 }
 
+/* The maximum registers we need to save are:
+   - 62 general purpose registers (r15 is stack pointer, r63 is zero)
+   - 32 floating point registers (for each pair, we save none,
+         one single precision value, or a double precision value).
+   -  8 target registers
+   -  add 1 entry for a delimiter.  */
+#define MAX_SAVED_REGS (62+32+8)
+
+typedef struct save_entry_s
+{
+  unsigned char reg;
+  unsigned char mode;
+  short offset;
+} save_entry;
+
+#define MAX_TEMPS 4
+
+/* There will be a delimiter entry with VOIDmode both at the start and the
+   end of a filled in schedule.  The end delimiter has the offset of the
+   save with the smallest (i.e. most negative) offset.  */
+typedef struct save_schedule_s
+{
+  save_entry entries[MAX_SAVED_REGS + 2];
+  int temps[MAX_TEMPS+1];
+} save_schedule;
+
+/* Fill in SCHEDULE according to LIVE_REGS_MASK.  If RESTORE is nonzero,
+   use reverse order.  Returns the last entry written to (not counting
+   the delimiter).  OFFSET_BASE is a number to be added to all offset
+   entries.  */
+   
+static save_entry *
+sh5_schedule_saves (HARD_REG_SET *live_regs_mask, save_schedule *schedule,
+                   int offset_base)
+{
+  int align, i;
+  save_entry *entry = schedule->entries;
+  int tmpx = 0;
+  int offset;
+
+  if (! current_function_interrupt)
+    for (i = FIRST_GENERAL_REG; tmpx < MAX_TEMPS && i <= LAST_GENERAL_REG; i++)
+      if (call_used_regs[i] && ! fixed_regs[i] && i != PR_MEDIA_REG
+         && ! FUNCTION_ARG_REGNO_P (i)
+         && i != FIRST_RET_REG
+         && ! (current_function_needs_context && i == STATIC_CHAIN_REGNUM)
+         && ! (current_function_calls_eh_return
+               && (i == EH_RETURN_STACKADJ_REGNO
+                   || ((unsigned)i <= EH_RETURN_DATA_REGNO (0)
+                       && (unsigned)i >= EH_RETURN_DATA_REGNO (3)))))
+       schedule->temps[tmpx++] = i;
+  entry->reg = -1;
+  entry->mode = VOIDmode;
+  entry->offset = offset_base;
+  entry++;
+  /* We loop twice: first, we save 8-byte aligned registers in the
+     higher addresses, that are known to be aligned.  Then, we
+     proceed to saving 32-bit registers that don't need 8-byte
+     alignment.
+     If this is an interrupt function, all registers that need saving
+     need to be saved in full.  moreover, we need to postpone saving
+     target registers till we have saved some general purpose registers
+     we can then use as scratch registers.  */
+  offset = offset_base;
+  for (align = 1; align >= 0; align--)
+    {
+      for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+       if (TEST_HARD_REG_BIT (*live_regs_mask, i))
+         {
+           enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+           int reg = i;
+
+           if (current_function_interrupt)
+             {
+               if (TARGET_REGISTER_P (i))
+                 continue;
+               if (GENERAL_REGISTER_P (i))
+                 mode = DImode;
+             }
+           if (mode == SFmode && (i % 2) == 1
+               && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+               && (TEST_HARD_REG_BIT (*live_regs_mask, (i ^ 1))))
+             {
+               mode = DFmode;
+               i--;
+               reg--;
+             }
+
+           /* If we're doing the aligned pass and this is not aligned,
+              or we're doing the unaligned pass and this is aligned,
+              skip it.  */
+           if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT) == 0)
+               != align)
+             continue;
+
+           if (current_function_interrupt
+               && GENERAL_REGISTER_P (i)
+               && tmpx < MAX_TEMPS)
+             schedule->temps[tmpx++] = i;
+
+           offset -= GET_MODE_SIZE (mode);
+           entry->reg = i;
+           entry->mode = mode;
+           entry->offset = offset;
+           entry++;
+         }
+      if (align && current_function_interrupt)
+       for (i = LAST_TARGET_REG; i >= FIRST_TARGET_REG; i--)
+         if (TEST_HARD_REG_BIT (*live_regs_mask, i))
+           {
+             offset -= GET_MODE_SIZE (DImode);
+             entry->reg = i;
+             entry->mode = DImode;
+             entry->offset = offset;
+             entry++;
+           }
+    }
+  entry->reg = -1;
+  entry->mode = VOIDmode;
+  entry->offset = offset;
+  schedule->temps[tmpx] = -1;
+  return entry - 1;
+}
+
 void
 sh_expand_prologue ()
 {
@@ -4850,7 +5129,7 @@ sh_expand_prologue ()
      and partially on the stack, e.g. a large structure.  */
   output_stack_adjust (-current_function_pretend_args_size
                       - current_function_args_info.stack_regs * 8,
-                      stack_pointer_rtx, TARGET_SH5 ? 0 : 1, frame_insn);
+                      stack_pointer_rtx, 0, NULL);
 
   extra_push = 0;
 
@@ -4896,6 +5175,9 @@ sh_expand_prologue ()
          rtx insn = emit_move_insn (gen_rtx_REG (DImode, tr),
                                     gen_rtx_REG (DImode, PR_MEDIA_REG));
 
+         /* ??? We should suppress saving pr when we don't need it, but this
+            is tricky because of builtin_return_address.  */
+
          /* If this function only exits with sibcalls, this copy
             will be flagged as dead.  */
          REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
@@ -4939,155 +5221,191 @@ sh_expand_prologue ()
     
   if (TARGET_SH5)
     {
-      int i;
-      int offset;
-      int align;
-      rtx r0 = gen_rtx_REG (Pmode, R0_REG);
+      int offset_base, offset;
+      rtx r0 = NULL_RTX;
       int offset_in_r0 = -1;
       int sp_in_r0 = 0;
-
-      if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
+      int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
+      int total_size, save_size;
+      save_schedule schedule;
+      save_entry *entry;
+      int *tmp_pnt;
+
+      if (call_used_regs[R0_REG] && ! fixed_regs[R0_REG]
+         && ! current_function_interrupt)
+       r0 = gen_rtx_REG (Pmode, R0_REG);
+
+      /* D is the actual number of bytes that we need for saving registers,
+        however, in initial_elimination_offset we have committed to using
+        an additional TREGS_SPACE amount of bytes - in order to keep both
+        addresses to arguments supplied by the caller and local variables
+        valid, we must keep this gap.  Place it between the incoming
+        arguments and the actually saved registers in a bid to optimize
+        locality of reference.  */
+      total_size = d + tregs_space;
+      total_size += rounded_frame_size (total_size);
+      save_size = total_size - rounded_frame_size (d);
+      if (save_size % (STACK_BOUNDARY / BITS_PER_UNIT))
        d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
-                     - d % (STACK_BOUNDARY / BITS_PER_UNIT));
-
-      offset = d + d_rounding;
-      output_stack_adjust (-offset, stack_pointer_rtx, 1, frame_insn);
-
-      /* We loop twice: first, we save 8-byte aligned registers in the
-        higher addresses, that are known to be aligned.  Then, we
-        proceed to saving 32-bit registers that don't need 8-byte
-        alignment.  */
-      /* Note that if you change this code in a way that affects where
-        the return register is saved, you have to update not only
-        sh_expand_epilogue, but also sh_set_return_address.  */
-      for (align = 1; align >= 0; align--)
-       for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
-         if (TEST_HARD_REG_BIT (live_regs_mask, i))
-           {
-             enum machine_mode mode = REGISTER_NATURAL_MODE (i);
-             int reg = i;
-             rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
-
-             if (mode == SFmode && (i % 2) == 1
-                 && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
-                 && (TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1))))
-               {
-                 mode = DFmode;
-                 i--;
-                 reg--;
-               }
-               
-             /* If we're doing the aligned pass and this is not aligned,
-                or we're doing the unaligned pass and this is aligned,
-                skip it.  */
-             if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
-                  == 0) != align)
-               continue;
+                       - save_size % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+      /* If adjusting the stack in a single step costs nothing extra, do so.
+        I.e. either if a single addi is enough, or we need a movi anyway,
+        and we don't exceed the maximum offset range (the test for the
+        latter is conservative for simplicity).  */
+      if (TARGET_SHMEDIA
+         && (CONST_OK_FOR_I10 (-total_size)
+             || (! CONST_OK_FOR_I10 (-(save_size + d_rounding))
+                 && total_size <= 2044)))
+       d_rounding = total_size - save_size;
+
+      offset_base = d + d_rounding;
+
+      output_stack_adjust (-(save_size + d_rounding), stack_pointer_rtx,
+                          0, NULL);
+
+      sh5_schedule_saves (&live_regs_mask, &schedule, offset_base);
+      tmp_pnt = schedule.temps;
+      for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
+        {
+         enum machine_mode mode = entry->mode;
+         int reg = entry->reg;
+         rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
 
-             offset -= GET_MODE_SIZE (mode);
+         offset = entry->offset;
 
-             reg_rtx = gen_rtx_REG (mode, reg);
+         reg_rtx = gen_rtx_REG (mode, reg);
 
-             mem_rtx = gen_rtx_MEM (mode,
-                                    gen_rtx_PLUS (Pmode,
-                                                  stack_pointer_rtx,
-                                                  GEN_INT (offset)));
+         mem_rtx = gen_rtx_MEM (mode,
+                                gen_rtx_PLUS (Pmode,
+                                              stack_pointer_rtx,
+                                              GEN_INT (offset)));
 
-             GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec);
+         GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec);
 
-             mem_rtx = NULL_RTX;
+         if (! r0)
+           abort ();
+         mem_rtx = NULL_RTX;
 
-           try_pre_dec:
-             do
-               if (HAVE_PRE_DECREMENT
-                   && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
-                       || mem_rtx == NULL_RTX
-                       || i == PR_REG || SPECIAL_REGISTER_P (i)))
-                 {
-                   pre_dec = gen_rtx_MEM (mode,
-                                          gen_rtx_PRE_DEC (Pmode, r0));
+       try_pre_dec:
+         do
+           if (HAVE_PRE_DECREMENT
+               && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
+                   || mem_rtx == NULL_RTX
+                   || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
+             {
+               pre_dec = gen_rtx_MEM (mode,
+                                      gen_rtx_PRE_DEC (Pmode, r0));
 
-                   GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0),
-                                             pre_dec_ok);
+               GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0),
+                                         pre_dec_ok);
 
-                   pre_dec = NULL_RTX;
+               pre_dec = NULL_RTX;
 
-                   break;
+               break;
 
-                 pre_dec_ok:
-                   mem_rtx = NULL_RTX;
-                   offset += GET_MODE_SIZE (mode);
-                 }
-             while (0);
+             pre_dec_ok:
+               mem_rtx = NULL_RTX;
+               offset += GET_MODE_SIZE (mode);
+             }
+         while (0);
 
-             if (mem_rtx != NULL_RTX)
-               goto addr_ok;
+         if (mem_rtx != NULL_RTX)
+           goto addr_ok;
 
-             if (offset_in_r0 == -1)
-               {
-                 emit_move_insn (r0, GEN_INT (offset));
-                 offset_in_r0 = offset;
-               }
-             else if (offset != offset_in_r0)
+         if (offset_in_r0 == -1)
+           {
+             emit_move_insn (r0, GEN_INT (offset));
+             offset_in_r0 = offset;
+           }
+         else if (offset != offset_in_r0)
+           {
+             emit_move_insn (r0,
+                             gen_rtx_PLUS
+                             (Pmode, r0,
+                              GEN_INT (offset - offset_in_r0)));
+             offset_in_r0 += offset - offset_in_r0;
+           }
+                                             
+         if (pre_dec != NULL_RTX)
+           {
+             if (! sp_in_r0)
                {
                  emit_move_insn (r0,
                                  gen_rtx_PLUS
-                                 (Pmode, r0,
-                                  GEN_INT (offset - offset_in_r0)));
-                 offset_in_r0 += offset - offset_in_r0;
+                                 (Pmode, r0, stack_pointer_rtx));
+                 sp_in_r0 = 1;
                }
-                                                 
-             if (pre_dec != NULL_RTX)
-               {
-                 if (! sp_in_r0)
-                   {
-                     emit_move_insn (r0,
-                                     gen_rtx_PLUS
-                                     (Pmode, r0, stack_pointer_rtx));
-                     sp_in_r0 = 1;
-                   }
 
-                 offset -= GET_MODE_SIZE (mode);
-                 offset_in_r0 -= GET_MODE_SIZE (mode);
+             offset -= GET_MODE_SIZE (mode);
+             offset_in_r0 -= GET_MODE_SIZE (mode);
 
-                 mem_rtx = pre_dec;
-               }
-             else if (sp_in_r0)
-               mem_rtx = gen_rtx_MEM (mode, r0);
-             else
-               mem_rtx = gen_rtx_MEM (mode,
-                                      gen_rtx_PLUS (Pmode,
-                                                    stack_pointer_rtx,
-                                                    r0));
-
-             /* We must not use an r0-based address for target-branch
-                registers or for special registers without pre-dec
-                memory addresses, since we store their values in r0
-                first.  */
-             if (TARGET_REGISTER_P (i)
-                 || ((i == PR_REG || SPECIAL_REGISTER_P (i))
-                     && mem_rtx != pre_dec))
-               abort ();
-
-           addr_ok:
-             if (TARGET_REGISTER_P (i)
-                 || ((i == PR_REG || SPECIAL_REGISTER_P (i))
-                     && mem_rtx != pre_dec))
-               {
-                 rtx r0mode = gen_rtx_REG (GET_MODE (reg_rtx), R0_REG);
+             mem_rtx = pre_dec;
+           }
+         else if (sp_in_r0)
+           mem_rtx = gen_rtx_MEM (mode, r0);
+         else
+           mem_rtx = gen_rtx_MEM (mode,
+                                  gen_rtx_PLUS (Pmode,
+                                                stack_pointer_rtx,
+                                                r0));
+
+         /* We must not use an r0-based address for target-branch
+            registers or for special registers without pre-dec
+            memory addresses, since we store their values in r0
+            first.  */
+         if (TARGET_REGISTER_P (reg)
+             || ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
+                 && mem_rtx != pre_dec))
+           abort ();
 
-                 emit_move_insn (r0mode, reg_rtx);
+       addr_ok:
+         if (TARGET_REGISTER_P (reg)
+             || ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
+                 && mem_rtx != pre_dec))
+           {
+             rtx tmp_reg = gen_rtx_REG (GET_MODE (reg_rtx), *tmp_pnt);
+
+             emit_move_insn (tmp_reg, reg_rtx);
 
+             if (REGNO (tmp_reg) == R0_REG)
+               {
                  offset_in_r0 = -1;
                  sp_in_r0 = 0;
-
-                 reg_rtx = r0mode;
+                 if (refers_to_regno_p (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0))
+                   abort ();
                }
 
-             emit_move_insn (mem_rtx, reg_rtx);
+             if (*++tmp_pnt <= 0)
+               tmp_pnt = schedule.temps;
+
+             reg_rtx = tmp_reg;
            }
+         {
+           rtx insn;
+
+           /* Mark as interesting for dwarf cfi generator */
+           insn = emit_move_insn (mem_rtx, reg_rtx);
+           RTX_FRAME_RELATED_P (insn) = 1;
+
+           if (TARGET_SHCOMPACT && (offset_in_r0 != -1)) 
+             {
+               rtx reg_rtx = gen_rtx_REG (mode, reg);
+               rtx set, note_rtx;
+               rtx mem_rtx = gen_rtx_MEM (mode,
+                                          gen_rtx_PLUS (Pmode,
+                                                        stack_pointer_rtx,
+                                                        GEN_INT (offset)));
+
+               set = gen_rtx_SET (VOIDmode, mem_rtx, reg_rtx);
+               note_rtx = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, set,
+                                             REG_NOTES (insn));
+               REG_NOTES (insn) = note_rtx;
+             }
+         }
+       }
 
-      if (offset != d_rounding)
+      if (entry->offset != d_rounding)
        abort ();
     }
   else
@@ -5115,10 +5433,9 @@ sh_expand_prologue ()
   if (SHMEDIA_REGS_STACK_ADJUST ())
     {
       emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
-                     gen_rtx_SYMBOL_REF (Pmode,
-                                         TARGET_FPU_ANY
-                                         ? "__GCC_push_shmedia_regs"
-                                         : "__GCC_push_shmedia_regs_nofpu"));
+                     function_symbol (TARGET_FPU_ANY
+                                      ? "__GCC_push_shmedia_regs"
+                                      : "__GCC_push_shmedia_regs_nofpu"));
       /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
       emit_insn (gen_shmedia_save_restore_regs_compact
@@ -5140,7 +5457,7 @@ sh_expand_prologue ()
   target_flags = save_flags;
 
   output_stack_adjust (-rounded_frame_size (d) + d_rounding,
-                      stack_pointer_rtx, TARGET_SH5 ? 0 : 1, frame_insn);
+                      stack_pointer_rtx, 0, NULL);
 
   if (frame_pointer_needed)
     frame_insn (GEN_MOV (frame_pointer_rtx, stack_pointer_rtx));
@@ -5151,8 +5468,7 @@ sh_expand_prologue ()
       /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
       emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
-                     gen_rtx_SYMBOL_REF (Pmode,
-                                         "__GCC_shcompact_incoming_args"));
+                     function_symbol ("__GCC_shcompact_incoming_args"));
       emit_insn (gen_shcompact_incoming_args ());
     }
 }
@@ -5165,20 +5481,43 @@ sh_expand_epilogue ()
   int d_rounding = 0;
 
   int save_flags = target_flags;
-  int frame_size;
+  int frame_size, save_size;
   int fpscr_deferred = 0;
 
   d = calc_live_regs (&live_regs_mask);
 
-  if (TARGET_SH5 && d % (STACK_BOUNDARY / BITS_PER_UNIT))
-    d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
-                 - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+  save_size = d;
+  frame_size = rounded_frame_size (d);
 
-  frame_size = rounded_frame_size (d) - d_rounding;
+  if (TARGET_SH5)
+    {
+      int tregs_space = shmedia_target_regs_stack_adjust (&live_regs_mask);
+      int total_size;
+      if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
+      d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+                   - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+      total_size = d + tregs_space;
+      total_size += rounded_frame_size (total_size);
+      save_size = total_size - frame_size;
+
+      /* If adjusting the stack in a single step costs nothing extra, do so.
+        I.e. either if a single addi is enough, or we need a movi anyway,
+        and we don't exceed the maximum offset range (the test for the
+        latter is conservative for simplicity).  */
+      if (TARGET_SHMEDIA
+         && ! frame_pointer_needed
+         && (CONST_OK_FOR_I10 (total_size)
+             || (! CONST_OK_FOR_I10 (save_size + d_rounding)
+                 && total_size <= 2044)))
+       d_rounding = frame_size;
+
+      frame_size -= d_rounding;
+    }
 
   if (frame_pointer_needed)
     {
-      output_stack_adjust (frame_size, frame_pointer_rtx, 7, emit_insn);
+      output_stack_adjust (frame_size, frame_pointer_rtx, 1, &live_regs_mask);
 
       /* We must avoid moving the stack pointer adjustment past code
         which reads from the local frame, else an interrupt could
@@ -5194,16 +5533,15 @@ sh_expand_epilogue ()
         occur after the SP adjustment and clobber data in the local
         frame.  */
       emit_insn (gen_blockage ());
-      output_stack_adjust (frame_size, stack_pointer_rtx, 7, emit_insn);
+      output_stack_adjust (frame_size, stack_pointer_rtx, 1, &live_regs_mask);
     }
 
   if (SHMEDIA_REGS_STACK_ADJUST ())
     {
       emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
-                     gen_rtx_SYMBOL_REF (Pmode,
-                                         TARGET_FPU_ANY
-                                         ? "__GCC_pop_shmedia_regs"
-                                         : "__GCC_pop_shmedia_regs_nofpu"));
+                     function_symbol (TARGET_FPU_ANY
+                                      ? "__GCC_pop_shmedia_regs"
+                                      : "__GCC_pop_shmedia_regs_nofpu"));
       /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
       emit_insn (gen_shmedia_save_restore_regs_compact
@@ -5216,173 +5554,159 @@ sh_expand_epilogue ()
     emit_insn (gen_toggle_sz ());
   if (TARGET_SH5)
     {
-      int offset = d_rounding;
+      int offset_base, offset;
       int offset_in_r0 = -1;
       int sp_in_r0 = 0;
-      int align;
       rtx r0 = gen_rtx_REG (Pmode, R0_REG);
-      int tmp_regno = R20_REG;
-      
-      /* We loop twice: first, we save 8-byte aligned registers in the
-        higher addresses, that are known to be aligned.  Then, we
-        proceed to saving 32-bit registers that don't need 8-byte
-        alignment.  */
-      for (align = 0; align <= 1; align++)
-       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-         if (TEST_HARD_REG_BIT (live_regs_mask, i))
-           {
-             enum machine_mode mode = REGISTER_NATURAL_MODE (i);
-             int reg = i;
-             rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn;
+      save_schedule schedule;
+      save_entry *entry;
+      int *tmp_pnt;
+
+      entry = sh5_schedule_saves (&live_regs_mask, &schedule, d_rounding);
+      offset_base = -entry[1].offset + d_rounding;
+      tmp_pnt = schedule.temps;
+      for (; entry->mode != VOIDmode; entry--)
+       {
+         enum machine_mode mode = entry->mode;
+         int reg = entry->reg;
+         rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn;
 
-             if (mode == SFmode && (i % 2) == 0
-                 && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
-                 && (TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1))))
-               {
-                 mode = DFmode;
-                 i++;
-               }
+         offset = offset_base + entry->offset;
+         reg_rtx = gen_rtx_REG (mode, reg);
 
-             /* If we're doing the aligned pass and this is not aligned,
-                or we're doing the unaligned pass and this is aligned,
-                skip it.  */
-             if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
-                  == 0) != align)
-               continue;
+         mem_rtx = gen_rtx_MEM (mode,
+                                gen_rtx_PLUS (Pmode,
+                                              stack_pointer_rtx,
+                                              GEN_INT (offset)));
 
-             reg_rtx = gen_rtx_REG (mode, reg);
+         GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc);
 
-             mem_rtx = gen_rtx_MEM (mode,
-                                    gen_rtx_PLUS (Pmode,
-                                                  stack_pointer_rtx,
-                                                  GEN_INT (offset)));
+         mem_rtx = NULL_RTX;
 
-             GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc);
-
-             mem_rtx = NULL_RTX;
+       try_post_inc:
+         do
+           if (HAVE_POST_INCREMENT
+               && (offset == offset_in_r0
+                   || (offset + GET_MODE_SIZE (mode) != d + d_rounding
+                       && mem_rtx == NULL_RTX)
+                   || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
+             {
+               post_inc = gen_rtx_MEM (mode,
+                                       gen_rtx_POST_INC (Pmode, r0));
 
-           try_post_inc:
-             do
-               if (HAVE_POST_INCREMENT
-                   && (offset == offset_in_r0
-                       || (offset + GET_MODE_SIZE (mode) != d + d_rounding
-                           && mem_rtx == NULL_RTX)
-                       || i == PR_REG || SPECIAL_REGISTER_P (i)))
-                 {
-                   post_inc = gen_rtx_MEM (mode,
-                                           gen_rtx_POST_INC (Pmode, r0));
+               GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0),
+                                         post_inc_ok);
 
-                   GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0),
-                                             post_inc_ok);
+               post_inc = NULL_RTX;
 
-                   post_inc = NULL_RTX;
+               break;
+               
+             post_inc_ok:
+               mem_rtx = NULL_RTX;
+             }
+         while (0);
+         
+         if (mem_rtx != NULL_RTX)
+           goto addr_ok;
 
-                   break;
-                   
-                 post_inc_ok:
-                   mem_rtx = NULL_RTX;
-                 }
-             while (0);
+         if (offset_in_r0 == -1)
+           {
+             emit_move_insn (r0, GEN_INT (offset));
+             offset_in_r0 = offset;
+           }
+         else if (offset != offset_in_r0)
+           {
+             emit_move_insn (r0,
+                             gen_rtx_PLUS
+                             (Pmode, r0,
+                              GEN_INT (offset - offset_in_r0)));
+             offset_in_r0 += offset - offset_in_r0;
+           }
              
-             if (mem_rtx != NULL_RTX)
-               goto addr_ok;
-
-             if (offset_in_r0 == -1)
-               {
-                 emit_move_insn (r0, GEN_INT (offset));
-                 offset_in_r0 = offset;
-               }
-             else if (offset != offset_in_r0)
+         if (post_inc != NULL_RTX)
+           {
+             if (! sp_in_r0)
                {
                  emit_move_insn (r0,
                                  gen_rtx_PLUS
-                                 (Pmode, r0,
-                                  GEN_INT (offset - offset_in_r0)));
-                 offset_in_r0 += offset - offset_in_r0;
+                                 (Pmode, r0, stack_pointer_rtx));
+                 sp_in_r0 = 1;
                }
-                 
-             if (post_inc != NULL_RTX)
-               {
-                 if (! sp_in_r0)
-                   {
-                     emit_move_insn (r0,
-                                     gen_rtx_PLUS
-                                     (Pmode, r0, stack_pointer_rtx));
-                     sp_in_r0 = 1;
-                   }
-                 
-                 mem_rtx = post_inc;
+             
+             mem_rtx = post_inc;
 
-                 offset_in_r0 += GET_MODE_SIZE (mode);
-               }
-             else if (sp_in_r0)
-               mem_rtx = gen_rtx_MEM (mode, r0);
-             else
-               mem_rtx = gen_rtx_MEM (mode,
-                                      gen_rtx_PLUS (Pmode,
-                                                    stack_pointer_rtx,
-                                                    r0));
-
-             if ((i == PR_REG || SPECIAL_REGISTER_P (i))
-                 && mem_rtx != post_inc)
-               abort ();
-
-           addr_ok:
-             if ((i == PR_REG || SPECIAL_REGISTER_P (i))
-                 && mem_rtx != post_inc)
-               {
-                 insn = emit_move_insn (r0, mem_rtx);
-                 mem_rtx = r0;
-               }
-             else if (TARGET_REGISTER_P (i))
-               {
-                 rtx tmp_reg = gen_rtx_REG (mode, tmp_regno);
-
-                 /* Give the scheduler a bit of freedom by using R20..R23
-                    in a round-robin fashion.  Don't use R1 here because
-                    we want to use it for EH_RETURN_STACKADJ_RTX.  */
-                 insn = emit_move_insn (tmp_reg, mem_rtx);
-                 mem_rtx = tmp_reg;
-                 if (++tmp_regno > R23_REG)
-                   tmp_regno = R20_REG;
-               }
+             offset_in_r0 += GET_MODE_SIZE (mode);
+           }
+         else if (sp_in_r0)
+           mem_rtx = gen_rtx_MEM (mode, r0);
+         else
+           mem_rtx = gen_rtx_MEM (mode,
+                                  gen_rtx_PLUS (Pmode,
+                                                stack_pointer_rtx,
+                                                r0));
 
-             insn = emit_move_insn (reg_rtx, mem_rtx);
+         if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
+             && mem_rtx != post_inc)
+           abort ();
 
-             offset += GET_MODE_SIZE (mode);
+       addr_ok:
+         if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
+             && mem_rtx != post_inc)
+           {
+             insn = emit_move_insn (r0, mem_rtx);
+             mem_rtx = r0;
+           }
+         else if (TARGET_REGISTER_P (reg))
+           {
+             rtx tmp_reg = gen_rtx_REG (mode, *tmp_pnt);
+
+             /* Give the scheduler a bit of freedom by using up to
+                MAX_TEMPS registers in a round-robin fashion.  */
+             insn = emit_move_insn (tmp_reg, mem_rtx);
+             mem_rtx = tmp_reg;
+             if (*++tmp_pnt < 0)
+               tmp_pnt = schedule.temps;
            }
 
-      if (offset != d + d_rounding)
-       abort ();
+         insn = emit_move_insn (reg_rtx, mem_rtx);
+         if (reg == PR_MEDIA_REG && sh_media_register_for_return () >= 0)
+           /* This is dead, unless we return with a sibcall.  */
+           REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
+                                                 const0_rtx,
+                                                 REG_NOTES (insn));
+       }
 
-      goto finish;
+      if (entry->offset + offset_base != d + d_rounding)
+       abort ();
     }
-  else
-    d = 0;
-  if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
-    pop (PR_REG);
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+  else /* ! TARGET_SH5 */
     {
-      int j = (FIRST_PSEUDO_REGISTER - 1) - i;
+      save_size = 0;
+      if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
+       pop (PR_REG);
+      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+       {
+         int j = (FIRST_PSEUDO_REGISTER - 1) - i;
+  
+         if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
+             && hard_regs_intersect_p (&live_regs_mask,
+                                       &reg_class_contents[DF_REGS]))
+           fpscr_deferred = 1;
+         else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j))
+           pop (j);
+         if (j == FIRST_FP_REG && fpscr_deferred)
+           pop (FPSCR_REG);
 
-      if (j == FPSCR_REG && current_function_interrupt && TARGET_FMOVD
-         && hard_regs_intersect_p (&live_regs_mask,
-                                   &reg_class_contents[DF_REGS]))
-       fpscr_deferred = 1;
-      else if (j != PR_REG && TEST_HARD_REG_BIT (live_regs_mask, j))
-       pop (j);
-      if (j == FIRST_FP_REG && fpscr_deferred)
-       pop (FPSCR_REG);
+       }
     }
- finish:
   if (target_flags != save_flags && ! current_function_interrupt)
     emit_insn (gen_toggle_sz ());
   target_flags = save_flags;
 
   output_stack_adjust (extra_push + current_function_pretend_args_size
-                      + d + d_rounding
+                      + save_size + d_rounding
                       + current_function_args_info.stack_regs * 8,
-                      stack_pointer_rtx, 7, emit_insn);
+                      stack_pointer_rtx, 1, NULL);
 
   if (current_function_calls_eh_return)
     emit_insn (GEN_ADD3 (stack_pointer_rtx, stack_pointer_rtx,
@@ -5427,7 +5751,6 @@ sh_set_return_address (ra, tmp)
 {
   HARD_REG_SET live_regs_mask;
   int d;
-  int d_rounding = 0;
   int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
   int pr_offset;
 
@@ -5459,56 +5782,26 @@ sh_set_return_address (ra, tmp)
 
   if (TARGET_SH5)
     {
-      int i;
       int offset;
-      int align;
+      save_schedule schedule;
+      save_entry *entry;
       
-      if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
-       d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
-                     - d % (STACK_BOUNDARY / BITS_PER_UNIT));
-
-      offset = 0;
-
-      /* We loop twice: first, we save 8-byte aligned registers in the
-        higher addresses, that are known to be aligned.  Then, we
-        proceed to saving 32-bit registers that don't need 8-byte
-        alignment.  */
-      for (align = 0; align <= 1; align++)
-       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
-         if (TEST_HARD_REG_BIT (live_regs_mask, i))
-           {
-             enum machine_mode mode = REGISTER_NATURAL_MODE (i);
-
-             if (mode == SFmode && (i % 2) == 0
-                 && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
-                 && (TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1))))
-               {
-                 mode = DFmode;
-                 i++;
-               }
-
-             /* If we're doing the aligned pass and this is not aligned,
-                or we're doing the unaligned pass and this is aligned,
-                skip it.  */
-             if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
-                  == 0) != align)
-               continue;
-
-             if (i == pr_reg)
-               goto found;
-
-             offset += GET_MODE_SIZE (mode);
-           }
+      entry = sh5_schedule_saves (&live_regs_mask, &schedule, 0);
+      offset = entry[1].offset;
+      for (; entry->mode != VOIDmode; entry--)
+       if (entry->reg == pr_reg)
+         goto found;
 
       /* We can't find pr register.  */
       abort ();
 
     found:
-      pr_offset = (rounded_frame_size (d) - d_rounding + offset
+      offset = entry->offset - offset;
+      pr_offset = (rounded_frame_size (d) + offset
                   + SHMEDIA_REGS_STACK_ADJUST ());
     }
   else
-    pr_offset = rounded_frame_size (d) - d_rounding;
+    pr_offset = rounded_frame_size (d);
 
   emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset)));
   emit_insn (GEN_ADD3 (tmp, tmp, frame_pointer_rtx));
@@ -5614,7 +5907,7 @@ sh_builtin_saveregs ()
     move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg,
                         adjust_address (regbuf, BLKmode,
                                         n_floatregs * UNITS_PER_WORD),
-                        n_intregs, n_intregs * UNITS_PER_WORD);
+                        n_intregs);
 
   if (TARGET_SHMEDIA)
     /* Return the address of the regbuf.  */
@@ -5801,8 +6094,9 @@ sh_va_arg (valist, type)
   HOST_WIDE_INT size, rsize;
   tree tmp, pptr_type_node;
   rtx addr_rtx, r;
-  rtx result;
+  rtx result_ptr, result = NULL_RTX;
   int pass_by_ref = MUST_PASS_IN_STACK (TYPE_MODE (type), type);
+  rtx lab_over;
 
   size = int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
@@ -5816,7 +6110,7 @@ sh_va_arg (valist, type)
       tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
       tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
       int pass_as_float;
-      rtx lab_false, lab_over;
+      rtx lab_false;
 
       f_next_o = TYPE_FIELDS (va_list_type_node);
       f_next_o_limit = TREE_CHAIN (f_next_o);
@@ -5834,6 +6128,16 @@ sh_va_arg (valist, type)
       next_stack = build (COMPONENT_REF, TREE_TYPE (f_next_stack),
                          valist, f_next_stack);
 
+      /* Structures with a single member with a distinct mode are passed
+        like their member.  This is relevant if the latter has a REAL_TYPE
+        or COMPLEX_TYPE type.  */
+      if (TREE_CODE (type) == RECORD_TYPE
+         && TYPE_FIELDS (type)
+         && TREE_CODE (TYPE_FIELDS (type)) == FIELD_DECL
+         && (TREE_CODE (TREE_TYPE (TYPE_FIELDS (type))) == REAL_TYPE
+             || TREE_CODE (TREE_TYPE (TYPE_FIELDS (type))) == COMPLEX_TYPE)
+          && TREE_CHAIN (TYPE_FIELDS (type)) == NULL_TREE)
+       type = TREE_TYPE (TYPE_FIELDS (type));
       if (TARGET_SH4)
        {
          pass_as_float = ((TREE_CODE (type) == REAL_TYPE && size <= 8)
@@ -5850,6 +6154,9 @@ sh_va_arg (valist, type)
       lab_false = gen_label_rtx ();
       lab_over = gen_label_rtx ();
 
+      tmp = make_tree (pptr_type_node, addr_rtx);
+      valist = build1 (INDIRECT_REF, ptr_type_node, tmp);
+
       if (pass_as_float)
        {
          int first_floatreg
@@ -5879,6 +6186,37 @@ sh_va_arg (valist, type)
          if (r != addr_rtx)
            emit_move_insn (addr_rtx, r);
 
+#ifdef FUNCTION_ARG_SCmode_WART
+         if (TYPE_MODE (type) == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
+           {
+             rtx addr, real, imag, result_value, slot;
+             tree subtype = TREE_TYPE (type);
+
+             addr = std_expand_builtin_va_arg (valist, subtype);
+#ifdef POINTERS_EXTEND_UNSIGNED
+             if (GET_MODE (addr) != Pmode)
+               addr = convert_memory_address (Pmode, addr);
+#endif
+             imag = gen_rtx_MEM (TYPE_MODE (type), addr);
+             set_mem_alias_set (imag, get_varargs_alias_set ());
+
+             addr = std_expand_builtin_va_arg (valist, subtype);
+#ifdef POINTERS_EXTEND_UNSIGNED
+             if (GET_MODE (addr) != Pmode)
+               addr = convert_memory_address (Pmode, addr);
+#endif
+             real = gen_rtx_MEM (TYPE_MODE (type), addr);
+             set_mem_alias_set (real, get_varargs_alias_set ());
+
+             result_value = gen_rtx_CONCAT (SCmode, real, imag);
+             /* ??? this interface is stupid - why require a pointer?  */
+             result = gen_reg_rtx (Pmode);
+             slot = assign_stack_temp (SCmode, 8, 0);
+             emit_move_insn (slot, result_value);
+             emit_move_insn (result, XEXP (slot, 0));
+           }
+#endif /* FUNCTION_ARG_SCmode_WART */
+
          emit_jump_insn (gen_jump (lab_over));
          emit_barrier ();
          emit_label (lab_false);
@@ -5921,16 +6259,22 @@ sh_va_arg (valist, type)
            emit_move_insn (addr_rtx, r);
        }
 
-      emit_label (lab_over);
-
-      tmp = make_tree (pptr_type_node, addr_rtx);
-      valist = build1 (INDIRECT_REF, ptr_type_node, tmp);
+      if (! result)
+        emit_label (lab_over);
     }
 
   /* ??? In va-sh.h, there had been code to make values larger than
      size 8 indirect.  This does not match the FUNCTION_ARG macros.  */
 
-  result = std_expand_builtin_va_arg (valist, type);
+  result_ptr = std_expand_builtin_va_arg (valist, type);
+  if (result)
+    {
+      emit_move_insn (result, result_ptr);
+      emit_label (lab_over);
+    }
+  else
+    result = result_ptr;
+
   if (pass_by_ref)
     {
 #ifdef POINTERS_EXTEND_UNSIGNED
@@ -5959,10 +6303,18 @@ initial_elimination_offset (from, to)
   int total_auto_space;
   int save_flags = target_flags;
   int copy_flags;
-
   HARD_REG_SET live_regs_mask;
+
+  shmedia_space_reserved_for_target_registers = false;
   regs_saved = calc_live_regs (&live_regs_mask);
   regs_saved += SHMEDIA_REGS_STACK_ADJUST ();
+
+  if (shmedia_reserve_space_for_target_registers_p (regs_saved, &live_regs_mask))
+    {
+      shmedia_space_reserved_for_target_registers = true;
+      regs_saved += shmedia_target_regs_stack_adjust (&live_regs_mask);
+    }
+
   if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT))
     regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
                           - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT));
@@ -5990,9 +6342,10 @@ initial_elimination_offset (from, to)
     {
       if (TARGET_SH5)
        {
-         int i, n = total_saved_regs_space;
-         int align;
+         int n = total_saved_regs_space;
          int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
+         save_schedule schedule;
+         save_entry *entry;
          
          n += total_auto_space;
 
@@ -6002,40 +6355,13 @@ initial_elimination_offset (from, to)
 
          target_flags = copy_flags;
 
-         /* We loop twice: first, check 8-byte aligned registers,
-            that are stored in the higher addresses, that are known
-            to be aligned.  Then, check 32-bit registers that don't
-            need 8-byte alignment.  */
-         for (align = 1; align >= 0; align--)
-           for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
-             if (TEST_HARD_REG_BIT (live_regs_mask, i))
-               {
-                 enum machine_mode mode = REGISTER_NATURAL_MODE (i);
-
-                 if (mode == SFmode && (i % 2) == 1
-                     && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
-                     && TEST_HARD_REG_BIT (live_regs_mask, (i ^ 1)))
-                   {
-                     mode = DFmode;
-                     i--;
-                   }
-               
-                 /* If we're doing the aligned pass and this is not aligned,
-                    or we're doing the unaligned pass and this is aligned,
-                    skip it.  */
-                 if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
-                      == 0) != align)
-                   continue;
-
-                 n -= GET_MODE_SIZE (mode);
-
-                 if (i == pr_reg)
-                   {
-                     target_flags = save_flags;
-                     return n;
-                   }
-               }
-
+         sh5_schedule_saves (&live_regs_mask, &schedule, n);
+         for (entry = &schedule.entries[1]; entry->mode != VOIDmode; entry++)
+           if (entry->reg == pr_reg)
+             {
+               target_flags = save_flags;
+               return entry->offset;
+             }
          abort ();
        }
       else
@@ -6045,7 +6371,7 @@ initial_elimination_offset (from, to)
   abort ();
 }
 \f
-/* Handle machine specific pragmas to be semi-compatible with Hitachi
+/* Handle machine specific pragmas to be semi-compatible with Renesas
    compiler.  */
 
 void
@@ -6381,17 +6707,17 @@ arith_operand (op, mode)
   if (TARGET_SHMEDIA)
     {
       /* FIXME: We should be checking whether the CONST_INT fits in a
-        CONST_OK_FOR_J here, but this causes reload_cse to crash when
+        CONST_OK_FOR_I16 here, but this causes reload_cse to crash when
         attempting to transform a sequence of two 64-bit sets of the
         same register from literal constants into a set and an add,
         when the difference is too wide for an add.  */
       if (GET_CODE (op) == CONST_INT
-         || EXTRA_CONSTRAINT_S (op))
+         || EXTRA_CONSTRAINT_C16 (op))
        return 1;
       else
        return 0;
     }
-  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I (INTVAL (op)))
+  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I08 (INTVAL (op)))
     return 1;
 
   return 0;
@@ -6407,7 +6733,7 @@ arith_reg_or_0_operand (op, mode)
   if (arith_reg_operand (op, mode))
     return 1;
 
-  if (EXTRA_CONSTRAINT_U (op))
+  if (EXTRA_CONSTRAINT_Z (op))
     return 1;
 
   return 0;
@@ -6422,7 +6748,7 @@ shmedia_6bit_operand (op, mode)
      enum machine_mode mode;
 {
   return (arith_reg_operand (op, mode)
-         || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_O (INTVAL (op))));
+         || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I06 (INTVAL (op))));
 }
 
 /* Returns 1 if OP is a valid source operand for a logical operation.  */
@@ -6437,12 +6763,12 @@ logical_operand (op, mode)
 
   if (TARGET_SHMEDIA)
     {
-      if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_P (INTVAL (op)))
+      if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I10 (INTVAL (op)))
        return 1;
       else
        return 0;
     }
-  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_L (INTVAL (op)))
+  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_K08 (INTVAL (op)))
     return 1;
 
   return 0;
@@ -6460,8 +6786,7 @@ and_operand (op, mode)
   if (TARGET_SHMEDIA
       && mode == DImode
       && GET_CODE (op) == CONST_INT
-      && (INTVAL (op) == (unsigned) 0xffffffff
-         || INTVAL (op) == (HOST_WIDE_INT) -1 << 32))
+      && CONST_OK_FOR_J16 (INTVAL (op)))
        return 1;
 
   return 0;
@@ -6555,28 +6880,9 @@ tls_symbolic_operand (op, mode)
      rtx op;
      enum machine_mode mode ATTRIBUTE_UNUSED;
 {
-  const char *str;
-
   if (GET_CODE (op) != SYMBOL_REF)
     return 0;
-
-  str = XSTR (op, 0);
-  STRIP_DATALABEL_ENCODING(str, str);  
-  if (! TLS_SYMNAME_P (str))
-    return 0;
-
-  switch (str[1])
-    {
-    case 'G':
-      return TLS_MODEL_GLOBAL_DYNAMIC;
-    case 'L':
-      return TLS_MODEL_LOCAL_DYNAMIC;
-    case 'i':
-      return TLS_MODEL_INITIAL_EXEC;
-    case 'l':
-      return TLS_MODEL_LOCAL_EXEC;
-    }
-  return 0;
+  return SYMBOL_REF_TLS_MODEL (op);
 }
 
 int
@@ -6755,7 +7061,7 @@ target_operand (op, mode)
     return 0;
 
   if ((GET_MODE (op) == DImode || GET_MODE (op) == VOIDmode)
-      && EXTRA_CONSTRAINT_T (op))
+      && EXTRA_CONSTRAINT_Csy (op))
     return ! reload_completed;
 
   return target_reg_operand (op, mode);
@@ -7359,10 +7665,7 @@ legitimize_pic_address (orig, mode, reg)
     return orig;
 
   if (GET_CODE (orig) == LABEL_REF
-      || (GET_CODE (orig) == SYMBOL_REF
-         && (CONSTANT_POOL_ADDRESS_P (orig)
-             /* SYMBOL_REF_FLAG is set on static symbols.  */
-             || SYMBOL_REF_FLAG (orig))))
+      || (GET_CODE (orig) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (orig)))
     {
       if (reg == 0)
        reg = gen_reg_rtx (Pmode);
@@ -7680,106 +7983,25 @@ sh_cannot_modify_jumps_p ()
   return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
 }
 
-static bool
-sh_ms_bitfield_layout_p (record_type)
-     tree record_type ATTRIBUTE_UNUSED;
+static int
+sh_target_reg_class (void)
 {
-  return TARGET_SH5;
+  return TARGET_SHMEDIA ? TARGET_REGS : NO_REGS;
 }
 
-/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
-   may access it using GOTOFF instead of GOT.  */
-
-static void
-sh_encode_section_info (decl, first)
-     tree decl;
-     int first;
+static bool
+sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen)
 {
-  rtx rtl, symbol;
-
-  if (DECL_P (decl))
-    rtl = DECL_RTL (decl);
-  else
-    rtl = TREE_CST_RTL (decl);
-  if (GET_CODE (rtl) != MEM)
-    return;
-  symbol = XEXP (rtl, 0);
-  if (GET_CODE (symbol) != SYMBOL_REF)
-    return;
-
-  if (flag_pic)
-    SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
-
-  if (TREE_CODE (decl) == VAR_DECL && DECL_THREAD_LOCAL (decl))
-    {
-      const char *symbol_str, *orig_str;
-      bool is_local;
-      enum tls_model kind;
-      char encoding;
-      char *newstr;
-      size_t len, dlen;
-
-      orig_str = XSTR (symbol, 0);
-      is_local = (*targetm.binds_local_p) (decl);
-
-      if (! flag_pic)
-       {
-         if (is_local)
-           kind = TLS_MODEL_LOCAL_EXEC;
-         else
-           kind = TLS_MODEL_INITIAL_EXEC;
-       }
-      else if (is_local)
-       kind = TLS_MODEL_LOCAL_DYNAMIC;
-      else
-       kind = TLS_MODEL_GLOBAL_DYNAMIC;
-      if (kind < flag_tls_default)
-       kind = flag_tls_default;
-
-      STRIP_DATALABEL_ENCODING (symbol_str, orig_str);
-      dlen = symbol_str - orig_str;
-
-      encoding = " GLil"[kind];
-      if (TLS_SYMNAME_P (symbol_str))
-       {
-         if (encoding == symbol_str[1])
-           return;
-         /* Handle the changes from initial-exec to local-exec and
-            from global-dynamic to local-dynamic.  */
-         if ((encoding == 'l' && symbol_str[1] == 'i')
-             || (encoding == 'L' && symbol_str[1] == 'G'))
-           symbol_str += 2;
-         else
-           abort ();
-       }
-
-      len = strlen (symbol_str);
-      newstr = alloca (dlen + len + 3);
-      if (dlen)
-       memcpy (newstr, orig_str, dlen);
-      newstr[dlen + 0] = SH_TLS_ENCODING[0];
-      newstr[dlen + 1] = encoding;
-      memcpy (newstr + dlen + 2, symbol_str, len + 1);
-
-      XSTR (symbol, 0) = ggc_alloc_string (newstr, dlen + len + 2);
-    }
-
-  if (TARGET_SH5 && first && TREE_CODE (decl) != FUNCTION_DECL)
-    XEXP (rtl, 0) = gen_datalabel_ref (symbol);
+  return (shmedia_space_reserved_for_target_registers
+         && (! after_prologue_epilogue_gen || TARGET_SAVE_ALL_TARGET_REGS));
 }
 
-/* Undo the effects of the above.  */
-
-static const char *
-sh_strip_name_encoding (str)
-     const char *str;
+static bool
+sh_ms_bitfield_layout_p (record_type)
+     tree record_type ATTRIBUTE_UNUSED;
 {
-  STRIP_DATALABEL_ENCODING (str, str);
-  STRIP_TLS_ENCODING (str, str);
-  str += *str == '*';
-  return str;
+  return TARGET_SH5;
 }
-
 \f
 /* 
    On the SH1..SH4, the trampoline looks like
@@ -7944,7 +8166,7 @@ sh_initialize_trampoline (tramp, fnaddr, cxt)
   if (TARGET_HARVARD)
     {
       if (TARGET_USERMODE)
-       emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__ic_invalidate"),
+       emit_library_call (function_symbol ("__ic_invalidate"),
                           0, VOIDmode, 1, tramp, SImode);
       else
        emit_insn (gen_ic_invalidate_line (tramp));
@@ -8132,7 +8354,7 @@ sh_media_init_builtins ()
   const struct builtin_description *d;
 
   memset (shared, 0, sizeof shared);
-  for (d = bdesc; d - bdesc < (int) (sizeof bdesc / sizeof bdesc[0]); d++)
+  for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++)
     {
       tree type, arg_type;
       int signature = d->signature;
@@ -8359,11 +8581,18 @@ sh_register_move_cost (mode, srcclass, dstclass)
   if (dstclass == T_REGS || dstclass == PR_REGS)
     return 10;
 
+  if (dstclass == MAC_REGS && srcclass == MAC_REGS)
+    return 4;
+
   if (mode == SImode && ! TARGET_SHMEDIA && TARGET_FMOVD
       && REGCLASS_HAS_FP_REG (srcclass)
       && REGCLASS_HAS_FP_REG (dstclass))
     return 4;
 
+  if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
+      || (dstclass== MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
+    return 9;
+
   if ((REGCLASS_HAS_FP_REG (dstclass)
        && REGCLASS_HAS_GENERAL_REG (srcclass))
       || (REGCLASS_HAS_GENERAL_REG (dstclass)
@@ -8437,16 +8666,16 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
   int structure_value_byref = 0;
   rtx this, this_value, sibcall, insns, funexp;
   tree funtype = TREE_TYPE (function);
-  int simple_add
-    = (TARGET_SHMEDIA ? CONST_OK_FOR_J (delta) : CONST_OK_FOR_I (delta));
+  int simple_add = CONST_OK_FOR_ADD (delta);
   int did_load = 0;
   rtx scratch0, scratch1, scratch2;
 
   reload_completed = 1;
+  epilogue_completed = 1;
   no_new_pseudos = 1;
   current_function_uses_only_leaf_regs = 1;
 
-  emit_note (NULL, NOTE_INSN_PROLOGUE_END);
+  emit_note (NOTE_INSN_PROLOGUE_END);
 
   /* Find the "this" pointer.  We have such a wide range of ABIs for the
      SH that it's best to do this completely machine independently.
@@ -8521,9 +8750,7 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
          emit_move_insn (scratch1, GEN_INT (vcall_offset));
          offset_addr = gen_rtx_PLUS (Pmode, scratch0, scratch1);
        }
-      else if (TARGET_SHMEDIA
-              ? CONST_OK_FOR_J (vcall_offset)
-              : CONST_OK_FOR_I (vcall_offset))
+      else if (CONST_OK_FOR_ADD (vcall_offset))
        {
          emit_insn (gen_add2_insn (scratch0, GEN_INT (vcall_offset)));
          offset_addr = scratch0;
@@ -8557,9 +8784,11 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
   use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this);
   emit_barrier ();
 
-    /* Run just enough of rest_of_compilation to do scheduling and get
+  /* Run just enough of rest_of_compilation to do scheduling and get
      the insns emitted.  Note that use_thunk calls
      assemble_start_function and assemble_end_function.  */
+
+  insn_locators_initialize ();
   insns = get_insns ();
 
   if (optimize > 0 && flag_schedule_insns_after_reload)
@@ -8573,7 +8802,7 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
       schedule_insns (rtl_dump_file);
     }
 
-  MACHINE_DEPENDENT_REORG (insns);
+  sh_reorg ();
 
   if (optimize > 0 && flag_delayed_branch)
       dbr_schedule (insns, rtl_dump_file);
@@ -8592,7 +8821,55 @@ sh_output_mi_thunk (file, thunk_fndecl, delta, vcall_offset, function)
     }
 
   reload_completed = 0;
+  epilogue_completed = 0;
   no_new_pseudos = 0;
 }
 
+rtx
+function_symbol (const char *name)
+{
+  rtx sym = gen_rtx_SYMBOL_REF (Pmode, name);
+  SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
+  return sym;
+}
+
+/* Find the number of a general purpose register in S.  */
+static int
+scavenge_reg (HARD_REG_SET *s)
+{
+  int r;
+  for (r = FIRST_GENERAL_REG; r <= LAST_GENERAL_REG; r++)
+    if (TEST_HARD_REG_BIT (*s, r))
+      return r;
+  return -1;
+}
+
+rtx
+sh_get_pr_initial_val (void)
+{
+  rtx val;
+
+  /* ??? Unfortunately, get_hard_reg_initial_val doesn't always work for the
+     PR register on SHcompact, because it might be clobbered by the prologue.
+     We check first if that is known to be the case.  */
+  if (TARGET_SHCOMPACT
+      && ((current_function_args_info.call_cookie
+          & ~ CALL_COOKIE_RET_TRAMP (1))
+         || current_function_has_nonlocal_label))
+    return gen_rtx_MEM (SImode, return_address_pointer_rtx);
+
+  /* If we haven't finished rtl generation, there might be a nonlocal label
+     that we haven't seen yet.
+     ??? get_hard_reg_initial_val fails if it is called while no_new_pseudos
+     is set, unless it has been called before for the same register.  And even
+     then, we end in trouble if we didn't use the register in the same
+     basic block before.  So call get_hard_reg_initial_val now and wrap it
+     in an unspec if we might need to replace it.  */
+  val
+    = get_hard_reg_initial_val (Pmode, TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+  if (TARGET_SHCOMPACT && rtx_equal_function_value_matters)
+    return gen_rtx_UNSPEC (SImode, gen_rtvec (1, val), UNSPEC_RA);
+  return val;
+}
+
 #include "gt-sh.h"