OSDN Git Service

* config/sh/sh.c (mark_use): Remove.
[pf3gnuchains/gcc-fork.git] / gcc / config / sh / sh.c
index 162f5c6..ae1f916 100644 (file)
@@ -1,6 +1,6 @@
 /* Output routines for GCC for Renesas / SuperH SH.
-   Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002,
-   2003, 2004 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
    Contributed by Steve Chamberlain (sac@cygnus.com).
    Improved by Jim Wilson (wilson@cygnus.com).
 
@@ -18,8 +18,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -47,12 +47,15 @@ Boston, MA 02111-1307, USA.  */
 #include "real.h"
 #include "langhooks.h"
 #include "basic-block.h"
-#include "ra.h"
+#include "df.h"
 #include "cfglayout.h"
 #include "intl.h"
 #include "sched-int.h"
 #include "ggc.h"
 #include "tree-gimple.h"
+#include "cfgloop.h"
+#include "alloc-pool.h"
+#include "tm-constrs.h"
 
 
 int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
@@ -70,35 +73,8 @@ int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
 /* Set to 1 by expand_prologue() when the function is an interrupt handler.  */
 int current_function_interrupt;
 
-/* ??? The pragma interrupt support will not work for SH3.  */
-/* This is set by #pragma interrupt and #pragma trapa, and causes gcc to
-   output code for the next function appropriate for an interrupt handler.  */
-int pragma_interrupt;
-
-/* This is set by the trap_exit attribute for functions.   It specifies
-   a trap number to be used in a trapa instruction at function exit
-   (instead of an rte instruction).  */
-int trap_exit;
-
-/* This is used by the sp_switch attribute for functions.  It specifies
-   a variable holding the address of the stack the interrupt function
-   should switch to/from at entry/exit.  */
-rtx sp_switch;
-
-/* This is set by #pragma trapa, and is similar to the above, except that
-   the compiler doesn't emit code to preserve all registers.  */
-static int pragma_trapa;
-
-/* This is set by #pragma nosave_low_regs.  This is useful on the SH3,
-   which has a separate set of low regs for User and Supervisor modes.
-   This should only be used for the lowest level of interrupts.  Higher levels
-   of interrupts must save the registers in case they themselves are
-   interrupted.  */
-int pragma_nosave_low_regs;
-
-/* This is used for communication between TARGET_SETUP_INCOMING_VARARGS and
-   sh_expand_prologue.  */
-int current_function_anonymous_args;
+tree sh_deferred_function_attributes;
+tree *sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
 
 /* Global variables for machine-dependent things.  */
 
@@ -113,6 +89,9 @@ static short *regmode_weight[2];
 /* Total SFmode and SImode weights of scheduled insns.  */
 static int curr_regmode_pressure[2];
 
+/* Number of r0 life regions.  */
+static int r0_life_regions;
+
 /* If true, skip cycles for Q -> R movement.  */
 static int skip_cycles = 0;
 
@@ -169,7 +148,7 @@ enum reg_class regno_reg_class[FIRST_PSEUDO_REGISTER] =
   DF_REGS, DF_REGS, DF_REGS, DF_REGS,
   NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
   MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
-  GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS,
 };
 
 char sh_register_names[FIRST_PSEUDO_REGISTER] \
@@ -179,25 +158,11 @@ char sh_additional_register_names[ADDREGNAMES_SIZE] \
   [MAX_ADDITIONAL_REGISTER_NAME_LENGTH + 1]
   = SH_ADDITIONAL_REGISTER_NAMES_INITIALIZER;
 
-/* Provide reg_class from a letter such as appears in the machine
-   description.  *: target independently reserved letter.
-   reg_class_from_letter['e' - 'a'] is set to NO_REGS for TARGET_FMOVD.  */
-
-enum reg_class reg_class_from_letter[] =
-{
-  /* a */ ALL_REGS,  /* b */ TARGET_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS,
-  /* e */ FP_REGS,   /* f */ FP_REGS,  /* g **/ NO_REGS,     /* h */ NO_REGS,
-  /* i **/ NO_REGS,  /* j */ NO_REGS,  /* k */ SIBCALL_REGS, /* l */ PR_REGS,
-  /* m **/ NO_REGS,  /* n **/ NO_REGS, /* o **/ NO_REGS,     /* p **/ NO_REGS,
-  /* q */ NO_REGS,   /* r **/ NO_REGS, /* s **/ NO_REGS,     /* t */ T_REGS,
-  /* u */ NO_REGS,   /* v */ NO_REGS,  /* w */ FP0_REGS,     /* x */ MAC_REGS,
-  /* y */ FPUL_REGS, /* z */ R0_REGS
-};
-
 int assembler_dialect;
 
 static bool shmedia_space_reserved_for_target_registers;
 
+static bool sh_handle_option (size_t, const char *, int);
 static void split_branches (rtx);
 static int branch_dest (rtx);
 static void force_into (rtx, rtx);
@@ -217,7 +182,6 @@ static rtx push (int);
 static void pop (int);
 static void push_regs (HARD_REG_SET *, int);
 static int calc_live_regs (HARD_REG_SET *);
-static void mark_use (rtx, rtx *);
 static HOST_WIDE_INT rounded_frame_size (int);
 static rtx mark_constant_pool_use (rtx);
 const struct attribute_spec sh_attribute_table[];
@@ -227,12 +191,14 @@ static tree sh_handle_trap_exit_attribute (tree *, tree, tree, int, bool *);
 static tree sh_handle_renesas_attribute (tree *, tree, tree, int, bool *);
 static void sh_output_function_epilogue (FILE *, HOST_WIDE_INT);
 static void sh_insert_attributes (tree, tree *);
+static const char *sh_check_pch_target_flags (int);
 static int sh_adjust_cost (rtx, rtx, rtx, int);
 static int sh_issue_rate (void);
 static int sh_dfa_new_cycle (FILE *, int, rtx, int, int, int *sort_p);
 static short find_set_regmode_weight (rtx, enum machine_mode);
 static short find_insn_regmode_weight (rtx, enum machine_mode);
-static void find_regmode_weight (int, enum machine_mode);
+static void find_regmode_weight (basic_block, enum machine_mode);
+static int find_r0_life_regions (basic_block);
 static void  sh_md_init_global (FILE *, int, int);
 static void  sh_md_finish_global (FILE *, int);
 static int rank_for_reorder (const void *, const void *);
@@ -266,6 +232,8 @@ static bool unspec_caller_rtx_p (rtx);
 static bool sh_cannot_copy_insn_p (rtx);
 static bool sh_rtx_costs (rtx, int, int, int *);
 static int sh_address_cost (rtx);
+static int sh_pr_n_sets (void);
+static rtx sh_allocate_initial_value (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 *);
@@ -286,6 +254,8 @@ static bool sh_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
                                  tree, bool);
 static bool sh_callee_copies (CUMULATIVE_ARGS *, enum machine_mode,
                              tree, bool);
+static int sh_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
+                                tree, bool);
 static int sh_dwarf_calling_convention (tree);
 
 \f
@@ -319,6 +289,11 @@ static int sh_dwarf_calling_convention (tree);
 #undef TARGET_ASM_FILE_START_FILE_DIRECTIVE
 #define TARGET_ASM_FILE_START_FILE_DIRECTIVE true
 
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS TARGET_DEFAULT
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION sh_handle_option
+
 #undef TARGET_INSERT_ATTRIBUTES
 #define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
 
@@ -409,6 +384,8 @@ static int sh_dwarf_calling_convention (tree);
 #define TARGET_RTX_COSTS sh_rtx_costs
 #undef TARGET_ADDRESS_COST
 #define TARGET_ADDRESS_COST sh_address_cost
+#undef TARGET_ALLOCATE_INITIAL_VALUE
+#define TARGET_ALLOCATE_INITIAL_VALUE sh_allocate_initial_value
 
 #undef TARGET_MACHINE_DEPENDENT_REORG
 #define TARGET_MACHINE_DEPENDENT_REORG sh_reorg
@@ -444,6 +421,8 @@ static int sh_dwarf_calling_convention (tree);
 #define TARGET_PASS_BY_REFERENCE sh_pass_by_reference
 #undef TARGET_CALLEE_COPIES
 #define TARGET_CALLEE_COPIES sh_callee_copies
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES sh_arg_partial_bytes
 
 #undef TARGET_BUILD_BUILTIN_VA_LIST
 #define TARGET_BUILD_BUILTIN_VA_LIST sh_build_builtin_va_list
@@ -453,8 +432,8 @@ static int sh_dwarf_calling_convention (tree);
 #undef TARGET_VECTOR_MODE_SUPPORTED_P
 #define TARGET_VECTOR_MODE_SUPPORTED_P sh_vector_mode_supported_p
 
-#undef TARGET_PCH_VALID_P
-#define TARGET_PCH_VALID_P sh_pch_valid_p
+#undef TARGET_CHECK_PCH_TARGET_FLAGS
+#define TARGET_CHECK_PCH_TARGET_FLAGS sh_check_pch_target_flags
 
 #undef TARGET_DWARF_CALLING_CONVENTION
 #define TARGET_DWARF_CALLING_CONVENTION sh_dwarf_calling_convention
@@ -476,8 +455,132 @@ static int sh_dwarf_calling_convention (tree);
 
 #endif /* SYMBIAN */
 
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD sh_secondary_reload
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+sh_handle_option (size_t code, const char *arg ATTRIBUTE_UNUSED,
+                 int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case OPT_m1:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH1;
+      return true;
+
+    case OPT_m2:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2;
+      return true;
+
+    case OPT_m2a:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A;
+      return true;
+
+    case OPT_m2a_nofpu:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_NOFPU;
+      return true;
+
+    case OPT_m2a_single:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE;
+      return true;
+
+    case OPT_m2a_single_only:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2A_SINGLE_ONLY;
+      return true;
+
+    case OPT_m2e:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH2E;
+      return true;
+
+    case OPT_m3:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3;
+      return true;
+
+    case OPT_m3e:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH3E;
+      return true;
+
+    case OPT_m4:
+    case OPT_m4_100:
+    case OPT_m4_200:
+    case OPT_m4_300:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4;
+      return true;
+
+    case OPT_m4_nofpu:
+    case OPT_m4_100_nofpu:
+    case OPT_m4_200_nofpu:
+    case OPT_m4_300_nofpu:
+    case OPT_m4_340:
+    case OPT_m4_400:
+    case OPT_m4_500:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_NOFPU;
+      return true;
+
+    case OPT_m4_single:
+    case OPT_m4_100_single:
+    case OPT_m4_200_single:
+    case OPT_m4_300_single:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE;
+      return true;
+
+    case OPT_m4_single_only:
+    case OPT_m4_100_single_only:
+    case OPT_m4_200_single_only:
+    case OPT_m4_300_single_only:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4_SINGLE_ONLY;
+      return true;
+
+    case OPT_m4a:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A;
+      return true;
+
+    case OPT_m4a_nofpu:
+    case OPT_m4al:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_NOFPU;
+      return true;
+
+    case OPT_m4a_single:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE;
+      return true;
+
+    case OPT_m4a_single_only:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH4A_SINGLE_ONLY;
+      return true;
+
+    case OPT_m5_32media:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA;
+      return true;
+
+    case OPT_m5_32media_nofpu:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_32MEDIA_NOFPU;
+      return true;
+
+    case OPT_m5_64media:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA;
+      return true;
+
+    case OPT_m5_64media_nofpu:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_64MEDIA_NOFPU;
+      return true;
+
+    case OPT_m5_compact:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT;
+      return true;
+
+    case OPT_m5_compact_nofpu:
+      target_flags = (target_flags & ~MASK_ARCH) | SELECT_SH5_COMPACT_NOFPU;
+      return true;
+
+    default:
+      return true;
+    }
+}
+\f
 /* Print the operand address in x to the stream.  */
 
 void
@@ -514,8 +617,7 @@ print_operand_address (FILE *stream, rtx x)
            }
 
          default:
-           debug_rtx (x);
-           abort ();
+           gcc_unreachable ();
          }
       }
       break;
