OSDN Git Service

Add -mabi=n32 support.
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index aff1861..be27f85 100644 (file)
@@ -1,6 +1,6 @@
 /* Medium-level subroutines: convert bit-field store and extract
    and shifts, multiplies and divides to rtl instructions.
-   Copyright (C) 1987, 1988, 1989, 1992, 1993 Free Software Foundation, Inc.
+   Copyright (C) 1987, 88, 89, 92-5, 1996 Free Software Foundation, Inc.
 
 This file is part of GNU CC.
 
@@ -16,7 +16,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
+the Free Software Foundation, 59 Temple Place - Suite 330,
+Boston, MA 02111-1307, USA.  */
 
 
 #include "config.h"
@@ -30,12 +31,15 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
 #include "real.h"
 #include "recog.h"
 
-static rtx extract_split_bit_field ();
-static rtx extract_fixed_bit_field ();
-static void store_split_bit_field ();
-static void store_fixed_bit_field ();
-static rtx mask_rtx ();
-static rtx lshift_value ();
+static void store_fixed_bit_field      PROTO((rtx, int, int, int, rtx, int));
+static void store_split_bit_field      PROTO((rtx, int, int, rtx, int));
+static rtx extract_fixed_bit_field     PROTO((enum machine_mode, rtx, int,
+                                              int, int, rtx, int, int));
+static rtx mask_rtx                    PROTO((enum machine_mode, int,
+                                              int, int));
+static rtx lshift_value                        PROTO((enum machine_mode, rtx,
+                                              int, int));
+static rtx extract_split_bit_field     PROTO((rtx, int, int, int, int));
 
 #define CEIL(x,y) (((x) + (y) - 1) / (y))
 
@@ -58,11 +62,16 @@ static int sdiv_pow2_cheap, smod_pow2_cheap;
 #define MAX_BITS_PER_WORD BITS_PER_WORD
 #endif
 
-/* Cost of various pieces of RTL.  */
+/* Cost of various pieces of RTL.  Note that some of these are indexed by shift count,
+   and some by mode.  */
 static int add_cost, negate_cost, zero_cost;
 static int shift_cost[MAX_BITS_PER_WORD];
 static int shiftadd_cost[MAX_BITS_PER_WORD];
 static int shiftsub_cost[MAX_BITS_PER_WORD];
+static int mul_cost[NUM_MACHINE_MODES];
+static int div_cost[NUM_MACHINE_MODES];
+static int mul_widen_cost[NUM_MACHINE_MODES];
+static int mul_highpart_cost[NUM_MACHINE_MODES];
 
 void
 init_expmed ()
@@ -74,6 +83,7 @@ init_expmed ()
   rtx shift_insn, shiftadd_insn, shiftsub_insn;
   int dummy;
   int m;
+  enum machine_mode mode, wider_mode;
 
   start_sequence ();
 
@@ -135,6 +145,32 @@ init_expmed ()
     = (rtx_cost (gen_rtx (MOD, word_mode, reg, GEN_INT (32)), SET)
        <= 2 * add_cost);
 
+  for (mode = GET_CLASS_NARROWEST_MODE (MODE_INT);
+       mode != VOIDmode;
+       mode = GET_MODE_WIDER_MODE (mode))
+    {
+      reg = gen_rtx (REG, mode, 10000);
+      div_cost[(int) mode] = rtx_cost (gen_rtx (UDIV, mode, reg, reg), SET);
+      mul_cost[(int) mode] = rtx_cost (gen_rtx (MULT, mode, reg, reg), SET);
+      wider_mode = GET_MODE_WIDER_MODE (mode);
+      if (wider_mode != VOIDmode)
+       {
+         mul_widen_cost[(int) wider_mode]
+           = rtx_cost (gen_rtx (MULT, wider_mode,
+                                gen_rtx (ZERO_EXTEND, wider_mode, reg),
+                                gen_rtx (ZERO_EXTEND, wider_mode, reg)),
+                       SET);
+         mul_highpart_cost[(int) mode]
+           = rtx_cost (gen_rtx (TRUNCATE, mode,
+                                gen_rtx (LSHIFTRT, wider_mode,
+                                         gen_rtx (MULT, wider_mode,
+                                                  gen_rtx (ZERO_EXTEND, wider_mode, reg),
+                                                  gen_rtx (ZERO_EXTEND, wider_mode, reg)),
+                                         GEN_INT (GET_MODE_BITSIZE (mode)))),
+                       SET);
+       }
+    }
+
   /* Free the objects we just allocated.  */
   end_sequence ();
   obfree (free_point);
@@ -149,21 +185,12 @@ negate_rtx (mode, x)
      enum machine_mode mode;
      rtx x;
 {
-  if (GET_CODE (x) == CONST_INT)
-    {
-      HOST_WIDE_INT val = - INTVAL (x);
-      if (GET_MODE_BITSIZE (mode) < HOST_BITS_PER_WIDE_INT)
-       {
-         /* Sign extend the value from the bits that are significant.  */
-         if (val & ((HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))
-           val |= (HOST_WIDE_INT) (-1) << GET_MODE_BITSIZE (mode);
-         else
-           val &= ((HOST_WIDE_INT) 1 << GET_MODE_BITSIZE (mode)) - 1;
-       }
-      return GEN_INT (val);
-    }
-  else
-    return expand_unop (GET_MODE (x), neg_optab, x, NULL_RTX, 0);
+  rtx result = simplify_unary_operation (NEG, mode, x, mode);
+
+  if (result == 0)
+    result = expand_unop (mode, neg_optab, x, NULL_RTX, 0);
+
+  return result;
 }
 \f
 /* Generate code to store value from rtx VALUE
@@ -218,13 +245,13 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       op0 = SUBREG_REG (op0);
     }
 
-#if BYTES_BIG_ENDIAN
   /* If OP0 is a register, BITPOS must count within a word.
      But as we have it, it counts within whatever size OP0 now has.
      On a bigendian machine, these are not the same, so convert.  */
-  if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
+  if (BYTES_BIG_ENDIAN
+      && GET_CODE (op0) != MEM
+      && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
     bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
-#endif
 
   value = protect_from_queue (value, 0);
 
@@ -258,11 +285,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
      can be done with a movestrict instruction.  */
 
   if (GET_CODE (op0) != MEM
-#if BYTES_BIG_ENDIAN
-      && bitpos + bitsize == unit
-#else
-      && bitpos == 0
-#endif
+      && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0)
       && bitsize == GET_MODE_BITSIZE (fieldmode)
       && (GET_MODE (op0) == fieldmode
          || (movstrict_optab->handlers[(int) fieldmode].insn_code
@@ -296,7 +319,10 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       /* Here we transfer the words of the field
         in the order least significant first.
         This is because the most significant word is the one which may
-        be less than full.  */
+        be less than full.
+        However, only do that if the value is not BLKmode.  */
+
+      int backwards = WORDS_BIG_ENDIAN && fieldmode != BLKmode;
 
       int nwords = (bitsize + (BITS_PER_WORD - 1)) / BITS_PER_WORD;
       int i;
@@ -312,8 +338,8 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
        {
          /* If I is 0, use the low-order word in both field and target;
             if I is 1, use the next to lowest word; and so on.  */
-         int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
-         int bit_offset = (WORDS_BIG_ENDIAN
+         int wordnum = (backwards ? nwords - i - 1 : i);
+         int bit_offset = (backwards
                            ? MAX (bitsize - (i + 1) * BITS_PER_WORD, 0)
                            : i * BITS_PER_WORD);
          store_bit_field (op0, MIN (BITS_PER_WORD,
@@ -347,15 +373,30 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       op0 = protect_from_queue (op0, 1);
     }
 
+  /* If VALUE is a floating-point mode, access it as an integer of the
+     corresponding size.  This can occur on a machine with 64 bit registers
+     that uses SFmode for float.  This can also occur for unaligned float
+     structure fields.  */
+  if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT)
+    {
+      if (GET_CODE (value) != REG)
+       value = copy_to_reg (value);
+      value = gen_rtx (SUBREG, word_mode, value, 0);
+    }
+
   /* Now OFFSET is nonzero only if OP0 is memory
      and is therefore always measured in bytes.  */
 
 #ifdef HAVE_insv
   if (HAVE_insv
+      && GET_MODE (value) != BLKmode
       && !(bitsize == 1 && GET_CODE (value) == CONST_INT)
       /* Ensure insv's size is wide enough for this field.  */
       && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3])
-         >= bitsize))
+         >= bitsize)
+      && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+           && (bitsize + bitpos
+               > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_insv][3]))))
     {
       int xbitpos = bitpos;
       rtx value1;
@@ -393,7 +434,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
            bestmode = GET_MODE (op0);
 
          if (bestmode == VOIDmode
-             || (STRICT_ALIGNMENT && GET_MODE_SIZE (bestmode) > align))
+             || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align))
            goto insv_loses;
 
          /* Adjust address to point to the containing unit of that mode.  */
@@ -421,22 +462,23 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       /* If xop0 is a register, we need it in MAXMODE
         to make it acceptable to the format of insv.  */
       if (GET_CODE (xop0) == SUBREG)
-       PUT_MODE (xop0, maxmode);
+       /* We can't just change the mode, because this might clobber op0,
+          and we will need the original value of op0 if insv fails.  */
+       xop0 = gen_rtx (SUBREG, maxmode, SUBREG_REG (xop0), SUBREG_WORD (xop0));
       if (GET_CODE (xop0) == REG && GET_MODE (xop0) != maxmode)
        xop0 = gen_rtx (SUBREG, maxmode, xop0, 0);
 
       /* On big-endian machines, we count bits from the most significant.
         If the bit field insn does not, we must invert.  */
 
-#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
-      xbitpos = unit - bitsize - xbitpos;
-#endif
+      if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+       xbitpos = unit - bitsize - xbitpos;
+
       /* We have been counting XBITPOS within UNIT.
         Count instead within the size of the register.  */
-#if BITS_BIG_ENDIAN
-      if (GET_CODE (xop0) != MEM)
+      if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM)
        xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
-#endif
+
       unit = GET_MODE_BITSIZE (maxmode);
 
       /* Convert VALUE to maxmode (which insv insn wants) in VALUE1.  */
@@ -556,6 +598,16 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
 
       total_bits = GET_MODE_BITSIZE (mode);
 
+      /* Make sure bitpos is valid for the chosen mode.  Adjust BITPOS to
+        be be in the range 0 to total_bits-1, and put any excess bytes in
+        OFFSET.  */
+      if (bitpos >= total_bits)
+       {
+         offset += (bitpos / total_bits) * (total_bits / BITS_PER_UNIT);
+         bitpos -= ((bitpos / total_bits) * (total_bits / BITS_PER_UNIT)
+                    * BITS_PER_UNIT);
+       }
+
       /* Get ref to an aligned byte, halfword, or word containing the field.
         Adjust BITPOS to be position within a word,
         and OFFSET to be the offset of that word.
@@ -574,13 +626,12 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
      BITPOS is the starting bit number within OP0.
      (OP0's mode may actually be narrower than MODE.)  */
 
-#if BYTES_BIG_ENDIAN
-  /* BITPOS is the distance between our msb
-     and that of the containing datum.
-     Convert it to the distance from the lsb.  */
+  if (BYTES_BIG_ENDIAN)
+      /* BITPOS is the distance between our msb
+        and that of the containing datum.
+        Convert it to the distance from the lsb.  */
+      bitpos = total_bits - bitsize - bitpos;
 
-  bitpos = total_bits - bitsize - bitpos;
-#endif
   /* Now BITPOS is always the distance between our lsb
      and that of OP0.  */
 
@@ -610,17 +661,6 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
 
       if (GET_MODE (value) != mode)
        {
-         /* If VALUE is a floating-point mode, access it as an integer
-            of the corresponding size, then convert it.  This can occur on
-            a machine with 64 bit registers that uses SFmode for float.  */
-         if (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT)
-           {
-             if (GET_CODE (value) != REG)
-               value = copy_to_reg (value);
-             value
-               = gen_rtx (SUBREG, word_mode, value, 0);
-           }
-
          if ((GET_CODE (value) == REG || GET_CODE (value) == SUBREG)
              && GET_MODE_SIZE (mode) < GET_MODE_SIZE (GET_MODE (value)))
            value = gen_lowpart (mode, value);
@@ -679,18 +719,31 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
      rtx value;
      int align;
 {
-  /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
-     much at a time.  */
-  int unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
-  rtx word;
+  int unit;
   int bitsdone = 0;
 
-  if (GET_CODE (value) == CONST_DOUBLE
-      && (word = gen_lowpart_common (word_mode, value)) != 0)
-    value = word;
+  /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
+     much at a time.  */
+  if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+    unit = BITS_PER_WORD;
+  else
+    unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
 
+  /* If VALUE is a constant other than a CONST_INT, get it into a register in
+     WORD_MODE.  If we can do this using gen_lowpart_common, do so.  Note
+     that VALUE might be a floating-point constant.  */
   if (CONSTANT_P (value) && GET_CODE (value) != CONST_INT)
-    value = copy_to_mode_reg (word_mode, value);
+    {
+      rtx word = gen_lowpart_common (word_mode, value);
+
+      if (word && (value != word))
+       value = word;
+      else
+       value = gen_lowpart_common (word_mode,
+                                   force_reg (GET_MODE (value) != VOIDmode
+                                              ? GET_MODE (value)
+                                              : word_mode, value));
+    }
 
   while (bitsdone < bitsize)
     {
@@ -708,41 +761,79 @@ store_split_bit_field (op0, bitsize, bitpos, value, align)
       thissize = MIN (bitsize - bitsdone, BITS_PER_WORD);
       thissize = MIN (thissize, unit - thispos);
 
-#if BYTES_BIG_ENDIAN
-      /* Fetch successively less significant portions.  */
-      if (GET_CODE (value) == CONST_INT)
-       part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
-                        >> (bitsize - bitsdone - thissize))
-                       & (((HOST_WIDE_INT) 1 << thissize) - 1));
-      else
-       /* The args are chosen so that the last part
-          includes the lsb.  */
-       part = extract_fixed_bit_field (word_mode, value, 0, thissize,
-                                       BITS_PER_WORD - bitsize + bitsdone,
-                                       NULL_RTX, 1, align);
-#else
-      /* Fetch successively more significant portions.  */
-      if (GET_CODE (value) == CONST_INT)
-       part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value)) >> bitsdone)
-                       & (((HOST_WIDE_INT) 1 << thissize) - 1));
+      if (BYTES_BIG_ENDIAN)
+       {
+         int total_bits;
+
+         /* We must do an endian conversion exactly the same way as it is
+            done in extract_bit_field, so that the two calls to
+            extract_fixed_bit_field will have comparable arguments.  */
+         if (GET_CODE (value) != MEM || GET_MODE (value) == BLKmode)
+           total_bits = BITS_PER_WORD;
+         else
+           total_bits = GET_MODE_BITSIZE (GET_MODE (value));
+
+         /* Fetch successively less significant portions.  */
+         if (GET_CODE (value) == CONST_INT)
+           part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
+                            >> (bitsize - bitsdone - thissize))
+                           & (((HOST_WIDE_INT) 1 << thissize) - 1));
+         else
+           /* The args are chosen so that the last part includes the
+              lsb.  Give extract_bit_field the value it needs (with
+              endianness compensation) to fetch the piece we want.
+
+              ??? We have no idea what the alignment of VALUE is, so
+              we have to use a guess.  */
+           part
+             = extract_fixed_bit_field
+               (word_mode, value, 0, thissize,
+                total_bits - bitsize + bitsdone, NULL_RTX, 1,
+                GET_MODE (value) == VOIDmode
+                ? UNITS_PER_WORD
+                : (GET_MODE (value) == BLKmode
+                   ? 1
+                   : GET_MODE_ALIGNMENT (GET_MODE (value)) / BITS_PER_UNIT));
+       }
       else
