OSDN Git Service

* config/h8300/h8300.md (pushhi1_h8300): Correct the mode
[pf3gnuchains/gcc-fork.git] / gcc / config / h8300 / h8300.c
index 7ad2489..517176d 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines for insn-output.c for Hitachi H8/300.
-   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001
-   Free Software Foundation, Inc. 
+   Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000,
+   2001, 2002 Free Software Foundation, Inc.
    Contributed by Steve Chamberlain (sac@cygnus.com),
    Jim Wilson (wilson@cygnus.com), and Doug Evans (dje@cygnus.com).
 
@@ -53,28 +53,33 @@ static unsigned int compute_saved_regs PARAMS ((void));
 static void push PARAMS ((FILE *, int));
 static void pop PARAMS ((FILE *, int));
 static const char *cond_string PARAMS ((enum rtx_code));
-static int h8300_valid_decl_attribute PARAMS ((tree, tree, tree, tree));
+const struct attribute_spec h8300_attribute_table[];
+static tree h8300_handle_fndecl_attribute PARAMS ((tree *, tree, tree, int, bool *));
+static tree h8300_handle_eightbit_data_attribute PARAMS ((tree *, tree, tree, int, bool *));
+static tree h8300_handle_tiny_data_attribute PARAMS ((tree *, tree, tree, int, bool *));
 static void h8300_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));
 static void h8300_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
+#ifndef OBJECT_FORMAT_ELF
 static void h8300_asm_named_section PARAMS ((const char *, unsigned int));
+#endif
 
 /* CPU_TYPE, says what cpu we're compiling for.  */
 int cpu_type;
 
 /* True if the current function is an interrupt handler
    (either via #pragma or an attribute specification).  */
-int interrupt_handler;
+static int interrupt_handler;
 
 /* True if the current function is an OS Task
    (via an attribute specification).  */
-int os_task;
+static int os_task;
 
 /* True if the current function is a monitor
    (via an attribute specification).  */
-int monitor;
+static int monitor;
 
 /* True if a #pragma saveall has been seen for the current function.  */
-int pragma_saveall;
+static int pragma_saveall;
 
 static const char *const names_big[] =
 { "r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7" };
@@ -91,15 +96,14 @@ const char * const *h8_reg_names;
 
 /* Various operations needed by the following, indexed by CPU_TYPE.  */
 
-static const char *const h8_push_ops[2] = { "push", "push.l" };
-static const char *const h8_pop_ops[2] = { "pop", "pop.l" };
-static const char *const h8_mov_ops[2] = { "mov.w", "mov.l" };
-
 const char *h8_push_op, *h8_pop_op, *h8_mov_op;
 \f
 /* Initialize the GCC target structure.  */
-#undef TARGET_VALID_DECL_ATTRIBUTE
-#define TARGET_VALID_DECL_ATTRIBUTE h8300_valid_decl_attribute
+#undef TARGET_ATTRIBUTE_TABLE
+#define TARGET_ATTRIBUTE_TABLE h8300_attribute_table
+
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
 
 #undef TARGET_ASM_FUNCTION_PROLOGUE
 #define TARGET_ASM_FUNCTION_PROLOGUE h8300_output_function_prologue
@@ -113,6 +117,10 @@ struct gcc_target targetm = TARGET_INITIALIZER;
 void
 h8300_init_once ()
 {
+  static const char *const h8_push_ops[2] = { "push" , "push.l" };
+  static const char *const h8_pop_ops[2]  = { "pop"  , "pop.l"  };
+  static const char *const h8_mov_ops[2]  = { "mov.w", "mov.l"  };
+
   if (TARGET_H8300)
     {
       cpu_type = (int) CPU_H8300;
@@ -130,7 +138,7 @@ h8300_init_once ()
 
   if (!TARGET_H8300S && TARGET_MAC)
     {
-      error ("-ms2600 is used without -ms.");
+      error ("-ms2600 is used without -ms");
       target_flags |= 1;
     }
 }
@@ -140,30 +148,31 @@ byte_reg (x, b)
      rtx x;
      int b;
 {
-  static const char *const names_small[] =
-  {"r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
-   "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7l", "r7h"};
+  static const char *const names_small[] = {
+    "r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h",
+    "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7l", "r7h"
+  };
 
   return names_small[REGNO (x) * 2 + b];
 }
 
 /* REGNO must be saved/restored across calls if this macro is true.  */
 
-#define WORD_REG_USED(regno)                                   \
-  (regno < 7                                                   \
-   /* No need to save registers if this function will not return.  */\
-   && ! TREE_THIS_VOLATILE (current_function_decl)             \
-   && (pragma_saveall                                          \
-       /* Save any call saved register that was used.  */      \
-       || (regs_ever_live[regno] && !call_used_regs[regno])    \
-       /* Save the frame pointer if it was used.  */           \
-       || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno])\
-       /* Save any register used in an interrupt handler.  */  \
-       || (interrupt_handler && regs_ever_live[regno])         \
-       /* Save call clobbered registers in non-leaf interrupt  \
-         handlers.  */                                         \
-       || (interrupt_handler                                   \
-          && call_used_regs[regno]                             \
+#define WORD_REG_USED(regno)                                           \
+  (regno < 7                                                           \
+   /* No need to save registers if this function will not return.  */  \
+   && ! TREE_THIS_VOLATILE (current_function_decl)                     \
+   && (pragma_saveall                                                  \
+       /* Save any call saved register that was used.  */              \
+       || (regs_ever_live[regno] && !call_used_regs[regno])            \
+       /* Save the frame pointer if it was used.  */                   \
+       || (regno == FRAME_POINTER_REGNUM && regs_ever_live[regno])     \
+       /* Save any register used in an interrupt handler.  */          \
+       || (interrupt_handler && regs_ever_live[regno])                 \
+       /* Save call clobbered registers in non-leaf interrupt          \
+         handlers.  */                                                 \
+       || (interrupt_handler                                           \
+          && call_used_regs[regno]                                     \
           && !current_function_is_leaf)))
 
 /* Output assembly language to FILE for the operation OP with operand size
@@ -184,6 +193,7 @@ dosize (file, op, size)
      subs since this shouldn't happen often.  */
   if ((TARGET_H8300 && size <= 4)
       || ((TARGET_H8300H || TARGET_H8300S) && size <= 8)
+      || (TARGET_H8300 && interrupt_handler)
       || (TARGET_H8300 && current_function_needs_context
          && ! strcmp (op, "sub")))
     {
@@ -538,7 +548,7 @@ general_operand_src (op, mode)
 }
 
 /* Return true if OP is a valid destination operand for an integer move
-   instruction, excluding those involving pre_modify.  */
+   instruction.  */
 
 int
 general_operand_dst (op, mode)
@@ -550,20 +560,6 @@ general_operand_dst (op, mode)
   return general_operand (op, mode);
 }
 
-/* Return true if OP is a valid destination operand for an integer move
-   instruction, including those involving pre_modify.  */
-
-int
-general_operand_dst_push (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  if (push_operand (op, mode))
-    return 1;
-
-  return general_operand_dst (op, mode);
-}
-
 /* Return true if OP is a const valid for a bit clear instruction.  */
 
 int
@@ -636,12 +632,11 @@ two_insn_adds_subs_operand (op, mode)
        }
       else
        {
-         /* A constant addition/subtraction takes 2 states in
-            QImode. It takes 6 states in HImode, requiring the
-            constant to be loaded to a register first, and a lot more
-            in SImode.  Thus the only case we can win is when either
-            HImode or SImode is used.  */
-         if (mode != QImode
+         /* We do not profit directly by splitting addition or
+            subtraction of 3 and 4.  However, since these are
+            implemented as a sequence of adds or subs, they do not
+            clobber (cc0) unlike a sequence of add.b and add.x.  */
+         if (mode == HImode
              && (value == 2 + 1
                  || value == 2 + 2))
            return 1;
@@ -774,16 +769,6 @@ bit_memory_operand (op, mode)
          && EXTRA_CONSTRAINT (op, 'U'));
 }
 
-/* Recognize valid operators for bit test.  */
-
-int
-eq_operator (x, mode)
-     rtx x;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (x) == EQ || GET_CODE (x) == NE);
-}
-
 /* Handle machine specific pragmas for compatibility with existing
    compilers for the H8/300.
 
@@ -808,32 +793,10 @@ h8300_pr_saveall (pfile)
   pragma_saveall = 1;
 }
 
-/* If the next arg with MODE and TYPE is to be passed in a register, return
-   the rtx to represent where it is passed.  CUM represents the state after
-   the last argument.  NAMED is not used.  */
-
-static const char *const hand_list[] =
-{
-  "__main",
-  "__cmpsi2",
-  "__divhi3",
-  "__modhi3",
-  "__udivhi3",
-  "__umodhi3",
-  "__divsi3",
-  "__modsi3",
-  "__udivsi3",
-  "__umodsi3",
-  "__mulhi3",
-  "__mulsi3",
-  "__reg_memcpy",
-  "__reg_memset",
-  "__ucmpsi2",
-  0,
-};
-
-/* Return an RTX to represent where a value with mode MODE will be returned
-   from a function.  If the result is 0, the argument is pushed.  */
+/* If the next function argument with MODE and TYPE is to be passed in
+   a register, return a reg RTX for the hard register in which to pass
+   the argument.  CUM represents the state after the last argument.
+   If the argument is to be pushed, NULL_RTX is returned.  */
 
 rtx
 function_arg (cum, mode, type, named)
