OSDN Git Service

(output_stack_adjust): When splitting an adjustment into two parts,
[pf3gnuchains/gcc-fork.git] / gcc / config / sh / sh.c
index 3a53b86..12a49ca 100644 (file)
@@ -1,5 +1,5 @@
 /* Output routines for GCC for Hitachi Super-H.
-   Copyright (C) 1993, 1994, 1995 Free Software Foundation, Inc.
+   Copyright (C) 1993, 1994, 1995, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -33,6 +33,7 @@ Boston, MA 02111-1307, USA.  */
 #include "regs.h"
 #include "hard-reg-set.h"
 #include "output.h"
+#include "insn-attr.h"
 
 #define MSW (TARGET_LITTLE_ENDIAN ? 1 : 0)
 #define LSW (TARGET_LITTLE_ENDIAN ? 0 : 1)
@@ -46,6 +47,13 @@ int pragma_interrupt;
    the compiler doesn't emit code to preserve all registers.  */
 static int pragma_trapa;
 
+/* This is set by #pragma nosave_low_regs.  This is useful on the SH3,
+   which has a separate set of low regs for User and Supervisor modes.
+   This should only be used for the lowest level of interrupts.  Higher levels
+   of interrupts must save the registers in case they themselves are
+   interrupted.  */
+int pragma_nosave_low_regs;
+
 /* This is used for communication between SETUP_INCOMING_VARARGS and
    sh_expand_prologue.  */
 int current_function_anonymous_args;
@@ -76,7 +84,11 @@ 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, PR_REGS, T_REGS, NO_REGS,
-  MAC_REGS, MAC_REGS,
+  MAC_REGS, MAC_REGS, FPUL_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,
 };
 
 /* Provide reg_class from a letter such as appears in the machine
@@ -85,12 +97,12 @@ int regno_reg_class[FIRST_PSEUDO_REGISTER] =
 enum reg_class reg_class_from_letter[] =
 {
   /* a */ NO_REGS, /* b */ NO_REGS, /* c */ NO_REGS, /* d */ NO_REGS,
-  /* e */ NO_REGS, /* f */ NO_REGS, /* g */ NO_REGS, /* h */ NO_REGS,
+  /* e */ NO_REGS, /* f */ FP_REGS, /* g */ NO_REGS, /* h */ NO_REGS,
   /* i */ NO_REGS, /* j */ NO_REGS, /* k */ NO_REGS, /* l */ PR_REGS,
   /* m */ NO_REGS, /* n */ NO_REGS, /* o */ NO_REGS, /* p */ NO_REGS,
   /* q */ NO_REGS, /* r */ NO_REGS, /* s */ NO_REGS, /* t */ T_REGS,
-  /* u */ NO_REGS, /* v */ NO_REGS, /* w */ NO_REGS, /* x */ MAC_REGS,
-  /* y */ NO_REGS, /* z */ R0_REGS
+  /* u */ NO_REGS, /* v */ NO_REGS, /* w */ FP0_REGS, /* x */ MAC_REGS,
+  /* y */ FPUL_REGS, /* z */ R0_REGS
 };
 \f
 /* Print the operand address in x to the stream.  */
@@ -303,11 +315,23 @@ prepare_move_operands (operands, mode)
      rtx operands[];
      enum machine_mode mode;
 {
-  /* Copy the source to a register if both operands aren't registers.  */
-  if (! reload_in_progress && ! reload_completed
-      && ! register_operand (operands[0], mode)
-      && ! register_operand (operands[1], mode))
-    operands[1] = copy_to_mode_reg (mode, operands[1]);
+  if (! reload_in_progress && ! reload_completed)
+    {
+      /* Copy the source to a register if both operands aren't registers.  */
+      if (! register_operand (operands[0], mode)
+         && ! register_operand (operands[1], mode))
+       operands[1] = copy_to_mode_reg (mode, operands[1]);
+
+      /* This case can happen while generating code to move the result
+        of a library call to the target.  Reject `st r0,@(rX,rY)' because
+        reload will fail to find a spill register for rX, since r0 is already
+        being used for the source.  */
+      else if (GET_CODE (operands[1]) == REG && REGNO (operands[1]) == 0
+              && GET_CODE (operands[0]) == MEM
+              && GET_CODE (XEXP (operands[0], 0)) == PLUS
+              && GET_CODE (XEXP (XEXP (operands[0], 0), 1)) == REG)
+       operands[1] = copy_to_mode_reg (mode, operands[1]);
+    }
 
   return 0;
 }
@@ -320,6 +344,7 @@ prepare_scc_operands (code)
 {
   rtx t_reg = gen_rtx (REG, SImode, T_REG);
   enum rtx_code oldcode = code;
+  enum machine_mode mode;
 
   /* First need a compare insn.  */
   switch (code)
@@ -347,12 +372,18 @@ prepare_scc_operands (code)
       sh_compare_op1 = tmp;
     }
 
-  sh_compare_op0 = force_reg (SImode, sh_compare_op0);
+  mode = GET_MODE (sh_compare_op0);
+  if (mode == VOIDmode)
+    mode = GET_MODE (sh_compare_op1);
+
+  sh_compare_op0 = force_reg (mode, sh_compare_op0);
   if (code != EQ && code != NE
       && (sh_compare_op1 != const0_rtx
          || code == GTU  || code == GEU || code == LTU || code == LEU))
-    sh_compare_op1 = force_reg (SImode, sh_compare_op1);
+    sh_compare_op1 = force_reg (mode, sh_compare_op1);
 
+  /* ??? This should be `mode' not `SImode' in the compare, but that would
+     require fixing the branch patterns too.  */
   emit_insn (gen_rtx (SET, VOIDmode, t_reg,
                      gen_rtx (code, SImode, sh_compare_op0,
                               sh_compare_op1)));
@@ -369,11 +400,15 @@ from_compare (operands, code)
 {
   if (code != EQ && code != NE)
     {
+      enum machine_mode mode = GET_MODE (sh_compare_op0);
+      if (mode == VOIDmode)
+       mode = GET_MODE (sh_compare_op1);
+
       /* Force args into regs, since we can't use constants here.  */
-      sh_compare_op0 = force_reg (SImode, sh_compare_op0);
+      sh_compare_op0 = force_reg (mode, sh_compare_op0);
       if (sh_compare_op1 != const0_rtx
          || code == GTU  || code == GEU || code == LTU || code == LEU)
-       sh_compare_op1 = force_reg (SImode, sh_compare_op1);
+       sh_compare_op1 = force_reg (mode, sh_compare_op1);
     }
   operands[1] = sh_compare_op0;
   operands[2] = sh_compare_op1;
@@ -517,8 +552,22 @@ output_branch (logic, insn, operands)
      rtx *operands;
 {
   int label = lf++;
+  int length = get_attr_length (insn);
+  int adjusted_length;
+
+  /* Undo the effects of ADJUST_INSN_LENGTH, so that we get the real
+     length.  If NEXT_INSN (PREV_INSN (insn)) != insn, then the insn
+     is inside a sequence, and ADJUST_INSN_LENGTH was not called on
+     it.  */
+  if (PREV_INSN (insn) == NULL
+      || NEXT_INSN (PREV_INSN (insn)) == insn)
+    {
+      adjusted_length = length;
+      ADJUST_INSN_LENGTH (insn, adjusted_length);
+      length -= (adjusted_length - length);
+    }
 
-  switch (get_attr_length (insn))
+  switch (length)
     {
     case 2:
       /* A branch with an unfilled delay slot.  */
@@ -591,102 +640,15 @@ output_branch (logic, insn, operands)
       }
       return "";
     }
-  return "bad";
-}
-\f
-/* A copy of the option structure defined in toplev.c.  */
-
-struct option
-{
-  char *string;
-  int *variable;
-  int on_value;
-};
-
-/* Output a single output option string NAME to FILE, without generating
-   lines longer than MAX.  */
-
-static int
-output_option (file, sep, type, name, indent, pos, max)
-     FILE *file;
-     char *sep;
-     char *type;
-     char *name;
-     char *indent;
-     int pos;
-     int max;
-{
-  if (strlen (sep) + strlen (type) + strlen (name) + pos > max)
-    {
-      fprintf (file, indent);
-      return fprintf (file, "%s%s", type, name);
-    }
-  return pos + fprintf (file, "%s%s%s", sep, type, name);
-}
-
-/* A copy of the target_switches variable in toplev.c.  */
 
-static struct
-{
-  char *name;
-  int value;
-} m_options[] = TARGET_SWITCHES;
-
-/* Output all options to the assembly language file.  */
-
-static void
-output_options (file, f_options, f_len, W_options, W_len,
-               pos, max, sep, indent, term)
-     FILE *file;
-     struct option *f_options;
-     struct option *W_options;
-     int f_len, W_len;
-     int pos;
-     int max;
-     char *sep;
-     char *indent;
-     char *term;
-{
-  register int j;
-
-  if (optimize)
-    pos = output_option (file, sep, "-O", "", indent, pos, max);
-  if (write_symbols != NO_DEBUG)
-    pos = output_option (file, sep, "-g", "", indent, pos, max);
-  if (profile_flag)
-    pos = output_option (file, sep, "-p", "", indent, pos, max);
-  if (profile_block_flag)
-    pos = output_option (file, sep, "-a", "", indent, pos, max);
-
-  for (j = 0; j < f_len; j++)
-    if (*f_options[j].variable == f_options[j].on_value)
-      pos = output_option (file, sep, "-f", f_options[j].string,
-                          indent, pos, max);
-
-  for (j = 0; j < W_len; j++)
-    if (*W_options[j].variable == W_options[j].on_value)
-      pos = output_option (file, sep, "-W", W_options[j].string,
-                          indent, pos, max);
-
-  for (j = 0; j < sizeof m_options / sizeof m_options[0]; j++)
-    if (m_options[j].name[0] != '\0'
-       && m_options[j].value > 0
-       && ((m_options[j].value & target_flags)
-           == m_options[j].value))
-      pos = output_option (file, sep, "-m", m_options[j].name,
-                          indent, pos, max);
-
-  fprintf (file, term);
+  abort ();
 }