-       part = extract_fixed_bit_field (word_mode, value, 0, thissize,
-                                       bitsdone, NULL_RTX, 1, align);
-#endif
+       {
+         /* Fetch successively more significant portions.  */
+         if (GET_CODE (value) == CONST_INT)
+           part = GEN_INT (((unsigned HOST_WIDE_INT) (INTVAL (value))
+                            >> bitsdone)
+                           & (((HOST_WIDE_INT) 1 << thissize) - 1));
+         else
+           part
+             = extract_fixed_bit_field
+               (word_mode, value, 0, thissize, bitsdone, NULL_RTX, 1,
+                GET_MODE (value) == VOIDmode
+                ? UNITS_PER_WORD
+                : (GET_MODE (value) == BLKmode
+                   ? 1
+                   : GET_MODE_ALIGNMENT (GET_MODE (value)) / BITS_PER_UNIT));
+       }
 
       /* If OP0 is a register, then handle OFFSET here.
-        In the register case, UNIT must be a whole word.  */
-      if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
+
+        When handling multiword bitfields, extract_bit_field may pass
+        down a word_mode SUBREG of a larger REG for a bitfield that actually
+        crosses a word boundary.  Thus, for a SUBREG, we must find
+        the current word starting from the base register.  */
+      if (GET_CODE (op0) == SUBREG)
+       {
+         word = operand_subword_force (SUBREG_REG (op0),
+                                       SUBREG_WORD (op0) + offset,
+                                       GET_MODE (SUBREG_REG (op0)));
+         offset = 0;
+       }
+      else if (GET_CODE (op0) == REG)
        {
-         word = operand_subword (op0, offset, 1, GET_MODE (op0));
+         word = operand_subword_force (op0, offset, GET_MODE (op0));
          offset = 0;
        }
       else
        word = op0;
 
-      if (word == 0)
-       abort ();
-
       /* OFFSET is in UNITs, and UNIT is in bits.
          store_fixed_bit_field wants offset in bytes.  */
       store_fixed_bit_field (word, offset * unit / BITS_PER_UNIT,
@@ -791,9 +882,6 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
   rtx spec_target = target;
   rtx spec_target_subreg = 0;
 
-  if (GET_CODE (str_rtx) == MEM && ! MEM_IN_STRUCT_P (str_rtx))
-    abort ();
-
   /* Discount the part of the structure before the desired byte.
      We need to know how many bytes are safe to reference after it.  */
   if (total_size >= 0)
@@ -804,17 +892,35 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
     tmode = mode;
   while (GET_CODE (op0) == SUBREG)
     {
+      int outer_size = GET_MODE_BITSIZE (GET_MODE (op0));
+      int inner_size = GET_MODE_BITSIZE (GET_MODE (SUBREG_REG (op0)));
+
       offset += SUBREG_WORD (op0);
+
+      if (BYTES_BIG_ENDIAN && (outer_size < inner_size))
+       {
+         bitpos += inner_size - outer_size;
+         if (bitpos > unit)
+           {
+             offset += (bitpos / unit);
+             bitpos %= unit;
+           }
+       }
+
       op0 = SUBREG_REG (op0);
     }
+
+  /* ??? We currently assume TARGET is at least as big as BITSIZE.
+     If that's wrong, the solution is to test for it and set TARGET to 0
+     if needed.  */
   
-#if BYTES_BIG_ENDIAN
   /* If OP0 is a register, BITPOS must count within a word.
      But as we have it, it counts within whatever size OP0 now has.
      On a bigendian machine, these are not the same, so convert.  */
-  if (GET_CODE (op0) != MEM && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
+  if (BYTES_BIG_ENDIAN &&
+      GET_CODE (op0) != MEM
+      && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
     bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
-#endif
 
   /* Extracting a full-word or multi-word value
      from a structure in a register or aligned memory.
@@ -830,12 +936,9 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
           && bitpos % BITS_PER_WORD == 0)
          || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
-#if BYTES_BIG_ENDIAN
-             && bitpos + bitsize == BITS_PER_WORD
-#else
-             && bitpos == 0
-#endif
-             )))
+             && (BYTES_BIG_ENDIAN
+                 ? bitpos + bitsize == BITS_PER_WORD
+                 : bitpos == 0))))
     {
       enum machine_mode mode1
        = mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0);
@@ -868,11 +971,18 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       if (target == 0 || GET_CODE (target) != REG)
        target = gen_reg_rtx (mode);
 
+      /* Indicate for flow that the entire target reg is being set.  */
+      emit_insn (gen_rtx (CLOBBER, VOIDmode, target));
+
       for (i = 0; i < nwords; i++)
        {
          /* If I is 0, use the low-order word in both field and target;
             if I is 1, use the next to lowest word; and so on.  */
-         int wordnum = (WORDS_BIG_ENDIAN ? nwords - i - 1 : i);
+         /* Word number in TARGET to use.  */
+         int wordnum = (WORDS_BIG_ENDIAN
+                        ? GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD - i - 1
+                        : i);
+         /* Offset from start of field in OP0.  */
          int bit_offset = (WORDS_BIG_ENDIAN
                            ? MAX (0, bitsize - (i + 1) * BITS_PER_WORD)
                            : i * BITS_PER_WORD);
@@ -891,7 +1001,32 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
            emit_move_insn (target_part, result_part);
        }
 
-      return target;
+      if (unsignedp)
+       {
+         /* Unless we've filled TARGET, the upper regs in a multi-reg value
+            need to be zero'd out.  */
+         if (GET_MODE_SIZE (GET_MODE (target)) > nwords * UNITS_PER_WORD)
+           {
+             int i,total_words;
+
+             total_words = GET_MODE_SIZE (GET_MODE (target)) / UNITS_PER_WORD;
+             for (i = nwords; i < total_words; i++)
+               {
+                 int wordnum = WORDS_BIG_ENDIAN ? total_words - i - 1 : i;
+                 rtx target_part = operand_subword (target, wordnum, 1, VOIDmode);
+                 emit_move_insn (target_part, const0_rtx);
+               }
+           }
+         return target;
+       }
+
+      /* Signed bit field: sign-extend with two arithmetic shifts.  */
+      target = expand_shift (LSHIFT_EXPR, mode, target,
+                            build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
+                            NULL_RTX, 0);
+      return expand_shift (RSHIFT_EXPR, mode, target,
+                          build_int_2 (GET_MODE_BITSIZE (mode) - bitsize, 0),
+                          NULL_RTX, 0);
     }
   
   /* From here on we know the desired field is smaller than a word
@@ -922,7 +1057,10 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 #ifdef HAVE_extzv
       if (HAVE_extzv
          && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0])
-             >= bitsize))
+             >= bitsize)
+         && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+               && (bitsize + bitpos
+                   > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extzv][0]))))
        {
          int xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
@@ -964,7 +1102,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                    bestmode = GET_MODE (xop0);
 
                  if (bestmode == VOIDmode
-                     || (STRICT_ALIGNMENT && GET_MODE_SIZE (bestmode) > align))
+                     || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align))
                    goto extzv_loses;
 
                  /* Compute offset as multiple of this unit,
@@ -997,14 +1135,13 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
          /* On big-endian machines, we count bits from the most significant.
             If the bit field insn does not, we must invert.  */
-#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
-         xbitpos = unit - bitsize - xbitpos;
-#endif
+         if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+           xbitpos = unit - bitsize - xbitpos;
+
          /* Now convert from counting within UNIT to counting in MAXMODE.  */
-#if BITS_BIG_ENDIAN
-         if (GET_CODE (xop0) != MEM)
+         if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM)
            xbitpos += GET_MODE_BITSIZE (maxmode) - unit;
-#endif
+
          unit = GET_MODE_BITSIZE (maxmode);
 
          if (xtarget == 0
@@ -1061,7 +1198,10 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 #ifdef HAVE_extv
       if (HAVE_extv
          && (GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0])
-             >= bitsize))
+             >= bitsize)
+         && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+               && (bitsize + bitpos
+                   > GET_MODE_BITSIZE (insn_operand_mode[(int) CODE_FOR_extv][0]))))
        {
          int xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
@@ -1098,7 +1238,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
                    bestmode = GET_MODE (xop0);
 
                  if (bestmode == VOIDmode
-                     || (STRICT_ALIGNMENT && GET_MODE_SIZE (bestmode) > align))
+                     || (SLOW_UNALIGNED_ACCESS && GET_MODE_SIZE (bestmode) > align))
                    goto extv_loses;
 
                  /* Compute offset as multiple of this unit,
@@ -1129,15 +1269,14 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
          /* On big-endian machines, we count bits from the most significant.
             If the bit field insn does not, we must invert.  */
-#if BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN
-         xbitpos = unit - bitsize - xbitpos;
-#endif
+         if (BITS_BIG_ENDIAN != BYTES_BIG_ENDIAN)
+           xbitpos = unit - bitsize - xbitpos;
+
          /* XBITPOS counts within a size of UNIT.
             Adjust to count within a size of MAXMODE.  */
-#if BITS_BIG_ENDIAN
-         if (GET_CODE (xop0) != MEM)
+         if (BITS_BIG_ENDIAN && GET_CODE (xop0) != MEM)
            xbitpos += (GET_MODE_BITSIZE (maxmode) - unit);