@@ -842,20 +805,38 @@ function_arg (cum, mode, type, named)
      tree type;
      int named;
 {
-  rtx result = 0;
+  static const char *const hand_list[] = {
+    "__main",
+    "__cmpsi2",
+    "__divhi3",
+    "__modhi3",
+    "__udivhi3",
+    "__umodhi3",
+    "__divsi3",
+    "__modsi3",
+    "__udivsi3",
+    "__umodsi3",
+    "__mulhi3",
+    "__mulsi3",
+    "__reg_memcpy",
+    "__reg_memset",
+    "__ucmpsi2",
+    0,
+  };
+
+  rtx result = NULL_RTX;
   const char *fname;
   int regpass = 0;
 
   /* Never pass unnamed arguments in registers.  */
   if (!named)
-    return 0;
+    return NULL_RTX;
 
   /* Pass 3 regs worth of data in regs when user asked on the command line.  */
   if (TARGET_QUICKCALL)
     regpass = 3;
 
   /* If calling hand written assembler, use 4 regs of args.  */
-
   if (cum->libcall)
     {
       const char * const *p;
@@ -863,7 +844,6 @@ function_arg (cum, mode, type, named)
       fname = XSTR (cum->libcall, 0);
 
       /* See if this libcall is one of the hand coded ones.  */
-
       for (p = hand_list; *p && strcmp (*p, fname) != 0; p++)
        ;
 
@@ -880,30 +860,9 @@ function_arg (cum, mode, type, named)
       else
        size = GET_MODE_SIZE (mode);
 
-      if (size + cum->nbytes > regpass * UNITS_PER_WORD)
-       {
-         result = 0;
-       }
-      else
-       {
-         switch (cum->nbytes / UNITS_PER_WORD)
-           {
-           case 0:
-             result = gen_rtx_REG (mode, 0);
-             break;
-           case 1:
-             result = gen_rtx_REG (mode, 1);
-             break;
-           case 2:
-             result = gen_rtx_REG (mode, 2);
-             break;
-           case 3:
-             result = gen_rtx_REG (mode, 3);
-             break;
-           default:
-             result = 0;
-           }
-       }
+      if (size + cum->nbytes <= regpass * UNITS_PER_WORD
+         && cum->nbytes / UNITS_PER_WORD <= 3)
+       result = gen_rtx_REG (mode, cum->nbytes / UNITS_PER_WORD);
     }
 
   return result;
@@ -952,18 +911,13 @@ const_costs (r, c)
 \f
 /* Documentation for the machine specific operand escapes:
 
-   'A' print rn in H8/300 mode, erN in H8/300H mode
-   'C' print (operand - 2).
    'E' like s but negative.
    'F' like t but negative.
    'G' constant just the negative
-   'M' turn a 'M' constant into its negative mod 2.
-   'P' if operand is incing/decing sp, print .w, otherwise .b.
    'R' print operand as a byte:8 address if appropriate, else fall back to
        'X' handling.
    'S' print operand as a long word
    'T' print operand as a word
-   'U' if operand is incing/decing sp, print l, otherwise nothing.
    'V' find the set bit, and print its number.
    'W' find the clear bit, and print its number.
    'X' print operand as a byte
@@ -971,13 +925,10 @@ const_costs (r, c)
        If this operand isn't a register, fall back to 'R' handling.
    'Z' print int & 7.
    'b' print the bit opcode
-   'c' print the ibit opcode
-   'd' bcc if EQ, bcs if NE
    'e' first word of 32 bit value - if reg, then least reg. if mem
        then least. if const then most sig word
    'f' second word of 32 bit value - if reg, then biggest reg. if mem
        then +2. if const then least sig word
-   'g' bcs if EQ, bcc if NE
    'j' print operand as condition code.
    'k' print operand as reverse condition code.
    's' print as low byte of 16 bit value
@@ -1030,23 +981,11 @@ print_operand (file, x, code)
      rtx x;
      int code;
 {
-  /* This is used for communication between the 'P' and 'U' codes.  */
-  static const char *last_p;
-
   /* This is used for communication between codes V,W,Z and Y.  */
   static int bitint;
 
   switch (code)
     {
-    case 'A':
-      if (GET_CODE (x) == REG)
-       fprintf (file, "%s", h8_reg_names[REGNO (x)]);
-      else
-       goto def;
-      break;
-    case 'C':
-      fprintf (file, "#%d", INTVAL (x) - 2);
-      break;
     case 'E':
       switch (GET_CODE (x))
        {
@@ -1078,38 +1017,6 @@ print_operand (file, x, code)
        abort ();
       fprintf (file, "#%d", 0xff & (-INTVAL (x)));
       break;
-    case 'M':
-      /* For 3/-3 and 4/-4, the other 2 is handled separately.  */
-      switch (INTVAL (x))
-       {
-       case 2:
-       case 4:
-       case -2:
-       case -4:
-         fprintf (file, "#2");
-         break;
-       case 1:
-       case 3:
-       case -1:
-       case -3:
-         fprintf (file, "#1");
-         break;
-       default:
-         abort ();
-       }
-      break;
-    case 'P':
-      if (REGNO (XEXP (XEXP (x, 0), 0)) == STACK_POINTER_REGNUM)
-       {
-         last_p = "";
-         fprintf (file, ".w");
-       }
-      else
-       {
-         last_p = "l";
-         fprintf (file, ".b");
-       }
-      break;
     case 'S':
       if (GET_CODE (x) == REG)
        fprintf (file, "%s", names_extended[REGNO (x)]);
@@ -1122,9 +1029,6 @@ print_operand (file, x, code)
       else
        goto def;
       break;
-    case 'U':
-      fprintf (file, "%s%s", names_big[REGNO (x)], last_p);
-      break;
     case 'V':
       bitint = exact_log2 (INTVAL (x));
       if (bitint == -1)
@@ -1173,35 +1077,6 @@ print_operand (file, x, code)
          break;
        }
       break;
-    case 'c':
-      switch (GET_CODE (x))
-       {
-       case IOR:
-         fprintf (file, "bior");
-         break;
-       case XOR:
-         fprintf (file, "bixor");
-         break;
-       case AND:
-         fprintf (file, "biand");
-         break;
-       default:
-         break;
-       }
-      break;
-    case 'd':
-      switch (GET_CODE (x))
-       {
-       case EQ:
-         fprintf (file, "bcc");
-         break;
-       case NE:
-         fprintf (file, "bcs");
-         break;
-       default:
-         abort ();
-       }
-      break;
     case 'e':
       switch (GET_CODE (x))
        {
@@ -1260,19 +1135,6 @@ print_operand (file, x, code)
          abort ();
        }
       break;
-    case 'g':
-      switch (GET_CODE (x))
-       {
-       case NE:
-         fprintf (file, "bcc");
-         break;
-       case EQ:
-         fprintf (file, "bcs");
-         break;
-       default:
-         abort ();
-       }
-      break;
     case 'j':
       asm_fprintf (file, cond_string (GET_CODE (x)));
       break;
@@ -1686,7 +1548,7 @@ output_logical_op (mode, code, operands)
       /* First, see if we can finish with one insn.
 
         If code is either AND or XOR, we exclude two special cases,
-        0xffffff00 and 0xffff00ff, because insns like sub.w or neg.w
+        0xffffff00 and 0xffff00ff, because insns like sub.w or not.w
         can do a better job.  */
       if ((TARGET_H8300H || TARGET_H8300S)
          && ((det & 0x0000ffff) != 0)
@@ -1705,11 +1567,10 @@ output_logical_op (mode, code, operands)
             1) the special insn (in case of AND or XOR),
             2) the word-wise insn, and
             3) The byte-wise insn.  */
-         if ((TARGET_H8300H || TARGET_H8300S)
-             && ((det & 0x0000ffff) == 0x0000ffff)
-             && code != IOR)
+         if ((det & 0x0000ffff) == 0x0000ffff
+             && (TARGET_H8300 ? (code == AND) : (code != IOR)))
            output_asm_insn ((code == AND)
-                            ? "sub.w\t%f0,%f0" : "neg.w\t%f0",
+                            ? "sub.w\t%f0,%f0" : "not.w\t%f0",
                             operands);
          else if ((TARGET_H8300H || TARGET_H8300S)
                   && ((det & 0x000000ff) != 0)
@@ -1732,11 +1593,10 @@ output_logical_op (mode, code, operands)
                }
            }
 
