OSDN Git Service

* config/h8300/h8300.md (output_a_shift): Clean up the code to
[pf3gnuchains/gcc-fork.git] / gcc / config / h8300 / h8300.c
index 1f178b8..ab2cc15 100644 (file)
@@ -23,6 +23,8 @@ Boston, MA 02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "tree.h"
 #include "regs.h"
@@ -44,24 +46,30 @@ Boston, MA 02111-1307, USA.  */
 #include "target-def.h"
 
 /* Forward declarations.  */
+static const char *byte_reg PARAMS ((rtx, int));
 static int h8300_interrupt_function_p PARAMS ((tree));
 static int h8300_monitor_function_p PARAMS ((tree));
 static int h8300_os_task_function_p PARAMS ((tree));
-static void dosize PARAMS ((FILE *, const char *, unsigned int));
+static void dosize PARAMS ((FILE *, int, unsigned int));
 static int round_frame_size PARAMS ((int));
 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 unsigned int h8300_asm_insn_count PARAMS ((const char *));
 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));
+static void h8300_insert_attributes PARAMS ((tree, tree *));
 #ifndef OBJECT_FORMAT_ELF
 static void h8300_asm_named_section PARAMS ((const char *, unsigned int));
 #endif
+static void h8300_encode_label PARAMS ((tree));
+static void h8300_encode_section_info PARAMS ((tree, int));
+static const char *h8300_strip_name_encoding PARAMS ((const char *));
 
 /* CPU_TYPE, says what cpu we're compiling for.  */
 int cpu_type;
@@ -109,9 +117,176 @@ const char *h8_push_op, *h8_pop_op, *h8_mov_op;
 #define TARGET_ASM_FUNCTION_PROLOGUE h8300_output_function_prologue
 #undef TARGET_ASM_FUNCTION_EPILOGUE
 #define TARGET_ASM_FUNCTION_EPILOGUE h8300_output_function_epilogue
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO h8300_encode_section_info
+#undef TARGET_STRIP_NAME_ENCODING
+#define TARGET_STRIP_NAME_ENCODING h8300_strip_name_encoding
+
+#undef TARGET_INSERT_ATTRIBUTES
+#define TARGET_INSERT_ATTRIBUTES h8300_insert_attributes
 
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
+/* See below where shifts are handled for explanation of this enum.  */
+
+enum shift_alg
+{
+  SHIFT_INLINE,
+  SHIFT_ROT_AND,
+  SHIFT_SPECIAL,
+  SHIFT_LOOP
+};
+
+/* Symbols of the various shifts which can be used as indices.  */
+
+enum shift_type
+{
+  SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
+};
+
+/* 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 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 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, INL, INL, SPC,
+      SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, LOP, LOP, SPC,
+      SPC, SPC, SPC, SPC, SPC, SPC, SPC, 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, INL, INL, SPC,
+      SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, INL, INL, INL, INL, SPC,
+      SPC, SPC, SPC, SPC, SPC, ROT, ROT, ROT }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, INL, INL, INL, INL, SPC,
+      SPC, SPC, SPC, SPC, SPC, SPC, SPC, 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, SPC, SPC, SPC }, /* SHIFT_ASHIFTRT */
+  }
+};
+
+static 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, SPC, SPC, SPC, SPC, LOP, LOP, LOP,
+      SPC, SPC, SPC, SPC, LOP, LOP, LOP, SPC }, /* SHIFT_ASHIFT   */
+    { INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
+      SPC, SPC, LOP, LOP, LOP, LOP, LOP, SPC,
+      SPC, SPC, SPC, LOP, LOP, LOP, LOP, LOP,
+      SPC, SPC, SPC, SPC, SPC, LOP, LOP, SPC }, /* SHIFT_LSHIFTRT */
+    { INL, INL, INL, LOP, LOP, LOP, LOP, LOP,
+      SPC, LOP, LOP, LOP, LOP, LOP, LOP, SPC,
+      SPC, SPC, LOP, LOP, LOP, LOP, LOP, LOP,
+      SPC, SPC, SPC, 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, SPC, SPC, SPC, 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, SPC, SPC, SPC, 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, SPC, SPC, SPC, 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, SPC, SPC, SPC, 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
+
+enum h8_cpu
+{
+  H8_300,
+  H8_300H,
+  H8_S
+};
+
 /* Initialize various cpu specific globals at start up.  */
 
 void
@@ -128,7 +303,7 @@ h8300_init_once ()
     }
   else
     {
-      /* For this we treat the H8/300H and H8/S the same.  */
+      /* For this we treat the H8/300H and H8S the same.  */
       cpu_type = (int) CPU_H8300H;
       h8_reg_names = names_extended;
     }
@@ -139,11 +314,51 @@ h8300_init_once ()
   if (!TARGET_H8300S && TARGET_MAC)
     {
       error ("-ms2600 is used without -ms");
-      target_flags |= 1;
+      target_flags |= MASK_H8300S;
+    }
+
+  if (TARGET_H8300 && TARGET_NORMAL_MODE)
+    {
+      error ("-mn is used without -mh or -ms");
+      target_flags ^= MASK_NORMAL_MODE;
+    }
+
+  /* Some of the shifts are optimized for speed by default.
+     See http://gcc.gnu.org/ml/gcc-patches/2002-07/msg01858.html
+     If optimizing for size, change shift_alg for those shift to
+     SHIFT_LOOP.  */
+  if (optimize_size)
+    {
+      /* H8/300 */
+      shift_alg_hi[H8_300][SHIFT_ASHIFT][5] = SHIFT_LOOP;
+      shift_alg_hi[H8_300][SHIFT_ASHIFT][6] = SHIFT_LOOP;
+      shift_alg_hi[H8_300][SHIFT_ASHIFT][13] = SHIFT_LOOP;
+      shift_alg_hi[H8_300][SHIFT_ASHIFT][14] = SHIFT_LOOP;
+
+      shift_alg_hi[H8_300][SHIFT_LSHIFTRT][13] = SHIFT_LOOP;
+      shift_alg_hi[H8_300][SHIFT_LSHIFTRT][14] = SHIFT_LOOP;
+
+      shift_alg_hi[H8_300][SHIFT_ASHIFTRT][13] = SHIFT_LOOP;
+      shift_alg_hi[H8_300][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
+
+      /* H8/300H */
+      shift_alg_hi[H8_300H][SHIFT_ASHIFT][5] = SHIFT_LOOP;
+      shift_alg_hi[H8_300H][SHIFT_ASHIFT][6] = SHIFT_LOOP;
+
+      shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][5] = SHIFT_LOOP;
+      shift_alg_hi[H8_300H][SHIFT_LSHIFTRT][6] = SHIFT_LOOP;
+
+      shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][5] = SHIFT_LOOP;
+      shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][6] = SHIFT_LOOP;
+      shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][13] = SHIFT_LOOP;
+      shift_alg_hi[H8_300H][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
+
+      /* H8S */
+      shift_alg_hi[H8_S][SHIFT_ASHIFTRT][14] = SHIFT_LOOP;
     }
 }
 
-const char *
+static const char *
 byte_reg (x, b)
      rtx x;
      int b;
@@ -179,12 +394,12 @@ byte_reg (x, b)
    SIZE to adjust the stack pointer.  */
 
 static void
-dosize (file, op, size)
+dosize (file, sign, size)
      FILE *file;
-     const char *op;
+     int sign;
      unsigned int size;
 {
-  /* On the H8/300H and H8/S, for sizes <= 8 bytes, it is as good or
+  /* On the H8/300H and H8S, for sizes <= 8 bytes, it is as good or
      better to use adds/subs insns rather than add.l/sub.l with an
      immediate value.
 
@@ -195,8 +410,9 @@ dosize (file, op, size)
       || ((TARGET_H8300H || TARGET_H8300S) && size <= 8)
       || (TARGET_H8300 && interrupt_handler)
       || (TARGET_H8300 && current_function_needs_context
-         && ! strcmp (op, "sub")))
+         && sign < 0))
     {
+      const char *op = (sign > 0) ? "add" : "sub";
       unsigned HOST_WIDE_INT amount;
 
       /* Try different amounts in descending order.  */
@@ -204,16 +420,24 @@ dosize (file, op, size)
           amount > 0;
           amount /= 2)
        {
+         char insn[100];
+
+         sprintf (insn, "\t%ss\t#%d,%s\n", op, amount,
+                  TARGET_H8300 ? "r7" : "er7");
          for (; size >= amount; size -= amount)
-           fprintf (file, "\t%ss\t#%d,sp\n", op, amount);
+           fputs (insn, file);
        }
     }
   else
     {
       if (TARGET_H8300)
-       fprintf (file, "\tmov.w\t#%d,r3\n\t%s.w\tr3,sp\n", size, op);
+       {
+         fprintf (file, "\tmov.w\t#%d,r3\n\tadd.w\tr3,r7\n", sign * size);
+       }
       else
-       fprintf (file, "\t%s.l\t#%d,sp\n", op, size);
+       {
+         fprintf (file, "\tadd.l\t#%d,er7\n", sign * size);
+       }
     }
 }
 