-#endif
+
          unit = GET_MODE_BITSIZE (maxmode);
 
          if (xtarget == 0
@@ -1291,12 +1430,14 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
 
   mode = GET_MODE (op0);
 
-#if BYTES_BIG_ENDIAN
-  /* BITPOS is the distance between our msb and that of OP0.
-     Convert it to the distance from the lsb.  */
+  if (BYTES_BIG_ENDIAN)
+    {
+      /* BITPOS is the distance between our msb and that of OP0.
+        Convert it to the distance from the lsb.  */
+
+      bitpos = total_bits - bitsize - bitpos;
+    }
 
-  bitpos = total_bits - bitsize - bitpos;
-#endif
   /* Now BITPOS is always the distance between the field's lsb and that of OP0.
      We have reduced the big-endian case to the little-endian case.  */
 
@@ -1373,7 +1514,8 @@ extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
 /* Return a constant integer (CONST_INT or CONST_DOUBLE) mask value
    of mode MODE with BITSIZE ones followed by BITPOS zeros, or the
    complement of that if COMPLEMENT.  The mask is truncated if
-   necessary to the width of mode MODE.  */
+   necessary to the width of mode MODE.  The mask is zero-extended if
+   BITSIZE+BITPOS is too small for MODE.  */
 
 static rtx
 mask_rtx (mode, bitpos, bitsize, complement)
@@ -1455,13 +1597,18 @@ extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
      rtx op0;
      int bitsize, bitpos, unsignedp, align;
 {
-  /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
-     much at a time.  */
-  int unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
+  int unit;
   int bitsdone = 0;
   rtx result;
   int first = 1;
 
+  /* Make sure UNIT isn't larger than BITS_PER_WORD, we can only handle that
+     much at a time.  */
+  if (GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
+    unit = BITS_PER_WORD;
+  else
+    unit = MIN (align * BITS_PER_UNIT, BITS_PER_WORD);
+
   while (bitsdone < bitsize)
     {
       int thissize;
@@ -1479,8 +1626,19 @@ extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
       thissize = MIN (thissize, unit - thispos);
 
       /* If OP0 is a register, then handle OFFSET here.
-        In the register case, UNIT must be a whole word.  */
-      if (GET_CODE (op0) == SUBREG || GET_CODE (op0) == REG)
+
+        When handling multiword bitfields, extract_bit_field may pass
+        down a word_mode SUBREG of a larger REG for a bitfield that actually
+        crosses a word boundary.  Thus, for a SUBREG, we must find
+        the current word starting from the base register.  */
+      if (GET_CODE (op0) == SUBREG)
+       {
+         word = operand_subword_force (SUBREG_REG (op0),
+                                       SUBREG_WORD (op0) + offset,
+                                       GET_MODE (SUBREG_REG (op0)));
+         offset = 0;
+       }
+      else if (GET_CODE (op0) == REG)
        {
          word = operand_subword_force (op0, offset, GET_MODE (op0));
          offset = 0;
@@ -1488,9 +1646,6 @@ extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
       else
        word = op0;
 
-      if (word == 0)
-       abort ();
-
       /* Extract the parts in bit-counting order,
         whose meaning is determined by BYTES_PER_UNIT.
         OFFSET is in UNITs, and UNIT is in bits.
@@ -1501,15 +1656,18 @@ extract_split_bit_field (op0, bitsize, bitpos, unsignedp, align)
       bitsdone += thissize;
 
       /* Shift this part into place for the result.  */
-#if BYTES_BIG_ENDIAN
-      if (bitsize != bitsdone)
-       part = expand_shift (LSHIFT_EXPR, word_mode, part,
-                            build_int_2 (bitsize - bitsdone, 0), 0, 1);
-#else
-      if (bitsdone != thissize)
-       part = expand_shift (LSHIFT_EXPR, word_mode, part,
-                            build_int_2 (bitsdone - thissize, 0), 0, 1);
-#endif
+      if (BYTES_BIG_ENDIAN)
+       {
+         if (bitsize != bitsdone)
+           part = expand_shift (LSHIFT_EXPR, word_mode, part,
+                                build_int_2 (bitsize - bitsdone, 0), 0, 1);
+       }
+      else
+       {
+         if (bitsdone != thissize)
+           part = expand_shift (LSHIFT_EXPR, word_mode, part,
+                                build_int_2 (bitsdone - thissize, 0), 0, 1);
+       }
 
       if (first)
        result = part;
@@ -1586,6 +1744,14 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
 
   op1 = expand_expr (amount, NULL_RTX, VOIDmode, 0);
 
+#ifdef SHIFT_COUNT_TRUNCATED
+  if (SHIFT_COUNT_TRUNCATED
+      && GET_CODE (op1) == CONST_INT
+      && (unsigned HOST_WIDE_INT) INTVAL (op1) >= GET_MODE_BITSIZE (mode))
+    op1 = GEN_INT ((unsigned HOST_WIDE_INT) INTVAL (op1)
+                  % GET_MODE_BITSIZE (mode));
+#endif
+
   if (op1 == const0_rtx)
     return shifted;
 
@@ -1607,8 +1773,7 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
            continue;
          else if (methods == OPTAB_LIB_WIDEN)
            {
-             /* If we are rotating by a constant that is valid and
-                we have been unable to open-code this by a rotation,
+             /* If we have been unable to open-code this by a rotation,
                 do it as the IOR of two shifts.  I.e., to rotate A
                 by N bits, compute (A << N) | ((unsigned) A >> (C - N))
                 where C is the bitsize of A.
@@ -1620,25 +1785,25 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
                 this extremely unlikely lossage to avoid complicating the
                 code below.  */
 
-             if (GET_CODE (op1) == CONST_INT && INTVAL (op1) > 0
-                 && INTVAL (op1) < GET_MODE_BITSIZE (mode))
-               {
-                 rtx subtarget = target == shifted ? 0 : target;
-                 rtx temp1;
-                 tree other_amount
-                   = build_int_2 (GET_MODE_BITSIZE (mode) - INTVAL (op1), 0);
-
-                 shifted = force_reg (mode, shifted);
-
-                 temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR,
-                                      mode, shifted, amount, subtarget, 1);
-                 temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR,
-                                       mode, shifted, other_amount, 0, 1);
-                 return expand_binop (mode, ior_optab, temp, temp1, target,
-                                      unsignedp, methods);
-               }
-             else
-               methods = OPTAB_LIB;
+             rtx subtarget = target == shifted ? 0 : target;
+             rtx temp1;
+             tree type = TREE_TYPE (amount);
+             tree new_amount = make_tree (type, op1);
+             tree other_amount
+               = fold (build (MINUS_EXPR, type,
+                              convert (type,
+                                       build_int_2 (GET_MODE_BITSIZE (mode),
+                                                    0)),
+                              amount));
+
+             shifted = force_reg (mode, shifted);
+
+             temp = expand_shift (left ? LSHIFT_EXPR : RSHIFT_EXPR,
+                                  mode, shifted, new_amount, subtarget, 1);
+             temp1 = expand_shift (left ? RSHIFT_EXPR : LSHIFT_EXPR,
+                                   mode, shifted, other_amount, 0, 1);
+             return expand_binop (mode, ior_optab, temp, temp1, target,
+                                  unsignedp, methods);
            }
 
          temp = expand_binop (mode,
@@ -1658,14 +1823,9 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
                                 target, unsignedp, methods);
        }
       else if (unsignedp)
-       {
-         temp = expand_binop (mode,
-                              left ? lshl_optab : lshr_optab,
-                              shifted, op1, target, unsignedp, methods);
-         if (temp == 0 && left)
-           temp = expand_binop (mode, ashl_optab,
-                                shifted, op1, target, unsignedp, methods);
-       }
+       temp = expand_binop (mode,
+                            left ? ashl_optab : lshr_optab,
+                            shifted, op1, target, unsignedp, methods);
 
       /* Do arithmetic shifts.
         Also, if we are going to widen the operand, we can just as well
@@ -1687,99 +1847,10 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
                               shifted, op1, target, unsignedp, methods1);
        }
 
-#ifdef HAVE_extzv
-      /* We can do a logical (unsigned) right shift with a bit-field
-        extract insn.  But first check if one of the above methods worked.  */
-      if (temp != 0)
-       return temp;
-
-      if (unsignedp && code == RSHIFT_EXPR && ! BITS_BIG_ENDIAN && HAVE_extzv)
-       {
-         enum machine_mode output_mode
-           = insn_operand_mode[(int) CODE_FOR_extzv][0];
-
-         if ((methods == OPTAB_DIRECT && mode == output_mode)
-             || (methods == OPTAB_WIDEN
-                 && GET_MODE_SIZE (mode) < GET_MODE_SIZE (output_mode)))
-           {
-             rtx shifted1 = convert_modes (output_mode, mode,
-                                           protect_from_queue (shifted, 0),
-                                           1);
-             enum machine_mode length_mode
-               = insn_operand_mode[(int) CODE_FOR_extzv][2];
-             enum machine_mode pos_mode
-               = insn_operand_mode[(int) CODE_FOR_extzv][3];
-             rtx target1 = 0;
-             rtx last = get_last_insn ();
-             rtx width;
-             rtx xop1 = op1;
-             rtx pat;
-
-             if (target != 0)
-               target1 = protect_from_queue (target, 1);
-
-             /* We define extract insns as having OUTPUT_MODE in a register
-                and the mode of operand 1 in memory.  Since we want
-                OUTPUT_MODE, we will always force the operand into a
-                register.  At some point we might want to support MEM
-                directly. */
-             shifted1 = force_reg (output_mode, shifted1);
-
-             /* If we don't have or cannot use a suggested target,
-                make a place for the result, in the proper mode.  */
-             if (methods == OPTAB_WIDEN || target1 == 0
-                 || ! ((*insn_operand_predicate[(int) CODE_FOR_extzv][0])
-                       (target1, output_mode)))
-               target1 = gen_reg_rtx (output_mode);
-
-             xop1 = protect_from_queue (xop1, 0);
-             xop1 = convert_modes (pos_mode, TYPE_MODE (TREE_TYPE (amount)),
-                                   xop1, TREE_UNSIGNED (TREE_TYPE (amount)));
-
-             /* If this machine's extzv insists on a register for
-                operand 3 (position), arrange for that.  */
-             if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][3])
-                    (xop1, pos_mode)))
-               xop1 = force_reg (pos_mode, xop1);
-
-             /* WIDTH gets the width of the bit field to extract:
-                wordsize minus # bits to shift by.  */
-             if (GET_CODE (xop1) == CONST_INT)
-               width = GEN_INT (GET_MODE_BITSIZE (mode) - INTVAL (op1));
-             else
-               {
-                 /* Now get the width in the proper mode.  */
-                 op1 = protect_from_queue (op1, 0);
-                 width = convert_to_mode (length_mode, op1,
-                                          TREE_UNSIGNED (TREE_TYPE (amount)));
-
-                 width = expand_binop (length_mode, sub_optab,
-                                       GEN_INT (GET_MODE_BITSIZE (mode)),
-                                       width, NULL_RTX, 0, OPTAB_LIB_WIDEN);
-               }
-
-             /* If this machine's extzv insists on a register for
-                operand 2 (length), arrange for that.  */
-             if (! ((*insn_operand_predicate[(int) CODE_FOR_extzv][2])
-                    (width, length_mode)))
-               width = force_reg (length_mode, width);
-
-             /* Now extract with WIDTH, omitting OP1 least sig bits.  */
-             pat = gen_extzv (target1, shifted1, width, xop1);
-             if (pat)
-               {
-                 emit_insn (pat);
-                 temp = convert_to_mode (mode, target1, 1);
-               }
-             else
-               delete_insns_since (last);
-           }
-
-         /* Can also do logical shift with signed bit-field extract
-            followed by inserting the bit-field at a different position.
-            That strategy is not yet implemented.  */
-       }
-#endif /* HAVE_extzv */
+      /* We used to try extzv here for logical right shifts, but that was
+        only useful for one machine, the VAX, and caused poor code 
+        generation there for lshrdi3, so the code was deleted and a
+        define_expand for lshrsi3 was added to vax.md.  */
     }
 
   if (temp == 0)
@@ -1837,10 +1908,7 @@ synth_mult (alg_out, t, cost_limit)
      int cost_limit;
 {
   int m;
-  struct algorithm *best_alg
-    = (struct algorithm *)alloca (sizeof (struct algorithm));
-  struct algorithm *alg_in
-    = (struct algorithm *)alloca (sizeof (struct algorithm));
+  struct algorithm *alg_in, *best_alg;
   unsigned int cost;
   unsigned HOST_WIDE_INT q;
 
@@ -1875,6 +1943,11 @@ synth_mult (alg_out, t, cost_limit)
        }
     }
 
+  /* We'll be needing a couple extra algorithm structures now.  */
+
+  alg_in = (struct algorithm *)alloca (sizeof (struct algorithm));
+  best_alg = (struct algorithm *)alloca (sizeof (struct algorithm));
+
   /* If we have a group of zero bits at the low-order part of T, try
      multiplying by the remaining bits and then doing a shift.  */
 
@@ -2037,23 +2110,25 @@ synth_mult (alg_out, t, cost_limit)
        }
     }
 
-  /* If we are getting a too long sequence for `struct algorithm'
-     to record, make this search fail.  */
-  if (best_alg->ops == MAX_BITS_PER_WORD)
-    return;
-
   /* If cost_limit has not decreased since we stored it in alg_out->cost,
      we have not found any algorithm.  */
   if (cost_limit == alg_out->cost)
     return;
 
+  /* If we are getting a too long sequence for `struct algorithm'
+     to record, make this search fail.  */
+  if (best_alg->ops == MAX_BITS_PER_WORD)
+    return;
+
   /* Copy the algorithm from temporary space to the space at alg_out.
      We avoid using structure assignment because the majority of
      best_alg is normally undefined, and this is a critical function.  */
   alg_out->ops = best_alg->ops + 1;
   alg_out->cost = cost_limit;
-  bcopy (best_alg->op, alg_out->op, alg_out->ops * sizeof *alg_out->op);
-  bcopy (best_alg->log, alg_out->log, alg_out->ops * sizeof *alg_out->log);
+  bcopy ((char *) best_alg->op, (char *) alg_out->op,
+        alg_out->ops * sizeof *alg_out->op);
+  bcopy ((char *) best_alg->log, (char *) alg_out->log,
+        alg_out->ops * sizeof *alg_out->log);
 }
 \f
 /* Perform a multiplication and return an rtx for the result.
@@ -2072,48 +2147,64 @@ expand_mult (mode, op0, op1, target, unsignedp)
 {
   rtx const_op1 = op1;
 
+  /* synth_mult does an `unsigned int' multiply.  As long as the mode is
+     less than or equal in size to `unsigned int' this doesn't matter.
+     If the mode is larger than `unsigned int', then synth_mult works only
+     if the constant value exactly fits in an `unsigned int' without any
+     truncation.  This means that multiplying by negative values does
+     not work; results are off by 2^32 on a 32 bit machine.  */
+
   /* If we are multiplying in DImode, it may still be a win
      to try to work with shifts and adds.  */
   if (GET_CODE (op1) == CONST_DOUBLE
       && GET_MODE_CLASS (GET_MODE (op1)) == MODE_INT
-      && HOST_BITS_PER_INT <= BITS_PER_WORD)
-    {
-      if ((CONST_DOUBLE_HIGH (op1) == 0 && CONST_DOUBLE_LOW (op1) >= 0)
-         || (CONST_DOUBLE_HIGH (op1) == -1 && CONST_DOUBLE_LOW (op1) < 0))
-       const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
-    }
+      && HOST_BITS_PER_INT >= BITS_PER_WORD
+      && CONST_DOUBLE_HIGH (op1) == 0)
+    const_op1 = GEN_INT (CONST_DOUBLE_LOW (op1));
+  else if (HOST_BITS_PER_INT < GET_MODE_BITSIZE (mode)
+          && GET_CODE (op1) == CONST_INT
+          && INTVAL (op1) < 0)
+    const_op1 = 0;
 
   /* We used to test optimize here, on the grounds that it's better to
      produce a smaller program when -O is not used.
      But this causes such a terrible slowdown sometimes
      that it seems better to use synth_mult always.  */
 