-
+\f
 /* Output to FILE the start of the assembler file.  */
 
 void
-output_file_start (file, f_options, f_len, W_options, W_len)
+output_file_start (file)
      FILE *file;
-     struct option *f_options;
-     struct option *W_options;
-     int f_len, W_len;
 {
   register int pos;
 
@@ -696,10 +658,6 @@ output_file_start (file, f_options, f_len, W_options, W_len)
      gcc2_compiled. symbol aren't in the text section.  */
   data_section ();
 
-  pos = fprintf (file, "\n! Hitachi SH cc1 (%s) arguments:", version_string);
-  output_options (file, f_options, f_len, W_options, W_len,
-                 pos, 75, " ", "\n! ", "\n\n");
-
   if (TARGET_LITTLE_ENDIAN)
     fprintf (file, "\t.little\n");
 }
@@ -726,6 +684,28 @@ static short shift_amounts[32][5] = {
   {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
   {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
 
+/* Likewise, but for shift amounts < 16, up to three highmost bits
+   might be clobbered.  This is typically used when combined with some
+   kind of sign or zero extension.  */
+   
+static 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] = {
+  {0}, {1}, {2}, {2, 1},
+  {2, 2}, {2, 1, 2}, {8, -2}, {8, -1},
+  {8}, {8, 1}, {8, 2}, {8, 1, 2},
+  {8, 2, 2}, {16, -2, -1}, {16, -2}, {16, -1},
+  {16}, {16, 1}, {16, 2}, {16, 1, 2},
+  {16, 2, 2}, {16, 2, 1, 2}, {16, -2, 8}, {16, -1, 8},
+  {16, 8}, {16, 1, 8}, {16, 8, 2}, {16, 8, 1, 2},
+  {16, 8, 2, 2}, {16, -1, -2, 16}, {16, -2, 16}, {16, -1, 16}};
+
+/* Assuming we have a value that has been sign-extended by at least one bit,
+   can we use the ext_shift_amounts with the last shift turned to an arithmetic shift
+   to shift it by N without data loss, and quicker than by other means?  */
+#define EXT_SHIFT_SIGNED(n) (((n) | 8) == 15)
+
 /* This is used in length attributes in sh.md to help compute the length
    of arbitrary constant shift instructions.  */
 
@@ -768,7 +748,13 @@ shiftcosts (x)
 
   /* Otherwise, return the true cost in instructions.  */
   if (GET_CODE (x) == ASHIFTRT)
-    return ashiftrt_insns[value];
+    {
+      int cost = ashiftrt_insns[value];
+      /* If SH3, then we put the constant in a reg and use shad.  */
+      if (TARGET_SH3 && cost > 3)
+       cost = 3;
+      return cost;
+    }
   else
     return shift_insns[value];
 }
@@ -861,12 +847,44 @@ gen_ashift (type, n, reg)
     }
 }
 
+/* Same for HImode */
+
+void
+gen_ashift_hi (type, n, reg)
+     int type;
+     int n;
+     rtx reg;
+{
+  /* Negative values here come from the shift_amounts array.  */
+  if (n < 0)
+    {
+      if (type == ASHIFT)
+       type = LSHIFTRT;
+      else
+       type = ASHIFT;
+      n = -n;
+    }
+
+  switch (type)
+    {
+    case ASHIFTRT:
+      emit_insn (gen_ashrhi3_k (reg, reg, GEN_INT (n)));
+      break;
+    case LSHIFTRT:
+      if (n == 1)
+       emit_insn (gen_lshrhi3_m (reg, reg, GEN_INT (n)));
+      else
+       emit_insn (gen_lshrhi3_k (reg, reg, GEN_INT (n)));
+      break;
+    case ASHIFT:
+      emit_insn (gen_ashlhi3_k (reg, reg, GEN_INT (n)));
+      break;
+    }
+}
+
 /* Output RTL to split a constant shift into its component SH constant
    shift instructions.  */
    
-/* ??? For SH3, should reject constant shifts when slower than loading the
-   shift count into a register?  */
-
 int
 gen_shifty_op (code, operands)
      int code;
@@ -875,6 +893,9 @@ gen_shifty_op (code, operands)
   int value = INTVAL (operands[2]);
   int max, i;
 
+  /* Truncate the shift count in case it is out of bounds.  */
+  value = value & 0x1f;
   if (value == 31)
     {
       if (code == LSHIFTRT)
@@ -895,11 +916,54 @@ gen_shifty_op (code, operands)
            }
        }
     }
+  else if (value == 0)
+    {
+      /* This can happen when not optimizing.  We must output something here
+        to prevent the compiler from aborting in final.c after the try_split
+        call.  */
+      emit_insn (gen_nop ());
+      return;
+    }
 
   max = shift_insns[value];
   for (i = 0; i < max; i++)
     gen_ashift (code, shift_amounts[value][i], operands[0]);
 }
+   
+/* Same as above, but optimized for values where the topmost bits don't
+   matter.  */
+
+int
+gen_shifty_hi_op (code, operands)
+     int code;
+     rtx *operands;
+{
+  int value = INTVAL (operands[2]);
+  int max, i;
+  void (*gen_fun)();
+
+  /* This operation is used by and_shl for SImode values with a few
+     high bits known to be cleared.  */
+  value &= 31;
+  if (value == 0)
+    {
+      emit_insn (gen_nop ());
+      return;
+    }
+
+  gen_fun = GET_MODE (operands[0]) == HImode ? gen_ashift_hi : gen_ashift;
+  if (code == ASHIFT)
+    {
+      max = ext_shift_insns[value];
+      for (i = 0; i < max; i++)
+       gen_fun (code, ext_shift_amounts[value][i], operands[0]);
+    }
+  else
+    /* When shifting right, emit the shifts in reverse order, so that
+       solitary negative values come first.  */
+    for (i = ext_shift_insns[value] - 1; i >= 0; i--)
+      gen_fun (code, ext_shift_amounts[value][i], operands[0]);
+}
 
 /* Output RTL for an arithmetic right shift.  */
 
@@ -914,12 +978,21 @@ expand_ashiftrt (operands)
   tree func_name;
   int value;
 
-  if (TARGET_SH3 && GET_CODE (operands[2]) != CONST_INT)
+  if (TARGET_SH3)
     {
-      rtx count = copy_to_mode_reg (SImode, operands[2]);
-      emit_insn (gen_negsi2 (count, count));
-      emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
-      return 1;
+      if (GET_CODE (operands[2]) != CONST_INT)
+       {
+         rtx count = copy_to_mode_reg (SImode, operands[2]);
+         emit_insn (gen_negsi2 (count, count));
+         emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
+         return 1;
+       }
+      else if (ashiftrt_insns[INTVAL (operands[2])] > 3)
+       {
+         rtx count = force_reg (SImode, GEN_INT (- INTVAL (operands[2])));
+         emit_insn (gen_ashrsi3_d (operands[0], operands[1], count));
+         return 1;
+       }
     }
   if (GET_CODE (operands[2]) != CONST_INT)
     return 0;
@@ -964,6 +1037,533 @@ expand_ashiftrt (operands)
   emit_move_insn (operands[0], gen_rtx (REG, SImode, 4));
   return 1;
 }