@@ -223,7 +447,8 @@ static int
 round_frame_size (size)
      int size;
 {
-  return (size + STACK_BOUNDARY / 8 - 1) & -STACK_BOUNDARY / 8;
+  return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1)
+         & -STACK_BOUNDARY / BITS_PER_UNIT);
 }
 
 /* Compute which registers to push/pop.
@@ -236,7 +461,7 @@ compute_saved_regs ()
   int regno;
 
   /* Construct a bit vector of registers to be pushed/popped.  */
-  for (regno = 0; regno <= 6; regno++)
+  for (regno = 0; regno <= FRAME_POINTER_REGNUM; regno++)
     {
       if (WORD_REG_USED (regno))
        saved_regs |= 1 << regno;
@@ -256,7 +481,10 @@ push (file, rn)
      FILE *file;
      int rn;
 {
-  fprintf (file, "\t%s\t%s\n", h8_push_op, h8_reg_names[rn]);
+  if (TARGET_H8300)
+    fprintf (file, "\t%s\t%s,@-r7\n", h8_mov_op, h8_reg_names[rn]);
+  else
+    fprintf (file, "\t%s\t%s,@-er7\n", h8_mov_op, h8_reg_names[rn]);
 }
 
 /* Output assembly language code to pop register RN.  */
@@ -266,17 +494,20 @@ pop (file, rn)
      FILE *file;
      int rn;
 {
-  fprintf (file, "\t%s\t%s\n", h8_pop_op, h8_reg_names[rn]);
+  if (TARGET_H8300)
+    fprintf (file, "\t%s\t@r7+,%s\n", h8_mov_op, h8_reg_names[rn]);
+  else
+    fprintf (file, "\t%s\t@er7+,%s\n", h8_mov_op, h8_reg_names[rn]);
 }
 
-/* This is what the stack looks like after the prolog of 
+/* This is what the stack looks like after the prolog of
    a function with a frame has been set up:
 
    <args>
    PC
    FP                  <- fp
    <locals>
-   <saved registers>   <- sp
+   <saved registers>   <- sp
 
    This is what the stack looks like after the prolog of
    a function which doesn't have a frame:
@@ -284,7 +515,7 @@ pop (file, rn)
    <args>
    PC
    <locals>
-   <saved registers>           <- sp
+   <saved registers>   <- sp
 */
 
 /* Output assembly language code for the function prologue.  */
@@ -295,7 +526,7 @@ h8300_output_function_prologue (file, size)
      HOST_WIDE_INT size;
 {
   int fsize = round_frame_size (size);
-  int idx;
+  int regno;
   int saved_regs;
   int n_regs;
 
@@ -361,14 +592,12 @@ h8300_output_function_prologue (file, size)
     }
 
   /* Leave room for locals.  */
-  dosize (file, "sub", fsize);
+  dosize (file, -1, fsize);
 
   /* Push the rest of the registers in ascending order.  */
   saved_regs = compute_saved_regs ();
-  for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx += n_regs)
+  for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs)
     {
-      int regno = idx;
-
       n_regs = 1;
       if (saved_regs & (1 << regno))
        {
@@ -388,12 +617,29 @@ h8300_output_function_prologue (file, size)
                n_regs = 2;
            }
 
-         if (n_regs == 1)
-           push (file, regno);
-         else
-           fprintf (file, "\tstm.l\t%s-%s,@-sp\n",
-                    h8_reg_names[regno],
-                    h8_reg_names[regno + (n_regs - 1)]);
+         switch (n_regs)
+           {
+           case 1:
+             push (file, regno);
+             break;
+           case 2:
+             fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
+                      h8_reg_names[regno],
+                      h8_reg_names[regno + 1]);
+             break;
+           case 3:
+             fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
+                      h8_reg_names[regno],
+                      h8_reg_names[regno + 2]);
+             break;
+           case 4:
+             fprintf (file, "\tstm.l\t%s-%s,@-er7\n",
+                      h8_reg_names[regno],
+                      h8_reg_names[regno + 3]);
+             break;
+           default:
+             abort ();
+           }
        }
     }
 }
@@ -406,7 +652,7 @@ h8300_output_function_epilogue (file, size)
      HOST_WIDE_INT size;
 {
   int fsize = round_frame_size (size);
-  int idx;
+  int regno;
   rtx insn = get_last_insn ();
   int saved_regs;
   int n_regs;
@@ -433,10 +679,8 @@ h8300_output_function_epilogue (file, size)
 
   /* Pop the saved registers in descending order.  */
   saved_regs = compute_saved_regs ();
-  for (idx = 0; idx < FIRST_PSEUDO_REGISTER; idx += n_regs)
+  for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno -= n_regs)
     {
-      int regno = (FIRST_PSEUDO_REGISTER - 1) - idx;
-
       n_regs = 1;
       if (saved_regs & (1 << regno))
        {
@@ -456,17 +700,34 @@ h8300_output_function_epilogue (file, size)
                n_regs = 2;
            }
 
-         if (n_regs == 1)
-           pop (file, regno);
-         else
-           fprintf (file, "\tldm.l\t@sp+,%s-%s\n",
-                    h8_reg_names[regno - (n_regs - 1)],
-                    h8_reg_names[regno]);
+         switch (n_regs)
+           {
+           case 1:
+             pop (file, regno);
+             break;
+           case 2:
+             fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
+                      h8_reg_names[regno - 1],
+                      h8_reg_names[regno]);
+             break;
+           case 3:
+             fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
+                      h8_reg_names[regno - 2],
+                      h8_reg_names[regno]);
+             break;
+           case 4:
+             fprintf (file, "\tldm.l\t@er7+,%s-%s\n",
+                      h8_reg_names[regno - 3],
+                      h8_reg_names[regno]);
+             break;
+           default:
+             abort ();
+           }
        }
     }
 
   /* Deallocate locals.  */
-  dosize (file, "add", fsize);
+  dosize (file, 1, fsize);
 
   /* Pop frame pointer if we had one.  */
   if (frame_pointer_needed)
@@ -492,7 +753,10 @@ asm_file_start (file)
 {
   fprintf (file, ";\tGCC For the Hitachi H8/300\n");
   fprintf (file, ";\tBy Hitachi America Ltd and Cygnus Support\n");
-  if (optimize)
+
+  if (optimize_size)
+    fprintf (file, "; -Os\n");
+  else if (optimize)
     fprintf (file, "; -O%d\n", optimize);
   if (TARGET_H8300H)
     fprintf (file, "\n\t.h8300h\n");
@@ -512,28 +776,6 @@ asm_file_end (file)
   fprintf (file, "\t.end\n");
 }
 \f
-/* Return true if VALUE is a valid constant for constraint 'P'.
-   IE: VALUE is a power of two <= 2**15.  */
-
-int
-small_power_of_two (value)
-     HOST_WIDE_INT value;
-{
-  int power = exact_log2 (value);
-  return power >= 0 && power <= 15;
-}
-
-/* Return true if VALUE is a valid constant for constraint 'O', which
-   means that the constant would be ok to use as a bit for a bclr
-   instruction.  */
-
-int
-ok_for_bclr (value)
-     HOST_WIDE_INT value;
-{
-  return small_power_of_two ((~value) & 0xff);
-}
-
 /* Return true if OP is a valid source operand for an integer move
    instruction.  */
 
@@ -560,26 +802,54 @@ general_operand_dst (op, mode)
   return general_operand (op, mode);
 }
 
-/* Return true if OP is a const valid for a bit clear instruction.  */
+/* Return true if OP is a constant that contains only one 1 in its
+   binary representation.  */
 
 int