-  if (GET_CODE (const_op1) == CONST_INT)
+  if (const_op1 && GET_CODE (const_op1) == CONST_INT)
     {
       struct algorithm alg;
-      struct algorithm neg_alg;
-      int negate = 0;
+      struct algorithm alg2;
       HOST_WIDE_INT val = INTVAL (op1);
       HOST_WIDE_INT val_so_far;
       rtx insn;
       int mult_cost;
+      enum {basic_variant, negate_variant, add_variant} variant = basic_variant;
 
-      /* Try to do the computation two ways: multiply by the negative of OP1
-        and then negate, or do the multiplication directly.  The latter is
-        usually faster for positive numbers and the former for negative
-        numbers, but the opposite can be faster if the original value
-        has a factor of 2**m +/- 1, while the negated value does not or
-        vice versa.  */
+      /* Try to do the computation three ways: multiply by the negative of OP1
+        and then negate, do the multiplication directly, or do multiplication
+        by OP1 - 1.  */
 
       mult_cost = rtx_cost (gen_rtx (MULT, mode, op0, op1), SET);
       mult_cost = MIN (12 * add_cost, mult_cost);
 
       synth_mult (&alg, val, mult_cost);
-      synth_mult (&neg_alg, - val,
-                 (alg.cost < mult_cost ? alg.cost : mult_cost) - negate_cost);
 
-      if (neg_alg.cost + negate_cost < alg.cost)
-       alg = neg_alg, negate = 1;
+      /* This works only if the inverted value actually fits in an
+        `unsigned int' */
+      if (HOST_BITS_PER_INT >= GET_MODE_BITSIZE (mode))
+       {
+         synth_mult (&alg2, - val,
+                     (alg.cost < mult_cost ? alg.cost : mult_cost) - negate_cost);
+         if (alg2.cost + negate_cost < alg.cost)
+           alg = alg2, variant = negate_variant;
+       }
+
+      /* This proves very useful for division-by-constant.  */
+      synth_mult (&alg2, val - 1,
+                 (alg.cost < mult_cost ? alg.cost : mult_cost) - add_cost);
+      if (alg2.cost + add_cost < alg.cost)
+       alg = alg2, variant = add_variant;
 
       if (alg.cost < mult_cost)
        {
@@ -2147,9 +2238,13 @@ expand_mult (mode, op0, op1, target, unsignedp)
          for (opno = 1; opno < alg.ops; opno++)
            {
              int log = alg.log[opno];
-             rtx shift_subtarget = preserve_subexpressions_p () ? 0 : accum;
-             rtx add_target = opno == alg.ops - 1 && target != 0 ? target : 0;
-
+             int preserve = preserve_subexpressions_p ();
+             rtx shift_subtarget = preserve ? 0 : accum;
+             rtx add_target
+               = (opno == alg.ops - 1 && target != 0 && variant != add_variant
+                 ? target : 0);
+             rtx accum_target = preserve ? 0 : accum;
+             
              switch (alg.op[opno])
                {
                case alg_shift:
@@ -2162,7 +2257,7 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  tem = expand_shift (LSHIFT_EXPR, mode, op0,
                                      build_int_2 (log, 0), NULL_RTX, 0);
                  accum = force_operand (gen_rtx (PLUS, mode, accum, tem),
-                                        add_target ? add_target : accum);
+                                        add_target ? add_target : accum_target);
                  val_so_far += (HOST_WIDE_INT) 1 << log;
                  break;
 
@@ -2170,23 +2265,25 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  tem = expand_shift (LSHIFT_EXPR, mode, op0,
                                      build_int_2 (log, 0), NULL_RTX, 0);
                  accum = force_operand (gen_rtx (MINUS, mode, accum, tem),
-                                        add_target ? add_target : accum);
+                                        add_target ? add_target : accum_target);
                  val_so_far -= (HOST_WIDE_INT) 1 << log;
                  break;
 
                case alg_add_t2_m:
                  accum = expand_shift (LSHIFT_EXPR, mode, accum,
-                                       build_int_2 (log, 0), accum, 0);
+                                       build_int_2 (log, 0), shift_subtarget,
+                                       0);
                  accum = force_operand (gen_rtx (PLUS, mode, accum, op0),
-                                        add_target ? add_target : accum);
+                                        add_target ? add_target : accum_target);
                  val_so_far = (val_so_far << log) + 1;
                  break;
 
                case alg_sub_t2_m:
                  accum = expand_shift (LSHIFT_EXPR, mode, accum,
-                                       build_int_2 (log, 0), accum, 0);
+                                       build_int_2 (log, 0), shift_subtarget,
+                                       0);
                  accum = force_operand (gen_rtx (MINUS, mode, accum, op0),
-                                        add_target ? add_target : accum);
+                                        add_target ? add_target : accum_target);
                  val_so_far = (val_so_far << log) - 1;
                  break;
 
@@ -2194,7 +2291,7 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  tem = expand_shift (LSHIFT_EXPR, mode, accum,
                                      build_int_2 (log, 0), NULL_RTX, 0);
                  accum = force_operand (gen_rtx (PLUS, mode, accum, tem),
-                                        add_target ? add_target : accum);
+                                        add_target ? add_target : accum_target);
                  val_so_far += val_so_far << log;
                  break;
 
@@ -2202,7 +2299,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
                  tem = expand_shift (LSHIFT_EXPR, mode, accum,
                                      build_int_2 (log, 0), NULL_RTX, 0);
                  accum = force_operand (gen_rtx (MINUS, mode, tem, accum),
-                                        add_target ? add_target : tem);
+                                        (add_target ? add_target
+                                         : preserve ? 0 : tem));
                  val_so_far = (val_so_far << log) - val_so_far;
                  break;
 
@@ -2220,11 +2318,16 @@ expand_mult (mode, op0, op1, target, unsignedp)
                           REG_NOTES (insn));
            }
 
-         if (negate)
+         if (variant == negate_variant)
            {
              val_so_far = - val_so_far;
              accum = expand_unop (mode, neg_optab, accum, target, 0);
            }
+         else if (variant == add_variant)
+           {
+             val_so_far = val_so_far + 1;
+             accum = force_operand (gen_rtx (PLUS, mode, accum, op0), target);
+           }
 
          if (val != val_so_far)
            abort ();
@@ -2242,6 +2345,334 @@ expand_mult (mode, op0, op1, target, unsignedp)
   return op0;
 }
 \f
+/* Return the smallest n such that 2**n >= X.  */
+
+int
+ceil_log2 (x)
+     unsigned HOST_WIDE_INT x;
+{
+  return floor_log2 (x - 1) + 1;
+}
+
+/* Choose a minimal N + 1 bit approximation to 1/D that can be used to
+   replace division by D, and put the least significant N bits of the result
+   in *MULTIPLIER_PTR and return the most significant bit.
+
+   The width of operations is N (should be <= HOST_BITS_PER_WIDE_INT), the
+   needed precision is in PRECISION (should be <= N).
+
+   PRECISION should be as small as possible so this function can choose
+   multiplier more freely.
+
+   The rounded-up logarithm of D is placed in *lgup_ptr.  A shift count that
+   is to be used for a final right shift is placed in *POST_SHIFT_PTR.
+
+   Using this function, x/D will be equal to (x * m) >> (*POST_SHIFT_PTR),
+   where m is the full HOST_BITS_PER_WIDE_INT + 1 bit multiplier.  */
+
+static
+unsigned HOST_WIDE_INT
+choose_multiplier (d, n, precision, multiplier_ptr, post_shift_ptr, lgup_ptr)
+     unsigned HOST_WIDE_INT d;
+     int n;
+     int precision;
+     unsigned HOST_WIDE_INT *multiplier_ptr;
+     int *post_shift_ptr;
+     int *lgup_ptr;
+{
+  unsigned HOST_WIDE_INT mhigh_hi, mhigh_lo;
+  unsigned HOST_WIDE_INT mlow_hi, mlow_lo;
+  int lgup, post_shift;
+  int pow, pow2;
+  unsigned HOST_WIDE_INT nh, nl, dummy1, dummy2;
+
+  /* lgup = ceil(log2(divisor)); */
+  lgup = ceil_log2 (d);
+
+  if (lgup > n)
+    abort ();
+
+  pow = n + lgup;
+  pow2 = n + lgup - precision;
+
+  if (pow == 2 * HOST_BITS_PER_WIDE_INT)
+    {
+      /* We could handle this with some effort, but this case is much better
+        handled directly with a scc insn, so rely on caller using that.  */
+      abort ();
+    }
+
+  /* mlow = 2^(N + lgup)/d */
+ if (pow >= HOST_BITS_PER_WIDE_INT)
+    {
+      nh = (unsigned HOST_WIDE_INT) 1 << (pow - HOST_BITS_PER_WIDE_INT);
+      nl = 0;
+    }
+  else
+    {
+      nh = 0;
+      nl = (unsigned HOST_WIDE_INT) 1 << pow;
+    }
+  div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
+                       &mlow_lo, &mlow_hi, &dummy1, &dummy2);
+
+  /* mhigh = (2^(N + lgup) + 2^N + lgup - precision)/d */
+  if (pow2 >= HOST_BITS_PER_WIDE_INT)
+    nh |= (unsigned HOST_WIDE_INT) 1 << (pow2 - HOST_BITS_PER_WIDE_INT);
+  else
+    nl |= (unsigned HOST_WIDE_INT) 1 << pow2;
+  div_and_round_double (TRUNC_DIV_EXPR, 1, nl, nh, d, (HOST_WIDE_INT) 0,
+                       &mhigh_lo, &mhigh_hi, &dummy1, &dummy2);
+
+  if (mhigh_hi && nh - d >= d)
+    abort ();
+  if (mhigh_hi > 1 || mlow_hi > 1)
+    abort ();
+  /* assert that mlow < mhigh.  */
+  if (! (mlow_hi < mhigh_hi || (mlow_hi == mhigh_hi && mlow_lo < mhigh_lo)))
+    abort();
+
+  /* If precision == N, then mlow, mhigh exceed 2^N
+     (but they do not exceed 2^(N+1)).  */
+
+  /* Reduce to lowest terms */
+  for (post_shift = lgup; post_shift > 0; post_shift--)
+    {
+      unsigned HOST_WIDE_INT ml_lo = (mlow_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mlow_lo >> 1);
+      unsigned HOST_WIDE_INT mh_lo = (mhigh_hi << (HOST_BITS_PER_WIDE_INT - 1)) | (mhigh_lo >> 1);
+      if (ml_lo >= mh_lo)
+       break;
+
+      mlow_hi = 0;
+      mlow_lo = ml_lo;
+      mhigh_hi = 0;
+      mhigh_lo = mh_lo;
+    }
+
+  *post_shift_ptr = post_shift;
+  *lgup_ptr = lgup;
+  if (n < HOST_BITS_PER_WIDE_INT)
+    {
+      unsigned HOST_WIDE_INT mask = ((unsigned HOST_WIDE_INT) 1 << n) - 1;
+      *multiplier_ptr = mhigh_lo & mask;
+      return mhigh_lo >= mask;
+    }
+  else
+    {
+      *multiplier_ptr = mhigh_lo;
+      return mhigh_hi;
+    }
+}
+
+/* Compute the inverse of X mod 2**n, i.e., find Y such that X * Y is
+   congruent to 1 (mod 2**N).  */
+
+static unsigned HOST_WIDE_INT
+invert_mod2n (x, n)
+     unsigned HOST_WIDE_INT x;
+     int n;
+{
+  /* Solve x*y == 1 (mod 2^n), where x is odd.  Return y. */
+
+  /* The algorithm notes that the choice y = x satisfies
+     x*y == 1 mod 2^3, since x is assumed odd.
+     Each iteration doubles the number of bits of significance in y.  */
+
+  unsigned HOST_WIDE_INT mask;
+  unsigned HOST_WIDE_INT y = x;
+  int nbit = 3;
+
+  mask = (n == HOST_BITS_PER_WIDE_INT
+         ? ~(unsigned HOST_WIDE_INT) 0
+         : ((unsigned HOST_WIDE_INT) 1 << n) - 1);
+
+  while (nbit < n)
+    {
+      y = y * (2 - x*y) & mask;                /* Modulo 2^N */
+      nbit *= 2;
+    }
+  return y;
+}
+
+/* Emit code to adjust ADJ_OPERAND after multiplication of wrong signedness
+   flavor of OP0 and OP1.  ADJ_OPERAND is already the high half of the
+   product OP0 x OP1.  If UNSIGNEDP is nonzero, adjust the signed product
+   to become unsigned, if UNSIGNEDP is zero, adjust the unsigned product to
+   become signed.
+
+   The result is put in TARGET if that is convenient.
+
+   MODE is the mode of operation.  */
+
+rtx
+expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
+     enum machine_mode mode;
+     register rtx adj_operand, op0, op1, target;
+     int unsignedp;
+{
+  rtx tem;
+  enum rtx_code adj_code = unsignedp ? PLUS : MINUS;
+
+  tem = expand_shift (RSHIFT_EXPR, mode, op0,
+                     build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
+                     NULL_RTX, 0);
+  tem = expand_and (tem, op1, NULL_RTX);
+  adj_operand = force_operand (gen_rtx (adj_code, mode, adj_operand, tem),
+                              adj_operand);
+
+  tem = expand_shift (RSHIFT_EXPR, mode, op1,
+                     build_int_2 (GET_MODE_BITSIZE (mode) - 1, 0),
+                     NULL_RTX, 0);
+  tem = expand_and (tem, op0, NULL_RTX);
+  target = force_operand (gen_rtx (adj_code, mode, adj_operand, tem), target);
+
+  return target;
+}
+
+/* Emit code to multiply OP0 and CNST1, putting the high half of the result
+   in TARGET if that is convenient, and return where the result is.  If the
+   operation can not be performed, 0 is returned.
+
+   MODE is the mode of operation and result.
+
+   UNSIGNEDP nonzero means unsigned multiply.
+
+   MAX_COST is the total allowed cost for the expanded RTL.  */
+
+rtx
+expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
+     enum machine_mode mode;
+     register rtx op0, target;
+     unsigned HOST_WIDE_INT cnst1;
+     int unsignedp;
+     int max_cost;
+{
+  enum machine_mode wider_mode = GET_MODE_WIDER_MODE (mode);
+  optab mul_highpart_optab;
+  optab moptab;
+  rtx tem;
+  int size = GET_MODE_BITSIZE (mode);
+  rtx op1, wide_op1;
+
+  /* We can't support modes wider than HOST_BITS_PER_INT.  */
+  if (size > HOST_BITS_PER_WIDE_INT)
+    abort ();
+
+  op1 = GEN_INT (cnst1);
+
+  if (GET_MODE_BITSIZE (wider_mode) <= HOST_BITS_PER_INT)
+    wide_op1 = op1;
+  else
+    wide_op1
+      = immed_double_const (cnst1,
+                           (unsignedp
+                            ? (HOST_WIDE_INT) 0
+                            : -(cnst1 >> (HOST_BITS_PER_WIDE_INT - 1))),
+                           wider_mode);
+
+  /* expand_mult handles constant multiplication of word_mode
+     or narrower.  It does a poor job for large modes.  */
+  if (size < BITS_PER_WORD
+      && mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost)
+    {
+      /* We have to do this, since expand_binop doesn't do conversion for
+        multiply.  Maybe change expand_binop to handle widening multiply?  */
+      op0 = convert_to_mode (wider_mode, op0, unsignedp);
+
+      tem = expand_mult (wider_mode, op0, wide_op1, NULL_RTX, unsignedp);
+      tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+                         build_int_2 (size, 0), NULL_RTX, 1);
+      return convert_modes (mode, wider_mode, tem, unsignedp);
+    }
+
+  if (target == 0)
+    target = gen_reg_rtx (mode);
+
+  /* Firstly, try using a multiplication insn that only generates the needed
+     high part of the product, and in the sign flavor of unsignedp.  */
+  if (mul_highpart_cost[(int) mode] < max_cost)
+    {
+      mul_highpart_optab = unsignedp ? umul_highpart_optab : smul_highpart_optab;
+      target = expand_binop (mode, mul_highpart_optab,
+                            op0, wide_op1, target, unsignedp, OPTAB_DIRECT);
+      if (target)
+       return target;
+    }
+
+  /* Secondly, same as above, but use sign flavor opposite of unsignedp.
+     Need to adjust the result after the multiplication.  */
+  if (mul_highpart_cost[(int) mode] + 2 * shift_cost[size-1] + 4 * add_cost < max_cost)
+    {
+      mul_highpart_optab = unsignedp ? smul_highpart_optab : umul_highpart_optab;
+      target = expand_binop (mode, mul_highpart_optab,
+                            op0, wide_op1, target, unsignedp, OPTAB_DIRECT);
+      if (target)
+       /* We used the wrong signedness.  Adjust the result.  */
+       return expand_mult_highpart_adjust (mode, target, op0,
+                                           op1, target, unsignedp);
+    }
+
+  /* Try widening multiplication.  */
+  moptab = unsignedp ? umul_widen_optab : smul_widen_optab;
+  if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
+      && mul_widen_cost[(int) wider_mode] < max_cost)
+    {
+      op1 = force_reg (mode, op1);
+      goto try;
+    } 
+
+  /* Try widening the mode and perform a non-widening multiplication.  */
+  moptab = smul_optab;
+  if (smul_optab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
+      && mul_cost[(int) wider_mode] + shift_cost[size-1] < max_cost)
+    {
+      op1 = wide_op1;
+      goto try;
+    }
+
+  /* Try widening multiplication of opposite signedness, and adjust.  */
+  moptab = unsignedp ? smul_widen_optab : umul_widen_optab;
+  if (moptab->handlers[(int) wider_mode].insn_code != CODE_FOR_nothing
+      && (mul_widen_cost[(int) wider_mode]
+         + 2 * shift_cost[size-1] + 4 * add_cost < max_cost))
+    {
+      rtx regop1 = force_reg (mode, op1);
+      tem = expand_binop (wider_mode, moptab, op0, regop1,
+                         NULL_RTX, ! unsignedp, OPTAB_WIDEN);
+      if (tem != 0)
+       {
+         /* Extract the high half of the just generated product.  */
+         tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+                             build_int_2 (size, 0), NULL_RTX, 1);
+         tem = convert_modes (mode, wider_mode, tem, unsignedp);
+         /* We used the wrong signedness.  Adjust the result.  */
+         return expand_mult_highpart_adjust (mode, tem, op0, op1,
+                                             target, unsignedp);
+       }
+    }
+
+  return 0;
+
+ try:
+  /* Pass NULL_RTX as target since TARGET has wrong mode.  */
+  tem = expand_binop (wider_mode, moptab, op0, op1,
+                     NULL_RTX, unsignedp, OPTAB_WIDEN);
+  if (tem == 0)
+    return 0;
+
+  /* Extract the high half of the just generated product.  */
+  if (mode == word_mode)
+    {
+      return gen_highpart (mode, tem);
+    }
+  else
+    {
+      tem = expand_shift (RSHIFT_EXPR, wider_mode, tem,
+                         build_int_2 (size, 0), NULL_RTX, 1);
+      return convert_modes (mode, wider_mode, tem, unsignedp);
+    }
+}
+\f
 /* Emit the code to divide OP0 by OP1, putting the result in TARGET
    if that is convenient, and returning where the result is.
    You may request either the quotient or the remainder as the result;
@@ -2259,6 +2690,8 @@ expand_mult (mode, op0, op1, target, unsignedp)
    But C doesn't use these operations, so their optimizations are
    left for later.  */
 
