OSDN Git Service

* (REG_CLASS_FROM_CONSTRAINT): Only define if not already defined.
[pf3gnuchains/gcc-fork.git] / gcc / expmed.c
index 7670f1c..4300756 100644 (file)
@@ -1,7 +1,7 @@
 /* Medium-level subroutines: convert bit-field store and extract
    and shifts, multiplies and divides to rtl instructions.
    Copyright (C) 1987, 1988, 1989, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
-   1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
 
 This file is part of GCC.
 
@@ -23,6 +23,8 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "toplev.h"
 #include "rtl.h"
 #include "tree.h"
@@ -54,7 +56,7 @@ static rtx extract_split_bit_field    PARAMS ((rtx, unsigned HOST_WIDE_INT,
 static void do_cmp_and_jump            PARAMS ((rtx, rtx, enum rtx_code,
                                                 enum machine_mode, rtx));
 
-/* Non-zero means divides or modulus operations are relatively cheap for
+/* Nonzero means divides or modulus operations are relatively cheap for
    powers of two, so don't use branches; emit the operation instead.
    Usually, this will mean that the MD file will emit non-branch
    sequences.  */
@@ -104,16 +106,15 @@ static int mul_highpart_cost[NUM_MACHINE_MODES];
 void
 init_expmed ()
 {
-  /* This is "some random pseudo register" for purposes of calling recog
-     to see what insns exist.  */
-  rtx reg = gen_rtx_REG (word_mode, 10000);
-  rtx shift_insn, shiftadd_insn, shiftsub_insn;
+  rtx reg, shift_insn, shiftadd_insn, shiftsub_insn;
   int dummy;
   int m;
   enum machine_mode mode, wider_mode;
 
   start_sequence ();
 
+  /* This is "some random pseudo register" for purposes of calling recog
+     to see what insns exist.  */
   reg = gen_rtx_REG (word_mode, 10000);
 
   zero_cost = rtx_cost (const0_rtx, 0);
@@ -535,7 +536,9 @@ store_bit_field (str_rtx, bitsize, bitnum, fieldmode, value, total_size)
      structure fields.  */
   if (GET_MODE_CLASS (GET_MODE (value)) != MODE_INT
       && GET_MODE_CLASS (GET_MODE (value)) != MODE_PARTIAL_INT)
-    value = gen_lowpart (word_mode, value);
+    value = gen_lowpart ((GET_MODE (value) == VOIDmode
+                         ? word_mode : int_mode_for_mode (GET_MODE (value))),
+                        value);
 
   /* Now OFFSET is nonzero only if OP0 is memory
      and is therefore always measured in bytes.  */
@@ -1032,25 +1035,15 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
 
   if (tmode == VOIDmode)
     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_BYTE (op0) / UNITS_PER_WORD;
-
-      inner_size = MIN (inner_size, BITS_PER_WORD);
-
-      if (BYTES_BIG_ENDIAN && (outer_size < inner_size))
+      bitpos += SUBREG_BYTE (op0) * BITS_PER_UNIT;
+      if (bitpos > unit)
        {
-         bitpos += inner_size - outer_size;
-         if (bitpos > unit)
-           {
-             offset += (bitpos / unit);
-             bitpos %= unit;
-           }
+         offset += (bitpos / unit);
+         bitpos %= unit;
        }
-
       op0 = SUBREG_REG (op0);
     }
 
@@ -1087,9 +1080,13 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       set_mem_expr (op0, 0);
     }
 
-  /* ??? 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.  */
+  /* Extraction of a full-word or multi-word value from a structure
+     in a register or aligned memory can be done with just a SUBREG.
+     A subword value in the least significant part of a register
+     can also be extracted with a SUBREG.  For this, we need the
+     byte offset of the value in op0.  */
+
+  byte_offset = bitpos / BITS_PER_UNIT + offset * UNITS_PER_WORD;
 
   /* If OP0 is a register, BITPOS must count within a word.
      But as we have it, it counts within whatever size OP0 now has.
@@ -1099,38 +1096,33 @@ extract_bit_field (str_rtx, bitsize, bitnum, unsignedp,
       && unit > GET_MODE_BITSIZE (GET_MODE (op0)))
     bitpos += unit - GET_MODE_BITSIZE (GET_MODE (op0));
 
-  /* Extracting a full-word or multi-word value
-     from a structure in a register or aligned memory.
-     This can be done with just SUBREG.
-     So too extracting a subword value in
-     the least significant part of the register.  */
-
-  byte_offset = (bitnum % BITS_PER_WORD) / BITS_PER_UNIT
-                + (offset * UNITS_PER_WORD);
+  /* ??? 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.  */
 
   mode1  = (VECTOR_MODE_P (tmode)
            ? mode
            : mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0));
 