-o_operand (operand, mode)
+single_one_operand (operand, mode)
      rtx operand;
      enum machine_mode mode ATTRIBUTE_UNUSED;
 {
-  return (GET_CODE (operand) == CONST_INT
-         && CONST_OK_FOR_O (INTVAL (operand)));
+  if (GET_CODE (operand) == CONST_INT)
+    {
+      /* We really need to do this masking because 0x80 in QImode is
+        represented as -128 for example.  */
+      unsigned HOST_WIDE_INT mask =
+       (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
+       ? ((unsigned HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1
+       : ~(unsigned HOST_WIDE_INT) 0;
+      unsigned HOST_WIDE_INT value = INTVAL (operand);
+
+      if (exact_log2 (value & mask) >= 0)
+       return 1;
+    }
+
+  return 0;
 }
 
-/* Return true if OP is a const valid for a bit set or bit xor instruction.  */
+/* Return true if OP is a constant that contains only one 0 in its
+   binary representation.  */
 
 int
-p_operand (operand, mode)
+single_zero_operand (operand, mode)
      rtx operand;
      enum machine_mode mode ATTRIBUTE_UNUSED;
 {
-  return (GET_CODE (operand) == CONST_INT
-         && CONST_OK_FOR_P (INTVAL (operand)));
+  if (GET_CODE (operand) == CONST_INT)
+    {
+      /* We really need to do this masking because 0x80 in QImode is
+        represented as -128 for example.  */
+      unsigned HOST_WIDE_INT mask =
+       (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
+       ? ((unsigned HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1
+       : ~(unsigned HOST_WIDE_INT) 0;
+      unsigned HOST_WIDE_INT value = INTVAL (operand);
+
+      if (exact_log2 (~value & mask) >= 0)
+       return 1;
+    }
+
+  return 0;
 }
 
 /* Return true if OP is a valid call operand.  */
@@ -632,12 +902,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;
@@ -729,7 +998,7 @@ jump_address_operand (op, mode)
   return 0;
 }
 
-/* Recognize valid operands for bitfield instructions.  */
+/* Recognize valid operands for bit-field instructions.  */
 
 extern int rtx_equal_function_value_matters;
 
@@ -794,26 +1063,6 @@ h8300_pr_saveall (pfile)
   pragma_saveall = 1;
 }
 
-static const char *const hand_list[] =
-{
-  "__main",
-  "__cmpsi2",
-  "__divhi3",
-  "__modhi3",
-  "__udivhi3",
-  "__umodhi3",
-  "__divsi3",
-  "__modsi3",
-  "__udivsi3",
-  "__umodsi3",
-  "__mulhi3",
-  "__mulsi3",
-  "__reg_memcpy",
-  "__reg_memset",
-  "__ucmpsi2",
-  0,
-};
-
 /* 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.
@@ -826,6 +1075,25 @@ function_arg (cum, mode, type, named)
      tree type;
      int named;
 {
+  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;
@@ -873,9 +1141,10 @@ function_arg (cum, mode, type, named)
 /* Return the cost of the rtx R with code CODE.  */
 
 int
-const_costs (r, c)
+const_costs (r, c, outer_code)
      rtx r;
      enum rtx_code c;
+     enum rtx_code outer_code;
 {
   switch (c)
     {
@@ -883,15 +1152,16 @@ const_costs (r, c)
       switch (INTVAL (r))
        {
        case 0:
+         return 0;
        case 1:
        case 2:
        case -1:
        case -2:
-         return 0;
+         return 0 + (outer_code == SET);
        case 4:
        case -4:
          if (TARGET_H8300H || TARGET_H8300S)
-           return 0;
+           return 0 + (outer_code == SET);
          else
            return 1;
        default:
@@ -910,6 +1180,44 @@ const_costs (r, c)
       return 4;
     }
 }
+
+int
+h8300_and_costs (x)
+     rtx x;
+{
+  rtx operands[4];
+
+  if (GET_MODE (x) == QImode)
+    return 1;
+
+  if (GET_MODE (x) != HImode
+      && GET_MODE (x) != SImode)
+    return 100;
+
+  operands[0] = NULL;
+  operands[1] = NULL;
+  operands[2] = XEXP (x, 1);
+  operands[3] = x;
+  return compute_logical_op_length (GET_MODE (x), operands);
+}
+
+int
+h8300_shift_costs (x)
+     rtx x;
+{
+  rtx operands[4];
+
+  if (GET_MODE (x) != QImode
+      && GET_MODE (x) != HImode
+      && GET_MODE (x) != SImode)
+    return 100;
+
+  operands[0] = NULL;
+  operands[1] = NULL;
+  operands[2] = XEXP (x, 1);
+  operands[3] = x;
+  return compute_a_shift_length (NULL, operands);
+}
 \f
 /* Documentation for the machine specific operand escapes:
 
@@ -1032,16 +1340,16 @@ print_operand (file, x, code)
        goto def;
       break;
     case 'V':
-      bitint = exact_log2 (INTVAL (x));
+      bitint = exact_log2 (INTVAL (x) & 0xff);
       if (bitint == -1)
        abort ();
-      fprintf (file, "#%d", bitint & 7);
+      fprintf (file, "#%d", bitint);
       break;
     case 'W':
       bitint = exact_log2 ((~INTVAL (x)) & 0xff);
       if (bitint == -1)
        abort ();
-      fprintf (file, "#%d", bitint & 7);
+      fprintf (file, "#%d", bitint);
       break;
     case 'R':
     case 'X':
@@ -1138,10 +1446,10 @@ print_operand (file, x, code)
        }
       break;
     case 'j':
-      asm_fprintf (file, cond_string (GET_CODE (x)));
+      fputs (cond_string (GET_CODE (x)), file);
       break;
     case 'k':
-      asm_fprintf (file, cond_string (reverse_condition (GET_CODE (x))));
+      fputs (cond_string (reverse_condition (GET_CODE (x))), file);
       break;
     case 's':
       if (GET_CODE (x) == CONST_INT)
@@ -1214,22 +1522,39 @@ print_operand (file, x, code)
          break;
 
        case MEM:
-         fprintf (file, "@");
-         output_address (XEXP (x, 0));
-
-         /* If this is an 'R' operand (reference into the 8-bit
-            area), then specify a symbolic address as "foo:8",
-            otherwise if operand is still in eight bit section, use
-            "foo:16".  */
-         if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
-             && SYMBOL_REF_FLAG (XEXP (x, 0)))
-           fprintf (file, (code == 'R' ? ":8" : ":16"));
-         else if (GET_CODE (XEXP (x, 0)) == SYMBOL_REF
-                  && TINY_DATA_NAME_P (XSTR (XEXP (x, 0), 0)))
-           fprintf (file, ":16");
-         else if ((code == 'R')
-                  && EIGHTBIT_CONSTANT_ADDRESS_P (XEXP (x, 0)))
-           fprintf (file, ":8");
+         {
+           rtx addr = XEXP (x, 0);
+
+           fprintf (file, "@");
+           output_address (addr);
+
+           /* We fall back from smaller addressing to larger
+              addressing in various ways depending on CODE.  */
+           switch (code)
+             {
+             case 'R':
+               /* Used for mov.b and bit operations.  */
+               if (h8300_eightbit_constant_address_p (addr))
+                 {
+                   fprintf (file, ":8");
+                   break;
+                 }
+
+               /* Fall through.  We should not get here if we are
+                  processing bit operations on H8/300 or H8/300H
+                  because 'U' constraint does not allow bit
+                  operations on the tiny area on these machines.  */
+
+             case 'T':
+             case 'S':
+               /* Used for mov.w and mov.l.  */
+               if (h8300_tiny_constant_address_p (addr))
+                 fprintf (file, ":16");
+               break;
+             default:
+               break;
+             }
+         }
          break;
 
        case CONST_INT:
@@ -1373,7 +1698,7 @@ do_movsi (operands)
    the other its replacement, at the start of a routine.  */
 
 int
-initial_offset (from, to)
+h8300_initial_elimination_offset (from, to)
      int from, to;
 {
   int offset = 0;
@@ -1393,12 +1718,15 @@ initial_offset (from, to)
       /* See the comments for get_frame_size.  We need to round it up to
         STACK_BOUNDARY.  */
 
-      offset += ((get_frame_size () + STACK_BOUNDARY / BITS_PER_UNIT - 1)
-                & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
+      offset += round_frame_size (get_frame_size ());
 
       if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
        offset += UNITS_PER_WORD;       /* Skip saved PC */
     }
+
+  if ((TARGET_H8300H || TARGET_H8300S) && TARGET_NORMAL_MODE)
+    offset -= 2;
+
   return offset;
 }
 
@@ -1440,6 +1768,9 @@ notice_update_cc (body, insn)
       if (cc_status.value1 != 0
          && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1))
        cc_status.value1 = 0;
+      if (cc_status.value2 != 0
+         && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2))
+       cc_status.value2 = 0;
       break;
 
     case CC_SET_ZN:
@@ -1458,6 +1789,8 @@ notice_update_cc (body, insn)
       CC_STATUS_INIT;
       cc_status.flags |= CC_NO_CARRY;
       cc_status.value1 = recog_data.operand[0];
+      if (GET_CODE (body) == SET && REG_P (SET_SRC (body)))
+       cc_status.value2 = SET_SRC (body);
       break;
 
     case CC_COMPARE:
@@ -1488,11 +1821,12 @@ bit_operator (x, mode)
 }
 \f
 const char *
-output_logical_op (mode, code, operands)
+output_logical_op (mode, operands)
      enum machine_mode mode;
-     int code;
      rtx *operands;
 {
+  /* Figure out the logical op that we need to perform.  */
+  enum rtx_code code = GET_CODE (operands[3]);
   /* Pretend that every byte is affected if both operands are registers.  */
   unsigned HOST_WIDE_INT intval =
     (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
@@ -1569,9 +1903,8 @@ 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" : "not.w\t%f0",
                             operands);
@@ -1596,9 +1929,8 @@ 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" : "not.w\t%e0",
                             operands);
@@ -1630,15 +1962,176 @@ output_logical_op (mode, code, operands)
     }
   return "";
 }
-\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.
 
