OSDN Git Service

2001-11-11 H.J. Lu <hjl@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index 0a9b41c..9435a62 100644 (file)
@@ -3,22 +3,22 @@
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
    1999, 2000, 2001 Free Software Foundation, Inc.
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 2, or (at your option) any later
+version.
 
-GNU CC is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-GNU General Public License for more details.
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the 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, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 
 #include "config.h"
@@ -30,6 +30,7 @@ Boston, MA 02111-1307, USA.  */
 #include "flags.h"
 #include "insn-config.h"
 #include "expr.h"
+#include "optabs.h"
 #include "real.h"
 #include "recog.h"
 
@@ -74,6 +75,23 @@ static int sdiv_pow2_cheap, smod_pow2_cheap;
 #define MAX_BITS_PER_WORD BITS_PER_WORD
 #endif
 
+/* Reduce conditional compilation elsewhere.  */
+#ifndef HAVE_insv
+#define HAVE_insv      0
+#define CODE_FOR_insv  CODE_FOR_nothing
+#define gen_insv(a,b,c,d) NULL_RTX
+#endif
+#ifndef HAVE_extv
+#define HAVE_extv      0
+#define CODE_FOR_extv  CODE_FOR_nothing
+#define gen_extv(a,b,c,d) NULL_RTX
+#endif
+#ifndef HAVE_extzv
+#define HAVE_extzv     0
+#define CODE_FOR_extzv CODE_FOR_nothing
+#define gen_extzv(a,b,c,d) NULL_RTX
+#endif
+
 /* 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;
@@ -202,6 +220,58 @@ negate_rtx (mode, x)
 
   return result;
 }
+
+/* Report on the availability of insv/extv/extzv and the desired mode
+   of each of their operands.  Returns MAX_MACHINE_MODE if HAVE_foo
+   is false; else the mode of the specified operand.  If OPNO is -1,
+   all the caller cares about is whether the insn is available.  */
+enum machine_mode
+mode_for_extraction (pattern, opno)
+     enum extraction_pattern pattern;
+     int opno;
+{
+  const struct insn_data *data;
+
+  switch (pattern)
+    {
+    case EP_insv:
+      if (HAVE_insv)
+       {
+         data = &insn_data[CODE_FOR_insv];
+         break;
+       }
+      return MAX_MACHINE_MODE;
+
+    case EP_extv:
+      if (HAVE_extv)
+       {
+         data = &insn_data[CODE_FOR_extv];
+         break;
+       }
+      return MAX_MACHINE_MODE;
+
+    case EP_extzv:
+      if (HAVE_extzv)
+       {
+         data = &insn_data[CODE_FOR_extzv];
+         break;
+       }
+      return MAX_MACHINE_MODE;
+
+    default:
+      abort ();
+    }
+
+  if (opno == -1)
+    return VOIDmode;
+
+  /* Everyone who uses this function used to follow it with
+     if (result == VOIDmode) result = word_mode; */
+  if (data->operand[opno].mode == VOIDmode)
+    return word_mode;
+  return data->operand[opno].mode;
+}
+
 \f
 /* Generate code to store value from rtx VALUE
    into a bit-field within structure STR_RTX
@@ -232,16 +302,9 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
     = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
   unsigned HOST_WIDE_INT offset = bitnum / unit;
   unsigned HOST_WIDE_INT bitpos = bitnum % unit;
-  register rtx op0 = str_rtx;
-#ifdef HAVE_insv
-  unsigned HOST_WIDE_INT insv_bitsize;
-  enum machine_mode op_mode;
-
-  op_mode = insn_data[(int) CODE_FOR_insv].operand[3].mode;
-  if (op_mode == VOIDmode)
-    op_mode = word_mode;
-  insv_bitsize = GET_MODE_BITSIZE (op_mode);
-#endif
+  rtx op0 = str_rtx;
+
+  enum machine_mode op_mode = mode_for_extraction (EP_insv, 3);
 
   /* It is wrong to have align==0, since every object is aligned at
      least at a bit boundary.  This usually means a bug elsewhere.  */
@@ -267,14 +330,6 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       op0 = SUBREG_REG (op0);
     }
 
-  /* 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 (BYTES_BIG_ENDIAN
-      && GET_CODE (op0) != MEM
-      && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
-    bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
-
   value = protect_from_queue (value, 0);
 
   if (flag_force_mem)
@@ -287,14 +342,14 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
      done with a simple store.  For targets that support fast unaligned
      memory, any naturally sized, unit aligned field can be done directly.  */
      
