]> gcc.gnu.org Git - gcc.git/blobdiff - gcc/config/sh/sh.c
hard-reg-set.h (REG_CANNOT_CHANGE_MODE_P): New.
[gcc.git] / gcc / config / sh / sh.c
index 30d890e8dca6225507a1b120f770ddf51f606ee0..d2cc47186d9e9fe168be6ca0f20570aca90daffb 100644 (file)
@@ -1,5 +1,5 @@
-/* Output routines for GCC for Hitachi Super-H.
-   Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001
+/* Output routines for GCC for Hitachi / SuperH SH.
+   Copyright (C) 1993, 1994, 1995, 1997, 1997, 1998, 1999, 2000, 2001, 2002
    Free Software Foundation, Inc.
    Contributed by Steve Chamberlain (sac@cygnus.com).
    Improved by Jim Wilson (wilson@cygnus.com). 
@@ -28,6 +28,7 @@ Boston, MA 02111-1307, USA.  */
 #include "tree.h"
 #include "flags.h"
 #include "expr.h"
+#include "optabs.h"
 #include "function.h"
 #include "regs.h"
 #include "hard-reg-set.h"
@@ -36,13 +37,28 @@ Boston, MA 02111-1307, USA.  */
 #include "toplev.h"
 #include "recog.h"
 #include "c-pragma.h"
+#include "integrate.h"
 #include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+#include "real.h"
+#include "langhooks.h"
 
 int code_for_indirect_jump_scratch = CODE_FOR_indirect_jump_scratch;
 
 #define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
 #define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
 
+/* These are some macros to abstract register modes.  */
+#define CONST_OK_FOR_ADD(size) \
+  (TARGET_SHMEDIA ? CONST_OK_FOR_P (size) : CONST_OK_FOR_I (size))
+#define GEN_MOV (*(TARGET_SHMEDIA64 ? gen_movdi : gen_movsi))
+#define GEN_ADD3 (*(TARGET_SHMEDIA64 ? gen_adddi3 : gen_addsi3))
+#define GEN_SUB3 (*(TARGET_SHMEDIA64 ? gen_subdi3 : gen_subsi3))
+
+/* 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.  */
@@ -73,7 +89,7 @@ int pragma_nosave_low_regs;
    sh_expand_prologue.  */
 int current_function_anonymous_args;
 
-/* Global variables for machine-dependent things. */
+/* Global variables for machine-dependent things.  */
 
 /* Which cpu are we scheduling for.  */
 enum processor_type sh_cpu;
@@ -93,31 +109,56 @@ int regno_reg_class[FIRST_PSEUDO_REGISTER] =
   GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
   GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
   GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
-  GENERAL_REGS, PR_REGS, T_REGS, NO_REGS,
-  MAC_REGS, MAC_REGS, FPUL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
+  GENERAL_REGS, GENERAL_REGS, GENERAL_REGS, GENERAL_REGS,
   FP0_REGS,FP_REGS, FP_REGS, FP_REGS,
   FP_REGS, FP_REGS, FP_REGS, FP_REGS,
   FP_REGS, FP_REGS, FP_REGS, FP_REGS,
   FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  FP_REGS, FP_REGS, FP_REGS, FP_REGS,
+  TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
+  TARGET_REGS, TARGET_REGS, TARGET_REGS, TARGET_REGS,
   DF_REGS, DF_REGS, DF_REGS, DF_REGS,
   DF_REGS, DF_REGS, DF_REGS, DF_REGS,
-  FPSCR_REGS,
+  NO_REGS, GENERAL_REGS, PR_REGS, T_REGS,
+  MAC_REGS, MAC_REGS, FPUL_REGS, FPSCR_REGS,
+  GENERAL_REGS,
 };
 
-char fp_reg_names[][5] =
-{
-  "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7",
-  "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15",
-  "fpul",
-  "xd0","xd2","xd4", "xd6", "xd8", "xd10", "xd12", "xd14",
-};
+char sh_register_names[FIRST_PSEUDO_REGISTER] \
+  [MAX_REGISTER_NAME_LENGTH + 1] = SH_REGISTER_NAMES_INITIALIZER;
+
+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.  */
 