-   All shift code falls into one of the following ways of
-   implementation:
+unsigned int
+compute_logical_op_length (mode, operands)
+     enum machine_mode mode;
+     rtx *operands;
+{
+  /* Figure out the logical op that we need to perform.  */
+  enum rtx_code code = GET_CODE (operands[3]);
+  /* Pretend that every byte is affected if both operands are registers.  */
+  unsigned HOST_WIDE_INT intval =
+    (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+                             ? INTVAL (operands[2]) : 0x55555555);
+  /* The determinant of the algorithm.  If we perform an AND, 0
+     affects a bit.  Otherwise, 1 affects a bit.  */
+  unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
+  /* Insn length.  */
+  unsigned int length = 0;
+
+  switch (mode)
+    {
+    case HImode:
+      /* First, see if we can finish with one insn.  */
+      if ((TARGET_H8300H || TARGET_H8300S)
+         && ((det & 0x00ff) != 0)
+         && ((det & 0xff00) != 0))
+       {
+         if (REG_P (operands[2]))
+           length += 2;
+         else
+           length += 4;
+       }
+      else
+       {
+         /* Take care of the lower byte.  */
+         if ((det & 0x00ff) != 0)
+           length += 2;
+
+         /* Take care of the upper byte.  */
+         if ((det & 0xff00) != 0)
+           length += 2;
+       }
+      break;
+    case SImode:
+      /* 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 not.w
+        can do a better job.  */
+      if ((TARGET_H8300H || TARGET_H8300S)
+         && ((det & 0x0000ffff) != 0)
+         && ((det & 0xffff0000) != 0)
+         && (code == IOR || det != 0xffffff00)
+         && (code == IOR || det != 0xffff00ff))
+       {
+         if (REG_P (operands[2]))
+           length += 4;
+         else
+           length += 6;
+       }
+      else
+       {
+         /* Take care of the lower and upper words individually.  For
+            each word, we try different methods in the order of
+
+            1) the special insn (in case of AND or XOR),
+            2) the word-wise insn, and
+            3) The byte-wise insn.  */
+         if ((det & 0x0000ffff) == 0x0000ffff
+             && (TARGET_H8300 ? (code == AND) : (code != IOR)))
+           {
+             length += 2;
+           }
+         else if ((TARGET_H8300H || TARGET_H8300S)
+                  && ((det & 0x000000ff) != 0)
+                  && ((det & 0x0000ff00) != 0))
+           {
+             length += 4;
+           }
+         else
+           {
+             if ((det & 0x000000ff) != 0)
+               length += 2;
+
+             if ((det & 0x0000ff00) != 0)
+               length += 2;
+           }
+
+         if ((det & 0xffff0000) == 0xffff0000
+             && (TARGET_H8300 ? (code == AND) : (code != IOR)))
+           {
+             length += 2;
+           }
+         else if (TARGET_H8300H || TARGET_H8300S)
+           {
+             if ((det & 0xffff0000) != 0)
+               length += 4;
+           }
+         else
+           {
+             if ((det & 0x00ff0000) != 0)
+               length += 2;
+
+             if ((det & 0xff000000) != 0)
+               length += 2;
+           }
+       }
+      break;
+    default:
+      abort ();
+    }
+  return length;
+}
+
+int
+compute_logical_op_cc (mode, operands)
+     enum machine_mode mode;
+     rtx *operands;
+{
+  /* Figure out the logical op that we need to perform.  */
+  enum rtx_code code = GET_CODE (operands[3]);
+  /* Pretend that every byte is affected if both operands are registers.  */
+  unsigned HOST_WIDE_INT intval =
+    (unsigned HOST_WIDE_INT) ((GET_CODE (operands[2]) == CONST_INT)
+                             ? INTVAL (operands[2]) : 0x55555555);
+  /* The determinant of the algorithm.  If we perform an AND, 0
+     affects a bit.  Otherwise, 1 affects a bit.  */
+  unsigned HOST_WIDE_INT det = (code != AND) ? intval : ~intval;
+  /* Condition code.  */
+  enum attr_cc cc = CC_CLOBBER;
+
+  switch (mode)
+    {
+    case HImode:
+      /* First, see if we can finish with one insn.  */
+      if ((TARGET_H8300H || TARGET_H8300S)
+         && ((det & 0x00ff) != 0)
+         && ((det & 0xff00) != 0))
+       {
+         cc = CC_SET_ZNV;
+       }
+      break;
+    case SImode:
+      /* 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 not.w
+        can do a better job.  */
+      if ((TARGET_H8300H || TARGET_H8300S)
+         && ((det & 0x0000ffff) != 0)
+         && ((det & 0xffff0000) != 0)
+         && (code == IOR || det != 0xffffff00)
+         && (code == IOR || det != 0xffff00ff))
+       {
+         cc = CC_SET_ZNV;
+       }
+      break;
+    default:
+      abort ();
+    }
+  return cc;
+}
+\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 H8S.
+
+   All shift code falls into one of the following ways of
+   implementation:
 
    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
@@ -1654,98 +2147,18 @@ output_logical_op (mode, code, operands)
      16.  This case also includes other oddballs that are not worth
      explaning here.
 
-   o SHIFT_LOOP: Emit a loop using one (or two on H8/S) bit shifts.
-
-   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.
-
-   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
-   7      - ASHIFTRT: shll, subx (propagate carry bit to all bits)
-
-   H8/300 HImode shifts
-   7      - shift 2nd half other way into carry.
-           copy 1st half into 2nd half
-           rotate 2nd half other way with carry
-           rotate 1st half other way (no carry)
-           mask off bits in 1st half (ASHIFT | LSHIFTRT).
-           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
-   15     - ASHIFTRT: shll, subx, set other byte
-
-   H8/300 SImode shifts
-   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
-   15*    - shift other way once, move word into place, move carry into place
-   16     - move word, zero or sign extend other
-   24*    - move bytes into place, zero or sign extend other
-   31     - ASHIFTRT: shll top byte, subx, copy to other bytes
-
-   H8/300H QImode shifts (same as H8/300 QImode shifts)
-   7      - ASHIFTRT: shll, subx (propagate carry bit to all bits)
-
-   H8/300H HImode shifts
-   7      - shift 2nd half other way into carry.
-           copy 1st half into 2nd half
-           rotate entire word other way using carry
-           mask off remaining bits  (ASHIFT | LSHIFTRT)
-           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
-   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)
-   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
-   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,
-                      zero word 1, zero byte 1
-            ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
-                      sign extend byte 0, sign extend word 0
-   25-27* - either loop, or
-            do 24 bit shift, inline rest
-   31     - shll, subx byte 0, sign extend byte 0, sign extend word 0
-
-   H8/S QImode shifts
-   7      - ASHIFTRT: shll, subx (propagate carry bit to all bits)
-
-   H8/S HImode shifts
-   8      - move byte, zero (ASHIFT | LSHIFTRT) or sign extend other (ASHIFTRT)
-   9-12   - do shift by 8, inline remaining shifts
-   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)
-   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
-   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,
-                      zero word 1, zero byte 1
-            ASHIFTRT: move word 1 to word 0, move byte 1 to byte 0,
-                      sign extend byte 0, sign extend word 0
-   25-27* - either loop, or
-            do 24 bit shift, inline rest
-   31     - shll, subx byte 0, sign extend byte 0, sign extend word 0
-
-   Panic!!!  */
+   o SHIFT_LOOP: Emit a loop using one (or two on H8S) bit shifts.
+
+   For each shift count, we try to use code that has no trade-off
+   between code size and speed whenever possible.
+
+   If the trade-off is unavoidable, we try to be reasonable.
+   Specifically, the fastest version is one instruction longer than
+   the shortest version, we take the fastest version.  We also provide
+   the use a way to switch back to the shortest version with -Os.
+
+   For the details of the shift algorithms for various shift counts,
+   refer to shift_alg_[qhs]i.  */
 
 int
 nshift_operator (x, mode)
@@ -1791,23 +2204,6 @@ expand_a_shift (mode, code, operands)
   return 1;
 }
 
-/* See above for explanation of this enum.  */
-
-enum shift_alg
-{
-  SHIFT_INLINE,
-  SHIFT_ROT_AND,
-  SHIFT_SPECIAL,
-  SHIFT_LOOP
-};
-
-/* Symbols of the various shifts which can be used as indices.  */
-
-enum shift_type
-{
-  SHIFT_ASHIFT, SHIFT_LSHIFTRT, SHIFT_ASHIFTRT
-};
-
 /* Symbols of the various modes which can be used as indices.  */
 
 enum shift_mode