-  if (bitsize == GET_MODE_BITSIZE (fieldmode)
+  if (bitpos == 0
+      && bitsize == GET_MODE_BITSIZE (fieldmode)
       && (GET_CODE (op0) != MEM
          ? (GET_MODE_SIZE (fieldmode) >= UNITS_PER_WORD
             || GET_MODE_SIZE (GET_MODE (op0)) == GET_MODE_SIZE (fieldmode))
          : (! SLOW_UNALIGNED_ACCESS (fieldmode, align)
             || (offset * BITS_PER_UNIT % bitsize == 0
-                && align % GET_MODE_BITSIZE (fieldmode) == 0)))
-      && (BYTES_BIG_ENDIAN ? bitpos + bitsize == unit : bitpos == 0))
+                && align % GET_MODE_BITSIZE (fieldmode) == 0))))
     {
       if (GET_MODE (op0) != fieldmode)
        {
@@ -338,6 +393,14 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
       }
   }
 
+  /* 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 (BYTES_BIG_ENDIAN
+      && GET_CODE (op0) != MEM
+      && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
+    bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
+
   /* Storing an lsb-aligned field in a register
      can be done with a movestrict instruction.  */
 
@@ -474,27 +537,22 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
   /* 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.  */
-      && (insv_bitsize >= bitsize)
+      && (GET_MODE_BITSIZE (op_mode) >= bitsize)
       && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-           && (bitsize + bitpos > insv_bitsize)))
+           && (bitsize + bitpos > GET_MODE_BITSIZE (op_mode))))
     {
       int xbitpos = bitpos;
       rtx value1;
       rtx xop0 = op0;
       rtx last = get_last_insn ();
       rtx pat;
-      enum machine_mode maxmode;
+      enum machine_mode maxmode = mode_for_extraction (EP_insv, 3);
       int save_volatile_ok = volatile_ok;
 
-      maxmode = insn_data[(int) CODE_FOR_insv].operand[3].mode;
-      if (maxmode == VOIDmode)
-       maxmode = word_mode;
-
       volatile_ok = 1;
 
       /* If this machine's insv can only insert into a register, copy OP0
@@ -580,12 +638,7 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
                 if we must narrow it, be sure we do it correctly.  */
 
              if (GET_MODE_SIZE (GET_MODE (value)) < GET_MODE_SIZE (maxmode))
-               {
-                 /* Avoid making subreg of a subreg, or of a mem.  */
-                 if (GET_CODE (value1) != REG)
-                   value1 = copy_to_reg (value1);
-                 value1 = gen_rtx_SUBREG (maxmode, value1, 0);
-               }
+               value1 = simplify_gen_subreg (maxmode, value1, GET_MODE (value1), 0);
              else
                value1 = gen_lowpart (maxmode, value1);
            }
@@ -616,7 +669,6 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
     }
   else
     insv_loses:
-#endif
     /* Insv is not available; store using shifts and boolean ops.  */
     store_fixed_bit_field (op0, offset, bitsize, bitpos, value, align);
   return value;
@@ -636,12 +688,12 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, align, total_size)
 
 static void
 store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
-     register rtx op0;
+     rtx op0;
      unsigned HOST_WIDE_INT offset, bitsize, bitpos;