-enum reg_class reg_class_from_letter[] =
+const enum reg_class reg_class_from_letter[] =
 {
-  /* a */ ALL_REGS, /* b */ NO_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS,
+  /* a */ ALL_REGS, /* b */ TARGET_REGS, /* c */ FPSCR_REGS, /* d */ DF_REGS,
   /* e */ NO_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,
@@ -140,14 +181,85 @@ static int mova_p PARAMS ((rtx));
 static rtx find_barrier PARAMS ((int, rtx, rtx));
 static int noncall_uses_reg PARAMS ((rtx, rtx, rtx *));
 static rtx gen_block_redirect PARAMS ((rtx, int, int));
-static void output_stack_adjust PARAMS ((int, rtx, int));
-static void push PARAMS ((int));
+static void output_stack_adjust PARAMS ((int, rtx, int, rtx (*) (rtx)));
+static rtx frame_insn PARAMS ((rtx));
+static rtx push PARAMS ((int));
 static void pop PARAMS ((int));
-static void push_regs PARAMS ((int, int));
-static int calc_live_regs PARAMS ((int *, int *));
+static void push_regs PARAMS ((HOST_WIDE_INT *));
+static void calc_live_regs PARAMS ((int *, HOST_WIDE_INT *));
 static void mark_use PARAMS ((rtx, rtx *));
 static HOST_WIDE_INT rounded_frame_size PARAMS ((int));
 static rtx mark_constant_pool_use PARAMS ((rtx));
+const struct attribute_spec sh_attribute_table[];
+static tree sh_handle_interrupt_handler_attribute PARAMS ((tree *, tree, tree, int, bool *));
+static tree sh_handle_sp_switch_attribute PARAMS ((tree *, tree, tree, int, bool *));
+static tree sh_handle_trap_exit_attribute PARAMS ((tree *, tree, tree, int, bool *));
+static void sh_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+static void sh_insert_attributes PARAMS ((tree, tree *));
+static int sh_adjust_cost PARAMS ((rtx, rtx, rtx, int));
+static int sh_use_dfa_interface PARAMS ((void));
+static int sh_issue_rate PARAMS ((void));
+
+static bool sh_cannot_modify_jumps_p PARAMS ((void));
+static bool sh_ms_bitfield_layout_p PARAMS ((tree));
+
+static void sh_encode_section_info PARAMS ((tree, int));
+static const char *sh_strip_name_encoding PARAMS ((const char *));
+static void sh_init_builtins PARAMS ((void));
+static void sh_media_init_builtins PARAMS ((void));
+static rtx sh_expand_builtin PARAMS ((tree, rtx, rtx, enum machine_mode, int));
+static int flow_dependent_p PARAMS ((rtx, rtx));
+static void flow_dependent_p_1 PARAMS ((rtx, rtx, void *));
+
+\f
+/* Initialize the GCC target structure.  */
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE sh_attribute_table
+
+/* The next two are used for debug info when compiling with -gdwarf.  */
+#undef TARGET_ASM_UNALIGNED_HI_OP
+#define TARGET_ASM_UNALIGNED_HI_OP "\t.uaword\t"
+#undef TARGET_ASM_UNALIGNED_SI_OP
+#define TARGET_ASM_UNALIGNED_SI_OP "\t.ualong\t"
+
+/* These are NULLed out on non-SH5 in OVERRIDE_OPTIONS.  */
+#undef TARGET_ASM_UNALIGNED_DI_OP
+#define TARGET_ASM_UNALIGNED_DI_OP "\t.uaquad\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
+
+#undef TARGET_ASM_FUNCTION_EPILOGUE
+#define TARGET_ASM_FUNCTION_EPILOGUE sh_output_function_epilogue
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES sh_insert_attributes
+
+#undef TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST sh_adjust_cost
+
+#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE 
+#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE \
+                               sh_use_dfa_interface
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE sh_issue_rate
+
+#undef TARGET_CANNOT_MODIFY_JUMPS_P
+#define TARGET_CANNOT_MODIFY_JUMPS_P sh_cannot_modify_jumps_p
+
+#undef TARGET_MS_BITFIELD_LAYOUT_P
+#define TARGET_MS_BITFIELD_LAYOUT_P sh_ms_bitfield_layout_p
+
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO sh_encode_section_info
+#undef TARGET_STRIP_NAME_ENCODING
+#define TARGET_STRIP_NAME_ENCODING sh_strip_name_encoding
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS sh_init_builtins
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN sh_expand_builtin
+
+struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Print the operand address in x to the stream.  */
 
@@ -215,10 +327,15 @@ print_operand_address (stream, x)
    ','  print LOCAL_LABEL_PREFIX
    '@'  print trap, rte or rts depending upon pragma interruptness
    '#'  output a nop if there is nothing to put in the delay slot
+   '''  print likelyhood suffix (/u for unlikely).
    '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'.
+   'N'  print 'r63' if the operand is (const_int 0).
+   'm'  print a pair `base,offset' or `base,index', for LD and ST.
+   'u'  prints the lowest 16 bits of CONST_INT, as an unsigned value.
    'o'  output an operator.  */
 
 void
@@ -231,37 +348,34 @@ print_operand (stream, x, code)
     {
     case '.':
       if (final_sequence
-         && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))
+         && ! INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0))
+         && get_attr_length (XVECEXP (final_sequence, 0, 1)))
        fprintf (stream, ASSEMBLER_DIALECT ? "/s" : ".s");
       break;
     case ',':
       fprintf (stream, "%s", LOCAL_LABEL_PREFIX);
       break;
     case '@':
-      {
-       int interrupt_handler;
-
-       if ((lookup_attribute
-            ("interrupt_handler",
-             DECL_MACHINE_ATTRIBUTES (current_function_decl)))
-           != NULL_TREE)
-         interrupt_handler = 1;
-       else
-         interrupt_handler = 0;
-       
       if (trap_exit)
        fprintf (stream, "trapa #%d", trap_exit);
-      else if (interrupt_handler)
+      else if (sh_cfun_interrupt_handler_p ())
        fprintf (stream, "rte");
       else
        fprintf (stream, "rts");
       break;
-      }
     case '#':
       /* Output a nop if there's nothing in the delay slot.  */
       if (dbr_sequence_length () == 0)
        fprintf (stream, "\n\tnop");
       break;
+    case '\'':
+      {
+       rtx note = find_reg_note (current_output_insn, REG_BR_PROB, 0);
+
+       if (note && INTVAL (XEXP (note, 0)) * 2 < REG_BR_PROB_BASE)
+         fputs ("/u", stream);
+       break;
+      }
     case 'O':
       x = mark_constant_pool_use (x);
       output_addr_const (stream, x);
@@ -282,7 +396,7 @@ print_operand (stream, x, code)
        case MEM:
          if (GET_CODE (XEXP (x, 0)) != PRE_DEC
              && GET_CODE (XEXP (x, 0)) != POST_INC)
-           x = adj_offsettable_operand (x, 4);
+           x = adjust_address (x, SImode, 4);
          print_operand_address (stream, XEXP (x, 0));
          break;
        default:
@@ -296,25 +410,138 @@ print_operand (stream, x, code)
        case MINUS: fputs ("sub", stream); break;
        case MULT:  fputs ("mul", stream); break;
        case DIV:   fputs ("div", stream); break;
+       case EQ:    fputs ("eq",  stream); break;
+       case NE:    fputs ("ne",  stream); break;
+       case GT:  case LT:  fputs ("gt",  stream); break;
+       case GE:  case LE:  fputs ("ge",  stream); break;
+       case GTU: case LTU: fputs ("gtu", stream); break;
+       case GEU: case LEU: fputs ("geu", stream); break;
        default:
          break;
        }
       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);
+      break;
+
+    case 'm':
+      if (GET_CODE (x) != MEM)
+       abort ();
+      x = XEXP (x, 0);
+      switch (GET_CODE (x))
+       {
+       case REG:
+       case SUBREG:
+         print_operand (stream, x, 0);
+         fputs (", 0", stream);
+         break;
+
+       case PLUS:
+         print_operand (stream, XEXP (x, 0), 0);
+         fputs (", ", stream);
+         print_operand (stream, XEXP (x, 1), 0);
+         break;
+
+       default:
+         abort ();
+       }
+      break;
+
+    case 'N':
+      if (x == CONST0_RTX (GET_MODE (x)))
+       {
+         fprintf ((stream), "r63");
+         break;
+       }
+      goto default_output;
+    case 'u':
+      if (GET_CODE (x) == CONST_INT)
+       {
+         fprintf ((stream), "%u", (unsigned) INTVAL (x) & (0x10000 - 1));
+         break;
+       }
+      /* Fall through.  */
+
+    default_output:
     default:
       switch (GET_CODE (x))
        {
+         /* 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 SUBREG:
+         if (SUBREG_BYTE (x) != 0
+             || GET_CODE (SUBREG_REG (x)) != REG)
+           abort ();
+
+         x = SUBREG_REG (x);
+         /* Fall through.  */
+
        case REG:
          if (FP_REGISTER_P (REGNO (x))
-             && GET_MODE_SIZE (GET_MODE (x)) > 4)
-           fprintf ((stream), "d%s", reg_names[REGNO (x)]+1);
+             && GET_MODE (x) == V16SFmode)
+           fprintf ((stream), "mtrx%s", reg_names[REGNO (x)] + 2);
+         else if (FP_REGISTER_P (REGNO (x))
+                  && GET_MODE (x) == V4SFmode)
+           fprintf ((stream), "fv%s", reg_names[REGNO (x)] + 2);
+         else if (GET_CODE (x) == REG
+                  && GET_MODE (x) == V2SFmode)
+           fprintf ((stream), "fp%s", reg_names[REGNO (x)] + 2);
+         else if (FP_REGISTER_P (REGNO (x))
+                  && GET_MODE_SIZE (GET_MODE (x)) > 4)
+           fprintf ((stream), "d%s", reg_names[REGNO (x)] + 1);
          else
            fputs (reg_names[REGNO (x)], (stream));
          break;
+
        case MEM:
          output_address (XEXP (x, 0));
          break;
+         
+       case CONST:
+         if (TARGET_SHMEDIA
+             && GET_CODE (XEXP (x, 0)) == SIGN_EXTEND
+             && GET_MODE (XEXP (x, 0)) == DImode
+             && 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);
+
+             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);
+                 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;
+           }
+
+         /* Fall through.  */
        default:
-         fputc ('#', stream);
+         if (TARGET_SH1)
+           fputc ('#', stream);
          output_addr_const (stream, x);
          break;
        }
@@ -460,17 +687,21 @@ prepare_move_operands (operands, mode)
      rtx operands[];
      enum machine_mode mode;
 {
-  if (mode == SImode && flag_pic)
+  if ((mode == SImode || mode == DImode) && flag_pic)
     {
       rtx temp;
       if (SYMBOLIC_CONST_P (operands[1]))
        {
          if (GET_CODE (operands[0]) == MEM)
            operands[1] = force_reg (Pmode, operands[1]);
+         else if (TARGET_SHMEDIA
+                  && GET_CODE (operands[1]) == LABEL_REF
+                  && target_reg_operand (operands[0], mode))
+           /* It's ok.  */;
          else
            {
              temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
-             operands[1] = legitimize_pic_address (operands[1], SImode, temp);
+             operands[1] = legitimize_pic_address (operands[1], mode, temp);
            }
        }
       else if (GET_CODE (operands[1]) == CONST
@@ -479,8 +710,8 @@ prepare_move_operands (operands, mode)
        {
          temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
          temp = legitimize_pic_address (XEXP (XEXP (operands[1], 0), 0),
-                                        SImode, temp);
-         operands[1] = expand_binop (SImode, add_optab, temp,
+                                        mode, temp);
+         operands[1] = expand_binop (mode, add_optab, temp,
                                      XEXP (XEXP (operands[1], 0), 1),
                                      no_new_pseudos ? temp
                                      : gen_reg_rtx (Pmode),
@@ -717,7 +948,7 @@ output_far_jump (insn, op)
      rtx op;
 {
   struct { rtx lab, reg, op; } this;
-  rtx braf_base_lab;
+  rtx braf_base_lab = NULL_RTX;
   const char *jump;
   int far;
   int offset = branch_dest (insn) - INSN_ADDRESSES (INSN_UID (insn));
@@ -764,9 +995,19 @@ output_far_jump (insn, op)
        print_slot (final_sequence);
 
       this.reg = gen_rtx_REG (SImode, 13);
-      output_asm_insn ("mov.l  r13,@-r15", 0);
+      /* We must keep the stack aligned to 8-byte boundaries on SH5.
+        Fortunately, MACL is fixed and call-clobbered, and we never
+        need its value across jumps, so save r13 in it instead of in
+        the stack.  */
+      if (TARGET_SH5)
+       output_asm_insn ("lds   r13, macl", 0);
+      else
+       output_asm_insn ("mov.l r13,@-r15", 0);
       output_asm_insn (jump, &this.lab);
-      output_asm_insn ("mov.l  @r15+,r13", 0);
+      if (TARGET_SH5)
+       output_asm_insn ("sts   macl, r13", 0);
+      else
+       output_asm_insn ("mov.l @r15+,r13", 0);
     }
   if (far && flag_pic && TARGET_SH2)
     {
@@ -916,21 +1157,27 @@ output_file_start (file)
 
   if (TARGET_LITTLE_ENDIAN)
     fprintf (file, "\t.little\n");
+
+  if (TARGET_SHCOMPACT)
+    fprintf (file, "\t.mode\tSHcompact\n");
+  else if (TARGET_SHMEDIA)
+    fprintf (file, "\t.mode\tSHmedia\n\t.abi\t%i\n",
+            TARGET_SHMEDIA64 ? 64 : 32);
 }
 \f
 /* Actual number of instructions used to make a shift by N.  */
-static char ashiftrt_insns[] =
+static const char ashiftrt_insns[] =
   { 0,1,2,3,4,5,8,8,8,8,8,8,8,8,8,8,2,3,4,5,8,8,8,8,8,8,8,8,8,8,8,2};
 
 /* Left shift and logical right shift are the same.  */
-static char shift_insns[]    =
+static const char shift_insns[]    =
   { 0,1,1,2,2,3,3,4,1,2,2,3,3,4,3,3,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
 
 /* Individual shift amounts needed to get the above length sequences.
    One bit right shifts clobber the T bit, so when possible, put one bit
    shifts in the middle of the sequence, so the ends are eligible for
    branch delay slots.  */
-static short shift_amounts[32][5] = {
+static const short shift_amounts[32][5] = {
   {0}, {1}, {2}, {2, 1},
   {2, 2}, {2, 1, 2}, {2, 2, 2}, {2, 2, 1, 2},
   {8}, {8, 1}, {8, 2}, {8, 1, 2},
@@ -944,10 +1191,10 @@ static short shift_amounts[32][5] = {
    might be clobbered.  This is typically used when combined with some
    kind of sign or zero extension.  */
    
-static char ext_shift_insns[]    =
+static const char ext_shift_insns[]    =
   { 0,1,1,2,2,3,2,2,1,2,2,3,3,3,2,2,1,2,2,3,3,4,3,3,2,3,3,4,4,4,3,3};
 
-static short ext_shift_amounts[32][4] = {
+static const short ext_shift_amounts[32][4] = {
   {0}, {1}, {2}, {2, 1},
   {2, 2}, {2, 1, 2}, {8, -2}, {8, -1},
   {8}, {8, 1}, {8, 2}, {8, 1, 2},
@@ -993,6 +1240,19 @@ shiftcosts (x)
 {
   int value;
 
+  if (TARGET_SHMEDIA)
+    return 1;
+
+  if (GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD)
+    {
+      if (GET_MODE (x) == DImode
+         && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && INTVAL (XEXP (x, 1)) == 1)
+       return 2;
+
+      /* Everything else is invalid, because there is no pattern for it.  */
+      return 10000;
+    }
   /* If shift by a non constant, then this will be expensive.  */
   if (GET_CODE (XEXP (x, 1)) != CONST_INT)
     return SH_DYNAMIC_SHIFT_COST;
@@ -1025,6 +1285,17 @@ andcosts (x)
     return 1;
 
   i = INTVAL (XEXP (x, 1));
+
+  if (TARGET_SHMEDIA)
+    {
+      if ((GET_CODE (XEXP (x, 1)) == CONST_INT
+          && CONST_OK_FOR_J (INTVAL (XEXP (x, 1))))
+         || EXTRA_CONSTRAINT_S (XEXP (x, 1)))
+       return 1;
+      else
+       return 2;
+    }
+
   /* These constants are single cycle extu.[bw] instructions.  */
   if (i == 0xff || i == 0xffff)
     return 1;
@@ -1054,9 +1325,30 @@ addsubcosts (x)
 
   /* Likewise for small constants.  */
   if (GET_CODE (XEXP (x, 1)) == CONST_INT
-      && CONST_OK_FOR_I (INTVAL (XEXP (x, 1))))
+      && CONST_OK_FOR_ADD (INTVAL (XEXP (x, 1))))
     return 1;
 
+  if (TARGET_SHMEDIA)
+    switch (GET_CODE (XEXP (x, 1)))
+      {
+      case CONST:
+      case LABEL_REF:
+      case SYMBOL_REF:
+       return TARGET_SHMEDIA64 ? 5 : 3;
+
+      case CONST_INT:
+       if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1))))
+          return 2;
+       else if (CONST_OK_FOR_J (INTVAL (XEXP (x, 1)) >> 16))
+         return 3;
+       else if (CONST_OK_FOR_J ((INTVAL (XEXP (x, 1)) >> 16) >> 16))
+         return 4;
+
+       /* Fall through.  */
+      default:
+         return 5;
+      }
+
   /* Any other constant requires a 2 cycle pc-relative load plus an
      addition.  */
   return 3;
@@ -1067,6 +1359,9 @@ int
 multcosts (x)
      rtx x ATTRIBUTE_UNUSED;
 {
+  if (TARGET_SHMEDIA)
+    return 3;
+
   if (TARGET_SH2)
     {
       /* We have a mul insn, so we can never take more than the mul and the
@@ -1407,7 +1702,7 @@ shl_and_kind (left_rtx, mask_rtx, attrp)
              continue;
            }
          /* ??? Could try to put zero extend into initial right shift,
-            or even shift a bit left before the right shift. */
+            or even shift a bit left before the right shift.  */
          /* Determine value of first part of left shift, to get to the
             zero extend cut-off point.  */
          first = width - exact_log2 (lsb2) + right;
@@ -1674,7 +1969,7 @@ shl_sext_kind (left_rtx, size_rtx, costp)
          cost = ext_shift_insns[ext - insize] + 1 + shift_insns[size - ext];
          if (cost < best_cost)
            {
-             kind = ext / 8U;
+             kind = ext / (unsigned) 8;
              best_cost = cost;
            }
        }
@@ -1691,7 +1986,7 @@ shl_sext_kind (left_rtx, size_rtx, costp)
        continue;
       if (cost < best_cost)
        {
-         kind = ext / 8U + 2;
+         kind = ext / (unsigned) 8 + 2;
          best_cost = cost;
        }
     }
@@ -1868,6 +2163,27 @@ gen_shl_sext (dest, left_rtx, size_rtx, source)
     }
   return 0;
 }
+
+/* Prefix a symbol_ref name with "datalabel".  */
+
+rtx
+gen_datalabel_ref (sym)
+     rtx sym;
+{
+  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 ();
+
+  XSTR (sym, 0) = concat (SH_DATALABEL_ENCODING, XSTR (sym, 0), NULL);
+
+  return sym;
+}
+
 \f
 /* 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
@@ -2030,6 +2346,7 @@ dump_table (scan)
   int i;
   int need_align = 1;
   rtx lab, ref;
+  int have_di = 0;
 
   /* Do two passes, first time dump out the HI sized constants.  */
 
@@ -2054,10 +2371,87 @@ dump_table (scan)
              scan = emit_insn_after (gen_consttable_window_end (lab), scan);
            }
        }
+      else if (p->mode == DImode || p->mode == DFmode)
+       have_di = 1;
     }
 
   need_align = 1;
 
+  if (TARGET_SHCOMPACT && have_di)
+    {
+      rtx align_insn = NULL_RTX;
+
+      scan = emit_label_after (gen_label_rtx (), scan);
+      scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+      need_align = 0;
+
+      for (i = 0; i < pool_size; i++)
+       {
+         pool_node *p = &pool_vector[i];
+
+         switch (p->mode)
+           {
+           case HImode:
+             break;
+           case SImode:
+           case SFmode:
+             if (align_insn)
+               {
+                 for (lab = p->label; lab; lab = LABEL_REFS (lab))
+                   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))
+                   {
+                     lab = XEXP (ref, 0);
+                     emit_insn_before (gen_consttable_window_end (lab),
+                                      align_insn);
+                   }
+                 delete_insn (align_insn);
+                 align_insn = NULL_RTX;
+                 continue;
+               }
+             else
+               {
+                 for (lab = p->label; lab; lab = LABEL_REFS (lab))
+                   scan = emit_label_after (lab, scan);
+                 scan = emit_insn_after (gen_consttable_4 (p->value,
+                                                           const0_rtx), scan);
+                 need_align = ! need_align;
+               }
+             break;
+           case DFmode:
+           case DImode:
+             if (need_align)
+               {
+                 scan = emit_insn_after (gen_align_log (GEN_INT (3)), scan);
+                 align_insn = scan;
+                 need_align = 0;
+               }
+             for (lab = p->label; lab; lab = LABEL_REFS (lab))
+               scan = emit_label_after (lab, scan);
+             scan = emit_insn_after (gen_consttable_8 (p->value, const0_rtx),
+                                     scan);
+             break;
+           default:
+             abort ();
+             break;
+           }
+
+         if (p->mode != HImode)
+           {
+             for (ref = p->wend; ref; ref = LABEL_NEXTREF (ref))
+               {
+                 lab = XEXP (ref, 0);
+                 scan = emit_insn_after (gen_consttable_window_end (lab),
+                                         scan);
+               }
+           }
+       }
+
+      pool_size = 0;
+    }
+  
   for (i = 0; i < pool_size; i++)
     {
       pool_node *p = &pool_vector[i];
@@ -2114,7 +2508,7 @@ dump_table (scan)
   pool_window_last = 0;
 }
 
-/* Return non-zero if constant would be an ok source for a
+/* Return nonzero if constant would be an ok source for a
    mov.w instead of a mov.l.  */
 
 static int
@@ -2126,7 +2520,7 @@ hi_const (src)
          && INTVAL (src) <= 32767);
 }
 
-/* Non-zero if the insn is a move instruction which needs to be fixed.  */
+/* Nonzero if the insn is a move instruction which needs to be fixed.  */
 
 /* ??? For a DImode/DFmode moves, we don't need to fix it if each half of the
    CONST_DOUBLE input value is CONST_OK_FOR_I.  For a SFmode move, we don't
@@ -2154,9 +2548,16 @@ broken_move (insn)
                && GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
                && (fp_zero_operand (SET_SRC (pat))
                    || fp_one_operand (SET_SRC (pat)))
-               /* ??? If this is a -m4 or -m4-single compilation, we don't
-                  know the current setting of fpscr, so disable fldi.  */
-               && (! TARGET_SH4 || TARGET_FMOVD)
+               /* ??? If this is a -m4 or -m4-single compilation, in general
+                  we don't know the current setting of fpscr, so disable fldi.
+                  There is an exception if this was a register-register move
+                  before reload - and hence it was ascertained that we have
+                  single precision setting - and in a post-reload optimization
+                  we changed this to do a constant load.  In that case
+                  we don't have an r0 clobber, hence we must use fldi.  */
+               && (! TARGET_SH4 || TARGET_FMOVD
+                   || (GET_CODE (XEXP (XVECEXP (PATTERN (insn), 0, 2), 0))
+                       == SCRATCH))
                && GET_CODE (SET_DEST (pat)) == REG
                && FP_REGISTER_P (REGNO (SET_DEST (pat))))
          && (GET_CODE (SET_SRC (pat)) != CONST_INT
@@ -2192,6 +2593,7 @@ find_barrier (num_mova, mova, from)
   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;
@@ -2273,6 +2675,18 @@ find_barrier (num_mova, mova, from)
            }
          else
            {
+             /* We dump DF/DI constants before SF/SI ones, because
+                the limit is the same, but the alignment requirements
+                are higher.  We may waste up to 4 additional bytes
+                for alignment, and the DF/DI constant may have
+                another SF/SI constant placed before it. */
+             if (TARGET_SHCOMPACT
+                 && ! found_di
+                 && (mode == DFmode || mode == DImode))
+               {
+                 found_di = 1;
+                 si_limit -= 8;
+               }
              while (si_align > 2 && found_si + si_align - 2 > count_si)
                si_align >>= 1;
              if (found_si > count_si)
@@ -2646,7 +3060,8 @@ gen_block_redirect (jump, addr, need_block)
   dest = XEXP (SET_SRC (PATTERN (jump)), 0);
   /* If the branch is out of range, try to find a scratch register for it.  */
   if (optimize
-      && (INSN_ADDRESSES (INSN_UID (dest)) - addr + 4092U > 4092 + 4098))
+      && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
+         > 4092 + 4098))
     {
       rtx scan;
       /* Don't look for the stack pointer as a scratch register,
@@ -2722,7 +3137,7 @@ gen_block_redirect (jump, addr, need_block)
        {
          dest = JUMP_LABEL (next);
          if (dest
-             && (INSN_ADDRESSES (INSN_UID (dest)) - addr + 4092U
+             && (INSN_ADDRESSES (INSN_UID (dest)) - addr + (unsigned) 4092
                  > 4092 + 4098))
            gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), -1);
        }
@@ -2801,6 +3216,11 @@ gen_far_branch (bp)
   JUMP_LABEL (jump) = bp->far_label;
   if (! invert_jump (insn, label, 1))
     abort ();
+  (emit_insn_after
+   (gen_stuff_delay_slot
+    (GEN_INT (INSN_UID (XEXP (SET_SRC (PATTERN (jump)), 0))),
+     GEN_INT (recog_memoized (insn) == CODE_FOR_branch_false)),
+    insn));
   /* Prevent reorg from undoing our splits.  */
   gen_block_redirect (jump, bp->address += 2, 2);
 }
@@ -2877,9 +3297,9 @@ barrier_align (barrier_or_label)
       /* If this is a very small table, we want to keep the alignment after
         the table to the minimum for proper code alignment.  */
       return ((TARGET_SMALLCODE
-              || (XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
+              || ((unsigned) XVECLEN (pat, 1) * GET_MODE_SIZE (GET_MODE (pat))
                   <= (unsigned)1 << (CACHE_LOG - 2)))
-             ? 1 : CACHE_LOG);
+             ? 1 << TARGET_SHMEDIA : CACHE_LOG);
     }
 
   if (TARGET_SMALLCODE)
@@ -2936,29 +3356,32 @@ barrier_align (barrier_or_label)
        }
       if (prev
          && GET_CODE (prev) == JUMP_INSN
-         && JUMP_LABEL (prev)
-         && (jump_to_next || next_real_insn (JUMP_LABEL (prev)) == next
+         && JUMP_LABEL (prev))
+       {
+         rtx x;
+         if (jump_to_next
+             || next_real_insn (JUMP_LABEL (prev)) == next
              /* If relax_delay_slots() decides NEXT was redundant
                 with some previous instruction, it will have
                 redirected PREV's jump to the following insn.  */
              || JUMP_LABEL (prev) == next_nonnote_insn (next)
-             /* There is no upper bound on redundant instructions that
-                might have been skipped, but we must not put an alignment
-                where none had been before.  */
-             || (NEXT_INSN (PREV_INSN (prev)) != prev
-                 && ((INSN_CODE (NEXT_INSN (NEXT_INSN (prev)))
-                      == CODE_FOR_block_branch_redirect)
-                     || (INSN_CODE (NEXT_INSN (NEXT_INSN (prev)))
-                         == CODE_FOR_indirect_jump_scratch)))))
-       {
-         rtx pat = PATTERN (prev);
-         if (GET_CODE (pat) == PARALLEL)
-           pat = XVECEXP (pat, 0, 0);
-         if (credit - slot >= (GET_CODE (SET_SRC (pat)) == PC ? 2 : 0))
-           return 0;
+             /* There is no upper bound on redundant instructions
+                that might have been skipped, but we must not put an
+                alignment where none had been before.  */
+             || (x = (NEXT_INSN (NEXT_INSN (PREV_INSN (prev)))),           
+                 (INSN_P (x) 
+                  && (INSN_CODE (x) == CODE_FOR_block_branch_redirect
+                      || INSN_CODE (x) == CODE_FOR_indirect_jump_scratch))))
+           {
+             rtx pat = PATTERN (prev);
+             if (GET_CODE (pat) == PARALLEL)
+               pat = XVECEXP (pat, 0, 0);
+             if (credit - slot >= (GET_CODE (SET_SRC (pat)) == PC ? 2 : 0))
+               return 0;
+           }
        }
     }
-
+  
   return CACHE_LOG;
 }
 
@@ -2983,6 +3406,10 @@ sh_loop_align (label)
       || GET_CODE (PATTERN (next)) == ADDR_DIFF_VEC
       || recog_memoized (next) == CODE_FOR_consttable_2)
     return 0;
+
+  if (TARGET_SH5)
+    return 3;
+
   return 2;
 }
 
@@ -3004,7 +3431,10 @@ machine_dependent_reorg (first)
      optimizing, they'll have already been split.  Otherwise, make
      sure we don't split them too late.  */
   if (! optimize)
-    split_all_insns (0);
+    split_all_insns_noflow ();
+
+  if (TARGET_SHMEDIA)
+    return;
 
   /* If relaxing, generate pseudo-ops to associate function calls with
      the symbols they call.  It does no harm to not generate these
@@ -3184,7 +3614,7 @@ machine_dependent_reorg (first)
                 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. */
+                not the most recent insn which sets the register.  */
 
              if (foundinsn
                  && (scanset
@@ -3209,9 +3639,9 @@ machine_dependent_reorg (first)
              or pseudo-op.  */
 
          label = gen_label_rtx ();
-         REG_NOTES (link) = gen_rtx_EXPR_LIST (REG_LABEL, label,
+         REG_NOTES (link) = gen_rtx_INSN_LIST (REG_LABEL, label,
                                                REG_NOTES (link));
-         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_LABEL, label,
+         REG_NOTES (insn) = gen_rtx_INSN_LIST (REG_LABEL, label,
                                                REG_NOTES (insn));
          if (rescan)
            {
@@ -3227,7 +3657,7 @@ machine_dependent_reorg (first)
                          || ((reg2 = sfunc_uses_reg (scan))
                              && REGNO (reg2) == REGNO (reg))))
                    REG_NOTES (scan)
-                     = gen_rtx_EXPR_LIST (REG_LABEL, label, REG_NOTES (scan));
+                     = gen_rtx_INSN_LIST (REG_LABEL, label, REG_NOTES (scan));
                }
              while (scan != dies);
            }
@@ -3288,6 +3718,7 @@ machine_dependent_reorg (first)
             behind.  */
          rtx barrier = find_barrier (num_mova, mova, insn);
          rtx last_float_move, last_float = 0, *last_float_addr;
+         int may_need_align = 1;
 
          if (num_mova && ! mova_p (mova))
            {
@@ -3345,6 +3776,27 @@ machine_dependent_reorg (first)
                      if (last_float
                          && reg_set_between_p (r0_rtx, last_float_move, scan))
                        last_float = 0;
+                     if (TARGET_SHCOMPACT)
+                       {
+                         /* The first SFmode constant after a DFmode
+                            constant may be pulled before a sequence
+                            of DFmode constants, so the second SFmode
+                            needs a label, just in case.  */
+                         if (GET_MODE_SIZE (mode) == 4)
+                           {
+                             if (last_float && may_need_align)
+                               last_float = 0;
+                             may_need_align = 0;
+                           }
+                         if (last_float
+                             && (GET_MODE_SIZE (GET_MODE (last_float))
+                                 != GET_MODE_SIZE (mode)))
+                           {
+                             last_float = 0;
+                             if (GET_MODE_SIZE (mode) == 4)
+                               may_need_align = 1;
+                           }
+                       }
                      lab = add_constant (src, mode, last_float);
                      if (lab)
                        emit_insn_before (gen_mova (lab), scan);
@@ -3376,6 +3828,7 @@ machine_dependent_reorg (first)
 
                      /* Remove the clobber of r0.  */
                      XEXP (clobber, 0) = gen_rtx_SCRATCH (Pmode);
+                     RTX_UNCHANGING_P (newsrc) = 1;
                    }
                  /* This is a mova needing a label.  Create it.  */
                  else if (GET_CODE (src) == UNSPEC
@@ -3384,7 +3837,7 @@ machine_dependent_reorg (first)
                    {
                      lab = add_constant (XVECEXP (src, 0, 0), mode, 0);
                      newsrc = gen_rtx_LABEL_REF (VOIDmode, lab);
-                     newsrc = gen_rtx_UNSPEC (VOIDmode,
+                     newsrc = gen_rtx_UNSPEC (SImode,
                                               gen_rtvec (1, newsrc),
                                               UNSPEC_MOVA);
                    }
@@ -3393,8 +3846,8 @@ machine_dependent_reorg (first)
                      lab = add_constant (src, mode, 0);
                      newsrc = gen_rtx_MEM (mode,
                                            gen_rtx_LABEL_REF (VOIDmode, lab));
+                     RTX_UNCHANGING_P (newsrc) = 1;
                    }
-                 RTX_UNCHANGING_P (newsrc) = 1;
                  *patp = gen_rtx_SET (VOIDmode, dst, newsrc);
                  INSN_CODE (scan) = -1;
                }
@@ -3409,7 +3862,7 @@ machine_dependent_reorg (first)
   split_branches (first);
 
   /* The INSN_REFERENCES_ARE_DELAYED in sh.h is problematic because it
-     also has an effect on the register that holds the addres of the sfunc.
+     also has an effect on the register that holds the address of the sfunc.
      Insert an extra dummy insn in front of each sfunc that pretends to
      use this register.  */
   if (flag_delayed_branch)
@@ -3585,7 +4038,7 @@ split_branches (first)
                    && recog_memoized (beyond) == CODE_FOR_jump
                    && ((INSN_ADDRESSES
                         (INSN_UID (XEXP (SET_SRC (PATTERN (beyond)), 0)))
-                        - INSN_ADDRESSES (INSN_UID (insn)) + 252U)
+                        - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
                        > 252 + 258 + 2))
                  gen_block_redirect (beyond,
                                      INSN_ADDRESSES (INSN_UID (beyond)), 1);
@@ -3599,7 +4052,7 @@ split_branches (first)
                && recog_memoized (next) == CODE_FOR_jump
                && ((INSN_ADDRESSES
                     (INSN_UID (XEXP (SET_SRC (PATTERN (next)), 0)))
-                    - INSN_ADDRESSES (INSN_UID (insn)) + 252U)
+                    - INSN_ADDRESSES (INSN_UID (insn)) + (unsigned) 252)
                    > 252 + 258 + 2))
              gen_block_redirect (next, INSN_ADDRESSES (INSN_UID (next)), 1);
          }
@@ -3783,55 +4236,78 @@ static int extra_push;
   of a general register that we may clobber.  */
 
 static void
-output_stack_adjust (size, reg, temp)
+output_stack_adjust (size, reg, temp, emit_fn)
      int size;
      rtx reg;
      int temp;
+     rtx (*emit_fn) PARAMS ((rtx));
 {
   if (size)
     {
-      if (CONST_OK_FOR_I (size))
-       emit_insn (gen_addsi3 (reg, reg, GEN_INT (size)));
+      HOST_WIDE_INT align = STACK_BOUNDARY / BITS_PER_UNIT;
+
+      if (size % align)
+       abort ();
+
+      if (CONST_OK_FOR_ADD (size))
+       emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size)));
       /* Try to do it with two partial adjustments; however, we must make
         sure that the stack is properly aligned at all times, in case
-        an interrupt occurs between the two partial adjustments. */
-      else if (CONST_OK_FOR_I (size / 2 & -4)
-              && CONST_OK_FOR_I (size - (size / 2 & -4)))
+        an interrupt occurs between the two partial adjustments.  */
+      else if (CONST_OK_FOR_ADD (size / 2 & -align)
+              && CONST_OK_FOR_ADD (size - (size / 2 & -align)))
        {
-         emit_insn (gen_addsi3 (reg, reg, GEN_INT (size / 2 & -4)));
-         emit_insn (gen_addsi3 (reg, reg, GEN_INT (size - (size / 2 & -4))));
+         emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size / 2 & -align)));
+         emit_fn (GEN_ADD3 (reg, reg, GEN_INT (size - (size / 2 & -align))));
        }
       else
        {
          rtx const_reg;
+         rtx insn;
 
          /* If TEMP is invalid, we could temporarily save a general
             register to MACL.  However, there is currently no need
             to handle this case, so just abort when we see it.  */
          if (temp < 0)
            abort ();
-         const_reg = gen_rtx_REG (SImode, temp);
+         const_reg = gen_rtx_REG (GET_MODE (reg), temp);
 
          /* If SIZE is negative, subtract the positive value.
             This sometimes allows a constant pool entry to be shared
             between prologue and epilogue code.  */
          if (size < 0)
            {
-             emit_insn (gen_movsi (const_reg, GEN_INT (-size)));
-             emit_insn (gen_subsi3 (reg, reg, const_reg));
+             emit_insn (GEN_MOV (const_reg, GEN_INT (-size)));
+             insn = emit_fn (GEN_SUB3 (reg, reg, const_reg));
            }
          else
            {
-             emit_insn (gen_movsi (const_reg, GEN_INT (size)));
-             emit_insn (gen_addsi3 (reg, reg, const_reg));
+             emit_insn (GEN_MOV (const_reg, GEN_INT (size)));
+             insn = emit_fn (GEN_ADD3 (reg, reg, const_reg));
            }
+         if (emit_fn == frame_insn)
+           REG_NOTES (insn)
+             = (gen_rtx_EXPR_LIST
+                (REG_FRAME_RELATED_EXPR,
+                 gen_rtx_SET (VOIDmode, reg,
+                              gen_rtx_PLUS (SImode, reg, GEN_INT (size))),
+                 REG_NOTES (insn)));
        }
     }
 }
 
+static rtx
+frame_insn (x)
+     rtx x;
+{
+  x = emit_insn (x);
+  RTX_FRAME_RELATED_P (x) = 1;
+  return x;
+}
+
 /* Output RTL to push register RN onto the stack.  */
 
-static void
+static rtx
 push (rn)
      int rn;
 {
@@ -3842,7 +4318,7 @@ push (rn)
           && FP_OR_XD_REGISTER_P (rn))
     {
       if (FP_REGISTER_P (rn) && (rn - FIRST_FP_REG) & 1)
-       return;
+       return NULL_RTX;
       x = gen_push_4 (gen_rtx_REG (DFmode, rn));
     }
   else if (TARGET_SH3E && FP_REGISTER_P (rn))
@@ -3850,10 +4326,11 @@ push (rn)
   else
     x = gen_push (gen_rtx_REG (SImode, rn));
 
-  x = emit_insn (x);
+  x = frame_insn (x);
   REG_NOTES (x)
     = gen_rtx_EXPR_LIST (REG_INC,
                         gen_rtx_REG (SImode, STACK_POINTER_REGNUM), 0);
+  return x;
 }
 
 /* Output RTL to pop register RN from the stack.  */
@@ -3886,21 +4363,18 @@ pop (rn)
 /* Generate code to push the regs specified in the mask.  */
 
 static void
-push_regs (mask, mask2)
-     int mask, mask2;
+push_regs (mask)
+     HOST_WIDE_INT *mask;
 {
   int i;
 
   /* 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 < 32; i++)
-    if (mask & (1 << i) && i != PR_REG)
-      push (i);
-  for (i = 32; i < FIRST_PSEUDO_REGISTER; i++)
-    if (mask2 & (1 << (i - 32)))
+  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+    if (i != PR_REG && mask[i / 32] & (1 << (i % 32)))
       push (i);
-  if (mask & (1 << PR_REG))
+  if (mask[PR_REG / 32] & (1 << (PR_REG % 32)))
     push (PR_REG);
 }
 
@@ -3911,25 +4385,20 @@ push_regs (mask, mask2)
    function, and if we call another function (we can tell by looking at PR),
    make sure that all the regs it clobbers are safe too.  */
 
-static int
-calc_live_regs (count_ptr, live_regs_mask2)
+static void
+calc_live_regs (count_ptr, live_regs_mask)
      int *count_ptr;
-     int *live_regs_mask2;
+     HOST_WIDE_INT *live_regs_mask;
 {
   int reg;
-  int live_regs_mask = 0;
   int count;
   int interrupt_handler;
+  int pr_live;
 
-  if ((lookup_attribute
-       ("interrupt_handler",
-       DECL_MACHINE_ATTRIBUTES (current_function_decl)))
-      != NULL_TREE)
-    interrupt_handler = 1;
-  else
-    interrupt_handler = 0;
+  interrupt_handler = sh_cfun_interrupt_handler_p ();
 
-  *live_regs_mask2 = 0;
+  for (count = 0; 32 * count < FIRST_PSEUDO_REGISTER; count++)
+    live_regs_mask[count] = 0;
   /* If we can save a lot of saves by switching to double mode, do that.  */
   if (TARGET_SH4 && TARGET_FMOVD && TARGET_FPU_SINGLE)
     for (count = 0, reg = FIRST_FP_REG; reg <= LAST_FP_REG; reg += 2)
@@ -3940,50 +4409,65 @@ calc_live_regs (count_ptr, live_regs_mask2)
          target_flags &= ~FPU_SINGLE_BIT;
          break;
        }
+  /* PR_MEDIA_REG is a general purpose register, thus global_alloc already
+     knows how to use it.  That means the pseudo originally allocated for
+     the initial value can become the PR_MEDIA_REG hard register, as seen for
+     execute/20010122-1.c:test9.  */
+  if (TARGET_SHMEDIA)
+    pr_live = regs_ever_live[PR_MEDIA_REG];
+  else
+    {
+      rtx pr_initial = has_hard_reg_initial_val (Pmode, PR_REG);
+      pr_live = (pr_initial
+                ? REGNO (pr_initial) != (PR_REG) : regs_ever_live[PR_REG]);
+    }
+  /* Force PR to be live if the prologue has to call the SHmedia
+     argument decoder or register saver.  */
+  if (TARGET_SHCOMPACT
+      && ((current_function_args_info.call_cookie
+          & ~ CALL_COOKIE_RET_TRAMP (1))
+         || current_function_has_nonlocal_label))
+    pr_live = 1;
   for (count = 0, reg = FIRST_PSEUDO_REGISTER - 1; reg >= 0; reg--)
     {
-      if ((interrupt_handler && ! pragma_trapa)
+      if (reg == (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG)
+         ? pr_live
+         : (interrupt_handler && ! pragma_trapa)
          ? (/* Need to save all the regs ever live.  */
             (regs_ever_live[reg]
              || (call_used_regs[reg]
                  && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG)
-                 && regs_ever_live[PR_REG]))
+                 && pr_live))
             && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
             && reg != RETURN_ADDRESS_POINTER_REGNUM
-            && reg != T_REG && reg != GBR_REG && reg != FPSCR_REG)
+            && reg != T_REG && reg != GBR_REG)
          : (/* Only push those regs which are used and need to be saved.  */
             regs_ever_live[reg] && ! call_used_regs[reg]))
        {
-         if (reg >= 32)
-           *live_regs_mask2 |= 1 << (reg - 32);
-         else
-           live_regs_mask |= 1 << reg;
-         count++;
-         if (TARGET_SH4 && TARGET_FMOVD && FP_OR_XD_REGISTER_P (reg))
+         live_regs_mask[reg / 32] |= 1 << (reg % 32);
+         count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg));
+
+         if ((TARGET_SH4 || TARGET_SH5) && TARGET_FMOVD
+             && GET_MODE_CLASS (REGISTER_NATURAL_MODE (reg)) == MODE_FLOAT)
            {
              if (FP_REGISTER_P (reg))
                {
                  if (! TARGET_FPU_SINGLE && ! regs_ever_live[reg ^ 1])
                    {
-                     if (reg >= 32)
-                       *live_regs_mask2 |= 1 << ((reg ^ 1) - 32);
-                     else
-                       live_regs_mask |= 1 << (reg ^ 1);
-                     count++;
+                     live_regs_mask[(reg ^ 1) / 32] |= 1 << ((reg ^ 1) % 32);
+                     count += GET_MODE_SIZE (REGISTER_NATURAL_MODE (reg ^ 1));
                    }
                }
-             else /* if (XD_REGISTER_P (reg)) */
+             else if (XD_REGISTER_P (reg))
                {
                  /* Must switch to double mode to access these registers.  */
                  target_flags &= ~FPU_SINGLE_BIT;
-                 count++;
                }
            }
        }
     }
 
-  *count_ptr = count * UNITS_PER_WORD;
-  return live_regs_mask;
+  *count_ptr = count;
 }
 
 /* Code to generate prologue and epilogue sequences */
@@ -4001,39 +4485,113 @@ rounded_frame_size (pushed)
   return ((size + pushed + align - 1) & -align) - pushed;
 }
 
+/* Choose a call-clobbered target-branch register that remains
+   unchanged along the whole function.  We set it up as the return
+   value in the prologue.  */
+int
+sh_media_register_for_return ()
+{
+  int regno;
+  int tr0_used;
+
+  if (! current_function_is_leaf)
+    return -1;
+
+  tr0_used = flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
+
+  for (regno = FIRST_TARGET_REG + tr0_used; regno <= LAST_TARGET_REG; regno++)
+    if (call_used_regs[regno] && ! regs_ever_live[regno])
+      return regno;
+
+  return -1;
+}
+
 void
 sh_expand_prologue ()
 {
-  int live_regs_mask;
+  HOST_WIDE_INT live_regs_mask[(FIRST_PSEUDO_REGISTER + 31) / 32];
   int d, i;
-  int live_regs_mask2;
+  int d_rounding = 0;
   int save_flags = target_flags;
 
+  current_function_interrupt = sh_cfun_interrupt_handler_p ();
+
   /* We have pretend args if we had an object sent partially in registers
      and partially on the stack, e.g. a large structure.  */
-  output_stack_adjust (-current_function_pretend_args_size,
-                      stack_pointer_rtx, 1);
+  output_stack_adjust (-current_function_pretend_args_size
+                      - current_function_args_info.stack_regs * 8,
+                      stack_pointer_rtx, TARGET_SH5 ? 0 : 1, frame_insn);
 
   extra_push = 0;
 
-  /* This is set by SETUP_VARARGS to indicate that this is a varargs
-     routine.  Clear it here so that the next function isn't affected. */
-  if (current_function_anonymous_args)
+  if (TARGET_SHCOMPACT && flag_pic && current_function_args_info.call_cookie)
+    /* We're going to use the PIC register to load the address of the
+       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;
+
+  if (TARGET_SHCOMPACT
+      && (current_function_args_info.call_cookie & ~ CALL_COOKIE_RET_TRAMP(1)))
+    {
+      int reg;
+
+      /* First, make all registers with incoming arguments that will
+        be pushed onto the stack live, so that register renaming
+        doesn't overwrite them.  */
+      for (reg = 0; reg < NPARM_REGS (SImode); reg++)
+       if (CALL_COOKIE_STACKSEQ_GET (current_function_args_info.call_cookie)
+           >= NPARM_REGS (SImode) - reg)
+         for (; reg < NPARM_REGS (SImode); reg++)
+           emit_insn (gen_shcompact_preserve_incoming_args
+                      (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
+       else if (CALL_COOKIE_INT_REG_GET
+                (current_function_args_info.call_cookie, reg) == 1)
+         emit_insn (gen_shcompact_preserve_incoming_args
+                    (gen_rtx_REG (SImode, FIRST_PARM_REG + reg)));
+
+      emit_move_insn (gen_rtx_REG (Pmode, MACL_REG),
+                     stack_pointer_rtx);
+      emit_move_insn (gen_rtx_REG (SImode, R0_REG),
+                     GEN_INT (current_function_args_info.call_cookie));
+      emit_move_insn (gen_rtx_REG (SImode, MACH_REG),
+                     gen_rtx_REG (SImode, R0_REG));
+    }
+  else if (TARGET_SHMEDIA)
     {
-      current_function_anonymous_args = 0;
+      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));
+
+         /* 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 the code for SETUP_VARARGS.  */
+  if (current_function_stdarg)
+    {
       /* This is not used by the SH3E calling convention  */
-      if (! TARGET_SH3E && ! TARGET_HITACHI)
+      if (TARGET_SH1 && ! TARGET_SH3E && ! TARGET_SH5 && ! TARGET_HITACHI)
        {
          /* Push arg regs as if they'd been provided by caller in stack.  */
          for (i = 0; i < NPARM_REGS(SImode); i++)
            {
              int rn = NPARM_REGS(SImode) + FIRST_PARM_REG - i - 1;
+             rtx insn;
+
              if (i >= (NPARM_REGS(SImode) 
                        - current_function_args_info.arg_count[(int) SH_ARG_INT]
                        ))
                break;
-             push (rn);
+             insn = push (rn);
+             RTX_FRAME_RELATED_P (insn) = 0;
              extra_push += 4;
            }
        }
@@ -4043,13 +4601,164 @@ sh_expand_prologue ()
   if (sp_switch)
     emit_insn (gen_sp_switch_1 ());
 
-  live_regs_mask = calc_live_regs (&d, &live_regs_mask2);
+  calc_live_regs (&d, live_regs_mask);
   /* ??? Maybe we could save some switching if we can move a mode switch
      that already happens to be at the function start into the prologue.  */
   if (target_flags != save_flags)
     emit_insn (gen_toggle_sz ());
     
-  push_regs (live_regs_mask, live_regs_mask2);
+  if (TARGET_SH5)
+    {
+      int i;
+      int offset;
+      int align;
+      rtx r0 = gen_rtx_REG (Pmode, R0_REG);
+      int offset_in_r0 = -1;
+      int sp_in_r0 = 0;
+
+      if (d % (STACK_BOUNDARY / BITS_PER_UNIT))
+       d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+                     - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+      offset = d + d_rounding;
+      output_stack_adjust (-offset, stack_pointer_rtx, 1, frame_insn);
+
+      /* We loop twice: first, we save 8-byte aligned registers in the
+        higher addresses, that are known to be aligned.  Then, we
+        proceed to saving 32-bit registers that don't need 8-byte
+        alignment.  */
+      for (align = 1; align >= 0; align--)
+       for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+         if (live_regs_mask[i/32] & (1 << (i % 32)))
+           {
+             enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+             int reg = i;
+             rtx reg_rtx, mem_rtx, pre_dec = NULL_RTX;
+
+             if (mode == SFmode && (i % 2) == 1
+                 && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+                 && (live_regs_mask[(i ^ 1) / 32] & (1 << ((i ^ 1) % 32))))
+               {
+                 mode = DFmode;
+                 i--;
+                 reg--;
+               }
+               
+             /* If we're doing the aligned pass and this is not aligned,
+                or we're doing the unaligned pass and this is aligned,
+                skip it.  */
+             if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
+                  == 0) != align)
+               continue;
+
+             offset -= GET_MODE_SIZE (mode);
+
+             reg_rtx = gen_rtx_REG (mode, reg);
+
+             mem_rtx = gen_rtx_MEM (mode,
+                                    gen_rtx_PLUS (Pmode,
+                                                  stack_pointer_rtx,
+                                                  GEN_INT (offset)));
+
+             GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_pre_dec);
+
+             mem_rtx = NULL_RTX;
+
+           try_pre_dec:
+             do
+               if (HAVE_PRE_DECREMENT
+                   && (offset_in_r0 - offset == GET_MODE_SIZE (mode)
+                       || mem_rtx == NULL_RTX
+                       || i == PR_REG || SPECIAL_REGISTER_P (i)))
+                 {
+                   pre_dec = gen_rtx_MEM (mode,
+                                          gen_rtx_PRE_DEC (Pmode, r0));
+
+                   GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (pre_dec, 0),
+                                             pre_dec_ok);
+
+                   pre_dec = NULL_RTX;
+
+                   break;
+
+                 pre_dec_ok:
+                   mem_rtx = NULL_RTX;
+                   offset += GET_MODE_SIZE (mode);
+                 }
+             while (0);
+
+             if (mem_rtx != NULL_RTX)
+               goto addr_ok;
+
+             if (offset_in_r0 == -1)
+               {
+                 emit_move_insn (r0, GEN_INT (offset));
+                 offset_in_r0 = offset;
+               }
+             else if (offset != offset_in_r0)
+               {
+                 emit_move_insn (r0,
+                                 gen_rtx_PLUS
+                                 (Pmode, r0,
+                                  GEN_INT (offset - offset_in_r0)));
+                 offset_in_r0 += offset - offset_in_r0;
+               }
+                                                 
+             if (pre_dec != NULL_RTX)
+               {
+                 if (! sp_in_r0)
+                   {
+                     emit_move_insn (r0,
+                                     gen_rtx_PLUS
+                                     (Pmode, r0, stack_pointer_rtx));
+                     sp_in_r0 = 1;
+                   }
+
+                 offset -= GET_MODE_SIZE (mode);
+                 offset_in_r0 -= GET_MODE_SIZE (mode);
+
+                 mem_rtx = pre_dec;
+               }
+             else if (sp_in_r0)
+               mem_rtx = gen_rtx_MEM (mode, r0);
+             else
+               mem_rtx = gen_rtx_MEM (mode,
+                                      gen_rtx_PLUS (Pmode,
+                                                    stack_pointer_rtx,
+                                                    r0));
+
+             /* We must not use an r0-based address for target-branch
+                registers or for special registers without pre-dec
+                memory addresses, since we store their values in r0
+                first.  */
+             if (TARGET_REGISTER_P (i)
+                 || ((i == PR_REG || SPECIAL_REGISTER_P (i))
+                     && mem_rtx != pre_dec))
+               abort ();
+
+           addr_ok:
+             if (TARGET_REGISTER_P (i)
+                 || ((i == PR_REG || SPECIAL_REGISTER_P (i))
+                     && mem_rtx != pre_dec))
+               {
+                 rtx r0mode = gen_rtx_REG (GET_MODE (reg_rtx), R0_REG);
+
+                 emit_move_insn (r0mode, reg_rtx);
+
+                 offset_in_r0 = -1;
+                 sp_in_r0 = 0;
+
+                 reg_rtx = r0mode;
+               }
+
+             emit_move_insn (mem_rtx, reg_rtx);
+           }
+
+      if (offset != d_rounding)
+       abort ();
+    }
+  else
+    push_regs (live_regs_mask);
 
   if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
     {
@@ -4070,6 +4779,19 @@ sh_expand_prologue ()
       while (insn != last);
     }
 
+  if (SHMEDIA_REGS_STACK_ADJUST ())
+    {
+      emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
+                     gen_rtx_SYMBOL_REF (Pmode,
+                                         TARGET_FPU_ANY
+                                         ? "__GCC_push_shmedia_regs"
+                                         : "__GCC_push_shmedia_regs_nofpu"));
+      /* This must NOT go through the PLT, otherwise mach and macl
+        may be clobbered.  */
+      emit_insn (gen_shmedia_save_restore_regs_compact
+                (GEN_INT (-SHMEDIA_REGS_STACK_ADJUST ())));
+    }
+
   if (target_flags != save_flags)
     {
       rtx insn = emit_insn (gen_toggle_sz ());
@@ -4084,37 +4806,52 @@ sh_expand_prologue ()
 
   target_flags = save_flags;
 
-  output_stack_adjust (-rounded_frame_size (d),
-                      stack_pointer_rtx, 1);
+  output_stack_adjust (-rounded_frame_size (d) + d_rounding,
+                      stack_pointer_rtx, TARGET_SH5 ? 0 : 1, frame_insn);
 
   if (frame_pointer_needed)
-    emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
+    frame_insn (GEN_MOV (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),
+                     gen_rtx_SYMBOL_REF (Pmode,
+                                         "__GCC_shcompact_incoming_args"));
+      emit_insn (gen_shcompact_incoming_args ());
+    }
 }
 
 void
 sh_expand_epilogue ()
 {
-  int live_regs_mask;
+  HOST_WIDE_INT live_regs_mask[(FIRST_PSEUDO_REGISTER + 31) / 32];
   int d, i;
+  int d_rounding = 0;
 
-  int live_regs_mask2;
   int save_flags = target_flags;
   int frame_size;
 
-  live_regs_mask = calc_live_regs (&d, &live_regs_mask2);
+  calc_live_regs (&d, live_regs_mask);
 
-  frame_size = rounded_frame_size (d);
+  if (TARGET_SH5 && d % (STACK_BOUNDARY / BITS_PER_UNIT))
+    d_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+                 - d % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+  frame_size = rounded_frame_size (d) - d_rounding;
 
   if (frame_pointer_needed)
     {
-      output_stack_adjust (frame_size, frame_pointer_rtx, 7);
+      output_stack_adjust (frame_size, frame_pointer_rtx, 7, emit_insn);
 
       /* 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_movsi (stack_pointer_rtx, frame_pointer_rtx));
+      emit_insn (GEN_MOV (stack_pointer_rtx, frame_pointer_rtx));
     }
   else if (frame_size)
     {
@@ -4123,36 +4860,193 @@ sh_expand_epilogue ()
         occur after the SP adjustment and clobber data in the local
         frame.  */
       emit_insn (gen_blockage ());
-      output_stack_adjust (frame_size, stack_pointer_rtx, 7);
+      output_stack_adjust (frame_size, stack_pointer_rtx, 7, emit_insn);
+    }
+
+  if (SHMEDIA_REGS_STACK_ADJUST ())
+    {
+      emit_move_insn (gen_rtx_REG (Pmode, R0_REG),
+                     gen_rtx_SYMBOL_REF (Pmode,
+                                         TARGET_FPU_ANY
+                                         ? "__GCC_pop_shmedia_regs"
+                                         : "__GCC_pop_shmedia_regs_nofpu"));
+      /* This must NOT go through the PLT, otherwise mach and macl
+        may be clobbered.  */
+      emit_insn (gen_shmedia_save_restore_regs_compact
+                (GEN_INT (SHMEDIA_REGS_STACK_ADJUST ())));
     }
 
   /* Pop all the registers.  */
 
   if (target_flags != save_flags)
     emit_insn (gen_toggle_sz ());
-  if (live_regs_mask & (1 << PR_REG))
+  if (TARGET_SH5)
+    {
+      int offset = d_rounding;
+      int offset_in_r0 = -1;
+      int sp_in_r0 = 0;
+      int align;
+      rtx r0 = gen_rtx_REG (Pmode, R0_REG);
+      
+      /* We loop twice: first, we save 8-byte aligned registers in the
+        higher addresses, that are known to be aligned.  Then, we
+        proceed to saving 32-bit registers that don't need 8-byte
+        alignment.  */
+      for (align = 0; align <= 1; align++)
+       for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+         if (live_regs_mask[i/32] & (1 << (i % 32)))
+           {
+             enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+             int reg = i;
+             rtx reg_rtx, mem_rtx, post_inc = NULL_RTX, insn;
+
+             if (mode == SFmode && (i % 2) == 0
+                 && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+                 && (live_regs_mask[(i ^ 1) / 32] & (1 << ((i ^ 1) % 32))))
+               {
+                 mode = DFmode;
+                 i++;
+               }
+
+             /* If we're doing the aligned pass and this is not aligned,
+                or we're doing the unaligned pass and this is aligned,
+                skip it.  */
+             if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
+                  == 0) != align)
+               continue;
+
+             reg_rtx = gen_rtx_REG (mode, reg);
+
+             mem_rtx = gen_rtx_MEM (mode,
+                                    gen_rtx_PLUS (Pmode,
+                                                  stack_pointer_rtx,
+                                                  GEN_INT (offset)));
+
+             GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (mem_rtx, 0), try_post_inc);
+
+             mem_rtx = NULL_RTX;
+
+           try_post_inc:
+             do
+               if (HAVE_POST_INCREMENT
+                   && (offset == offset_in_r0
+                       || (offset + GET_MODE_SIZE (mode) != d + d_rounding
+                           && mem_rtx == NULL_RTX)
+                       || i == PR_REG || SPECIAL_REGISTER_P (i)))
+                 {
+                   post_inc = gen_rtx_MEM (mode,
+                                           gen_rtx_POST_INC (Pmode, r0));
+
+                   GO_IF_LEGITIMATE_ADDRESS (mode, XEXP (post_inc, 0),
+                                             post_inc_ok);
+
+                   post_inc = NULL_RTX;
+
+                   break;
+                   
+                 post_inc_ok:
+                   mem_rtx = NULL_RTX;
+                 }
+             while (0);
+             
+             if (mem_rtx != NULL_RTX)
+               goto addr_ok;
+
+             if (offset_in_r0 == -1)
+               {
+                 emit_move_insn (r0, GEN_INT (offset));
+                 offset_in_r0 = offset;
+               }
+             else if (offset != offset_in_r0)
+               {
+                 emit_move_insn (r0,
+                                 gen_rtx_PLUS
+                                 (Pmode, r0,
+                                  GEN_INT (offset - offset_in_r0)));
+                 offset_in_r0 += offset - offset_in_r0;
+               }
+                 
+             if (post_inc != NULL_RTX)
+               {
+                 if (! sp_in_r0)
+                   {
+                     emit_move_insn (r0,
+                                     gen_rtx_PLUS
+                                     (Pmode, r0, stack_pointer_rtx));
+                     sp_in_r0 = 1;
+                   }
+                 
+                 mem_rtx = post_inc;
+
+                 offset_in_r0 += GET_MODE_SIZE (mode);
+               }
+             else if (sp_in_r0)
+               mem_rtx = gen_rtx_MEM (mode, r0);
+             else
+               mem_rtx = gen_rtx_MEM (mode,
+                                      gen_rtx_PLUS (Pmode,
+                                                    stack_pointer_rtx,
+                                                    r0));
+
+             if ((i == PR_REG || SPECIAL_REGISTER_P (i))
+                 && mem_rtx != post_inc)
+               abort ();
+
+           addr_ok:
+             if ((i == PR_REG || SPECIAL_REGISTER_P (i))
+                 && mem_rtx != post_inc)
+               {
+                 insn = emit_move_insn (r0, mem_rtx);
+                 mem_rtx = r0;
+               }
+             else if (TARGET_REGISTER_P (i))
+               {
+                 rtx r1 = gen_rtx_REG (mode, R1_REG);
+
+                 insn = emit_move_insn (r1, mem_rtx);
+                 mem_rtx = r1;
+               }
+
+             insn = emit_move_insn (reg_rtx, mem_rtx);
+
+             offset += GET_MODE_SIZE (mode);
+           }
+
+      if (offset != d + d_rounding)
+       abort ();
+
+      goto finish;
+    }
+  else
+    d = 0;
+  if (live_regs_mask[PR_REG / 32] & (1 << (PR_REG % 32)))
     pop (PR_REG);
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
       int j = (FIRST_PSEUDO_REGISTER - 1) - i;
-      if (j < 32 && (live_regs_mask & (1 << j)) && j != PR_REG)
-       pop (j);
-      else if (j >= 32 && (live_regs_mask2 & (1 << (j - 32))))
+
+      if (j != PR_REG && live_regs_mask[j / 32] & (1 << (j % 32)))
        pop (j);
     }
+ finish:
   if (target_flags != save_flags)
     emit_insn (gen_toggle_sz ());
   target_flags = save_flags;
 
-  output_stack_adjust (extra_push + current_function_pretend_args_size,
-                      stack_pointer_rtx, 7);
+  output_stack_adjust (extra_push + current_function_pretend_args_size
+                      + d + d_rounding
+                      + current_function_args_info.stack_regs * 8,
+                      stack_pointer_rtx, 7, emit_insn);
 
   /* Switch back to the normal stack if necessary.  */
   if (sp_switch)
     emit_insn (gen_sp_switch_2 ());
 
   /* Tell flow the insn that pops PR isn't dead.  */
-  if (live_regs_mask & (1 << PR_REG))
+  /* PR_REG will never be live in SHmedia mode, and we don't need to
+     USE PR_MEDIA_REG, since it will be explicitly copied to TR0_REG
+     by the return pattern.  */
+  if (live_regs_mask[PR_REG / 32] & (1 << (PR_REG % 32)))
     emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (SImode, PR_REG)));
 }
 
@@ -4167,21 +5061,19 @@ sh_need_epilogue ()
 
       start_sequence ();
       sh_expand_epilogue ();
-      epilogue = gen_sequence ();
+      epilogue = get_insns ();
       end_sequence ();
-      sh_need_epilogue_known
-       = (GET_CODE (epilogue) == SEQUENCE && XVECLEN (epilogue, 0) == 0
-          ? -1 : 1);
+      sh_need_epilogue_known = (epilogue == NULL ? -1 : 1);
     }
   return sh_need_epilogue_known > 0;
 }
 
 /* Clear variables at function end.  */
 
-void
-function_epilogue (stream, size)
-     FILE *stream ATTRIBUTE_UNUSED;
-     int size ATTRIBUTE_UNUSED;
+static void
+sh_output_function_epilogue (file, size)
+     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;
@@ -4200,28 +5092,85 @@ sh_builtin_saveregs ()
   /* Number of SFmode float regs to save.  */
   int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
   rtx regbuf, fpregs;
-  int bufsize, regno, alias_set;
+  int bufsize, regno;
+  HOST_WIDE_INT alias_set;
+
+  if (TARGET_SH5)
+    {
+      if (n_intregs)
+       {
+         int pushregs = n_intregs;
+
+         while (pushregs < NPARM_REGS (SImode) - 1
+                && (CALL_COOKIE_INT_REG_GET
+                       (current_function_args_info.call_cookie,
+                        NPARM_REGS (SImode) - pushregs)
+                    == 1))
+           {
+             current_function_args_info.call_cookie
+               &= ~ CALL_COOKIE_INT_REG (NPARM_REGS (SImode)
+                                         - pushregs, 1);
+             pushregs++;
+           }
+
+         if (pushregs == NPARM_REGS (SImode))
+           current_function_args_info.call_cookie
+             |= (CALL_COOKIE_INT_REG (0, 1)
+                 | CALL_COOKIE_STACKSEQ (pushregs - 1));
+         else
+           current_function_args_info.call_cookie
+             |= CALL_COOKIE_STACKSEQ (pushregs);
+
+         current_function_pretend_args_size += 8 * n_intregs;
+       }
+      if (TARGET_SHCOMPACT)
+       return const0_rtx;
+    }
+  
+  if (! TARGET_SH3E && ! TARGET_SH4 && ! TARGET_SH5)
+    {
+      error ("__builtin_saveregs not supported by this subtarget");
+      return const0_rtx;
+    }
+
+  if (TARGET_SHMEDIA)
+    n_floatregs = 0;
 
-  /* Allocate block of memory for the regs. */
+  /* Allocate block of memory for the regs.  */
   /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte?
      Or can assign_stack_local accept a 0 SIZE argument?  */
   bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * UNITS_PER_WORD);
 
-  regbuf = assign_stack_local (BLKmode, bufsize, 0);
+  if (TARGET_SHMEDIA)
+    regbuf = gen_rtx_MEM (BLKmode,
+                         gen_rtx_REG (Pmode, ARG_POINTER_REGNUM));
+  else if (n_floatregs & 1)
+    {
+      rtx addr;
+
+      regbuf = assign_stack_local (BLKmode, bufsize + UNITS_PER_WORD, 0);
+      addr = copy_to_mode_reg (Pmode, XEXP (regbuf, 0));
+      emit_insn (gen_iorsi3 (addr, addr, GEN_INT (UNITS_PER_WORD)));
+      regbuf = change_address (regbuf, BLKmode, addr);
+    }
+  else
+    regbuf = assign_stack_local (BLKmode, bufsize, 0);
   alias_set = get_varargs_alias_set ();
-  MEM_ALIAS_SET (regbuf) = alias_set;
+  set_mem_alias_set (regbuf, alias_set);
 
   /* Save int args.
      This is optimized to only save the regs that are necessary.  Explicitly
      named args need not be saved.  */
   if (n_intregs > 0)
     move_block_from_reg (BASE_ARG_REG (SImode) + first_intreg,
-                        change_address (regbuf, BLKmode,
-                                        plus_constant (XEXP (regbuf, 0),
-                                                       (n_floatregs
-                                                        * UNITS_PER_WORD))), 
+                        adjust_address (regbuf, BLKmode,
+                                        n_floatregs * UNITS_PER_WORD),
                         n_intregs, n_intregs * UNITS_PER_WORD);
 
+  if (TARGET_SHMEDIA)
+    /* Return the address of the regbuf.  */
+    return XEXP (regbuf, 0);
+
   /* Save float args.
      This is optimized to only save the regs that are necessary.  Explicitly
      named args need not be saved.
@@ -4242,7 +5191,7 @@ sh_builtin_saveregs ()
          emit_insn (gen_addsi3 (fpregs, fpregs,
                                 GEN_INT (-2 * UNITS_PER_WORD)));
          mem = gen_rtx_MEM (DFmode, fpregs);
-         MEM_ALIAS_SET (mem) = alias_set;
+         set_mem_alias_set (mem, alias_set);
          emit_move_insn (mem, 
                          gen_rtx (REG, DFmode, BASE_ARG_REG (DFmode) + regno));
        }
@@ -4251,7 +5200,7 @@ sh_builtin_saveregs ()
        {
          emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (- UNITS_PER_WORD)));
          mem = gen_rtx_MEM (SFmode, fpregs);
-         MEM_ALIAS_SET (mem) = alias_set;
+         set_mem_alias_set (mem, alias_set);
          emit_move_insn (mem,
                          gen_rtx (REG, SFmode, BASE_ARG_REG (SFmode) + regno
                                                - (TARGET_LITTLE_ENDIAN != 0)));
@@ -4261,9 +5210,10 @@ sh_builtin_saveregs ()
     for (regno = NPARM_REGS (SFmode) - 1; regno >= first_floatreg; regno--)
       {
         rtx mem;
+
        emit_insn (gen_addsi3 (fpregs, fpregs, GEN_INT (- UNITS_PER_WORD)));
        mem = gen_rtx_MEM (SFmode, fpregs);
-       MEM_ALIAS_SET (mem) = alias_set;
+       set_mem_alias_set (mem, alias_set);
        emit_move_insn (mem,
                        gen_rtx_REG (SFmode, BASE_ARG_REG (SFmode) + regno));
       }
@@ -4280,7 +5230,7 @@ sh_build_va_list ()
   tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
   tree record;
 
-  if ((! TARGET_SH3E && ! TARGET_SH4) || TARGET_HITACHI)
+  if (TARGET_SH5 || (! TARGET_SH3E && ! TARGET_SH4) || TARGET_HITACHI)
     return ptr_type_node;
 
   record = make_node (RECORD_TYPE);
@@ -4318,8 +5268,7 @@ sh_build_va_list ()
 /* Implement `va_start' for varargs and stdarg.  */
 
 void
-sh_va_start (stdarg_p, valist, nextarg)
-     int stdarg_p;
+sh_va_start (valist, nextarg)
      tree valist;
      rtx nextarg;
 {
@@ -4328,9 +5277,16 @@ sh_va_start (stdarg_p, valist, nextarg)
   tree t, u;
   int nfp, nint;
 
+  if (TARGET_SH5)
+    {
+      expand_builtin_saveregs ();
+      std_expand_builtin_va_start (valist, nextarg);
+      return;
+    }
+
   if ((! TARGET_SH3E && ! TARGET_SH4) || TARGET_HITACHI)
     {
-      std_expand_builtin_va_start (stdarg_p, valist, nextarg);
+      std_expand_builtin_va_start (valist, nextarg);
       return;
     }
 
@@ -4382,11 +5338,6 @@ sh_va_start (stdarg_p, valist, nextarg)
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 
   u = make_tree (ptr_type_node, nextarg);
-  if (! stdarg_p && (nint == 0 || nfp == 0))
-    {
-      u = fold (build (PLUS_EXPR, ptr_type_node, u,
-                      build_int_2 (-UNITS_PER_WORD, -1)));
-    }
   t = build (MODIFY_EXPR, ptr_type_node, next_stack, u);
   TREE_SIDE_EFFECTS (t) = 1;
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -4401,12 +5352,17 @@ sh_va_arg (valist, type)
   HOST_WIDE_INT size, rsize;
   tree tmp, pptr_type_node;
   rtx addr_rtx, r;
+  rtx result;
+  int pass_by_ref = MUST_PASS_IN_STACK (TYPE_MODE (type), type);
 
   size = int_size_in_bytes (type);
   rsize = (size + UNITS_PER_WORD - 1) & -UNITS_PER_WORD;
   pptr_type_node = build_pointer_type (ptr_type_node);
 
-  if ((TARGET_SH3E || TARGET_SH4) && ! TARGET_HITACHI)
+  if (pass_by_ref)
+    type = build_pointer_type (type);
+
+  if (! TARGET_SH5 && (TARGET_SH3E || TARGET_SH4) && ! TARGET_HITACHI)
     {
       tree f_next_o, f_next_o_limit, f_next_fp, f_next_fp_limit, f_next_stack;
       tree next_o, next_o_limit, next_fp, next_fp_limit, next_stack;
@@ -4447,13 +5403,19 @@ sh_va_arg (valist, type)
 
       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);
+
          emit_cmp_and_jump_insns (expand_expr (next_fp, NULL_RTX, Pmode,
                                                EXPAND_NORMAL),
                                   expand_expr (next_fp_limit, NULL_RTX,
                                                Pmode, EXPAND_NORMAL),
-                                  GE, const1_rtx, Pmode, 1, 1, lab_false);
+                                  GE, const1_rtx, Pmode, 1, lab_false);
 
-         if (TYPE_ALIGN (type) > BITS_PER_WORD)
+         if (TYPE_ALIGN (type) > BITS_PER_WORD
+             || (((TREE_CODE (type) == REAL_TYPE && size == 8) || size == 16)
+                 && (n_floatregs & 1)))
            {
              tmp = build (BIT_AND_EXPR, ptr_type_node, next_fp,
                           build_int_2 (UNITS_PER_WORD, 0));
@@ -4486,7 +5448,7 @@ sh_va_arg (valist, type)
                                                EXPAND_NORMAL),
                                   expand_expr (next_o_limit, NULL_RTX,
                                                Pmode, EXPAND_NORMAL),
-                                  GT, const1_rtx, Pmode, 1, 1, lab_false);
+                                  GT, const1_rtx, Pmode, 1, lab_false);
 
          tmp = build1 (ADDR_EXPR, pptr_type_node, next_o);
          r = expand_expr (tmp, addr_rtx, Pmode, EXPAND_NORMAL);
@@ -4519,7 +5481,19 @@ sh_va_arg (valist, type)
   /* ??? In va-sh.h, there had been code to make values larger than
      size 8 indirect.  This does not match the FUNCTION_ARG macros.  */
 
-  return std_expand_builtin_va_arg (valist, type);
+  result = std_expand_builtin_va_arg (valist, type);
+  if (pass_by_ref)
+    {
+#ifdef POINTERS_EXTEND_UNSIGNED
+      if (GET_MODE (addr) != Pmode)
+       addr = convert_memory_address (Pmode, result);
+#endif
+      result = gen_rtx_MEM (ptr_mode, force_reg (Pmode, result));
+      set_mem_alias_set (result, get_varargs_alias_set ());
+    }
+  /* ??? expand_builtin_va_arg will also set the alias set of the dereferenced
+     argument to the varargs alias set.  */
+  return result;
 }
 
 /* Define the offset between two registers, one to be eliminated, and
@@ -4531,22 +5505,32 @@ initial_elimination_offset (from, to)
      int to;
 {
   int regs_saved;
+  int regs_saved_rounding = 0;
   int total_saved_regs_space;
   int total_auto_space;
   int save_flags = target_flags;
+  int copy_flags;
 
-  int live_regs_mask, live_regs_mask2;
-  live_regs_mask = calc_live_regs (&regs_saved, &live_regs_mask2);
-  total_auto_space = rounded_frame_size (regs_saved);
+  HOST_WIDE_INT live_regs_mask[(FIRST_PSEUDO_REGISTER + 31) / 32];
+  calc_live_regs (&regs_saved, live_regs_mask);
+  regs_saved += SHMEDIA_REGS_STACK_ADJUST ();
+  if (TARGET_SH5 && regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT))
+    regs_saved_rounding = ((STACK_BOUNDARY / BITS_PER_UNIT)
+                          - regs_saved % (STACK_BOUNDARY / BITS_PER_UNIT));
+
+  total_auto_space = rounded_frame_size (regs_saved) - regs_saved_rounding;
+  copy_flags = target_flags;
   target_flags = save_flags;
 
-  total_saved_regs_space = regs_saved;
+  total_saved_regs_space = regs_saved + regs_saved_rounding;
 
   if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
-    return total_saved_regs_space + total_auto_space;
+    return total_saved_regs_space + total_auto_space
+      + current_function_args_info.byref_regs * 8;
 
   if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
-    return total_saved_regs_space + total_auto_space;
+    return total_saved_regs_space + total_auto_space
+      + current_function_args_info.byref_regs * 8;
 
   /* Initial gap between fp and sp is 0.  */
   if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
@@ -4554,7 +5538,61 @@ initial_elimination_offset (from, to)
 
   if (from == RETURN_ADDRESS_POINTER_REGNUM
       && (to == FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM))
-    return UNITS_PER_WORD + total_auto_space;
+    {
+      if (TARGET_SH5)
+       {
+         int i, n = total_saved_regs_space;
+         int align;
+         int pr_reg = TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG;
+         
+         n += total_auto_space;
+
+         /* If it wasn't saved, there's not much we can do.  */
+         if ((live_regs_mask[pr_reg / 32] & (1 << (pr_reg % 32))) == 0)
+           return n;
+
+         target_flags = copy_flags;
+
+         /* We loop twice: first, check 8-byte aligned registers,
+            that are stored in the higher addresses, that are known
+            to be aligned.  Then, check 32-bit registers that don't
+            need 8-byte alignment.  */
+         for (align = 1; align >= 0; align--)
+           for (i = FIRST_PSEUDO_REGISTER - 1; i >= 0; i--)
+             if (live_regs_mask[i/32] & (1 << (i % 32)))
+               {
+                 enum machine_mode mode = REGISTER_NATURAL_MODE (i);
+
+                 if (mode == SFmode && (i % 2) == 1
+                     && ! TARGET_FPU_SINGLE && FP_REGISTER_P (i)
+                     && (live_regs_mask[(i ^ 1) / 32]
+                         & (1 << ((i ^ 1) % 32))))
+                   {
+                     mode = DFmode;
+                     i--;
+                   }
+               
+                 /* If we're doing the aligned pass and this is not aligned,
+                    or we're doing the unaligned pass and this is aligned,
+                    skip it.  */
+                 if ((GET_MODE_SIZE (mode) % (STACK_BOUNDARY / BITS_PER_UNIT)
+                      == 0) != align)
+                   continue;
+
+                 n -= GET_MODE_SIZE (mode);
+
+                 if (i == pr_reg)
+                   {
+                     target_flags = save_flags;
+                     return n;
+                   }
+               }
+
+         abort ();
+       }
+      else
+       return total_auto_space;
+    }
 
   abort ();
 }
@@ -4585,11 +5623,10 @@ sh_pr_nosave_low_regs (pfile)
 
 /* Generate 'handle_interrupt' attribute for decls */
 
-void
-sh_pragma_insert_attributes (node, attributes, prefix)
+static void
+sh_insert_attributes (node, attributes)
      tree node;
      tree * attributes;
-     tree * prefix ATTRIBUTE_UNUSED;
 {
   if (! pragma_interrupt
       || TREE_CODE (node) != FUNCTION_DECL)
@@ -4605,11 +5642,7 @@ sh_pragma_insert_attributes (node, attributes, prefix)
   return;
 }
 
-/* Return nonzero if ATTR is a valid attribute for DECL.
-   ATTRIBUTES are any existing attributes and ARGS are the arguments
-   supplied with ATTR.
-
-   Supported attributes:
+/* Supported attributes:
 
    interrupt_handler -- specifies this function is an interrupt handler.
 
@@ -4619,61 +5652,124 @@ sh_pragma_insert_attributes (node, attributes, prefix)
    trap_exit -- use a trapa to exit an interrupt function instead of
    an rte instruction.  */
 
-int
-sh_valid_machine_decl_attribute (decl, attributes, attr, args)
-     tree decl;
-     tree attributes ATTRIBUTE_UNUSED;
-     tree attr;
-     tree args;
+const struct attribute_spec sh_attribute_table[] =
 {
-  if (TREE_CODE (decl) != FUNCTION_DECL)
-    return 0;
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  { "interrupt_handler", 0, 0, true,  false, false, sh_handle_interrupt_handler_attribute },
+  { "sp_switch",         1, 1, true,  false, false, sh_handle_sp_switch_attribute },
+  { "trap_exit",         1, 1, true,  false, false, sh_handle_trap_exit_attribute },
+  { NULL,                0, 0, false, false, false, NULL }
+};
 
-  if (is_attribute_p ("interrupt_handler", attr))
+/* Handle an "interrupt_handler" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+sh_handle_interrupt_handler_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name;
+     tree args ATTRIBUTE_UNUSED;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
     {
-      return 1;
+      warning ("`%s' attribute only applies to functions",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
     }
-
-  if (is_attribute_p ("sp_switch", attr))
+  else if (TARGET_SHCOMPACT)
     {
-      /* The sp_switch attribute only has meaning for interrupt functions.  */
-      if (!pragma_interrupt)
-       return 0;
+      error ("attribute interrupt_handler is not compatible with -m5-compact");
+      *no_add_attrs = true;
+    }
 
-      /* sp_switch must have an argument.  */
-      if (!args || TREE_CODE (args) != TREE_LIST)
-       return 0;
+  return NULL_TREE;
+}
 
+/* Handle an "sp_switch" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+sh_handle_sp_switch_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name;
+     tree args;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning ("`%s' 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 ("`%s' attribute only applies to interrupt functions",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+  else if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
+    {
       /* The argument must be a constant string.  */
-      if (TREE_CODE (TREE_VALUE (args)) != STRING_CST)
-       return 0;
-
+      warning ("`%s' attribute argument not a string constant",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+  else
+    {
       sp_switch = gen_rtx_SYMBOL_REF (VOIDmode,
                                      TREE_STRING_POINTER (TREE_VALUE (args)));
-      return 1;
     }
 
-  if (is_attribute_p ("trap_exit", attr))
+  return NULL_TREE;
+}
+
+/* Handle an "trap_exit" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+sh_handle_trap_exit_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name;
+     tree args;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning ("`%s' 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.  */
-      if (!pragma_interrupt)
-       return 0;
-
-      /* trap_exit must have an argument.  */
-      if (!args || TREE_CODE (args) != TREE_LIST)
-       return 0;
-
+      warning ("`%s' attribute only applies to interrupt functions",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+  else if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
+    {
       /* The argument must be a constant integer.  */
-      if (TREE_CODE (TREE_VALUE (args)) != INTEGER_CST)
-       return 0;
-
+      warning ("`%s' attribute argument not an integer constant",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+  else
+    {
       trap_exit = TREE_INT_CST_LOW (TREE_VALUE (args));
-      return 1;
     }
 
-  return 0;
+  return NULL_TREE;
 }
 
+int
+sh_cfun_interrupt_handler_p ()
+{
+  return (lookup_attribute ("interrupt_handler",
+                           DECL_ATTRIBUTES (current_function_decl))
+         != NULL_TREE);
+}
 \f
 /* Predicates used by the templates.  */
 
@@ -4766,12 +5862,42 @@ arith_reg_operand (op, mode)
        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 (op, mode)
+     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 (op, mode)
+     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 (op, mode)
      rtx op;
@@ -4804,7 +5930,20 @@ arith_operand (op, mode)
   if (arith_reg_operand (op, mode))
     return 1;
 
-  if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I (INTVAL (op)))
+  if (TARGET_SHMEDIA)
+    {
+      /* FIXME: We should be checking whether the CONST_INT fits in a
+        CONST_OK_FOR_J here, but this causes reload_cse to crash when
+        attempting to transform a sequence of two 64-bit sets of the
+        same register from literal constants into a set and an add,
+        when the difference is too wide for an add.  */
+      if (GET_CODE (op) == CONST_INT
+         || EXTRA_CONSTRAINT_S (op))
+       return 1;
+      else
+       return 0;
+    }
+  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_I (INTVAL (op)))
     return 1;
 
   return 0;
@@ -4820,12 +5959,24 @@ arith_reg_or_0_operand (op, mode)
   if (arith_reg_operand (op, mode))
     return 1;
 
-  if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_N (INTVAL (op)))
+  if (EXTRA_CONSTRAINT_U (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 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (arith_reg_operand (op, mode)
+         || (GET_CODE (op) == CONST_INT && CONST_OK_FOR_O (INTVAL (op))));
+}
+
 /* Returns 1 if OP is a valid source operand for a logical operation.  */
 
 int
@@ -4836,9 +5987,35 @@ logical_operand (op, mode)
   if (arith_reg_operand (op, mode))
     return 1;
 
-  if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_L (INTVAL (op)))
+  if (TARGET_SHMEDIA)
+    {
+      if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_P (INTVAL (op)))
+       return 1;
+      else
+       return 0;
+    }
+  else if (GET_CODE (op) == CONST_INT && CONST_OK_FOR_L (INTVAL (op)))
+    return 1;
+
+  return 0;
+}
+
+int
+and_operand (op, mode)
+     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
+      && (INTVAL (op) == (unsigned) 0xffffffff
+         || INTVAL (op) == (HOST_WIDE_INT) -1 << 32))
+       return 1;
+
   return 0;
 }
 
@@ -4908,6 +6085,9 @@ fpul_operand (op, mode)
      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);
@@ -4957,6 +6137,25 @@ noncommutative_float_operator (op, mode)
   return 0;
 }
 
+int
+unary_float_operator (op, mode)
+     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 (op, mode)
      rtx op;
@@ -4976,43 +6175,299 @@ binary_float_operator (op, mode)
     }
   return 0;
 }
-\f
-/* Return the destination address of a branch.  */
-   
-static int
-branch_dest (branch)
-     rtx branch;
-{
-  rtx dest = SET_SRC (PATTERN (branch));
-  int dest_uid;
 
-  if (GET_CODE (dest) == IF_THEN_ELSE)
-    dest = XEXP (dest, 1);
-  dest = XEXP (dest, 0);
-  dest_uid = INSN_UID (dest);
-  return INSN_ADDRESSES (dest_uid);
-}
-\f
-/* Return non-zero if REG is not used after INSN.
-   We assume REG is a reload reg, and therefore does
-   not live past labels.  It may live past calls or jumps though.  */
 int
-reg_unused_after (reg, insn)
-     rtx reg;
-     rtx insn;
+binary_logical_operator (op, mode)
+     rtx op;
+     enum machine_mode mode;
 {
-  enum rtx_code code;
-  rtx set;
-
-  /* If the reg is set by this instruction, then it is safe for our
-     case.  Disregard the case where this is a store to memory, since
-     we are checking a register used in the store address.  */
-  set = single_set (insn);
-  if (set && GET_CODE (SET_DEST (set)) != MEM
-      && reg_overlap_mentioned_p (reg, SET_DEST (set)))
-    return 1;
-
-  while ((insn = NEXT_INSN (insn)))
+  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 (op, mode)
+     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 (op, mode)
+     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 (op, mode)
+     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 (op, mode)
+     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 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  if (mode != DImode)
+    return 0;
+
+  if ((GET_MODE (op) == DImode || GET_MODE (op) == VOIDmode)
+      && EXTRA_CONSTRAINT_T (op))
+    return ! reload_completed;
+
+  return target_reg_operand (op, mode);
+}
+
+int
+mextr_bit_offset (op, mode)
+     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 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (GET_CODE (op) == TRUNCATE
+         ? arith_operand
+         : arith_reg_operand) (op, mode);
+}
+
+int
+trunc_hi_operand (op, mode)
+     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 (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (GET_CODE (op) == TRUNCATE
+         ? arith_operand
+         : arith_reg_or_0_operand) (op, mode);
+}
+
+int
+general_extend_operand (op, mode)
+     rtx op;
+     enum machine_mode mode;
+{
+  return (GET_CODE (op) == TRUNCATE
+         ? arith_operand
+         : nonimmediate_operand) (op, mode);
+}
+
+int
+inqhi_operand (op, mode)
+     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 (v, mode)
+     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 (v, mode)
+     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 significat 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 (v, mode)
+     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.  */
+   
+static int
+branch_dest (branch)
+     rtx branch;
+{
+  rtx dest = SET_SRC (PATTERN (branch));
+  int dest_uid;
+
+  if (GET_CODE (dest) == IF_THEN_ELSE)
+    dest = XEXP (dest, 1);
+  dest = XEXP (dest, 0);
+  dest_uid = INSN_UID (dest);
+  return INSN_ADDRESSES (dest_uid);
+}
+\f
+/* Return nonzero if REG is not used after INSN.
+   We assume REG is a reload reg, and therefore does
+   not live past labels.  It may live past calls or jumps though.  */
+int
+reg_unused_after (reg, insn)
+     rtx reg;
+     rtx insn;
+{
+  enum rtx_code code;
+  rtx set;
+
+  /* If the reg is set by this instruction, then it is safe for our
+     case.  Disregard the case where this is a store to memory, since
+     we are checking a register used in the store address.  */
+  set = single_set (insn);
+  if (set && GET_CODE (SET_DEST (set)) != MEM
+      && reg_overlap_mentioned_p (reg, SET_DEST (set)))
+    return 1;
+
+  while ((insn = NEXT_INSN (insn)))
     {
       code = GET_CODE (insn);
 
@@ -5031,7 +6486,7 @@ reg_unused_after (reg, insn)
 
       /* If this is a sequence, we must handle them all at once.
         We could have for instance a call that sets the target register,
-        and a insn in a delay slot that uses the register.  In this case,
+        and an insn in a delay slot that uses the register.  In this case,
         we must return 0.  */
       else if (code == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE)
        {
@@ -5090,16 +6545,14 @@ reg_unused_after (reg, insn)
 \f
 #include "ggc.h"
 
+static GTY(()) rtx fpscr_rtx;
 rtx
 get_fpscr_rtx ()
 {
-  static rtx fpscr_rtx;
-
   if (! fpscr_rtx)
     {
       fpscr_rtx = gen_rtx (REG, PSImode, FPSCR_REG);
       REG_USERVAR_P (fpscr_rtx) = 1;
-      ggc_add_rtx_root (&fpscr_rtx, 1);
       mark_user_reg (fpscr_rtx);
     }
   if (! reload_completed || mdep_reorg_phase != SH_AFTER_MDEP_REORG)
@@ -5295,8 +6748,8 @@ sh_insn_length_adjustment (insn)
   /* Instructions with unfilled delay slots take up an extra two bytes for
      the nop in the delay slot.  */
   if (((GET_CODE (insn) == INSN
-        && GET_CODE (PATTERN (insn)) != USE
-        && GET_CODE (PATTERN (insn)) != CLOBBER)
+       && GET_CODE (PATTERN (insn)) != USE
+       && GET_CODE (PATTERN (insn)) != CLOBBER)
        || GET_CODE (insn) == CALL_INSN
        || (GET_CODE (insn) == JUMP_INSN
           && GET_CODE (PATTERN (insn)) != ADDR_DIFF_VEC
@@ -5369,13 +6822,20 @@ nonpic_symbol_mentioned_p (x)
   register const char *fmt;
   register int i;
 
-  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF)
+  if (GET_CODE (x) == SYMBOL_REF || GET_CODE (x) == LABEL_REF
+      || GET_CODE (x) == PC)
     return 1;
 
+  /* We don't want to look into the possible MEM location of a
+     CONST_DOUBLE, since we're not going to use it, in general.  */
+  if (GET_CODE (x) == CONST_DOUBLE)
+    return 0;
+
   if (GET_CODE (x) == UNSPEC
       && (XINT (x, 1) == UNSPEC_PIC
          || XINT (x, 1) == UNSPEC_GOT
          || XINT (x, 1) == UNSPEC_GOTOFF
+         || XINT (x, 1) == UNSPEC_GOTPLT
          || XINT (x, 1) == UNSPEC_PLT))
       return 0;
 
@@ -5398,7 +6858,7 @@ nonpic_symbol_mentioned_p (x)
 }
 
 /* Convert a non-PIC address in `orig' to a PIC address using @GOT or
-   @GOTOFF in `reg'. */
+   @GOTOFF in `reg'.  */
 rtx
 legitimize_pic_address (orig, mode, reg)
      rtx orig;
@@ -5430,7 +6890,8 @@ legitimize_pic_address (orig, mode, reg)
 
 /* Mark the use of a constant in the literal table. If the constant
    has multiple labels, make it unique.  */
-static rtx mark_constant_pool_use (x)
+static rtx
+mark_constant_pool_use (x)
      rtx x;
 {
   rtx insn, lab, pattern;
@@ -5492,3 +6953,806 @@ static rtx mark_constant_pool_use (x)
 
   return lab;
 }
+\f
+/* Return true if it's possible to redirect BRANCH1 to the destination
+   of an unconditional jump BRANCH2.  We only want to do this if the
+   resulting branch will have a short displacement.  */
+int 
+sh_can_redirect_branch (branch1, branch2)
+     rtx branch1;
+     rtx branch2;
+{
+  if (flag_expensive_optimizations && simplejump_p (branch2))
+    {
+      rtx dest = XEXP (SET_SRC (single_set (branch2)), 0);
+      rtx insn;
+      int distance;
+      
+      for (distance = 0, insn = NEXT_INSN (branch1); 
+          insn && distance < 256; 
+          insn = PREV_INSN (insn))
+       {
+         if (insn == dest)    
+           return 1;
+         else
+           distance += get_attr_length (insn);
+       }
+      for (distance = 0, insn = NEXT_INSN (branch1); 
+          insn && distance < 256; 
+          insn = NEXT_INSN (insn))
+       {
+         if (insn == dest)    
+           return 1;
+         else
+           distance += get_attr_length (insn);
+       }
+    }
+  return 0;
+}
+
+/* Return nonzero if register old_reg can be renamed to register new_reg.  */
+int
+sh_hard_regno_rename_ok (old_reg, new_reg)
+     unsigned int old_reg ATTRIBUTE_UNUSED;
+     unsigned int new_reg;
+{
+
+/* Interrupt functions can only use registers that have already been
+   saved by the prologue, even if they would normally be
+   call-clobbered.  */
+
+  if (sh_cfun_interrupt_handler_p () && !regs_ever_live[new_reg])
+     return 0;
+
+   return 1;
+}
+
+/* A C statement (sans semicolon) to update the integer variable COST
+   based on the relationship between INSN that is dependent on
+   DEP_INSN through the dependence LINK.  The default is to make no
+   adjustment to COST.  This can be used for example to specify to
+   the scheduler that an output- or anti-dependence does not incur
+   the same cost as a data-dependence.  */
+static int
+sh_adjust_cost (insn, link, dep_insn, cost)
+     rtx insn;
+     rtx link ATTRIBUTE_UNUSED;
+     rtx dep_insn;
+     int cost;
+{
+  rtx reg, use_pat;
+
+  if (TARGET_SHMEDIA)
+    {
+      /* On SHmedia, if the dependence is an anti-dependence or
+         output-dependence, there is no cost. */              
+      if (REG_NOTE_KIND (link) != 0)
+        cost = 0;
+
+      if (get_attr_is_mac_media (insn)
+          && get_attr_is_mac_media (dep_insn))
+        cost = 1;
+    }
+  else if (REG_NOTE_KIND (link) == 0)
+    {
+      enum attr_type dep_type, type;
+
+      if (recog_memoized (insn) < 0
+         || recog_memoized (dep_insn) < 0)
+       return;
+
+      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--;
+
+      /* The only input for a call that is timing-critical is the
+        function's address.  */
+      if (GET_CODE(insn) == CALL_INSN)
+       {
+         rtx call = PATTERN (insn);
+
+         if (GET_CODE (call) == PARALLEL)
+           call = XVECEXP (call, 0 ,0);
+         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;
+       }
+      /* 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.  */
+      /* 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
+              && XVECLEN (PATTERN (insn), 0) >= 4
+              && (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;
+    }
+  /* 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
+          && recog_memoized (dep_insn) >= 0
+          && get_attr_type (dep_insn) == TYPE_DFP_ARITH
+          /* 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;
+}
+
+/* Check if INSN is flow-dependent on DEP_INSN.  Can also be used to check
+   if DEP_INSN is anti-flow dependent on INSN.  */
+static int
+flow_dependent_p (insn, dep_insn)
+     rtx insn, dep_insn;
+{
+  rtx tmp = PATTERN (insn);
+
+  note_stores (PATTERN (dep_insn), flow_dependent_p_1, &tmp);
+  return tmp == NULL_RTX;
+}
+
+/* A helper function for flow_dependent_p called through note_stores.  */
+static void
+flow_dependent_p_1 (x, pat, data)
+     rtx x;
+     rtx pat ATTRIBUTE_UNUSED;
+     void *data;
+{
+  rtx * pinsn = (rtx *) data;
+
+  if (*pinsn && reg_referenced_p (x, *pinsn))
+    *pinsn = NULL_RTX;
+}
+
+/* For use by 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
+sh_pr_n_sets ()
+{
+  return REG_N_SETS (TARGET_SHMEDIA ? PR_MEDIA_REG : PR_REG);
+}
+
+/* This Function returns nonzero if the DFA based scheduler interface
+   is to be used.  At present this is supported for the SH4 only.  */
+static int
+sh_use_dfa_interface()
+{
+  if (TARGET_HARD_SH4)
+    return 1;
+  else
+    return 0;
+}
+
+/* This function returns "2" to indicate dual issue for the SH4
+   processor.  To be used by the DFA pipeline description.  */
+static int
+sh_issue_rate()
+{
+  if (TARGET_SUPERSCALAR)
+    return 2;
+  else
+    return 1;
+}
+
+/* SHmedia requires registers for branches, so we can't generate new
+   branches past reload.  */
+static bool
+sh_cannot_modify_jumps_p ()
+{
+  return (TARGET_SHMEDIA && (reload_in_progress || reload_completed));
+}
+
+static bool
+sh_ms_bitfield_layout_p (record_type)
+     tree record_type ATTRIBUTE_UNUSED;
+{
+  return TARGET_SH5;
+}
+
+/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
+   may access it using GOTOFF instead of GOT.  */
+
+static void
+sh_encode_section_info (decl, first)
+     tree decl;
+     int first;
+{
+  rtx rtl, symbol;
+
+  if (DECL_P (decl))
+    rtl = DECL_RTL (decl);
+  else
+    rtl = TREE_CST_RTL (decl);
+  if (GET_CODE (rtl) != MEM)
+    return;
+  symbol = XEXP (rtl, 0);
+  if (GET_CODE (symbol) != SYMBOL_REF)
+    return;
+
+  if (flag_pic)
+    SYMBOL_REF_FLAG (symbol) = (*targetm.binds_local_p) (decl);
+
+  if (TARGET_SH5 && first && TREE_CODE (decl) != FUNCTION_DECL)
+    XEXP (rtl, 0) = gen_datalabel_ref (symbol);
+}
+
+/* Undo the effects of the above.  */
+
+static const char *
+sh_strip_name_encoding (str)
+     const char *str;
+{
+  STRIP_DATALABEL_ENCODING (str, str);
+  str += *str == '*';
+  return str;
+}
+
+\f
+/* 
+   On the SH1..SH4, the trampoline looks like
+   2 0002 D202                 mov.l   l2,r2
+   1 0000 D301                 mov.l   l1,r3
+   3 0004 422B                 jmp     @r2
+   4 0006 0009                 nop
+   5 0008 00000000     l1:     .long   area
+   6 000c 00000000     l2:     .long   function
+
+   SH5 (compact) uses r1 instead of r3 for the static chain.  */
+
+
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+   FNADDR is an RTX for the address of the function's pure code.
+   CXT is an RTX for the static chain value for the function.  */
+
+void
+sh_initialize_trampoline (tramp, fnaddr, cxt)
+     rtx tramp, fnaddr, cxt;
+{
+  if (TARGET_SHMEDIA64)
+    {
+      rtx tramp_templ;
+      int fixed_len;
+
+      rtx movi1 = GEN_INT (0xcc000010);
+      rtx shori1 = GEN_INT (0xc8000010);
+      rtx src, dst;
+
+      /* The following trampoline works within a +- 128 KB range for cxt:
+        ptb/u cxt,tr1; movi fnaddr >> 48,r0; shori fnaddr >> 32,r0;
+         shori fnaddr >> 16,r0; shori fnaddr,r0; ptabs/l r0,tr0
+         gettr tr1,r1; blink tr0,r63  */
+      /* Address rounding makes it hard to compute the exact bounds of the
+        offset for this trampoline, but we have a rather generous offset
+        range, so frame_offset should do fine as an upper bound.  */
+      if (cxt == virtual_stack_vars_rtx && frame_offset < 0x20000)
+       {
+         /* ??? could optimize this trampoline initialization
+            by writing DImode words with two insns each.  */
+         rtx mask = force_reg (DImode, GEN_INT (0x3fffc00));
+         rtx insn = gen_rtx_MINUS (DImode, cxt, tramp);
+         insn = gen_rtx_ASHIFT (DImode, insn, GEN_INT (10-2));
+         insn = gen_rtx_AND (DImode, insn, mask);
+         /* Or in ptb/u .,tr1 pattern */
+         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);
+         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);
+         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);
+         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);
+         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)),
+                         GEN_INT (0x6bf10600));
+         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 24)),
+                         GEN_INT (0x4415fc10));
+         emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 28)),
+                         GEN_INT (0x4401fff0));
+         emit_insn (gen_ic_invalidate_line (tramp));
+         return;
+       }
+      tramp_templ = gen_rtx_SYMBOL_REF (Pmode,"__GCC_nested_trampoline");
+      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);
+      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))), 
+                     cxt);
+      emit_insn (gen_ic_invalidate_line (tramp));
+      return;
+    }
+  else if (TARGET_SHMEDIA)
+    {
+      /* movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
+         movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63  */
+      rtx quad0 = gen_reg_rtx (DImode), cxtload = gen_reg_rtx (DImode);
+      rtx quad1 = gen_reg_rtx (DImode), quad2 = gen_reg_rtx (DImode);
+      /* movi 0,r1: 0xcc000010 shori 0,r1: c8000010  concatenated,
+        rotated 10 right, and higer 16 bit of every 32 selected.  */
+      rtx movishori
+       = force_reg (V2HImode, (simplify_gen_subreg
+                               (V2HImode, GEN_INT (0x4330432), SImode, 0)));
+      rtx ptabs = force_reg (DImode, GEN_INT (0x6bf10600));
+      rtx blink = force_reg (DImode, GEN_INT (0x4401fff0));
+
+      tramp = force_reg (Pmode, tramp);
+      fnaddr = force_reg (SImode, fnaddr);
+      cxt = force_reg (SImode, cxt);
+      emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, quad0, 0),
+                                gen_rtx_SUBREG (V2HImode, fnaddr, 0),
+                                movishori));
+      emit_insn (gen_rotldi3_mextr (quad0, quad0,
+                                   GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
+      emit_insn (gen_ashldi3_media (quad0, quad0, GEN_INT (2)));
+      emit_move_insn (gen_rtx_MEM (DImode, tramp), quad0);
+      emit_insn (gen_mshflo_w_x (gen_rtx_SUBREG (V4HImode, cxtload, 0),
+                                gen_rtx_SUBREG (V2HImode, cxt, 0),
+                                movishori));
+      emit_insn (gen_rotldi3_mextr (cxtload, cxtload,
+                                   GEN_INT (TARGET_LITTLE_ENDIAN ? 24 : 56)));
+      emit_insn (gen_ashldi3_media (cxtload, cxtload, GEN_INT (2)));
+      if (TARGET_LITTLE_ENDIAN)
+       {
+         emit_insn (gen_mshflo_l_di (quad1, ptabs, cxtload));
+         emit_insn (gen_mextr4 (quad2, cxtload, blink));
+       }
+      else
+       {
+         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_insn (gen_ic_invalidate_line (tramp));
+      return;
+    }
+  else if (TARGET_SHCOMPACT)
+    {
+      emit_insn (gen_initialize_trampoline (tramp, cxt, fnaddr));
+      return;
+    }
+  emit_move_insn (gen_rtx_MEM (SImode, tramp),
+                 gen_int_mode (TARGET_LITTLE_ENDIAN ? 0xd301d202 : 0xd202d301,
+                               SImode));
+  emit_move_insn (gen_rtx_MEM (SImode, plus_constant (tramp, 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);
+  if (TARGET_HARVARD)
+    {
+      if (TARGET_USERMODE)
+       emit_library_call (gen_rtx_SYMBOL_REF (Pmode, "__ic_invalidate"),
+                          0, VOIDmode, 1, tramp, SImode);
+      else
+       emit_insn (gen_ic_invalidate_line (tramp));
+    }
+}
+
+\f
+/* Machine specific built-in functions.  */
+
+struct builtin_description
+{
+  const enum insn_code icode;
+  const char *const name;
+  int signature;
+};
+
+/* describe number and signedness of arguments; arg[0] == result
+   (1: unsigned, 2: signed, 4: don't care, 8: pointer 0: no argument */
+static const char signature_args[][4] =
+{
+#define SH_BLTIN_V2SI2 0
+  { 4, 4 },
+#define SH_BLTIN_V4HI2 1
+  { 4, 4 },
+#define SH_BLTIN_V2SI3 2
+  { 4, 4, 4 },
+#define SH_BLTIN_V4HI3 3
+  { 4, 4, 4 },
+#define SH_BLTIN_V8QI3 4
+  { 4, 4, 4 },
+#define SH_BLTIN_MAC_HISI 5
+  { 1, 4, 4, 1 },
+#define SH_BLTIN_SH_HI 6
+  { 4, 4, 1 },
+#define SH_BLTIN_SH_SI 7
+  { 4, 4, 1 },
+#define SH_BLTIN_V4HI2V2SI 8
+  { 4, 4, 4 },
+#define SH_BLTIN_V4HI2V8QI 9
+  { 4, 4, 4 },
+#define SH_BLTIN_SISF 10
+  { 4, 2 },
+#define SH_BLTIN_LDUA_L 11
+  { 2, 8 },
+#define SH_BLTIN_LDUA_Q 12
+  { 1, 8 },
+#define SH_BLTIN_STUA_L 13
+  { 0, 8, 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
+  { 1, 2 },
+#define SH_BLTIN_3 17
+#define SH_BLTIN_SUS 17
+  { 2, 2, 1 },
+#define SH_BLTIN_PSSV 18
+  { 0, 8, 2, 2 },
+#define SH_BLTIN_XXUU 19
+#define SH_BLTIN_UUUU 19
+  { 1, 1, 1, 1 },
+#define SH_BLTIN_PV 20
+  { 0, 8 },
+};
+/* mcmv: operands considered unsigned. */
+/* mmulsum_wq, msad_ubq: result considered unsigned long long.  */
+/* mperm: control value considered unsigned int. */
+/* mshalds, mshard, mshards, mshlld, mshlrd: shift count is unsigned int. */
+/* mshards_q: returns signed short.  */
+/* nsb: takes long long arg, returns unsigned char.  */
+static const struct builtin_description bdesc[] =
+{
+  { CODE_FOR_absv2si2, "__builtin_absv2si2", SH_BLTIN_V2SI2 },
+  { CODE_FOR_absv4hi2, "__builtin_absv4hi2", SH_BLTIN_V4HI2 },
+  { CODE_FOR_addv2si3, "__builtin_addv2si3", SH_BLTIN_V2SI3 },
+  { CODE_FOR_addv4hi3, "__builtin_addv4hi3", SH_BLTIN_V4HI3 },
+  { 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_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 },
+  { CODE_FOR_negcmpgtuv8qi,"__builtin_sh_media_MCMPGT_UB", SH_BLTIN_V8QI3 },
+  { CODE_FOR_negcmpgtv2si,"__builtin_sh_media_MCMPGT_L", SH_BLTIN_V2SI3 },
+  { CODE_FOR_negcmpgtv4hi,"__builtin_sh_media_MCMPGT_W", SH_BLTIN_V4HI3 },
+  { CODE_FOR_mcmv,     "__builtin_sh_media_MCMV", SH_BLTIN_UUUU },
+  { 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_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, },
+  { CODE_FOR_mulv4hi3, "__builtin_mulv4hi3", SH_BLTIN_V4HI3 },
+  { CODE_FOR_mmulfx_l, "__builtin_sh_media_MMULFX_L", SH_BLTIN_V2SI3 },
+  { CODE_FOR_mmulfx_w, "__builtin_sh_media_MMULFX_W", SH_BLTIN_V4HI3 },
+  { CODE_FOR_mmulfxrp_w,"__builtin_sh_media_MMULFXRP_W", SH_BLTIN_V4HI3 },
+  { CODE_FOR_mmulhi_wl,        "__builtin_sh_media_MMULHI_WL", SH_BLTIN_V4HI2V2SI },
+  { CODE_FOR_mmullo_wl,        "__builtin_sh_media_MMULLO_WL", SH_BLTIN_V4HI2V2SI },
+  { CODE_FOR_mmulsum_wq,"__builtin_sh_media_MMULSUM_WQ", SH_BLTIN_XXUU },
+  { CODE_FOR_mperm_w,  "__builtin_sh_media_MPERM_W", SH_BLTIN_SH_HI },
+  { CODE_FOR_msad_ubq, "__builtin_sh_media_MSAD_UBQ", SH_BLTIN_XXUU },
+  { CODE_FOR_mshalds_l,        "__builtin_sh_media_MSHALDS_L", SH_BLTIN_SH_SI },
+  { CODE_FOR_mshalds_w,        "__builtin_sh_media_MSHALDS_W", SH_BLTIN_SH_HI },
+  { CODE_FOR_ashrv2si3,        "__builtin_ashrv2si3", SH_BLTIN_SH_SI },
+  { CODE_FOR_ashrv4hi3,        "__builtin_ashrv4hi3", SH_BLTIN_SH_HI },
+  { CODE_FOR_mshards_q,        "__builtin_sh_media_MSHARDS_Q", SH_BLTIN_SUS },
+  { CODE_FOR_mshfhi_b, "__builtin_sh_media_MSHFHI_B", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mshfhi_l, "__builtin_sh_media_MSHFHI_L", SH_BLTIN_V2SI3 },
+  { CODE_FOR_mshfhi_w, "__builtin_sh_media_MSHFHI_W", SH_BLTIN_V4HI3 },
+  { CODE_FOR_mshflo_b, "__builtin_sh_media_MSHFLO_B", SH_BLTIN_V8QI3 },
+  { CODE_FOR_mshflo_l, "__builtin_sh_media_MSHFLO_L", SH_BLTIN_V2SI3 },
+  { CODE_FOR_mshflo_w, "__builtin_sh_media_MSHFLO_W", SH_BLTIN_V4HI3 },
+  { CODE_FOR_ashlv2si3,        "__builtin_ashlv2si3", SH_BLTIN_SH_SI },
+  { CODE_FOR_ashlv4hi3,        "__builtin_ashlv4hi3", SH_BLTIN_SH_HI },
+  { CODE_FOR_lshrv2si3,        "__builtin_lshrv2si3", SH_BLTIN_SH_SI },
+  { CODE_FOR_lshrv4hi3,        "__builtin_lshrv4hi3", SH_BLTIN_SH_HI },
+  { CODE_FOR_subv2si3, "__builtin_subv2si3", SH_BLTIN_V2SI3 },
+  { CODE_FOR_subv4hi3, "__builtin_subv4hi3", SH_BLTIN_V4HI3 },
+  { CODE_FOR_sssubv2si3,"__builtin_sssubv2si3", SH_BLTIN_V2SI3 },
+  { CODE_FOR_ussubv8qi3,"__builtin_ussubv8qi3", SH_BLTIN_V8QI3 },
+  { CODE_FOR_sssubv4hi3,"__builtin_sssubv4hi3", SH_BLTIN_V4HI3 },
+  { CODE_FOR_fcosa_s,  "__builtin_sh_media_FCOSA_S", SH_BLTIN_SISF },
+  { 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_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 },
+  { CODE_FOR_ldlo_q,   "__builtin_sh_media_LDLO_Q", SH_BLTIN_LDUA_Q },
+  { CODE_FOR_sthi_l,   "__builtin_sh_media_STHI_L", SH_BLTIN_STUA_L },
+  { 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_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
+};
+
+static void
+sh_media_init_builtins ()
+{
+  tree shared[SH_BLTIN_NUM_SHARED_SIGNATURES];
+  const struct builtin_description *d;
+
+  memset (shared, 0, sizeof shared);
+  for (d = bdesc; d - bdesc < (int) (sizeof bdesc / sizeof bdesc[0]); d++)
+    {
+      tree type, arg_type;
+      int signature = d->signature;
+      int i;
+
+      if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES && shared[signature])
+       type = shared[signature];
+      else
+       {
+         int has_result = signature_args[signature][0] != 0;
+
+         if (signature_args[signature][1] == 8
+             && (insn_data[d->icode].operand[has_result].mode != Pmode))
+           continue;
+         if (! TARGET_FPU_ANY
+             && FLOAT_MODE_P (insn_data[d->icode].operand[0].mode))
+           continue;
+         type = void_list_node;
+         for (i = 3; ; i--)
+           {
+             int arg = signature_args[signature][i];
+             int opno = i - 1 + has_result;
+
+             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)));
+             else if (i)
+               continue;
+             else
+               arg_type = void_type_node;
+             if (i == 0)
+               break;
+             type = tree_cons (NULL_TREE, arg_type, type);
+           }
+         type = build_function_type (arg_type, type);
+         if (signature < SH_BLTIN_NUM_SHARED_SIGNATURES)
+           shared[signature] = type;
+       }
+      builtin_function (d->name, type, d - bdesc, BUILT_IN_MD,
+                       NULL, NULL_TREE);
+    }
+}
+
+static void
+sh_init_builtins ()
+{
+  if (TARGET_SHMEDIA)
+    sh_media_init_builtins ();
+}
+
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
+
+static rtx
+sh_expand_builtin (exp, target, subtarget, mode, ignore)
+     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);
+  unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+  const struct builtin_description *d = &bdesc[fcode];
+  enum insn_code icode = d->icode;
+  int signature = d->signature;
+  enum machine_mode tmode = VOIDmode;
+  int nop = 0, i;
+  rtx op[4];
+  rtx pat;
+
+  if (signature_args[signature][0])
+    {
+      if (ignore)
+       return 0;
+
+      tmode = insn_data[icode].operand[0].mode;
+      if (! target
+         || GET_MODE (target) != tmode
+         || ! (*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+      op[nop++] = target;
+    }
+  else
+    target = 0;
+
+  for (i = 1; i <= 3; i++, nop++)
+    {
+      tree arg;
+      enum machine_mode opmode, argmode;
+
+      if (! signature_args[signature][i])
+       break;
+      arg = TREE_VALUE (arglist);
+      if (arg == error_mark_node)
+       return const0_rtx;
+      arglist = TREE_CHAIN (arglist);
+      opmode = insn_data[icode].operand[nop].mode;
+      argmode = TYPE_MODE (TREE_TYPE (arg));
+      if (argmode != opmode)
+       arg = build1 (NOP_EXPR,
+                     (*lang_hooks.types.type_for_mode) (opmode, 0), 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]);
+    }
+
+  switch (nop)
+    {
+    case 1:
+      pat = (*insn_data[d->icode].genfun) (op[0]);
+      break;
+    case 2:
+      pat = (*insn_data[d->icode].genfun) (op[0], op[1]);
+      break;
+    case 3:
+      pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2]);
+      break;
+    case 4:
+      pat = (*insn_data[d->icode].genfun) (op[0], op[1], op[2], op[3]);
+      break;
+    }
+  if (! pat)
+    return 0;
+  emit_insn (pat);
+  return target;
+}
+
+void
+sh_expand_unop_v2sf (code, op0, op1)
+     enum rtx_code code;
+     rtx op0, op1;
+{
+  rtx sel0 = const0_rtx;
+  rtx sel1 = const1_rtx;
+  rtx (*fn) PARAMS ((rtx, rtx, rtx, rtx, rtx)) = gen_unary_sf_op;
+  rtx op = gen_rtx_fmt_e (code, SFmode, op1);
+
+  emit_insn ((*fn) (op0, op1, op, sel0, sel0));
+  emit_insn ((*fn) (op0, op1, op, sel1, sel1));
+}
+
+void
+sh_expand_binop_v2sf (code, op0, op1, op2)
+     enum rtx_code code;
+     rtx op0, op1, op2;
+{
+  rtx sel0 = const0_rtx;
+  rtx sel1 = const1_rtx;
+  rtx (*fn) PARAMS ((rtx, rtx, rtx, rtx, rtx, rtx, rtx)) = gen_binary_sf_op;
+  rtx op = gen_rtx_fmt_ee (code, SFmode, op1, op2);
+
+  emit_insn ((*fn) (op0, op1, op2, op, sel0, sel0, sel0));
+  emit_insn ((*fn) (op0, op1, op2, op, sel1, sel1, sel1));
+}
+
+/* Return the class of registers for which a mode change from FROM to TO
+   is invalid.  */
+enum reg_class 
+sh_cannot_change_mode_class (from, to)
+     enum machine_mode from, to;
+{
+  if (GET_MODE_SIZE (from) != GET_MODE_SIZE (to))
+    {
+       if (TARGET_LITTLE_ENDIAN)
+         {
+          if (GET_MODE_SIZE (to) < 8 || GET_MODE_SIZE (from) < 8)
+            return DF_REGS;
+        }
+       else
+        {
+          if (GET_MODE_SIZE (from) < 8)
+            return DF_HI_REGS;
+        }
+    }
+  return NO_REGS;
+}
+
+#include "gt-sh.h"
This page took 0.283217 seconds and 5 git commands to generate.