@@ -1970,142 +2366,6 @@ 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;
@@ -2118,11 +2378,11 @@ struct shift_info {
   const char *special;
 
   /* Insn for a one-bit shift.  Valid when ALG is either SHIFT_INLINE
-     or SHIFT_SPECIAL, and REMAINDER is non-zero.  */
+     or SHIFT_SPECIAL, and REMAINDER is nonzero.  */
   const char *shift1;
 
   /* Insn for a two-bit shift.  Valid when ALG is either SHIFT_INLINE
-     or SHIFT_SPECIAL, and REMAINDER is non-zero.  */
+     or SHIFT_SPECIAL, and REMAINDER is nonzero.  */
   const char *shift2;
 
   /* Valid CC flags.  */
@@ -2135,16 +2395,15 @@ static void get_shift_alg PARAMS ((enum shift_type,
 
 /* Given SHIFT_TYPE, SHIFT_MODE, and shift count COUNT, determine the
    best algorithm for doing the shift.  The assembler code is stored
-   in the pointers in INFO.  We don't achieve maximum efficiency in
-   all cases, but the hooks are here to do so.
-
-   For now we just use lots of switch statements.  Since we don't even come
-   close to supporting all the cases, this is simplest.  If this function ever
-   gets too big, perhaps resort to a more table based lookup.  Of course,
-   at this point you may just wish to do it all in rtl.
+   in the pointers in INFO.  We achieve the maximum efficiency in most
+   cases when !TARGET_H8300.  In case of TARGET_H8300, shifts in
+   SImode in particular have a lot of room to optimize.
 
-   WARNING: The constraints on insns shiftbyn_QI/HI/SI assume shifts of
-   1,2,3,4 will be inlined (1,2 for SI).  */
+   We first determine the strategy of the shift algorithm by a table
+   lookup.  If that tells us to use a hand crafted assembly code, we
+   go into the big switch statement to find what that is.  Otherwise,
+   we resort to a generic way, such as inlining.  In either case, the
+   result is returned through INFO.  */
 
 static void
 get_shift_alg (shift_type, shift_mode, count, info)
@@ -2153,37 +2412,32 @@ get_shift_alg (shift_type, shift_mode, count, info)
      unsigned int count;
      struct shift_info *info;
 {
-  int cpu;
+  enum h8_cpu cpu;
 
   /* Find the target CPU.  */
   if (TARGET_H8300)
-    cpu = 0;
+    cpu = H8_300;
   else if (TARGET_H8300H)
-    cpu = 1;
+    cpu = H8_300H;
   else
-    cpu = 2;
+    cpu = H8_S;
 
   /* Find the shift algorithm.  */
+  info->alg = SHIFT_LOOP;
   switch (shift_mode)
     {
     case QIshift:
-      if (GET_MODE_BITSIZE (QImode) <= count)
-       info->alg = SHIFT_LOOP;
-      else
+      if (count < GET_MODE_BITSIZE (QImode))
        info->alg = shift_alg_qi[cpu][shift_type][count];
       break;
 
     case HIshift:
-      if (GET_MODE_BITSIZE (HImode) <= count)
-       info->alg = SHIFT_LOOP;
-      else
+      if (count < GET_MODE_BITSIZE (HImode))
        info->alg = shift_alg_hi[cpu][shift_type][count];
       break;
 
     case SIshift:
-      if (GET_MODE_BITSIZE (SImode) <= count)
-       info->alg = SHIFT_LOOP;
-      else
+      if (count < GET_MODE_BITSIZE (SImode))
        info->alg = shift_alg_si[cpu][shift_type][count];
       break;
 
@@ -2255,7 +2509,7 @@ get_shift_alg (shift_type, shift_mode, count, info)
              goto end;
            }
        }
-      else if (8 <= count && count <= 12)
+      else if (8 <= count && count <= 13)
        {
          info->remainder = count - 8;
 
@@ -2281,16 +2535,50 @@ get_shift_alg (shift_type, shift_mode, count, info)
              goto end;
            }
        }
-      else if (count == 15 && shift_type == SHIFT_ASHIFTRT)
+      else if (count == 14)
        {
-         info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0";
-         goto end;
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             if (TARGET_H8300)
+               info->special = "mov.b\t%s0,%t0\n\trotr.b\t%t0\n\trotr.b\t%t0\n\tand.b\t#0xC0,%t0\n\tsub.b\t%s0,%s0";
+             goto end;
+           case SHIFT_LSHIFTRT:
+             if (TARGET_H8300)
+               info->special = "mov.b\t%t0,%s0\n\trotl.b\t%s0\n\trotl.b\t%s0\n\tand.b\t#3,%s0\n\tsub.b\t%t0,%t0";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             if (TARGET_H8300)
+               info->special = "mov.b\t%t0,%s0\n\tshll.b\t%s0\n\tsubx.b\t%t0,%t0\n\tshll.b\t%s0\n\tmov.b\t%t0,%s0\n\tbst.b\t#0,%s0";
+             else if (TARGET_H8300H)
+               info->special = "shll.b\t%t0\n\tsubx.b\t%s0,%s0\n\tshll.b\t%t0\n\trotxl.b\t%s0\n\texts.w\t%T0";
+             else /* TARGET_H8300S */
+               info->special = "mov.b\t%t0,%s0\n\texts.w\t%T0\n\tshar.w\t#2,%T0\n\tshar.w\t#2,%T0\n\tshar.w\t#2,%T0";
+             goto end;
+           }
+       }
+      else if (count == 15)
+       {
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             info->special = "bld\t#0,%s0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#7,%t0";
+             goto end;
+           case SHIFT_LSHIFTRT:
+             info->special = "bld\t#7,%t0\n\txor\t%s0,%s0\n\txor\t%t0,%t0\n\tbst\t#0,%s0";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             info->special = "shll\t%t0\n\tsubx\t%t0,%t0\n\tmov.b\t%t0,%s0";
+             goto end;
+           }
        }
       abort ();
 
     case SIshift:
-      if (count == 8 && TARGET_H8300)
+      if (TARGET_H8300 && 8 <= count && count <= 9)
        {
+         info->remainder = count - 8;
+
          switch (shift_type)
            {
            case SHIFT_ASHIFT:
@@ -2298,6 +2586,7 @@ get_shift_alg (shift_type, shift_mode, count, info)
              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->shift1  = "shlr\t%y0\n\trotxr\t%x0\n\trotxr\t%w0";
              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";
@@ -2319,6 +2608,20 @@ get_shift_alg (shift_type, shift_mode, count, info)
              goto end;
            }
        }
+      else if (count == 15 && TARGET_H8300)
+       {
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             abort ();
+           case SHIFT_LSHIFTRT:
+             info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\txor\t%y0,%y0\n\txor\t%z0,%z0\n\trotxl\t%w0,%w0\n\trotxl\t%x0,%x0\n\trotxl\t%y0,%y0";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             info->special = "bld\t#7,%z0\n\tmov.w\t%e0,%f0\n\trotxl\t%w0,%w0\n\trotxl\t%x0,%x0\n\tsubx\t%y0,%y0\n\tsubx\t%z0,%z0";
+             goto end;
+           }
+       }
       else if (count == 15 && !TARGET_H8300)
        {
          switch (shift_type)
@@ -2333,7 +2636,7 @@ get_shift_alg (shift_type, shift_mode, count, info)
              abort ();
            }
        }
-      else if ((TARGET_H8300 && count == 16)
+      else if ((TARGET_H8300 && 16 <= count && count <= 20)
               || (TARGET_H8300H && 16 <= count && count <= 19)
               || (TARGET_H8300S && 16 <= count && count <= 21))
        {
@@ -2343,21 +2646,60 @@ get_shift_alg (shift_type, shift_mode, count, info)
            {
            case SHIFT_ASHIFT:
              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";
+             if (TARGET_H8300)
+               {
+                 info->shift1 = "add.w\t%e0,%e0";
+               }
+             else
+               {
+                 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\tsub.w\t%e0,%e0";
-             info->shift1  = "shlr.l\t%S0";
-             info->shift2  = "shlr.l\t#2,%S0";
+             if (TARGET_H8300)
+               {
+                 info->shift1 = "shlr\t%x0\n\trotxr\t%w0";
+               }
+             else
+               {
+                 info->shift1 = "shlr.l\t%S0";
+                 info->shift2 = "shlr.l\t#2,%S0";
+               }
              goto end;
            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";
+               {
+                 info->special = "mov.w\t%e0,%f0\n\tshll\t%z0\n\tsubx\t%z0,%z0\n\tmov.b\t%z0,%y0";
+                 info->shift1  = "shar\t%x0\n\trotxr\t%w0";
+               }
              else
-               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";
+               {
+                 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 (TARGET_H8300 && 24 <= count && count <= 28)
+       {
+         info->remainder = count - 24;
+
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             info->special = "mov.b\t%w0,%z0\n\tsub.b\t%y0,%y0\n\tsub.w\t%f0,%f0";
+             info->shift1  = "shll.b\t%z0";
+             goto end;
+           case SHIFT_LSHIFTRT:
+             info->special = "mov.b\t%z0,%w0\n\tsub.b\t%x0,%x0\n\tsub.w\t%e0,%e0";
+             info->shift1  = "shlr.b\t%w0";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             info->special = "mov.b\t%z0,%w0\n\tbld\t#7,%w0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0\n\tsubx\t%x0,%x0";
+             info->shift1  = "shar.b\t%w0";
              goto end;
            }
        }
@@ -2385,6 +2727,78 @@ get_shift_alg (shift_type, shift_mode, count, info)
              goto end;
            }
        }
+      else if (!TARGET_H8300 && count == 28)
+       {
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             if (TARGET_H8300H)
+               info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
+             else
+               info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0";
+             info->shift1  = "";
+             info->shift2  = "";
+             goto end;
+           case SHIFT_LSHIFTRT:
+             if (TARGET_H8300H)
+               info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
+             else
+               info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t#2,%S0\n\tsub.w\t%e0,%e0";
+             info->shift1  = "";
+             info->shift2  = "";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             abort ();
+           }
+       }
+      else if (!TARGET_H8300 && count == 29)
+       {
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             if (TARGET_H8300H)
+               info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
+             else
+               info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
+             info->shift1  = "";
+             info->shift2  = "";
+             goto end;
+           case SHIFT_LSHIFTRT:
+             if (TARGET_H8300H)
+               info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
+             else
+               info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
+             info->shift1  = "";
+             info->shift2  = "";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             abort ();
+           }
+       }
+      else if (!TARGET_H8300 && count == 30)
+       {
+         switch (shift_type)
+           {
+           case SHIFT_ASHIFT:
+             if (TARGET_H8300H)
+               info->special = "sub.w\t%e0,%e0\n\trotr.l\t%S0\n\trotr.l\t%S0\n\tsub.w\t%f0,%f0";
+             else
+               info->special = "sub.w\t%e0,%e0\n\trotr.l\t#2,%S0\n\tsub.w\t%f0,%f0";
+             info->shift1  = "";
+             info->shift2  = "";
+             goto end;
+           case SHIFT_LSHIFTRT:
+             if (TARGET_H8300H)
+               info->special = "sub.w\t%f0,%f0\n\trotl.l\t%S0\n\trotl.l\t%S0\n\tsub.w\t%e0,%e0";
+             else
+               info->special = "sub.w\t%f0,%f0\n\trotl.l\t#2,%S0\n\tsub.w\t%e0,%e0";
+             info->shift1  = "";
+             info->shift2  = "";
+             goto end;
+           case SHIFT_ASHIFTRT:
+             abort ();
+           }
+       }
       else if (count == 31)
        {
          if (TARGET_H8300)
@@ -2424,9 +2838,61 @@ get_shift_alg (shift_type, shift_mode, count, info)
       abort ();
     }
 