-  if (((GET_CODE (op0) != MEM
-       && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
-                                 GET_MODE_BITSIZE (GET_MODE (op0)))
-       && GET_MODE_SIZE (mode1) != 0
-       && byte_offset % GET_MODE_SIZE (mode1) == 0)
-       || (GET_CODE (op0) == MEM
-          && (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (op0))
-              || (offset * BITS_PER_UNIT % bitsize == 0
-                  && MEM_ALIGN (op0) % bitsize == 0))))
-      && ((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
-          && bitpos % BITS_PER_WORD == 0)
-         || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
-             /* ??? The big endian test here is wrong.  This is correct
-                if the value is in a register, and if mode_for_size is not
-                the same mode as op0.  This causes us to get unnecessarily
-                inefficient code from the Thumb port when -mbig-endian.  */
-             && (BYTES_BIG_ENDIAN
-                 ? bitpos + bitsize == BITS_PER_WORD
-                 : bitpos == 0))))
+  if (((bitsize >= BITS_PER_WORD && bitsize == GET_MODE_BITSIZE (mode)
+       && bitpos % BITS_PER_WORD == 0)
+       || (mode_for_size (bitsize, GET_MODE_CLASS (tmode), 0) != BLKmode
+          /* ??? The big endian test here is wrong.  This is correct
+             if the value is in a register, and if mode_for_size is not
+             the same mode as op0.  This causes us to get unnecessarily
+             inefficient code from the Thumb port when -mbig-endian.  */
+          && (BYTES_BIG_ENDIAN
+              ? bitpos + bitsize == BITS_PER_WORD
+              : bitpos == 0)))
+      && ((GET_CODE (op0) != MEM
+          && TRULY_NOOP_TRUNCATION (GET_MODE_BITSIZE (mode),
+                                    GET_MODE_BITSIZE (GET_MODE (op0)))
+          && GET_MODE_SIZE (mode1) != 0
+          && byte_offset % GET_MODE_SIZE (mode1) == 0)
+         || (GET_CODE (op0) == MEM
+             && (! SLOW_UNALIGNED_ACCESS (mode, MEM_ALIGN (op0))
+                 || (offset * BITS_PER_UNIT % bitsize == 0
+                     && MEM_ALIGN (op0) % bitsize == 0)))))
     {
       if (mode1 != GET_MODE (op0))
        {
@@ -1706,7 +1698,9 @@ mask_rtx (mode, bitpos, bitsize, complement)
 {
   HOST_WIDE_INT masklow, maskhigh;
 
-  if (bitpos < HOST_BITS_PER_WIDE_INT)
+  if (bitsize == 0)
+    masklow = 0;
+  else if (bitpos < HOST_BITS_PER_WIDE_INT)
     masklow = (HOST_WIDE_INT) -1 << bitpos;
   else
     masklow = 0;
@@ -1720,7 +1714,9 @@ mask_rtx (mode, bitpos, bitsize, complement)
   else
     maskhigh = (HOST_WIDE_INT) -1 << (bitpos - HOST_BITS_PER_WIDE_INT);
 
-  if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT)
+  if (bitsize == 0)
+    maskhigh = 0;
+  else if (bitpos + bitsize > HOST_BITS_PER_WIDE_INT)
     maskhigh &= ((unsigned HOST_WIDE_INT) -1
                 >> (2 * HOST_BITS_PER_WIDE_INT - bitpos - bitsize));
   else
@@ -2932,7 +2928,7 @@ expand_mult_highpart (mode, op0, cnst1, target, unsignedp, max_cost)
    the result is exact for inputs up to 0x1fffffff.
    The input range can be reduced by using cross-sum rules.
    For odd divisors >= 3, the following table gives right shift counts
-   so that if an number is shifted by an integer multiple of the given
+   so that if a number is shifted by an integer multiple of the given
    amount, the remainder stays the same:
    2, 4, 3, 6, 10, 12, 4, 8, 18, 6, 11, 20, 18, 0, 5, 10, 12, 0, 12, 20,
    14, 12, 23, 21, 8, 0, 20, 18, 0, 0, 6, 12, 0, 22, 0, 18, 20, 30, 0, 0,
@@ -2963,14 +2959,20 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
   int size;
   rtx insn, set;
   optab optab1, optab2;
-  int op1_is_constant, op1_is_pow2;
+  int op1_is_constant, op1_is_pow2 = 0;
   int max_cost, extra_cost;
   static HOST_WIDE_INT last_div_const = 0;
+  static HOST_WIDE_INT ext_op1;
 
   op1_is_constant = GET_CODE (op1) == CONST_INT;
-  op1_is_pow2 = (op1_is_constant
-                && ((EXACT_POWER_OF_2_OR_ZERO_P (INTVAL (op1))
-                     || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-INTVAL (op1))))));
+  if (op1_is_constant)
+    {
+      ext_op1 = INTVAL (op1);
+      if (unsignedp)
+       ext_op1 &= GET_MODE_MASK (mode);
+      op1_is_pow2 = ((EXACT_POWER_OF_2_OR_ZERO_P (ext_op1)
+                    || (! unsignedp && EXACT_POWER_OF_2_OR_ZERO_P (-ext_op1))));
+    }
 
   /*
      This is the structure of expand_divmod:
@@ -3050,9 +3052,12 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
      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)
+  optab1 = ((op1_is_pow2 && op1 != const0_rtx)
+           ? (unsignedp ? lshr_optab : ashr_optab)
            : (unsignedp ? udiv_optab : sdiv_optab));
-  optab2 = (op1_is_pow2 ? optab1 : (unsignedp ? udivmod_optab : sdivmod_optab));
+  optab2 = ((op1_is_pow2 && op1 != const0_rtx)
+           ? optab1
+           : (unsignedp ? udivmod_optab : sdivmod_optab));
 
   for (compute_mode = mode; compute_mode != VOIDmode;
        compute_mode = GET_MODE_WIDER_MODE (compute_mode))
@@ -3147,7 +3152,8 @@ expand_divmod (rem_flag, code, mode, op0, op1, target, unsignedp)
                unsigned HOST_WIDE_INT mh, ml;
                int pre_shift, post_shift;
                int dummy;
-               unsigned HOST_WIDE_INT d = INTVAL (op1);
+               unsigned HOST_WIDE_INT d = (INTVAL (op1)
+                                           & GET_MODE_MASK (compute_mode));
 
                if (EXACT_POWER_OF_2_OR_ZERO_P (d))
                  {
@@ -4137,6 +4143,13 @@ make_tree (type, x)
                            build (TRUNC_DIV_EXPR, t,
                                   make_tree (t, XEXP (x, 0)),
                                   make_tree (t, XEXP (x, 1)))));
+
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      t = (*lang_hooks.types.type_for_mode) (GET_MODE (XEXP (x, 0)),
+                                            GET_CODE (x) == ZERO_EXTEND);
+      return fold (convert (type, make_tree (t, XEXP (x, 0))));
+
    default:
       t = make_node (RTL_EXPR);
       TREE_TYPE (t) = type;
@@ -4161,7 +4174,7 @@ make_tree (type, x)
    MODE is the machine mode for the computation.
    X and MULT must have mode MODE.  ADD may have a different mode.
    So can X (defaults to same as MODE).
-   UNSIGNEDP is non-zero to do unsigned multiplication.  */
+   UNSIGNEDP is nonzero to do unsigned multiplication.  */
 
 bool
 const_mult_add_overflow_p (x, mult, add, mode, unsignedp)