+
+/* Try to find a good way to implement the combiner pattern
+  [(set (match_operand:SI 0 "register_operand" "r")
+        (and:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+                           (match_operand:SI 2 "const_int_operand" "n"))
+                (match_operand:SI 3 "const_int_operand" "n"))) .
+  LEFT_RTX is operand 2 in the above pattern, and MASK_RTX is operand 3.
+  return 0 for simple right / left or left/right shift combination.
+  return 1 for a combination of shifts with zero_extend.
+  return 2 for a combination of shifts with an AND that needs r0.
+  return 3 for a combination of shifts with an AND that needs an extra
+    scratch register, when the three highmost bits of the AND mask are clear.
+  return 4 for a combination of shifts with an AND that needs an extra
+    scratch register, when any of the three highmost bits of the AND mask
+    is set.
+  If ATTRP is set, store an initial right shift width in ATTRP[0],
+  and the instruction length in ATTRP[1] .  These values are not valid
+  when returning 0.
+  When ATTRP is set and returning 1, ATTRP[2] gets set to the index into
+  shift_amounts for the last shift value that is to be used before the
+  sign extend.  */
+int
+shl_and_kind (left_rtx, mask_rtx, attrp)
+     rtx left_rtx, mask_rtx;
+     int *attrp;
+{
+  unsigned HOST_WIDE_INT mask, lsb, mask2, lsb2;
+  int left = INTVAL (left_rtx), right;
+  int best = 0;
+  int cost, best_cost = 10000;
+  int best_right = 0, best_len = 0;
+  int i;
+  int can_ext;
+
+  if (left < 0 || left > 31)
+    return 0;
+  if (GET_CODE (mask_rtx) == CONST_INT)
+    mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> left;
+  else
+    mask = (unsigned HOST_WIDE_INT) GET_MODE_MASK (SImode) >> left;
+  /* Can this be expressed as a right shift / left shift pair ? */
+  lsb = ((mask ^ (mask - 1)) >> 1) + 1;
+  right = exact_log2 (lsb);
+  mask2 = ~(mask + lsb - 1);
+  lsb2 = ((mask2 ^ (mask2 - 1)) >> 1) + 1;
+  /* mask has no zeroes but trailing zeroes <==> ! mask2 */
+  if (! mask2)
+    best_cost = shift_insns[right] + shift_insns[right + left];
+  /* mask has no trailing zeroes <==> ! right */
+  else if (! right && mask2 == ~(lsb2 - 1))
+    {
+      int late_right = exact_log2 (lsb2);
+      best_cost = shift_insns[left + late_right] + shift_insns[late_right];
+    }
+  /* Try to use zero extend */
+  if (mask2 == ~(lsb2 - 1))
+    {
+      int width, first;
+
+      for (width = 8; width <= 16; width += 8)
+       {
+         /* Can we zero-extend right away? */
+         if (lsb2 == (HOST_WIDE_INT)1 << width)
+           {
+             cost
+               = 1 + ext_shift_insns[right] + ext_shift_insns[left + right];
+             if (cost < best_cost)
+               {
+                 best = 1;
+                 best_cost = cost;
+                 best_right = right;
+                 best_len = cost;
+                 if (attrp)
+                   attrp[2] = -1;
+               }
+             continue;
+           }
+         /* ??? Could try to put zero extend into initial 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;
+         if (first >= 0 && right + left - first >= 0)
+           {
+             cost = ext_shift_insns[right] + ext_shift_insns[first] + 1
+               + ext_shift_insns[right + left - first];
+             if (cost < best_cost)
+               {
+                 best = 1;
+                 best_cost = cost;
+                 best_right = right;
+                 best_len = cost;
+                 if (attrp)
+                   attrp[2] = first;
+                 }
+           }
+       }
+    }
+  /* Try to use r0 AND pattern */
+  for (i = 0; i <= 2; i++)
+    {
+      if (i > right)
+       break;
+      if (! CONST_OK_FOR_L (mask >> i))
+       continue;
+      cost = (i != 0) + 2 + ext_shift_insns[left + i];
+      if (cost < best_cost)
+       {
+         best = 2;
+         best_cost = cost;
+         best_right = i;
+         best_len = cost - 1;
+       }
+    }
+  /* Try to use a scratch register to hold the AND operand.  */
+  can_ext = ((mask << left) & 0xe0000000) == 0;
+  for (i = 0; i <= 2; i++)
+    {
+      if (i > right)
+       break;
+      cost = (i != 0) + (CONST_OK_FOR_I (mask >> i) ? 2 : 3)
+       + (can_ext ? ext_shift_insns : shift_insns)[left + i];
+      if (cost < best_cost)
+       {
+         best = 4 - can_ext;
+         best_cost = cost;
+         best_right = i;
+         best_len = cost - 1 - ! CONST_OK_FOR_I (mask >> i);
+       }
+    }
+
+  if (attrp)
+    {
+      attrp[0] = best_right;
+      attrp[1] = best_len;
+    }
+  return best;
+}
+
+/* This is used in length attributes of the unnamed instructions
+   corresponding to shl_and_kind return values of 1 and 2.  */
+int
+shl_and_length (insn)
+     rtx insn;
+{
+  rtx set_src, left_rtx, mask_rtx;
+  int attributes[3];
+
+  set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+  left_rtx = XEXP (XEXP (set_src, 0), 1);
+  mask_rtx = XEXP (set_src, 1);
+  shl_and_kind (left_rtx, mask_rtx, attributes);
+  return attributes[1];
+}
+
+/* This is used in length attribute of the and_shl_scratch instruction.  */
+
+int
+shl_and_scr_length (insn)
+     rtx insn;
+{
+  rtx set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+  int len = shift_insns[INTVAL (XEXP (set_src, 1))];
+  rtx op = XEXP (set_src, 0);
+  len += shift_insns[INTVAL (XEXP (op, 1))] + 1;
+  op = XEXP (XEXP (op, 0), 0);
+  return len + shift_insns[INTVAL (XEXP (op, 1))];
+}
+
+/* Generating rtl? */
+extern int rtx_equal_function_value_matters;
+
+/* Generate rtl for instructions for which shl_and_kind advised a particular
+   method of generating them, i.e. returned zero.  */
+
+int
+gen_shl_and (dest, left_rtx, mask_rtx, source)
+     rtx dest, left_rtx, mask_rtx, source;
+{
+  int attributes[3];
+  unsigned HOST_WIDE_INT mask;
+  int kind = shl_and_kind (left_rtx, mask_rtx, attributes);
+  int right, total_shift;
+  int (*shift_gen_fun) PROTO((int, rtx*)) = gen_shifty_hi_op;
+
+  right = attributes[0];
+  total_shift = INTVAL (left_rtx) + right;
+  mask = (unsigned HOST_WIDE_INT) INTVAL (mask_rtx) >> total_shift;
+  switch (kind)
+    {
+    default:
+      return -1;
+    case 1:
+      {
+       int first = attributes[2];
+       rtx operands[3];
+
+       if (first < 0)
+         {
+           emit_insn ((mask << right) == 0xff
+                      ? gen_zero_extendqisi2(dest,
+                                             gen_lowpart (QImode, source))
+                      : gen_zero_extendhisi2(dest,
+                                             gen_lowpart (HImode, source)));
+           source = dest;
+         }
+       if (source != dest)
+         emit_insn (gen_movsi (dest, source));
+       operands[0] = dest;
+       if (right)
+         {
+           operands[2] = GEN_INT (right);
+           gen_shifty_hi_op (LSHIFTRT, operands);
+         }
+       if (first > 0)
+         {
+           operands[2] = GEN_INT (first);
+           gen_shifty_hi_op (ASHIFT, operands);
+           total_shift -= first;
+           mask <<= first;
+         }
+       if (first >= 0)
+         emit_insn (mask == 0xff
+                    ? gen_zero_extendqisi2(dest, gen_lowpart (QImode, dest))
+                    : gen_zero_extendhisi2(dest, gen_lowpart (HImode, dest)));
+       if (total_shift > 0)
+         {
+           operands[2] = GEN_INT (total_shift);
+           gen_shifty_hi_op (ASHIFT, operands);
+         }
+       break;
+      }
+    case 4:
+      shift_gen_fun = gen_shifty_op;
+    case 2:
+    case 3:
+      /* If the topmost bit that matters is set, set the topmost bits
+        that don't matter.  This way, we might be able to get a shorter
+        signed constant.  */
+      if (mask & ((HOST_WIDE_INT)1 << 31 - total_shift))
+       mask |= (HOST_WIDE_INT)~0 << (31 - total_shift);
+      /* Don't expand fine-grained when combining, because that will
+         make the pattern fail.  */
+      if (rtx_equal_function_value_matters
+         || reload_in_progress || reload_completed)
+       {
+         rtx operands[3];
+  
+         if (right)
+           {
+             emit_insn (gen_lshrsi3 (dest, source, GEN_INT (right)));
+             source = dest;
+           }
+         emit_insn (gen_andsi3 (dest, source, GEN_INT (mask)));
+         if (total_shift)
+           {
+             operands[0] = dest;
+             operands[1] = dest;
+             operands[2] = GEN_INT (total_shift);
+             shift_gen_fun (ASHIFT, operands);
+           }
+         break;
+       }
+      else
+       {
+         int neg = 0;
+         if (kind != 4 && total_shift < 16)
+           {
+             neg = -ext_shift_amounts[total_shift][1];
+             if (neg > 0)
+               neg -= ext_shift_amounts[total_shift][2];
+             else
+               neg = 0;
+           }
+         emit_insn (gen_and_shl_scratch (dest, source,
+                                         GEN_INT (right),
+                                         GEN_INT (mask),
+                                         GEN_INT (total_shift + neg),
+                                         GEN_INT (neg)));
+         emit_insn (gen_movsi (dest, dest));
+         break;
+       }
+    }
+  return 0;
+}
+
+/* Try to find a good way to implement the combiner pattern
+  [(set (match_operand:SI 0 "register_operand" "=r")
+        (sign_extract:SI (ashift:SI (match_operand:SI 1 "register_operand" "r")
+                                    (match_operand:SI 2 "const_int_operand" "n")
+                         (match_operand:SI 3 "const_int_operand" "n")
+                         (const_int 0)))
+   (clobber (reg:SI 18))]
+  LEFT_RTX is operand 2 in the above pattern, and SIZE_RTX is operand 3.
+  return 0 for simple left / right shift combination.
+  return 1 for left shift / 8 bit sign extend / left shift.
+  return 2 for left shift / 16 bit sign extend / left shift.
+  return 3 for left shift / 8 bit sign extend / shift / sign extend.
+  return 4 for left shift / 16 bit sign extend / shift / sign extend.
+  return 5 for left shift / 16 bit sign extend / right shift
+  return 6 for < 8 bit sign extend / left shift.
+  return 7 for < 8 bit sign extend / left shift / single right shift.
+  If COSTP is nonzero, assign the calculated cost to *COSTP.  */
+
+int
+shl_sext_kind (left_rtx, size_rtx, costp)
+     rtx left_rtx, size_rtx;
+     int *costp;
+{
+  int left, size, insize, ext;
+  int cost, best_cost;
+  int kind;
+
+  left = INTVAL (left_rtx);
+  size = INTVAL (size_rtx);
+  insize = size - left;
+  if (insize <= 0)
+    abort ();
+  /* Default to left / right shift.  */
+  kind = 0;
+  best_cost = shift_insns[32 - insize] + ashiftrt_insns[32 - size];
+  if (size <= 16)
+    {
+      /* 16 bit shift / sign extend / 16 bit shift */
+      cost = shift_insns[16 - insize] + 1 + ashiftrt_insns[16 - size];
+      /* If ashiftrt_insns[16 - size] is 8, this choice will be overridden
+        below, by alternative 3 or something even better.  */
+      if (cost < best_cost)
+       {
+         kind = 5;
+         best_cost = cost;
+       }
+    }
+  /* Try a plain sign extend between two shifts.  */
+  for (ext = 16; ext >= insize; ext -= 8)
+    {
+      if (ext <= size)
+       {
+         cost = ext_shift_insns[ext - insize] + 1 + shift_insns[size - ext];
+         if (cost < best_cost)
+           {
+             kind = ext / 8U;
+             best_cost = cost;
+           }
+       }
+      /* Check if we can do a sloppy shift with a final signed shift
+        restoring the sign.  */
+      if (EXT_SHIFT_SIGNED (size - ext))
+       cost = ext_shift_insns[ext - insize] + ext_shift_insns[size - ext] + 1;
+      /* If not, maybe it's still cheaper to do the second shift sloppy,
+        and do a final sign extend?  */
+      else if (size <= 16)
+       cost = ext_shift_insns[ext - insize] + 1
+         + ext_shift_insns[size > ext ? size - ext : ext - size] + 1;
+      else
+       continue;
+      if (cost < best_cost)
+       {
+         kind = ext / 8U + 2;
+         best_cost = cost;
+       }
+    }
+  /* Check if we can sign extend in r0 */
+  if (insize < 8)
+    {
+      cost = 3 + shift_insns[left];
+      if (cost < best_cost)
+       {
+         kind = 6;
+         best_cost = cost;
+       }
+      /* Try the same with a final signed shift.  */
+      if (left < 31)
+       {
+         cost = 3 + ext_shift_insns[left + 1] + 1;
+         if (cost < best_cost)
+           {
+             kind = 7;
+             best_cost = cost;
+           }
+       }
+    }
+  if (TARGET_SH3)
+    {
+      /* Try to use a dynamic shift.  */
+      cost = shift_insns[32 - insize] + 3;
+      if (cost < best_cost)
+       {
+         kind = 0;
+         best_cost = cost;
+       }
+    }
+  if (costp)
+    *costp = cost;
+  return kind;
+}
+
+/* Function to be used in the length attribute of the instructions
+   implementing this pattern.  */
+
+int
+shl_sext_length (insn)
+     rtx insn;
+{
+  rtx set_src, left_rtx, size_rtx;
+  int cost;
+
+  set_src = SET_SRC (XVECEXP (PATTERN (insn), 0, 0));
+  left_rtx = XEXP (XEXP (set_src, 0), 1);
+  size_rtx = XEXP (set_src, 1);
+  shl_sext_kind (left_rtx, size_rtx, &cost);
+  return cost;
+}
+
+/* Generate rtl for this pattern */
+
+int
+gen_shl_sext (dest, left_rtx, size_rtx, source)
+     rtx dest, left_rtx, size_rtx, source;
+{
+  int kind;
+  int left, size, insize, cost;
+  rtx operands[3];
+
+  kind = shl_sext_kind (left_rtx, size_rtx, &cost);
+  left = INTVAL (left_rtx);
+  size = INTVAL (size_rtx);
+  insize = size - left;
+  switch (kind)
+    {
+    case 1:
+    case 2:
+    case 3:
+    case 4:
+      {
+       int ext = kind & 1 ? 8 : 16;
+       int shift2 = size - ext;
+
+       /* Don't expand fine-grained when combining, because that will
+          make the pattern fail.  */
+       if (! rtx_equal_function_value_matters
+           && ! reload_in_progress && ! reload_completed)
+         {
+           emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+           emit_insn (gen_movsi (dest, source));
+           break;
+         }
+       if (dest != source)
+         emit_insn (gen_movsi (dest, source));
+       operands[0] = dest;
+       if (ext - insize)
+         {
+           operands[2] = GEN_INT (ext - insize);
+           gen_shifty_hi_op (ASHIFT, operands);
+         }
+       emit_insn (kind & 1
+                  ? gen_extendqisi2(dest, gen_lowpart (QImode, dest))
+                  : gen_extendhisi2(dest, gen_lowpart (HImode, dest)));
+       if (kind <= 2)
+         {
+           if (shift2)
+             {
+               operands[2] = GEN_INT (shift2);
+               gen_shifty_op (ASHIFT, operands);
+             }
+         }
+       else
+         {
+           if (shift2 > 0)
+             {
+               if (EXT_SHIFT_SIGNED (shift2))
+                 {
+                   operands[2] = GEN_INT (shift2 + 1);
+                   gen_shifty_op (ASHIFT, operands);
+                   operands[2] = GEN_INT (1);
+                   gen_shifty_op (ASHIFTRT, operands);
+                   break;
+                 }
+               operands[2] = GEN_INT (shift2);
+               gen_shifty_hi_op (ASHIFT, operands);
+             }
+           else if (shift2)
+             {
+               operands[2] = GEN_INT (-shift2);
+               gen_shifty_hi_op (LSHIFTRT, operands);
+             }
+           emit_insn (size <= 8
+                      ? gen_extendqisi2 (dest, gen_lowpart (QImode, dest))
+                      : gen_extendhisi2 (dest, gen_lowpart (HImode, dest)));
+         }
+       break;
+      }
+    case 5:
+      {
+       int i = 16 - size;
+       emit_insn (gen_shl_sext_ext (dest, source, GEN_INT (16 - insize),
+                                    GEN_INT (16)));
+       /* Don't use gen_ashrsi3 because it generates new pseudos.  */
+       while (--i >= 0)
+         gen_ashift (ASHIFTRT, 1, dest);
+       break;
+      }
+    case 6:
+    case 7:
+      /* Don't expand fine-grained when combining, because that will
+        make the pattern fail.  */
+      if (! rtx_equal_function_value_matters
+         && ! reload_in_progress && ! reload_completed)
+       {
+         emit_insn (gen_shl_sext_ext (dest, source, left_rtx, size_rtx));
+         emit_insn (gen_movsi (dest, source));
+         break;
+       }
+      emit_insn (gen_andsi3 (dest, source, GEN_INT ((1 << insize) - 1)));
+      emit_insn (gen_xorsi3 (dest, dest, GEN_INT (1 << (insize - 1))));
+      emit_insn (gen_addsi3 (dest, dest, GEN_INT (-1 << (insize - 1))));
+      operands[0] = dest;
+      operands[2] = kind == 7 ? GEN_INT (left + 1) : left_rtx;
+      gen_shifty_op (ASHIFT, operands);
+      if (kind == 7)
+       emit_insn (gen_ashrsi3_k (dest, dest, GEN_INT (1)));
+      break;
+    default:
+      return -1;
+    }
+  return 0;
+}
 \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
@@ -1116,6 +1716,7 @@ dump_table (scan)
        case HImode:
          break;
        case SImode:
+       case SFmode:
          if (need_align)
            {
              need_align = 0;
@@ -1125,6 +1726,7 @@ dump_table (scan)
          scan = emit_label_after (p->label, scan);
          scan = emit_insn_after (gen_consttable_4 (p->value), scan);
          break;
+       case DFmode:
        case DImode:
          if (need_align)
            {
@@ -1168,15 +1770,26 @@ static int
 broken_move (insn)
      rtx insn;
 {
-  if (GET_CODE (insn) == INSN
-      && GET_CODE (PATTERN (insn)) == SET
-      /* We can load any 8 bit value if we don't care what the high
-        order bits end up as.  */
-      && GET_MODE (SET_DEST (PATTERN (insn))) != QImode
-      && CONSTANT_P (SET_SRC (PATTERN (insn)))
-      && (GET_CODE (SET_SRC (PATTERN (insn))) != CONST_INT
-         || ! CONST_OK_FOR_I (INTVAL (SET_SRC (PATTERN (insn))))))
-    return 1;
+  if (GET_CODE (insn) == INSN)
+    {
+      rtx pat = PATTERN (insn);
+      if (GET_CODE (pat) == PARALLEL)
+       pat = XVECEXP (pat, 0, 0);
+      if (GET_CODE (pat) == SET
+         /* We can load any 8 bit value if we don't care what the high
+            order bits end up as.  */
+         && GET_MODE (SET_DEST (pat)) != QImode
+         && CONSTANT_P (SET_SRC (pat))
+         && ! (GET_CODE (SET_SRC (pat)) == CONST_DOUBLE
+               && (fp_zero_operand (SET_SRC (pat))
+                   || fp_one_operand (SET_SRC (pat)))
+               && GET_CODE (SET_DEST (pat)) == REG
+               && REGNO (SET_DEST (pat)) >= FIRST_FP_REG
+               && REGNO (SET_DEST (pat)) <= LAST_FP_REG)
+         && (GET_CODE (SET_SRC (pat)) != CONST_INT
+             || ! CONST_OK_FOR_I (INTVAL (SET_SRC (pat)))))
+       return 1;
+    }
 
   return 0;
 }
@@ -1203,15 +1816,59 @@ find_barrier (from)
   int found_si = 0;
   rtx found_barrier = 0;
   rtx found_mova = 0;
+  int si_limit;
+  int hi_limit;
 
   /* For HImode: range is 510, add 4 because pc counts from address of
      second instruction after this one, subtract 2 for the jump instruction
-     that we may need to emit before the table.  This gives 512.
+     that we may need to emit before the table, subtract 2 for the instruction
+     that fills the jump delay slot (in very rare cases, reorg will take an
+     instruction from after the constant pool or will leave the delay slot
+     empty).  This gives 510.
      For SImode: range is 1020, add 4 because pc counts from address of
      second instruction after this one, subtract 2 in case pc is 2 byte
      aligned, subtract 2 for the jump instruction that we may need to emit
-     before the table.  This gives 1020.  */
-  while (from && count_si < 1020 && count_hi < 512)
+     before the table, subtract 2 for the instruction that fills the jump
+     delay slot.  This gives 1018.  */
+
+  /* If not optimizing, then it is possible that the jump instruction we add
+     won't be shortened, and thus will have a length of 14 instead of 2.
+     We must adjust the limits downwards to account for this, giving a limit
+     of 1008 for SImode and 500 for HImode.  */
+
+  if (optimize)
+    {
+      si_limit = 1018;
+      hi_limit = 510;
+    }
+  else
+    {
+      si_limit = 1008;
+      hi_limit = 500;
+    }
+
+  /* If not optimizing for space, then the constant pool will be
+     aligned to a 4 to 16 byte boundary.  We must make room for that
+     alignment that by reducing the limits.
+     ??? It would be better to not align the constant pool, but
+     ASM_OUTPUT_ALIGN_CODE does not make any provision for basing the
+     alignment on the instruction.  */
+
+  if (! TARGET_SMALLCODE)
+    {
+      if (TARGET_SH3 || TARGET_SH3E)
+       {
+         si_limit -= 14;
+         hi_limit -= 14;
+       }
+      else
+       {
+         si_limit -= 2;
+         hi_limit -= 2;
+       }
+    }
+
+  while (from && count_si < si_limit && count_hi < hi_limit)
     {
       int inc = get_attr_length (from);
 
@@ -1220,9 +1877,15 @@ find_barrier (from)
 
       if (broken_move (from))
        {
-         rtx src = SET_SRC (PATTERN (from));
-
-         if (hi_const (src))
+         rtx pat = PATTERN (from);
+         rtx src = SET_SRC (pat);
+         rtx dst = SET_DEST (pat);
+         enum machine_mode mode = GET_MODE (dst);
+
+         /* We must explicitly check the mode, because sometimes the
+            front end will generate code to load unsigned constants into
+            HImode targets without properly sign extending them.  */
+         if (mode == HImode || (mode == SImode && hi_const (src)))
            {
              found_hi = 1;
              /* We put the short constants before the long constants, so
@@ -1267,7 +1930,7 @@ find_barrier (from)
       /* If we exceeded the range, then we must back up over the last
         instruction we looked at.  Otherwise, we just need to undo the
         NEXT_INSN at the end of the loop.  */
-      if (count_hi > 512 || count_si > 1020)
+      if (count_hi > hi_limit || count_si > si_limit)
        from = PREV_INSN (PREV_INSN (from));
       else
        from = PREV_INSN (from);
@@ -1283,6 +1946,7 @@ find_barrier (from)
 
       from = emit_jump_insn_after (gen_jump (label), from);
       JUMP_LABEL (from) = label;
+      LABEL_NUSES (label) = 1;
       found_barrier = emit_barrier_after (from);
       emit_label_after (label, found_barrier);
     }
@@ -1290,10 +1954,136 @@ find_barrier (from)
   return found_barrier;
 }
 
+/* If the instruction INSN is implemented by a special function, and we can
+   positively find the register that is used to call the sfunc, and this
+   register is not used anywhere else in this instruction - except as the
+   destination of a set, return this register; else, return 0.  */
+static rtx
+sfunc_uses_reg (insn)
+     rtx insn;
+{
+  int i;
+  rtx pattern, part, reg_part, reg;
+
+  if (GET_CODE (insn) != INSN)
+    return 0;
+  pattern = PATTERN (insn);
+  if (GET_CODE (pattern) != PARALLEL || get_attr_type (insn) != TYPE_SFUNC)
+    return 0;
+
+  for (reg_part = 0, i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+    {
+      part = XVECEXP (pattern, 0, i);
+      if (GET_CODE (part) == USE)
+       reg_part = part;
+    }
+  if (! reg_part)
+    return 0;
+  reg = XEXP (reg_part, 0);
+  for (i = XVECLEN (pattern, 0) - 1; i >= 0; i--)
+    {
+      part = XVECEXP (pattern, 0, i);
+      if (part == reg_part)
+       continue;
+      if (reg_mentioned_p (reg, ((GET_CODE (part) == SET
+                                 && GET_CODE (SET_DEST (part)) == REG)
+                                ? SET_SRC (part) : part)))
+       return 0;
+    }
+  return reg;
+}
+
+/* See if the only way in which INSN uses REG is by calling it, or by
+   setting it while calling it.  Set *SET to a SET rtx if the register
+   is set by INSN.  */
+
+static int
+noncall_uses_reg (reg, insn, set)
+     rtx reg;
+     rtx insn;
+     rtx *set;
+{
+  rtx pattern, reg2;
+
+  *set = NULL_RTX;
+
+  reg2 = sfunc_uses_reg (insn);
+  if (reg2 && REGNO (reg2) == REGNO (reg))
+    {
+      pattern = single_set (insn);
+      if (pattern
+         && GET_CODE (SET_DEST (pattern)) == REG
+         && REGNO (reg) == REGNO (SET_DEST (pattern)))
+       *set = pattern;
+      return 0;
+    }
+  if (GET_CODE (insn) != CALL_INSN)
+    {
+      /* We don't use rtx_equal_p because we don't care if the mode is
+        different.  */
+      pattern = single_set (insn);
+      if (pattern
+         && GET_CODE (SET_DEST (pattern)) == REG
+         && REGNO (reg) == REGNO (SET_DEST (pattern)))
+       {
+         rtx par, part;
+         int i;
+
+         *set = pattern;
+         par = PATTERN (insn);
+         if (GET_CODE (par) == PARALLEL)
+           for (i = XVECLEN (par, 0) - 1; i >= 0; i--)
+             {
+               part = XVECEXP (par, 0, i);
+               if (GET_CODE (part) != SET && reg_mentioned_p (reg, part))
+                 return 1;
+             }
+         return reg_mentioned_p (reg, SET_SRC (pattern));
+       }
+
+      return 1;
+    }
+
+  pattern = PATTERN (insn);
+
+  if (GET_CODE (pattern) == PARALLEL)
+    {
+      int i;
+
+      for (i = XVECLEN (pattern, 0) - 1; i >= 1; i--)
+       if (reg_mentioned_p (reg, XVECEXP (pattern, 0, i)))
+         return 1;
+      pattern = XVECEXP (pattern, 0, 0);
+    }
+
+  if (GET_CODE (pattern) == SET)
+    {
+      if (reg_mentioned_p (reg, SET_DEST (pattern)))
+       {
+         /* We don't use rtx_equal_p, because we don't care if the
+             mode is different.  */
+         if (GET_CODE (SET_DEST (pattern)) != REG
+             || REGNO (reg) != REGNO (SET_DEST (pattern)))
+           return 1;
+
+         *set = pattern;
+       }
+
+      pattern = SET_SRC (pattern);
+    }
+
+  if (GET_CODE (pattern) != CALL
+      || GET_CODE (XEXP (pattern, 0)) != MEM
+      || ! rtx_equal_p (reg, XEXP (XEXP (pattern, 0), 0)))
+    return 1;
+
+  return 0;
+}
+
 /* Exported to toplev.c.
 
-   Scan the function looking for move instructions which have to be changed to
-   pc-relative loads and insert the literal tables.  */
+   Do a final pass over the function, just before delayed branch
+   scheduling.  */
 
 void
 machine_dependent_reorg (first)
@@ -1301,6 +2091,236 @@ machine_dependent_reorg (first)
 {
   rtx insn;
 
+  /* If relaxing, generate pseudo-ops to associate function calls with
+     the symbols they call.  It does no harm to not generate these
+     pseudo-ops.  However, when we can generate them, it enables to
+     linker to potentially relax the jsr to a bsr, and eliminate the
+     register load and, possibly, the constant pool entry.  */
+
+  if (TARGET_RELAX)
+    {
+      /* Remove all REG_LABEL notes.  We want to use them for our own
+        purposes.  This works because none of the remaining passes
+        need to look at them.
+
+        ??? But it may break in the future.  We should use a machine
+        dependent REG_NOTE, or some other approach entirely.  */
+      for (insn = first; insn; insn = NEXT_INSN (insn))
+       {
+         if (GET_RTX_CLASS (GET_CODE (insn)) == 'i')
+           {
+             rtx note;
+
+             while ((note = find_reg_note (insn, REG_LABEL, NULL_RTX)) != 0)
+               remove_note (insn, note);
+           }
+       }
+
+      for (insn = first; insn; insn = NEXT_INSN (insn))
+       {
+         rtx pattern, reg, link, set, scan, dies, label;
+         int rescan = 0, foundinsn = 0;
+
+         if (GET_CODE (insn) == CALL_INSN)
+           {
+             pattern = PATTERN (insn);
+
+             if (GET_CODE (pattern) == PARALLEL)
+               pattern = XVECEXP (pattern, 0, 0);
+             if (GET_CODE (pattern) == SET)
+               pattern = SET_SRC (pattern);
+
+             if (GET_CODE (pattern) != CALL
+                 || GET_CODE (XEXP (pattern, 0)) != MEM)
+               continue;
+
+             reg = XEXP (XEXP (pattern, 0), 0);
+           }
+         else
+           {
+             reg = sfunc_uses_reg (insn);
+             if (! reg)
+               continue;
+           }
+
+         if (GET_CODE (reg) != REG)
+           continue;
+
+         /* This is a function call via REG.  If the only uses of REG
+            between the time that it is set and the time that it dies
+            are in function calls, then we can associate all the
+            function calls with the setting of REG.  */
+
+         for (link = LOG_LINKS (insn); link; link = XEXP (link, 1))
+           {
+             if (REG_NOTE_KIND (link) != 0)
+               continue;
+             set = single_set (XEXP (link, 0));
+             if (set && rtx_equal_p (reg, SET_DEST (set)))
+               {
+                 link = XEXP (link, 0);
+                 break;
+               }
+           }
+
+         if (! link)
+           {
+             /* ??? Sometimes global register allocation will have
+                 deleted the insn pointed to by LOG_LINKS.  Try
+                 scanning backward to find where the register is set.  */
+             for (scan = PREV_INSN (insn);
+                  scan && GET_CODE (scan) != CODE_LABEL;
+                  scan = PREV_INSN (scan))
+               {
+                 if (GET_RTX_CLASS (GET_CODE (scan)) != 'i')
+                   continue;
+
+                 if (! reg_mentioned_p (reg, scan))
+                   continue;
+
+                 if (noncall_uses_reg (reg, scan, &set))
+                   break;
+
+                 if (set)
+                   {
+                     link = scan;
+                     break;
+                   }
+               }
+           }
+
+         if (! link)
+           continue;
+
+         /* The register is set at LINK.  */
+
+         /* We can only optimize the function call if the register is
+             being set to a symbol.  In theory, we could sometimes
+             optimize calls to a constant location, but the assembler
+             and linker do not support that at present.  */
+         if (GET_CODE (SET_SRC (set)) != SYMBOL_REF
+             && GET_CODE (SET_SRC (set)) != LABEL_REF)
+           continue;
+
+         /* Scan forward from LINK to the place where REG dies, and
+             make sure that the only insns which use REG are
+             themselves function calls.  */
+
+         /* ??? This doesn't work for call targets that were allocated
+            by reload, since there may not be a REG_DEAD note for the
+            register.  */
+
+         dies = NULL_RTX;
+         for (scan = NEXT_INSN (link); scan; scan = NEXT_INSN (scan))
+           {
+             rtx scanset;
+
+             /* Don't try to trace forward past a CODE_LABEL if we haven't
+                seen INSN yet.  Ordinarily, we will only find the setting insn
+                in LOG_LINKS if it is in the same basic block.  However,
+                cross-jumping can insert code labels in between the load and
+                the call, and can result in situations where a single call
+                insn may have two targets depending on where we came from.  */
+
+             if (GET_CODE (scan) == CODE_LABEL && ! foundinsn)
+               break;
+
+             if (GET_RTX_CLASS (GET_CODE (scan)) != 'i')
+               continue;
+
+             /* Don't try to trace forward past a JUMP.  To optimize
+                 safely, we would have to check that all the
+                 instructions at the jump destination did not use REG.  */
+
+             if (GET_CODE (scan) == JUMP_INSN)
+               break;
+
+             if (! reg_mentioned_p (reg, scan))
+               continue;
+
+             if (noncall_uses_reg (reg, scan, &scanset))
+               break;
+
+             if (scan == insn)
+               foundinsn = 1;
+
+             if (scan != insn
+                 && (GET_CODE (scan) == CALL_INSN || sfunc_uses_reg (scan)))
+               {
+                 /* There is a function call to this register other
+                     than the one we are checking.  If we optimize
+                     this call, we need to rescan again below.  */
+                 rescan = 1;
+               }
+
+             /* ??? We shouldn't have to worry about SCANSET here.
+                We should just be able to check for a REG_DEAD note
+                on a function call.  However, the REG_DEAD notes are
+                apparently not dependable around libcalls; c-torture
+                execute/920501-2 is a test case.  If SCANSET is set,
+                then this insn sets the register, so it must have
+                died earlier.  Unfortunately, this will only handle
+                the cases in which the register is, in fact, set in a
+                later insn.  */
+
+             /* ??? We shouldn't have to use FOUNDINSN here.
+                However, the LOG_LINKS fields are apparently not
+                entirely reliable around libcalls;
+                newlib/libm/math/e_pow.c is a test case.  Sometimes
+                an insn will appear in LOG_LINKS even though it is
+                not the most recent insn which sets the register. */
+
+             if (foundinsn
+                 && (scanset
+                     || find_reg_note (scan, REG_DEAD, reg)))
+               {
+                 dies = scan;
+                 break;
+               }
+           }
+
+         if (! dies)
+           {
+             /* Either there was a branch, or some insn used REG
+                 other than as a function call address.  */
+             continue;
+           }
+
+         /* Create a code label, and put it in a REG_LABEL note on
+             the insn which sets the register, and on each call insn
+             which uses the register.  In final_prescan_insn we look
+             for the REG_LABEL notes, and output the appropriate label
+             or pseudo-op.  */
+
+         label = gen_label_rtx ();
+         REG_NOTES (link) = gen_rtx (EXPR_LIST, REG_LABEL, label,
+                                     REG_NOTES (link));
+         REG_NOTES (insn) = gen_rtx (EXPR_LIST, REG_LABEL, label,
+                                     REG_NOTES (insn));
+         if (rescan)
+           {
+             scan = link;
+             do
+               {
+                 rtx reg2;
+
+                 scan = NEXT_INSN (scan);
+                 if (scan != insn
+                     && ((GET_CODE (scan) == CALL_INSN
+                          && reg_mentioned_p (reg, scan))
+                         || ((reg2 = sfunc_uses_reg (scan))
+                             && REGNO (reg2) == REGNO (reg))))
+                   REG_NOTES (scan) = gen_rtx (EXPR_LIST, REG_LABEL,
+                                               label, REG_NOTES (scan));
+               }
+             while (scan != dies);
+           }
+       }
+    }
+
+  /* Scan the function looking for move instructions which have to be
+     changed to pc-relative loads and insert the literal tables.  */
+
   for (insn = first; insn; insn = NEXT_INSN (insn))
     {
       if (broken_move (insn))
@@ -1315,13 +2335,18 @@ machine_dependent_reorg (first)
            {
              if (broken_move (scan))
                {
-                 rtx pat = PATTERN (scan);
-                 rtx src = SET_SRC (pat);
-                 rtx dst = SET_DEST (pat);
-                 enum machine_mode mode = GET_MODE (dst);
+                 rtx *patp = &PATTERN (scan), pat = *patp;
+                 rtx src, dst;
                  rtx lab;
                  rtx newinsn;
                  rtx newsrc;
+                 enum machine_mode mode;
+
+                 if (GET_CODE (pat) == PARALLEL)
+                   patp = &XVECEXP (pat, 0, 0), pat = *patp;
+                 src = SET_SRC (pat);
+                 dst = SET_DEST (pat);
+                 mode = GET_MODE (dst);
 
                  if (mode == SImode && hi_const (src))
                    {
@@ -1340,11 +2365,8 @@ machine_dependent_reorg (first)
                  newsrc = gen_rtx (MEM, mode,
                                    gen_rtx (LABEL_REF, VOIDmode, lab));
                  RTX_UNCHANGING_P (newsrc) = 1;
-                 newinsn = emit_insn_after (gen_rtx (SET, VOIDmode,
-                                                     dst, newsrc), scan);
-
-                 delete_insn (scan);
-                 scan = newinsn;
+                 *patp = gen_rtx (SET, VOIDmode, dst, newsrc);
+                 INSN_CODE (scan) = -1;
                }
            }
          dump_table (barrier);
@@ -1353,7 +2375,10 @@ machine_dependent_reorg (first)
 }
 
 /* Dump out instruction addresses, which is useful for debugging the
-   constant pool table stuff.  */
+   constant pool table stuff.
+
+   If relaxing, output the label and pseudo-ops used to link together
+   calls and the instruction which set the registers.  */
 
 /* ??? This is unnecessary, and probably should be deleted.  This makes
    the insn_addresses declaration above unnecessary.  */
@@ -1371,6 +2396,32 @@ final_prescan_insn (insn, opvec, noperands)
 {
   if (TARGET_DUMPISIZE)
     fprintf (asm_out_file, "\n! at %04x\n", insn_addresses[INSN_UID (insn)]);
+
+  if (TARGET_RELAX)
+    {
+      rtx note;
+
+      note = find_reg_note (insn, REG_LABEL, NULL_RTX);
+      if (note)
+       {
+         rtx pattern;
+
+         pattern = PATTERN (insn);
+         if (GET_CODE (pattern) == PARALLEL)
+           pattern = XVECEXP (pattern, 0, 0);
+         if (GET_CODE (pattern) == CALL
+             || (GET_CODE (pattern) == SET
+                 && (GET_CODE (SET_SRC (pattern)) == CALL
+                     || get_attr_type (insn) == TYPE_SFUNC)))
+           fprintf (asm_out_file, "\t.uses L%d\n",
+                    CODE_LABEL_NUMBER (XEXP (note, 0)));
+         else if (GET_CODE (pattern) == SET)
+           ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+                                      CODE_LABEL_NUMBER (XEXP (note, 0)));
+         else
+           abort ();
+       }
+    }
 }
 
 /* Dump out any constants accumulated in the final pass.  These will
@@ -1422,12 +2473,15 @@ output_jump_label_table ()
 
 static int extra_push;
 
-/* Adjust the stack and return the number of bytes taken to do it.  */
+/* Adjust the stack by SIZE bytes.  REG holds the rtl of the register
+  to be adjusted, and TEMP, if nonnegative, holds the register number
+  of a general register that we may clobber.  */
 
 static void
-output_stack_adjust (size, reg)
+output_stack_adjust (size, reg, temp)
      int size;
      rtx reg;
+     int temp;
 {
   if (size)
     {
@@ -1436,9 +2490,29 @@ output_stack_adjust (size, reg)
 
       if (! CONST_OK_FOR_I (size))
        {
-         rtx reg = gen_rtx (REG, SImode, 3);
-         emit_insn (gen_movsi (reg, val));
-         val = reg;
+         /* Try to do it with two partial adjustments; however, must make
+            sure that the stack is properly aligned at all times, in case
+            an interrupt occurs between the two partial adjustments. */
+         if (CONST_OK_FOR_I (size / 2 & -4)
+             && CONST_OK_FOR_I (size - (size / 2 & -4)))
+           {
+             val = GEN_INT (size / 2 & -4);
+             emit_insn (gen_addsi3 (reg, reg, val));
+             val = GEN_INT (size - (size / 2 & -4));
+           }
+         else
+           {
+             rtx reg;
+
+             /* 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 ();
+             reg = gen_rtx (REG, SImode, temp);
+             emit_insn (gen_movsi (reg, val));
+             val = reg;
+           }
        }
 
       insn = gen_addsi3 (reg, reg, val);
@@ -1453,7 +2527,12 @@ push (rn)
      int rn;
 {
   rtx x;
-  x = emit_insn (gen_push (gen_rtx (REG, SImode, rn)));
+  if ((rn >= FIRST_FP_REG && rn <= LAST_FP_REG)
+      || rn == FPUL_REG)
+    x = emit_insn (gen_push_e (gen_rtx (REG, SFmode, rn)));
+  else
+    x = emit_insn (gen_push (gen_rtx (REG, SImode, rn)));
+
   REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC,
                           gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0);
 }
@@ -1465,7 +2544,12 @@ pop (rn)
      int rn;
 {
   rtx x;
-  x = emit_insn (gen_pop (gen_rtx (REG, SImode, rn)));
+  if ((rn >= FIRST_FP_REG && rn <= LAST_FP_REG)
+      || rn == FPUL_REG)
+    x = emit_insn (gen_pop_e (gen_rtx (REG, SFmode, rn)));
+  else
+    x = emit_insn (gen_pop (gen_rtx (REG, SImode, rn)));
+    
   REG_NOTES (x) = gen_rtx (EXPR_LIST, REG_INC,
                           gen_rtx(REG, SImode, STACK_POINTER_REGNUM), 0);
 }
@@ -1474,14 +2558,17 @@ pop (rn)
    the number of bytes the insns take.  */
 
 static void
-push_regs (mask)
-     int mask;
+push_regs (mask, mask2)
+     int mask, mask2;
 {
   int i;
 
-  for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
+  for (i = 0; i < 32; i++)
     if (mask & (1 << i))
       push (i);
+  for (i = 32; i < FIRST_PSEUDO_REGISTER; i++)
+    if (mask2 & (1 << (i - 32)))
+      push (i);
 }
 
 /* Work out the registers which need to be saved, both as a mask and a
@@ -1492,24 +2579,32 @@ push_regs (mask)
    make sure that all the regs it clobbers are safe too.  */
 
 static int
-calc_live_regs (count_ptr)
+calc_live_regs (count_ptr, live_regs_mask2)
      int *count_ptr;
+     int *live_regs_mask2;
 {
   int reg;
   int live_regs_mask = 0;
   int count = 0;
 
+  *live_regs_mask2 = 0;
   for (reg = 0; reg < FIRST_PSEUDO_REGISTER; reg++)
     {
       if (pragma_interrupt && ! pragma_trapa)
        {
          /* Need to save all the regs ever live.  */
          if ((regs_ever_live[reg]
-              || (call_used_regs[reg] && regs_ever_live[PR_REG]))
+              || (call_used_regs[reg]
+                  && (! fixed_regs[reg] || reg == MACH_REG || reg == MACL_REG)
+                  && regs_ever_live[PR_REG]))
              && reg != STACK_POINTER_REGNUM && reg != ARG_POINTER_REGNUM
+             && reg != RETURN_ADDRESS_POINTER_REGNUM
              && reg != T_REG && reg != GBR_REG)
            {
-             live_regs_mask |= 1 << reg;
+             if (reg >= 32)
+               *live_regs_mask2 |= 1 << (reg - 32);
+             else
+               live_regs_mask |= 1 << reg;
              count++;
            }
        }
@@ -1518,7 +2613,10 @@ calc_live_regs (count_ptr)
          /* Only push those regs which are used and need to be saved.  */
          if (regs_ever_live[reg] && ! call_used_regs[reg])
            {
-             live_regs_mask |= (1 << reg);
+             if (reg >= 32)
+               *live_regs_mask2 |= 1 << (reg - 32);
+             else
+               live_regs_mask |= (1 << reg);
              count++;
            }
        }
@@ -1535,33 +2633,42 @@ sh_expand_prologue ()
 {
   int live_regs_mask;
   int d, i;
-  live_regs_mask = calc_live_regs (&d);
+  int live_regs_mask2;
+  live_regs_mask = calc_live_regs (&d, &live_regs_mask2);
 
   /* 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);
+  output_stack_adjust (-current_function_pretend_args_size,
+                      stack_pointer_rtx, 3);
 
   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.  */
+     routine.  Clear it here so that the next function isn't affected. */
   if (current_function_anonymous_args)
     {
       current_function_anonymous_args = 0;
 
-      /* Push arg regs as if they'd been provided by caller in stack.  */
-      for (i = 0; i < NPARM_REGS; i++)
-       {
-         int rn = NPARM_REGS + FIRST_PARM_REG - i - 1;
-         if (i > (NPARM_REGS - current_function_args_info
-                  - current_function_varargs))
-           break;
-         push (rn);
-         extra_push += 4;
-       }
+      /* This is not used by the SH3E calling convention  */
+      if (!TARGET_SH3E)
+        {
+         /* 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;
+             if (i > (NPARM_REGS(SImode) 
+                      - current_function_args_info.arg_count[(int) SH_ARG_INT]
+                      - current_function_varargs))
+               break;
+             push (rn);
+             extra_push += 4;
+           }
+        }
     }
-  push_regs (live_regs_mask);
-  output_stack_adjust (-get_frame_size (), stack_pointer_rtx);
+
+  push_regs (live_regs_mask, live_regs_mask2);
+
+  output_stack_adjust (-get_frame_size (), stack_pointer_rtx, 3);
 
   if (frame_pointer_needed)
     emit_insn (gen_movsi (frame_pointer_rtx, stack_pointer_rtx));
@@ -1573,7 +2680,8 @@ sh_expand_epilogue ()
   int live_regs_mask;
   int d, i;
 
-  live_regs_mask = calc_live_regs (&d);
+  int live_regs_mask2;
+  live_regs_mask = calc_live_regs (&d, &live_regs_mask2);
 
   if (frame_pointer_needed)
     {
@@ -1581,23 +2689,25 @@ sh_expand_epilogue ()
         to ensure that instruction scheduling won't move the stack pointer
         adjust before instructions reading from the frame.  This can fail
         if there is an interrupt which then writes to the stack.  */
-      output_stack_adjust (get_frame_size (), frame_pointer_rtx);
+      output_stack_adjust (get_frame_size (), frame_pointer_rtx, 7);
       emit_insn (gen_movsi (stack_pointer_rtx, frame_pointer_rtx));
     }
   else
-    output_stack_adjust (get_frame_size (), stack_pointer_rtx);
+    output_stack_adjust (get_frame_size (), stack_pointer_rtx, 7);
 
   /* Pop all the registers.  */
 
   for (i = 0; i < FIRST_PSEUDO_REGISTER; i++)
     {
       int j = (FIRST_PSEUDO_REGISTER - 1) - i;
-      if (live_regs_mask & (1 << j))
+      if (j < 32 && (live_regs_mask & (1 << j)))
+       pop (j);
+      else if (j >= 32 && (live_regs_mask2 & (1 << (j - 32))))
        pop (j);
     }
 
   output_stack_adjust (extra_push + current_function_pretend_args_size,
-                      stack_pointer_rtx);
+                      stack_pointer_rtx, 7);
 }
 
 /* Clear variables at function end.  */
@@ -1607,7 +2717,63 @@ function_epilogue (stream, size)
      FILE *stream;
      int size;
 {
-  pragma_interrupt = pragma_trapa = 0;
+  pragma_interrupt = pragma_trapa = pragma_nosave_low_regs = 0;
+}
+
+rtx
+sh_builtin_saveregs (arglist)
+     tree arglist;
+{
+  tree fntype = TREE_TYPE (current_function_decl);
+  /* First unnamed integer register.  */
+  int first_intreg = current_function_args_info.arg_count[(int) SH_ARG_INT];
+  /* Number of integer registers we need to save.  */
+  int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg);
+  /* First unnamed SFmode float reg */
+  int first_floatreg = current_function_args_info.arg_count[(int) SH_ARG_FLOAT];
+  /* Number of SFmode float regs to save.  */
+  int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg);
+  int ptrsize = GET_MODE_SIZE (Pmode);
+  rtx valist, regbuf, fpregs;
+  int bufsize, regno;
+
+  /* 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);
+  MEM_IN_STRUCT_P (regbuf) = 1;
+
+  /* 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,
+                        gen_rtx (MEM, BLKmode, 
+                               plus_constant (XEXP (regbuf, 0),
+                                       n_floatregs * UNITS_PER_WORD)), 
+                        n_intregs, n_intregs * UNITS_PER_WORD);
+
+  /* Save float args.
+     This is optimized to only save the regs that are necessary.  Explicitly
+     named args need not be saved.
+     We explicitly build a pointer to the buffer because it halves the insn
+     count when not optimizing (otherwise the pointer is built for each reg
+     saved).  */
+
+  fpregs = gen_reg_rtx (Pmode);
+  emit_move_insn (fpregs, XEXP (regbuf, 0));
+  for (regno = first_floatreg; regno < NPARM_REGS (SFmode); regno ++)
+    emit_move_insn (gen_rtx (MEM, SFmode,
+                            plus_constant (fpregs,
+                                           GET_MODE_SIZE (SFmode)
+                                           * (regno - first_floatreg))),
+                   gen_rtx (REG, SFmode,
+                            BASE_ARG_REG (SFmode) + regno));
+
+  /* Return the address of the regbuf.  */
+  return XEXP (regbuf, 0);
 }
 
 /* Define the offset between two registers, one to be eliminated, and
@@ -1622,7 +2788,9 @@ initial_elimination_offset (from, to)
   int total_saved_regs_space;
   int total_auto_space = get_frame_size ();
 
-  calc_live_regs (&regs_saved);
+  int live_regs_mask, live_regs_mask2;
+  live_regs_mask = calc_live_regs (&regs_saved, &live_regs_mask2);
+
   total_saved_regs_space = (regs_saved) * 4;
 
   if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
@@ -1635,6 +2803,19 @@ initial_elimination_offset (from, to)
   if (from == FRAME_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
     return 0;
 
+  if (from == RETURN_ADDRESS_POINTER_REGNUM
+      && (to == FRAME_POINTER_REGNUM || to == STACK_POINTER_REGNUM))
+    {
+      int i, n = 0;
+      for (i = PR_REG+1; i < 32; i++)
+       if (live_regs_mask & (1 << i))
+         n += 4;
+      for (i = 32; i < FIRST_PSEUDO_REGISTER; i++)
+       if (live_regs_mask2 & (1 << (i - 32)))
+         n += 4;
+      return n + total_auto_space;
+    }
+
   abort ();
 }
 \f
@@ -1642,36 +2823,25 @@ initial_elimination_offset (from, to)
    compiler.  */
 
 int
-handle_pragma (file)
+handle_pragma (file, t)
      FILE *file;
+     tree t;
 {
-  int c;
-  char pbuf[200];
-  int psize = 0;
+  int retval = 0;
+  register char *pname;
 
-  c = getc (file);
-  while (c == ' ' || c == '\t')
-    c = getc (file);
+  if (TREE_CODE (t) != IDENTIFIER_NODE)
+    return 0;
 
-  if (c == '\n' || c == EOF)
-    return c;
+  pname = IDENTIFIER_POINTER (t);
+  if (strcmp (pname, "interrupt") == 0)
+    pragma_interrupt = retval = 1;
+  else if (strcmp (pname, "trapa") == 0)
+    pragma_interrupt = pragma_trapa = retval = 1;
+  else if (strcmp (pname, "nosave_low_regs") == 0)
+    pragma_nosave_low_regs = retval = 1;
 
-  while (psize < sizeof (pbuf) - 1 && c != '\n')
-    {
-      pbuf[psize++] = c;
-      if (psize == 9 && strncmp (pbuf, "interrupt", 9) == 0)
-       {
-         pragma_interrupt = 1;
-         return ' ';
-       }
-      if (psize == 5 && strncmp (pbuf, "trapa", 5) == 0)
-       {
-         pragma_interrupt = pragma_trapa = 1;
-         return ' ';
-       }
-      c = getc (file);
-    }
-  return c;
+  return retval;
 }
 \f
 /* Predicates used by the templates.  */
@@ -1755,12 +2925,17 @@ arith_reg_operand (op, mode)
 {
   if (register_operand (op, mode))
     {
+      int regno;
+
       if (GET_CODE (op) == REG)
-       return (REGNO (op) != T_REG
-               && REGNO (op) != PR_REG
-               && REGNO (op) != MACH_REG
-               && REGNO (op) != MACL_REG);
-      return 1;
+       regno = REGNO (op);
+      else if (GET_CODE (op) == SUBREG && GET_CODE (SUBREG_REG (op)) == REG)
+       regno = REGNO (SUBREG_REG (op));
+      else
+       return 1;
+
+      return (regno != T_REG && regno != PR_REG && regno != FPUL_REG
+             && regno != MACH_REG && regno != MACL_REG);
     }
   return 0;
 }
@@ -1812,65 +2987,40 @@ logical_operand (op, mode)
 
   return 0;
 }
-\f
-/* Determine where to put an argument to a function.
-   Value is zero to push the argument on the stack,
-   or a hard register in which to store the argument.
-
-   MODE is the argument's machine mode.
-   TYPE is the data type of the argument (as a tree).
-    This is null for libcalls where that information may
-    not be available.
-   CUM is a variable of type CUMULATIVE_ARGS which gives info about
-    the preceding args and about the function being called.
-   NAMED is nonzero if this argument is a named parameter
-    (otherwise it is an extra parameter matching an ellipsis).  */
 
-rtx
-sh_function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS cum;
-     enum machine_mode mode;
-     tree type;
-     int named;
+/* Nonzero if OP is a floating point value with value 0.0.  */
+
+int
+fp_zero_operand (op)
+     rtx op;
 {
-  if (named)
-    {
-      int rr = (ROUND_REG (cum, mode));
+  REAL_VALUE_TYPE r;
 
-      if (rr < NPARM_REGS)
-       return ((type == 0 || ! TREE_ADDRESSABLE (type))
-               ? gen_rtx (REG, mode, FIRST_PARM_REG + rr) : 0);
-    }
-  return 0;
+  if (GET_MODE (op) != SFmode)
+    return 0;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+  return REAL_VALUES_EQUAL (r, dconst0) && ! REAL_VALUE_MINUS_ZERO (r);
 }
 
-/* For an arg passed partly in registers and partly in memory,
-   this is the number of registers used.
-   For args passed entirely in registers or entirely in memory, zero.
-   Any arg that starts in the first 4 regs but won't entirely fit in them
-   needs partial registers on the SH.  */
+/* Nonzero if OP is a floating point value with value 1.0.  */
 
 int
-sh_function_arg_partial_nregs (cum, mode, type, named)
-     CUMULATIVE_ARGS cum;
-     enum machine_mode mode;
-     tree type;
-     int named;
+fp_one_operand (op)
+     rtx op;
 {
-  if (cum < NPARM_REGS)
-    {
-      if ((type == 0 || ! TREE_ADDRESSABLE (type))
-         && (cum + (mode == BLKmode
-                    ? ROUND_ADVANCE (int_size_in_bytes (type))
-                    : ROUND_ADVANCE (GET_MODE_SIZE (mode))) - NPARM_REGS > 0))
-       return NPARM_REGS - cum;
-    }
-  return 0;
+  REAL_VALUE_TYPE r;
+
+  if (GET_MODE (op) != SFmode)
+    return 0;
+
+  REAL_VALUE_FROM_CONST_DOUBLE (r, op);
+  return REAL_VALUES_EQUAL (r, dconst1);
 }
 \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 or calls or jumps.  */
+   not live past labels.  It may live past calls or jumps though.  */
 int
 reg_unused_after (reg, insn)
      rtx reg;
@@ -1891,8 +3041,18 @@ reg_unused_after (reg, insn)
     {
       code = GET_CODE (insn);
 
+#if 0
+      /* If this is a label that existed before reload, then the register
+        if dead here.  However, if this is a label added by reorg, then
+        the register may still be live here.  We can't tell the difference,
+        so we just ignore labels completely.  */
       if (code == CODE_LABEL)
        return 1;
+      /* else */
+#endif
+
+      if (code == JUMP_INSN)
+       return 0;
 
       /* If this is a sequence, we must handle them all at once.
         We could have for instance a call that sets the target register,
@@ -1910,6 +3070,12 @@ reg_unused_after (reg, insn)
 
              if (GET_CODE (this_insn) == CALL_INSN)
                code = CALL_INSN;
+             else if (GET_CODE (this_insn) == JUMP_INSN)
+               {
+                 if (INSN_ANNULLED_BRANCH_P (this_insn))
+                   return 0;
+                 code = JUMP_INSN;
+               }
 
              if (set && reg_overlap_mentioned_p (reg, SET_SRC (set)))
                return 0;
@@ -1926,6 +3092,8 @@ reg_unused_after (reg, insn)
            }
          if (retval == 1)
            return 1;
+         else if (code == JUMP_INSN)
+           return 0;
        }
       else if (GET_RTX_CLASS (code) == 'i')
        {