+#define EXACT_POWER_OF_2_OR_ZERO_P(x) (((x) & ((x) - 1)) == 0)
+
 rtx
 expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      int rem_flag;
@@ -2267,16 +2700,51 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      register rtx op0, op1, target;
      int unsignedp;
 {
-  register rtx result = 0;
   enum machine_mode compute_mode;
-  int log = -1;
+  register rtx tquotient;
+  rtx quotient = 0, remainder = 0;
+  rtx last;
   int size;
-  int can_clobber_op0;
-  int mod_insn_no_good = 0;
-  rtx adjusted_op0 = op0;
+  rtx insn, set;
   optab optab1, optab2;
+  int op1_is_constant, op1_is_pow2;
+  int max_cost, extra_cost;
+
+  op1_is_constant = GET_CODE (op1) == CONST_INT;
+  op1_is_pow2 = (op1_is_constant
+                && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
+                     || EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1)))));
+
+  /*
+     This is the structure of expand_divmod:
+
+     First comes code to fix up the operands so we can perform the operations
+     correctly and efficiently.
+
+     Second comes a switch statement with code specific for each rounding mode.
+     For some special operands this code emits all RTL for the desired
+     operation, for other cases, it generates only a quotient and stores it in
+     QUOTIENT.  The case for trunc division/remainder might leave quotient = 0,
+     to indicate that it has not done anything.
+
+     Last comes code that finishes the operation.  If QUOTIENT is set and
+     REM_FLAG is set, the remainder is computed as OP0 - QUOTIENT * OP1.  If
+     QUOTIENT is not set, it is computed using trunc rounding.
 
-  /* We shouldn't be called with op1 == const1_rtx, but some of the
+     We try to generate special code for division and remainder when OP1 is a
+     constant.  If |OP1| = 2**n we can use shifts and some other fast
+     operations.  For other values of OP1, we compute a carefully selected
+     fixed-point approximation m = 1/OP1, and generate code that multiplies OP0
+     by m.
+
+     In all cases but EXACT_DIV_EXPR, this multiplication requires the upper
+     half of the product.  Different strategies for generating the product are
+     implemented in expand_mult_highpart.
+
+     If what we actually want is the remainder, we generate that by another
+     by-constant multiplication and a subtraction.  */
+
+  /* We shouldn't be called with OP1 == const1_rtx, but some of the
      code below will malfunction if we are, so check here and handle
      the special case if so.  */
   if (op1 == const1_rtx)
@@ -2288,44 +2756,13 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
         and function-inlining gets confused by this.  */
       && ((REG_P (target) && REG_FUNCTION_VALUE_P (target))
          /* Don't clobber an operand while doing a multi-step calculation.  */
-         || (rem_flag
+         || ((rem_flag || op1_is_constant)
              && (reg_mentioned_p (target, op0)
                  || (GET_CODE (op0) == MEM && GET_CODE (target) == MEM)))
          || reg_mentioned_p (target, op1)
          || (GET_CODE (op1) == MEM && GET_CODE (target) == MEM)))
     target = 0;
 
-  /* See if we are dividing by 2**log, and hence will do it by shifting,
-     which is really floor-division, or if we will really do a divide,
-     and we assume that is trunc-division.
-
-     We must correct the dividend by adding or subtracting something
-     based on the divisor, in order to do the kind of rounding specified
-     by CODE.  The correction depends on what kind of rounding is actually
-     available, and that depends on whether we will shift or divide.
-
-     In many of these cases it is possible to perform the operation by a
-     clever series of logical operations (shifts and/or exclusive-ors).
-     Although avoiding the jump has the advantage that it extends the basic
-     block and allows further optimization, the branch-free code is normally
-     at least one instruction longer in the (most common) case where the
-     dividend is non-negative.  Performance measurements of the two
-     alternatives show that the branch-free code is slightly faster on the
-     IBM ROMP but slower on CISC processors (significantly slower on the
-     VAX).  Accordingly, the jump code has been retained when BRANCH_COST
-     is small.
-
-     On machines where the jump code is slower, the cost of a DIV or MOD
-     operation can be set small (less than twice that of an addition); in 
-     that case, we pretend that we don't have a power of two and perform
-     a normal division or modulus operation.  */
-
-  if (GET_CODE (op1) == CONST_INT
-      && ! ((code == TRUNC_MOD_EXPR || code == TRUNC_DIV_EXPR)
-           && ! unsignedp
-           && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap)))
-    log = exact_log2 (INTVAL (op1));
-
   /* Get the mode in which to perform this computation.  Normally it will
      be MODE, but sometimes we can't do the desired operation in MODE.
      If so, pick a wider mode in which we can do the operation.  Convert
@@ -2341,9 +2778,14 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      (either a division, modulus, or shift).  Finally, check for the smallest
      mode for which we can do the operation with a library call.  */
 
-  optab1 = (log >= 0 ? (unsignedp ? lshr_optab : ashr_optab)
+  /* We might want to refine this now that we have division-by-constant
+     optimization.  Since expand_mult_highpart tries so many variants, it is
+     not straightforward to generalize this.  Maybe we should make an array
+     of possible modes in init_expmed?  Save this for GCC 2.7.  */
+
+  optab1 = (op1_is_pow2 ? (unsignedp ? lshr_optab : ashr_optab)
            : (unsignedp ? udiv_optab : sdiv_optab));
-  optab2 = (log >= 0 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
+  optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
 
   for (compute_mode = mode; compute_mode != VOIDmode;
        compute_mode = GET_MODE_WIDER_MODE (compute_mode))
@@ -2363,291 +2805,905 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
   if (compute_mode == VOIDmode)
     compute_mode = mode;
 
+  if (target && GET_MODE (target) == compute_mode)
+    tquotient = target;
+  else
+    tquotient = gen_reg_rtx (compute_mode);
+
   size = GET_MODE_BITSIZE (compute_mode);
+#if 0
+  /* It should be possible to restrict the precision to GET_MODE_BITSIZE
+     (mode), and thereby get better code when OP1 is a constant.  Do that
+     later.  It will require going over all usages of SIZE below.  */
+  size = GET_MODE_BITSIZE (mode);
+#endif
 
-  /* If OP0 is a register that is used as the target, we can modify
-     it in place; otherwise, we have to ensure we copy OP0 before
-     modifying it.  */
-  can_clobber_op0 = (GET_CODE (op0) == REG && op0 == target);
+  max_cost = div_cost[(int) compute_mode]
+    - (rem_flag ? mul_cost[(int) compute_mode] + add_cost : 0);
 
-  /* Now convert to the best mode to use.  Show we made a copy of OP0
-     and hence we can clobber it (we cannot use a SUBREG to widen
-     something.  */
+  /* Now convert to the best mode to use.  */
   if (compute_mode != mode)
     {
-      adjusted_op0 = op0 = convert_modes (compute_mode, mode, op0, unsignedp);
-      can_clobber_op0 = 1;
+      op0 = convert_modes (compute_mode, mode, op0, unsignedp);
       op1 = convert_modes (compute_mode, mode, op1, unsignedp);
     }
 
-  /* If we are computing the remainder and one of the operands is a volatile
-     MEM, copy it into a register.  */
+  /* If one of the operands is a volatile MEM, copy it into a register.  */
 
-  if (rem_flag && GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0))
-    adjusted_op0 = op0 = force_reg (compute_mode, op0), can_clobber_op0 = 1;
-  if (rem_flag && GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1))
+  if (GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0))
+    op0 = force_reg (compute_mode, op0);
+  if (GET_CODE (op1) == MEM && MEM_VOLATILE_P (op1))
     op1 = force_reg (compute_mode, op1);
 