- end:
-  if (!TARGET_H8300S)
-    info->shift2 = NULL;
+ end:
+  if (!TARGET_H8300S)
+    info->shift2 = NULL;
+}
+
+/* Given COUNT and MODE of a shift, return 1 if a scratch reg may be
+   needed for some shift with COUNT and MODE.  Return 0 otherwise.  */
+
+int
+h8300_shift_needs_scratch_p (count, mode)
+     int count;
+     enum machine_mode mode;
+{
+  enum h8_cpu cpu;
+  int a, lr, ar;
+
+  if (GET_MODE_BITSIZE (mode) <= count)
+    return 1;
+
+  /* Find out the target CPU.  */
+  if (TARGET_H8300)
+    cpu = H8_300;
+  else if (TARGET_H8300H)
+    cpu = H8_300H;
+  else
+    cpu = H8_S;
+
+  /* Find the shift algorithm.  */
+  switch (mode)
+    {
+    case QImode:
+      a  = shift_alg_qi[cpu][SHIFT_ASHIFT][count];
+      lr = shift_alg_qi[cpu][SHIFT_LSHIFTRT][count];
+      ar = shift_alg_qi[cpu][SHIFT_ASHIFTRT][count];
+      break;
+
+    case HImode:
+      a  = shift_alg_hi[cpu][SHIFT_ASHIFT][count];
+      lr = shift_alg_hi[cpu][SHIFT_LSHIFTRT][count];
+      ar = shift_alg_hi[cpu][SHIFT_ASHIFTRT][count];
+      break;
+
+    case SImode:
+      a  = shift_alg_si[cpu][SHIFT_ASHIFT][count];
+      lr = shift_alg_si[cpu][SHIFT_LSHIFTRT][count];
+      ar = shift_alg_si[cpu][SHIFT_ASHIFTRT][count];
+      break;
+
+    default:
+      abort ();
+    }
+
+  /* On H8/300H and H8S, count == 8 uses the scratch register.  */
+  return (a == SHIFT_LOOP || lr == SHIFT_LOOP || ar == SHIFT_LOOP
+         || (!TARGET_H8300 && mode == SImode && count == 8));
 }
 
 /* Emit the assembler code for doing shifts.  */
@@ -2560,30 +3026,28 @@ output_a_shift (operands)
              output_asm_insn (info.shift1, operands);
 
            /* Now mask off the high bits.  */
-           if (TARGET_H8300)
+           switch (mode)
              {
-               switch (mode)
+             case QImode:
+               sprintf (insn_buf, "and\t#%d,%%X0", mask);
+               cc_status.value1 = operands[0];
+               cc_status.flags |= CC_NO_CARRY;
+               break;
+             case HImode:
+               if (TARGET_H8300)
                  {
-                 case QImode:
-                   sprintf (insn_buf, "and\t#%d,%%X0", mask);
-                   cc_status.value1 = operands[0];
-                   cc_status.flags |= CC_NO_CARRY;
-                   break;
-                 case HImode:
                    sprintf (insn_buf, "and\t#%d,%%s0\n\tand\t#%d,%%t0",
                             mask & 255, mask >> 8);
-                   break;
-                 default:
-                   abort ();
                  }
-             }
-           else
-             {
-               sprintf (insn_buf, "and.%c\t#%d,%%%c0",
-                        "bwl"[shift_mode], mask,
-                        mode == QImode ? 'X' : mode == HImode ? 'T' : 'S');
-               cc_status.value1 = operands[0];
-               cc_status.flags |= CC_NO_CARRY;
+               else
+                 {
+                   sprintf (insn_buf, "and.w\t#%d,%%T0", mask);
+                   cc_status.value1 = operands[0];
+                   cc_status.flags |= CC_NO_CARRY;
+                 }
+               break;
+             default:
+               abort ();
              }
            output_asm_insn (insn_buf, operands);
            return "";
@@ -2619,6 +3083,169 @@ output_a_shift (operands)
        }
     }
 }
+
+static unsigned int
+h8300_asm_insn_count (template)
+     const char *template;
+{
+  unsigned int count = 1;
+
+  for (; *template; template++)
+    if (*template == '\n')
+      count++;
+
+  return count;
+}
+
+unsigned int
+compute_a_shift_length (insn, operands)
+     rtx insn ATTRIBUTE_UNUSED;
+     rtx *operands;
+{
+  rtx shift = operands[3];
+  enum machine_mode mode = GET_MODE (shift);
+  enum rtx_code code = GET_CODE (shift);
+  enum shift_type shift_type;
+  enum shift_mode shift_mode;
+  struct shift_info info;
+  unsigned int wlength = 0;
+
+  switch (mode)
+    {
+    case QImode:
+      shift_mode = QIshift;
+      break;
+    case HImode:
+      shift_mode = HIshift;
+      break;
+    case SImode:
+      shift_mode = SIshift;
+      break;
+    default:
+      abort ();
+    }
+
+  switch (code)
+    {
+    case ASHIFTRT:
+      shift_type = SHIFT_ASHIFTRT;
+      break;
+    case LSHIFTRT:
+      shift_type = SHIFT_LSHIFTRT;
+      break;
+    case ASHIFT:
+      shift_type = SHIFT_ASHIFT;
+      break;
+    default:
+      abort ();
+    }
+
+  if (GET_CODE (operands[2]) != CONST_INT)
+    {
+      /* Get the assembler code to do one shift.  */
+      get_shift_alg (shift_type, shift_mode, 1, &info);
+
+      return (4 + h8300_asm_insn_count (info.shift1)) * 2;
+    }
+  else
+    {
+      int n = INTVAL (operands[2]);
+
+      /* If the count is negative, make it 0.  */
+      if (n < 0)
+       n = 0;
+      /* If the count is too big, truncate it.
+         ANSI says shifts of GET_MODE_BITSIZE are undefined - we choose to
+        do the intuitive thing.  */
+      else if ((unsigned int) n > GET_MODE_BITSIZE (mode))
+       n = GET_MODE_BITSIZE (mode);
+
+      get_shift_alg (shift_type, shift_mode, n, &info);
+
+      switch (info.alg)
+       {
+       case SHIFT_SPECIAL:
+         wlength += h8300_asm_insn_count (info.special);
+
+         /* Every assembly instruction used in SHIFT_SPECIAL case
+            takes 2 bytes except xor.l, which takes 4 bytes, so if we
+            see xor.l, we just pretend that xor.l counts as two insns
+            so that the insn length will be computed correctly.  */
+         if (strstr (info.special, "xor.l") != NULL)
+           wlength++;
+
+         /* Fall through.  */
+
+       case SHIFT_INLINE:
+         n = info.remainder;
+
+         if (info.shift2 != NULL)
+           {
+             wlength += h8300_asm_insn_count (info.shift2) * (n / 2);
+             n = n % 2;
+           }
+
+         wlength += h8300_asm_insn_count (info.shift1) * n;
+
+         return 2 * wlength;
+
+       case SHIFT_ROT_AND:
+         {
+           int m = GET_MODE_BITSIZE (mode) - n;
+
+           /* Not all possibilities of rotate are supported.  They shouldn't
+              be generated, but let's watch for 'em.  */
+           if (info.shift1 == 0)
+             abort ();
+
+           if (info.shift2 != NULL)
+             {
+               wlength += h8300_asm_insn_count (info.shift2) * (m / 2);
+               m = m % 2;
+             }
+
+           wlength += h8300_asm_insn_count (info.shift1) * m;
+
+           /* Now mask off the high bits.  */
+           switch (mode)
+             {
+             case QImode:
+               wlength += 1;
+               break;
+             case HImode:
+               wlength += 2;
+               break;
+             case SImode:
+               if (TARGET_H8300)
+                 abort ();
+               wlength += 3;
+               break;
+             default:
+               abort ();
+             }
+           return 2 * wlength;
+         }
+
+       case SHIFT_LOOP:
+         /* A loop to shift by a "large" constant value.
+            If we have shift-by-2 insns, use them.  */
+         if (info.shift2 != NULL)
+           {
+             wlength += 3 + h8300_asm_insn_count (info.shift2);
+             if (n % 2)
+               wlength += h8300_asm_insn_count (info.shift1);
+           }
+         else
+           {
+             wlength += 3 + h8300_asm_insn_count (info.shift1);
+           }
+         return 2 * wlength;
+
+       default:
+         abort ();
+       }
+    }
+}
 \f
 /* A rotation by a non-constant will cause a loop to be generated, in
    which a rotation by one bit is used.  A rotation by a constant,
@@ -2663,7 +3290,7 @@ expand_a_rotate (code, operands)
       tmp = gen_rtx_PLUS (QImode, counter, GEN_INT (-1));
       emit_insn (gen_rtx_SET (VOIDmode, counter, tmp));
 
-      /* If the loop counter is non-zero, we go back to the beginning
+      /* If the loop counter is nonzero, we go back to the beginning
         of the loop.  */
       emit_cmp_and_jump_insns (counter, GEN_INT (0), NE, NULL_RTX, QImode, 1,
                               start_label);