-         if ((TARGET_H8300H || TARGET_H8300S)
-             && ((det & 0xffff0000) == 0xffff0000)
-             && code != IOR)
+         if ((det & 0xffff0000) == 0xffff0000
+             && (TARGET_H8300 ? (code == AND) : (code != IOR)))
            output_asm_insn ((code == AND)
-                            ? "sub.w\t%e0,%e0" : "neg.w\t%e0",
+                            ? "sub.w\t%e0,%e0" : "not.w\t%e0",
                             operands);
          else if (TARGET_H8300H || TARGET_H8300S)
            {
@@ -1769,53 +1629,42 @@ output_logical_op (mode, code, operands)
 \f
 /* Shifts.
 
-   We devote a fair bit of code to getting efficient shifts since we can only
-   shift one bit at a time on the H8/300 and H8/300H and only one or two
-   bits at a time on the H8/S.
+   We devote a fair bit of code to getting efficient shifts since we
+   can only shift one bit at a time on the H8/300 and H8/300H and only
+   one or two bits at a time on the H8/S.
 
-   The basic shift methods:
+   All shift code falls into one of the following ways of
+   implementation:
 
-     * loop shifts -- emit a loop using one (or two on H8/S) bit shifts;
-     this is the default.  SHIFT_LOOP
+   o SHIFT_INLINE: Emit straight line code for the shift; this is used
+     when a straight line shift is about the same size or smaller than
+     a loop.
 
-     * inlined shifts -- emit straight line code for the shift; this is
-     used when a straight line shift is about the same size or smaller
-     than a loop.  We allow the inline version to be slightly longer in
-     some cases as it saves a register.  SHIFT_INLINE
+   o SHIFT_ROT_AND: Rotate the value the opposite direction, then mask
+     off the bits we don't need.  This is used when only a few of the
+     bits in the original value will survive in the shifted value.
 
-     * rotate + and -- rotate the value the opposite direction, then
-     mask off the values we don't need.  This is used when only a few
-     of the bits in the original value will survive in the shifted value.
-     Again, this is used when it's about the same size or smaller than
-     a loop.  We allow this version to be slightly longer as it is usually
-     much faster than a loop.  SHIFT_ROT_AND
+   o SHIFT_SPECIAL: Often it's possible to move a byte or a word to
+     simulate a shift by 8, 16, or 24 bits.  Once moved, a few inline
+     shifts can be added if the shift count is slightly more than 8 or
+     16.  This case also includes other oddballs that are not worth
+     explaning here.
 
-     * swap (+ shifts) -- often it's possible to swap bytes/words to
-     simulate a shift by 8/16.  Once swapped a few inline shifts can be
-     added if the shift count is slightly more than 8 or 16.  This is used
-     when it's about the same size or smaller than a loop.  We allow this
-     version to be slightly longer as it is usually much faster than a loop.
-     SHIFT_SPECIAL
+   o SHIFT_LOOP: Emit a loop using one (or two on H8/S) bit shifts.
 
-     * There other oddballs.  Not worth explaining.  SHIFT_SPECIAL
+   Here are some thoughts on what the absolutely positively best code
+   is.  "Best" here means some rational trade-off between code size
+   and speed, where speed is more preferred but not at the expense of
+   generating 20 insns.
 
-   Here are some thoughts on what the absolutely positively best code is.
-   "Best" here means some rational trade-off between code size and speed,
-   where speed is more preferred but not at the expense of generating 20 insns.
-
-   A trailing '*' after the shift count indicates the "best" mode isn't
-   implemented.
+   Below, a trailing '*' after the shift count indicates the "best"
+   mode isn't implemented.  We only describe SHIFT_SPECIAL cases to
+   simplify the table.  For other cases, refer to shift_alg_[qhs]i.
    
    H8/300 QImode shifts
-   1-4    - do them inline
-   5-6    - ASHIFT | LSHIFTRT: rotate, mask off other bits
-            ASHIFTRT: loop
-   7      - ASHIFT | LSHIFTRT: rotate, mask off other bits
-            ASHIFTRT: shll, subx (propagate carry bit to all bits)
+   7      - ASHIFTRT: shll, subx (propagate carry bit to all bits)
 
    H8/300 HImode shifts
-   1-4    - do them inline
-   5-6    - loop
    7      - shift 2nd half other way into carry.
            copy 1st half into 2nd half
            rotate 2nd half other way with carry
@@ -1824,40 +1673,21 @@ output_logical_op (mode, code, operands)
            sign extend 1st half (ASHIFTRT)
    8      - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
    9-12   - do shift by 8, inline remaining shifts
-   13-14* - ASHIFT | LSHIFTRT: rotate 3/2, mask, move byte, set other byte to 0
-          - ASHIFTRT: loop
-   15     - ASHIFT | LSHIFTRT: rotate 1, mask, move byte, set other byte to 0
-          - ASHIFTRT: shll, subx, set other byte
+   15     - ASHIFTRT: shll, subx, set other byte
 
    H8/300 SImode shifts
-   1-2    - do them inline
-   3-6    - loop
    7*     - shift other way once, move bytes into place,
             move carry into place (possibly with sign extension)
    8      - move bytes into place, zero or sign extend other
-   9-14   - loop
    15*    - shift other way once, move word into place, move carry into place
    16     - move word, zero or sign extend other
-   17-23  - loop
    24*    - move bytes into place, zero or sign extend other
-   25-27  - loop
-   28-30* - ASHIFT | LSHIFTRT: rotate top byte, mask, move byte into place,
-                               zero others
-            ASHIFTRT: loop
-   31     - ASHIFT | LSHIFTRT: rotate top byte, mask, move byte into place,
-                               zero others
-            ASHIFTRT: shll top byte, subx, copy to other bytes
+   31     - ASHIFTRT: shll top byte, subx, copy to other bytes
 
    H8/300H QImode shifts (same as H8/300 QImode shifts)
-   1-4    - do them inline
-   5-6    - ASHIFT | LSHIFTRT: rotate, mask off other bits
-            ASHIFTRT: loop
-   7      - ASHIFT | LSHIFTRT: rotate, mask off other bits
-            ASHIFTRT: shll, subx (propagate carry bit to all bits)
+   7      - ASHIFTRT: shll, subx (propagate carry bit to all bits)
 
    H8/300H HImode shifts
-   1-4    - do them inline
-   5-6    - loop
    7      - shift 2nd half other way into carry.
            copy 1st half into 2nd half
            rotate entire word other way using carry
@@ -1865,22 +1695,16 @@ output_logical_op (mode, code, operands)
            sign extend remaining bits (ASHIFTRT)
    8      - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
    9-12   - do shift by 8, inline remaining shifts
-   13-14  - ASHIFT | LSHIFTRT: rotate 3/2, mask, move byte, set other byte to 0
-          - ASHIFTRT: loop
-   15     - ASHIFT | LSHIFTRT: rotate 1, mask, move byte, set other byte to 0
-          - ASHIFTRT: shll, subx, set other byte
+   15     - ASHIFTRT: shll, subx, set other byte
 
    H8/300H SImode shifts
    (These are complicated by the fact that we don't have byte level access to
    the top word.)
    A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw)
-   1-4    - do them inline
-   5-14   - loop
    15*    - shift other way once, move word into place, move carry into place
             (with sign extension for ASHIFTRT)
    16     - move word into place, zero or sign extend other
    17-20  - do 16bit shift, then inline remaining shifts
-   20-23  - loop
    24*    - ASHIFT: move byte 0(msb) to byte 1, zero byte 0,
                     move word 0 to word 1, zero word 0
             LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
@@ -1889,36 +1713,24 @@ output_logical_op (mode, code, operands)
                       sign extend byte 0, sign extend word 0
    25-27* - either loop, or
             do 24 bit shift, inline rest
-   28-30  - ASHIFT: rotate 4/3/2, mask
-            LSHIFTRT: rotate 4/3/2, mask
-            ASHIFTRT: loop
    31     - shll, subx byte 0, sign extend byte 0, sign extend word 0
 
    H8/S QImode shifts
-   1-6    - do them inline
-   7      - ASHIFT | LSHIFTRT: rotate, mask off other bits
-            ASHIFTRT: shll, subx (propagate carry bit to all bits)
+   7      - ASHIFTRT: shll, subx (propagate carry bit to all bits)
 
    H8/S HImode shifts
-   1-7   - do them inline
    8      - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
    9-12   - do shift by 8, inline remaining shifts
-   13-14  - ASHIFT | LSHIFTRT: rotate 3/2, mask, move byte, set other byte to 0
-          - ASHIFTRT: loop
-   15     - ASHIFT | LSHIFTRT: rotate 1, mask, move byte, set other byte to 0
-          - ASHIFTRT: shll, subx, set other byte
+   15     - ASHIFTRT: shll, subx, set other byte
 
    H8/S SImode shifts
    (These are complicated by the fact that we don't have byte level access to
    the top word.)
    A word is: bytes 3,2,1,0 (msb -> lsb), word 1,0 (msw -> lsw)
-   1-10   - do them inline
-   11-14  - loop
    15*    - shift other way once, move word into place, move carry into place
             (with sign extension for ASHIFTRT)
    16     - move word into place, zero or sign extend other
    17-20  - do 16bit shift, then inline remaining shifts
-   21-23  - loop
    24*    - ASHIFT: move byte 0(msb) to byte 1, zero byte 0,
                     move word 0 to word 1, zero word 0
             LSHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
@@ -1927,9 +1739,6 @@ output_logical_op (mode, code, operands)
                       sign extend byte 0, sign extend word 0
    25-27* - either loop, or
             do 24 bit shift, inline rest
-   28-30  - ASHIFT: rotate 4/3/2, mask
-            LSHIFTRT: rotate 4/3/2, mask
-            ASHIFTRT: loop
    31     - shll, subx byte 0, sign extend byte 0, sign extend word 0
 
    Panic!!!  */
@@ -1978,23 +1787,14 @@ expand_a_shift (mode, code, operands)
   return 1;
 }
 