-  /* If we are computing the remainder, op0 will be needed later to calculate
-     X - Y * (X / Y), therefore cannot be clobbered. */
-  if (rem_flag)
-    can_clobber_op0 = 0;
-
-  /* See if we will need to modify ADJUSTED_OP0.  Note that this code
-     must agree with that in the switch below.  */
-  if (((code == TRUNC_MOD_EXPR || code == TRUNC_DIV_EXPR)
-       && log >= 0 && ! unsignedp)
-      || ((code == FLOOR_MOD_EXPR || code == FLOOR_DIV_EXPR)
-         && log < 0 && ! unsignedp)
-      || code == CEIL_MOD_EXPR || code == CEIL_DIV_EXPR
-      || code == ROUND_MOD_EXPR || code == ROUND_DIV_EXPR)
-    {
-      /* If we want the remainder, we may need to use OP0, so make sure
-        it and ADJUSTED_OP0 are in different registers.  We force OP0
-        to a register in case it has any queued subexpressions, because
-        emit_cmp_insn will call emit_queue.
-
-        If we don't want the remainder, we aren't going to use OP0 anymore.
-        However, if we cannot clobber OP0 (and hence ADJUSTED_OP0), we must
-        make a copy of it, hopefully to TARGET.
-
-        This code is somewhat tricky.  Note that if REM_FLAG is nonzero,
-        CAN_CLOBBER_OP0 will be zero and we know that OP0 cannot
-        equal TARGET.  */
+  /* If we need the remainder or if OP1 is constant, we need to
+     put OP0 in a register in case it has any queued subexpressions.  */
+  if (rem_flag || op1_is_constant)
+    op0 = force_reg (compute_mode, op0);
 
-      if (rem_flag)
-       op0 = force_reg (compute_mode, op0);
+  last = get_last_insn ();
 
-      if (! can_clobber_op0)
-       {
-         if (target && GET_MODE (target) == compute_mode)
-           adjusted_op0 = target;
-         else
-           adjusted_op0 = 0;
-         adjusted_op0 = copy_to_suggested_reg (op0, adjusted_op0,
-                                               compute_mode);
-       }
+  /* Promote floor rounding to trunc rounding for unsigned operations.  */
+  if (unsignedp)
+    {
+      if (code == FLOOR_DIV_EXPR)
+       code = TRUNC_DIV_EXPR;
+      if (code == FLOOR_MOD_EXPR)
+       code = TRUNC_MOD_EXPR;
     }
 
-  /* Adjust ADJUSTED_OP0 as described above.  Unless CAN_CLOBBER_OP0
-     is now non-zero, OP0 will retain it's original value.  */
-
-  switch (code)
-    {
-    case TRUNC_MOD_EXPR:
-    case TRUNC_DIV_EXPR:
-      if (log >= 0 && ! unsignedp)
-       {
-         /* Here we need to add OP1-1 if OP0 is negative, 0 otherwise.
-            This can be computed without jumps by arithmetically shifting
-            OP0 right LOG-1 places and then shifting right logically
-            SIZE-LOG bits.  The resulting value is unconditionally added
-            to OP0.
-
-            If OP0 cannot be modified in place, copy it, possibly to
-            TARGET.  Note that we will have previously only allowed
-            it to be modified in place if it is a register, so that
-            after this `if', ADJUSTED_OP0 is known to be a
-            register.  */
-         if (log == 1 || BRANCH_COST >= 3)
-           {
-             rtx temp;
+  if (op1 != const0_rtx)
+    switch (code)
+      {
+      case TRUNC_MOD_EXPR:
+      case TRUNC_DIV_EXPR:
+       if (op1_is_constant)
+         {
+           if (unsignedp)
+             {
+               unsigned HOST_WIDE_INT mh, ml;
+               int pre_shift, post_shift;
+               int dummy;
+               unsigned HOST_WIDE_INT d = INTVAL (op1);
+
+               if (EXACT_POWER_OF_2_OR_ZERO_P (d))
+                 {
+                   pre_shift = floor_log2 (d);
+                   if (rem_flag)
+                     {
+                       remainder =
+                         expand_binop (compute_mode, and_optab, op0,
+                                       GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
+                                       remainder, 1,
+                                       OPTAB_LIB_WIDEN);
+                       if (remainder)
+                         return gen_lowpart (mode, remainder);
+                     }
+                   quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                            build_int_2 (pre_shift, 0),
+                                            tquotient, 1);
+                 }
+               else if (size <= HOST_BITS_PER_WIDE_INT)
+                 {
+                   if (d >= ((unsigned HOST_WIDE_INT) 1 << (size - 1)))
+                     {
+                       /* Most significant bit of divisor is set; emit an scc
+                          insn.  */
+                       quotient = emit_store_flag (tquotient, GEU, op0, op1,
+                                                   compute_mode, 1, 1);
+                       if (quotient == 0)
+                         goto fail1;
+                     }
+                   else
+                     {
+                       /* Find a suitable multiplier and right shift count
+                          instead of multiplying with D.  */
+
+                       mh = choose_multiplier (d, size, size,
+                                               &ml, &post_shift, &dummy);
+
+                       /* If the suggested multiplier is more than SIZE bits,
+                          we can do better for even divisors, using an
+                          initial right shift.  */
+                       if (mh != 0 && (d & 1) == 0)
+                         {
+                           pre_shift = floor_log2 (d & -d);
+                           mh = choose_multiplier (d >> pre_shift, size,
+                                                   size - pre_shift,
+                                                   &ml, &post_shift, &dummy);
+                           if (mh)
+                             abort ();
+                         }
+                       else
+                         pre_shift = 0;
+
+                       if (mh != 0)
+                         {
+                           rtx t1, t2, t3, t4;
+
+                           extra_cost = (shift_cost[post_shift - 1]
+                                         + shift_cost[1] + 2 * add_cost);
+                           t1 = expand_mult_highpart (compute_mode, op0, ml,
+                                                      NULL_RTX, 1,
+                                                      max_cost - extra_cost);
+                           if (t1 == 0)
+                             goto fail1;
+                           t2 = force_operand (gen_rtx (MINUS, compute_mode,
+                                                        op0, t1),
+                                               NULL_RTX);
+                           t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+                                              build_int_2 (1, 0), NULL_RTX,1);
+                           t4 = force_operand (gen_rtx (PLUS, compute_mode,
+                                                        t1, t3),
+                                               NULL_RTX);
+                           quotient =
+                             expand_shift (RSHIFT_EXPR, compute_mode, t4,
+                                           build_int_2 (post_shift - 1, 0),
+                                           tquotient, 1);
+                         }
+                       else
+                         {
+                           rtx t1, t2;
+
+                           t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                              build_int_2 (pre_shift, 0),
+                                              NULL_RTX, 1);
+                           extra_cost = (shift_cost[pre_shift]
+                                         + shift_cost[post_shift]);
+                           t2 = expand_mult_highpart (compute_mode, t1, ml,
+                                                      NULL_RTX, 1,
+                                                      max_cost - extra_cost);
+                           if (t2 == 0)
+                             goto fail1;
+                           quotient =
+                             expand_shift (RSHIFT_EXPR, compute_mode, t2,
+                                           build_int_2 (post_shift, 0),
+                                           tquotient, 1);
+                         }
+                     }
+                 }
+               else            /* Too wide mode to use tricky code */
+                 break;
 
-             temp = expand_shift (RSHIFT_EXPR, compute_mode, adjusted_op0,
-                                  build_int_2 (log - 1, 0), NULL_RTX, 0);
+               insn = get_last_insn ();
+               if (insn != last
+                   && (set = single_set (insn)) != 0
+                   && SET_DEST (set) == quotient)
+                 REG_NOTES (insn)
+                   = gen_rtx (EXPR_LIST, REG_EQUAL,
+                              gen_rtx (UDIV, compute_mode, op0, op1),
+                              REG_NOTES (insn));
+             }
+           else                /* TRUNC_DIV, signed */
+             {
+               unsigned HOST_WIDE_INT ml;
+               int lgup, post_shift;
+               HOST_WIDE_INT d = INTVAL (op1);
+               unsigned HOST_WIDE_INT abs_d = d >= 0 ? d : -d;
+
+               /* n rem d = n rem -d */
+               if (rem_flag && d < 0)
+                 {
+                   d = abs_d;
+                   op1 = GEN_INT (abs_d);
+                 }
+
+               if (d == 1)
+                 quotient = op0;
+               else if (d == -1)
+                 quotient = expand_unop (compute_mode, neg_optab, op0,
+                                         tquotient, 0);
+               else if (abs_d == (unsigned HOST_WIDE_INT) 1 << (size - 1))
+                 {
+                   /* This case is not handled correctly below.  */
+                   quotient = emit_store_flag (tquotient, EQ, op0, op1,
+                                               compute_mode, 1, 1);
+                   if (quotient == 0)
+                     goto fail1;
+                 }
+               else if (EXACT_POWER_OF_2_OR_ZERO_P (d)
+                        && (rem_flag ? smod_pow2_cheap : sdiv_pow2_cheap))
+                 ;
+               else if (EXACT_POWER_OF_2_OR_ZERO_P (abs_d))
+                 {
+                   lgup = floor_log2 (abs_d);
+                   if (abs_d != 2 && BRANCH_COST < 3)
+                     {
+                       rtx label = gen_label_rtx ();
+                       rtx t1;
+
+                       t1 = copy_to_mode_reg (compute_mode, op0);
+                       emit_cmp_insn (t1, const0_rtx, GE, 
+                                      NULL_RTX, compute_mode, 0, 0);
+                       emit_jump_insn (gen_bge (label));
+                       expand_inc (t1, GEN_INT (abs_d - 1));
+                       emit_label (label);
+                       quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+                                                build_int_2 (lgup, 0),
+                                                tquotient, 0);
+                     }
+                   else
+                     {
+                       rtx t1, t2, t3;
+                       t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                          build_int_2 (size - 1, 0),
+                                          NULL_RTX, 0);
+                       t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+                                          build_int_2 (size - lgup, 0),
+                                          NULL_RTX, 1);
+                       t3 = force_operand (gen_rtx (PLUS, compute_mode,
+                                                    op0, t2),
+                                           NULL_RTX);
+                       quotient = expand_shift (RSHIFT_EXPR, compute_mode, t3,
+                                                build_int_2 (lgup, 0),
+                                                tquotient, 0);
+                     }
+
+                   /* We have computed OP0 / abs(OP1).  If OP1 is negative, negate
+                      the quotient.  */
+                   if (d < 0)
+                     {
+                       insn = get_last_insn ();
+                       if (insn != last
+                           && (set = single_set (insn)) != 0
+                           && SET_DEST (set) == quotient)
+                         REG_NOTES (insn)
+                           = gen_rtx (EXPR_LIST, REG_EQUAL,
+                                      gen_rtx (DIV, compute_mode, op0,
+                                               GEN_INT (abs_d)),
+                                      REG_NOTES (insn));
+
+                       quotient = expand_unop (compute_mode, neg_optab,
+                                               quotient, quotient, 0);
+                     }
+                 }
+               else if (size <= HOST_BITS_PER_WIDE_INT)
+                 {
+                   choose_multiplier (abs_d, size, size - 1,
+                                      &ml, &post_shift, &lgup);
+                   if (ml < (unsigned HOST_WIDE_INT) 1 << (size - 1))
+                     {
+                       rtx t1, t2, t3;
+
+                       extra_cost = (shift_cost[post_shift]
+                                     + shift_cost[size - 1] + add_cost);
+                       t1 = expand_mult_highpart (compute_mode, op0, ml,
+                                                  NULL_RTX, 0,
+                                                  max_cost - extra_cost);
+                       if (t1 == 0)
+                         goto fail1;
+                       t2 = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+                                          build_int_2 (post_shift, 0), NULL_RTX, 0);
+                       t3 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                          build_int_2 (size - 1, 0), NULL_RTX, 0);
+                       if (d < 0)
+                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t2),
+                                                   tquotient);
+                       else
+                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t2, t3),
+                                                   tquotient);
+                     }
+                   else
+                     {
+                       rtx t1, t2, t3, t4;
+
+                       ml |= (~(unsigned HOST_WIDE_INT) 0) << (size - 1);
+                       extra_cost = (shift_cost[post_shift]
+                                     + shift_cost[size - 1] + 2 * add_cost);
+                       t1 = expand_mult_highpart (compute_mode, op0, ml,
+                                                  NULL_RTX, 0,
+                                                  max_cost - extra_cost);
+                       if (t1 == 0)
+                         goto fail1;
+                       t2 = force_operand (gen_rtx (PLUS, compute_mode, t1, op0),
+                                           NULL_RTX);
+                       t3 = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+                                          build_int_2 (post_shift, 0), NULL_RTX, 0);
+                       t4 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                          build_int_2 (size - 1, 0), NULL_RTX, 0);
+                       if (d < 0)
+                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t4, t3),
+                                                   tquotient);
+                       else
+                         quotient = force_operand (gen_rtx (MINUS, compute_mode, t3, t4),
+                                                   tquotient);
+                     }
+                 }
+               else            /* Too wide mode to use tricky code */
+                 break;
 
-             /* We cannot allow TEMP to be ADJUSTED_OP0 here.  */
-             temp = expand_shift (RSHIFT_EXPR, compute_mode, temp,
-                                  build_int_2 (size - log, 0),
-                                  temp != adjusted_op0 ? temp : NULL_RTX, 1);
+               insn = get_last_insn ();
+               if (insn != last
+                   && (set = single_set (insn)) != 0
+                   && SET_DEST (set) == quotient)
+                 REG_NOTES (insn)
+                   = gen_rtx (EXPR_LIST, REG_EQUAL,
+                              gen_rtx (DIV, compute_mode, op0, op1),
+                              REG_NOTES (insn));
+             }
+           break;
+         }
+      fail1:
+       delete_insns_since (last);
+       break;
 
-             adjusted_op0 = expand_binop (compute_mode, add_optab,
-                                          adjusted_op0, temp, adjusted_op0,
-                                          0, OPTAB_LIB_WIDEN);
-           }
-         else
-           {
-             rtx label = gen_label_rtx ();
+      case FLOOR_DIV_EXPR:
+      case FLOOR_MOD_EXPR:
+      /* We will come here only for signed operations.  */
+       if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
+         {
+           unsigned HOST_WIDE_INT mh, ml;
+           int pre_shift, lgup, post_shift;
+           HOST_WIDE_INT d = INTVAL (op1);
+
+           if (d > 0)
+             {
+               /* We could just as easily deal with negative constants here,
+                  but it does not seem worth the trouble for GCC 2.6.  */
+               if (EXACT_POWER_OF_2_OR_ZERO_P (d))
+                 {
+                   pre_shift = floor_log2 (d);
+                   if (rem_flag)
+                     {
+                       remainder = expand_binop (compute_mode, and_optab, op0,
+                                                 GEN_INT (((HOST_WIDE_INT) 1 << pre_shift) - 1),
+                                                 remainder, 0, OPTAB_LIB_WIDEN);
+                       if (remainder)
+                         return gen_lowpart (mode, remainder);
+                     }
+                   quotient = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                            build_int_2 (pre_shift, 0),
+                                            tquotient, 0);
+                 }
+               else
+                 {
+                   rtx t1, t2, t3, t4;
+
+                   mh = choose_multiplier (d, size, size - 1,
+                                           &ml, &post_shift, &lgup);
+                   if (mh)
+                     abort ();
+
+                   t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                      build_int_2 (size - 1, 0), NULL_RTX, 0);
+                   t2 = expand_binop (compute_mode, xor_optab, op0, t1,
+                                      NULL_RTX, 0, OPTAB_WIDEN);
+                   extra_cost = (shift_cost[post_shift]
+                                 + shift_cost[size - 1] + 2 * add_cost);
+                   t3 = expand_mult_highpart (compute_mode, t2, ml,
+                                              NULL_RTX, 1,
+                                              max_cost - extra_cost);
+                   if (t3 != 0)
+                     {
+                       t4 = expand_shift (RSHIFT_EXPR, compute_mode, t3,
+                                          build_int_2 (post_shift, 0),
+                                          NULL_RTX, 1);
+                       quotient = expand_binop (compute_mode, xor_optab,
+                                                t4, t1, tquotient, 0,
+                                                OPTAB_WIDEN);
+                     }
+                 }
+             }
+           else
+             {
+               rtx nsign, t1, t2, t3, t4;
+               t1 = force_operand (gen_rtx (PLUS, compute_mode,
+                                            op0, constm1_rtx), NULL_RTX);
+               t2 = expand_binop (compute_mode, ior_optab, op0, t1, NULL_RTX,
+                                  0, OPTAB_WIDEN);
+               nsign = expand_shift (RSHIFT_EXPR, compute_mode, t2,
+                                     build_int_2 (size - 1, 0), NULL_RTX, 0);
+               t3 = force_operand (gen_rtx (MINUS, compute_mode, t1, nsign),
+                                   NULL_RTX);
+               t4 = expand_divmod (0, TRUNC_DIV_EXPR, compute_mode, t3, op1,
+                                   NULL_RTX, 0);
+               if (t4)
+                 {
+                   rtx t5;
+                   t5 = expand_unop (compute_mode, one_cmpl_optab, nsign,
+                                     NULL_RTX, 0);
+                   quotient = force_operand (gen_rtx (PLUS, compute_mode,
+                                                      t4, t5),
+                                             tquotient);
+                 }
+             }
+         }
+
+       if (quotient != 0)
+         break;
+       delete_insns_since (last);
 