-     register rtx value;
+     rtx value;
      unsigned int struct_align;
 {
-  register enum machine_mode mode;
+  enum machine_mode mode;
   unsigned int total_bits = BITS_PER_WORD;
   rtx subtarget, temp;
   int all_zero = 0;
@@ -673,10 +725,15 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
     {
       /* Get the proper mode to use for this field.  We want a mode that
         includes the entire field.  If such a mode would be larger than
-        a word, we won't be doing the extraction the normal way.  */
+        a word, we won't be doing the extraction the normal way.  
+        We don't want a mode bigger than the destination.  */
 
+      mode = GET_MODE (op0);
+      if (GET_MODE_BITSIZE (mode) == 0
+          || GET_MODE_BITSIZE (mode) > GET_MODE_BITSIZE (word_mode))
+        mode = word_mode;
       mode = get_best_mode (bitsize, bitpos + offset * BITS_PER_UNIT,
-                           struct_align, word_mode,
+                           struct_align, mode,
                            GET_CODE (op0) == MEM && MEM_VOLATILE_P (op0));
 
       if (mode == VOIDmode)
@@ -732,7 +789,7 @@ store_fixed_bit_field (op0, offset, bitsize, bitpos, value, struct_align)
 
   if (GET_CODE (value) == CONST_INT)
     {
-      register HOST_WIDE_INT v = INTVAL (value);
+      HOST_WIDE_INT v = INTVAL (value);
 
       if (bitsize < HOST_BITS_PER_WIDE_INT)
        v &= ((HOST_WIDE_INT) 1 << bitsize) - 1;
@@ -971,32 +1028,12 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
     = (GET_CODE (str_rtx) == MEM) ? BITS_PER_UNIT : BITS_PER_WORD;
   unsigned HOST_WIDE_INT offset = bitnum / unit;
   unsigned HOST_WIDE_INT bitpos = bitnum % unit;
-  register rtx op0 = str_rtx;
+  rtx op0 = str_rtx;
   rtx spec_target = target;
   rtx spec_target_subreg = 0;
   enum machine_mode int_mode;
-#ifdef HAVE_extv
-  unsigned HOST_WIDE_INT extv_bitsize;
-  enum machine_mode extv_mode;
-#endif
-#ifdef HAVE_extzv
-  unsigned HOST_WIDE_INT extzv_bitsize;
-  enum machine_mode extzv_mode;
-#endif
-
-#ifdef HAVE_extv
-  extv_mode = insn_data[(int) CODE_FOR_extv].operand[0].mode;
-  if (extv_mode == VOIDmode)
-    extv_mode = word_mode;
-  extv_bitsize = GET_MODE_BITSIZE (extv_mode);
-#endif
-
-#ifdef HAVE_extzv
-  extzv_mode = insn_data[(int) CODE_FOR_extzv].operand[0].mode;
-  if (extzv_mode == VOIDmode)
-    extzv_mode = word_mode;
-  extzv_bitsize = GET_MODE_BITSIZE (extzv_mode);
-#endif
+  enum machine_mode extv_mode = mode_for_extraction (EP_extv, 0);
+  enum machine_mode extzv_mode = mode_for_extraction (EP_extzv, 0);
 
   /* Discount the part of the structure before the desired byte.
      We need to know how many bytes are safe to reference after it.  */
@@ -1230,11 +1267,10 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
   if (unsignedp)
     {
-#ifdef HAVE_extzv
       if (HAVE_extzv
-         && (extzv_bitsize >= bitsize)
+         && (GET_MODE_BITSIZE (extzv_mode) >= bitsize)
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-               && (bitsize + bitpos > extzv_bitsize)))
+               && (bitsize + bitpos > GET_MODE_BITSIZE (extzv_mode))))
        {
          unsigned HOST_WIDE_INT xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
@@ -1244,11 +1280,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          rtx xspec_target = spec_target;
          rtx xspec_target_subreg = spec_target_subreg;
          rtx pat;
-         enum machine_mode maxmode;
-
-         maxmode = insn_data[(int) CODE_FOR_extzv].operand[0].mode;
-         if (maxmode == VOIDmode)
-           maxmode = word_mode;
+         enum machine_mode maxmode = mode_for_extraction (EP_extzv, 0);
 
          if (GET_CODE (xop0) == MEM)
            {
@@ -1363,17 +1395,15 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
        }
       else
         extzv_loses:
-#endif
       target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, 
                                        bitpos, target, 1, align);
     }
   else
     {
-#ifdef HAVE_extv
       if (HAVE_extv
-         && (extv_bitsize >= bitsize)
+         && (GET_MODE_BITSIZE (extv_mode) >= bitsize)
          && ! ((GET_CODE (op0) == REG || GET_CODE (op0) == SUBREG)
-               && (bitsize + bitpos > extv_bitsize)))
+               && (bitsize + bitpos > GET_MODE_BITSIZE (extv_mode))))
        {
          int xbitpos = bitpos, xoffset = offset;
          rtx bitsize_rtx, bitpos_rtx;
@@ -1382,11 +1412,7 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
          rtx xspec_target = spec_target;
          rtx xspec_target_subreg = spec_target_subreg;
          rtx pat;
-         enum machine_mode maxmode;
-
-         maxmode = insn_data[(int) CODE_FOR_extv].operand[0].mode;
-         if (maxmode == VOIDmode)
-           maxmode = word_mode;
+         enum machine_mode maxmode = mode_for_extraction (EP_extv, 0);
 
          if (GET_CODE (xop0) == MEM)
            {
@@ -1497,7 +1523,6 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
        } 
       else
        extv_loses:
-#endif
       target = extract_fixed_bit_field (int_mode, op0, offset, bitsize, 
                                        bitpos, target, 0, align);
     }