@@ -4199,7 +4212,7 @@ const_mult_add_overflow_p (x, mult, add, mode, unsignedp)
    MODE is the machine mode for the computation.
    X and MULT must have mode MODE.  ADD may have a different mode.
    So can X (defaults to same as MODE).
-   UNSIGNEDP is non-zero to do unsigned multiplication.
+   UNSIGNEDP is nonzero to do unsigned multiplication.
    This may emit insns.  */
 
 rtx
@@ -4341,19 +4354,27 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
     {
       if (code == EQ || code == NE)
        {
+         rtx op00, op01, op0both;
+
          /* Do a logical OR of the two words and compare the result.  */
-         rtx op0h = gen_highpart (word_mode, op0);
-         rtx op0l = gen_lowpart (word_mode, op0);
-         rtx op0both = expand_binop (word_mode, ior_optab, op0h, op0l,
-                                     NULL_RTX, unsignedp, OPTAB_DIRECT);
+         op00 = simplify_gen_subreg (word_mode, op0, mode, 0);
+         op01 = simplify_gen_subreg (word_mode, op0, mode, UNITS_PER_WORD);
+         op0both = expand_binop (word_mode, ior_optab, op00, op01,
+                                 NULL_RTX, unsignedp, OPTAB_DIRECT);
          if (op0both != 0)
            return emit_store_flag (target, code, op0both, op1, word_mode,
                                    unsignedp, normalizep);
        }
       else if (code == LT || code == GE)
-       /* If testing the sign bit, can just test on high word.  */
-       return emit_store_flag (target, code, gen_highpart (word_mode, op0),
-                               op1, word_mode, unsignedp, normalizep);
+       {
+         rtx op0h;
+
+         /* If testing the sign bit, can just test on high word.  */
+         op0h = simplify_gen_subreg (word_mode, op0, mode,
+                                     subreg_highpart_offset (word_mode, mode));
+         return emit_store_flag (target, code, op0h, op1, word_mode,
+                                 unsignedp, normalizep);
+       }
     }
 
   /* From now on, we won't change CODE, so set ICODE now.  */
@@ -4366,7 +4387,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
       && (normalizep || STORE_FLAG_VALUE == 1
          || (GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
              && ((STORE_FLAG_VALUE & GET_MODE_MASK (mode))
-                 == (HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))))
+                 == (unsigned HOST_WIDE_INT) 1 << (GET_MODE_BITSIZE (mode) - 1)))))
     {
       subtarget = target;
 
@@ -4613,7 +4634,7 @@ emit_store_flag (target, code, op0, op1, mode, unsignedp, normalizep)
   if (code == EQ || code == NE)
     {
       /* For EQ or NE, one way to do the comparison is to apply an operation
-        that converts the operand into a positive number if it is non-zero
+        that converts the operand into a positive number if it is nonzero
         or zero if it was originally zero.  Then, for EQ, we subtract 1 and
         for NE we negate.  This puts the result in the sign bit.  Then we
         normalize with a shift, if needed.