-             emit_cmp_insn (adjusted_op0, const0_rtx, GE, 
-                            NULL_RTX, compute_mode, 0, 0);
-             emit_jump_insn (gen_bge (label));
-             expand_inc (adjusted_op0, plus_constant (op1, -1));
-             emit_label (label);
-           }
-         mod_insn_no_good = 1;
-       }
-      break;
+       /* Try using an instruction that produces both the quotient and
+          remainder, using truncation.  We can easily compensate the quotient
+          or remainder to get floor rounding, once we have the remainder.
+          Notice that we compute also the final remainder value here,
+          and return the result right away.  */
+       if (target == 0 || GET_MODE (target) != compute_mode)
+         target = gen_reg_rtx (compute_mode);
+
+       if (rem_flag)
+         {
+           remainder
+             = GET_CODE (target) == REG ? target : gen_reg_rtx (compute_mode);
+           quotient = gen_reg_rtx (compute_mode);
+         }
+       else
+         {
+           quotient
+             = GET_CODE (target) == REG ? target : gen_reg_rtx (compute_mode);
+           remainder = gen_reg_rtx (compute_mode);
+         }
+
+       if (expand_twoval_binop (sdivmod_optab, op0, op1,
+                                quotient, remainder, 0))
+         {
+           /* This could be computed with a branch-less sequence.
+              Save that for later.  */
+           rtx tem;
+           rtx label = gen_label_rtx ();
+           emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
+                          compute_mode, 0, 0);
+           emit_jump_insn (gen_beq (label));
+           tem = expand_binop (compute_mode, xor_optab, op0, op1,
+                               NULL_RTX, 0, OPTAB_WIDEN);
+           emit_cmp_insn (tem, const0_rtx, GE, NULL_RTX, compute_mode, 0, 0);
+           emit_jump_insn (gen_bge (label));
+           expand_dec (quotient, const1_rtx);
+           expand_inc (remainder, op1);
+           emit_label (label);
+           return gen_lowpart (mode, rem_flag ? remainder : quotient);
+         }
 
-    case FLOOR_DIV_EXPR:
-    case FLOOR_MOD_EXPR:
-      if (log < 0 && ! unsignedp)
+       /* No luck with division elimination or divmod.  Have to do it
+          by conditionally adjusting op0 *and* the result.  */
        {
-         rtx label = gen_label_rtx ();
-
-         emit_cmp_insn (adjusted_op0, const0_rtx, GE, 
-                        NULL_RTX, compute_mode, 0, 0);
-         emit_jump_insn (gen_bge (label));
-         expand_dec (adjusted_op0, op1);
+         rtx label1, label2, label3, label4, label5;
+         rtx adjusted_op0;
+         rtx tem;
+
+         quotient = gen_reg_rtx (compute_mode);
+         adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
+         label1 = gen_label_rtx ();
+         label2 = gen_label_rtx ();
+         label3 = gen_label_rtx ();
+         label4 = gen_label_rtx ();
+         label5 = gen_label_rtx ();
+         emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX, compute_mode, 0, 0);
+         emit_jump_insn (gen_blt (label2));
+         emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX,
+                        compute_mode, 0, 0);
+         emit_jump_insn (gen_blt (label1));
+         tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+                             quotient, 0, OPTAB_LIB_WIDEN);
+         if (tem != quotient)
+           emit_move_insn (quotient, tem);
+         emit_jump_insn (gen_jump (label5));
+         emit_barrier ();
+         emit_label (label1);
          expand_inc (adjusted_op0, const1_rtx);
-         emit_label (label);
-         mod_insn_no_good = 1;
+         emit_jump_insn (gen_jump (label4));
+         emit_barrier ();
+         emit_label (label2);
+         emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX,
+                        compute_mode, 0, 0);
+         emit_jump_insn (gen_bgt (label3));
+         tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+                             quotient, 0, OPTAB_LIB_WIDEN);
+         if (tem != quotient)
+           emit_move_insn (quotient, tem);
+         emit_jump_insn (gen_jump (label5));
+         emit_barrier ();
+         emit_label (label3);
+         expand_dec (adjusted_op0, const1_rtx);
+         emit_label (label4);
+         tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+                             quotient, 0, OPTAB_LIB_WIDEN);
+         if (tem != quotient)
+           emit_move_insn (quotient, tem);
+         expand_dec (quotient, const1_rtx);
+         emit_label (label5);
        }
-      break;
+       break;
 
-    case CEIL_DIV_EXPR:
-    case CEIL_MOD_EXPR:
-      if (log < 0)
-       {
-         rtx label = 0;
-         if (! unsignedp)
+      case CEIL_DIV_EXPR:
+      case CEIL_MOD_EXPR:
+       if (unsignedp)
+         {
+           if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1)))
+             {
+               rtx t1, t2, t3;
+               unsigned HOST_WIDE_INT d = INTVAL (op1);
+               t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                  build_int_2 (floor_log2 (d), 0),
+                                  tquotient, 1);
+               t2 = expand_binop (compute_mode, and_optab, op0,
+                                  GEN_INT (d - 1),
+                                  NULL_RTX, 1, OPTAB_LIB_WIDEN);
+               t3 = gen_reg_rtx (compute_mode);
+               t3 = emit_store_flag (t3, NE, t2, const0_rtx,
+                                     compute_mode, 1, 1);
+               if (t3 == 0)
+                 {
+                   rtx lab;
+                   lab = gen_label_rtx ();
+                   emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX,
+                                  compute_mode, 0, 0);
+                   emit_jump_insn (gen_beq (lab));
+                   expand_inc (t1, const1_rtx);
+                   emit_label (lab);
+                   quotient = t1;
+                 }
+               else
+                 quotient = force_operand (gen_rtx (PLUS, compute_mode,
+                                                    t1, t3),
+                                           tquotient);
+               break;
+             }
+
+           /* Try using an instruction that produces both the quotient and
+              remainder, using truncation.  We can easily compensate the
+              quotient or remainder to get ceiling rounding, once we have the
+              remainder.  Notice that we compute also the final remainder
+              value here, and return the result right away.  */
+           if (target == 0 || GET_MODE (target) != compute_mode)
+             target = gen_reg_rtx (compute_mode);
+
+           if (rem_flag)
+             {
+               remainder = (GET_CODE (target) == REG
+                            ? target : gen_reg_rtx (compute_mode));
+               quotient = gen_reg_rtx (compute_mode);
+             }
+           else
+             {
+               quotient = (GET_CODE (target) == REG
+                           ? target : gen_reg_rtx (compute_mode));
+               remainder = gen_reg_rtx (compute_mode);
+             }
+
+           if (expand_twoval_binop (udivmod_optab, op0, op1, quotient,
+                                    remainder, 1))
+             {
+               /* This could be computed with a branch-less sequence.
+                  Save that for later.  */
+               rtx label = gen_label_rtx ();
+               emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
+                              compute_mode, 0, 0);
+               emit_jump_insn (gen_beq (label));
+               expand_inc (quotient, const1_rtx);
+               expand_dec (remainder, op1);
+               emit_label (label);
+               return gen_lowpart (mode, rem_flag ? remainder : quotient);
+             }
+
+           /* No luck with division elimination or divmod.  Have to do it
+              by conditionally adjusting op0 *and* the result.  */
            {
-             label = gen_label_rtx ();
-             emit_cmp_insn (adjusted_op0, const0_rtx, LE, 
-                            NULL_RTX, compute_mode, 0, 0);
-             emit_jump_insn (gen_ble (label));
+             rtx label1, label2;
+             rtx adjusted_op0, tem;
+
+             quotient = gen_reg_rtx (compute_mode);
+             adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
+             label1 = gen_label_rtx ();
+             label2 = gen_label_rtx ();
+             emit_cmp_insn (adjusted_op0, const0_rtx, NE, NULL_RTX,
+                            compute_mode, 0, 0);
+             emit_jump_insn (gen_bne (label1));
+             emit_move_insn  (quotient, const0_rtx);
+             emit_jump_insn (gen_jump (label2));
+             emit_barrier ();
+             emit_label (label1);
+             expand_dec (adjusted_op0, const1_rtx);
+             tem = expand_binop (compute_mode, udiv_optab, adjusted_op0, op1,
+                                 quotient, 1, OPTAB_LIB_WIDEN);
+             if (tem != quotient)
+               emit_move_insn (quotient, tem);
+             expand_inc (quotient, const1_rtx);
+             emit_label (label2);
            }
-         expand_inc (adjusted_op0, op1);
-         expand_dec (adjusted_op0, const1_rtx);
-         if (! unsignedp)
-           emit_label (label);
-       }
-      else
-       adjusted_op0 = expand_binop (compute_mode, add_optab,
-                                    adjusted_op0, plus_constant (op1, -1),
-                                    adjusted_op0, 0, OPTAB_LIB_WIDEN);
-
-      mod_insn_no_good = 1;
-      break;
-
-    case ROUND_DIV_EXPR:
-    case ROUND_MOD_EXPR:
-      if (log < 0)
-       {
-         op1 = expand_shift (RSHIFT_EXPR, compute_mode, op1,
-                             integer_one_node, NULL_RTX, 0);
-         if (! unsignedp)
+         }
+       else /* signed */
+         {
+           if (op1_is_constant && EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
+               && INTVAL (op1) >= 0)
+             {
+               /* This is extremely similar to the code for the unsigned case
+                  above.  For 2.7 we should merge these variants, but for
+                  2.6.1 I don't want to touch the code for unsigned since that
+                  get used in C.  The signed case will only be used by other
+                  languages (Ada).  */
+
+               rtx t1, t2, t3;
+               unsigned HOST_WIDE_INT d = INTVAL (op1);
+               t1 = expand_shift (RSHIFT_EXPR, compute_mode, op0,
+                                  build_int_2 (floor_log2 (d), 0),
+                                  tquotient, 0);
+               t2 = expand_binop (compute_mode, and_optab, op0,
+                                  GEN_INT (d - 1),
+                                  NULL_RTX, 1, OPTAB_LIB_WIDEN);
+               t3 = gen_reg_rtx (compute_mode);
+               t3 = emit_store_flag (t3, NE, t2, const0_rtx,
+                                     compute_mode, 1, 1);
+               if (t3 == 0)
+                 {
+                   rtx lab;
+                   lab = gen_label_rtx ();
+                   emit_cmp_insn (t2, const0_rtx, EQ, NULL_RTX,
+                                  compute_mode, 0, 0);
+                   emit_jump_insn (gen_beq (lab));
+                   expand_inc (t1, const1_rtx);
+                   emit_label (lab);
+                   quotient = t1;
+                 }
+               else
+                 quotient = force_operand (gen_rtx (PLUS, compute_mode,
+                                                    t1, t3),
+                                           tquotient);
+               break;
+             }
+
+           /* Try using an instruction that produces both the quotient and
+              remainder, using truncation.  We can easily compensate the
+              quotient or remainder to get ceiling rounding, once we have the
+              remainder.  Notice that we compute also the final remainder
+              value here, and return the result right away.  */
+           if (target == 0 || GET_MODE (target) != compute_mode)
+             target = gen_reg_rtx (compute_mode);
+           if (rem_flag)
+             {
+               remainder= (GET_CODE (target) == REG
+                           ? target : gen_reg_rtx (compute_mode));
+               quotient = gen_reg_rtx (compute_mode);
+             }
+           else
+             {
+               quotient = (GET_CODE (target) == REG
+                           ? target : gen_reg_rtx (compute_mode));
+               remainder = gen_reg_rtx (compute_mode);
+             }
+
+           if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient,
+                                    remainder, 0))
+             {
+               /* This could be computed with a branch-less sequence.
+                  Save that for later.  */
+               rtx tem;
+               rtx label = gen_label_rtx ();
+               emit_cmp_insn (remainder, const0_rtx, EQ, NULL_RTX,
+                              compute_mode, 0, 0);
+               emit_jump_insn (gen_beq (label));
+               tem = expand_binop (compute_mode, xor_optab, op0, op1,
+                                   NULL_RTX, 0, OPTAB_WIDEN);
+               emit_cmp_insn (tem, const0_rtx, LT, NULL_RTX,
+                              compute_mode, 0, 0);
+               emit_jump_insn (gen_blt (label));
+               expand_inc (quotient, const1_rtx);
+               expand_dec (remainder, op1);
+               emit_label (label);
+               return gen_lowpart (mode, rem_flag ? remainder : quotient);
+             }
+
+           /* No luck with division elimination or divmod.  Have to do it
+              by conditionally adjusting op0 *and* the result.  */
            {
-             if (BRANCH_COST >= 2)
-               {
-                 /* Negate OP1 if OP0 < 0.  Do this by computing a temporary
-                    that has all bits equal to the sign bit and exclusive
-                    or-ing it with OP1.  */
-                 rtx temp = expand_shift (RSHIFT_EXPR, compute_mode,
-                                          adjusted_op0,
-                                          build_int_2 (size - 1, 0),
-                                          NULL_RTX, 0);
-                 op1 = expand_binop (compute_mode, xor_optab, op1, temp, op1,
-                                     unsignedp, OPTAB_LIB_WIDEN);
-               }
-             else
-               {
-                 rtx label = gen_label_rtx ();
-                 emit_cmp_insn (adjusted_op0, const0_rtx, GE, NULL_RTX,
-                                compute_mode, 0, 0);
-                 emit_jump_insn (gen_bge (label));
-                 expand_unop (compute_mode, neg_optab, op1, op1, 0);
-                 emit_label (label);
-               }
+             rtx label1, label2, label3, label4, label5;
+             rtx adjusted_op0;
+             rtx tem;
+
+             quotient = gen_reg_rtx (compute_mode);
+             adjusted_op0 = copy_to_mode_reg (compute_mode, op0);
+             label1 = gen_label_rtx ();
+             label2 = gen_label_rtx ();
+             label3 = gen_label_rtx ();
+             label4 = gen_label_rtx ();
+             label5 = gen_label_rtx ();
+             emit_cmp_insn (op1, const0_rtx, LT, NULL_RTX,
+                            compute_mode, 0, 0);
+             emit_jump_insn (gen_blt (label2));
+             emit_cmp_insn (adjusted_op0, const0_rtx, GT, NULL_RTX,
+                            compute_mode, 0, 0);
+             emit_jump_insn (gen_bgt (label1));
+             tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+                                 quotient, 0, OPTAB_LIB_WIDEN);
+             if (tem != quotient)
+               emit_move_insn (quotient, tem);
+             emit_jump_insn (gen_jump (label5));
+             emit_barrier ();
+             emit_label (label1);
+             expand_dec (adjusted_op0, const1_rtx);
+             emit_jump_insn (gen_jump (label4));
+             emit_barrier ();
+             emit_label (label2);
+             emit_cmp_insn (adjusted_op0, const0_rtx, LT, NULL_RTX,
+                            compute_mode, 0, 0);
+             emit_jump_insn (gen_blt (label3));
+             tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+                                 quotient, 0, OPTAB_LIB_WIDEN);
+             if (tem != quotient)
+               emit_move_insn (quotient, tem);
+             emit_jump_insn (gen_jump (label5));
+             emit_barrier ();
+             emit_label (label3);
+             expand_inc (adjusted_op0, const1_rtx);
+             emit_label (label4);
+             tem = expand_binop (compute_mode, sdiv_optab, adjusted_op0, op1,
+                                 quotient, 0, OPTAB_LIB_WIDEN);
+             if (tem != quotient)
+               emit_move_insn (quotient, tem);
+             expand_inc (quotient, const1_rtx);
+             emit_label (label5);
            }