-/* Shift algorithm determination.
-
-   There are various ways of doing a shift:
-   SHIFT_INLINE: If the amount is small enough, just generate as many one-bit
-                 shifts as we need.
-   SHIFT_ROT_AND: If the amount is large but close to either end, rotate the
-                  necessary bits into position and then set the rest to zero.
-   SHIFT_SPECIAL: Hand crafted assembler.
-   SHIFT_LOOP:    If the above methods fail, just loop.  */
+/* See above for explanation of this enum.  */
 
 enum shift_alg
 {
   SHIFT_INLINE,
   SHIFT_ROT_AND,
   SHIFT_SPECIAL,
-  SHIFT_LOOP,
-  SHIFT_MAX
+  SHIFT_LOOP
 };
 
 /* Symbols of the various shifts which can be used as indices.  */
@@ -2017,8 +1817,8 @@ enum shift_mode
 
 struct shift_insn
 {
-  const char *assembler;
-  int cc_valid;
+  const char *const assembler;
+  const int cc_valid;
 };
 
 /* Assembler instruction shift table.
@@ -2166,6 +1966,142 @@ static const char *const rotate_two[3][3] =
     }
 };
 
+/* Macros to keep the shift algorithm tables small.  */
+#define INL SHIFT_INLINE
+#define ROT SHIFT_ROT_AND
+#define LOP SHIFT_LOOP
+#define SPC SHIFT_SPECIAL
+
+/* The shift algorithms for each machine, mode, shift type, and shift
+   count are defined below.  The three tables below correspond to
+   QImode, HImode, and SImode, respectively.  Each table is organized
+   by, in the order of indecies, machine, shift type, and shift count.  */
+
+static const enum shift_alg shift_alg_qi[3][3][8] = {
+  {
+    /* TARGET_H8300  */
+    /* 0    1    2    3    4    5    6    7  */
+    { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC }  /* SHIFT_ASHIFTRT */
+  },
+  {
+    /* TARGET_H8300H  */
+    /* 0    1    2    3    4    5    6    7  */
+    { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC }  /* SHIFT_ASHIFTRT */
+  },
+  {
+    /* TARGET_H8300S  */
+    /*  0    1    2    3    4    5    6    7  */
+    { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, INL, ROT, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, INL, INL, SPC }  /* SHIFT_ASHIFTRT */
+  }
+};
+
+static const enum shift_alg shift_alg_hi[3][3][16] = {
+  {
+    /* TARGET_H8300  */
+    /*  0    1    2    3    4    5    6    7  */
+    /*  8    9   10   11   12   13   14   15  */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, LOP, LOP, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, LOP, LOP, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
+  },
+  {
+    /* TARGET_H8300H  */
+    /*  0    1    2    3    4    5    6    7  */
+    /*  8    9   10   11   12   13   14   15  */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
+  },
+  {
+    /* TARGET_H8300S  */
+    /*  0    1    2    3    4    5    6    7  */
+    /*  8    9   10   11   12   13   14   15  */
+    { INL, INL, INL, INL, INL, INL, INL, INL,
+      SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, INL, INL, INL,
+      SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, INL, INL, INL,
+      SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
+  }
+};
+
+static const enum shift_alg shift_alg_si[3][3][32] = {
+  {
+    /* TARGET_H8300  */
+    /*  0    1    2    3    4    5    6    7  */
+    /*  8    9   10   11   12   13   14   15  */
+    /* 16   17   18   19   20   21   22   23  */
+    /* 24   25   26   27   28   29   30   31  */
+    { INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      LOP, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
+  },
+  {
+    /* TARGET_H8300H  */
+    /*  0    1    2    3    4    5    6    7  */
+    /*  8    9   10   11   12   13   14   15  */
+    /* 16   17   18   19   20   21   22   23  */
+    /* 24   25   26   27   28   29   30   31  */
+    { INL, INL, INL, INL, INL, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, LOP,
+      SPC, SPC, SPC, SPC, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
+  },
+  {
+    /* TARGET_H8300S  */
+    /*  0    1    2    3    4    5    6    7  */
+    /*  8    9   10   11   12   13   14   15  */
+    /* 16   17   18   19   20   21   22   23  */
+    /* 24   25   26   27   28   29   30   31  */
+    { INL, INL, INL, INL, INL, INL, INL, INL,
+      INL, INL, INL, LOP, LOP, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
+      SPC, SPC, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, INL, INL, INL,
+      INL, INL, INL, LOP, LOP, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
+      SPC, SPC, LOP, LOP, ROT, ROT, ROT, SPC }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, INL, INL, INL,
+      INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
+      SPC, SPC, SPC, SPC, SPC, SPC, LOP, LOP,
+      SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFTRT */
+  }
+};
+
+#undef INL
+#undef ROT
+#undef LOP
+#undef SPC
+
 struct shift_info {
   /* Shift algorithm.  */
   enum shift_alg alg;
@@ -2189,9 +2125,9 @@ struct shift_info {
   int cc_valid_p;
 };
 
-static enum shift_alg get_shift_alg PARAMS ((enum shift_type,
-                                            enum shift_mode, int,
-                                            struct shift_info *));
+static void get_shift_alg PARAMS ((enum shift_type,
+                                  enum shift_mode, unsigned int,
+                                  struct shift_info *));
 
 /* Given SHIFT_TYPE, SHIFT_MODE, and shift count COUNT, determine the
    best algorithm for doing the shift.  The assembler code is stored
@@ -2206,271 +2142,162 @@ static enum shift_alg get_shift_alg PARAMS ((enum shift_type,
    WARNING: The constraints on insns shiftbyn_QI/HI/SI assume shifts of
    1,2,3,4 will be inlined (1,2 for SI).  */
 
-static enum shift_alg
+static void
 get_shift_alg (shift_type, shift_mode, count, info)
      enum shift_type shift_type;
      enum shift_mode shift_mode;
-     int count;
+     unsigned int count;
      struct shift_info *info;
 {
-  /* Assume either SHIFT_LOOP or SHIFT_INLINE.
-     It is up to the caller to know that looping clobbers cc.  */
-  info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
-  if (TARGET_H8300S)
-    info->shift2 = shift_two[shift_type][shift_mode].assembler;
-  else
-    info->shift2 = NULL;
-  info->cc_valid_p = shift_one[cpu_type][shift_type][shift_mode].cc_valid;
+  int cpu;
 
-  /* Now look for cases we want to optimize.  */
+  /* Find the target CPU.  */
+  if (TARGET_H8300)
+    cpu = 0;
+  else if (TARGET_H8300H)
+    cpu = 1;
+  else
+    cpu = 2;
 
+  /* Find the shift algorithm.  */
   switch (shift_mode)
     {
     case QIshift:
-      if (count <= 4)
-       return SHIFT_INLINE;
+      if (GET_MODE_BITSIZE (QImode) <= count)
+       info->alg = SHIFT_LOOP;
       else
-       {
-         /* Shift by 5/6 are only 3 insns on the H8/S, so it's just as
-            fast as SHIFT_ROT_AND, plus CC is valid.  */
-         if (TARGET_H8300S && count <= 6)
-           return SHIFT_INLINE;
-
-         /* For ASHIFTRT by 7 bits, the sign bit is simply replicated
-            through the entire value.  */
-         if (shift_type == SHIFT_ASHIFTRT && count == 7)
-           {
-             info->special = "shll\t%X0\n\tsubx\t%X0,%X0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-
-         /* Other ASHIFTRTs are too much of a pain.  */
-         if (shift_type == SHIFT_ASHIFTRT)
-           return SHIFT_LOOP;
-
-         /* Other shifts by 5, 6, or 7 bits use SHIFT_ROT_AND.  */
-         info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
-         if (TARGET_H8300S)
-           info->shift2 = rotate_two[shift_type][shift_mode];
-         info->cc_valid_p = 0;
-         return SHIFT_ROT_AND;
-       }
+       info->alg = shift_alg_qi[cpu][shift_type][count];
+      break;
 
     case HIshift:
-      if (count <= 4)
-       return SHIFT_INLINE;
-      else if (TARGET_H8300S && count <= 7)
-       return SHIFT_INLINE;
-      else if (count == 7)
-       {
-         if (shift_type == SHIFT_ASHIFT && TARGET_H8300)
-           {
-             info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.b\t%t0\n\trotr.b\t%s0\n\tand.b\t#0x80,%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
+      if (GET_MODE_BITSIZE (HImode) <= count)
+       info->alg = SHIFT_LOOP;
+      else
+       info->alg = shift_alg_hi[cpu][shift_type][count];
+      break;
 
-         if (shift_type == SHIFT_ASHIFT && TARGET_H8300H)
-           {
-             info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.w\t%T0\n\tand.b\t#0x80,%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
+    case SIshift:
+      if (GET_MODE_BITSIZE (SImode) <= count)
+       info->alg = SHIFT_LOOP;
+      else
+       info->alg = shift_alg_si[cpu][shift_type][count];
+      break;
 
-         if (shift_type == SHIFT_LSHIFTRT && TARGET_H8300)
-           {
-             info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\trotl.b\t%t0\n\tand.b\t#0x01,%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
+    default:
+      abort ();
+    }
 
-         if (shift_type == SHIFT_LSHIFTRT && TARGET_H8300H)
-           {
-             info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.w\t%T0\n\tand.b\t#0x01,%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
+  /* Fill in INFO.  Return unless we have SHIFT_SPECIAL.  */
+  switch (info->alg)
+    {
+    case SHIFT_INLINE:
+      info->remainder = count;
+      /* Fall through.  */
+
+    case SHIFT_LOOP:
+      /* It is up to the caller to know that looping clobbers cc.  */
+      info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
+      info->shift2 = shift_two[shift_type][shift_mode].assembler;
+      info->cc_valid_p = shift_one[cpu_type][shift_type][shift_mode].cc_valid;
+      goto end;
+
+    case SHIFT_ROT_AND:
+      info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
+      info->shift2 = rotate_two[shift_type][shift_mode];
+      info->cc_valid_p = 0;
+      goto end;
+
+    case SHIFT_SPECIAL:
+      /* REMAINDER is 0 for most cases, so initialize it to 0.  */
+      info->remainder = 0;
+      info->shift1 = shift_one[cpu_type][shift_type][shift_mode].assembler;
+      info->shift2 = shift_two[shift_type][shift_mode].assembler;
+      info->cc_valid_p = 0;
+      break;
+    }
 
-         if (shift_type == SHIFT_ASHIFTRT)
-           {
-             info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\tsubx\t%t0,%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count == 8)
+  /* Here we only deal with SHIFT_SPECIAL.  */
+  switch (shift_mode)
+    {
+    case QIshift:
+      /* For ASHIFTRT by 7 bits, the sign bit is simply replicated
+        through the entire value.  */
+      if (shift_type == SHIFT_ASHIFTRT && count == 7)
        {
-         switch (shift_type)
-           {
-           case SHIFT_ASHIFT:
-             info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_LSHIFTRT:
-             info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_ASHIFTRT:
-             if (TARGET_H8300)
-               info->special = "mov.b\t%t0,%s0\n\tshll\t%t0\n\tsubx\t%t0,%t0";
-             else
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
+         info->special = "shll\t%X0\n\tsubx\t%X0,%X0";
+         goto end;
        }
-      else if (count == 9)
+      abort ();
+
+    case HIshift:
+      if (count == 7)
        {
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
-             info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_LSHIFTRT:
-             info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_ASHIFTRT:
              if (TARGET_H8300)
-               info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0\n\tshar.b\t%s0";
+               info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.b\t%t0\n\trotr.b\t%s0\n\tand.b\t#0x80,%s0";
              else
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count == 10)
-       {
-         switch (shift_type)
-           {
-           case SHIFT_ASHIFT:
-             if (TARGET_H8300S)
-               info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t#2,%t0";
-             else
-               info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t%t0\n\tshal.b\t%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+               info->special = "shar.b\t%t0\n\tmov.b\t%s0,%t0\n\trotxr.w\t%T0\n\tand.b\t#0x80,%s0";
+             goto end;
            case SHIFT_LSHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t#2,%s0";
-             else
-               info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t%s0\n\tshlr.b\t%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_ASHIFTRT:
              if (TARGET_H8300)
-               info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0\n\tshar.b\t%s0\n\tshar.b\t%s0";
-             else if (TARGET_H8300H)
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t%s0\n\tshar.b\t%s0";
-             else if (TARGET_H8300S)
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t#2,%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count == 11)
-       {
-         switch (shift_type)
-           {
-           case SHIFT_ASHIFT:
-             if (TARGET_H8300S)
-               info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t#2,%t0\n\tshal.b\t%t0";
-             else
-               info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t%t0\n\tshal.b\t%t0\n\tshal.b\t%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_LSHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t#2,%s0\n\tshlr.b\t%s0";
+               info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\trotl.b\t%t0\n\tand.b\t#0x01,%t0";
              else
-               info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t%s0\n\tshlr.b\t%s0\n\tshlr.b\t%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+               info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.w\t%T0\n\tand.b\t#0x01,%t0";
+             goto end;
            case SHIFT_ASHIFTRT:
-             if (TARGET_H8300)
-               info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0\n\tshar.b\t%s0\n\tshar.b\t%s0\n\tshar.b\t%s0";
-             else if (TARGET_H8300H)
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t%s0\n\tshar.b\t%s0\n\tshar.b\t%s0";
-             else if (TARGET_H8300S)
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t#2,%s0\n\tshar.b\t%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "shal.b\t%s0\n\tmov.b\t%t0,%s0\n\trotxl.b\t%s0\n\tsubx\t%t0,%t0";
+             goto end;
            }
        }
-      else if (count == 12)
+      else if (8 <= count && count <= 12)
        {
+         info->remainder = count - 8;
+
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
-             if (TARGET_H8300S)
-               info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t#2,%t0\n\tshal.b\t#2,%t0";
-             else
-               info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tshal.b\t%t0\n\tshal.b\t%t0\n\tshal.b\t%t0\n\tshal.b\t%t0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0";
+             info->shift1  = "shal.b\t%t0";
+             info->shift2  = "shal.b\t#2,%t0";
+             goto end;
            case SHIFT_LSHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t#2,%s0\n\tshlr.b\t#2,%s0";
-             else
-               info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0\n\tshlr.b\t%s0\n\tshlr.b\t%s0\n\tshlr.b\t%s0\n\tshlr.b\t%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "mov.b\t%t0,%s0\n\tsub.b\t%t0,%t0";
+             info->shift1  = "shlr.b\t%s0";
+             info->shift2  = "shlr.b\t#2,%s0";
+             goto end;
            case SHIFT_ASHIFTRT:
              if (TARGET_H8300)
-               info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0\n\tshar.b\t%s0\n\tshar.b\t%s0\n\tshar.b\t%s0\n\tshar.b\t%s0";
-             else if (TARGET_H8300H)
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t%s0\n\tshar.b\t%s0\n\tshar.b\t%s0\n\tshar.b\t%s0";
-             else if (TARGET_H8300S)
-               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.b\t#2,%s0\n\tshar.b\t#2,%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+               info->special = "mov.b\t%t0,%s0\n\tbld\t#7,%s0\n\tsubx\t%t0,%t0";
+             else
+               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0";
+             info->shift1 = "shar.b\t%s0";
+             info->shift2 = "shar.b\t#2,%s0";
+             goto end;
            }
        }
-      else if ((!TARGET_H8300 && (count == 13 || count == 14))
-              || count == 15)
+      else if (count == 15 && shift_type == SHIFT_ASHIFTRT)
        {
-         if (count == 15 && shift_type == SHIFT_ASHIFTRT)
-           {
-             info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-         else if (shift_type != SHIFT_ASHIFTRT)
-           {
-             info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
-             if (TARGET_H8300S)
-               info->shift2 = rotate_two[shift_type][shift_mode];
-             else
-               info->shift2 = NULL;
-             info->cc_valid_p = 0;
-             return SHIFT_ROT_AND;
-           }
+         info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0";
+         goto end;
        }
-      break;
+      abort ();
 
     case SIshift:
-      if (count <= (TARGET_H8300 ? 2 : 4))
-       return SHIFT_INLINE;
-      else if (TARGET_H8300S && count <= 10)
-       return SHIFT_INLINE;
-      else if (count == 8 && TARGET_H8300)
+      if (count == 8 && TARGET_H8300)
        {
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
              info->special = "mov.b\t%y0,%z0\n\tmov.b\t%x0,%y0\n\tmov.b\t%w0,%x0\n\tsub.b\t%w0,%w0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             goto end;
            case SHIFT_LSHIFTRT:
              info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tsub.b\t%z0,%z0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             goto end;
            case SHIFT_ASHIFTRT:
              info->special = "mov.b\t%x0,%w0\n\tmov.b\t%y0,%x0\n\tmov.b\t%z0,%y0\n\tshll\t%z0\n\tsubx\t%z0,%z0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             goto end;
            }
        }
       else if (count == 8 && !TARGET_H8300)
@@ -2479,213 +2306,129 @@ get_shift_alg (shift_type, shift_mode, count, info)
            {
            case SHIFT_ASHIFT:
              info->special = "mov.w\t%e0,%f4\n\tmov.b\t%s4,%t4\n\tmov.b\t%t0,%s4\n\tmov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tmov.w\t%f4,%e0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             goto end;
            case SHIFT_LSHIFTRT:
              info->special = "mov.w\t%e0,%f4\n\tmov.b\t%t0,%s0\n\tmov.b\t%s4,%t0\n\tmov.b\t%t4,%s4\n\textu.w\t%f4\n\tmov.w\t%f4,%e0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             goto end;
            case SHIFT_ASHIFTRT:
              info->special = "mov.w\t%e0,%f4\n\tmov.b\t%t0,%s0\n\tmov.b\t%s4,%t0\n\tmov.b\t%t4,%s4\n\texts.w\t%f4\n\tmov.w\t%f4,%e0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count == 16)
-       {
-         switch (shift_type)
-           {
-           case SHIFT_ASHIFT:
-             info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_LSHIFTRT:
-             info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_ASHIFTRT:
-             if (TARGET_H8300)
-               info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0";
-             else
-               info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count == 17 && !TARGET_H8300)
-       {
-         switch (shift_type)
-           {
-           case SHIFT_ASHIFT:
-             info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0\n\tshll.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_LSHIFTRT:
-             info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0\n\tshlr.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_ASHIFTRT:
-             info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0\n\tshar.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             goto end;
            }
        }
-      else if (count == 18 && !TARGET_H8300)
+      else if (count == 15 && !TARGET_H8300)
        {
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
-             if (TARGET_H8300S)
-               info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0\n\tshll.l\t#2,%S0";
-             else
-               info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0\n\tshll.l\t%S0\n\tshll.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "shlr.w\t%e0\n\tmov.w\t%f0,%e0\n\txor.w\t%f0,%f0\n\trotxr.l\t%S0";
+             goto end;
            case SHIFT_LSHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0\n\tshlr.l\t#2,%S0";
-             else
-               info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0\n\tshlr.l\t%S0\n\tshlr.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "shll.w\t%f0\n\tmov.w\t%e0,%f0\n\txor.w\t%e0,%e0\n\trotxl.l\t%S0";
+             goto end;
            case SHIFT_ASHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0\n\tshar.l\t#2,%S0";
-             else
-               info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0\n\tshar.l\t%S0\n\tshar.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             abort ();
            }
        }
-      else if (count == 19 && !TARGET_H8300)
+      else if ((TARGET_H8300 && count == 16)
+              || (TARGET_H8300H && 16 <= count && count <= 19)
+              || (TARGET_H8300S && 16 <= count && count <= 21))
        {
+         info->remainder = count - 16;
+
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
-             if (TARGET_H8300S)
-               info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0\n\tshll.l\t#2,%S0\n\tshll.l\t%S0";
-             else
-               info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0\n\tshll.l\t%S0\n\tshll.l\t%S0\n\tshll.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
+             info->shift1  = "shll.l\t%S0";
+             info->shift2  = "shll.l\t#2,%S0";
+             goto end;
            case SHIFT_LSHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0\n\tshlr.l\t#2,%S0\n\tshlr.l\t%S0";
-             else
-               info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0\n\tshlr.l\t%S0\n\tshlr.l\t%S0\n\tshlr.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0";
+             info->shift1  = "shlr.l\t%S0";
+             info->shift2  = "shlr.l\t#2,%S0";
+             goto end;
            case SHIFT_ASHIFTRT:
-             if (TARGET_H8300S)
-               info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0\n\tshar.l\t#2,%S0\n\tshar.l\t%S0";
+             if (TARGET_H8300)
+               info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0";
              else
-               info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0\n\tshar.l\t%S0\n\tshar.l\t%S0\n\tshar.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count == 20 && TARGET_H8300S)
-       {
-         switch (shift_type)
-           {
-           case SHIFT_ASHIFT:
-             info->special = "mov.w\t%f0,%e0\n\tsub.w\t%f0,%f0\n\tshll.l\t#2,%S0\n\tshll.l\t#2,%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_LSHIFTRT:
-             info->special = "mov.w\t%e0,%f0\n\tsub.w\t%e0,%e0\n\tshlr.l\t#2,%S0\n\tshlr.l\t#2,%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           case SHIFT_ASHIFTRT:
-             info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0\n\tshar.l\t#2,%S0\n\tshar.l\t#2,%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+               info->special = "mov.w\t%e0,%f0\n\texts.l\t%S0";
+             info->shift1 = "shar.l\t%S0";
+             info->shift2 = "shar.l\t#2,%S0";
+             goto end;
            }
        }
-      else if (count == 24 && !TARGET_H8300)
+      else if ((TARGET_H8300H && count == 24)
+              || (TARGET_H8300S && 24 <= count && count <= 25))
        {
+         info->remainder = count - 24;
+
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
              info->special = "mov.b\t%s0,%t0\n\tsub.b\t%s0,%s0\n\tmov.w\t%f0,%e0\n\tsub.w\t%f0,%f0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->shift1  = "shll.l\t%S0";
+             info->shift2  = "shll.l\t#2,%S0";
+             goto end;
            case SHIFT_LSHIFTRT:
              info->special = "mov.w\t%e0,%f0\n\tmov.b\t%t0,%s0\n\textu.w\t%f0\n\textu.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             info->shift1  = "shlr.l\t%S0";
+             info->shift2  = "shlr.l\t#2,%S0";
+             goto end;
            case SHIFT_ASHIFTRT:
              info->special = "mov.w\t%e0,%f0\n\tmov.b\t%t0,%s0\n\texts.w\t%f0\n\texts.l\t%S0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
-           }
-       }
-      else if (count >= 28 && count <= 30 && !TARGET_H8300)
-       {
-         if (shift_type == SHIFT_ASHIFTRT)
-           {
-             return SHIFT_LOOP;
-           }
-         else
-           {
-             info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
-             if (TARGET_H8300S)
-               info->shift2 = rotate_two[shift_type][shift_mode];
-             else
-               info->shift2 = NULL;
-             info->cc_valid_p = 0;
-             return SHIFT_ROT_AND;
+             info->shift1  = "shar.l\t%S0";
+             info->shift2  = "shar.l\t#2,%S0";
+             goto end;
            }
        }
       else if (count == 31)
        {
-         if (shift_type == SHIFT_ASHIFTRT)
+         if (TARGET_H8300)
            {
-             if (TARGET_H8300)
-               info->special = "shll\t%z0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
-             else
-               info->special = "shll\t%e0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
-             info->cc_valid_p = 0;
-             return SHIFT_SPECIAL;
+             switch (shift_type)
+               {
+               case SHIFT_ASHIFT:
+                 info->special = "sub.w\t%e0,%e0\n\tshlr\t%w0\n\tmov.w\t%e0,%f0\n\trotxr\t%z0";
+                 goto end;
+               case SHIFT_LSHIFTRT:
+                 info->special = "sub.w\t%f0,%f0\n\tshll\t%z0\n\tmov.w\t%f0,%e0\n\trotxl\t%w0";
+                 goto end;
+               case SHIFT_ASHIFTRT:
+                 info->special = "shll\t%z0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
+                 goto end;
+               }
            }
          else
            {
-             if (TARGET_H8300)
+             switch (shift_type)
                {
-                 if (shift_type == SHIFT_ASHIFT)
-                   info->special = "sub.w\t%e0,%e0\n\tshlr\t%w0\n\tmov.w\t%e0,%f0\n\trotxr\t%z0";
-                 else
-                   info->special = "sub.w\t%f0,%f0\n\tshll\t%z0\n\tmov.w\t%f0,%e0\n\trotxl\t%w0";
-                 info->cc_valid_p = 0;
-                 return SHIFT_SPECIAL;
-               }
-             else
-               {
-                 info->shift1 = rotate_one[cpu_type][shift_type][shift_mode];
-                 if (TARGET_H8300S)
-                   info->shift2 = rotate_two[shift_type][shift_mode];
-                 else
-                   info->shift2 = NULL;
-                 info->cc_valid_p = 0;
-                 return SHIFT_ROT_AND;
+               case SHIFT_ASHIFT:
+                 info->special = "shlr.l\t%S0\n\txor.l\t%S0,%S0\n\trotxr.l\t%S0";
+                 goto end;
+               case SHIFT_LSHIFTRT:
+                 info->special = "shll.l\t%S0\n\txor.l\t%S0,%S0\n\trotxl.l\t%S0";
+                 goto end;
+               case SHIFT_ASHIFTRT:
+                 info->special = "shll\t%e0\n\tsubx\t%w0,%w0\n\tmov.b\t%w0,%x0\n\tmov.w\t%f0,%e0";
+                 goto end;
                }
            }
        }
-      break;
+      abort ();
 
     default:
       abort ();
     }
 
-  /* No fancy method is available.  Just loop.  */
-  return SHIFT_LOOP;
+ end:
+  if (!TARGET_H8300S)
+    info->shift2 = NULL;
 }
 
 /* Emit the assembler code for doing shifts.  */
 
 const char *
-emit_a_shift (insn, operands)
-     rtx insn ATTRIBUTE_UNUSED;
+output_a_shift (operands)
      rtx *operands;
 {
   static int loopend_lab;
@@ -2748,7 +2491,6 @@ emit_a_shift (insn, operands)
   else
     {
       int n = INTVAL (operands[2]);
-      enum shift_alg alg;
 
       /* If the count is negative, make it 0.  */
       if (n < 0)
@@ -2759,24 +2501,27 @@ emit_a_shift (insn, operands)
       else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
        n = GET_MODE_BITSIZE (mode);
 
-      alg = get_shift_alg (shift_type, shift_mode, n, &info);
+      get_shift_alg (shift_type, shift_mode, n, &info);
 
-      switch (alg)
+      switch (info.alg)
        {
+       case SHIFT_SPECIAL:
+         output_asm_insn (info.special, operands);
+         /* Fall through.  */
+
        case SHIFT_INLINE:
+         n = info.remainder;
+
          /* Emit two bit shifts first.  */
-         while (n > 1 && info.shift2 != NULL)
+         if (info.shift2 != NULL)
            {
-             output_asm_insn (info.shift2, operands);
-             n -= 2;
+             for (; n > 1; n -= 2)
+               output_asm_insn (info.shift2, operands);
            }
 
          /* Now emit one bit shifts for any residual.  */
-         while (n > 0)
-           {
-             output_asm_insn (info.shift1, operands);
-             n -= 1;
-           }
+         for (; n > 0; n--)
+           output_asm_insn (info.shift1, operands);
 
          /* Keep track of CC.  */
          if (info.cc_valid_p)
@@ -2790,8 +2535,8 @@ emit_a_shift (insn, operands)
          {
            int m = GET_MODE_BITSIZE (mode) - n;
            int mask = (shift_type == SHIFT_ASHIFT
-                       ? ((1 << (GET_MODE_BITSIZE (mode) - n)) - 1) << n
-                       : (1 << (GET_MODE_BITSIZE (mode) - n)) - 1);
+                       ? ((1 << m) - 1) << n
+                       : (1 << m) - 1);
            char insn_buf[200];
 
            /* Not all possibilities of rotate are supported.  They shouldn't
@@ -2800,18 +2545,15 @@ emit_a_shift (insn, operands)
              abort ();
 
            /* Emit two bit rotates first.  */
-           while (m > 1 && info.shift2 != NULL)
+           if (info.shift2 != NULL)
              {
-               output_asm_insn (info.shift2, operands);
-               m -= 2;
+               for (; m > 1; m -= 2)
+                 output_asm_insn (info.shift2, operands);
              }
 
            /* Now single bit rotates for any residual.  */
-           while (m > 0)
-             {
-               output_asm_insn (info.shift1, operands);
-               m -= 1;
-             }
+           for (; m > 0; m--)
+             output_asm_insn (info.shift1, operands);
 
            /* Now mask off the high bits.  */
            if (TARGET_H8300)
@@ -2827,10 +2569,8 @@ emit_a_shift (insn, operands)
                    sprintf (insn_buf, "and\t#%d,%%s0\n\tand\t#%d,%%t0",
                             mask & 255, mask >> 8);
                    break;
-                 case SImode:
-                   abort ();
                  default:
-                   break;
+                   abort ();
                  }
              }
            else
@@ -2845,10 +2585,6 @@ emit_a_shift (insn, operands)
            return "";
          }
 
-       case SHIFT_SPECIAL:
-         output_asm_insn (info.special, operands);
-         return "";
-
        case SHIFT_LOOP:
          /* A loop to shift by a "large" constant value.
             If we have shift-by-2 insns, use them.  */
@@ -2887,7 +2623,7 @@ emit_a_shift (insn, operands)
 
 int
 expand_a_rotate (code, operands)
-     int code;
+     enum rtx_code code;
      rtx operands[];
 {
   rtx dst = operands[0];
@@ -2907,8 +2643,8 @@ expand_a_rotate (code, operands)
 
       /* If the rotate amount is less than or equal to 0,
         we go out of the loop.  */
-      emit_cmp_and_jump_insns (rotate_amount, GEN_INT (0),
-                              LE, NULL_RTX, QImode, 0, 0, end_label);
+      emit_cmp_and_jump_insns (rotate_amount, GEN_INT (0), LE, NULL_RTX,
+                              QImode, 0, end_label);
 
       /* Initialize the loop counter.  */
       emit_move_insn (counter, rotate_amount);
@@ -2925,8 +2661,8 @@ expand_a_rotate (code, operands)
 
       /* If the loop counter is non-zero, we go back to the beginning
         of the loop.  */
-      emit_cmp_and_jump_insns (counter, GEN_INT (0),
-                              NE, NULL_RTX, QImode, 1, 0, start_label);
+      emit_cmp_and_jump_insns (counter, GEN_INT (0), NE, NULL_RTX, QImode, 1,
+                              start_label);
 
       emit_label (end_label);
     }
@@ -2944,7 +2680,7 @@ expand_a_rotate (code, operands)
 
 const char *
 emit_a_rotate (code, operands)
-     int code;
+     enum rtx_code code;
      rtx *operands;
 {
   rtx dst = operands[0];
@@ -2996,7 +2732,7 @@ emit_a_rotate (code, operands)
 
   /* Determine the faster direction.  After this phase, amount will be
      at most a half of GET_MODE_BITSIZE (mode).  */
-  if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2)
+  if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2U)
     {
       /* Flip the direction.  */
       amount = GET_MODE_BITSIZE (mode) - amount;
@@ -3116,7 +2852,7 @@ h8300_interrupt_function_p (func)
   if (TREE_CODE (func) != FUNCTION_DECL)
     return 0;
 
-  a = lookup_attribute ("interrupt_handler", DECL_MACHINE_ATTRIBUTES (func));
+  a = lookup_attribute ("interrupt_handler", DECL_ATTRIBUTES (func));
   return a != NULL_TREE;
 }
 
@@ -3132,7 +2868,7 @@ h8300_os_task_function_p (func)
   if (TREE_CODE (func) != FUNCTION_DECL)
     return 0;
 
-  a = lookup_attribute ("OS_Task", DECL_MACHINE_ATTRIBUTES (func));
+  a = lookup_attribute ("OS_Task", DECL_ATTRIBUTES (func));
   return a != NULL_TREE;
 }
 
@@ -3148,7 +2884,7 @@ h8300_monitor_function_p (func)
   if (TREE_CODE (func) != FUNCTION_DECL)
     return 0;
 
-  a = lookup_attribute ("monitor", DECL_MACHINE_ATTRIBUTES (func));
+  a = lookup_attribute ("monitor", DECL_ATTRIBUTES (func));
   return a != NULL_TREE;
 }
 
@@ -3164,7 +2900,7 @@ h8300_funcvec_function_p (func)
   if (TREE_CODE (func) != FUNCTION_DECL)
     return 0;
 
-  a = lookup_attribute ("function_vector", DECL_MACHINE_ATTRIBUTES (func));
+  a = lookup_attribute ("function_vector", DECL_ATTRIBUTES (func));
   return a != NULL_TREE;
 }
 
@@ -3180,7 +2916,7 @@ h8300_eightbit_data_p (decl)
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
-  a = lookup_attribute ("eightbit_data", DECL_MACHINE_ATTRIBUTES (decl));
+  a = lookup_attribute ("eightbit_data", DECL_ATTRIBUTES (decl));
   return a != NULL_TREE;
 }
 
@@ -3196,15 +2932,11 @@ h8300_tiny_data_p (decl)
   if (TREE_CODE (decl) != VAR_DECL)
     return 0;
 
-  a = lookup_attribute ("tiny_data", DECL_MACHINE_ATTRIBUTES (decl));
+  a = lookup_attribute ("tiny_data", DECL_ATTRIBUTES (decl));
   return a != NULL_TREE;
 }
 
-/* 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: output a prologue and epilogue suitable for an
    interrupt handler.
@@ -3218,47 +2950,87 @@ h8300_tiny_data_p (decl)
    tiny_data: This variable lives in the tiny data area and can be
    referenced with 16-bit absolute memory references.  */
 
-static int
-h8300_valid_decl_attribute (decl, attributes, attr, args)
-     tree decl;
-     tree attributes ATTRIBUTE_UNUSED;
-     tree attr;
-     tree args;
+const struct attribute_spec h8300_attribute_table[] =
 {
-  if (args != NULL_TREE)
-    return 0;
+  /* { name, min_len, max_len, decl_req, type_req, fn_type_req, handler } */
+  { "interrupt_handler", 0, 0, true,  false, false, h8300_handle_fndecl_attribute },
+  { "OS_Task",           0, 0, true,  false, false, h8300_handle_fndecl_attribute },
+  { "monitor",           0, 0, true,  false, false, h8300_handle_fndecl_attribute },
+  { "function_vector",   0, 0, true,  false, false, h8300_handle_fndecl_attribute },
+  { "eightbit_data",     0, 0, true,  false, false, h8300_handle_eightbit_data_attribute },
+  { "tiny_data",         0, 0, true,  false, false, h8300_handle_tiny_data_attribute },
+  { NULL,                0, 0, false, false, false, NULL }
+};
 
-  if (is_attribute_p ("interrupt_handler", attr)
-      || is_attribute_p ("OS_Task", attr)
-      || is_attribute_p ("monitor", attr)
-      || is_attribute_p ("function_vector", attr))
-    return TREE_CODE (decl) == FUNCTION_DECL;
 
-  if (is_attribute_p ("eightbit_data", attr)
-      && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
+/* Handle an attribute requiring a FUNCTION_DECL; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+h8300_handle_fndecl_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)
+    {
+      warning ("`%s' attribute only applies to functions",
+              IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
+/* Handle an "eightbit_data" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+h8300_handle_eightbit_data_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name;
+     tree args ATTRIBUTE_UNUSED;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  tree decl = *node;
+
+  if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
     {
-      if (DECL_INITIAL (decl) == NULL_TREE)
-       {
-         warning ("Only initialized variables can be placed into the 8-bit area.");
-         return 0;
-       }
       DECL_SECTION_NAME (decl) = build_string (7, ".eight");
-      return 1;
+    }
+  else
+    {
+      warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
     }
 
-  if (is_attribute_p ("tiny_data", attr)
-      && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
+  return NULL_TREE;
+}
+
+/* Handle an "tiny_data" attribute; arguments as in
+   struct attribute_spec.handler.  */
+static tree
+h8300_handle_tiny_data_attribute (node, name, args, flags, no_add_attrs)
+     tree *node;
+     tree name;
+     tree args ATTRIBUTE_UNUSED;
+     int flags ATTRIBUTE_UNUSED;
+     bool *no_add_attrs;
+{
+  tree decl = *node;
+
+  if (TREE_STATIC (decl) || DECL_EXTERNAL (decl))
     {
-      if (DECL_INITIAL (decl) == NULL_TREE)
-       {
-         warning ("Only initialized variables can be placed into the 8-bit area.");
-         return 0;
-       }
       DECL_SECTION_NAME (decl) = build_string (6, ".tiny");
-      return 1;
+    }
+  else
+    {
+      warning ("`%s' attribute ignored", IDENTIFIER_POINTER (name));
+      *no_add_attrs = true;
     }
 
-  return 0;
+  return NULL_TREE;
 }
 
 void
@@ -3267,19 +3039,18 @@ h8300_encode_label (decl)
 {
   const char *str = XSTR (XEXP (DECL_RTL (decl), 0), 0);
   int len = strlen (str);
-  char *newstr;
+  char *newstr = alloca (len + 2);
 
-  newstr = ggc_alloc_string (NULL, len + 1);
+  newstr[0] = '&';
+  strcpy (&newstr[1], str);
 
-  strcpy (newstr + 1, str);
-  *newstr = '&';
-  XSTR (XEXP (DECL_RTL (decl), 0), 0) = newstr;
+  XSTR (XEXP (DECL_RTL (decl), 0), 0) =
+    ggc_alloc_string (newstr, len + 1);
 }
 
 const char *
-output_simode_bld (bild, log2, operands)
+output_simode_bld (bild, operands)
      int bild;
-     int log2;
      rtx operands[];
 {
   /* Clear the destination register.  */
@@ -3288,10 +3059,6 @@ output_simode_bld (bild, log2, operands)
   else
     output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
 
-  /* Get the bit number we want to load.  */
-  if (log2)
-    operands[2] = GEN_INT (exact_log2 (INTVAL (operands[2])));
-
   /* Now output the bit load or bit inverse load, and store it in
      the destination.  */
   if (bild)
@@ -3313,21 +3080,19 @@ h8300_adjust_insn_length (insn, length)
      rtx insn;
      int length ATTRIBUTE_UNUSED;
 {
-  rtx pat;
+  rtx pat = PATTERN (insn);
 
-  /* We must filter these ou before calling get_attr_adjust_length.  */
-  if (GET_CODE (PATTERN (insn)) == USE
-      || GET_CODE (PATTERN (insn)) == CLOBBER
-      || GET_CODE (PATTERN (insn)) == SEQUENCE
-      || GET_CODE (PATTERN (insn)) == ADDR_VEC
-      || GET_CODE (PATTERN (insn)) == ADDR_DIFF_VEC)
+  /* We must filter these out before calling get_attr_adjust_length.  */
+  if (GET_CODE (pat) == USE
+      || GET_CODE (pat) == CLOBBER
+      || GET_CODE (pat) == SEQUENCE
+      || GET_CODE (pat) == ADDR_VEC
+      || GET_CODE (pat) == ADDR_DIFF_VEC)
     return 0;
 
   if (get_attr_adjust_length (insn) == ADJUST_LENGTH_NO)
     return 0;
 
-  pat = PATTERN (insn);
-
   /* Adjust length for reg->mem and mem->reg copies.  */
   if (GET_CODE (pat) == SET
       && (GET_CODE (SET_SRC (pat)) == MEM
@@ -3353,8 +3118,8 @@ h8300_adjust_insn_length (insn, length)
           && GET_CODE (addr) == REG)
        return -6;
 
-      /* On the H8/300H and H8/300S, reg + d, for small displacements is 4
-        bytes shorter than indicated in the machine description.  */
+      /* On the H8/300H and H8/S, reg + d, for small displacements is
+        bytes shorter than indicated in the machine description.  */
       if ((TARGET_H8300H || TARGET_H8300S)
          && GET_CODE (addr) == PLUS
          && GET_CODE (XEXP (addr, 0)) == REG
@@ -3363,7 +3128,7 @@ h8300_adjust_insn_length (insn, length)
          && INTVAL (XEXP (addr, 1)) < 32767)
        return -4;
 
-      /* On the H8/300H and H8/300S, abs:16 is two bytes shorter than the
+      /* On the H8/300H and H8/S, abs:16 is two bytes shorter than the
         more general abs:24.  */
       if ((TARGET_H8300H || TARGET_H8300S)
          && GET_CODE (addr) == SYMBOL_REF
@@ -3412,7 +3177,7 @@ h8300_adjust_insn_length (insn, length)
       shift = INTVAL (XEXP (src, 1));
       /* According to ANSI, negative shift is undefined.  It is
          considered to be zero in this case (see function
-         emit_a_shift above).  */
+         output_a_shift above).  */
       if (shift < 0)
        shift = 0;
 
@@ -3422,8 +3187,8 @@ h8300_adjust_insn_length (insn, length)
       if (mode == QImode && shift <= 4)
        return -(20 - shift * 2);
 
-      /* Similarly for HImode and SImode shifts by
-        small constants on the H8/300H and H8/300S.  */
+      /* Similarly for HImode and SImode shifts by small constants on
+        the H8/300H and H8/S.  */
       if ((TARGET_H8300H || TARGET_H8300S)
          && (mode == HImode || mode == SImode) && shift <= 4)
        return -(20 - shift * 2);
@@ -3462,7 +3227,7 @@ h8300_adjust_insn_length (insn, length)
 
       /* Determine the faster direction.  After this phase, amount
         will be at most a half of GET_MODE_BITSIZE (mode).  */
-      if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2)
+      if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / 2U)
        /* Flip the direction.  */
        amount = GET_MODE_BITSIZE (mode) - amount;
 
@@ -3493,6 +3258,7 @@ h8300_adjust_insn_length (insn, length)
   return 0;
 }
 
+#ifndef OBJECT_FORMAT_ELF
 static void
 h8300_asm_named_section (name, flags)
      const char *name;
@@ -3501,3 +3267,4 @@ h8300_asm_named_section (name, flags)
   /* ??? Perhaps we should be using default_coff_asm_named_section.  */
   fprintf (asm_out_file, "\t.section %s\n", name);
 }
+#endif /* ! OBJECT_FORMAT_ELF */