@@ -2736,7 +3363,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) / 2U)
+  if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2)
     {
       /* Flip the direction.  */
       amount = GET_MODE_BITSIZE (mode) - amount;
@@ -2761,7 +3388,7 @@ emit_a_rotate (code, operands)
          break;
 
        case SImode:
-         /* This code works on the H8/300H and H8/S.  */
+         /* This code works on the H8/300H and H8S.  */
          insn_buf = "xor.w\t%e0,%f0\n\txor.w\t%f0,%e0\n\txor.w\t%e0,%f0";
          output_asm_insn (insn_buf, operands);
          break;
@@ -2804,32 +3431,30 @@ fix_bit_operand (operands, what, type)
      only 'U' memory afterwards, so if this is a MEM operand, we must force
      it to be valid for 'U' by reloading the address.  */
 
-  if (GET_CODE (operands[2]) == CONST_INT)
+  if ((what == 0 && single_zero_operand (operands[2], QImode))
+      || (what == 1 && single_one_operand (operands[2], QImode)))
     {
-      if (CONST_OK_FOR_LETTER_P (INTVAL (operands[2]), what))
+      /* OK to have a memory dest.  */
+      if (GET_CODE (operands[0]) == MEM
+         && !EXTRA_CONSTRAINT (operands[0], 'U'))
        {
-         /* Ok to have a memory dest.  */
-         if (GET_CODE (operands[0]) == MEM
-             && !EXTRA_CONSTRAINT (operands[0], 'U'))
-           {
-             rtx mem = gen_rtx_MEM (GET_MODE (operands[0]),
-                                    copy_to_mode_reg (Pmode,
-                                                      XEXP (operands[0], 0)));
-             MEM_COPY_ATTRIBUTES (mem, operands[0]);
-             operands[0] = mem;
-           }
+         rtx mem = gen_rtx_MEM (GET_MODE (operands[0]),
+                                copy_to_mode_reg (Pmode,
+                                                  XEXP (operands[0], 0)));
+         MEM_COPY_ATTRIBUTES (mem, operands[0]);
+         operands[0] = mem;
+       }
 
-         if (GET_CODE (operands[1]) == MEM
-             && !EXTRA_CONSTRAINT (operands[1], 'U'))
-           {
-             rtx mem = gen_rtx_MEM (GET_MODE (operands[1]),
-                                    copy_to_mode_reg (Pmode,
-                                                      XEXP (operands[1], 0)));
-             MEM_COPY_ATTRIBUTES (mem, operands[0]);
-             operands[1] = mem;
-           }
-         return 0;
+      if (GET_CODE (operands[1]) == MEM
+         && !EXTRA_CONSTRAINT (operands[1], 'U'))
+       {
+         rtx mem = gen_rtx_MEM (GET_MODE (operands[1]),
+                                copy_to_mode_reg (Pmode,
+                                                  XEXP (operands[1], 0)));
+         MEM_COPY_ATTRIBUTES (mem, operands[0]);
+         operands[1] = mem;
        }
+      return 0;
     }
 
   /* Dest and src op must be register.  */
@@ -2940,6 +3565,22 @@ h8300_tiny_data_p (decl)
   return a != NULL_TREE;
 }
 
+/* Generate an 'interrupt_handler' attribute for decls.  */
+
+static void
+h8300_insert_attributes (node, attributes)
+     tree node;
+     tree *attributes;
+{
+  if (!interrupt_handler
+      || TREE_CODE (node) != FUNCTION_DECL)
+    return;
+
+  /* Add an 'interrupt_handler' attribute.  */
+  *attributes = tree_cons (get_identifier ("interrupt_handler"),
+                          NULL, *attributes);
+}
+
 /* Supported attributes:
 
    interrupt_handler: output a prologue and epilogue suitable for an
@@ -3037,7 +3678,7 @@ h8300_handle_tiny_data_attribute (node, name, args, flags, no_add_attrs)
   return NULL_TREE;
 }
 
-void
+static void
 h8300_encode_label (decl)
      tree decl;
 {
@@ -3052,23 +3693,67 @@ h8300_encode_label (decl)
     ggc_alloc_string (newstr, len + 1);
 }
 
+/* If we are referencing a function that is supposed to be called
+   through the function vector, the SYMBOL_REF_FLAG in the rtl
+   so the call patterns can generate the correct code.  */
+
+static void
+h8300_encode_section_info (decl, first)
+     tree decl;
+     int first;
+{
+  if (TREE_CODE (decl) == FUNCTION_DECL
+      && h8300_funcvec_function_p (decl))
+    SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
+  else if (TREE_CODE (decl) == VAR_DECL
+          && (TREE_STATIC (decl) || DECL_EXTERNAL (decl)))
+    {
+      if (h8300_eightbit_data_p (decl))
+       SYMBOL_REF_FLAG (XEXP (DECL_RTL (decl), 0)) = 1;
+      else if (first && h8300_tiny_data_p (decl))
+       h8300_encode_label (decl);
+    }
+}
+
+/* Undo the effects of the above.  */
+
+static const char *
+h8300_strip_name_encoding (str)
+     const char *str;
+{
+  return str + (*str == '*' || *str == '@' || *str == '&');
+}
+
 const char *
 output_simode_bld (bild, operands)
      int bild;
      rtx operands[];
 {
-  /* Clear the destination register.  */
-  if (TARGET_H8300H || TARGET_H8300S)
-    output_asm_insn ("sub.l\t%S0,%S0", operands);
-  else
-    output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
+  if (TARGET_H8300)
+    {
+      /* Clear the destination register.  */
+      output_asm_insn ("sub.w\t%e0,%e0\n\tsub.w\t%f0,%f0", operands);
+
+      /* Now output the bit load or bit inverse load, and store it in
+        the destination.  */
+      if (bild)
+       output_asm_insn ("bild\t%Z2,%Y1", operands);
+      else
+       output_asm_insn ("bld\t%Z2,%Y1", operands);
 
-  /* Now output the bit load or bit inverse load, and store it in
-     the destination.  */
-  if (bild)
-    output_asm_insn ("bild\t%Z2,%Y1\n\tbst\t#0,%w0", operands);
+      output_asm_insn ("bst\t#0,%w0", operands);
+    }
   else
-    output_asm_insn ("bld\t%Z2,%Y1\n\tbst\t#0,%w0", operands);
+    {
+      /* Output the bit load or bit inverse load.  */
+      if (bild)
+       output_asm_insn ("bild\t%Z2,%Y1", operands);
+      else
+       output_asm_insn ("bld\t%Z2,%Y1", operands);
+
+      /* Clear the destination register and perform the bit store.  */
+      output_asm_insn ("xor.l\t%S0,%S0\n\tbst\t#0,%w0", operands);
+    }
 
   /* All done.  */
   return "";
@@ -3110,34 +3795,51 @@ h8300_adjust_insn_length (insn, length)
       else
        addr = XEXP (SET_DEST (pat), 0);
 
-      /* On the H8/300, only one adjustment is necessary; if the
-        address mode is register indirect, then this insn is two
-        bytes shorter than indicated in the machine description.  */
-      if (TARGET_H8300 && GET_CODE (addr) == REG)
-       return -2;
+      if (TARGET_H8300)
+       {
+         /* On the H8/300, we subtract the difference between the
+             actual length and the longest one, which is @(d:16,ERs).  */
 
-      /* On the H8/300H and H8/S, register indirect is 6 bytes shorter than
-        indicated in the machine description.  */
-      if ((TARGET_H8300H || TARGET_H8300S)
-          && GET_CODE (addr) == REG)
-       return -6;
+         /* @Rs is 2 bytes shorter than the longest.  */
+         if (GET_CODE (addr) == REG)
+           return -2;
 
-      /* On the H8/300H and H8/S, reg + d, for small displacements is
-        4 bytes shorter than indicated in the machine description.  */
-      if ((TARGET_H8300H || TARGET_H8300S)
-         && GET_CODE (addr) == PLUS
-         && GET_CODE (XEXP (addr, 0)) == REG
-         && GET_CODE (XEXP (addr, 1)) == CONST_INT
-         && INTVAL (XEXP (addr, 1)) > -32768
-         && INTVAL (XEXP (addr, 1)) < 32767)
-       return -4;
-
-      /* 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
-         && TINY_DATA_NAME_P (XSTR (addr, 0)))
-       return -2;
+         /* @aa:8 is 2 bytes shorter than the longest.  */
+         if (GET_MODE (SET_SRC (pat)) == QImode
+             && h8300_eightbit_constant_address_p (addr))
+           return -2;
+       }
+      else
+       {
+         /* On the H8/300H and H8S, we subtract the difference
+             between the actual length and the longest one, which is
+             @(d:24,ERs).  */
+
+         /* @ERs is 6 bytes shorter than the longest.  */
+         if (GET_CODE (addr) == REG)
+           return -6;
+
+         /* @(d:16,ERs) is 6 bytes shorter than the longest.  */
+         if (GET_CODE (addr) == PLUS
+             && GET_CODE (XEXP (addr, 0)) == REG
+             && GET_CODE (XEXP (addr, 1)) == CONST_INT
+             && INTVAL (XEXP (addr, 1)) > -32768
+             && INTVAL (XEXP (addr, 1)) < 32767)
+           return -4;
+
+         /* @aa:8 is 6 bytes shorter than the longest.  */
+         if (GET_MODE (SET_SRC (pat)) == QImode
+             && h8300_eightbit_constant_address_p (addr))
+           return -6;
+
+         /* @aa:16 is 4 bytes shorter than the longest.  */
+         if (h8300_tiny_constant_address_p (addr))
+           return -4;
+
+         /* @aa:24 is 2 bytes shorter than the longest.  */
+         if (GET_CODE (addr) == CONST_INT)
+           return -2;
+       }
     }
 
   /* Loading some constants needs adjustment.  */