-         expand_inc (adjusted_op0, op1);
-       }
-      else
-       expand_inc (adjusted_op0, GEN_INT (((HOST_WIDE_INT) 1 << log) / 2));
+         }
+       break;
 
-      mod_insn_no_good = 1;
-      break;
-    }
+      case EXACT_DIV_EXPR:
+       if (op1_is_constant && HOST_BITS_PER_WIDE_INT >= size)
+         {
+           HOST_WIDE_INT d = INTVAL (op1);
+           unsigned HOST_WIDE_INT ml;
+           int post_shift;
+           rtx t1;
+
+           post_shift = floor_log2 (d & -d);
+           ml = invert_mod2n (d >> post_shift, size);
+           t1 = expand_mult (compute_mode, op0, GEN_INT (ml), NULL_RTX,
+                             unsignedp);
+           quotient = expand_shift (RSHIFT_EXPR, compute_mode, t1,
+                                    build_int_2 (post_shift, 0),
+                                    NULL_RTX, unsignedp);
+
+           insn = get_last_insn ();
+           REG_NOTES (insn)
+             = gen_rtx (EXPR_LIST, REG_EQUAL,
+                        gen_rtx (unsignedp ? UDIV : DIV, compute_mode,
+                                 op0, op1),
+                        REG_NOTES (insn));
+         }
+       break;
+
+      case ROUND_DIV_EXPR:
+      case ROUND_MOD_EXPR:
+       if (unsignedp)
+         {
+           rtx tem;
+           rtx label;
+           label = gen_label_rtx ();
+           quotient = gen_reg_rtx (compute_mode);
+           remainder = gen_reg_rtx (compute_mode);
+           if (expand_twoval_binop (udivmod_optab, op0, op1, quotient, remainder, 1) == 0)
+             {
+               rtx tem;
+               quotient = expand_binop (compute_mode, udiv_optab, op0, op1,
+                                        quotient, 1, OPTAB_LIB_WIDEN);
+               tem = expand_mult (compute_mode, quotient, op1, NULL_RTX, 1);
+               remainder = expand_binop (compute_mode, sub_optab, op0, tem,
+                                         remainder, 1, OPTAB_LIB_WIDEN);
+             }
+           tem = plus_constant (op1, -1);
+           tem = expand_shift (RSHIFT_EXPR, compute_mode, tem,
+                               build_int_2 (1, 0), NULL_RTX, 1);
+           emit_cmp_insn (remainder, tem, LEU, NULL_RTX, compute_mode, 0, 0);
+           emit_jump_insn (gen_bleu (label));
+           expand_inc (quotient, const1_rtx);
+           expand_dec (remainder, op1);
+           emit_label (label);
+         }
+       else
+         {
+           rtx abs_rem, abs_op1, tem, mask;
+           rtx label;
+           label = gen_label_rtx ();
+           quotient = gen_reg_rtx (compute_mode);
+           remainder = gen_reg_rtx (compute_mode);
+           if (expand_twoval_binop (sdivmod_optab, op0, op1, quotient, remainder, 0) == 0)
+             {
+               rtx tem;
+               quotient = expand_binop (compute_mode, sdiv_optab, op0, op1,
+                                        quotient, 0, OPTAB_LIB_WIDEN);
+               tem = expand_mult (compute_mode, quotient, op1, NULL_RTX, 0);
+               remainder = expand_binop (compute_mode, sub_optab, op0, tem,
+                                         remainder, 0, OPTAB_LIB_WIDEN);
+             }
+           abs_rem = expand_abs (compute_mode, remainder, NULL_RTX, 0, 0);
+           abs_op1 = expand_abs (compute_mode, op1, NULL_RTX, 0, 0);
+           tem = expand_shift (LSHIFT_EXPR, compute_mode, abs_rem,
+                               build_int_2 (1, 0), NULL_RTX, 1);
+           emit_cmp_insn (tem, abs_op1, LTU, NULL_RTX, compute_mode, 0, 0);
+           emit_jump_insn (gen_bltu (label));
+           tem = expand_binop (compute_mode, xor_optab, op0, op1,
+                               NULL_RTX, 0, OPTAB_WIDEN);
+           mask = expand_shift (RSHIFT_EXPR, compute_mode, tem,
+                               build_int_2 (size - 1, 0), NULL_RTX, 0);
+           tem = expand_binop (compute_mode, xor_optab, mask, const1_rtx,
+                               NULL_RTX, 0, OPTAB_WIDEN);
+           tem = expand_binop (compute_mode, sub_optab, tem, mask,
+                               NULL_RTX, 0, OPTAB_WIDEN);
+           expand_inc (quotient, tem);
+           tem = expand_binop (compute_mode, xor_optab, mask, op1,
+                               NULL_RTX, 0, OPTAB_WIDEN);
+           tem = expand_binop (compute_mode, sub_optab, tem, mask,
+                               NULL_RTX, 0, OPTAB_WIDEN);
+           expand_dec (remainder, tem);
+           emit_label (label);
+         }
+       return gen_lowpart (mode, rem_flag ? remainder : quotient);
+      }
 
-  if (rem_flag && !mod_insn_no_good)
+  if (quotient == 0)
     {
-      /* Try to produce the remainder directly */
-      if (log >= 0)
-       result = expand_binop (compute_mode, and_optab, adjusted_op0,
-                              GEN_INT (((HOST_WIDE_INT) 1 << log) - 1),
-                              target, 1, OPTAB_LIB_WIDEN);
-      else
+      if (target && GET_MODE (target) != compute_mode)
+       target = 0;
+
+      if (rem_flag)
        {
-         /* See if we can do remainder without a library call.  */
-         result = sign_expand_binop (mode, umod_optab, smod_optab,
-                                     adjusted_op0, op1, target,
-                                     unsignedp, OPTAB_WIDEN);
-         if (result == 0)
+         /* Try to produce the remainder directly without a library call.  */
+         remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
+                                        op0, op1, target,
+                                        unsignedp, OPTAB_WIDEN);
+         if (remainder == 0)
            {
              /* No luck there.  Can we do remainder and divide at once
                 without a library call?  */
-             result = gen_reg_rtx (compute_mode);
-             if (! expand_twoval_binop (unsignedp
-                                        ? udivmod_optab : sdivmod_optab,
-                                        adjusted_op0, op1,
-                                        NULL_RTX, result, unsignedp))
-               result = 0;
+             remainder = gen_reg_rtx (compute_mode);
+             if (! expand_twoval_binop ((unsignedp
+                                         ? udivmod_optab
+                                         : sdivmod_optab),
+                                        op0, op1,
+                                        NULL_RTX, remainder, unsignedp))
+               remainder = 0;
            }
+
+         if (remainder)
+           return gen_lowpart (mode, remainder);
        }
-    }
 
-  if (result)
-    return gen_lowpart (mode, result);
-
-  /* Produce the quotient.  */
-  if (log >= 0)
-    result = expand_shift (RSHIFT_EXPR, compute_mode, adjusted_op0,
-                          build_int_2 (log, 0), target, unsignedp);
-  else if (rem_flag && !mod_insn_no_good)
-    /* If producing quotient in order to subtract for remainder,
-       and a remainder subroutine would be ok,
-       don't use a divide subroutine.  */
-    result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
-                               adjusted_op0, op1, NULL_RTX, unsignedp,
-                               OPTAB_WIDEN);
-  else
-    {
+      /* Produce the quotient.  */
       /* Try a quotient insn, but not a library call.  */
-      result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
-                                 adjusted_op0, op1,
-                                 rem_flag ? NULL_RTX : target,
-                                 unsignedp, OPTAB_WIDEN);
-      if (result == 0)
+      quotient = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
+                                   op0, op1, rem_flag ? NULL_RTX : target,
+                                   unsignedp, OPTAB_WIDEN);
+      if (quotient == 0)
        {
          /* No luck there.  Try a quotient-and-remainder insn,
             keeping the quotient alone.  */
-         result = gen_reg_rtx (compute_mode);
+         quotient = gen_reg_rtx (compute_mode);
          if (! expand_twoval_binop (unsignedp ? udivmod_optab : sdivmod_optab,
-                                    adjusted_op0, op1,
-                                    result, NULL_RTX, unsignedp))
-           result = 0;
+                                    op0, op1,
+                                    quotient, NULL_RTX, unsignedp))
+           {
+             quotient = 0;
+             if (! rem_flag)
+               /* Still no luck.  If we are not computing the remainder,
+                  use a library call for the quotient.  */
+               quotient = sign_expand_binop (compute_mode,
+                                             udiv_optab, sdiv_optab,
+                                             op0, op1, target,
+                                             unsignedp, OPTAB_LIB_WIDEN);
+           }
        }
-
-      /* If still no luck, use a library call.  */
-      if (result == 0)
-       result = sign_expand_binop (compute_mode, udiv_optab, sdiv_optab,
-                                   adjusted_op0, op1,
-                                   rem_flag ? NULL_RTX : target,
-                                   unsignedp, OPTAB_LIB_WIDEN);
     }
 
-  /* If we really want the remainder, get it by subtraction.  */
   if (rem_flag)
     {
-      if (result == 0)
+      if (target && GET_MODE (target) != compute_mode)
+       target = 0;
+
+      if (quotient == 0)
        /* No divide instruction either.  Use library for remainder.  */
-       result = sign_expand_binop (compute_mode, umod_optab, smod_optab,
-                                   op0, op1, target,
-                                   unsignedp, OPTAB_LIB_WIDEN);
+       remainder = sign_expand_binop (compute_mode, umod_optab, smod_optab,
+                                      op0, op1, target,
+                                      unsignedp, OPTAB_LIB_WIDEN);
       else
        {
          /* We divided.  Now finish doing X - Y * (X / Y).  */
-         result = expand_mult (compute_mode, result, op1, target, unsignedp);
-         if (! result) abort ();
-         result = expand_binop (compute_mode, sub_optab, op0,
-                                result, target, unsignedp, OPTAB_LIB_WIDEN);
+         remainder = expand_mult (compute_mode, quotient, op1,
+                                  NULL_RTX, unsignedp);
+         remainder = expand_binop (compute_mode, sub_optab, op0,
+                                   remainder, target, unsignedp,
+                                   OPTAB_LIB_WIDEN);
        }
     }
 
-  if (result == 0)
-    abort ();
-
-  return gen_lowpart (mode, result);
+  return gen_lowpart (mode, rem_flag ? remainder : quotient);
 }
 \f
 /* Return a tree node with data type TYPE, describing the value of X.
@@ -2813,7 +3869,7 @@ expand_and (op0, op1, target)
    to perform the operation.  It says to use zero-extension.
 
    NORMALIZEP is 1 if we should convert the result to be either zero
-   or one one.  Normalize is -1 if we should convert the result to be
+   or one.  Normalize is -1 if we should convert the result to be
    either zero or -1.  If NORMALIZEP is zero, the result will be left
    "raw" out of the scc insn.  */
 
@@ -2831,12 +3887,9 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   enum machine_mode compare_mode;
   enum machine_mode target_mode = GET_MODE (target);
   rtx tem;
-  rtx last = 0;
+  rtx last = get_last_insn ();
   rtx pattern, comparison;
 
-  if (mode == VOIDmode)
-    mode = GET_MODE (op0);
-
   /* If one operand is constant, make it the second one.  Only do this
      if the other operand is not constant as well.  */
 
@@ -2849,6 +3902,9 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       code = swap_condition (code);
     }
 
+  if (mode == VOIDmode)
+    mode = GET_MODE (op0);
+
   /* For some comparisons with 1 and -1, we can convert this to 
      comparisons with zero.  This will often produce more opportunities for
      store-flag insns. */
@@ -2909,9 +3965,11 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
        subtarget = 0;
 
       if (code == GE)
-       op0 = expand_unop (mode, one_cmpl_optab, op0, subtarget, 0);
+       op0 = expand_unop (mode, one_cmpl_optab, op0,
+                          ((STORE_FLAG_VALUE == 1 || normalizep)
+                           ? 0 : subtarget), 0);
 
-      if (normalizep || STORE_FLAG_VALUE == 1)
+      if (STORE_FLAG_VALUE == 1 || normalizep)
        /* If we are supposed to produce a 0/1 value, we want to do
           a logical shift from the sign bit to the low-order bit; for
           a -1/0 value, we do an arithmetic shift.  */
@@ -3029,10 +4087,13 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
        }
     }
 
-  if (last)
-    delete_insns_since (last);
+  delete_insns_since (last);
 
-  subtarget = target_mode == mode ? target : 0;
+  /* If expensive optimizations, use different pseudo registers for each
+     insn, instead of reusing the same pseudo.  This leads to better CSE,
+     but slows down the compiler, since there are more pseudos */
+  subtarget = (!flag_expensive_optimizations
+              && (target_mode == mode)) ? target : NULL_RTX;
 
   /* If we reached here, we can't do this with a scc insn.  However, there
      are some comparisons that can be done directly.  For example, if
@@ -3177,15 +4238,22 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   if (tem && normalizep)
     tem = expand_shift (RSHIFT_EXPR, mode, tem,
                        size_int (GET_MODE_BITSIZE (mode) - 1),
-                       tem, normalizep == 1);
+                       subtarget, normalizep == 1);
 
-  if (tem && GET_MODE (tem) != target_mode)
+  if (tem)
     {
-      convert_move (target, tem, 0);
-      tem = target;
+      if (GET_MODE (tem) != target_mode)
+       {
+         convert_move (target, tem, 0);
+         tem = target;
+       }
+      else if (!subtarget)
+       {
+         emit_move_insn (target, tem);
+         tem = target;
+       }
     }
-
-  if (tem == 0)
+  else
     delete_insns_since (last);
 
   return tem;