@@ -1547,7 +1572,7 @@ static rtx
 extract_fixed_bit_field (tmode, op0, offset, bitsize, bitpos,
                         target, unsignedp, align)
      enum machine_mode tmode;
-     register rtx op0, target;
+     rtx op0, target;
      unsigned HOST_WIDE_INT offset, bitsize, bitpos;
      int unsignedp;
      unsigned int align;
@@ -1901,15 +1926,15 @@ expand_dec (target, dec)
 rtx
 expand_shift (code, mode, shifted, amount, target, unsignedp)
      enum tree_code code;
-     register enum machine_mode mode;
+     enum machine_mode mode;
      rtx shifted;
      tree amount;
-     register rtx target;
+     rtx target;
      int unsignedp;
 {
-  register rtx op1, temp = 0;
-  register int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
-  register int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
+  rtx op1, temp = 0;
+  int left = (code == LSHIFT_EXPR || code == LROTATE_EXPR);
+  int rotate = (code == LROTATE_EXPR || code == RROTATE_EXPR);
   int try;
 
   /* Previously detected shift-counts computed by NEGATE_EXPR
@@ -1994,7 +2019,8 @@ expand_shift (code, mode, shifted, amount, target, unsignedp)
             that is in range, try a rotate in the opposite direction.  */
 
          if (temp == 0 && GET_CODE (op1) == CONST_INT
-             && INTVAL (op1) > 0 && INTVAL (op1) < GET_MODE_BITSIZE (mode))
+             && INTVAL (op1) > 0
+             && (unsigned int) INTVAL (op1) < GET_MODE_BITSIZE (mode))
            temp = expand_binop (mode,
                                 left ? rotr_optab : rotl_optab,
                                 shifted, 
@@ -2340,7 +2366,7 @@ synth_mult (alg_out, t, cost_limit)
 rtx
 expand_mult (mode, op0, op1, target, unsignedp)
      enum machine_mode mode;
-     register rtx op0, op1, target;
+     rtx op0, op1, target;
      int unsignedp;
 {
   rtx const_op1 = op1;
@@ -2380,6 +2406,10 @@ expand_mult (mode, op0, op1, target, unsignedp)
       int mult_cost;
       enum {basic_variant, negate_variant, add_variant} variant = basic_variant;
 
+      /* op0 must be register to make mult_cost match the precomputed
+         shiftadd_cost array.  */
+      op0 = force_reg (mode, op0);
+
       /* 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.  */
@@ -2726,7 +2756,7 @@ invert_mod2n (x, n)
 rtx
 expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
      enum machine_mode mode;
-     register rtx adj_operand, op0, op1, target;
+     rtx adj_operand, op0, op1, target;
      int unsignedp;
 {
   rtx tem;
@@ -2763,7 +2793,7 @@ expand_mult_highpart_adjust (mode, adj_operand, op0, op1, target, unsignedp)
 rtx
 expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
      enum machine_mode mode;
-     register rtx op0, target;
+     rtx op0, target;
      unsigned HOST_WIDE_INT cnst1;
      int unsignedp;
      int max_cost;
@@ -2945,11 +2975,11 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      int rem_flag;
      enum tree_code code;
      enum machine_mode mode;
-     register rtx op0, op1, target;
+     rtx op0, op1, target;
      int unsignedp;
 {
   enum machine_mode compute_mode;
-  register rtx tquotient;
+  rtx tquotient;
   rtx quotient = 0, remainder = 0;
   rtx last;
   int size;
@@ -4019,7 +4049,7 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
 /* Return a tree node with data type TYPE, describing the value of X.
    Usually this is an RTL_EXPR, if there is no obvious better choice.
    X may be an expression, however we only support those expressions
-   generated by loop.c.   */
+   generated by loop.c.  */
 
 tree
 make_tree (type, x)
@@ -4552,7 +4582,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
 
       /* Note that ABS doesn't yield a positive number for INT_MIN, but 
         that is compensated by the subsequent overflow when subtracting 
-        one / negating. */
+        one / negating.  */
 
       if (abs_optab->handlers[(int) mode].insn_code != CODE_FOR_nothing)
        tem = expand_unop (mode, abs_optab, op0, subtarget, 1);