@@ -3157,57 +3859,24 @@ h8300_adjust_insn_length (insn, length)
        {
          if (val == (val & 0xff)
              || val == (val & 0xff00))
-           return -6;
+           return 4 - 6;
 
-         if (val == -4 || val == -2 || val == -1)
-           return -6;
+         switch (val & 0xffffffff)
+           {
+           case 0xffffffff:
+           case 0xfffffffe:
+           case 0xfffffffc:
+           case 0x0000ffff:
+           case 0x0000fffe:
+           case 0xffff0000:
+           case 0xfffe0000:
+           case 0x00010000:
+           case 0x00020000:
+             return 4 - 6;
+           }
        }
     }
 
-  /* Shifts need various adjustments.  */
-  if (GET_CODE (pat) == PARALLEL
-      && GET_CODE (XVECEXP (pat, 0, 0)) == SET
-      && (GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == ASHIFTRT
-         || GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == LSHIFTRT
-         || GET_CODE (SET_SRC (XVECEXP (pat, 0, 0))) == ASHIFT))
-    {
-      rtx src = SET_SRC (XVECEXP (pat, 0, 0));
-      enum machine_mode mode = GET_MODE (src);
-      int shift;
-
-      if (GET_CODE (XEXP (src, 1)) != CONST_INT)
-       return 0;
-
-      shift = INTVAL (XEXP (src, 1));
-      /* According to ANSI, negative shift is undefined.  It is
-         considered to be zero in this case (see function
-         output_a_shift above).  */
-      if (shift < 0)
-       shift = 0;
-
-      /* QImode shifts by small constants take one insn
-        per shift.  So the adjustment is 20 (md length) -
-        # shifts * 2.  */
-      if (mode == QImode && shift <= 4)
-       return -(20 - shift * 2);
-
-      /* 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);
-
-      /* HImode shifts by small constants for the H8/300.  */
-      if (mode == HImode && shift <= 4)
-       return -(20 - (shift * (GET_CODE (src) == ASHIFT ? 2 : 4)));
-
-      /* SImode shifts by small constants for the H8/300.  */
-      if (mode == SImode && shift <= 2)
-       return -(20 - (shift * (GET_CODE (src) == ASHIFT ? 6 : 8)));
-
-      /* XXX ??? Could check for more shift/rotate cases here.  */
-    }
-
   /* Rotations need various adjustments.  */
   if (GET_CODE (pat) == SET
       && (GET_CODE (SET_SRC (pat)) == ROTATE
@@ -3231,7 +3900,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) / 2U)
+      if ((unsigned int) amount > GET_MODE_BITSIZE (mode) / (unsigned) 2)
        /* Flip the direction.  */
        amount = GET_MODE_BITSIZE (mode) - amount;
 
@@ -3248,7 +3917,7 @@ h8300_adjust_insn_length (insn, length)
          states += 6;
        }
 
-      /* We use 2-bit rotatations on the H8/S.  */
+      /* We use 2-bit rotatations on the H8S.  */
       if (TARGET_H8300S)
        amount = amount / 2 + amount % 2;
 
@@ -3272,3 +3941,70 @@ h8300_asm_named_section (name, flags)
   fprintf (asm_out_file, "\t.section %s\n", name);
 }
 #endif /* ! OBJECT_FORMAT_ELF */
+
+/* Nonzero if X is a constant address suitable as an 8-bit absolute,
+   which is a special case of the 'R' operand.  */
+
+int
+h8300_eightbit_constant_address_p (x)
+     rtx x;
+{
+  /* The ranges of the 8-bit area. */
+  const unsigned HOST_WIDE_INT n1 = trunc_int_for_mode (0xff00, HImode);
+  const unsigned HOST_WIDE_INT n2 = trunc_int_for_mode (0xffff, HImode);
+  const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00ffff00, SImode);
+  const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00ffffff, SImode);
+  const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0xffffff00, SImode);
+  const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0xffffffff, SImode);
+
+  unsigned HOST_WIDE_INT addr;
+
+  /* We accept symbols declared with eightbit_data.  */
+  if (GET_CODE (x) == SYMBOL_REF && SYMBOL_REF_FLAG (x))
+    return 1;
+
+  if (GET_CODE (x) != CONST_INT)
+    return 0;
+
+  addr = INTVAL (x);
+
+  return (0
+         || ((TARGET_H8300 || TARGET_NORMAL_MODE) && IN_RANGE (addr, n1, n2))
+         || (TARGET_H8300H && IN_RANGE (addr, h1, h2))
+         || (TARGET_H8300S && IN_RANGE (addr, s1, s2)));
+}
+
+/* Nonzero if X is a constant address suitable as an 16-bit absolute
+   on H8/300H and H8S.  */
+
+int
+h8300_tiny_constant_address_p (x)
+     rtx x;
+{
+  /* The ranges of the 16-bit area.  */
+  const unsigned HOST_WIDE_INT h1 = trunc_int_for_mode (0x00000000, SImode);
+  const unsigned HOST_WIDE_INT h2 = trunc_int_for_mode (0x00007fff, SImode);
+  const unsigned HOST_WIDE_INT h3 = trunc_int_for_mode (0x00ff8000, SImode);
+  const unsigned HOST_WIDE_INT h4 = trunc_int_for_mode (0x00ffffff, SImode);
+  const unsigned HOST_WIDE_INT s1 = trunc_int_for_mode (0x00000000, SImode);
+  const unsigned HOST_WIDE_INT s2 = trunc_int_for_mode (0x00007fff, SImode);
+  const unsigned HOST_WIDE_INT s3 = trunc_int_for_mode (0xffff8000, SImode);
+  const unsigned HOST_WIDE_INT s4 = trunc_int_for_mode (0xffffffff, SImode);
+
+  unsigned HOST_WIDE_INT addr;
+
+  /* We accept symbols declared with tiny_data.  */
+  if (GET_CODE (x) == SYMBOL_REF && TINY_DATA_NAME_P (XSTR (x, 0)))
+    return 1;
+
+  if (GET_CODE (x) != CONST_INT)
+    return 0;
+
+  addr = INTVAL (x);
+
+  return (0
+         || ((TARGET_H8300H && !TARGET_NORMAL_MODE)
+             && (IN_RANGE (addr, h1, h2) || IN_RANGE (addr, h3, h4)))
+         || ((TARGET_H8300S && !TARGET_NORMAL_MODE)
+             && (IN_RANGE (addr, s1, s2) || IN_RANGE (addr, s3, s4))));
+}