@@ -543,22 +645,30 @@ print_operand_address (FILE *stream, rtx x)
    '@'  print trap, rte or rts depending upon pragma interruptness
    '#'  output a nop if there is nothing to put in the delay slot
    '''  print likelihood suffix (/u for unlikely).
+   '>'  print branch target if -fverbose-asm
    '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
    'T'  print the next word of a dp value - same as 'R' in big endian mode.
-   'M'  print an `x' if `m' will print `base,index'.
+   'M'  SHMEDIA: print an `x' if `m' will print `base,index'.
+        otherwise: print .b / .w / .l / .s / .d suffix if operand is a MEM.
    'N'  print 'r63' if the operand is (const_int 0).
    'd'  print a V2SF reg as dN instead of fpN.
    'm'  print a pair `base,offset' or `base,index', for LD and ST.
+   'U'  Likewise for {LD,ST}{HI,LO}.
    'u'  prints the lowest 16 bits of CONST_INT, as an unsigned value.
    'o'  output an operator.  */
 
 void
 print_operand (FILE *stream, rtx x, int code)
 {
+  int regno;
+  enum machine_mode mode;
+
   switch (code)
     {
+      tree trapa_attr;
+
     case '.':
       if (final_sequence
          && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
@@ -569,8 +679,11 @@ print_operand (FILE *stream, rtx x, int code)
       fprintf (stream, "%s", LOCAL_LABEL_PREFIX);
       break;
     case '@':
-      if (trap_exit)
-       fprintf (stream, "trapa #%d", trap_exit);
+      trapa_attr = lookup_attribute ("trap_exit",
+                                     DECL_ATTRIBUTES (current_function_decl));
+      if (trapa_attr)
+       fprintf (stream, "trapa #%ld",
+                (long) TREE_INT_CST_LOW (TREE_VALUE (TREE_VALUE (trapa_attr))));
       else if (sh_cfun_interrupt_handler_p ())
        fprintf (stream, "rte");
       else
@@ -589,15 +702,77 @@ print_operand (FILE *stream, rtx x, int code)
          fputs ("/u", stream);
        break;
       }
+    case '>':
+      if (flag_verbose_asm && JUMP_LABEL (current_output_insn))
+       {
+         fputs ("\t! target: ", stream);
+         output_addr_const (stream, JUMP_LABEL (current_output_insn));
+       }
+      break;
     case 'O':
       x = mark_constant_pool_use (x);
       output_addr_const (stream, x);
       break;
+    /* N.B.: %R / %S / %T adjust memory addresses by four.
+       For SHMEDIA, that means they can be used to access the first and
+       second 32 bit part of a 64 bit (or larger) value that
+       might be held in floating point registers or memory.
+       While they can be used to access 64 bit parts of a larger value
+       held in general purpose registers, that won't work with memory -
+       neither for fp registers, since the frxx names are used.  */
     case 'R':
-      fputs (reg_names[REGNO (x) + LSW], (stream));
+      if (REG_P (x) || GET_CODE (x) == SUBREG)
+       {
+         regno = true_regnum (x);
+         regno += FP_REGISTER_P (regno) ? 1 : LSW;
+         fputs (reg_names[regno], (stream));
+       }
+      else if (MEM_P (x))
+       {
+         x = adjust_address (x, SImode, 4 * LSW);
+         print_operand_address (stream, XEXP (x, 0));
+       }
+      else
+       {
+         rtx sub = NULL_RTX;
+
+         mode = GET_MODE (x);
+         if (mode == VOIDmode)
+           mode = DImode;
+         if (GET_MODE_SIZE (mode) >= 8)
+           sub = simplify_subreg (SImode, x, mode, 4 * LSW);
+         if (sub)
+           print_operand (stream, sub, 0);
+         else
+           output_operand_lossage ("invalid operand to %%R");
+       }
       break;
     case 'S':
-      fputs (reg_names[REGNO (x) + MSW], (stream));
+      if (REG_P (x) || GET_CODE (x) == SUBREG)
+       {
+         regno = true_regnum (x);
+         regno += FP_REGISTER_P (regno) ? 0 : MSW;
+         fputs (reg_names[regno], (stream));
+       }
+      else if (MEM_P (x))
+       {
+         x = adjust_address (x, SImode, 4 * MSW);
+         print_operand_address (stream, XEXP (x, 0));
+       }
+      else
+       {
+         rtx sub = NULL_RTX;
+
+         mode = GET_MODE (x);
+         if (mode == VOIDmode)
+           mode = DImode;
+         if (GET_MODE_SIZE (mode) >= 8)
+           sub = simplify_subreg (SImode, x, mode, 4 * MSW);
+         if (sub)
+           print_operand (stream, sub, 0);
+         else
+           output_operand_lossage ("invalid operand to %%S");
+       }
       break;
     case 'T':
       /* Next word of a double.  */
@@ -634,17 +809,36 @@ print_operand (FILE *stream, rtx x, int code)
        }
       break;
     case 'M':
-      if (GET_CODE (x) == MEM
-         && GET_CODE (XEXP (x, 0)) == PLUS
-         && (GET_CODE (XEXP (XEXP (x, 0), 1)) == REG
-             || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG))
-       fputc ('x', stream);
+      if (TARGET_SHMEDIA)
+       {
+         if (GET_CODE (x) == MEM
+             && GET_CODE (XEXP (x, 0)) == PLUS
+             && (GET_CODE (XEXP (XEXP (x, 0), 1)) == REG
+                 || GET_CODE (XEXP (XEXP (x, 0), 1)) == SUBREG))
+           fputc ('x', stream);
+       }
+      else
+       {
+         if (GET_CODE (x) == MEM)
+           {
+             switch (GET_MODE (x))
+               {
+               case QImode: fputs (".b", stream); break;
+               case HImode: fputs (".w", stream); break;
+               case SImode: fputs (".l", stream); break;
+               case SFmode: fputs (".s", stream); break;
+               case DFmode: fputs (".d", stream); break;
+               default: gcc_unreachable ();
+               }
+           }
+       }
       break;
 
     case 'm':
-      if (GET_CODE (x) != MEM)
-       abort ();
+      gcc_assert (GET_CODE (x) == MEM);
       x = XEXP (x, 0);
+      /* Fall through.  */
+    case 'U':
       switch (GET_CODE (x))
        {
        case REG:
@@ -660,13 +854,12 @@ print_operand (FILE *stream, rtx x, int code)
          break;
 
        default:
-         abort ();
+         gcc_unreachable ();
        }
       break;
 
     case 'd':
-      if (GET_CODE (x) != REG || GET_MODE (x) != V2SFmode)
-       abort ();
+      gcc_assert (GET_CODE (x) == REG && GET_MODE (x) == V2SFmode);
 
       fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
       break;
@@ -688,36 +881,87 @@ print_operand (FILE *stream, rtx x, int code)
 
     default_output:
     default:
+      regno = 0;
+      mode = GET_MODE (x);
+
       switch (GET_CODE (x))
        {
+       case TRUNCATE:
+         {
+           rtx inner = XEXP (x, 0);
+           int offset = 0;
+           enum machine_mode inner_mode;
+
+           /* We might see SUBREGs with vector mode registers inside.  */
+           if (GET_CODE (inner) == SUBREG
+               && (GET_MODE_SIZE (GET_MODE (inner))
+                   == GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+               && subreg_lowpart_p (inner))
+             inner = SUBREG_REG (inner);
+           if (GET_CODE (inner) == CONST_INT)
+             {
+               x = GEN_INT (trunc_int_for_mode (INTVAL (inner), GET_MODE (x)));
+               goto default_output;
+             }
+           inner_mode = GET_MODE (inner);
+           if (GET_CODE (inner) == SUBREG
+               && (GET_MODE_SIZE (GET_MODE (inner))
+                   < GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+               && GET_CODE (SUBREG_REG (inner)) == REG)
+             {
+               offset = subreg_regno_offset (REGNO (SUBREG_REG (inner)),
+                                             GET_MODE (SUBREG_REG (inner)),
+                                             SUBREG_BYTE (inner),
+                                             GET_MODE (inner));
+               inner = SUBREG_REG (inner);
+             }
+           if (GET_CODE (inner) != REG || GET_MODE_SIZE (inner_mode) > 8)
+             abort ();
+           /* Floating point register pairs are always big endian;
+              general purpose registers are 64 bit wide.  */
+           regno = REGNO (inner);
+           regno = (HARD_REGNO_NREGS (regno, inner_mode)
+                    - HARD_REGNO_NREGS (regno, mode))
+                    + offset;
+           x = inner;
+           goto reg;
+         }
+       case SIGN_EXTEND:
+         x = XEXP (x, 0);
+         goto reg;
          /* FIXME: We need this on SHmedia32 because reload generates
             some sign-extended HI or QI loads into DImode registers
             but, because Pmode is SImode, the address ends up with a
             subreg:SI of the DImode register.  Maybe reload should be
             fixed so as to apply alter_subreg to such loads?  */
+       case IF_THEN_ELSE:
+         gcc_assert (trapping_target_operand (x, VOIDmode));
+         x = XEXP (XEXP (x, 2), 0);
+         goto default_output;
        case SUBREG:
-         if (SUBREG_BYTE (x) != 0
-             || GET_CODE (SUBREG_REG (x)) != REG)
-           abort ();
+         gcc_assert (SUBREG_BYTE (x) == 0
+                     && GET_CODE (SUBREG_REG (x)) == REG);
 
          x = SUBREG_REG (x);
          /* Fall through.  */
 
+       reg:
        case REG:
-         if (FP_REGISTER_P (REGNO (x))
-             && GET_MODE (x) == V16SFmode)
-           fprintf ((stream), "mtrx%s", reg_names[REGNO (x)] + 2);
+         regno += REGNO (x);
+         if (FP_REGISTER_P (regno)
+             && mode == V16SFmode)
+           fprintf ((stream), "mtrx%s", reg_names[regno] + 2);
          else if (FP_REGISTER_P (REGNO (x))
-                  && GET_MODE (x) == V4SFmode)
-           fprintf ((stream), "fv%s", reg_names[REGNO (x)] + 2);
+                  && mode == V4SFmode)
+           fprintf ((stream), "fv%s", reg_names[regno] + 2);
          else if (GET_CODE (x) == REG
-                  && GET_MODE (x) == V2SFmode)
-           fprintf ((stream), "fp%s", reg_names[REGNO (x)] + 2);
+                  && mode == V2SFmode)
+           fprintf ((stream), "fp%s", reg_names[regno] + 2);
          else if (FP_REGISTER_P (REGNO (x))
-                  && GET_MODE_SIZE (GET_MODE (x)) > 4)
-           fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
+                  && GET_MODE_SIZE (mode) > 4)
+           fprintf ((stream), "d%s", reg_names[regno] + 1);
          else
-           fputs (reg_names[REGNO (x)], (stream));
+           fputs (reg_names[regno], (stream));
          break;
 
        case MEM:
@@ -726,34 +970,38 @@ print_operand (FILE *stream, rtx x, int code)
 
        case CONST:
          if (TARGET_SHMEDIA
-             && GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
-             && GET_MODE (XEXP (x, 0)) == DImode
+             && (GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+                 || GET_CODE (XEXP (x, 0)) == ZERO_EXTEND)
+             && (GET_MODE (XEXP (x, 0)) == DImode
+                 || GET_MODE (XEXP (x, 0)) == SImode)
              && GET_CODE (XEXP (XEXP (x, 0), 0)) == TRUNCATE
              && GET_MODE (XEXP (XEXP (x, 0), 0)) == HImode)
            {
              rtx val = XEXP (XEXP (XEXP (x, 0), 0), 0);
+             rtx val2 = val;
+             bool nested_expr = false;
 
              fputc ('(', stream);
              if (GET_CODE (val) == ASHIFTRT)
                {
                  fputc ('(', stream);
-                 if (GET_CODE (XEXP (val, 0)) == CONST)
-                   fputc ('(', stream);
-                 output_addr_const (stream, XEXP (val, 0));
-                 if (GET_CODE (XEXP (val, 0)) == CONST)
-                   fputc (')', stream);
+                 val2 = XEXP (val, 0);
+               }
+             if (GET_CODE (val2) == CONST
+                 || GET_RTX_CLASS (GET_CODE (val2)) != RTX_OBJ)
+               {
+                 fputc ('(', stream);
+                 nested_expr = true;
+               }
+             output_addr_const (stream, val2);
+             if (nested_expr)
+               fputc (')', stream);
+             if (GET_CODE (val) == ASHIFTRT)
+               {
                  fputs (" >> ", stream);
                  output_addr_const (stream, XEXP (val, 1));
                  fputc (')', stream);
                }
-             else
-               {
-                 if (GET_CODE (val) == CONST)
-                   fputc ('(', stream);
-                 output_addr_const (stream, val);
-                 if (GET_CODE (val) == CONST)
-                   fputc (')', stream);
-               }
              fputs (" & 65535)", stream);
              break;
            }
@@ -814,8 +1062,10 @@ expand_block_move (rtx *operands)
       while (copied + 4 <= bytes)
        {
          rtx to = adjust_address (dest, SImode, copied);
-         rtx from = adjust_automodify_address (src, SImode, src_addr, copied);
+         rtx from = adjust_automodify_address (src, BLKmode,
+                                               src_addr, copied);
 
+         set_mem_size (from, GEN_INT (4));
          emit_insn (gen_movua (temp, from));
          emit_move_insn (src_addr, plus_constant (src_addr, 4));
          emit_move_insn (to, temp);
@@ -842,16 +1092,11 @@ expand_block_move (rtx *operands)
        return 0;
       else if (bytes == 12)
        {
-         tree entry_name;
-         rtx sym;
-         rtx func_addr_rtx;
+         rtx func_addr_rtx = gen_reg_rtx (Pmode);
          rtx r4 = gen_rtx_REG (SImode, 4);
          rtx r5 = gen_rtx_REG (SImode, 5);
 
-         entry_name = get_identifier ("__movmemSI12_i4");
-
-         sym = function_symbol (IDENTIFIER_POINTER (entry_name));
-         func_addr_rtx = copy_to_mode_reg (Pmode, sym);
+         function_symbol (func_addr_rtx, "__movmemSI12_i4", SFUNC_STATIC);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[1], 0), r5);
          emit_insn (gen_block_move_real_i4 (func_addr_rtx));
@@ -859,19 +1104,15 @@ expand_block_move (rtx *operands)
        }
       else if (! TARGET_SMALLCODE)
        {
-         tree entry_name;
-         rtx sym;
-         rtx func_addr_rtx;
+         const char *entry_name;
+         rtx func_addr_rtx = gen_reg_rtx (Pmode);
          int dwords;
          rtx r4 = gen_rtx_REG (SImode, 4);
          rtx r5 = gen_rtx_REG (SImode, 5);
          rtx r6 = gen_rtx_REG (SImode, 6);
 
-         entry_name = get_identifier (bytes & 4
-                                      ? "__movmem_i4_odd"
-                                      : "__movmem_i4_even");
-         sym = function_symbol (IDENTIFIER_POINTER (entry_name));
-         func_addr_rtx = copy_to_mode_reg (Pmode, sym);
+         entry_name = (bytes & 4 ? "__movmem_i4_odd" : "__movmem_i4_even");
+         function_symbol (func_addr_rtx, entry_name, SFUNC_STATIC);
          force_into (XEXP (operands[0], 0), r4);
          force_into (XEXP (operands[1], 0), r5);
 
@@ -886,16 +1127,12 @@ expand_block_move (rtx *operands)
   if (bytes < 64)
     {
       char entry[30];
-      tree entry_name;
-      rtx sym;
-      rtx func_addr_rtx;
+      rtx func_addr_rtx = gen_reg_rtx (Pmode);
       rtx r4 = gen_rtx_REG (SImode, 4);
       rtx r5 = gen_rtx_REG (SImode, 5);
 
       sprintf (entry, "__movmemSI%d", bytes);
-      entry_name = get_identifier (entry);
-      sym = function_symbol (IDENTIFIER_POINTER (entry_name));
-      func_addr_rtx = copy_to_mode_reg (Pmode, sym);
+      function_symbol (func_addr_rtx, entry, SFUNC_STATIC);
       force_into (XEXP (operands[0], 0), r4);
       force_into (XEXP (operands[1], 0), r5);
       emit_insn (gen_block_move_real (func_addr_rtx));
@@ -906,17 +1143,13 @@ expand_block_move (rtx *operands)
      less common function name, so this will occasionally use more space.  */
   if (! TARGET_SMALLCODE)
     {
-      tree entry_name;
-      rtx sym;
-      rtx func_addr_rtx;
+      rtx func_addr_rtx = gen_reg_rtx (Pmode);
       int final_switch, while_loop;
       rtx r4 = gen_rtx_REG (SImode, 4);
       rtx r5 = gen_rtx_REG (SImode, 5);
       rtx r6 = gen_rtx_REG (SImode, 6);
 
-      entry_name = get_identifier ("__movmem");
-      sym = function_symbol (IDENTIFIER_POINTER (entry_name));
-      func_addr_rtx = copy_to_mode_reg (Pmode, sym);
+      function_symbol (func_addr_rtx, "__movmem", SFUNC_STATIC);
       force_into (XEXP (operands[0], 0), r4);
       force_into (XEXP (operands[1], 0), r5);
 
@@ -958,7 +1191,9 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
            /* It's ok.  */;
          else
            {
-             temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+             temp = (!can_create_pseudo_p ()
+                     ? operands[0]
+                     : gen_reg_rtx (Pmode));
              operands[1] = legitimize_pic_address (operands[1], mode, temp);
            }
        }
@@ -966,13 +1201,14 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
               && GET_CODE (XEXP (operands[1], 0)) == PLUS
               && SYMBOLIC_CONST_P (XEXP (XEXP (operands[1], 0), 0)))
        {
-         temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+         temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
          temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
                                         mode, temp);
          operands[1] = expand_binop (mode, add_optab, temp,
                                      XEXP (XEXP (operands[1], 0), 1),
-                                     no_new_pseudos ? temp
-                                     : gen_reg_rtx (Pmode),
+                                     (!can_create_pseudo_p ()
+                                      ? temp
+                                      : gen_reg_rtx (Pmode)),
                                      0, OPTAB_LIB_WIDEN);
        }
     }
@@ -997,7 +1233,8 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
         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 (refers_to_regno_p (R0_REG, R0_REG + 1, operands[1], (rtx *)0)
+      else if (TARGET_SH1
+              && 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)
@@ -1006,11 +1243,21 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
 
   if (mode == Pmode || mode == ptr_mode)
     {
-      rtx op0, op1;
+      rtx op0, op1, opc;
       enum tls_model tls_kind;
 
       op0 = operands[0];
       op1 = operands[1];
+      if (GET_CODE (op1) == CONST
+         && GET_CODE (XEXP (op1, 0)) == PLUS
+         && tls_symbolic_operand (XEXP (XEXP (op1, 0), 0), Pmode))
+       {
+         opc = XEXP (XEXP (op1, 0), 1);
+         op1 = XEXP (XEXP (op1, 0), 0);
+       }
+      else
+       opc = NULL_RTX;
+
       if ((tls_kind = tls_symbolic_operand (op1, Pmode)))
        {
          rtx tga_op1, tga_ret, tmp, tmp2;
@@ -1053,7 +1300,7 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
                  if (flag_schedule_insns)
                    emit_insn (gen_blockage ());
                }
-             tga_op1 = no_new_pseudos ? op0 : gen_reg_rtx (Pmode);
+             tga_op1 = !can_create_pseudo_p () ? op0 : gen_reg_rtx (Pmode);
              tmp = gen_sym2GOTTPOFF (op1);
              emit_insn (gen_tls_initial_exec (tga_op1, tmp));
              op1 = tga_op1;
@@ -1074,8 +1321,10 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
              break;
 
            default:
-             abort ();
+             gcc_unreachable ();
            }
+         if (opc)
+           emit_insn (gen_addsi3 (op1, op1, force_reg (SImode, opc)));
          operands[1] = op1;
        }
     }
@@ -1083,6 +1332,301 @@ prepare_move_operands (rtx operands[], enum machine_mode mode)
   return 0;
 }
 
+enum rtx_code
+prepare_cbranch_operands (rtx *operands, enum machine_mode mode,
+                         enum rtx_code comparison)
+{
+  rtx op1;
+  rtx scratch = NULL_RTX;
+
+  if (comparison == CODE_FOR_nothing)
+    comparison = GET_CODE (operands[0]);
+  else
+    scratch = operands[4];
+  if (GET_CODE (operands[1]) == CONST_INT
+      && GET_CODE (operands[2]) != CONST_INT)
+    {
+      rtx tmp = operands[1];
+
+      operands[1] = operands[2];
+      operands[2] = tmp;
+      comparison = swap_condition (comparison);
+    }
+  if (GET_CODE (operands[2]) == CONST_INT)
+    {
+      HOST_WIDE_INT val = INTVAL (operands[2]);
+      if ((val == -1 || val == -0x81)
+         && (comparison == GT || comparison == LE))
+       {
+         comparison = (comparison == GT) ? GE : LT;
+         operands[2] = gen_int_mode (val + 1, mode);
+       }
+      else if ((val == 1 || val == 0x80)
+              && (comparison == GE || comparison == LT))
+       {
+         comparison = (comparison == GE) ? GT : LE;
+         operands[2] = gen_int_mode (val - 1, mode);
+       }
+      else if (val == 1 && (comparison == GEU || comparison == LTU))
+       {
+         comparison = (comparison == GEU) ? NE : EQ;
+         operands[2] = CONST0_RTX (mode);
+       }
+      else if (val == 0x80 && (comparison == GEU || comparison == LTU))
+       {
+         comparison = (comparison == GEU) ? GTU : LEU;
+         operands[2] = gen_int_mode (val - 1, mode);
+       }
+      else if (val == 0 && (comparison == GTU || comparison == LEU))
+       comparison = (comparison == GTU) ? NE : EQ;
+      else if (mode == SImode
+              && ((val == 0x7fffffff
+                   && (comparison == GTU || comparison == LEU))
+                  || ((unsigned HOST_WIDE_INT) val
+                       == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
+                      && (comparison == GEU || comparison == LTU))))
+       {
+         comparison = (comparison == GTU || comparison == GEU) ? LT : GE;
+         operands[2] = CONST0_RTX (mode);
+       }
+    }
+  op1 = operands[1];
+  if (can_create_pseudo_p ())
+    operands[1] = force_reg (mode, op1);
+  /* When we are handling DImode comparisons, we want to keep constants so
+     that we can optimize the component comparisons; however, memory loads
+     are better issued as a whole so that they can be scheduled well.
+     SImode equality comparisons allow I08 constants, but only when they
+     compare r0.  Hence, if operands[1] has to be loaded from somewhere else
+     into a register, that register might as well be r0, and we allow the
+     constant.  If it is already in a register, this is likely to be
+     allocated to a different hard register, thus we load the constant into
+     a register unless it is zero.  */
+  if (!REG_P (operands[2])
+      && (GET_CODE (operands[2]) != CONST_INT
+         || (mode == SImode && operands[2] != CONST0_RTX (SImode)
+             && ((comparison != EQ && comparison != NE)
+                 || (REG_P (op1) && REGNO (op1) != R0_REG)
+                 || !satisfies_constraint_I08 (operands[2])))))
+    {
+      if (scratch && GET_MODE (scratch) == mode)
+       {
+         emit_move_insn (scratch, operands[2]);
+         operands[2] = scratch;
+       }
+      else if (can_create_pseudo_p ())
+       operands[2] = force_reg (mode, operands[2]);
+    }
+  return comparison;
+}
+
+void
+expand_cbranchsi4 (rtx *operands, enum rtx_code comparison, int probability)
+{
+  rtx (*branch_expander) (rtx) = gen_branch_true;
+  rtx jump;
+
+  comparison = prepare_cbranch_operands (operands, SImode, comparison);
+  switch (comparison)
+    {
+    case NE: case LT: case LE: case LTU: case LEU:
+      comparison = reverse_condition (comparison);
+      branch_expander = gen_branch_false;
+    default: ;
+    }
+  emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, T_REG),
+                          gen_rtx_fmt_ee (comparison, SImode,
+                                          operands[1], operands[2])));
+  jump = emit_jump_insn (branch_expander (operands[3]));
+  if (probability >= 0)
+    REG_NOTES (jump)
+      = gen_rtx_EXPR_LIST (REG_BR_PROB, GEN_INT (probability),
+                           REG_NOTES (jump));
+
+}
+
+/* ??? How should we distribute probabilities when more than one branch
+   is generated.  So far we only have soem ad-hoc observations:
+   - If the operands are random, they are likely to differ in both parts.
+   - If comparing items in a hash chain, the operands are random or equal;
+     operation should be EQ or NE.
+   - If items are searched in an ordered tree from the root, we can expect
+     the highpart to be unequal about half of the time; operation should be
+     an inequality comparison, operands non-constant, and overall probability
+     about 50%.  Likewise for quicksort.
+   - Range checks will be often made against constants.  Even if we assume for
+     simplicity an even distribution of the non-constant operand over a
+     sub-range here, the same probability could be generated with differently
+     wide sub-ranges - as long as the ratio of the part of the subrange that
+     is before the threshold to the part that comes after the threshold stays
+     the same.  Thus, we can't really tell anything here;
+     assuming random distribution is at least simple.
+ */
+
+bool
+expand_cbranchdi4 (rtx *operands, enum rtx_code comparison)
+{
+  enum rtx_code msw_taken, msw_skip, lsw_taken;
+  rtx skip_label = NULL_RTX;
+  rtx op1h, op1l, op2h, op2l;
+  int num_branches;
+  int prob, rev_prob;
+  int msw_taken_prob = -1, msw_skip_prob = -1, lsw_taken_prob = -1;
+  rtx scratch = operands[4];
+
+  comparison = prepare_cbranch_operands (operands, DImode, comparison);
+  op1h = gen_highpart_mode (SImode, DImode, operands[1]);
+  op2h = gen_highpart_mode (SImode, DImode, operands[2]);
+  op1l = gen_lowpart (SImode, operands[1]);
+  op2l = gen_lowpart (SImode, operands[2]);
+  msw_taken = msw_skip = lsw_taken = CODE_FOR_nothing;
+  prob = split_branch_probability;
+  rev_prob = REG_BR_PROB_BASE - prob;
+  switch (comparison)
+    {
+    /* ??? Should we use the cmpeqdi_t pattern for equality comparisons?
+       That costs 1 cycle more when the first branch can be predicted taken,
+       but saves us mispredicts because only one branch needs prediction.
+       It also enables generating the cmpeqdi_t-1 pattern.  */
+    case EQ:
+      if (TARGET_CMPEQDI_T)
+       {
+         emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
+         emit_jump_insn (gen_branch_true (operands[3]));
+         return true;
+       }
+      msw_skip = NE;
+      lsw_taken = EQ;
+      if (prob >= 0)
+       {
+         /* If we had more precision, we'd use rev_prob - (rev_prob >> 32) .
+          */
+         msw_skip_prob = rev_prob;
+         if (REG_BR_PROB_BASE <= 65535)
+           lsw_taken_prob = prob ? REG_BR_PROB_BASE : 0;
+         else
+           {
+             gcc_assert (HOST_BITS_PER_WIDEST_INT >= 64);
+             lsw_taken_prob
+               = (prob
+                  ? (REG_BR_PROB_BASE
+                     - ((HOST_WIDEST_INT) REG_BR_PROB_BASE * rev_prob
+                        / ((HOST_WIDEST_INT) prob << 32)))
+                  : 0);
+           }
+       }
+      break;
+    case NE:
+      if (TARGET_CMPEQDI_T)
+       {
+         emit_insn (gen_cmpeqdi_t (operands[1], operands[2]));
+         emit_jump_insn (gen_branch_false (operands[3]));
+         return true;
+       }
+      msw_taken = NE;
+      msw_taken_prob = prob;
+      lsw_taken = NE;
+      lsw_taken_prob = 0;
+      break;
+    case GTU: case GT:
+      msw_taken = comparison;
+      if (GET_CODE (op2l) == CONST_INT && INTVAL (op2l) == -1)
+       break;
+      if (comparison != GTU || op2h != CONST0_RTX (SImode))
+       msw_skip = swap_condition (msw_taken);
+      lsw_taken = GTU;
+      break;
+    case GEU: case GE:
+      if (op2l == CONST0_RTX (SImode))
+       msw_taken = comparison;
+      else
+       {
+         msw_taken = comparison == GE ? GT : GTU;
+         msw_skip = swap_condition (msw_taken);
+         lsw_taken = GEU;
+       }
+      break;
+    case LTU: case LT:
+      msw_taken = comparison;
+      if (op2l == CONST0_RTX (SImode))
+       break;
+      msw_skip = swap_condition (msw_taken);
+      lsw_taken = LTU;
+      break;
+    case LEU: case LE:
+      if (GET_CODE (op2l) == CONST_INT && INTVAL (op2l) == -1)
+       msw_taken = comparison;
+      else
+       {
+         lsw_taken = LEU;
+         if (comparison == LE)
+           msw_taken = LT;
+         else if (op2h != CONST0_RTX (SImode))
+           msw_taken = LTU;
+         else
+           break;
+         msw_skip = swap_condition (msw_taken);
+       }
+      break;
+    default: return false;
+    }
+  num_branches = ((msw_taken != CODE_FOR_nothing)
+                 + (msw_skip != CODE_FOR_nothing)
+                 + (lsw_taken != CODE_FOR_nothing));
+  if (comparison != EQ && comparison != NE && num_branches > 1)
+    {
+      if (!CONSTANT_P (operands[2])
+         && prob >= (int) (REG_BR_PROB_BASE * 3 / 8U)
+         && prob <= (int) (REG_BR_PROB_BASE * 5 / 8U))
+       {
+         msw_taken_prob = prob / 2U;
+         msw_skip_prob
+           = REG_BR_PROB_BASE * rev_prob / (REG_BR_PROB_BASE + rev_prob);
+         lsw_taken_prob = prob;
+       }
+      else
+       {
+         msw_taken_prob = prob;
+         msw_skip_prob = REG_BR_PROB_BASE;
+         /* ??? If we have a constant op2h, should we use that when
+            calculating lsw_taken_prob?  */
+         lsw_taken_prob = prob;
+       }
+    }
+  operands[1] = op1h;
+  operands[2] = op2h;
+  operands[4] = NULL_RTX;
+  if (reload_completed
+      && ! arith_reg_or_0_operand (op2h, SImode) && true_regnum (op1h)
+      && (msw_taken != CODE_FOR_nothing || msw_skip != CODE_FOR_nothing))
+    {
+      emit_move_insn (scratch, operands[2]);
+      operands[2] = scratch;
+    }
+  if (msw_taken != CODE_FOR_nothing)
+    expand_cbranchsi4 (operands, msw_taken, msw_taken_prob);
+  if (msw_skip != CODE_FOR_nothing)
+    {
+      rtx taken_label = operands[3];
+
+      operands[3] = skip_label = gen_label_rtx ();
+      expand_cbranchsi4 (operands, msw_skip, msw_skip_prob);
+      operands[3] = taken_label;
+    }
+  operands[1] = op1l;
+  operands[2] = op2l;
+  if (lsw_taken != CODE_FOR_nothing)
+    {
+      if (reload_completed
+         && ! arith_reg_or_0_operand (op2l, SImode) && true_regnum (op1l))
+       operands[4] = scratch;
+      expand_cbranchsi4 (operands, lsw_taken, lsw_taken_prob);
+    }
+  if (msw_skip != CODE_FOR_nothing)
+    emit_label (skip_label);
+  return true;
+}
+
 /* Prepare the operands for an scc instruction; make sure that the
    compare has been done.  */
 rtx
@@ -1097,7 +1641,7 @@ prepare_scc_operands (enum rtx_code code)
     {
     case NE:
       /* It isn't possible to handle this case.  */
-      abort ();
+      gcc_unreachable ();
     case LT:
       code = GT;
       break;
@@ -1235,12 +1779,17 @@ output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
       int dreg = REGNO (dst);
       rtx inside = XEXP (src, 0);
 
-      if (GET_CODE (inside) == REG)
-       ptrreg = REGNO (inside);
-      else if (GET_CODE (inside) == SUBREG)
-       ptrreg = subreg_regno (inside);
-      else if (GET_CODE (inside) == PLUS)
+      switch (GET_CODE (inside))
        {
+       case REG:
+         ptrreg = REGNO (inside);
+         break;
+
+       case SUBREG:
+         ptrreg = subreg_regno (inside);
+         break;
+
+       case PLUS:
          ptrreg = REGNO (XEXP (inside, 0));
          /* ??? A r0+REG address shouldn't be possible here, because it isn't
             an offsettable address.  Unfortunately, offsettable addresses use
@@ -1249,15 +1798,16 @@ output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
             supported, so we can't use the 'o' constraint.
             Thus we must check for and handle r0+REG addresses here.
             We punt for now, since this is likely very rare.  */
-         if (GET_CODE (XEXP (inside, 1)) == REG)
-           abort ();
+         gcc_assert (GET_CODE (XEXP (inside, 1)) != REG);
+         break;
+         
+       case LABEL_REF:
+         return "mov.l %1,%0\n\tmov.l  %1+4,%T0";
+       case POST_INC:
+         return "mov.l %1,%0\n\tmov.l  %1,%T0";
+       default:
+         gcc_unreachable ();
        }
-      else if (GET_CODE (inside) == LABEL_REF)
-       return "mov.l   %1,%0\n\tmov.l  %1+4,%T0";
-      else if (GET_CODE (inside) == POST_INC)
-       return "mov.l   %1,%0\n\tmov.l  %1,%T0";
-      else
-       abort ();
 
       /* Work out the safe way to copy.  Copy into the second half first.  */
       if (dreg == ptrreg)
@@ -1274,7 +1824,7 @@ output_movedouble (rtx insn ATTRIBUTE_UNUSED, rtx operands[],
 static void
 print_slot (rtx insn)
 {
-  final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 0, 1, NULL);
+  final_scan_insn (XVECEXP (insn, 0, 1), asm_out_file, optimize, 1, NULL);
 
   INSN_DELETED_P (XVECEXP (insn, 0, 1)) = 1;
 }
@@ -1426,9 +1976,9 @@ output_branch (int logic, rtx insn, rtx *operands)
        {
          int label = lf++;
 
-         if (final_sequence
-             && INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))
-           abort ();
+         gcc_assert (!final_sequence
+                     || !(INSN_ANNULLED_BRANCH_P
+                          (XVECEXP (final_sequence, 0, 0))));
          asm_fprintf (asm_out_file, "b%s%ss\t%LLF%d\n",
                       logic ? "f" : "t",
                       ASSEMBLER_DIALECT ? "/" : ".", label);
@@ -1455,10 +2005,16 @@ output_branch (int logic, rtx insn, rtx *operands)
       /* There should be no longer branches now - that would
         indicate that something has destroyed the branches set
         up in machine_dependent_reorg.  */
-      abort ();
+      gcc_unreachable ();
     }
 }
 
+/* Output a code sequence for INSN using TEMPLATE with OPERANDS; but before,
+   fill in operands 9 as a label to the successor insn.
+   We try to use jump threading where possible.
+   IF CODE matches the comparison in the IF_THEN_ELSE of a following jump,
+   we assume the jump is taken.  I.e. EQ means follow jmp and bf, NE means
+   follow jmp and bt, if the address is in range.  */
 const char *
 output_branchy_insn (enum rtx_code code, const char *template,
                     rtx insn, rtx *operands)
@@ -1503,7 +2059,8 @@ output_branchy_insn (enum rtx_code code, const char *template,
 const char *
 output_ieee_ccmpeq (rtx insn, rtx *operands)
 {
-  return output_branchy_insn (NE, "bt\t%l9\\;fcmp/eq\t%1,%0", insn, operands);
+  return output_branchy_insn (NE, "bt\t%l9\n\tfcmp/eq\t%1,%0",
+                             insn, operands);
 }
 \f
 /* Output the start of the assembler file.  */
@@ -1531,7 +2088,7 @@ sh_file_start (void)
   else
     /* Switch to the data section so that the coffsem symbol
        isn't in the text section.  */
-    data_section ();
+    switch_to_section (data_section);
 
   if (TARGET_LITTLE_ENDIAN)
     fputs ("\t.little\n", asm_out_file);
@@ -1571,7 +2128,7 @@ unspec_caller_rtx_p (rtx pat)
 }
 
 /* Indicate that INSN cannot be duplicated.  This is true for insn
-   that generates an unique label.  */
+   that generates a unique label.  */
 
 static bool
 sh_cannot_copy_insn_p (rtx insn)
@@ -1659,7 +2216,7 @@ shift_insns_rtx (rtx insn)
     case ASHIFT:
       return shift_insns[shift_count];
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -1681,7 +2238,7 @@ shiftcosts (rtx x)
        return 2;
 
       /* Everything else is invalid, because there is no pattern for it.  */
-      return 10000;
+      return MAX_COST;
     }
   /* If shift by a non constant, then this will be expensive.  */
   if (GET_CODE (XEXP (x, 1)) != CONST_INT)
@@ -1717,12 +2274,11 @@ andcosts (rtx x)
 
   if (TARGET_SHMEDIA)
     {
-      if ((GET_CODE (XEXP (x, 1)) == CONST_INT
-          && CONST_OK_FOR_I16 (INTVAL (XEXP (x, 1))))
-         || EXTRA_CONSTRAINT_C16 (XEXP (x, 1)))
+      if (satisfies_constraint_I10 (XEXP (x, 1))
+         || satisfies_constraint_J16 (XEXP (x, 1)))
        return 1;
       else
-       return 2;
+       return 1 + rtx_cost (XEXP (x, 1), AND);
     }
 
   /* These constants are single cycle extu.[bw] instructions.  */
@@ -1786,8 +2342,17 @@ addsubcosts (rtx x)
 static inline int
 multcosts (rtx x ATTRIBUTE_UNUSED)
 {
+  if (sh_multcost >= 0)
+    return sh_multcost;
   if (TARGET_SHMEDIA)
-    return 3;
+    /* ??? We have a mul insn, but it has a latency of three, and doesn't
+       accept constants.  Ideally, we would use a cost of one or two and
+       add the cost of the operand, but disregard the latter when inside loops
+       and loop invariant code motion is still to follow.
+       Using a multiply first and splitting it later if it's a loss
+       doesn't work because of different sign / zero extension semantics
+       of multiplies vs. shifts.  */
+    return TARGET_SMALLCODE ? 2 : 3;
 
   if (TARGET_SH2)
     {
@@ -1831,11 +2396,11 @@ sh_rtx_costs (rtx x, int code, int outer_code, int *total)
          else if (CONST_OK_FOR_I16 (INTVAL (x)))
             *total = COSTS_N_INSNS (outer_code != SET);
          else if (CONST_OK_FOR_I16 (INTVAL (x) >> 16))
-           *total = COSTS_N_INSNS (2);
+           *total = COSTS_N_INSNS ((outer_code != SET) + 1);
          else if (CONST_OK_FOR_I16 ((INTVAL (x) >> 16) >> 16))
-           *total = COSTS_N_INSNS (3);
+           *total = COSTS_N_INSNS ((outer_code != SET) + 2);
           else
-           *total = COSTS_N_INSNS (4);
+           *total = COSTS_N_INSNS ((outer_code != SET) + 3);
          return true;
         }
       if (CONST_OK_FOR_I08 (INTVAL (x)))
@@ -1843,6 +2408,15 @@ sh_rtx_costs (rtx x, int code, int outer_code, int *total)
       else if ((outer_code == AND || outer_code == IOR || outer_code == XOR)
               && CONST_OK_FOR_K08 (INTVAL (x)))
         *total = 1;
+      /* prepare_cmp_insn will force costly constants int registers before
+        the cbrach[sd]i4 patterns can see them, so preserve potentially
+        interesting ones not covered by I08 above.  */
+      else if (outer_code == COMPARE
+              && ((unsigned HOST_WIDE_INT) INTVAL (x)
+                   == (unsigned HOST_WIDE_INT) 0x7fffffff + 1
+                   || INTVAL (x) == 0x7fffffff
+                  || INTVAL (x) == 0x80 || INTVAL (x) == -0x81))
+        *total = 1;
       else
         *total = 8;
       return true;
@@ -1861,11 +2435,27 @@ sh_rtx_costs (rtx x, int code, int outer_code, int *total)
     case CONST_DOUBLE:
       if (TARGET_SHMEDIA)
         *total = COSTS_N_INSNS (4);
+      /* prepare_cmp_insn will force costly constants int registers before
+        the cbrachdi4 pattern can see them, so preserve potentially
+        interesting ones.  */
+      else if (outer_code == COMPARE && GET_MODE (x) == DImode)
+        *total = 1;
       else
         *total = 10;
       return true;
+    case CONST_VECTOR:
+      if (x == CONST0_RTX (GET_MODE (x)))
+       *total = 0;
+      else if (sh_1el_vec (x, VOIDmode))
+       *total = outer_code != SET;
+      if (sh_rep_vec (x, VOIDmode))
+       *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
+                 + (outer_code != SET));
+      *total = COSTS_N_INSNS (3) + (outer_code != SET);
+      return true;
 
     case PLUS:
+    case MINUS:
       *total = COSTS_N_INSNS (addsubcosts (x));
       return true;
 
@@ -1890,6 +2480,15 @@ sh_rtx_costs (rtx x, int code, int outer_code, int *total)
       *total = COSTS_N_INSNS (20);
       return true;
 
+    case PARALLEL:
+      if (sh_1el_vec (x, VOIDmode))
+       *total = outer_code != SET;
+      if (sh_rep_vec (x, VOIDmode))
+       *total = ((GET_MODE_UNIT_SIZE (GET_MODE (x)) + 3) / 4
+                 + (outer_code != SET));
+      *total = COSTS_N_INSNS (3) + (outer_code != SET);
+      return true;
+
     case FLOAT:
     case FIX:
       *total = 100;
@@ -2018,10 +2617,10 @@ gen_shifty_op (int code, rtx *operands)
     }
   else if (value == 0)
     {
-      /* This can happen when not optimizing.  We must output something here
-        to prevent the compiler from aborting in final.c after the try_split
-        call.  */
-      emit_insn (gen_nop ());
+      /* This can happen even when optimizing, if there were subregs before
+        reload.  Don't output a nop here, as this is never optimized away;
+        use a no-op move instead.  */
+      emit_insn (gen_rtx_SET (VOIDmode, operands[0], operands[0]));
       return;
     }
 
@@ -2070,10 +2669,8 @@ gen_shifty_hi_op (int code, rtx *operands)
 int
 expand_ashiftrt (rtx *operands)
 {
-  rtx sym;
   rtx wrk;
   char func[18];
-  tree func_name;
   int value;
 
   if (TARGET_SH3)
@@ -2101,6 +2698,16 @@ expand_ashiftrt (rtx *operands)
 
   if (value == 31)
     {
+      /* If we are called from abs expansion, arrange things so that we
+        we can use a single MT instruction that doesn't clobber the source,
+        if LICM can hoist out the load of the constant zero.  */
+      if (currently_expanding_to_rtl)
+       {
+         emit_insn (gen_cmpgtsi_t (force_reg (SImode, CONST0_RTX (SImode)),
+                                   operands[1]));
+         emit_insn (gen_mov_neg_si_t (operands[0]));
+         return 1;
+       }
       emit_insn (gen_ashrsi2_31 (operands[0], operands[1]));
       return 1;
     }
@@ -2130,9 +2737,7 @@ expand_ashiftrt (rtx *operands)
   /* Load the value into an arg reg and call a helper.  */
   emit_move_insn (gen_rtx_REG (SImode, 4), operands[1]);
   sprintf (func, "__ashiftrt_r4_%d", value);
-  func_name = get_identifier (func);
-  sym = function_symbol (IDENTIFIER_POINTER (func_name));
-  emit_move_insn (wrk, sym);
+  function_symbol (wrk, func, SFUNC_STATIC);
   emit_insn (gen_ashrsi3_n (GEN_INT (value), wrk));
   emit_move_insn (operands[0], gen_rtx_REG (SImode, 4));
   return 1;
@@ -2385,8 +2990,7 @@ gen_shl_and (rtx dest, rtx left_rtx, rtx mask_rtx, rtx source)
 
          /* Cases 3 and 4 should be handled by this split
             only while combining  */
-         if (kind > 2)
-           abort ();
+         gcc_assert (kind <= 2);
          if (right)
            {
              emit_insn (gen_lshrsi3 (dest, source, GEN_INT (right)));
@@ -2453,8 +3057,7 @@ shl_sext_kind (rtx left_rtx, rtx size_rtx, int *costp)
   left = INTVAL (left_rtx);
   size = INTVAL (size_rtx);
   insize = size - left;
-  if (insize <= 0)
-    abort ();
+  gcc_assert (insize > 0);
   /* Default to left / right shift.  */
   kind = 0;
   best_cost = shift_insns[32 - insize] + ashiftrt_insns[32 - size];
@@ -2676,22 +3279,37 @@ gen_shl_sext (rtx dest, rtx left_rtx, rtx size_rtx, rtx source)
 rtx
 gen_datalabel_ref (rtx sym)
 {
+  const char *str;
+
   if (GET_CODE (sym) == LABEL_REF)
     return gen_rtx_CONST (GET_MODE (sym),
                          gen_rtx_UNSPEC (GET_MODE (sym),
                                          gen_rtvec (1, sym),
                                          UNSPEC_DATALABEL));
 
-  if (GET_CODE (sym) != SYMBOL_REF)
-    abort ();
+  gcc_assert (GET_CODE (sym) == SYMBOL_REF);
+
+  str = XSTR (sym, 0);
+  /* Share all SYMBOL_REF strings with the same value - that is important
+     for cse.  */
+  str = IDENTIFIER_POINTER (get_identifier (str));
+  XSTR (sym, 0) = str;
 
   return sym;
 }
 
 \f
+static alloc_pool label_ref_list_pool;
+
+typedef struct label_ref_list_d
+{
+  rtx label;
+  struct label_ref_list_d *next;
+} *label_ref_list_t;
+
 /* The SH cannot load a large constant into a register, constants have to
    come from a pc relative load.  The reference of a pc relative load
-   instruction must be less than 1k infront of the instruction.  This
+   instruction must be less than 1k in front of the instruction.  This
    means that we often have to dump a constant inside a function, and
    generate code to branch around it.
 
@@ -2746,7 +3364,7 @@ typedef struct
 {
   rtx value;                   /* Value in table.  */
   rtx label;                   /* Label of value.  */
-  rtx wend;                    /* End of window.  */
+  label_ref_list_t wend;       /* End of window.  */
   enum machine_mode mode;      /* Mode of value.  */
 
   /* True if this constant is accessed as part of a post-increment
@@ -2755,15 +3373,17 @@ typedef struct
 } pool_node;
 
 /* The maximum number of constants that can fit into one pool, since
-   the pc relative range is 0...1020 bytes and constants are at least 4
-   bytes long.  */
+   constants in the range 0..510 are at least 2 bytes long, and in the
+   range from there to 1018 at least 4 bytes.  */
 
-#define MAX_POOL_SIZE (1020/4)
+#define MAX_POOL_SIZE 372
 static pool_node pool_vector[MAX_POOL_SIZE];
 static int pool_size;
 static rtx pool_window_label;
 static int pool_window_last;
 
+static int max_labelno_before_reorg;
+
 /* ??? If we need a constant in HImode which is the truncated value of a
    constant we need in SImode, we could combine the two entries thus saving
    two bytes.  Is this common enough to be worth the effort of implementing
@@ -2780,7 +3400,8 @@ static rtx
 add_constant (rtx x, enum machine_mode mode, rtx last_value)
 {
   int i;
-  rtx lab, new, ref, newref;
+  rtx lab, new;
+  label_ref_list_t ref, newref;
 
   /* First see if we've already got it.  */
   for (i = 0; i < pool_size; i++)
@@ -2806,9 +3427,10 @@ add_constant (rtx x, enum machine_mode mode, rtx last_value)
                }
              if (lab && pool_window_label)
                {
-                 newref = gen_rtx_LABEL_REF (VOIDmode, pool_window_label);
+                 newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
+                 newref->label = pool_window_label;
                  ref = pool_vector[pool_window_last].wend;
-                 LABEL_NEXTREF (newref) = ref;
+                 newref->next = ref;
                  pool_vector[pool_window_last].wend = newref;
                }
              if (new)
@@ -2830,13 +3452,14 @@ add_constant (rtx x, enum machine_mode mode, rtx last_value)
     lab = gen_label_rtx ();
   pool_vector[pool_size].mode = mode;
   pool_vector[pool_size].label = lab;
-  pool_vector[pool_size].wend = NULL_RTX;
+  pool_vector[pool_size].wend = NULL;
   pool_vector[pool_size].part_of_sequence_p = (lab == 0);
   if (lab && pool_window_label)
     {
-      newref = gen_rtx_LABEL_REF (VOIDmode, pool_window_label);
+      newref = (label_ref_list_t) pool_alloc (label_ref_list_pool);
+      newref->label = pool_window_label;
       ref = pool_vector[pool_window_last].wend;
-      LABEL_NEXTREF (newref) = ref;
+      newref->next = ref;
       pool_vector[pool_window_last].wend = newref;
     }
   if (lab)
@@ -2858,7 +3481,8 @@ dump_table (rtx start, rtx barrier)
   rtx scan = barrier;
   int i;
   int need_align = 1;
-  rtx lab, ref;
+  rtx lab;
+  label_ref_list_t ref;
   int have_df = 0;
 
   /* Do two passes, first time dump out the HI sized constants.  */
@@ -2878,9 +3502,9 @@ dump_table (rtx start, rtx barrier)
            scan = emit_label_after (lab, scan);
          scan = emit_insn_after (gen_consttable_2 (p->value, const0_rtx),
                                  scan);
-         for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+         for (ref = p->wend; ref; ref = ref->next)
            {
-             lab = XEXP (ref, 0);
+             lab = ref->label;
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
            }
        }
@@ -2928,9 +3552,9 @@ dump_table (rtx start, rtx barrier)
                    emit_label_before (lab, align_insn);
                  emit_insn_before (gen_consttable_4 (p->value, const0_rtx),
                                    align_insn);
-                 for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+                 for (ref = p->wend; ref; ref = ref->next)
                    {
-                     lab = XEXP (ref, 0);
+                     lab = ref->label;
                      emit_insn_before (gen_consttable_window_end (lab),
                                        align_insn);
                    }
@@ -2961,15 +3585,14 @@ dump_table (rtx start, rtx barrier)
                                      scan);
              break;
            default:
-             abort ();
-             break;
+             gcc_unreachable ();
            }
 
          if (p->mode != HImode)
            {
-             for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+             for (ref = p->wend; ref; ref = ref->next)
                {
-                 lab = XEXP (ref, 0);
+                 lab = ref->label;
                  scan = emit_insn_after (gen_consttable_window_end (lab),
                                          scan);
                }
@@ -3014,15 +3637,14 @@ dump_table (rtx start, rtx barrier)
                                  scan);
          break;
        default:
-         abort ();
-         break;
+         gcc_unreachable ();
        }
 
       if (p->mode != HImode)
        {
-         for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+         for (ref = p->wend; ref; ref = ref->next)
            {
-             lab = XEXP (ref, 0);
+             lab = ref->label;
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
            }
        }
@@ -3046,6 +3668,8 @@ hi_const (rtx src)
          && INTVAL (src) <= 32767);
 }
 
+#define MOVA_LABELREF(mova) XVECEXP (SET_SRC (PATTERN (mova)), 0, 0)
+
 /* 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
@@ -3061,7 +3685,7 @@ broken_move (rtx insn)
       if (GET_CODE (pat) == PARALLEL)
        pat = XVECEXP (pat, 0, 0);
       if (GET_CODE (pat) == SET
-         /* We can load any 8 bit value if we don't care what the high
+         /* We can load any 8-bit value if we don't care what the high
             order bits end up as.  */
          && GET_MODE (SET_DEST (pat)) != QImode
          && (CONSTANT_P (SET_SRC (pat))
@@ -3087,10 +3711,8 @@ broken_move (rtx insn)
                && FP_REGISTER_P (REGNO (SET_DEST (pat))))
          && ! (TARGET_SH2A
                && GET_MODE (SET_DEST (pat)) == SImode
-               && GET_CODE (SET_SRC (pat)) == CONST_INT
-               && CONST_OK_FOR_I20 (INTVAL (SET_SRC (pat))))
-         && (GET_CODE (SET_SRC (pat)) != CONST_INT
-             || ! CONST_OK_FOR_I08 (INTVAL (SET_SRC (pat)))))
+               && satisfies_constraint_I20 (SET_SRC (pat)))
+         && ! satisfies_constraint_I08 (SET_SRC (pat)))
        return 1;
     }
 
@@ -3105,16 +3727,17 @@ mova_p (rtx insn)
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
          && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_MOVA
          /* Don't match mova_const.  */
-         && GET_CODE (XVECEXP (SET_SRC (PATTERN (insn)), 0, 0)) == LABEL_REF);
+         && GET_CODE (MOVA_LABELREF (insn)) == LABEL_REF);
 }
 
 /* Fix up a mova from a switch that went out of range.  */
 static void
 fixup_mova (rtx mova)
 {
+  PUT_MODE (XEXP (MOVA_LABELREF (mova), 0), QImode);
   if (! flag_pic)
     {
-      SET_SRC (PATTERN (mova)) = XVECEXP (SET_SRC (PATTERN (mova)), 0, 0);
+      SET_SRC (PATTERN (mova)) = MOVA_LABELREF (mova);
       INSN_CODE (mova) = -1;
     }
   else
@@ -3126,11 +3749,11 @@ fixup_mova (rtx mova)
       do
        {
          worker = NEXT_INSN (worker);
-         if (! worker
-             || GET_CODE (worker) == CODE_LABEL
-             || GET_CODE (worker) == JUMP_INSN)
-           abort ();
-       } while (recog_memoized (worker) != CODE_FOR_casesi_worker_1);
+         gcc_assert (worker
+                     && GET_CODE (worker) != CODE_LABEL
+                     && GET_CODE (worker) != JUMP_INSN);
+       } while (GET_CODE (worker) == NOTE
+                || recog_memoized (worker) != CODE_FOR_casesi_worker_1);
       wpat = PATTERN (worker);
       wpat0 = XVECEXP (wpat, 0, 0);
       wpat1 = XVECEXP (wpat, 0, 1);
@@ -3148,20 +3771,67 @@ fixup_mova (rtx mova)
     }
 }
 
-/* Find the last barrier from insn FROM which is close enough to hold the
-   constant pool.  If we can't find one, then create one near the end of
-   the range.  */
-
-static rtx
-find_barrier (int num_mova, rtx mova, rtx from)
+/* NEW_MOVA is a mova we've just encountered while scanning forward.  Update
+   *num_mova, and check if the new mova is not nested within the first one.
+   return 0 if *first_mova was replaced, 1 if new_mova was replaced,
+   2 if new_mova has been assigned to *first_mova, -1 otherwise..  */
+static int
+untangle_mova (int *num_mova, rtx *first_mova, rtx new_mova)
 {
-  int count_si = 0;
-  int count_hi = 0;
-  int found_hi = 0;
-  int found_si = 0;
-  int found_di = 0;
-  int hi_align = 2;
-  int si_align = 2;
+  int n_addr = 0; /* Initialization to shut up spurious warning.  */
+  int f_target, n_target = 0; /* Likewise.  */
+
+  if (optimize)
+    {
+      n_addr = INSN_ADDRESSES (INSN_UID (new_mova));
+      n_target = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (new_mova), 0)));
+      if (n_addr > n_target || n_addr + 1022 < n_target)
+       {
+         /* Change the mova into a load.
+            broken_move will then return true for it.  */
+         fixup_mova (new_mova);
+         return 1;
+       }
+    }
+  if (!(*num_mova)++)
+    {
+      *first_mova = new_mova;
+      return 2;
+    }
+  if (!optimize
+      || ((f_target
+          = INSN_ADDRESSES (INSN_UID (XEXP (MOVA_LABELREF (*first_mova), 0))))
+         >= n_target))
+    return -1;
+
+  (*num_mova)--;
+  if (f_target - INSN_ADDRESSES (INSN_UID (*first_mova))
+      > n_target - n_addr)
+    {
+      fixup_mova (*first_mova);
+      return 0;
+    }
+  else
+    {
+      fixup_mova (new_mova);
+      return 1;
+    }
+}
+
+/* Find the last barrier from insn FROM which is close enough to hold the
+   constant pool.  If we can't find one, then create one near the end of
+   the range.  */
+
+static rtx
+find_barrier (int num_mova, rtx mova, rtx from)
+{
+  int count_si = 0;
+  int count_hi = 0;
+  int found_hi = 0;
+  int found_si = 0;
+  int found_di = 0;
+  int hi_align = 2;
+  int si_align = 2;
   int leading_mova = num_mova;
   rtx barrier_before_mova = 0, found_barrier = 0, good_barrier = 0;
   int si_limit;
@@ -3191,7 +3861,12 @@ find_barrier (int num_mova, rtx mova, rtx from)
       int inc = get_attr_length (from);
       int new_align = 1;
 
-      if (GET_CODE (from) == CODE_LABEL)
+      /* If this is a label that existed at the time of the compute_alignments
+        call, determine the alignment.  N.B.  When find_barrier recurses for
+        an out-of-reach mova, we might see labels at the start of previously
+        inserted constant tables.  */
+      if (GET_CODE (from) == CODE_LABEL
+         && CODE_LABEL_NUMBER (from) <= max_labelno_before_reorg)
        {
          if (optimize)
            new_align = 1 << label_to_alignment (from);
@@ -3201,6 +3876,22 @@ find_barrier (int num_mova, rtx mova, rtx from)
            new_align = 1;
          inc = 0;
        }
+      /* In case we are scanning a constant table because of recursion, check
+        for explicit alignments.  If the table is long, we might be forced
+        to emit the new table in front of it; the length of the alignment
+        might be the last straw.  */
+      else if (GET_CODE (from) == INSN
+              && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
+              && XINT (PATTERN (from), 1) == UNSPECV_ALIGN)
+       new_align = INTVAL (XVECEXP (PATTERN (from), 0, 0));
+      /* When we find the end of a constant table, paste the new constant
+        at the end.  That is better than putting it in front because
+        this way, we don't need extra alignment for adding a 4-byte-aligned
+        mov(a) label to a 2/4 or 8/4 byte aligned table.  */
+      else if (GET_CODE (from) == INSN
+              && GET_CODE (PATTERN (from)) == UNSPEC_VOLATILE
+              && XINT (PATTERN (from), 1) == UNSPECV_CONST_END)
+       return from;
 
       if (GET_CODE (from) == BARRIER)
        {
@@ -3261,20 +3952,20 @@ find_barrier (int num_mova, rtx mova, rtx from)
              if (num_mova)
                si_limit -= GET_MODE_SIZE (mode);
            }
-
-         /* See the code in machine_dependent_reorg, which has a similar if
-            statement that generates a new mova insn in many cases.  */
-         if (GET_CODE (dst) == REG && FP_ANY_REGISTER_P (REGNO (dst)))
-           inc += 2;
        }
 
       if (mova_p (from))
        {
-         if (! num_mova++)
+         switch (untangle_mova (&num_mova, &mova, from))
            {
-             leading_mova = 0;
-             mova = from;
-             barrier_before_mova = good_barrier ? good_barrier : found_barrier;
+             case 0:   return find_barrier (0, 0, mova);
+             case 2:
+               {
+                 leading_mova = 0;
+                 barrier_before_mova
+                   = good_barrier ? good_barrier : found_barrier;
+               }
+             default:  break;
            }
          if (found_si > count_si)
            count_si = found_si;
@@ -3283,7 +3974,10 @@ find_barrier (int num_mova, rtx mova, rtx from)
               && (GET_CODE (PATTERN (from)) == ADDR_VEC
                   || GET_CODE (PATTERN (from)) == ADDR_DIFF_VEC))
        {
-         if (num_mova)
+         if ((num_mova > 1 && GET_MODE (prev_nonnote_insn (from)) == VOIDmode)
+             || (num_mova
+                 && (prev_nonnote_insn (from)
+                     == XEXP (MOVA_LABELREF (mova), 0))))
            num_mova--;
          if (barrier_align (next_real_insn (from)) == align_jumps_log)
            {
@@ -3768,6 +4462,7 @@ gen_far_branch (struct far_branch *bp)
   rtx insn = bp->insert_place;
   rtx jump;
   rtx label = gen_label_rtx ();
+  int ok;
 
   emit_label_after (label, insn);
   if (bp->far_label)
@@ -3786,8 +4481,9 @@ gen_far_branch (struct far_branch *bp)
     emit_barrier_after (jump);
   emit_label_after (bp->near_label, insn);
   JUMP_LABEL (jump) = bp->far_label;
-  if (! invert_jump (insn, label, 1))
-    abort ();
+  ok = invert_jump (insn, label, 1);
+  gcc_assert (ok);
+  
   /* If we are branching around a jump (rather than a return), prevent
      reorg from using an insn from the jump target as the delay slot insn -
      when reorg did this, it pessimized code (we rather hide the delay slot)
@@ -4001,6 +4697,7 @@ sh_reorg (void)
   rtx r0_inc_rtx = gen_rtx_POST_INC (Pmode, r0_rtx);
 
   first = get_insns ();
+  max_labelno_before_reorg = max_label_num ();
 
   /* We must split call insns before introducing `mova's.  If we're
      optimizing, they'll have already been split.  Otherwise, make
@@ -4067,46 +4764,25 @@ sh_reorg (void)
          if (GET_CODE (reg) != REG)
            continue;
 
-         /* This is a function call via REG.  If the only uses of REG
-            between the time that it is set and the time that it dies
-            are in function calls, then we can associate all the
-            function calls with the setting of REG.  */
-
-         for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
-           {
-             if (REG_NOTE_KIND (link) != 0)
-               continue;
-             set = single_set (XEXP (link, 0));
-             if (set && rtx_equal_p (reg, SET_DEST (set)))
-               {
-                 link = XEXP (link, 0);
-                 break;
-               }
-           }
-
-         if (! link)
+         /* Try scanning backward to find where the register is set.  */
+         link = NULL;
+         for (scan = PREV_INSN (insn);
+              scan && GET_CODE (scan) != CODE_LABEL;
+              scan = PREV_INSN (scan))
            {
-             /* ??? Sometimes global register allocation will have
-                 deleted the insn pointed to by LOG_LINKS.  Try
-                 scanning backward to find where the register is set.  */
-             for (scan = PREV_INSN (insn);
-                  scan && GET_CODE (scan) != CODE_LABEL;
-                  scan = PREV_INSN (scan))
-               {
-                 if (! INSN_P (scan))
-                   continue;
+             if (! INSN_P (scan))
+               continue;
 
-                 if (! reg_mentioned_p (reg, scan))
-                   continue;
+             if (! reg_mentioned_p (reg, scan))
+               continue;
 
-                 if (noncall_uses_reg (reg, scan, &set))
-                   break;
+             if (noncall_uses_reg (reg, scan, &set))
+               break;
 
-                 if (set)
-                   {
-                     link = scan;
-                     break;
-                   }
+             if (set)
+               {
+                 link = scan;
+                 break;
                }
            }
 
@@ -4138,7 +4814,7 @@ sh_reorg (void)
 
              /* Don't try to trace forward past a CODE_LABEL if we haven't
                 seen INSN yet.  Ordinarily, we will only find the setting insn
-                in LOG_LINKS if it is in the same basic block.  However,
+                if it is in the same basic block.  However,
                 cross-jumping can insert code labels in between the load and
                 the call, and can result in situations where a single call
                 insn may have two targets depending on where we came from.  */
@@ -4185,11 +4861,8 @@ sh_reorg (void)
                 later insn.  */
 
              /* ??? We shouldn't have to use FOUNDINSN here.
-                However, the LOG_LINKS fields are apparently not
-                entirely reliable around libcalls;
-                newlib/libm/math/e_pow.c is a test case.  Sometimes
-                an insn will appear in LOG_LINKS even though it is
-                not the most recent insn which sets the register.  */
+                This dates back to when we used LOG_LINKS to find 
+                the most recent insn which sets the register.  */
 
              if (foundinsn
                  && (scanset
@@ -4247,9 +4920,12 @@ sh_reorg (void)
       mdep_reorg_phase = SH_SHORTEN_BRANCHES0;
       shorten_branches (first);
     }
+
   /* Scan the function looking for move instructions which have to be
      changed to pc-relative loads and insert the literal tables.  */
-
+  label_ref_list_pool = create_alloc_pool ("label references list",
+                                          sizeof (struct label_ref_list_d),
+                                          30);
   mdep_reorg_phase = SH_FIXUP_PCLOAD;
   for (insn = first, num_mova = 0; insn; insn = NEXT_INSN (insn))
     {
@@ -4259,21 +4935,23 @@ sh_reorg (void)
             below the switch table.  Check if that has happened.
             We only have the addresses available when optimizing; but then,
             this check shouldn't be needed when not optimizing.  */
-         rtx label_ref = XVECEXP (SET_SRC (PATTERN (insn)), 0, 0);
-         if (optimize
-             && (INSN_ADDRESSES (INSN_UID (insn))
-                 > INSN_ADDRESSES (INSN_UID (XEXP (label_ref, 0)))))
+         if (!untangle_mova (&num_mova, &mova, insn))
            {
-             /* Change the mova into a load.
-                broken_move will then return true for it.  */
-             fixup_mova (insn);
+             insn = mova;
+             num_mova = 0;
            }
-         else if (! num_mova++)
-           mova = insn;
        }
       else if (GET_CODE (insn) == JUMP_INSN
               && GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC
-              && num_mova)
+              && num_mova
+              /* ??? loop invariant motion can also move a mova out of a
+                 loop.  Since loop does this code motion anyway, maybe we
+                 should wrap UNSPEC_MOVA into a CONST, so that reload can
+                 move it back.  */
+              && ((num_mova > 1
+                   && GET_MODE (prev_nonnote_insn (insn)) == VOIDmode)
+                  || (prev_nonnote_insn (insn)
+                      == XEXP (MOVA_LABELREF (mova), 0))))
        {
          rtx scan;
          int total;
@@ -4361,9 +5039,8 @@ sh_reorg (void)
                                                - 1);
                      rtx clobber = *clobberp;
 
-                     if (GET_CODE (clobber) != CLOBBER
-                         || ! rtx_equal_p (XEXP (clobber, 0), r0_rtx))
-                       abort ();
+                     gcc_assert (GET_CODE (clobber) == CLOBBER
+                                 && rtx_equal_p (XEXP (clobber, 0), r0_rtx));
 
                      if (last_float
                          && reg_set_between_p (r0_rtx, last_float_move, scan))
@@ -4395,7 +5072,7 @@ sh_reorg (void)
                        }
                      last_float_move = scan;
                      last_float = src;
-                     newsrc = gen_rtx_MEM (mode,
+                     newsrc = gen_const_mem (mode,
                                        (((TARGET_SH4 && ! TARGET_FMOVD)
                                          || REGNO (dst) == FPUL_REG)
                                         ? r0_inc_rtx
@@ -4431,6 +5108,9 @@ sh_reorg (void)
          insn = barrier;
        }
     }
+  free_alloc_pool (label_ref_list_pool);
+  for (insn = first; insn; insn = NEXT_INSN (insn))
+    PUT_MODE (insn, VOIDmode);
 
   mdep_reorg_phase = SH_SHORTEN_BRANCHES1;
   INSN_ADDRESSES_FREE ();
@@ -4497,6 +5177,7 @@ split_branches (rtx first)
   rtx insn;
   struct far_branch **uid_branch, *far_branch_list = 0;
   int max_uid = get_max_uid ();
+  int ok;
 
   /* Find out which branches are out of range.  */
   shorten_branches (first);
@@ -4511,9 +5192,7 @@ split_branches (rtx first)
       {
        /* Shorten_branches would split this instruction again,
           so transform it into a note.  */
-       PUT_CODE (insn, NOTE);
-       NOTE_LINE_NUMBER (insn) = NOTE_INSN_DELETED;
-       NOTE_SOURCE_FILE (insn) = 0;
+       SET_INSN_DELETED (insn);
       }
     else if (GET_CODE (insn) == JUMP_INSN
             /* Don't mess with ADDR_DIFF_VEC */
@@ -4586,8 +5265,8 @@ split_branches (rtx first)
                    bp->insert_place = insn;
                    bp->address = addr;
                  }
-               if (! redirect_jump (insn, label, 1))
-                 abort ();
+               ok = redirect_jump (insn, label, 1);
+               gcc_assert (ok);
              }
            else
              {
@@ -4735,17 +5414,25 @@ final_prescan_insn (rtx insn, rtx *opvec ATTRIBUTE_UNUSED,
          pattern = PATTERN (insn);
          if (GET_CODE (pattern) == PARALLEL)
            pattern = XVECEXP (pattern, 0, 0);
-         if (GET_CODE (pattern) == CALL
-             || (GET_CODE (pattern) == SET
-                 && (GET_CODE (SET_SRC (pattern)) == CALL
-                     || get_attr_type (insn) == TYPE_SFUNC)))
-           asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
-                        CODE_LABEL_NUMBER (XEXP (note, 0)));
-         else if (GET_CODE (pattern) == SET)
-           (*targetm.asm_out.internal_label) (asm_out_file, "L",
-                                      CODE_LABEL_NUMBER (XEXP (note, 0)));
-         else
-           abort ();
+         switch (GET_CODE (pattern))
+           {
+           case SET:
+             if (GET_CODE (SET_SRC (pattern)) != CALL
+                 && get_attr_type (insn) != TYPE_SFUNC)
+               {
+                 targetm.asm_out.internal_label
+                   (asm_out_file, "L", CODE_LABEL_NUMBER (XEXP (note, 0)));
+                 break;
+               }
+             /* else FALLTHROUGH */
+           case CALL:
+             asm_fprintf (asm_out_file, "\t.uses %LL%d\n",
+                          CODE_LABEL_NUMBER (XEXP (note, 0)));
+             break;
+
+           default:
+             gcc_unreachable ();
+           }
        }
     }
 }
@@ -4815,8 +5502,7 @@ output_stack_adjust (int size, rtx reg, int epilogue_p,
 /* This test is bogus, as output_stack_adjust is used to re-align the
    stack.  */
 #if 0
-      if (size % align)
-       abort ();
+      gcc_assert (!(size % align));
 #endif
 
       if (CONST_OK_FOR_ADD (size))
@@ -4839,7 +5525,7 @@ output_stack_adjust (int size, rtx reg, int epilogue_p,
 
          /* 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.  */
+            to handle this case, so just die when we see it.  */
          if (epilogue_p < 0
              || current_function_interrupt
              || ! call_really_used_regs[temp] || fixed_regs[temp])
@@ -4883,56 +5569,65 @@ output_stack_adjust (int size, rtx reg, int epilogue_p,
              temp = scavenge_reg (&temps);
            }
          if (temp < 0 && live_regs_mask)
-           temp = scavenge_reg (live_regs_mask);
+           {
+             HARD_REG_SET temps;
+
+             COPY_HARD_REG_SET (temps, *live_regs_mask);
+             CLEAR_HARD_REG_BIT (temps, REGNO (reg));
+             temp = scavenge_reg (&temps);
+           }
          if (temp < 0)
            {
+             rtx adj_reg, tmp_reg, mem;
+             
              /* If we reached here, the most likely case is the (sibcall)
                 epilogue for non SHmedia.  Put a special push/pop sequence
                 for such case as the last resort.  This looks lengthy but
-                would not be problem because it seems to be very rare.  */
-             if (! TARGET_SHMEDIA && epilogue_p)
-               {
-                 rtx adj_reg, tmp_reg, mem;
-
-                 /* ??? There is still the slight possibility that r4 or r5
-                    have been reserved as fixed registers or assigned as
-                    global registers, and they change during an interrupt.
-                    There are possible ways to handle this:
-                    - If we are adjusting the frame pointer (r14), we can do
-                      with a single temp register and an ordinary push / pop
-                      on the stack.
-                    - Grab any call-used or call-saved registers (i.e. not
-                      fixed or globals) for the temps we need.  We might
-                      also grab r14 if we are adjusting the stack pointer.
-                      If we can't find enough available registers, issue
-                      a diagnostic and abort - the user must have reserved
-                      way too many registers.
-                    But since all this is rather unlikely to happen and
-                    would require extra testing, we just abort if r4 / r5
-                    are not available.  */
-                 if (fixed_regs[4] || fixed_regs[5]
-                     || global_regs[4] || global_regs[5])
-                   abort ();
-
-                 adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
-                 tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
-                 emit_move_insn (gen_rtx_MEM (Pmode, reg), adj_reg);
-                 emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
-                 emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
-                 mem = gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
-                 emit_move_insn (mem, tmp_reg);
-                 emit_move_insn (tmp_reg, gen_rtx_MEM (Pmode, reg));
-                 mem = gen_rtx_MEM (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
-                 emit_move_insn (mem, tmp_reg);
-                 emit_move_insn (reg, adj_reg);
-                 mem = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, reg));
-                 emit_move_insn (adj_reg, mem);
-                 mem = gen_rtx_MEM (Pmode, gen_rtx_POST_INC (Pmode, reg));
-                 emit_move_insn (tmp_reg, mem);
-                 return;
-               }
-             else
-               abort ();
+                would not be problem because it seems to be very
+                rare.  */
+             
+             gcc_assert (!TARGET_SHMEDIA && epilogue_p);
+             
+
+              /* ??? There is still the slight possibility that r4 or
+                 r5 have been reserved as fixed registers or assigned
+                 as global registers, and they change during an
+                 interrupt.  There are possible ways to handle this:
+                    
+                 - If we are adjusting the frame pointer (r14), we can do
+                   with a single temp register and an ordinary push / pop
+                   on the stack.
+                 - Grab any call-used or call-saved registers (i.e. not
+                   fixed or globals) for the temps we need.  We might
+                   also grab r14 if we are adjusting the stack pointer.
+                   If we can't find enough available registers, issue
+                   a diagnostic and die - the user must have reserved
+                   way too many registers.
+                But since all this is rather unlikely to happen and
+                would require extra testing, we just die if r4 / r5
+                are not available.  */
+             gcc_assert (!fixed_regs[4] && !fixed_regs[5]
+                         && !global_regs[4] && !global_regs[5]);
+
+             adj_reg = gen_rtx_REG (GET_MODE (reg), 4);
+             tmp_reg = gen_rtx_REG (GET_MODE (reg), 5);
+             emit_move_insn (gen_tmp_stack_mem (Pmode, reg), adj_reg);
+             emit_insn (GEN_MOV (adj_reg, GEN_INT (size)));
+             emit_insn (GEN_ADD3 (adj_reg, adj_reg, reg));
+             mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+             emit_move_insn (mem, tmp_reg);
+             emit_move_insn (tmp_reg, gen_tmp_stack_mem (Pmode, reg));
+             mem = gen_tmp_stack_mem (Pmode, gen_rtx_PRE_DEC (Pmode, adj_reg));
+             emit_move_insn (mem, tmp_reg);
+             emit_move_insn (reg, adj_reg);
+             mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
+             emit_move_insn (adj_reg, mem);
+             mem = gen_tmp_stack_mem (Pmode, gen_rtx_POST_INC (Pmode, reg));
+             emit_move_insn (tmp_reg, mem);
+             /* Tell flow the insns that pop r4/r5 aren't dead.  */
+             emit_insn (gen_rtx_USE (VOIDmode, tmp_reg));
+             emit_insn (gen_rtx_USE (VOIDmode, adj_reg));
+             return;
            }
          const_reg = gen_rtx_REG (GET_MODE (reg), temp);
 
@@ -5030,19 +5725,19 @@ pop (int rn)
 static void
 push_regs (HARD_REG_SET *mask, int interrupt_handler)
 {
-  int i;
+  int i = interrupt_handler ? LAST_BANKED_REG + 1 : 0;
   int skip_fpscr = 0;
 
   /* Push PR last; this gives better latencies after the prologue, and
      candidates for the return delay slot when there are no general
      registers pushed.  */
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+  for (; i < FIRST_PSEUDO_REGISTER; i++)
     {
       /* If this is an interrupt handler, and the SZ bit varies,
         and we have to push any floating point register, we need
         to switch to the correct precision first.  */
       if (i == FIRST_FP_REG && interrupt_handler && TARGET_FMOVD
-         && hard_regs_intersect_p (mask, &reg_class_contents[DF_REGS]))
+         && hard_reg_set_intersect_p (*mask, reg_class_contents[DF_REGS]))
        {
          HARD_REG_SET unsaved;
 
@@ -5056,6 +5751,13 @@ push_regs (HARD_REG_SET *mask, int interrupt_handler)
          && TEST_HARD_REG_BIT (*mask, i))
        push (i);
     }
+
+  /* Push banked registers last to improve delay slot opportunities.  */
+  if (interrupt_handler)
+    for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+      if (TEST_HARD_REG_BIT (*mask, i))
+       push (i);
+
   if (TEST_HARD_REG_BIT (*mask, PR_REG))
     push (PR_REG);
 }
@@ -5119,24 +5821,30 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
 {
   unsigned int reg;
   int count;
-  int interrupt_handler;
+  tree attrs;
+  bool interrupt_or_trapa_handler, trapa_handler, interrupt_handler;
+  bool nosave_low_regs;
   int pr_live, has_call;
 
-  interrupt_handler = sh_cfun_interrupt_handler_p ();
+  attrs = DECL_ATTRIBUTES (current_function_decl);
+  interrupt_or_trapa_handler = sh_cfun_interrupt_handler_p ();
+  trapa_handler = lookup_attribute ("trapa_handler", attrs) != NULL_TREE;
+  interrupt_handler = interrupt_or_trapa_handler && ! trapa_handler;
+  nosave_low_regs = lookup_attribute ("nosave_low_regs", attrs) != NULL_TREE;
 
   CLEAR_HARD_REG_SET (*live_regs_mask);
   if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && interrupt_handler
-      && regs_ever_live[FPSCR_REG])
-    target_flags &= ~FPU_SINGLE_BIT;
+      && df_regs_ever_live_p (FPSCR_REG))
+    target_flags &= ~MASK_FPU_SINGLE;
   /* If we can save a lot of saves by switching to double mode, do that.  */
   else if ((TARGET_SH4 || TARGET_SH2A_DOUBLE) && TARGET_FMOVD && TARGET_FPU_SINGLE)
     for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
-      if (regs_ever_live[reg] && regs_ever_live[reg+1]
+      if (df_regs_ever_live_p (reg) && df_regs_ever_live_p (reg+1)
          && (! call_really_used_regs[reg]
-             || (interrupt_handler && ! pragma_trapa))
+             || interrupt_handler)
          && ++count > 2)
        {
-         target_flags &= ~FPU_SINGLE_BIT;
+         target_flags &= ~MASK_FPU_SINGLE;
          break;
        }
   /* PR_MEDIA_REG is a general purpose register, thus global_alloc already
@@ -5153,11 +5861,11 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
       pr_live = (pr_initial
                 ? (GET_CODE (pr_initial) != REG
                    || REGNO (pr_initial) != (PR_REG))
-                : regs_ever_live[PR_REG]);
+                : df_regs_ever_live_p (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];
+      pr_live |= df_regs_ever_live_p (RETURN_ADDRESS_POINTER_REGNUM);
     }
   /* Force PR to be live if the prologue has to call the SHmedia
      argument decoder or register saver.  */
@@ -5171,14 +5879,15 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
     {
       if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
          ? pr_live
-         : (interrupt_handler && ! pragma_trapa)
+         : interrupt_handler
          ? (/* Need to save all the regs ever live.  */
-            (regs_ever_live[reg]
+            (df_regs_ever_live_p (reg)
              || (call_really_used_regs[reg]
                  && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG
                      || reg == PIC_OFFSET_TABLE_REGNUM)
                  && has_call)
-             || (has_call && REGISTER_NATURAL_MODE (reg) == SImode
+             || (TARGET_SHMEDIA && 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
@@ -5190,14 +5899,16 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
              && flag_pic
              && current_function_args_info.call_cookie
              && reg == PIC_OFFSET_TABLE_REGNUM)
-            || (regs_ever_live[reg] && ! call_really_used_regs[reg])
+            || (df_regs_ever_live_p (reg)
+                && (!call_really_used_regs[reg]
+                    || (trapa_handler && reg == FPSCR_REG && TARGET_FPU_ANY)))
             || (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 == MACL_REG || reg == MACH_REG)
-                && regs_ever_live[reg]
+                && df_regs_ever_live_p (reg)
                 && sh_cfun_attr_renesas_p ())
             ))
        {
@@ -5209,7 +5920,7 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
            {
              if (FP_REGISTER_P (reg))
                {
-                 if (! TARGET_FPU_SINGLE && ! regs_ever_live[reg ^ 1])
+                 if (! TARGET_FPU_SINGLE && ! df_regs_ever_live_p (reg ^ 1))
                    {
                      SET_HARD_REG_BIT (*live_regs_mask, (reg ^ 1));
                      count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
@@ -5218,10 +5929,12 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
              else if (XD_REGISTER_P (reg))
                {
                  /* Must switch to double mode to access these registers.  */
-                 target_flags &= ~FPU_SINGLE_BIT;
+                 target_flags &= ~MASK_FPU_SINGLE;
                }
            }
        }
+      if (nosave_low_regs && reg == R8_REG)
+       break;
     }
   /* If we have a target register optimization pass after prologue / epilogue
      threading, we need to assume all target registers will be live even if
@@ -5241,10 +5954,10 @@ calc_live_regs (HARD_REG_SET *live_regs_mask)
      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]))
+      && hard_reg_set_intersect_p (*live_regs_mask,
+                                  reg_class_contents[TARGET_REGS])
+      && ! hard_reg_set_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));
@@ -5281,11 +5994,13 @@ sh_media_register_for_return (void)
   if (lookup_attribute ("interrupt_handler",
                        DECL_ATTRIBUTES (current_function_decl)))
     return -1;
+  if (sh_cfun_interrupt_handler_p ())
+    return -1;
 
-  tr0_used = flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
+  tr0_used = flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
 
   for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
-    if (call_really_used_regs[regno] && ! regs_ever_live[regno])
+    if (call_really_used_regs[regno] && ! df_regs_ever_live_p (regno))
       return regno;
 
   return -1;
@@ -5423,6 +6138,8 @@ sh_expand_prologue (void)
   int d_rounding = 0;
   int save_flags = target_flags;
   int pretend_args;
+  tree sp_switch_attr
+    = lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl));
 
   current_function_interrupt = sh_cfun_interrupt_handler_p ();
 
@@ -5442,7 +6159,7 @@ sh_expand_prologue (void)
        incoming-argument decoder and/or of the return trampoline from
        the GOT, so make sure the PIC register is preserved and
        initialized.  */
-    regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+    df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
   if (TARGET_SHCOMPACT
       && (current_function_args_info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
@@ -5475,19 +6192,8 @@ sh_expand_prologue (void)
       int tr = sh_media_register_for_return ();
 
       if (tr >= 0)
-       {
-         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,
-                                               const0_rtx,
-                                               REG_NOTES (insn));
-       }
+       emit_move_insn (gen_rtx_REG (DImode, tr),
+                       gen_rtx_REG (DImode, PR_MEDIA_REG));
     }
 
   /* Emit the code for SETUP_VARARGS.  */
@@ -5512,8 +6218,16 @@ sh_expand_prologue (void)
     }
 
   /* If we're supposed to switch stacks at function entry, do so now.  */
-  if (sp_switch)
-    emit_insn (gen_sp_switch_1 ());
+  if (sp_switch_attr)
+    {
+      /* The argument specifies a variable holding the address of the
+        stack the interrupt function should switch to/from at entry/exit.  */
+      const char *s
+       = ggc_strdup (TREE_STRING_POINTER (TREE_VALUE (sp_switch_attr)));
+      rtx sp_switch = gen_rtx_SYMBOL_REF (Pmode, s);
+
+      emit_insn (gen_sp_switch_1 (sp_switch));
+    }
 
   d = calc_live_regs (&live_regs_mask);
   /* ??? Maybe we could save some switching if we can move a mode switch
@@ -5579,15 +6293,14 @@ sh_expand_prologue (void)
 
          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_frame_mem (mode,
+                                  gen_rtx_PLUS (Pmode,
+                                                stack_pointer_rtx,
+                                                GEN_INT (offset)));
 
          GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec);
 
-         if (! r0)
-           abort ();
+         gcc_assert (r0);
          mem_rtx = NULL_RTX;
 
        try_pre_dec:
@@ -5597,8 +6310,7 @@ sh_expand_prologue (void)
                    || mem_rtx == NULL_RTX
                    || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
              {
-               pre_dec = gen_rtx_MEM (mode,
-                                      gen_rtx_PRE_DEC (Pmode, r0));
+               pre_dec = gen_frame_mem (mode, gen_rtx_PRE_DEC (Pmode, r0));
 
                GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0),
                                          pre_dec_ok);
@@ -5646,22 +6358,21 @@ sh_expand_prologue (void)
              mem_rtx = pre_dec;
            }
          else if (sp_in_r0)
-           mem_rtx = gen_rtx_MEM (mode, r0);
+           mem_rtx = gen_frame_mem (mode, r0);
          else
-           mem_rtx = gen_rtx_MEM (mode,
-                                  gen_rtx_PLUS (Pmode,
-                                                stack_pointer_rtx,
-                                                r0));
+           mem_rtx = gen_frame_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 ();
-
+         gcc_assert (!TARGET_REGISTER_P (reg)
+                     && ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
+                         || mem_rtx == pre_dec));
+         
        addr_ok:
          orig_reg_rtx = reg_rtx;
          if (TARGET_REGISTER_P (reg)
@@ -5676,8 +6387,8 @@ sh_expand_prologue (void)
                {
                  offset_in_r0 = -1;
                  sp_in_r0 = 0;
-                 if (refers_to_regno_p (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0))
-                   abort ();
+                 gcc_assert (!refers_to_regno_p
+                             (R0_REG, R0_REG+1, mem_rtx, (rtx *) 0));
                }
 
              if (*++tmp_pnt <= 0)
@@ -5712,10 +6423,10 @@ sh_expand_prologue (void)
              {
                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)));
+               rtx mem_rtx = gen_frame_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,
@@ -5725,54 +6436,28 @@ sh_expand_prologue (void)
          }
        }
 
-      if (entry->offset != d_rounding)
-       abort ();
+      gcc_assert (entry->offset == d_rounding);
     }
   else
     push_regs (&live_regs_mask, current_function_interrupt);
 
-  if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
-    {
-      rtx insn = get_last_insn ();
-      rtx last = emit_insn (gen_GOTaddr2picreg ());
-
-      /* Mark these insns as possibly dead.  Sometimes, flow2 may
-        delete all uses of the PIC register.  In this case, let it
-        delete the initialization too.  */
-      do
-       {
-         insn = NEXT_INSN (insn);
-
-         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
-                                               const0_rtx,
-                                               REG_NOTES (insn));
-       }
-      while (insn != last);
-    }
+  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
+    emit_insn (gen_GOTaddr2picreg ());
 
   if (SHMEDIA_REGS_STACK_ADJUST ())
     {
-      emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
-                     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.  */
+      function_symbol (gen_rtx_REG (Pmode, R0_REG),
+                      (TARGET_FPU_ANY
+                       ? "__GCC_push_shmedia_regs"
+                       : "__GCC_push_shmedia_regs_nofpu"), SFUNC_GOT);
       emit_insn (gen_shmedia_save_restore_regs_compact
                 (GEN_INT (-SHMEDIA_REGS_STACK_ADJUST ())));
     }
 
   if (target_flags != save_flags && ! current_function_interrupt)
-    {
-      rtx insn = emit_insn (gen_toggle_sz ());
-
-      /* If we're lucky, a mode switch in the function body will
-        overwrite fpscr, turning this insn dead.  Tell flow this
-        insn is ok to delete.  */
-      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD,
-                                           const0_rtx,
-                                           REG_NOTES (insn));
-    }
+    emit_insn (gen_toggle_sz ());
 
   target_flags = save_flags;
 
@@ -5780,15 +6465,15 @@ sh_expand_prologue (void)
                       stack_pointer_rtx, 0, NULL);
 
   if (frame_pointer_needed)
-    frame_insn (GEN_MOV (frame_pointer_rtx, stack_pointer_rtx));
+    frame_insn (GEN_MOV (hard_frame_pointer_rtx, stack_pointer_rtx));
 
   if (TARGET_SHCOMPACT
       && (current_function_args_info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
     {
       /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
-      emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
-                     function_symbol ("__GCC_shcompact_incoming_args"));
+      function_symbol (gen_rtx_REG (Pmode, R0_REG),
+                     "__GCC_shcompact_incoming_args", SFUNC_GOT);
       emit_insn (gen_shcompact_incoming_args ());
     }
 }
@@ -5842,14 +6527,15 @@ sh_expand_epilogue (bool sibcall_p)
         when exception handling is enabled.  See PR/18032.  */
       if (flag_exceptions)
        emit_insn (gen_blockage ());
-      output_stack_adjust (frame_size, frame_pointer_rtx, e, &live_regs_mask);
+      output_stack_adjust (frame_size, hard_frame_pointer_rtx, e,
+                          &live_regs_mask);
 
       /* We must avoid moving the stack pointer adjustment past code
         which reads from the local frame, else an interrupt could
         occur after the SP adjustment and clobber data in the local
         frame.  */
       emit_insn (gen_blockage ());
-      emit_insn (GEN_MOV (stack_pointer_rtx, frame_pointer_rtx));
+      emit_insn (GEN_MOV (stack_pointer_rtx, hard_frame_pointer_rtx));
     }
   else if (frame_size)
     {
@@ -5863,10 +6549,10 @@ sh_expand_epilogue (bool sibcall_p)
 
   if (SHMEDIA_REGS_STACK_ADJUST ())
     {
-      emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
-                     function_symbol (TARGET_FPU_ANY
-                                      ? "__GCC_pop_shmedia_regs"
-                                      : "__GCC_pop_shmedia_regs_nofpu"));
+      function_symbol (gen_rtx_REG (Pmode, R0_REG),
+                      (TARGET_FPU_ANY
+                       ? "__GCC_pop_shmedia_regs"
+                       : "__GCC_pop_shmedia_regs_nofpu"), SFUNC_GOT);
       /* This must NOT go through the PLT, otherwise mach and macl
         may be clobbered.  */
       emit_insn (gen_shmedia_save_restore_regs_compact
@@ -5899,10 +6585,10 @@ sh_expand_epilogue (bool sibcall_p)
          offset = offset_base + entry->offset;
          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_frame_mem (mode,
+                                  gen_rtx_PLUS (Pmode,
+                                                stack_pointer_rtx,
+                                                GEN_INT (offset)));
 
          GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc);
 
@@ -5916,8 +6602,7 @@ sh_expand_epilogue (bool sibcall_p)
                        && mem_rtx == NULL_RTX)
                    || reg == PR_REG || SPECIAL_REGISTER_P (reg)))
              {
-               post_inc = gen_rtx_MEM (mode,
-                                       gen_rtx_POST_INC (Pmode, r0));
+               post_inc = gen_frame_mem (mode, gen_rtx_POST_INC (Pmode, r0));
 
                GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0),
                                          post_inc_ok);
@@ -5963,16 +6648,15 @@ sh_expand_epilogue (bool sibcall_p)
              offset_in_r0 += GET_MODE_SIZE (mode);
            }
          else if (sp_in_r0)
-           mem_rtx = gen_rtx_MEM (mode, r0);
+           mem_rtx = gen_frame_mem (mode, r0);
          else
-           mem_rtx = gen_rtx_MEM (mode,
-                                  gen_rtx_PLUS (Pmode,
-                                                stack_pointer_rtx,
-                                                r0));
+           mem_rtx = gen_frame_mem (mode,
+                                    gen_rtx_PLUS (Pmode,
+                                                  stack_pointer_rtx,
+                                                  r0));
 
-         if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
-             && mem_rtx != post_inc)
-           abort ();
+         gcc_assert ((reg != PR_REG && !SPECIAL_REGISTER_P (reg))
+                     || mem_rtx == post_inc);
 
        addr_ok:
          if ((reg == PR_REG || SPECIAL_REGISTER_P (reg))
@@ -5994,34 +6678,48 @@ sh_expand_epilogue (bool sibcall_p)
            }
 
          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));
        }
 
-      if (entry->offset + offset_base != d + d_rounding)
-       abort ();
+      gcc_assert (entry->offset + offset_base == d + d_rounding);
     }
   else /* ! TARGET_SH5 */
     {
+      int last_reg;
+
       save_size = 0;
       if (TEST_HARD_REG_BIT (live_regs_mask, PR_REG))
-       pop (PR_REG);
-      for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+       {
+         if (!frame_pointer_needed)
+           emit_insn (gen_blockage ());
+         pop (PR_REG);
+       }
+
+      /* Banked registers are poped first to avoid being scheduled in the
+        delay slot. RTE switches banks before the ds instruction.  */
+      if (current_function_interrupt)
+       {
+         for (i = FIRST_BANKED_REG; i <= LAST_BANKED_REG; i++)
+           if (TEST_HARD_REG_BIT (live_regs_mask, i)) 
+             pop (LAST_BANKED_REG - i);
+
+         last_reg = FIRST_PSEUDO_REGISTER - LAST_BANKED_REG - 1;
+       }
+      else
+       last_reg = FIRST_PSEUDO_REGISTER;
+
+      for (i = 0; i < last_reg; 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]))
+             && hard_reg_set_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 (target_flags != save_flags && ! current_function_interrupt)
@@ -6038,7 +6736,7 @@ sh_expand_epilogue (bool sibcall_p)
                         EH_RETURN_STACKADJ_RTX));
 
   /* Switch back to the normal stack if necessary.  */
-  if (sp_switch)
+  if (lookup_attribute ("sp_switch", DECL_ATTRIBUTES (current_function_decl)))
     emit_insn (gen_sp_switch_2 ());
 
   /* Tell flow the insn that pops PR isn't dead.  */
@@ -6117,7 +6815,7 @@ sh_set_return_address (rtx ra, rtx tmp)
          goto found;
 
       /* We can't find pr register.  */
-      abort ();
+      gcc_unreachable ();
 
     found:
       offset = entry->offset - offset;
@@ -6128,9 +6826,9 @@ sh_set_return_address (rtx ra, rtx tmp)
     pr_offset = rounded_frame_size (d);
 
   emit_insn (GEN_MOV (tmp, GEN_INT (pr_offset)));
-  emit_insn (GEN_ADD3 (tmp, tmp, frame_pointer_rtx));
+  emit_insn (GEN_ADD3 (tmp, tmp, hard_frame_pointer_rtx));
 
-  tmp = gen_rtx_MEM (Pmode, tmp);
+  tmp = gen_frame_mem (Pmode, tmp);
   emit_insn (GEN_MOV (tmp, ra));
 }
 
@@ -6140,9 +6838,7 @@ static void
 sh_output_function_epilogue (FILE *file ATTRIBUTE_UNUSED,
                             HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
-  trap_exit = pragma_interrupt = pragma_trapa = pragma_nosave_low_regs = 0;
   sh_need_epilogue_known = 0;
-  sp_switch = NULL_RTX;
 }
 
 static rtx
@@ -6207,8 +6903,7 @@ sh_builtin_saveregs (void)
   bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
 
   if (TARGET_SHMEDIA)
-    regbuf = gen_rtx_MEM (BLKmode,
-                         gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
+    regbuf = gen_frame_mem (BLKmode, gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
   else if (n_floatregs & 1)
     {
       rtx addr;
@@ -6218,8 +6913,18 @@ sh_builtin_saveregs (void)
       emit_insn (gen_iorsi3 (addr, addr, GEN_INT (UNITS_PER_WORD)));
       regbuf = change_address (regbuf, BLKmode, addr);
     }
+  else if (STACK_BOUNDARY < 64 && TARGET_FPU_DOUBLE && n_floatregs)
+    {
+      rtx addr, mask;
+
+      regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
+      addr = copy_to_mode_reg (Pmode, plus_constant (XEXP (regbuf, 0), 4));
+      mask = copy_to_mode_reg (Pmode, GEN_INT (-8));
+      emit_insn (gen_andsi3 (addr, addr, mask));
+      regbuf = change_address (regbuf, BLKmode, addr);
+    }
   else
-    regbuf = assign_stack_local (BLKmode, bufsize, 0);
+    regbuf = assign_stack_local (BLKmode, bufsize, TARGET_FPU_DOUBLE ? 64 : 0);
   alias_set = get_varargs_alias_set ();
   set_mem_alias_set (regbuf, alias_set);
 
@@ -6244,10 +6949,9 @@ sh_builtin_saveregs (void)
      saved).
      We emit the moves in reverse order so that we can use predecrement.  */
 
-  fpregs = gen_reg_rtx (Pmode);
-  emit_move_insn (fpregs, XEXP (regbuf, 0));
-  emit_insn (gen_addsi3 (fpregs, fpregs,
-                        GEN_INT (n_floatregs * UNITS_PER_WORD)));
+  fpregs = copy_to_mode_reg (Pmode,
+                            plus_constant (XEXP (regbuf, 0),
+                                            n_floatregs * UNITS_PER_WORD));
   if (TARGET_SH4 || TARGET_SH2A_DOUBLE)
     {
       rtx mem;
@@ -6255,8 +6959,7 @@ sh_builtin_saveregs (void)
        {
          emit_insn (gen_addsi3 (fpregs, fpregs,
                                 GEN_INT (-2 * UNITS_PER_WORD)));
-         mem = gen_rtx_MEM (DFmode, fpregs);
-         set_mem_alias_set (mem, alias_set);
+         mem = change_address (regbuf, DFmode, fpregs);
          emit_move_insn (mem,
                          gen_rtx_REG (DFmode, BASE_ARG_REG (DFmode) + regno));
        }
@@ -6264,8 +6967,7 @@ sh_builtin_saveregs (void)
       if (regno & 1)
        {
          emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD)));
-         mem = gen_rtx_MEM (SFmode, fpregs);
-         set_mem_alias_set (mem, alias_set);
+         mem = change_address (regbuf, SFmode, fpregs);
          emit_move_insn (mem,
                          gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno
                                                - (TARGET_LITTLE_ENDIAN != 0)));
@@ -6277,8 +6979,7 @@ sh_builtin_saveregs (void)
         rtx mem;
 
        emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (-UNITS_PER_WORD)));
-       mem = gen_rtx_MEM (SFmode, fpregs);
-       set_mem_alias_set (mem, alias_set);
+       mem = change_address (regbuf, SFmode, fpregs);
        emit_move_insn (mem,
                        gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno));
       }
@@ -6361,20 +7062,21 @@ sh_va_start (tree valist, rtx nextarg)
   f_next_fp_limit = TREE_CHAIN (f_next_fp);
   f_next_stack = TREE_CHAIN (f_next_fp_limit);
 
-  next_o = build (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
-                 NULL_TREE);
-  next_o_limit = build (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
-                       valist, f_next_o_limit, NULL_TREE);
-  next_fp = build (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
+  next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
                   NULL_TREE);
-  next_fp_limit = build (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
-                        valist, f_next_fp_limit, NULL_TREE);
-  next_stack = build (COMPONENT_REF, TREE_TYPE (f_next_stack),
-                     valist, f_next_stack, NULL_TREE);
+  next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
+                        valist, f_next_o_limit, NULL_TREE);
+  next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp), valist, f_next_fp,
+                   NULL_TREE);
+  next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
+                         valist, f_next_fp_limit, NULL_TREE);
+  next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
+                      valist, f_next_stack, NULL_TREE);
 
   /* Call __builtin_saveregs.  */
-  u = make_tree (ptr_type_node, expand_builtin_saveregs ());
-  t = build (MODIFY_EXPR, ptr_type_node, next_fp, u);
+  u = make_tree (sizetype, expand_builtin_saveregs ());
+  u = fold_convert (ptr_type_node, u);
+  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_fp, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -6383,13 +7085,13 @@ sh_va_start (tree valist, rtx nextarg)
     nfp = 8 - nfp;
   else
     nfp = 0;
-  u = fold (build (PLUS_EXPR, ptr_type_node, u,
-                  build_int_cst (NULL_TREE, UNITS_PER_WORD * nfp)));
-  t = build (MODIFY_EXPR, ptr_type_node, next_fp_limit, u);
+  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
+                  size_int (UNITS_PER_WORD * nfp));
+  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_fp_limit, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
-  t = build (MODIFY_EXPR, ptr_type_node, next_o, u);
+  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_o, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
@@ -6398,18 +7100,39 @@ sh_va_start (tree valist, rtx nextarg)
     nint = 4 - nint;
   else
     nint = 0;
-  u = fold (build (PLUS_EXPR, ptr_type_node, u,
-                  build_int_cst (NULL_TREE, UNITS_PER_WORD * nint)));
-  t = build (MODIFY_EXPR, ptr_type_node, next_o_limit, u);
+  u = fold_build2 (POINTER_PLUS_EXPR, ptr_type_node, u,
+                  size_int (UNITS_PER_WORD * nint));
+  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_o_limit, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
   u = make_tree (ptr_type_node, nextarg);
-  t = build (MODIFY_EXPR, ptr_type_node, next_stack, u);
+  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_stack, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 }
 
+/* TYPE is a RECORD_TYPE.  If there is only a single nonzero-sized
+   member, return it.  */
+static tree
+find_sole_member (tree type)
+{
+  tree field, member = NULL_TREE;
+
+  for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+    {
+      if (TREE_CODE (field) != FIELD_DECL)
+       continue;
+      if (!DECL_SIZE (field))
+       return NULL_TREE;
+      if (integer_zerop (DECL_SIZE (field)))
+       continue;
+      if (member)
+       return NULL_TREE;
+      member = field;
+    }
+  return member;
+}
 /* Implement `va_arg'.  */
 
 static tree
@@ -6420,6 +7143,7 @@ sh_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
   tree tmp, pptr_type_node;
   tree addr, lab_over = NULL, result = NULL;
   int pass_by_ref = targetm.calls.must_pass_in_stack (TYPE_MODE (type), type);
+  tree eff_type;
 
   if (pass_by_ref)
     type = build_pointer_type (type);
@@ -6435,6 +7159,7 @@ sh_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
       tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
       int pass_as_float;
       tree lab_false;
+      tree member;
 
       f_next_o = TYPE_FIELDS (va_list_type_node);
       f_next_o_limit = TREE_CHAIN (f_next_o);
@@ -6442,38 +7167,51 @@ sh_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
       f_next_fp_limit = TREE_CHAIN (f_next_fp);
       f_next_stack = TREE_CHAIN (f_next_fp_limit);
 
-      next_o = build (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
-                     NULL_TREE);
-      next_o_limit = build (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
-                           valist, f_next_o_limit, NULL_TREE);
-      next_fp = build (COMPONENT_REF, TREE_TYPE (f_next_fp),
-                      valist, f_next_fp, NULL_TREE);
-      next_fp_limit = build (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
-                            valist, f_next_fp_limit, NULL_TREE);
-      next_stack = build (COMPONENT_REF, TREE_TYPE (f_next_stack),
-                         valist, f_next_stack, NULL_TREE);
+      next_o = build3 (COMPONENT_REF, TREE_TYPE (f_next_o), valist, f_next_o,
+                      NULL_TREE);
+      next_o_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_o_limit),
+                            valist, f_next_o_limit, NULL_TREE);
+      next_fp = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp),
+                       valist, f_next_fp, NULL_TREE);
+      next_fp_limit = build3 (COMPONENT_REF, TREE_TYPE (f_next_fp_limit),
+                             valist, f_next_fp_limit, NULL_TREE);
+      next_stack = build3 (COMPONENT_REF, TREE_TYPE (f_next_stack),
+                          valist, f_next_stack, NULL_TREE);
 
       /* 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));
+      eff_type = type;
+      while (TREE_CODE (eff_type) == RECORD_TYPE
+            && (member = find_sole_member (eff_type))
+            && (TREE_CODE (TREE_TYPE (member)) == REAL_TYPE
+                || TREE_CODE (TREE_TYPE (member)) == COMPLEX_TYPE
+                || TREE_CODE (TREE_TYPE (member)) == RECORD_TYPE))
+       {
+         tree field_type = TREE_TYPE (member);
+
+         if (TYPE_MODE (eff_type) == TYPE_MODE (field_type))
+           eff_type = field_type;
+         else
+           {
+             gcc_assert ((TYPE_ALIGN (eff_type)
+                          < GET_MODE_ALIGNMENT (TYPE_MODE (field_type)))
+                         || (TYPE_ALIGN (eff_type)
+                             > GET_MODE_BITSIZE (TYPE_MODE (field_type))));
+             break;
+           }
+       }
 
       if (TARGET_SH4)
        {
-         pass_as_float = ((TREE_CODE (type) == REAL_TYPE && size <= 8)
-                          || (TREE_CODE (type) == COMPLEX_TYPE
-                              && TREE_CODE (TREE_TYPE (type)) == REAL_TYPE
+         pass_as_float = ((TREE_CODE (eff_type) == REAL_TYPE && size <= 8)
+                          || (TREE_CODE (eff_type) == COMPLEX_TYPE
+                              && TREE_CODE (TREE_TYPE (eff_type)) == REAL_TYPE
                               && size <= 16));
        }
       else
        {
-         pass_as_float = (TREE_CODE (type) == REAL_TYPE && size == 4);
+         pass_as_float = (TREE_CODE (eff_type) == REAL_TYPE && size == 4);
        }
 
       addr = create_tmp_var (pptr_type_node, NULL);
@@ -6484,92 +7222,113 @@ sh_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
 
       if (pass_as_float)
        {
-         int first_floatreg
-           = current_function_args_info.arg_count[(int) SH_ARG_FLOAT];
-         int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
+         tree next_fp_tmp = create_tmp_var (TREE_TYPE (f_next_fp), NULL);
+         tree cmp;
+         bool is_double = size == 8 && TREE_CODE (eff_type) == REAL_TYPE;
 
-         tmp = build (GE_EXPR, boolean_type_node, next_fp, next_fp_limit);
-         tmp = build (COND_EXPR, void_type_node, tmp,
-                      build (GOTO_EXPR, void_type_node, lab_false),
-                      NULL);
+         tmp = build1 (ADDR_EXPR, pptr_type_node, next_fp);
+         tmp = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, tmp);
          gimplify_and_add (tmp, pre_p);
 
-         if (TYPE_ALIGN (type) > BITS_PER_WORD
-             || (((TREE_CODE (type) == REAL_TYPE && size == 8) || size == 16)
-                 && (n_floatregs & 1)))
+         tmp = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_fp_tmp, valist);
+         gimplify_and_add (tmp, pre_p);
+         tmp = next_fp_limit;
+         if (size > 4 && !is_double)
+           tmp = build2 (POINTER_PLUS_EXPR, TREE_TYPE (tmp), tmp,
+                         size_int (4 - size));
+         tmp = build2 (GE_EXPR, boolean_type_node, next_fp_tmp, tmp);
+         cmp = build3 (COND_EXPR, void_type_node, tmp,
+                       build1 (GOTO_EXPR, void_type_node, lab_false),
+                       NULL_TREE);
+         if (!is_double)
+           gimplify_and_add (cmp, pre_p);
+
+         if (TYPE_ALIGN (eff_type) > BITS_PER_WORD
+             || (is_double || size == 16))
            {
-             tmp = fold_convert (ptr_type_node, size_int (UNITS_PER_WORD));
-             tmp = build (BIT_AND_EXPR, ptr_type_node, next_fp, tmp);
-             tmp = build (PLUS_EXPR, ptr_type_node, next_fp, tmp);
-             tmp = build (MODIFY_EXPR, ptr_type_node, next_fp, tmp);
+             tmp = fold_convert (sizetype, next_fp_tmp);
+             tmp = build2 (BIT_AND_EXPR, sizetype, tmp,
+                           size_int (UNITS_PER_WORD));
+             tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node,
+                           next_fp_tmp, tmp);
+             tmp = build2 (GIMPLE_MODIFY_STMT, ptr_type_node,
+                           next_fp_tmp, tmp);
              gimplify_and_add (tmp, pre_p);
            }
-
-         tmp = build1 (ADDR_EXPR, pptr_type_node, next_fp);
-         tmp = build (MODIFY_EXPR, void_type_node, addr, tmp);
-         gimplify_and_add (tmp, pre_p);
+         if (is_double)
+           gimplify_and_add (cmp, pre_p);
 
 #ifdef FUNCTION_ARG_SCmode_WART
-         if (TYPE_MODE (type) == SCmode && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
+         if (TYPE_MODE (eff_type) == SCmode
+             && TARGET_SH4 && TARGET_LITTLE_ENDIAN)
            {
-             tree subtype = TREE_TYPE (type);
+             tree subtype = TREE_TYPE (eff_type);
              tree real, imag;
 
-             imag = std_gimplify_va_arg_expr (valist, subtype, pre_p, NULL);
+             imag
+               = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
              imag = get_initialized_tmp_var (imag, pre_p, NULL);
 
-             real = std_gimplify_va_arg_expr (valist, subtype, pre_p, NULL);
+             real
+               = std_gimplify_va_arg_expr (next_fp_tmp, subtype, pre_p, NULL);
              real = get_initialized_tmp_var (real, pre_p, NULL);
 
-             result = build (COMPLEX_EXPR, type, real, imag);
+             result = build2 (COMPLEX_EXPR, type, real, imag);
              result = get_initialized_tmp_var (result, pre_p, NULL);
            }
 #endif /* FUNCTION_ARG_SCmode_WART */
 
-         tmp = build (GOTO_EXPR, void_type_node, lab_over);
+         tmp = build1 (GOTO_EXPR, void_type_node, lab_over);
          gimplify_and_add (tmp, pre_p);
 
-         tmp = build (LABEL_EXPR, void_type_node, lab_false);
+         tmp = build1 (LABEL_EXPR, void_type_node, lab_false);
          gimplify_and_add (tmp, pre_p);
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, next_stack);
-         tmp = build (MODIFY_EXPR, void_type_node, addr, tmp);
+         tmp = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, tmp);
+         gimplify_and_add (tmp, pre_p);
+         tmp = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, next_fp_tmp, valist);
          gimplify_and_add (tmp, pre_p);
+
+         tmp = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, valist, next_fp_tmp);
+         gimplify_and_add (tmp, post_p);
+         valist = next_fp_tmp;
        }
       else
        {
-         tmp = fold_convert (ptr_type_node, size_int (rsize));
-         tmp = build (PLUS_EXPR, ptr_type_node, next_o, tmp);
-         tmp = build (GT_EXPR, boolean_type_node, tmp, next_o_limit);
-         tmp = build (COND_EXPR, void_type_node, tmp,
-                      build (GOTO_EXPR, void_type_node, lab_false),
-                      NULL);
+         tmp = build2 (POINTER_PLUS_EXPR, ptr_type_node, next_o,
+                       size_int (rsize));
+         tmp = build2 (GT_EXPR, boolean_type_node, tmp, next_o_limit);
+         tmp = build3 (COND_EXPR, void_type_node, tmp,
+                       build1 (GOTO_EXPR, void_type_node, lab_false),
+                       NULL_TREE);
          gimplify_and_add (tmp, pre_p);
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, next_o);
-         tmp = build (MODIFY_EXPR, void_type_node, addr, tmp);
+         tmp = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, tmp);
          gimplify_and_add (tmp, pre_p);
 
-         tmp = build (GOTO_EXPR, void_type_node, lab_over);
+         tmp = build1 (GOTO_EXPR, void_type_node, lab_over);
          gimplify_and_add (tmp, pre_p);
 
-         tmp = build (LABEL_EXPR, void_type_node, lab_false);
+         tmp = build1 (LABEL_EXPR, void_type_node, lab_false);
          gimplify_and_add (tmp, pre_p);
 
          if (size > 4 && ! TARGET_SH4)
            {
-             tmp = build (MODIFY_EXPR, ptr_type_node, next_o, next_o_limit);
+             tmp = build2 (GIMPLE_MODIFY_STMT, ptr_type_node,
+                           next_o, next_o_limit);
              gimplify_and_add (tmp, pre_p);
            }
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, next_stack);
-         tmp = build (MODIFY_EXPR, void_type_node, addr, tmp);
+         tmp = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, tmp);
          gimplify_and_add (tmp, pre_p);
        }
 
       if (!result)
        {
-         tmp = build (LABEL_EXPR, void_type_node, lab_over);
+         tmp = build1 (LABEL_EXPR, void_type_node, lab_over);
          gimplify_and_add (tmp, pre_p);
        }
     }
@@ -6580,17 +7339,17 @@ sh_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p,
   tmp = std_gimplify_va_arg_expr (valist, type, pre_p, NULL);
   if (result)
     {
-      tmp = build (MODIFY_EXPR, void_type_node, result, tmp);
+      tmp = build2 (GIMPLE_MODIFY_STMT, void_type_node, result, tmp);
       gimplify_and_add (tmp, pre_p);
 
-      tmp = build (LABEL_EXPR, void_type_node, lab_over);
+      tmp = build1 (LABEL_EXPR, void_type_node, lab_over);
       gimplify_and_add (tmp, pre_p);
     }
   else
     result = tmp;
 
   if (pass_by_ref)
-    result = build_fold_indirect_ref (result);
+    result = build_va_arg_indirect_ref (result);
 
   return result;
 }
@@ -6668,6 +7427,30 @@ sh_callee_copies (CUMULATIVE_ARGS *cum, enum machine_mode mode,
              % SH_MIN_ALIGN_FOR_CALLEE_COPY == 0));
 }
 
+static int
+sh_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                     tree type, bool named ATTRIBUTE_UNUSED)
+{
+  int words = 0;
+
+  if (!TARGET_SH5
+      && PASS_IN_REG_P (*cum, mode, type)
+      && !(TARGET_SH4 || TARGET_SH2A_DOUBLE)
+      && (ROUND_REG (*cum, mode)
+         + (mode != BLKmode
+            ? ROUND_ADVANCE (GET_MODE_SIZE (mode))
+            : ROUND_ADVANCE (int_size_in_bytes (type)))
+         > NPARM_REGS (mode)))
+    words = NPARM_REGS (mode) - ROUND_REG (*cum, mode);
+
+  else if (!TARGET_SHCOMPACT
+          && SH5_WOULD_BE_PARTIAL_NREGS (*cum, mode, type, named))
+    words = NPARM_REGS (SImode) - cum->arg_count[SH_ARG_INT];
+
+  return words * UNITS_PER_WORD;
+}
+
+
 /* Define where to put the arguments to a function.
    Value is zero to push the argument on the stack,
    or a hard register in which to store the argument.
@@ -6962,8 +7745,7 @@ sh_setup_incoming_varargs (CUMULATIVE_ARGS *ca,
                           int *pretend_arg_size,
                           int second_time ATTRIBUTE_UNUSED)
 {
-  if (! current_function_stdarg)
-    abort ();
+  gcc_assert (current_function_stdarg);
   if (TARGET_VARARGS_PRETEND_ARGS (current_function_decl))
     {
       int named_parm_regs, anon_parm_regs;
@@ -7025,7 +7807,7 @@ initial_elimination_offset (int from, int to)
 
   total_saved_regs_space = regs_saved + regs_saved_rounding;
 
-  if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+  if (from == ARG_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
     return total_saved_regs_space + total_auto_space
       + current_function_args_info.byref_regs * 8;
 
@@ -7034,79 +7816,109 @@ initial_elimination_offset (int from, int to)
       + current_function_args_info.byref_regs * 8;
 
   /* Initial gap between fp and sp is 0.  */
-  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+  if (from == HARD_FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
     return 0;
 
-  if (from == RETURN_ADDRESS_POINTER_REGNUM
-      && (to == FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM))
-    {
-      if (TARGET_SH5)
-       {
-         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;
-
-         /* If it wasn't saved, there's not much we can do.  */
-         if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
-           return n;
+  if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+    return rounded_frame_size (0);
 
-         target_flags = copy_flags;
+  if (from == FRAME_POINTER_REGNUM && to == HARD_FRAME_POINTER_REGNUM)
+    return rounded_frame_size (0);
 
-         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
-       return total_auto_space;
+  gcc_assert (from == RETURN_ADDRESS_POINTER_REGNUM
+             && (to == HARD_FRAME_POINTER_REGNUM
+                 || to == STACK_POINTER_REGNUM));
+  if (TARGET_SH5)
+    {
+      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;
+      
+      /* If it wasn't saved, there's not much we can do.  */
+      if (! TEST_HARD_REG_BIT (live_regs_mask, pr_reg))
+       return n;
+      
+      target_flags = copy_flags;
+      
+      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;
+         }
+      gcc_unreachable ();
     }
-
-  abort ();
+  else
+    return total_auto_space;
 }
 \f
-/* Handle machine specific pragmas to be semi-compatible with Renesas
-   compiler.  */
-
-void
-sh_pr_interrupt (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
-{
-  pragma_interrupt = 1;
-}
-
-void
-sh_pr_trapa (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
-{
-  pragma_interrupt = pragma_trapa = 1;
-}
-
-void
-sh_pr_nosave_low_regs (struct cpp_reader *pfile ATTRIBUTE_UNUSED)
-{
-  pragma_nosave_low_regs = 1;
-}
-
-/* Generate 'handle_interrupt' attribute for decls */
-
+/* Insert any deferred function attributes from earlier pragmas.  */
 static void
 sh_insert_attributes (tree node, tree *attributes)
 {
-  if (! pragma_interrupt
-      || TREE_CODE (node) != FUNCTION_DECL)
+  tree attrs;
+
+  if (TREE_CODE (node) != FUNCTION_DECL)
     return;
 
   /* We are only interested in fields.  */
   if (!DECL_P (node))
     return;
 
-  /* Add a 'handle_interrupt' attribute.  */
-  * attributes = tree_cons (get_identifier ("interrupt_handler"), NULL, * attributes);
+  /* Append the attributes to the deferred attributes.  */
+  *sh_deferred_function_attributes_tail = *attributes;
+  attrs = sh_deferred_function_attributes;
+  if (!attrs)
+    return;
+
+  /* Some attributes imply or require the interrupt attribute.  */
+  if (!lookup_attribute ("interrupt_handler", attrs)
+      && !lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (node)))
+    {
+      /* If we have a trapa_handler, but no interrupt_handler attribute,
+        insert an interrupt_handler attribute.  */
+      if (lookup_attribute ("trapa_handler", attrs) != NULL_TREE)
+       /* We can't use sh_pr_interrupt here because that's not in the
+          java frontend.  */
+       attrs
+         = tree_cons (get_identifier("interrupt_handler"), NULL_TREE, attrs);
+      /* However, for sp_switch, trap_exit and nosave_low_regs, if the
+        interrupt attribute is missing, we ignore the attribute and warn.  */
+      else if (lookup_attribute ("sp_switch", attrs)
+              || lookup_attribute ("trap_exit", attrs)
+              || lookup_attribute ("nosave_low_regs", attrs))
+       {
+         tree *tail;
+
+         for (tail = attributes; attrs; attrs = TREE_CHAIN (attrs))
+           {
+             if (is_attribute_p ("sp_switch", TREE_PURPOSE (attrs))
+                 || is_attribute_p ("trap_exit", TREE_PURPOSE (attrs))
+                 || is_attribute_p ("nosave_low_regs", TREE_PURPOSE (attrs)))
+               warning (OPT_Wattributes,
+                        "%qs attribute only applies to interrupt functions",
+                        IDENTIFIER_POINTER (TREE_PURPOSE (attrs)));
+             else
+               {
+                 *tail = tree_cons (TREE_PURPOSE (attrs), NULL_TREE,
+                                    NULL_TREE);
+                 tail = &TREE_CHAIN (*tail);
+               }
+           }
+         attrs = *attributes;
+       }
+    }
+
+  /* Install the processed list.  */
+  *attributes = attrs;
+
+  /* Clear deferred attributes.  */
+  sh_deferred_function_attributes = NULL_TREE;
+  sh_deferred_function_attributes_tail = &sh_deferred_function_attributes;
 
   return;
 }
@@ -7115,12 +7927,21 @@ sh_insert_attributes (tree node, tree *attributes)
 
    interrupt_handler -- specifies this function is an interrupt handler.
 
+   trapa_handler - like above, but don't save all registers.
+
    sp_switch -- specifies an alternate stack for an interrupt handler
    to run on.
 
    trap_exit -- use a trapa to exit an interrupt function instead of
    an rte instruction.
 
+   nosave_low_regs - don't save r0..r7 in an interrupt handler.
+     This is useful on the SH3 and upwards,
+     which has a separate set of low regs for User and Supervisor modes.
+     This should only be used for the lowest level of interrupts.  Higher levels
+     of interrupts must save the registers in case they themselves are
+     interrupted.
+
    renesas -- use Renesas calling/layout conventions (functions and
    structures).
 
@@ -7133,6 +7954,8 @@ const struct attribute_spec sh_attribute_table[] =
   { "sp_switch",         1, 1, true,  false, false, sh_handle_sp_switch_attribute },
   { "trap_exit",         1, 1, true,  false, false, sh_handle_trap_exit_attribute },
   { "renesas",           0, 0, false, true, false, sh_handle_renesas_attribute },
+  { "trapa_handler",     0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
+  { "nosave_low_regs",   0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
 #ifdef SYMBIAN
   /* Symbian support adds three new attributes:
      dllexport - for exporting a function/variable that will live in a dll
@@ -7157,7 +7980,7 @@ sh_handle_interrupt_handler_attribute (tree *node, tree name,
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      warning ("%qs attribute only applies to functions",
+      warning (OPT_Wattributes, "%qs attribute only applies to functions",
               IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
@@ -7178,29 +8001,17 @@ sh_handle_sp_switch_attribute (tree *node, tree name, tree args,
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      warning ("%qs attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
-      *no_add_attrs = true;
-    }
-  else if (!pragma_interrupt)
-    {
-      /* The sp_switch attribute only has meaning for interrupt functions.  */
-      warning ("%qs attribute only applies to interrupt functions",
+      warning (OPT_Wattributes, "%qs attribute only applies to functions",
               IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
   else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
     {
       /* The argument must be a constant string.  */
-      warning ("%qs attribute argument not a string constant",
+      warning (OPT_Wattributes, "%qs attribute argument not a string constant",
               IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
-  else
-    {
-      const char *s = ggc_strdup (TREE_STRING_POINTER (TREE_VALUE (args)));
-      sp_switch = gen_rtx_SYMBOL_REF (VOIDmode, s);
-    }
 
   return NULL_TREE;
 }
@@ -7213,28 +8024,19 @@ sh_handle_trap_exit_attribute (tree *node, tree name, tree args,
 {
   if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      warning ("%qs attribute only applies to functions",
-              IDENTIFIER_POINTER (name));
-      *no_add_attrs = true;
-    }
-  else if (!pragma_interrupt)
-    {
-      /* The trap_exit attribute only has meaning for interrupt functions.  */
-      warning ("%qs attribute only applies to interrupt functions",
+      warning (OPT_Wattributes, "%qs attribute only applies to functions",
               IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
+  /* The argument specifies a trap number to be used in a trapa instruction
+     at function exit (instead of an rte instruction).  */
   else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
     {
       /* The argument must be a constant integer.  */
-      warning ("%qs attribute argument not an integer constant",
-              IDENTIFIER_POINTER (name));
+      warning (OPT_Wattributes, "%qs attribute argument not an "
+              "integer constant", IDENTIFIER_POINTER (name));
       *no_add_attrs = true;
     }
-  else
-    {
-      trap_exit = TREE_INT_CST_LOW (TREE_VALUE (args));
-    }
 
   return NULL_TREE;
 }
@@ -7259,6 +8061,8 @@ sh_attr_renesas_p (tree td)
     return 0;
   if (DECL_P (td))
     td = TREE_TYPE (td);
+  if (td == error_mark_node)
+    return 0;
   return (lookup_attribute ("renesas", TYPE_ATTRIBUTES (td))
          != NULL_TREE);
 }
@@ -7279,88 +8083,20 @@ sh_cfun_interrupt_handler_p (void)
          != NULL_TREE);
 }
 
-/* ??? target_switches in toplev.c is static, hence we have to duplicate it.  */
-static const struct
-{
-  const char *const name;
-  const int value;
-  const char *const description;
-}
-sh_target_switches[] = TARGET_SWITCHES;
-#define target_switches sh_target_switches
-
-/* Like default_pch_valid_p, but take flag_mask into account.  */
-const char *
-sh_pch_valid_p (const void *data_p, size_t len)
-{
-  const char *data = (const char *)data_p;
-  const char *flag_that_differs = NULL;
-  size_t i;
-  int old_flags;
-  int flag_mask
-    = (SH1_BIT | SH2_BIT | SH3_BIT | SH_E_BIT | HARD_SH4_BIT | FPU_SINGLE_BIT
-       | SH4_BIT | HITACHI_BIT | LITTLE_ENDIAN_BIT);
-
-  /* -fpic and -fpie also usually make a PCH invalid.  */
-  if (data[0] != flag_pic)
-    return _("created and used with different settings of -fpic");
-  if (data[1] != flag_pie)
-    return _("created and used with different settings of -fpie");
-  data += 2;
-
-  /* Check target_flags.  */
-  memcpy (&old_flags, data, sizeof (target_flags));
-  if (((old_flags ^ target_flags) & flag_mask) != 0)
-    {
-      for (i = 0; i < ARRAY_SIZE (target_switches); i++)
-       {
-         int bits;
-
-         bits = target_switches[i].value;
-         if (bits < 0)
-           bits = -bits;
-         bits &= flag_mask;
-         if ((target_flags & bits) != (old_flags & bits))
-           {
-             flag_that_differs = target_switches[i].name;
-             goto make_message;
-           }
-       }
-      abort ();
-    }
-  data += sizeof (target_flags);
-  len -= sizeof (target_flags);
-
-  /* Check string options.  */
-#ifdef TARGET_OPTIONS
-  for (i = 0; i < ARRAY_SIZE (target_options); i++)
-    {
-      const char *str = *target_options[i].variable;
-      size_t l;
-      if (! str)
-       str = "";
-      l = strlen (str) + 1;
-      if (len < l || memcmp (data, str, l) != 0)
-       {
-         flag_that_differs = target_options[i].prefix;
-         goto make_message;
-       }
-      data += l;
-      len -= l;
-    }
-#endif
+/* Implement TARGET_CHECK_PCH_TARGET_FLAGS.  */
 
+static const char *
+sh_check_pch_target_flags (int old_flags)
+{
+  if ((old_flags ^ target_flags) & (MASK_SH1 | MASK_SH2 | MASK_SH3
+                                   | MASK_SH_E | MASK_HARD_SH4
+                                   | MASK_FPU_SINGLE | MASK_SH4))
+    return _("created and used with different architectures / ABIs");
+  if ((old_flags ^ target_flags) & MASK_HITACHI)
+    return _("created and used with different ABIs");
+  if ((old_flags ^ target_flags) & MASK_LITTLE_ENDIAN)
+    return _("created and used with different endianness");
   return NULL;
-
- make_message:
-  {
-    char *r;
-    asprintf (&r, _("created and used with differing settings of '-m%s'"),
-                 flag_that_differs);
-    if (r == NULL)
-      return _("out of memory");
-    return r;
-  }
 }
 \f
 /* Predicates used by the templates.  */
@@ -7381,215 +8117,10 @@ system_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
   return 0;
 }
 
-/* Returns 1 if OP can be source of a simple move operation.
-   Same as general_operand, but a LABEL_REF is valid, PRE_DEC is
-   invalid as are subregs of system registers.  */
+/* Nonzero if OP is a floating point value with value 0.0.  */
 
 int
-general_movsrc_operand (rtx op, enum machine_mode mode)
-{
-  if (GET_CODE (op) == MEM)
-    {
-      rtx inside = XEXP (op, 0);
-      if (GET_CODE (inside) == CONST)
-       inside = XEXP (inside, 0);
-
-      if (GET_CODE (inside) == LABEL_REF)
-       return 1;
-
-      if (GET_CODE (inside) == PLUS
-         && GET_CODE (XEXP (inside, 0)) == LABEL_REF
-         && GET_CODE (XEXP (inside, 1)) == CONST_INT)
-       return 1;
-
-      /* Only post inc allowed.  */
-      if (GET_CODE (inside) == PRE_DEC)
-       return 0;
-    }
-
-  if ((mode == QImode || mode == HImode)
-      && (GET_CODE (op) == SUBREG
-         && GET_CODE (XEXP (op, 0)) == REG
-         && system_reg_operand (XEXP (op, 0), mode)))
-    return 0;
-
-  return general_operand (op, mode);
-}
-
-/* Returns 1 if OP can be a destination of a move.
-   Same as general_operand, but no preinc allowed.  */
-
-int
-general_movdst_operand (rtx op, enum machine_mode mode)
-{
-  /* Only pre dec allowed.  */
-  if (GET_CODE (op) == MEM && GET_CODE (XEXP (op, 0)) == POST_INC)
-    return 0;
-
-  return general_operand (op, mode);
-}
-
-/* Returns 1 if OP is a normal arithmetic register.  */
-
-int
-arith_reg_operand (rtx op, enum machine_mode mode)
-{
-  if (register_operand (op, mode))
-    {
-      int regno;
-
-      if (GET_CODE (op) == REG)
-       regno = REGNO (op);
-      else if (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG)
-       regno = REGNO (SUBREG_REG (op));
-      else
-       return 1;
-
-      return (regno != T_REG && regno != PR_REG
-             && ! TARGET_REGISTER_P (regno)
-             && (regno != FPUL_REG || TARGET_SH4)
-             && regno != MACH_REG && regno != MACL_REG);
-    }
-  return 0;
-}
-
-/* Like above, but for DImode destinations: forbid paradoxical DImode subregs,
-   because this would lead to missing sign extensions when truncating from
-   DImode to SImode.  */
-int
-arith_reg_dest (rtx op, enum machine_mode mode)
-{
-  if (mode == DImode && GET_CODE (op) == SUBREG
-      && GET_MODE_SIZE (GET_MODE (SUBREG_REG (op))) < 8)
-    return 0;
-  return arith_reg_operand (op, mode);
-}
-
-int
-int_gpr_dest (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  enum machine_mode op_mode = GET_MODE (op);
-
-  if (GET_MODE_CLASS (op_mode) != MODE_INT
-      || GET_MODE_SIZE (op_mode) >= UNITS_PER_WORD)
-    return 0;
-  if (! reload_completed)
-    return 0;
-  return true_regnum (op) <= LAST_GENERAL_REG;
-}
-
-int
-fp_arith_reg_operand (rtx op, enum machine_mode mode)
-{
-  if (register_operand (op, mode))
-    {
-      int regno;
-
-      if (GET_CODE (op) == REG)
-       regno = REGNO (op);
-      else if (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG)
-       regno = REGNO (SUBREG_REG (op));
-      else
-       return 1;
-
-      return (regno >= FIRST_PSEUDO_REGISTER
-             || FP_REGISTER_P (regno));
-    }
-  return 0;
-}
-
-/* Returns 1 if OP is a valid source operand for an arithmetic insn.  */
-
-int
-arith_operand (rtx op, enum machine_mode mode)
-{
-  if (arith_reg_operand (op, mode))
-    return 1;
-
-  if (TARGET_SHMEDIA)
-    {
-      /* FIXME: We should be checking whether the CONST_INT fits in a
-        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_C16 (op))
-       return 1;
-      else
-       return 0;
-    }
-  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I08 (INTVAL (op)))
-    return 1;
-
-  return 0;
-}
-
-/* Returns 1 if OP is a valid source operand for a compare insn.  */
-
-int
-arith_reg_or_0_operand (rtx op, enum machine_mode mode)
-{
-  if (arith_reg_operand (op, mode))
-    return 1;
-
-  if (EXTRA_CONSTRAINT_Z (op))
-    return 1;
-
-  return 0;
-}
-
-/* Return 1 if OP is a valid source operand for an SHmedia operation
-   that takes either a register or a 6-bit immediate.  */
-
-int
-shmedia_6bit_operand (rtx op, enum machine_mode mode)
-{
-  return (arith_reg_operand (op, mode)
-         || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I06 (INTVAL (op))));
-}
-
-/* Returns 1 if OP is a valid source operand for a logical operation.  */
-
-int
-logical_operand (rtx op, enum machine_mode mode)
-{
-  if (arith_reg_operand (op, mode))
-    return 1;
-
-  if (TARGET_SHMEDIA)
-    {
-      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_K08 (INTVAL (op)))
-    return 1;
-
-  return 0;
-}
-
-int
-and_operand (rtx op, enum machine_mode mode)
-{
-  if (logical_operand (op, mode))
-    return 1;
-
-  /* Check mshflo.l / mshflhi.l opportunities.  */
-  if (TARGET_SHMEDIA
-      && mode == DImode
-      && GET_CODE (op) == CONST_INT
-      && CONST_OK_FOR_J16 (INTVAL (op)))
-    return 1;
-
-  return 0;
-}
-
-/* Nonzero if OP is a floating point value with value 0.0.  */
-
-int
-fp_zero_operand (rtx op)
+fp_zero_operand (rtx op)
 {
   REAL_VALUE_TYPE r;
 
@@ -7634,33 +8165,6 @@ tertiary_reload_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
   return code == MEM || (TARGET_SH4 && code == CONST_DOUBLE);
 }
 
-int
-fpscr_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  return (GET_CODE (op) == REG
-         && (REGNO (op) == FPSCR_REG
-             || (REGNO (op) >= FIRST_PSEUDO_REGISTER
-                 && !(reload_in_progress || reload_completed)))
-         && GET_MODE (op) == PSImode);
-}
-
-int
-fpul_operand (rtx op, enum machine_mode mode)
-{
-  if (TARGET_SHMEDIA)
-    return fp_arith_reg_operand (op, mode);
-
-  return (GET_CODE (op) == REG
-         && (REGNO (op) == FPUL_REG || REGNO (op) >= FIRST_PSEUDO_REGISTER)
-         && GET_MODE (op) == mode);
-}
-
-int
-symbol_ref_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  return (GET_CODE (op) == SYMBOL_REF);
-}
-
 /* Return the TLS type for TLS symbols, 0 for otherwise.  */
 int
 tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
@@ -7669,301 +8173,6 @@ tls_symbolic_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
     return 0;
   return SYMBOL_REF_TLS_MODEL (op);
 }
-
-int
-commutative_float_operator (rtx op, enum machine_mode mode)
-{
-  if (GET_MODE (op) != mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case PLUS:
-    case MULT:
-      return 1;
-    default:
-      break;
-    }
-  return 0;
-}
-
-int
-noncommutative_float_operator (rtx op, enum machine_mode mode)
-{
-  if (GET_MODE (op) != mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case MINUS:
-    case DIV:
-      return 1;
-    default:
-      break;
-    }
-  return 0;
-}
-
-int
-unary_float_operator (rtx op, enum machine_mode mode)
-{
-  if (GET_MODE (op) != mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case ABS:
-    case NEG:
-    case SQRT:
-      return 1;
-    default:
-      break;
-    }
-  return 0;
-}
-
-int
-binary_float_operator (rtx op, enum machine_mode mode)
-{
-  if (GET_MODE (op) != mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case PLUS:
-    case MINUS:
-    case MULT:
-    case DIV:
-      return 1;
-    default:
-      break;
-    }
-  return 0;
-}
-
-int
-binary_logical_operator (rtx op, enum machine_mode mode)
-{
-  if (GET_MODE (op) != mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case IOR:
-    case AND:
-    case XOR:
-      return 1;
-    default:
-      break;
-    }
-  return 0;
-}
-
-int
-equality_comparison_operator (rtx op, enum machine_mode mode)
-{
-  return ((mode == VOIDmode || GET_MODE (op) == mode)
-         && (GET_CODE (op) == EQ || GET_CODE (op) == NE));
-}
-
-int
-greater_comparison_operator (rtx op, enum machine_mode mode)
-{
-  if (mode != VOIDmode && GET_MODE (op) == mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case GT:
-    case GE:
-    case GTU:
-    case GEU:
-      return 1;
-    default:
-      return 0;
-    }
-}
-
-int
-less_comparison_operator (rtx op, enum machine_mode mode)
-{
-  if (mode != VOIDmode && GET_MODE (op) == mode)
-    return 0;
-  switch (GET_CODE (op))
-    {
-    case LT:
-    case LE:
-    case LTU:
-    case LEU:
-      return 1;
-    default:
-      return 0;
-    }
-}
-
-/* Accept pseudos and branch target registers.  */
-int
-target_reg_operand (rtx op, enum machine_mode mode)
-{
-  if (mode != DImode
-      || GET_MODE (op) != DImode)
-    return 0;
-
-  if (GET_CODE (op) == SUBREG)
-    op = XEXP (op, 0);
-
-  if (GET_CODE (op) != REG)
-    return 0;
-
-  /* We must protect ourselves from matching pseudos that are virtual
-     register, because they will eventually be replaced with hardware
-     registers that aren't branch-target registers.  */
-  if (REGNO (op) > LAST_VIRTUAL_REGISTER
-      || TARGET_REGISTER_P (REGNO (op)))
-    return 1;
-
-  return 0;
-}
-
-/* Same as target_reg_operand, except that label_refs and symbol_refs
-   are accepted before reload.  */
-int
-target_operand (rtx op, enum machine_mode mode)
-{
-  if (mode != DImode)
-    return 0;
-
-  if ((GET_MODE (op) == DImode || GET_MODE (op) == VOIDmode)
-      && EXTRA_CONSTRAINT_Csy (op))
-    return ! reload_completed;
-
-  return target_reg_operand (op, mode);
-}
-
-int
-mextr_bit_offset (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  HOST_WIDE_INT i;
-
-  if (GET_CODE (op) != CONST_INT)
-    return 0;
-  i = INTVAL (op);
-  return i >= 1 * 8 && i <= 7 * 8 && (i & 7) == 0;
-}
-
-int
-extend_reg_operand (rtx op, enum machine_mode mode)
-{
-  return (GET_CODE (op) == TRUNCATE
-         ? arith_operand
-         : arith_reg_operand) (op, mode);
-}
-
-int
-trunc_hi_operand (rtx op, enum machine_mode mode)
-{
-  enum machine_mode op_mode = GET_MODE (op);
-
-  if (op_mode != SImode && op_mode != DImode
-      && op_mode != V4HImode && op_mode != V2SImode)
-    return 0;
-  return extend_reg_operand (op, mode);
-}
-
-int
-extend_reg_or_0_operand (rtx op, enum machine_mode mode)
-{
-  return (GET_CODE (op) == TRUNCATE
-         ? arith_operand
-         : arith_reg_or_0_operand) (op, mode);
-}
-
-int
-general_extend_operand (rtx op, enum machine_mode mode)
-{
-  return (GET_CODE (op) == TRUNCATE
-         ? arith_operand
-         : nonimmediate_operand) (op, mode);
-}
-
-int
-inqhi_operand (rtx op, enum machine_mode mode)
-{
-  if (GET_CODE (op) != TRUNCATE || mode != GET_MODE (op))
-    return 0;
-  op = XEXP (op, 0);
-  /* Can't use true_regnum here because copy_cost wants to know about
-     SECONDARY_INPUT_RELOAD_CLASS.  */
-  return GET_CODE (op) == REG && FP_REGISTER_P (REGNO (op));
-}
-
-int
-sh_rep_vec (rtx v, enum machine_mode mode)
-{
-  int i;
-  rtx x, y;
-
-  if ((GET_CODE (v) != CONST_VECTOR && GET_CODE (v) != PARALLEL)
-      || (GET_MODE (v) != mode && mode != VOIDmode))
-    return 0;
-  i = XVECLEN (v, 0) - 2;
-  x = XVECEXP (v, 0, i + 1);
-  if (GET_MODE_UNIT_SIZE (mode) == 1)
-    {
-      y = XVECEXP (v, 0, i);
-      for (i -= 2; i >= 0; i -= 2)
-       if (! rtx_equal_p (XVECEXP (v, 0, i + 1), x)
-           || ! rtx_equal_p (XVECEXP (v, 0, i), y))
-         return 0;
-    }
-  else
-    for (; i >= 0; i--)
-      if (XVECEXP (v, 0, i) != x)
-       return 0;
-  return 1;
-}
-
-/* Determine if V is a constant vector matching MODE with only one element
-   that is not a sign extension.  Two byte-sized elements count as one.  */
-int
-sh_1el_vec (rtx v, enum machine_mode mode)
-{
-  int unit_size;
-  int i, last, least, sign_ix;
-  rtx sign;
-
-  if (GET_CODE (v) != CONST_VECTOR
-      || (GET_MODE (v) != mode && mode != VOIDmode))
-    return 0;
-  /* Determine numbers of last and of least significant elements.  */
-  last = XVECLEN (v, 0) - 1;
-  least = TARGET_LITTLE_ENDIAN ? 0 : last;
-  if (GET_CODE (XVECEXP (v, 0, least)) != CONST_INT)
-    return 0;
-  sign_ix = least;
-  if (GET_MODE_UNIT_SIZE (mode) == 1)
-    sign_ix = TARGET_LITTLE_ENDIAN ? 1 : last - 1;
-  if (GET_CODE (XVECEXP (v, 0, sign_ix)) != CONST_INT)
-    return 0;
-  unit_size = GET_MODE_UNIT_SIZE (GET_MODE (v));
-  sign = (INTVAL (XVECEXP (v, 0, sign_ix)) >> (unit_size * BITS_PER_UNIT - 1)
-         ? constm1_rtx : const0_rtx);
-  i = XVECLEN (v, 0) - 1;
-  do
-    if (i != least && i != sign_ix && XVECEXP (v, 0, i) != sign)
-      return 0;
-  while (--i);
-  return 1;
-}
-
-int
-sh_const_vec (rtx v, enum machine_mode mode)
-{
-  int i;
-
-  if (GET_CODE (v) != CONST_VECTOR
-      || (GET_MODE (v) != mode && mode != VOIDmode))
-    return 0;
-  i = XVECLEN (v, 0) - 1;
-  for (; i >= 0; i--)
-    if (GET_CODE (XVECEXP (v, 0, i)) != CONST_INT)
-      return 0;
-  return 1;
-}
 \f
 /* Return the destination address of a branch.  */
 
@@ -8091,6 +8300,45 @@ get_fpscr_rtx (void)
   return fpscr_rtx;
 }
 
+static GTY(()) tree fpscr_values;
+
+static void
+emit_fpu_switch (rtx scratch, int index)
+{
+  rtx dst, src;
+
+  if (fpscr_values == NULL)
+    {
+      tree t;
+
+      t = build_index_type (integer_one_node);
+      t = build_array_type (integer_type_node, t);
+      t = build_decl (VAR_DECL, get_identifier ("__fpscr_values"), t);
+      DECL_ARTIFICIAL (t) = 1;
+      DECL_IGNORED_P (t) = 1;
+      DECL_EXTERNAL (t) = 1;
+      TREE_STATIC (t) = 1;
+      TREE_PUBLIC (t) = 1;
+      TREE_USED (t) = 1;
+
+      fpscr_values = t;
+    }
+
+  src = DECL_RTL (fpscr_values);
+  if (!can_create_pseudo_p ())
+    {
+      emit_move_insn (scratch, XEXP (src, 0));
+      if (index != 0)
+       emit_insn (gen_addsi3 (scratch, scratch, GEN_INT (index * 4)));
+      src = adjust_automodify_address (src, PSImode, scratch, index * 4);
+    }
+  else
+    src = adjust_address (src, PSImode, index * 4);
+
+  dst = get_fpscr_rtx ();
+  emit_move_insn (dst, src);
+}
+
 void
 emit_sf_insn (rtx pat)
 {
@@ -8129,86 +8377,6 @@ expand_df_binop (rtx (*fun) (rtx, rtx, rtx, rtx), rtx *operands)
                        get_fpscr_rtx ()));
 }
 \f
-/* ??? gcc does flow analysis strictly after common subexpression
-   elimination.  As a result, common subexpression elimination fails
-   when there are some intervening statements setting the same register.
-   If we did nothing about this, this would hurt the precision switching
-   for SH4 badly.  There is some cse after reload, but it is unable to
-   undo the extra register pressure from the unused instructions, and
-   it cannot remove auto-increment loads.
-
-   A C code example that shows this flow/cse weakness for (at least) SH
-   and sparc (as of gcc ss-970706) is this:
-
-double
-f(double a)
-{
-  double d;
-  d = 0.1;
-  a += d;
-  d = 1.1;
-  d = 0.1;
-  a *= d;
-  return a;
-}
-
-   So we add another pass before common subexpression elimination, to
-   remove assignments that are dead due to a following assignment in the
-   same basic block.  */
-
-static void
-mark_use (rtx x, rtx *reg_set_block)
-{
-  enum rtx_code code;
-
-  if (! x)
-    return;
-  code = GET_CODE (x);
-  switch (code)
-    {
-    case REG:
-      {
-       int regno = REGNO (x);
-       int nregs = (regno < FIRST_PSEUDO_REGISTER
-                    ? HARD_REGNO_NREGS (regno, GET_MODE (x))
-                    : 1);
-       do
-         {
-           reg_set_block[regno + nregs - 1] = 0;
-         }
-       while (--nregs);
-       break;
-      }
-    case SET:
-      {
-       rtx dest = SET_DEST (x);
-
-       if (GET_CODE (dest) == SUBREG)
-         dest = SUBREG_REG (dest);
-       if (GET_CODE (dest) != REG)
-         mark_use (dest, reg_set_block);
-       mark_use (SET_SRC (x), reg_set_block);
-       break;
-      }
-    case CLOBBER:
-      break;
-    default:
-      {
-       const char *fmt = GET_RTX_FORMAT (code);
-       int i, j;
-       for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--)
-         {
-           if (fmt[i] == 'e')
-             mark_use (XEXP (x, i), reg_set_block);
-           else if (fmt[i] == 'E')
-             for (j = XVECLEN (x, i) - 1; j >= 0; j--)
-               mark_use (XVECEXP (x, i, j), reg_set_block);
-         }
-       break;
-      }
-    }
-}
-\f
 static rtx get_free_reg (HARD_REG_SET);
 
 /* This function returns a register to use to load the address to load
@@ -8233,10 +8401,8 @@ get_free_reg (HARD_REG_SET regs_live)
 
   /* Hard reg 1 is live; since this is a SMALL_REGISTER_CLASSES target,
      there shouldn't be anything but a jump before the function end.  */
-  if (! TEST_HARD_REG_BIT (regs_live, 7))
-    return gen_rtx_REG (Pmode, 7);
-
-  abort ();
+  gcc_assert (!TEST_HARD_REG_BIT (regs_live, 7));
+  return gen_rtx_REG (Pmode, 7);
 }
 
 /* This function will set the fpscr from memory.
@@ -8245,12 +8411,11 @@ void
 fpscr_set_from_mem (int mode, HARD_REG_SET regs_live)
 {
   enum attr_fp_mode fp_mode = mode;
-  rtx addr_reg = get_free_reg (regs_live);
+  enum attr_fp_mode norm_mode = ACTUAL_NORMAL_MODE (FP_MODE);
+  rtx addr_reg;
 
-  if (fp_mode == (enum attr_fp_mode) ACTUAL_NORMAL_MODE (FP_MODE))
-    emit_insn (gen_fpu_switch1 (addr_reg));
-  else
-    emit_insn (gen_fpu_switch0 (addr_reg));
+  addr_reg = !can_create_pseudo_p () ? get_free_reg (regs_live) : NULL_RTX;
+  emit_fpu_switch (addr_reg, fp_mode == norm_mode);
 }
 
 /* Is the given character a logical line separator for the assembler?  */
@@ -8298,7 +8463,7 @@ sh_insn_length_adjustment (rtx insn)
        template = XSTR (body, 0);
       else if (asm_noperands (body) >= 0)
        template
-         = decode_asm_operands (body, NULL, NULL, NULL, NULL);
+         = decode_asm_operands (body, NULL, NULL, NULL, NULL, NULL);
       else
        return 0;
       do
@@ -8521,7 +8686,7 @@ sh_hard_regno_rename_ok (unsigned int old_reg ATTRIBUTE_UNUSED,
      saved by the prologue, even if they would normally be
      call-clobbered.  */
 
-  if (sh_cfun_interrupt_handler_p () && !regs_ever_live[new_reg])
+  if (sh_cfun_interrupt_handler_p () && !df_regs_ever_live_p (new_reg))
     return 0;
 
   return 1;
@@ -8544,31 +8709,87 @@ sh_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx dep_insn, int cost)
       /* On SHmedia, if the dependence is an anti-dependence or
          output-dependence, there is no cost.  */
       if (REG_NOTE_KIND (link) != 0)
-        cost = 0;
+       {
+         /* However, dependencies between target register loads and
+            uses of the register in a subsequent block that are separated
+            by a conditional branch are not modelled - we have to do with
+            the anti-dependency between the target register load and the
+            conditional branch that ends the current block.  */
+         if (REG_NOTE_KIND (link) == REG_DEP_ANTI
+             && GET_CODE (PATTERN (dep_insn)) == SET
+             && (get_attr_type (dep_insn) == TYPE_PT_MEDIA
+                 || get_attr_type (dep_insn) == TYPE_PTABS_MEDIA)
+             && get_attr_type (insn) == TYPE_CBRANCH_MEDIA)
+           {
+             int orig_cost = cost;
+             rtx note = find_reg_note (insn, REG_BR_PROB, 0);
+             rtx target = ((! note
+                            || INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
+                           ? insn : JUMP_LABEL (insn));
+             /* On the likely path, the branch costs 1, on the unlikely path,
+                it costs 3.  */
+             cost--;
+             do
+               target = next_active_insn (target);
+             while (target && ! flow_dependent_p (target, dep_insn)
+                    && --cost > 0);
+             /* If two branches are executed in immediate succession, with the
+                first branch properly predicted, this causes a stall at the
+                second branch, hence we won't need the target for the
+                second branch for two cycles after the launch of the first
+                branch.  */
+             if (cost > orig_cost - 2)
+               cost = orig_cost - 2;
+           }
+         else
+           cost = 0;
+       }
 
-      if (get_attr_is_mac_media (insn)
-          && get_attr_is_mac_media (dep_insn))
-        cost = 1;
+      else if (get_attr_is_mac_media (insn)
+              && get_attr_is_mac_media (dep_insn))
+       cost = 1;
+
+      else if (! reload_completed
+              && GET_CODE (PATTERN (insn)) == SET
+              && GET_CODE (SET_SRC (PATTERN (insn))) == FLOAT
+              && GET_CODE (PATTERN (dep_insn)) == SET
+              && fp_arith_reg_operand (SET_SRC (PATTERN (dep_insn)), VOIDmode)
+              && cost < 4)
+       cost = 4;
+      /* Schedule the ptabs for a casesi_jump_media in preference to stuff
+        that is needed at the target.  */
+      else if (get_attr_type (insn) == TYPE_JUMP_MEDIA
+              && ! flow_dependent_p (insn, dep_insn))
+       cost--;
     }
   else if (REG_NOTE_KIND (link) == 0)
     {
-      enum attr_type dep_type, type;
+      enum attr_type type;
+      rtx dep_set;
 
       if (recog_memoized (insn) < 0
          || recog_memoized (dep_insn) < 0)
        return cost;
 
-      dep_type = get_attr_type (dep_insn);
-      if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
-       cost--;
-      if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
-         && (type = get_attr_type (insn)) != TYPE_CALL
-         && type != TYPE_SFUNC)
-       cost--;
+      dep_set = single_set (dep_insn);
 
+      /* The latency that we specify in the scheduling description refers
+        to the actual output, not to an auto-increment register; for that,
+        the latency is one.  */
+      if (dep_set && MEM_P (SET_SRC (dep_set)) && cost > 1)
+       {
+         rtx set = single_set (insn);
+
+         if (set
+             && !reg_mentioned_p (SET_DEST (dep_set), SET_SRC (set))
+             && (!MEM_P (SET_DEST (set))
+                 || !reg_mentioned_p (SET_DEST (dep_set),
+                                      XEXP (SET_DEST (set), 0))))
+           cost = 1;
+       }
       /* The only input for a call that is timing-critical is the
         function's address.  */
-      if (GET_CODE(insn) == CALL_INSN)
+      if (GET_CODE (insn) == CALL_INSN)
        {
          rtx call = PATTERN (insn);
 
@@ -8577,13 +8798,19 @@ sh_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx dep_insn, int cost)
          if (GET_CODE (call) == SET)
            call = SET_SRC (call);
          if (GET_CODE (call) == CALL && GET_CODE (XEXP (call, 0)) == MEM
-             && ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn))
-           cost = 0;
+                 /* sibcalli_thunk uses a symbol_ref in an unspec.  */
+             && (GET_CODE (XEXP (XEXP (call, 0), 0)) == UNSPEC
+                 || ! reg_set_p (XEXP (XEXP (call, 0), 0), dep_insn)))
+           cost -= TARGET_SH4_300 ? 3 : 6;
        }
       /* Likewise, the most timing critical input for an sfuncs call
         is the function address.  However, sfuncs typically start
         using their arguments pretty quickly.
-        Assume a four cycle delay before they are needed.  */
+        Assume a four cycle delay for SH4 before they are needed.
+        Cached ST40-300 calls are quicker, so assume only a one
+        cycle delay there.
+        ??? Maybe we should encode the delays till input registers
+        are needed by sfuncs into the sfunc call insn.  */
       /* All sfunc calls are parallels with at least four components.
         Exploit this to avoid unnecessary calls to sfunc_uses_reg.  */
       else if (GET_CODE (PATTERN (insn)) == PARALLEL
@@ -8591,50 +8818,83 @@ sh_adjust_cost (rtx insn, rtx link ATTRIBUTE_UNUSED, rtx dep_insn, int cost)
               && (reg = sfunc_uses_reg (insn)))
        {
          if (! reg_set_p (reg, dep_insn))
-           cost -= 4;
-       }
-      /* When the preceding instruction loads the shift amount of
-        the following SHAD/SHLD, the latency of the load is increased
-        by 1 cycle.  */
-      else if (TARGET_SH4
-              && get_attr_type (insn) == TYPE_DYN_SHIFT
-              && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
-              && reg_overlap_mentioned_p (SET_DEST (PATTERN (dep_insn)),
-                                          XEXP (SET_SRC (single_set (insn)),
-                                                1)))
-       cost++;
-      /* When an LS group instruction with a latency of less than
-        3 cycles is followed by a double-precision floating-point
-        instruction, FIPR, or FTRV, the latency of the first
-        instruction is increased to 3 cycles.  */
-      else if (cost < 3
-              && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
-              && get_attr_dfp_comp (insn) == DFP_COMP_YES)
-       cost = 3;
-      /* The lsw register of a double-precision computation is ready one
-        cycle earlier.  */
-      else if (reload_completed
-              && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
-              && (use_pat = single_set (insn))
-              && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
-                                 SET_SRC (use_pat)))
-       cost -= 1;
-
-      if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
-         && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
-       cost -= 1;
+           cost -= TARGET_SH4_300 ? 1 : 4;
+       }
+      if (TARGET_HARD_SH4 && !TARGET_SH4_300)
+       {
+         enum attr_type dep_type = get_attr_type (dep_insn);
+
+         if (dep_type == TYPE_FLOAD || dep_type == TYPE_PCFLOAD)
+           cost--;
+         else if ((dep_type == TYPE_LOAD_SI || dep_type == TYPE_PCLOAD_SI)
+                  && (type = get_attr_type (insn)) != TYPE_CALL
+                  && type != TYPE_SFUNC)
+           cost--;
+         /* When the preceding instruction loads the shift amount of
+            the following SHAD/SHLD, the latency of the load is increased
+            by 1 cycle.  */
+         if (get_attr_type (insn) == TYPE_DYN_SHIFT
+             && get_attr_any_int_load (dep_insn) == ANY_INT_LOAD_YES
+             && reg_overlap_mentioned_p (SET_DEST (dep_set),
+                                         XEXP (SET_SRC (single_set (insn)),
+                                               1)))
+           cost++;
+         /* When an LS group instruction with a latency of less than
+            3 cycles is followed by a double-precision floating-point
+            instruction, FIPR, or FTRV, the latency of the first
+            instruction is increased to 3 cycles.  */
+         else if (cost < 3
+                  && get_attr_insn_class (dep_insn) == INSN_CLASS_LS_GROUP
+                  && get_attr_dfp_comp (insn) == DFP_COMP_YES)
+           cost = 3;
+         /* The lsw register of a double-precision computation is ready one
+            cycle earlier.  */
+         else if (reload_completed
+                  && get_attr_dfp_comp (dep_insn) == DFP_COMP_YES
+                  && (use_pat = single_set (insn))
+                  && ! regno_use_in (REGNO (SET_DEST (single_set (dep_insn))),
+                                     SET_SRC (use_pat)))
+           cost -= 1;
+
+         if (get_attr_any_fp_comp (dep_insn) == ANY_FP_COMP_YES
+             && get_attr_late_fp_use (insn) == LATE_FP_USE_YES)
+           cost -= 1;
+       }
+      else if (TARGET_SH4_300)
+       {
+         /* Stores need their input register two cycles later.  */
+         if (dep_set && cost >= 1
+             && ((type = get_attr_type (insn)) == TYPE_STORE
+                 || type == TYPE_PSTORE
+                 || type == TYPE_FSTORE || type == TYPE_MAC_MEM))
+           {
+             rtx set = single_set (insn);
+
+             if (!reg_mentioned_p (SET_SRC (set), XEXP (SET_DEST (set), 0))
+                 && rtx_equal_p (SET_SRC (set), SET_DEST (dep_set)))
+               {
+                 cost -= 2;
+                 /* But don't reduce the cost below 1 if the address depends
+                    on a side effect of dep_insn.  */
+                 if (cost < 1
+                     && modified_in_p (XEXP (SET_DEST (set), 0), dep_insn))
+                   cost = 1;
+               }
+           }
+       }
     }
   /* An anti-dependence penalty of two applies if the first insn is a double
      precision fadd / fsub / fmul.  */
-  else if (REG_NOTE_KIND (link) == REG_DEP_ANTI
+  else if (!TARGET_SH4_300
+          && REG_NOTE_KIND (link) == REG_DEP_ANTI
           && recog_memoized (dep_insn) >= 0
-          && get_attr_type (dep_insn) == TYPE_DFP_ARITH
+          && (get_attr_type (dep_insn) == TYPE_DFP_ARITH
+              || get_attr_type (dep_insn) == TYPE_DFP_MUL)
           /* A lot of alleged anti-flow dependences are fake,
              so check this one is real.  */
           && flow_dependent_p (dep_insn, insn))
     cost = 2;
 
-
   return cost;
 }
 
@@ -8659,14 +8919,39 @@ flow_dependent_p_1 (rtx x, rtx pat ATTRIBUTE_UNUSED, void *data)
     *pinsn = NULL_RTX;
 }
 
-/* For use by ALLOCATE_INITIAL_VALUE.  Note that sh.md contains some
+/* For use by sh_allocate_initial_value.  Note that sh.md contains some
    'special function' patterns (type sfunc) that clobber pr, but that
    do not look like function calls to leaf_function_p.  Hence we must
    do this extra check.  */
-int
+static int
 sh_pr_n_sets (void)
 {
-  return REG_N_SETS (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+  return DF_REG_DEF_COUNT (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+}
+
+/* Return where to allocate pseudo for a given hard register initial
+   value.  */
+static rtx
+sh_allocate_initial_value (rtx hard_reg)
+{
+  rtx x;
+
+  if (REGNO (hard_reg) == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG))
+    {
+      if (current_function_is_leaf
+         && ! sh_pr_n_sets ()
+         && ! (TARGET_SHCOMPACT
+               && ((current_function_args_info.call_cookie
+                    & ~ CALL_COOKIE_RET_TRAMP (1))
+                   || current_function_has_nonlocal_label)))
+       x = hard_reg;
+      else
+       x = gen_frame_mem (Pmode, return_address_pointer_rtx);
+    }
+  else
+    x = NULL_RTX;
+
+  return x;
 }
 
 /* This function returns "2" to indicate dual issue for the SH4
@@ -8736,11 +9021,11 @@ find_insn_regmode_weight (rtx insn, enum machine_mode mode)
 
 /* Calculate regmode weights for all insns of a basic block.  */
 static void
-find_regmode_weight (int b, enum machine_mode mode)
+find_regmode_weight (basic_block b, enum machine_mode mode)
 {
   rtx insn, next_tail, head, tail;
 
-  get_block_head_tail (b, &head, &tail);
+  get_ebb_head_tail (b, b, &head, &tail);
   next_tail = NEXT_INSN (tail);
 
   for (insn = head; insn != next_tail; insn = NEXT_INSN (insn))
@@ -8808,6 +9093,56 @@ ready_reorder (rtx *ready, int nready)
   SCHED_REORDER (ready, nready);
 }
 
+/* Count life regions of r0 for a block.  */
+static int
+find_r0_life_regions (basic_block b)
+{
+  rtx end, insn;
+  rtx pset;
+  rtx r0_reg;
+  int live;
+  int set;
+  int death = 0;
+
+  if (REGNO_REG_SET_P (df_get_live_in (b), R0_REG))
+    {
+      set = 1;
+      live = 1;
+    }
+  else
+    {
+      set = 0;
+      live = 0;
+    }
+
+  insn = BB_HEAD (b);
+  end = BB_END (b);
+  r0_reg = gen_rtx_REG (SImode, R0_REG);
+  while (1)
+    {
+      if (INSN_P (insn))
+       {
+         if (find_regno_note (insn, REG_DEAD, R0_REG))
+           {
+             death++;
+             live = 0;
+           }
+         if (!live
+             && (pset = single_set (insn))
+             && reg_overlap_mentioned_p (r0_reg, SET_DEST (pset))
+             && !find_regno_note (insn, REG_UNUSED, R0_REG))
+           {
+             set++;
+             live = 1;
+           }
+       }
+      if (insn == end)
+       break;
+      insn = NEXT_INSN (insn);
+    }
+  return set - death;
+}
+
 /* Calculate regmode weights for all insns of all basic block.  */
 static void
 sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
@@ -8818,11 +9153,14 @@ sh_md_init_global (FILE *dump ATTRIBUTE_UNUSED,
 
   regmode_weight[0] = (short *) xcalloc (old_max_uid, sizeof (short));
   regmode_weight[1] = (short *) xcalloc (old_max_uid, sizeof (short));
+  r0_life_regions = 0;
 
   FOR_EACH_BB_REVERSE (b)
   {
-    find_regmode_weight (b->index, SImode);
-    find_regmode_weight (b->index, SFmode);
+    find_regmode_weight (b, SImode);
+    find_regmode_weight (b, SFmode);
+    if (!reload_completed)
+      r0_life_regions += find_r0_life_regions (b);
   }
 
   CURR_REGMODE_PRESSURE (SImode) = 0;
@@ -8883,7 +9221,6 @@ sh_md_init (FILE *dump ATTRIBUTE_UNUSED,
 /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
    functions that already have high pressure on r0. */
 #define R0_MAX_LIFE_REGIONS 2
-#define R0_MAX_LIVE_LENGTH 12
 /* Register Pressure thresholds for SImode and SFmode registers.  */
 #define SIMODE_MAX_WEIGHT 5
 #define SFMODE_MAX_WEIGHT 10
@@ -8894,9 +9231,8 @@ high_pressure (enum machine_mode mode)
 {
   /* Pressure on register r0 can lead to spill failures. so avoid sched1 for
      functions that already have high pressure on r0. */
-  if ((REG_N_SETS (0) - REG_N_DEATHS (0)) >= R0_MAX_LIFE_REGIONS
-      && REG_LIVE_LENGTH (0) >= R0_MAX_LIVE_LENGTH)
-    return 1;
+   if (r0_life_regions >= R0_MAX_LIFE_REGIONS)
+     return 1;
 
   if (mode == SFmode)
     return (CURR_REGMODE_PRESSURE (SFmode) > SFMODE_MAX_WEIGHT);
@@ -8996,8 +9332,18 @@ sh_target_reg_class (void)
 static bool
 sh_optimize_target_register_callee_saved (bool after_prologue_epilogue_gen)
 {
-  return (shmedia_space_reserved_for_target_registers
-         && (! after_prologue_epilogue_gen || TARGET_SAVE_ALL_TARGET_REGS));
+  HARD_REG_SET dummy;
+#if 0
+  rtx insn;
+#endif
+
+  if (! shmedia_space_reserved_for_target_registers)
+    return 0;
+  if (after_prologue_epilogue_gen && ! TARGET_SAVE_ALL_TARGET_REGS)
+    return 0;
+  if (calc_live_regs (&dummy) >= 6 * 8)
+    return 1;
+  return 0;
 }
 
 static bool
@@ -9025,6 +9371,8 @@ sh_ms_bitfield_layout_p (tree record_type ATTRIBUTE_UNUSED)
 void
 sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
 {
+  rtx tramp_mem = gen_frame_mem (BLKmode, tramp);
+
   if (TARGET_SHMEDIA64)
     {
       rtx tramp_templ;
@@ -9053,34 +9401,32 @@ sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
          insn = gen_rtx_IOR (DImode, insn, gen_int_mode (0xec000010, SImode));
          insn = force_operand (insn, NULL_RTX);
          insn = gen_lowpart (SImode, insn);
-         emit_move_insn (gen_rtx_MEM (SImode, tramp), insn);
+         emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX), insn);
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (38));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, movi1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)), insn);
+         emit_move_insn (adjust_address (tramp_mem, SImode, 4), insn);
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (22));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)), insn);
+         emit_move_insn (adjust_address (tramp_mem, SImode, 8), insn);
          insn = gen_rtx_LSHIFTRT (DImode, fnaddr, GEN_INT (6));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)),
-                         insn);
+         emit_move_insn (adjust_address (tramp_mem, SImode, 12), insn);
          insn = gen_rtx_ASHIFT (DImode, fnaddr, GEN_INT (10));
          insn = gen_rtx_AND (DImode, insn, mask);
          insn = force_operand (gen_rtx_IOR (DImode, shori1, insn), NULL_RTX);
          insn = gen_lowpart (SImode, insn);
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 16)),
-                         insn);
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 20)),
+         emit_move_insn (adjust_address (tramp_mem, SImode, 16), insn);
+         emit_move_insn (adjust_address (tramp_mem, SImode, 20),
                          GEN_INT (0x6bf10600));
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 24)),
+         emit_move_insn (adjust_address (tramp_mem, SImode, 24),
                          GEN_INT (0x4415fc10));
-         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 28)),
+         emit_move_insn (adjust_address (tramp_mem, SImode, 28),
                          GEN_INT (0x4401fff0));
          emit_insn (gen_ic_invalidate_line (tramp));
          return;
@@ -9089,18 +9435,15 @@ sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
       fixed_len = TRAMPOLINE_SIZE - 2 * GET_MODE_SIZE (Pmode);
 
       tramp_templ = gen_datalabel_ref (tramp_templ);
-      dst = gen_rtx_MEM (BLKmode, tramp);
-      src = gen_rtx_MEM (BLKmode, tramp_templ);
+      dst = tramp_mem;
+      src = gen_const_mem (BLKmode, tramp_templ);
       set_mem_align (dst, 256);
       set_mem_align (src, 64);
       emit_block_move (dst, src, GEN_INT (fixed_len), BLOCK_OP_NORMAL);
 
-      emit_move_insn (gen_rtx_MEM (Pmode, plus_constant (tramp,        fixed_len)),
-                     fnaddr);
-      emit_move_insn (gen_rtx_MEM (Pmode,
-                                  plus_constant (tramp,
-                                                 fixed_len
-                                                 + GET_MODE_SIZE (Pmode))),
+      emit_move_insn (adjust_address (tramp_mem, Pmode, fixed_len), fnaddr);
+      emit_move_insn (adjust_address (tramp_mem, Pmode,
+                                     fixed_len + GET_MODE_SIZE (Pmode)),
                      cxt);
       emit_insn (gen_ic_invalidate_line (tramp));
       return;
@@ -9128,7 +9471,7 @@ sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
       emit_insn (gen_rotrdi3_mextr (quad0, quad0,
                                    GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
       emit_insn (gen_ashldi3_media (quad0, quad0, const2_rtx));
-      emit_move_insn (gen_rtx_MEM (DImode, tramp), quad0);
+      emit_move_insn (change_address (tramp_mem, DImode, NULL_RTX), quad0);
       emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0),
                                 gen_rtx_SUBREG (V2HImode, cxt, 0),
                                 movishori));
@@ -9145,8 +9488,8 @@ sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
          emit_insn (gen_mextr4 (quad1, cxtload, ptabs));
          emit_insn (gen_mshflo_l_di (quad2, blink, cxtload));
        }
-      emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 8)), quad1);
-      emit_move_insn (gen_rtx_MEM (DImode, plus_constant (tramp, 16)), quad2);
+      emit_move_insn (adjust_address (tramp_mem, DImode, 8), quad1);
+      emit_move_insn (adjust_address (tramp_mem, DImode, 16), quad2);
       emit_insn (gen_ic_invalidate_line (tramp));
       return;
     }
@@ -9155,20 +9498,20 @@ sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
       emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
       return;
     }
-  emit_move_insn (gen_rtx_MEM (SImode, tramp),
+  emit_move_insn (change_address (tramp_mem, SImode, NULL_RTX),
                  gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
                                SImode));
-  emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 4)),
+  emit_move_insn (adjust_address (tramp_mem, SImode, 4),
                  gen_int_mode (TARGET_LITTLE_ENDIAN ? 0x0009422b : 0x422b0009,
                                SImode));
-  emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 8)),
-                 cxt);
-  emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 12)),
-                 fnaddr);
+  emit_move_insn (adjust_address (tramp_mem, SImode, 8), cxt);
+  emit_move_insn (adjust_address (tramp_mem, SImode, 12), fnaddr);
   if (TARGET_HARVARD)
     {
-      if (TARGET_USERMODE)
-       emit_library_call (function_symbol ("__ic_invalidate"),
+      if (!TARGET_INLINE_IC_INVALIDATE
+         || (!(TARGET_SH4A_ARCH || TARGET_SH4_300) && TARGET_USERMODE))
+       emit_library_call (function_symbol (NULL, "__ic_invalidate",
+                                           FUNCTION_ORDINARY),
                           0, VOIDmode, 1, tramp, SImode);
       else
        emit_insn (gen_ic_invalidate_line (tramp));
@@ -9179,13 +9522,18 @@ sh_initialize_trampoline (rtx tramp, rtx fnaddr, rtx cxt)
    receives arguments ``by reference'' will have them stored in its
    own stack frame, so it must not pass pointers or references to
    these arguments to other functions by means of sibling calls.  */
+/* If PIC, we cannot make sibling calls to global functions
+   because the PLT requires r12 to be live.  */
 static bool
 sh_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
 {
-  return (decl
+  return (1
          && (! TARGET_SHCOMPACT
              || current_function_args_info.stack_regs == 0)
-         && ! sh_cfun_interrupt_handler_p ());
+         && ! sh_cfun_interrupt_handler_p ()
+         && (! flag_pic
+             || (decl && ! TREE_PUBLIC (decl))
+             || (decl && DECL_VISIBILITY (decl) != VISIBILITY_DEFAULT)));
 }
 \f
 /* Machine specific built-in functions.  */
@@ -9199,6 +9547,7 @@ struct builtin_description
 
 /* describe number and signedness of arguments; arg[0] == result
    (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
+/* 9: 64-bit pointer, 10: 32-bit pointer */
 static const char signature_args[][4] =
 {
 #define SH_BLTIN_V2SI2 0
@@ -9224,28 +9573,34 @@ static const char signature_args[][4] =
 #define SH_BLTIN_SISF 10
   { 4, 2 },
 #define SH_BLTIN_LDUA_L 11
-  { 2, 8 },
+  { 2, 10 },
 #define SH_BLTIN_LDUA_Q 12
-  { 1, 8 },
+  { 1, 10 },
 #define SH_BLTIN_STUA_L 13
-  { 0, 8, 2 },
+  { 0, 10, 2 },
 #define SH_BLTIN_STUA_Q 14
-  { 0, 8, 1 },
-#define SH_BLTIN_UDI 15
-  { 0, 8, 1 },
-#define SH_BLTIN_NUM_SHARED_SIGNATURES 16
-#define SH_BLTIN_2 16
-#define SH_BLTIN_SU 16
+  { 0, 10, 1 },
+#define SH_BLTIN_LDUA_L64 15
+  { 2, 9 },
+#define SH_BLTIN_LDUA_Q64 16
+  { 1, 9 },
+#define SH_BLTIN_STUA_L64 17
+  { 0, 9, 2 },
+#define SH_BLTIN_STUA_Q64 18
+  { 0, 9, 1 },
+#define SH_BLTIN_NUM_SHARED_SIGNATURES 19
+#define SH_BLTIN_2 19
+#define SH_BLTIN_SU 19
   { 1, 2 },
-#define SH_BLTIN_3 17
-#define SH_BLTIN_SUS 17
+#define SH_BLTIN_3 20
+#define SH_BLTIN_SUS 20
   { 2, 2, 1 },
-#define SH_BLTIN_PSSV 18
+#define SH_BLTIN_PSSV 21
   { 0, 8, 2, 2 },
-#define SH_BLTIN_XXUU 19
-#define SH_BLTIN_UUUU 19
+#define SH_BLTIN_XXUU 22
+#define SH_BLTIN_UUUU 22
   { 1, 1, 1, 1 },
-#define SH_BLTIN_PV 20
+#define SH_BLTIN_PV 23
   { 0, 8 },
 };
 /* mcmv: operands considered unsigned.  */
@@ -9263,10 +9618,7 @@ static const struct builtin_description bdesc[] =
   { CODE_FOR_ssaddv2si3,"__builtin_ssaddv2si3", SH_BLTIN_V2SI3 },
   { CODE_FOR_usaddv8qi3,"__builtin_usaddv8qi3", SH_BLTIN_V8QI3 },
   { CODE_FOR_ssaddv4hi3,"__builtin_ssaddv4hi3", SH_BLTIN_V4HI3 },
-#if 0
-  { CODE_FOR_alloco32, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV },
-  { CODE_FOR_alloco64, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV },
-#endif
+  { CODE_FOR_alloco_i, "__builtin_sh_media_ALLOCO", SH_BLTIN_PV },
   { CODE_FOR_negcmpeqv8qi,"__builtin_sh_media_MCMPEQ_B", SH_BLTIN_V8QI3 },
   { CODE_FOR_negcmpeqv2si,"__builtin_sh_media_MCMPEQ_L", SH_BLTIN_V2SI3 },
   { CODE_FOR_negcmpeqv4hi,"__builtin_sh_media_MCMPEQ_W", SH_BLTIN_V4HI3 },
@@ -9277,13 +9629,13 @@ static const struct builtin_description bdesc[] =
   { CODE_FOR_mcnvs_lw, "__builtin_sh_media_MCNVS_LW", SH_BLTIN_3 },
   { CODE_FOR_mcnvs_wb, "__builtin_sh_media_MCNVS_WB", SH_BLTIN_V4HI2V8QI },
   { CODE_FOR_mcnvs_wub,        "__builtin_sh_media_MCNVS_WUB", SH_BLTIN_V4HI2V8QI },
-  { CODE_FOR_mextr1,   "__builtin_sh_media_MEXTR1", SH_BLTIN_UDI },
-  { CODE_FOR_mextr2,   "__builtin_sh_media_MEXTR2", SH_BLTIN_UDI },
-  { CODE_FOR_mextr3,   "__builtin_sh_media_MEXTR3", SH_BLTIN_UDI },
-  { CODE_FOR_mextr4,   "__builtin_sh_media_MEXTR4", SH_BLTIN_UDI },
-  { CODE_FOR_mextr5,   "__builtin_sh_media_MEXTR5", SH_BLTIN_UDI },
-  { CODE_FOR_mextr6,   "__builtin_sh_media_MEXTR6", SH_BLTIN_UDI },
-  { CODE_FOR_mextr7,   "__builtin_sh_media_MEXTR7", SH_BLTIN_UDI },
+  { CODE_FOR_mextr1,   "__builtin_sh_media_MEXTR1", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mextr2,   "__builtin_sh_media_MEXTR2", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mextr3,   "__builtin_sh_media_MEXTR3", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mextr4,   "__builtin_sh_media_MEXTR4", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mextr5,   "__builtin_sh_media_MEXTR5", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mextr6,   "__builtin_sh_media_MEXTR6", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mextr7,   "__builtin_sh_media_MEXTR7", SH_BLTIN_V8QI3 },
   { CODE_FOR_mmacfx_wl,        "__builtin_sh_media_MMACFX_WL", SH_BLTIN_MAC_HISI },
   { CODE_FOR_mmacnfx_wl,"__builtin_sh_media_MMACNFX_WL", SH_BLTIN_MAC_HISI },
   { CODE_FOR_mulv2si3, "__builtin_mulv2si3", SH_BLTIN_V2SI3, },
@@ -9320,8 +9672,10 @@ static const struct builtin_description bdesc[] =
   { CODE_FOR_fsina_s,  "__builtin_sh_media_FSINA_S", SH_BLTIN_SISF },
   { CODE_FOR_fipr,     "__builtin_sh_media_FIPR_S", SH_BLTIN_3 },
   { CODE_FOR_ftrv,     "__builtin_sh_media_FTRV_S", SH_BLTIN_3 },
+  { CODE_FOR_mac_media,        "__builtin_sh_media_FMAC_S", SH_BLTIN_3 },
+  { CODE_FOR_sqrtdf2,  "__builtin_sh_media_FSQRT_D", SH_BLTIN_2 },
+  { CODE_FOR_sqrtsf2,  "__builtin_sh_media_FSQRT_S", SH_BLTIN_2 },
   { CODE_FOR_fsrra_s,  "__builtin_sh_media_FSRRA_S", SH_BLTIN_2 },
-#if 0
   { CODE_FOR_ldhi_l,   "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L },
   { CODE_FOR_ldhi_q,   "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q },
   { CODE_FOR_ldlo_l,   "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L },
@@ -9330,21 +9684,17 @@ static const struct builtin_description bdesc[] =
   { CODE_FOR_sthi_q,   "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q },
   { CODE_FOR_stlo_l,   "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L },
   { CODE_FOR_stlo_q,   "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q },
-  { CODE_FOR_ldhi_l64, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L },
-  { CODE_FOR_ldhi_q64, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q },
-  { CODE_FOR_ldlo_l64, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L },
-  { CODE_FOR_ldlo_q64, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q },
-  { CODE_FOR_sthi_l64, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L },
-  { CODE_FOR_sthi_q64, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q },
-  { CODE_FOR_stlo_l64, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L },
-  { CODE_FOR_stlo_q64, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q },
-#endif
+  { CODE_FOR_ldhi_l64, "__builtin_sh_media_LDHI_L", SH_BLTIN_LDUA_L64 },
+  { CODE_FOR_ldhi_q64, "__builtin_sh_media_LDHI_Q", SH_BLTIN_LDUA_Q64 },
+  { CODE_FOR_ldlo_l64, "__builtin_sh_media_LDLO_L", SH_BLTIN_LDUA_L64 },
+  { CODE_FOR_ldlo_q64, "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q64 },
+  { CODE_FOR_sthi_l64, "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L64 },
+  { CODE_FOR_sthi_q64, "__builtin_sh_media_STHI_Q", SH_BLTIN_STUA_Q64 },
+  { CODE_FOR_stlo_l64, "__builtin_sh_media_STLO_L", SH_BLTIN_STUA_L64 },
+  { CODE_FOR_stlo_q64, "__builtin_sh_media_STLO_Q", SH_BLTIN_STUA_Q64 },
   { CODE_FOR_nsb,      "__builtin_sh_media_NSB", SH_BLTIN_SU },
   { CODE_FOR_byterev,  "__builtin_sh_media_BYTEREV", SH_BLTIN_2 },
-#if 0
-  { CODE_FOR_prefetch32,"__builtin_sh_media_PREFO", SH_BLTIN_PSSV },
-  { CODE_FOR_prefetch64,"__builtin_sh_media_PREFO", SH_BLTIN_PSSV }
-#endif
+  { CODE_FOR_prefetch, "__builtin_sh_media_PREFO", SH_BLTIN_PSSV },
 };
 
 static void
@@ -9356,7 +9706,7 @@ sh_media_init_builtins (void)
   memset (shared, 0, sizeof shared);
   for (d = bdesc; d - bdesc < (int) ARRAY_SIZE (bdesc); d++)
     {
-      tree type, arg_type;
+      tree type, arg_type = 0;
       int signature = d->signature;
       int i;
 
@@ -9366,8 +9716,9 @@ sh_media_init_builtins (void)
        {
          int has_result = signature_args[signature][0] != 0;
 
-         if (signature_args[signature][1] == 8
-             && (insn_data[d->icode].operand[has_result].mode != Pmode))
+         if ((signature_args[signature][1] & 8)
+             && (((signature_args[signature][1] & 1) && TARGET_SHMEDIA32)
+                 || ((signature_args[signature][1] & 2) && TARGET_SHMEDIA64)))
            continue;
          if (! TARGET_FPU_ANY
              && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
@@ -9378,12 +9729,12 @@ sh_media_init_builtins (void)
              int arg = signature_args[signature][i];
              int opno = i - 1 + has_result;
 
-             if (arg == 8)
+             if (arg & 8)
                arg_type = ptr_type_node;
              else if (arg)
-               arg_type = ((*lang_hooks.types.type_for_mode)
-                           (insn_data[d->icode].operand[opno].mode,
-                            (arg & 1)));
+               arg_type = (*lang_hooks.types.type_for_mode)
+                 (insn_data[d->icode].operand[opno].mode,
+                  (arg & 1));
              else if (i)
                continue;
              else
@@ -9396,8 +9747,8 @@ sh_media_init_builtins (void)
          if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
            shared[signature] = type;
        }
-      lang_hooks.builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
-                                  NULL, NULL_TREE);
+      add_builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
+                           NULL, NULL_TREE);
     }
 }
 
@@ -9449,8 +9800,7 @@ static rtx
 sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
                   enum machine_mode mode ATTRIBUTE_UNUSED, int ignore)
 {
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
-  tree arglist = TREE_OPERAND (exp, 1);
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
   unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
   const struct builtin_description *d = &bdesc[fcode];
   enum insn_code icode = d->icode;
@@ -9458,7 +9808,7 @@ sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   enum machine_mode tmode = VOIDmode;
   int nop = 0, i;
   rtx op[4];
-  rtx pat;
+  rtx pat = 0;
 
   if (signature_args[signature][0])
     {
@@ -9479,18 +9829,26 @@ sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
     {
       tree arg;
       enum machine_mode opmode, argmode;
+      tree optype;
 
       if (! signature_args[signature][i])
        break;
-      arg = TREE_VALUE (arglist);
+      arg = CALL_EXPR_ARG (exp, i - 1);
       if (arg == error_mark_node)
        return const0_rtx;
-      arglist = TREE_CHAIN (arglist);
-      opmode = insn_data[icode].operand[nop].mode;
+      if (signature_args[signature][i] & 8)
+       {
+         opmode = ptr_mode;
+         optype = ptr_type_node;
+       }
+      else
+       {
+         opmode = insn_data[icode].operand[nop].mode;
+         optype = (*lang_hooks.types.type_for_mode) (opmode, 0);
+       }
       argmode = TYPE_MODE (TREE_TYPE (arg));
       if (argmode != opmode)
-       arg = build1 (NOP_EXPR,
-                     (*lang_hooks.types.type_for_mode) (opmode, 0), arg);
+       arg = build1 (NOP_EXPR, optype, arg);
       op[nop] = expand_expr (arg, NULL_RTX, opmode, 0);
       if (! (*insn_data[icode].operand[nop].predicate) (op[nop], opmode))
        op[nop] = copy_to_mode_reg (opmode, op[nop]);
@@ -9511,7 +9869,7 @@ sh_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
       pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
       break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
   if (! pat)
     return 0;
@@ -9613,6 +9971,9 @@ sh_register_move_cost (enum machine_mode mode,
       && REGCLASS_HAS_FP_REG (dstclass))
     return 4;
 
+  if (REGCLASS_HAS_FP_REG (dstclass) && srcclass == T_REGS)
+    return ((TARGET_HARD_SH4 && !optimize_size) ? 10 : 7);
+
   if ((REGCLASS_HAS_FP_REG (dstclass) && srcclass == MAC_REGS)
       || (dstclass == MAC_REGS && REGCLASS_HAS_FP_REG (srcclass)))
     return 9;
@@ -9640,6 +10001,16 @@ sh_register_move_cost (enum machine_mode mode,
       || ((dstclass) == TARGET_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
     return 20;
 
+  /* ??? ptabs faults on (value & 0x3) == 0x3  */
+  if (TARGET_SHMEDIA
+      && ((srcclass) == TARGET_REGS || (srcclass) == SIBCALL_REGS))
+    {
+      if (sh_gettrcost >= 0)
+       return sh_gettrcost;
+      else if (!TARGET_PT_FIXED)
+       return 100;
+    }
+
   if ((srcclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (dstclass))
       || (dstclass == FPSCR_REGS && ! REGCLASS_HAS_GENERAL_REG (srcclass)))
   return 4;
@@ -9653,38 +10024,19 @@ sh_register_move_cost (enum machine_mode mode,
   return 2 * ((GET_MODE_SIZE (mode) + 3) / 4U);
 }
 
-/* Like register_operand, but take into account that SHMEDIA can use
-   the constant zero like a general register.  */
-int
-sh_register_operand (rtx op, enum machine_mode mode)
-{
-  if (op == CONST0_RTX (mode) && TARGET_SHMEDIA)
-    return 1;
-  return register_operand (op, mode);
-}
-
-int
-cmpsi_operand (rtx op, enum machine_mode mode)
-{
-  if (GET_CODE (op) == REG && REGNO (op) == T_REG
-      && GET_MODE (op) == SImode)
-    return 1;
-  return arith_operand (op, mode);
-}
-
 static rtx emit_load_ptr (rtx, rtx);
 
 static rtx
 emit_load_ptr (rtx reg, rtx addr)
 {
-  rtx mem = gen_rtx_MEM (ptr_mode, addr);
+  rtx mem = gen_const_mem (ptr_mode, addr);
 
   if (Pmode != ptr_mode)
     mem = gen_rtx_SIGN_EXTEND (Pmode, mem);
   return emit_move_insn (reg, mem);
 }
 
-void
+static void
 sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
                    HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
                    tree function)
@@ -9696,12 +10048,11 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
   int simple_add = CONST_OK_FOR_ADD (delta);
   int did_load = 0;
   rtx scratch0, scratch1, scratch2;
+  unsigned i;
 
   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);
 
@@ -9726,18 +10077,39 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
      static chain pointer (even if you can't have nested virtual functions
      right now, someone might implement them sometime), and the rest of the
      registers are used for argument passing, are callee-saved, or reserved.  */
+  /* We need to check call_used_regs / fixed_regs in case -fcall_saved-reg /
+     -ffixed-reg has been used.  */
+  if (! call_used_regs[0] || fixed_regs[0])
+    error ("r0 needs to be available as a call-clobbered register");
   scratch0 = scratch1 = scratch2 = gen_rtx_REG (Pmode, 0);
   if (! TARGET_SH5)
     {
-      scratch1 = gen_rtx_REG (ptr_mode, 1);
+      if (call_used_regs[1] && ! fixed_regs[1])
+       scratch1 = gen_rtx_REG (ptr_mode, 1);
       /* N.B., if not TARGET_HITACHI, register 2 is used to pass the pointer
         pointing where to return struct values.  */
-      scratch2 = gen_rtx_REG (Pmode, 3);
+      if (call_used_regs[3] && ! fixed_regs[3])
+       scratch2 = gen_rtx_REG (Pmode, 3);
     }
   else if (TARGET_SHMEDIA)
     {
-      scratch1 = gen_rtx_REG (ptr_mode, 21);
-      scratch2 = gen_rtx_REG (Pmode, TR0_REG);
+      for (i = FIRST_GENERAL_REG; i <= LAST_GENERAL_REG; i++)
+       if (i != REGNO (scratch0) &&
+           call_used_regs[i] && ! fixed_regs[i] && ! FUNCTION_ARG_REGNO_P (i))
+         {
+           scratch1 = gen_rtx_REG (ptr_mode, i);
+           break;
+         }
+      if (scratch1 == scratch0)
+       error ("Need a second call-clobbered general purpose register");
+      for (i = FIRST_TARGET_REG; i <= LAST_TARGET_REG; i++)
+       if (call_used_regs[i] && ! fixed_regs[i])
+         {
+           scratch2 = gen_rtx_REG (Pmode, i);
+           break;
+         }
+      if (scratch2 == scratch0)
+       error ("Need a call-clobbered target register");
     }
 
   this_value = plus_constant (this, delta);
@@ -9769,7 +10141,7 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       offset_addr = plus_constant (scratch0, vcall_offset);
       if (strict_memory_address_p (ptr_mode, offset_addr))
        ; /* Do nothing.  */
-      else if (! TARGET_SH5)
+      else if (! TARGET_SH5 && scratch0 != scratch1)
        {
          /* scratch0 != scratch1, and we have indexed loads.  Get better
             schedule by loading the offset into r1 and using an indexed
@@ -9790,7 +10162,7 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
          offset_addr = scratch0;
        }
       else
-       abort (); /* FIXME */
+       gcc_unreachable (); /* FIXME */
       emit_load_ptr (scratch0, offset_addr);
 
       if (Pmode != ptr_mode)
@@ -9805,9 +10177,30 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
       TREE_USED (function) = 1;
     }
   funexp = XEXP (DECL_RTL (function), 0);
-  emit_move_insn (scratch2, funexp);
-  funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
-  sibcall = emit_call_insn (gen_sibcall (funexp, const0_rtx, NULL_RTX));
+  /* If the function is overridden, so is the thunk, hence we don't
+     need GOT addressing even if this is a public symbol.  */
+#if 0
+  if (TARGET_SH1 && ! flag_weak)
+    sibcall = gen_sibcalli_thunk (funexp, const0_rtx);
+  else
+#endif
+  if (TARGET_SH2 && flag_pic)
+    {
+      sibcall = gen_sibcall_pcrel (funexp, const0_rtx);
+      XEXP (XVECEXP (sibcall, 0, 2), 0) = scratch2;
+    }
+  else
+    {
+      if (TARGET_SHMEDIA && flag_pic)
+       {
+         funexp = gen_sym2PIC (funexp);
+         PUT_MODE (funexp, Pmode);
+       }
+      emit_move_insn (scratch2, funexp);
+      funexp = gen_rtx_MEM (FUNCTION_MODE, scratch2);
+      sibcall = gen_sibcall (funexp, const0_rtx, NULL_RTX);
+    }
+  sibcall = emit_call_insn (sibcall);
   SIBLING_CALL_P (sibcall) = 1;
   use_reg (&CALL_INSN_FUNCTION_USAGE (sibcall), this);
   emit_barrier ();
@@ -9816,47 +10209,119 @@ sh_output_mi_thunk (FILE *file, tree thunk_fndecl ATTRIBUTE_UNUSED,
      the insns emitted.  Note that use_thunk calls
      assemble_start_function and assemble_end_function.  */
 
-  insn_locators_initialize ();
+  insn_locators_alloc ();
   insns = get_insns ();
 
-  if (optimize > 0 && flag_schedule_insns_after_reload)
+#if 0
+  if (optimize > 0)
     {
-      if (! basic_block_info)
+      /* Initialize the bitmap obstacks.  */
+      bitmap_obstack_initialize (NULL);
+      bitmap_obstack_initialize (&reg_obstack);
+      if (! cfun->cfg)
        init_flow ();
       rtl_register_cfg_hooks ();
-      find_basic_blocks (insns, max_reg_num (), dump_file);
-      life_analysis (dump_file, PROP_FINAL);
+      init_rtl_bb_info (ENTRY_BLOCK_PTR);
+      init_rtl_bb_info (EXIT_BLOCK_PTR);
+      ENTRY_BLOCK_PTR->flags |= BB_RTL;
+      EXIT_BLOCK_PTR->flags |= BB_RTL;
+      find_basic_blocks (insns);
+
+      if (flag_schedule_insns_after_reload)
+       {
+         life_analysis (PROP_FINAL);
 
-      split_all_insns (1);
+         split_all_insns (1);
 
-      schedule_insns (dump_file);
+         schedule_insns ();
+       }
+      /* We must split jmp insn in PIC case.  */
+      else if (flag_pic)
+       split_all_insns_noflow ();
+    }
+#else
+  if (optimize > 0)
+    {
+      if (! cfun->cfg)
+       init_flow ();
+      split_all_insns_noflow ();
     }
+#endif
 
   sh_reorg ();
 
   if (optimize > 0 && flag_delayed_branch)
-    dbr_schedule (insns, dump_file);
+    dbr_schedule (insns);
+
   shorten_branches (insns);
   final_start_function (insns, file, 1);
-  final (insns, file, 1, 0);
+  final (insns, file, 1);
   final_end_function ();
 
-  if (optimize > 0 && flag_schedule_insns_after_reload)
+#if 0
+  if (optimize > 0)
     {
-      /* Release all memory allocated by flow.  */
-      free_basic_block_vars ();
+      /* Release all memory allocated by df.  */
+      if (rtl_df)
+       {
+         df_finish (rtl_df);
+         rtl_df = NULL;
+       }
+
+      /* Release the bitmap obstacks.  */
+      bitmap_obstack_release (&reg_obstack);
+      bitmap_obstack_release (NULL);
     }
+#endif
 
   reload_completed = 0;
   epilogue_completed = 0;
-  no_new_pseudos = 0;
 }
 
 rtx
-function_symbol (const char *name)
+function_symbol (rtx target, const char *name, enum sh_function_kind kind)
 {
-  rtx sym = gen_rtx_SYMBOL_REF (Pmode, name);
+  rtx sym;
+
+  /* If this is not an ordinary function, the name usually comes from a
+     string literal or an sprintf buffer.  Make sure we use the same
+     string consistently, so that cse will be able to unify address loads.  */
+  if (kind != FUNCTION_ORDINARY)
+    name = IDENTIFIER_POINTER (get_identifier (name));
+  sym = gen_rtx_SYMBOL_REF (Pmode, name);
   SYMBOL_REF_FLAGS (sym) = SYMBOL_FLAG_FUNCTION;
+  if (flag_pic)
+    switch (kind)
+      {
+      case FUNCTION_ORDINARY:
+       break;
+      case SFUNC_GOT:
+       {
+         rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+         emit_insn (gen_symGOT2reg (reg, sym));
+         sym = reg;
+         break;
+       }
+      case SFUNC_STATIC:
+       {
+         /* ??? To allow cse to work, we use GOTOFF relocations.
+            we could add combiner patterns to transform this into
+            straight pc-relative calls with sym2PIC / bsrf when
+            label load and function call are still 1:1 and in the
+            same basic block during combine.  */
+         rtx reg = target ? target : gen_reg_rtx (Pmode);
+
+         emit_insn (gen_symGOTOFF2reg (reg, sym));
+         sym = reg;
+         break;
+       }
+      }
+  if (target && sym != target)
+    {
+      emit_move_insn (target, sym);
+      return target;
+    }
   return sym;
 }
 
@@ -9883,15 +10348,16 @@ sh_get_pr_initial_val (void)
       && ((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);
+    return gen_frame_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.  */
+     ??? get_hard_reg_initial_val fails if it is called after register
+     allocation has started, 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.  */
   /* ??? We also must do this for TARGET_SH1 in general, because otherwise
      combine can put the pseudo returned by get_hard_reg_initial_val into
      instructions that need a general purpose registers, which will fail to
@@ -9948,9 +10414,8 @@ extract_sfunc_addr (rtx insn)
          && GENERAL_REGISTER_P (true_regnum (XEXP (part, 0))))
        return XEXP (part, 0);
     }
-  if (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE)
-    return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);
-  abort ();
+  gcc_assert (GET_CODE (XVECEXP (pattern, 0, 0)) == UNSPEC_VOLATILE);
+  return XVECEXP (XVECEXP (pattern, 0, 0), 0, 1);
 }
 
 /* Verify that the register in use_sfunc_addr still agrees with the address
@@ -9976,28 +10441,7 @@ check_use_sfunc_addr (rtx insn, rtx reg)
        continue;
       return rtx_equal_p (extract_sfunc_addr (insn), reg);
     }
-  abort ();
-}
-
-/* Returns 1 if OP is a MEM that can be source of a simple move operation.  */
-
-int
-unaligned_load_operand (rtx op, enum machine_mode mode)
-{
-  rtx inside;
-
-  if (GET_CODE (op) != MEM || GET_MODE (op) != mode)
-    return 0;
-
-  inside = XEXP (op, 0);
-
-  if (GET_CODE (inside) == POST_INC)
-    inside = XEXP (inside, 0);
-
-  if (GET_CODE (inside) == REG)
-    return 1;
-
-  return 0;
+  gcc_unreachable ();
 }
 
 /* This function returns a constant rtx that represents pi / 2**15 in
@@ -10120,7 +10564,7 @@ sh_init_cumulative_args (CUMULATIVE_ARGS *  pcum,
             the TYPE or the FNDECL available so we synthesize the
             contents of that function as best we can.  */
          pcum->force_mem =
-           (TARGET_DEFAULT & HITACHI_BIT)
+           (TARGET_DEFAULT & MASK_HITACHI)
            && (mode == BLKmode
                || (GET_MODE_SIZE (mode) > 4
                    && !(mode == DFmode
@@ -10134,4 +10578,373 @@ sh_init_cumulative_args (CUMULATIVE_ARGS *  pcum,
     }
 }
 
+/* Replace any occurrence of FROM(n) in X with TO(n).  The function does
+   not enter into CONST_DOUBLE for the replace.
+
+   Note that copying is not done so X must not be shared unless all copies
+   are to be modified.
+
+   This is like replace_rtx, except that we operate on N_REPLACEMENTS
+   replacements simultaneously - FROM(n) is replacements[n*2] and to(n) is
+   replacements[n*2+1] - and that we take mode changes into account.
+
+   If a replacement is ambiguous, return NULL_RTX.
+
+   If MODIFY is zero, don't modify any rtl in place,
+   just return zero or nonzero for failure / success.  */
+
+rtx
+replace_n_hard_rtx (rtx x, rtx *replacements, int n_replacements, int modify)
+{
+  int i, j;
+  const char *fmt;
+
+  /* The following prevents loops occurrence when we change MEM in
+     CONST_DOUBLE onto the same CONST_DOUBLE.  */
+  if (x != 0 && GET_CODE (x) == CONST_DOUBLE)
+    return x;
+
+  for (i = n_replacements - 1; i >= 0 ; i--)
+  if (x == replacements[i*2] && GET_MODE (x) == GET_MODE (replacements[i*2+1]))
+    return replacements[i*2+1];
+
+  /* Allow this function to make replacements in EXPR_LISTs.  */
+  if (x == 0)
+    return 0;
+
+  if (GET_CODE (x) == SUBREG)
+    {
+      rtx new = replace_n_hard_rtx (SUBREG_REG (x), replacements,
+                                   n_replacements, modify);
+
+      if (GET_CODE (new) == CONST_INT)
+       {
+         x = simplify_subreg (GET_MODE (x), new,
+                              GET_MODE (SUBREG_REG (x)),
+                              SUBREG_BYTE (x));
+         if (! x)
+           abort ();
+       }
+      else if (modify)
+       SUBREG_REG (x) = new;
+
+      return x;
+    }
+  else if (GET_CODE (x) == REG)
+    {
+      unsigned regno = REGNO (x);
+      unsigned nregs = (regno < FIRST_PSEUDO_REGISTER
+                       ? HARD_REGNO_NREGS (regno, GET_MODE (x)) : 1);
+      rtx result = NULL_RTX;
+
+      for (i = n_replacements - 1; i >= 0; i--)
+       {
+         rtx from = replacements[i*2];
+         rtx to = replacements[i*2+1];
+         unsigned from_regno, from_nregs, to_regno, new_regno;
+
+         if (GET_CODE (from) != REG)
+           continue;
+         from_regno = REGNO (from);
+         from_nregs = (from_regno < FIRST_PSEUDO_REGISTER
+                       ? HARD_REGNO_NREGS (from_regno, GET_MODE (from)) : 1);
+         if (regno < from_regno + from_nregs && regno + nregs > from_regno)
+           {
+             if (regno < from_regno
+                 || regno + nregs > from_regno + nregs
+                 || GET_CODE (to) != REG
+                 || result)
+               return NULL_RTX;
+             to_regno = REGNO (to);
+             if (to_regno < FIRST_PSEUDO_REGISTER)
+               {
+                 new_regno = regno + to_regno - from_regno;
+                 if ((unsigned) HARD_REGNO_NREGS (new_regno, GET_MODE (x))
+                     != nregs)
+                   return NULL_RTX;
+                 result = gen_rtx_REG (GET_MODE (x), new_regno);
+               }
+             else if (GET_MODE (x) <= GET_MODE (to))
+               result = gen_lowpart_common (GET_MODE (x), to);
+             else
+               result = gen_lowpart_SUBREG (GET_MODE (x), to);
+           }
+       }
+      return result ? result : x;
+    }
+  else if (GET_CODE (x) == ZERO_EXTEND)
+    {
+      rtx new = replace_n_hard_rtx (XEXP (x, 0), replacements,
+                                   n_replacements, modify);
+
+      if (GET_CODE (new) == CONST_INT)
+       {
+         x = simplify_unary_operation (ZERO_EXTEND, GET_MODE (x),
+                                       new, GET_MODE (XEXP (x, 0)));
+         if (! x)
+           abort ();
+       }
+      else if (modify)
+       XEXP (x, 0) = new;
+
+      return x;
+    }
+
+  fmt = GET_RTX_FORMAT (GET_CODE (x));
+  for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
+    {
+      rtx new;
+
+      if (fmt[i] == 'e')
+       {
+         new = replace_n_hard_rtx (XEXP (x, i), replacements,
+                                   n_replacements, modify);
+         if (!new)
+           return NULL_RTX;
+         if (modify)
+           XEXP (x, i) = new;
+       }
+      else if (fmt[i] == 'E')
+       for (j = XVECLEN (x, i) - 1; j >= 0; j--)
+         {
+           new = replace_n_hard_rtx (XVECEXP (x, i, j), replacements,
+                                     n_replacements, modify);
+         if (!new)
+           return NULL_RTX;
+           if (modify)
+             XVECEXP (x, i, j) = new;
+         }
+    }
+
+  return x;
+}
+
+rtx
+sh_gen_truncate (enum machine_mode mode, rtx x, int need_sign_ext)
+{
+  enum rtx_code code = TRUNCATE;
+
+  if (GET_CODE (x) == ZERO_EXTEND || GET_CODE (x) == SIGN_EXTEND)
+    {
+      rtx inner = XEXP (x, 0);
+      enum machine_mode inner_mode = GET_MODE (inner);
+
+      if (inner_mode == mode)
+       return inner;
+      else if (GET_MODE_SIZE (inner_mode) >= GET_MODE_SIZE (mode))
+       x = inner;
+      else if (GET_MODE_SIZE (inner_mode) < GET_MODE_SIZE (mode)
+              && (! need_sign_ext || GET_CODE (x) == SIGN_EXTEND))
+       {
+         code = GET_CODE (x);
+         x = inner;
+       }
+    }
+  return gen_rtx_fmt_e (code, mode, x);
+}
+
+/* called via for_each_rtx after reload, to clean up truncates of
+   registers that span multiple actual hard registers.  */
+int
+shmedia_cleanup_truncate (rtx *p, void *n_changes)
+{
+  rtx x = *p, reg;
+
+  if (GET_CODE (x) != TRUNCATE)
+    return 0;
+  reg = XEXP (x, 0);
+  if (GET_MODE_SIZE (GET_MODE (reg)) > 8 && GET_CODE (reg) == REG)
+    {
+      enum machine_mode reg_mode = GET_MODE (reg);
+      XEXP (x, 0) = simplify_subreg (DImode, reg, reg_mode,
+                                    subreg_lowpart_offset (DImode, reg_mode));
+      *(int*) n_changes += 1;
+      return -1;
+    }
+  return 0;
+}
+
+/* Load and store depend on the highpart of the address.  However,
+   set_attr_alternative does not give well-defined results before reload,
+   so we must look at the rtl ourselves to see if any of the feeding
+   registers is used in a memref.  */
+
+/* Called by sh_contains_memref_p via for_each_rtx.  */
+static int
+sh_contains_memref_p_1 (rtx *loc, void *data ATTRIBUTE_UNUSED)
+{
+  return (GET_CODE (*loc) == MEM);
+}
+
+/* Return nonzero iff INSN contains a MEM.  */
+int
+sh_contains_memref_p (rtx insn)
+{
+  return for_each_rtx (&PATTERN (insn), &sh_contains_memref_p_1, NULL);
+}
+
+/* Return nonzero iff INSN loads a banked register.  */
+int
+sh_loads_bankedreg_p (rtx insn)
+{
+  if (GET_CODE (PATTERN (insn)) == SET)
+    {
+      rtx op = SET_DEST (PATTERN(insn));
+      if (REG_P (op) && BANKED_REGISTER_P (REGNO (op)))
+       return 1;
+    }
+
+  return 0;  
+}
+
+/* FNADDR is the MEM expression from a call expander.  Return an address
+   to use in an SHmedia insn pattern.  */
+rtx
+shmedia_prepare_call_address (rtx fnaddr, int is_sibcall)
+{
+  int is_sym;
+
+  fnaddr = XEXP (fnaddr, 0);
+  is_sym = GET_CODE (fnaddr) == SYMBOL_REF;
+  if (flag_pic && is_sym)
+    {
+      if (! SYMBOL_REF_LOCAL_P (fnaddr))
+       {
+         rtx reg = gen_reg_rtx (Pmode);
+
+         /* We must not use GOTPLT for sibcalls, because PIC_REG
+            must be restored before the PLT code gets to run.  */
+         if (is_sibcall)
+           emit_insn (gen_symGOT2reg (reg, fnaddr));
+         else
+           emit_insn (gen_symGOTPLT2reg (reg, fnaddr));
+         fnaddr = reg;
+       }
+      else
+       {
+         fnaddr = gen_sym2PIC (fnaddr);
+         PUT_MODE (fnaddr, Pmode);
+       }
+    }
+  /* If ptabs might trap, make this visible to the rest of the compiler.
+     We generally assume that symbols pertain to valid locations, but
+     it is possible to generate invalid symbols with asm or linker tricks.
+     In a list of functions where each returns its successor, an invalid
+     symbol might denote an empty list.  */
+  if (!TARGET_PT_FIXED
+      && (!is_sym || TARGET_INVALID_SYMBOLS)
+      && (!REG_P (fnaddr) || ! TARGET_REGISTER_P (REGNO (fnaddr))))
+    {
+      rtx tr = gen_reg_rtx (PDImode);
+
+      emit_insn (gen_ptabs (tr, fnaddr));
+      fnaddr = tr;
+    }
+  else if (! target_reg_operand (fnaddr, Pmode))
+    fnaddr = copy_to_mode_reg (Pmode, fnaddr);
+  return fnaddr;
+}
+
+enum reg_class
+sh_secondary_reload (bool in_p, rtx x, enum reg_class class,
+                    enum machine_mode mode, secondary_reload_info *sri)
+{
+  if (in_p)
+    {
+      if (REGCLASS_HAS_FP_REG (class)
+         && ! TARGET_SHMEDIA
+         && immediate_operand ((x), mode)
+         && ! ((fp_zero_operand (x) || fp_one_operand (x))
+               && mode == SFmode && fldi_ok ()))
+       switch (mode)
+         {
+         case SFmode:
+           sri->icode = CODE_FOR_reload_insf__frn;
+           return NO_REGS;
+         case DFmode:
+           sri->icode = CODE_FOR_reload_indf__frn;
+           return NO_REGS;
+         case SImode:
+           /* ??? If we knew that we are in the appropriate mode -
+              single precision - we could use a reload pattern directly.  */
+           return FPUL_REGS;
+         default:
+           abort ();
+         }
+      if (class == FPUL_REGS
+          && ((GET_CODE (x) == REG
+               && (REGNO (x) == MACL_REG || REGNO (x) == MACH_REG
+                   || REGNO (x) == T_REG))
+              || GET_CODE (x) == PLUS))
+        return GENERAL_REGS;
+      if (class == FPUL_REGS && immediate_operand (x, mode))
+       {
+         if (satisfies_constraint_I08 (x))
+           return GENERAL_REGS;
+         sri->icode = CODE_FOR_reload_insi__i_fpul;
+         return NO_REGS;
+       }
+      if (class == FPSCR_REGS
+          && ((GET_CODE (x) == REG && REGNO (x) >= FIRST_PSEUDO_REGISTER)
+              || (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PLUS)))
+        return GENERAL_REGS;
+      if (REGCLASS_HAS_FP_REG (class)
+          && TARGET_SHMEDIA
+          && immediate_operand (x, mode)
+          && x != CONST0_RTX (GET_MODE (x))
+          && GET_MODE (x) != V4SFmode)
+        return GENERAL_REGS;
+      if ((mode == QImode || mode == HImode)
+          && TARGET_SHMEDIA && inqhi_operand (x, mode))
+       {
+         sri->icode = ((mode == QImode)
+                       ? CODE_FOR_reload_inqi : CODE_FOR_reload_inhi);
+         return NO_REGS;
+       }
+      if (TARGET_SHMEDIA && class == GENERAL_REGS
+          && (GET_CODE (x) == LABEL_REF || PIC_DIRECT_ADDR_P (x)))
+        return TARGET_REGS;
+    } /* end of input-only processing.  */
+
+  if (((REGCLASS_HAS_FP_REG (class)
+       && (GET_CODE (x) == REG
+           && (GENERAL_OR_AP_REGISTER_P (REGNO (x))
+               || (FP_REGISTER_P (REGNO (x)) && mode == SImode
+                   && TARGET_FMOVD))))
+       || (REGCLASS_HAS_GENERAL_REG (class)
+          && GET_CODE (x) == REG
+          && FP_REGISTER_P (REGNO (x))))
+      && ! TARGET_SHMEDIA
+      && (mode == SFmode || mode == SImode))
+    return FPUL_REGS;
+  if ((class == FPUL_REGS
+       || (REGCLASS_HAS_FP_REG (class)
+           && ! TARGET_SHMEDIA && mode == SImode))
+      && (GET_CODE (x) == MEM
+          || (GET_CODE (x) == REG
+              && (REGNO (x) >= FIRST_PSEUDO_REGISTER
+                  || REGNO (x) == T_REG
+                  || system_reg_operand (x, VOIDmode)))))
+    {
+      if (class == FPUL_REGS)
+       return GENERAL_REGS;
+      return FPUL_REGS;
+    }
+  if ((class == TARGET_REGS
+       || (TARGET_SHMEDIA && class == SIBCALL_REGS))
+      && !satisfies_constraint_Csy (x)
+      && (GET_CODE (x) != REG || ! GENERAL_REGISTER_P (REGNO (x))))
+    return GENERAL_REGS;
+  if ((class == MAC_REGS || class == PR_REGS)
+      && GET_CODE (x) == REG && ! GENERAL_REGISTER_P (REGNO (x))
+      && class != REGNO_REG_CLASS (REGNO (x)))
+    return GENERAL_REGS;
+  if (class != GENERAL_REGS && GET_CODE (x) == REG
+      && TARGET_REGISTER_P (REGNO (x)))
+    return GENERAL_REGS;
+  return NO_REGS;
+}
+
+enum sh_divide_strategy_e sh_div_strategy = SH_DIV_STRATEGY_DEFAULT;
+
 #include "gt-sh.h"