fold-const.c (fold_unary): New.
authorKazu Hirata <kazu@cs.umass.edu>
Wed, 2 Mar 2005 16:07:47 +0000 (16:07 +0000)
committerKazu Hirata <kazu@gcc.gnu.org>
Wed, 2 Mar 2005 16:07:47 +0000 (16:07 +0000)
* fold-const.c (fold_unary): New.
(fold): Call fold_unary on unary expressions.

From-SVN: r95783

gcc/ChangeLog
gcc/fold-const.c

index fccfa7e3606d91151e0ceb811ce1857a4eff3df6..247b1f26b9c1caa009d8c4b0db65941f516258dd 100644 (file)
@@ -10,6 +10,9 @@
        * expr.c (optimize_bitfield_assignment_op): Add a special case
        to handle BIT_IOR_EXPR and BIT_XOR_EXPR.
 
+       * fold-const.c (fold_unary): New.
+       (fold): Call fold_unary on unary expressions.
+
 2005-03-02  Richard Guenther  <rguenth@gcc.gnu.org>
 
        * cgraph.h (struct cgraph_edge): Add prev_caller and
index 5c3139ccb50a8a99c78fb48a47f18f7f31077d75..42d72969f8578b98a078887956cf67800355b7c0 100644 (file)
@@ -6596,6 +6596,430 @@ fold_complex_div (tree type, tree ac, tree bc, enum tree_code code)
   return fold_complex_div_parts (type, ar, ai, br, bi, code);
 }
 
+/* Fold a unary expression EXPR.  Return the folded expression if
+   folding is successful.  Otherwise, return the original
+   expression.  */
+
+static tree
+fold_unary (tree expr)
+{
+  const tree t = expr;
+  const tree type = TREE_TYPE (expr);
+  tree tem;
+  tree arg0;
+  enum tree_code code = TREE_CODE (t);
+  enum tree_code_class kind = TREE_CODE_CLASS (code);
+
+  gcc_assert (IS_EXPR_CODE_CLASS (kind)
+             && TREE_CODE_LENGTH (code) == 1);
+
+
+  arg0 = TREE_OPERAND (t, 0);
+  if (arg0)
+    {
+      if (code == NOP_EXPR || code == FLOAT_EXPR || code == CONVERT_EXPR)
+       {
+         /* Don't use STRIP_NOPS, because signedness of argument type matters.  */
+         STRIP_SIGN_NOPS (arg0);
+       }
+      else
+       {
+         /* Strip any conversions that don't change the mode.  This
+            is safe for every expression, except for a comparison
+            expression because its signedness is derived from its
+            operands.
+
+            Note that this is done as an internal manipulation within
+            the constant folder, in order to find the simplest
+            representation of the arguments so that their form can be
+            studied.  In any cases, the appropriate type conversions
+            should be put back in the tree that will get out of the
+            constant folder.  */
+         STRIP_NOPS (arg0);
+       }
+    }
+
+  if (TREE_CODE_CLASS (code) == tcc_unary)
+    {
+      if (TREE_CODE (arg0) == COMPOUND_EXPR)
+       return build2 (COMPOUND_EXPR, type, TREE_OPERAND (arg0, 0),
+                      fold (build1 (code, type, TREE_OPERAND (arg0, 1))));
+      else if (TREE_CODE (arg0) == COND_EXPR)
+       {
+         tree arg01 = TREE_OPERAND (arg0, 1);
+         tree arg02 = TREE_OPERAND (arg0, 2);
+         if (! VOID_TYPE_P (TREE_TYPE (arg01)))
+           arg01 = fold (build1 (code, type, arg01));
+         if (! VOID_TYPE_P (TREE_TYPE (arg02)))
+           arg02 = fold (build1 (code, type, arg02));
+         tem = fold (build3 (COND_EXPR, type, TREE_OPERAND (arg0, 0),
+                             arg01, arg02));
+
+         /* If this was a conversion, and all we did was to move into
+            inside the COND_EXPR, bring it back out.  But leave it if
+            it is a conversion from integer to integer and the
+            result precision is no wider than a word since such a
+            conversion is cheap and may be optimized away by combine,
+            while it couldn't if it were outside the COND_EXPR.  Then return
+            so we don't get into an infinite recursion loop taking the
+            conversion out and then back in.  */
+
+         if ((code == NOP_EXPR || code == CONVERT_EXPR
+              || code == NON_LVALUE_EXPR)
+             && TREE_CODE (tem) == COND_EXPR
+             && TREE_CODE (TREE_OPERAND (tem, 1)) == code
+             && TREE_CODE (TREE_OPERAND (tem, 2)) == code
+             && ! VOID_TYPE_P (TREE_OPERAND (tem, 1))
+             && ! VOID_TYPE_P (TREE_OPERAND (tem, 2))
+             && (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (tem, 1), 0))
+                 == TREE_TYPE (TREE_OPERAND (TREE_OPERAND (tem, 2), 0)))
+             && (! (INTEGRAL_TYPE_P (TREE_TYPE (tem))
+                    && (INTEGRAL_TYPE_P
+                        (TREE_TYPE (TREE_OPERAND (TREE_OPERAND (tem, 1), 0))))
+                    && TYPE_PRECISION (TREE_TYPE (tem)) <= BITS_PER_WORD)
+                 || flag_syntax_only))
+           tem = build1 (code, type,
+                         build3 (COND_EXPR,
+                                 TREE_TYPE (TREE_OPERAND
+                                            (TREE_OPERAND (tem, 1), 0)),
+                                 TREE_OPERAND (tem, 0),
+                                 TREE_OPERAND (TREE_OPERAND (tem, 1), 0),
+                                 TREE_OPERAND (TREE_OPERAND (tem, 2), 0)));
+         return tem;
+       }
+      else if (COMPARISON_CLASS_P (arg0))
+       {
+         if (TREE_CODE (type) == BOOLEAN_TYPE)
+           {
+             arg0 = copy_node (arg0);
+             TREE_TYPE (arg0) = type;
+             return arg0;
+           }
+         else if (TREE_CODE (type) != INTEGER_TYPE)
+           return fold (build3 (COND_EXPR, type, arg0,
+                                fold (build1 (code, type,
+                                              integer_one_node)),
+                                fold (build1 (code, type,
+                                              integer_zero_node))));
+       }
+   }
+
+  switch (code)
+    {
+    case NOP_EXPR:
+    case FLOAT_EXPR:
+    case CONVERT_EXPR:
+    case FIX_TRUNC_EXPR:
+    case FIX_CEIL_EXPR:
+    case FIX_FLOOR_EXPR:
+    case FIX_ROUND_EXPR:
+      if (TREE_TYPE (TREE_OPERAND (t, 0)) == type)
+       return TREE_OPERAND (t, 0);
+
+      /* Handle cases of two conversions in a row.  */
+      if (TREE_CODE (TREE_OPERAND (t, 0)) == NOP_EXPR
+         || TREE_CODE (TREE_OPERAND (t, 0)) == CONVERT_EXPR)
+       {
+         tree inside_type = TREE_TYPE (TREE_OPERAND (TREE_OPERAND (t, 0), 0));
+         tree inter_type = TREE_TYPE (TREE_OPERAND (t, 0));
+         int inside_int = INTEGRAL_TYPE_P (inside_type);
+         int inside_ptr = POINTER_TYPE_P (inside_type);
+         int inside_float = FLOAT_TYPE_P (inside_type);
+         unsigned int inside_prec = TYPE_PRECISION (inside_type);
+         int inside_unsignedp = TYPE_UNSIGNED (inside_type);
+         int inter_int = INTEGRAL_TYPE_P (inter_type);
+         int inter_ptr = POINTER_TYPE_P (inter_type);
+         int inter_float = FLOAT_TYPE_P (inter_type);
+         unsigned int inter_prec = TYPE_PRECISION (inter_type);
+         int inter_unsignedp = TYPE_UNSIGNED (inter_type);
+         int final_int = INTEGRAL_TYPE_P (type);
+         int final_ptr = POINTER_TYPE_P (type);
+         int final_float = FLOAT_TYPE_P (type);
+         unsigned int final_prec = TYPE_PRECISION (type);
+         int final_unsignedp = TYPE_UNSIGNED (type);
+
+         /* In addition to the cases of two conversions in a row
+            handled below, if we are converting something to its own
+            type via an object of identical or wider precision, neither
+            conversion is needed.  */
+         if (TYPE_MAIN_VARIANT (inside_type) == TYPE_MAIN_VARIANT (type)
+             && ((inter_int && final_int) || (inter_float && final_float))
+             && inter_prec >= final_prec)
+           return fold (build1 (code, type,
+                                TREE_OPERAND (TREE_OPERAND (t, 0), 0)));
+
+         /* Likewise, if the intermediate and final types are either both
+            float or both integer, we don't need the middle conversion if
+            it is wider than the final type and doesn't change the signedness
+            (for integers).  Avoid this if the final type is a pointer
+            since then we sometimes need the inner conversion.  Likewise if
+            the outer has a precision not equal to the size of its mode.  */
+         if ((((inter_int || inter_ptr) && (inside_int || inside_ptr))
+              || (inter_float && inside_float))
+             && inter_prec >= inside_prec
+             && (inter_float || inter_unsignedp == inside_unsignedp)
+             && ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (type))
+                   && TYPE_MODE (type) == TYPE_MODE (inter_type))
+             && ! final_ptr)
+           return fold (build1 (code, type,
+                                TREE_OPERAND (TREE_OPERAND (t, 0), 0)));
+
+         /* If we have a sign-extension of a zero-extended value, we can
+            replace that by a single zero-extension.  */
+         if (inside_int && inter_int && final_int
+             && inside_prec < inter_prec && inter_prec < final_prec
+             && inside_unsignedp && !inter_unsignedp)
+           return fold (build1 (code, type,
+                                TREE_OPERAND (TREE_OPERAND (t, 0), 0)));
+
+         /* Two conversions in a row are not needed unless:
+            - some conversion is floating-point (overstrict for now), or
+            - the intermediate type is narrower than both initial and
+              final, or
+            - the intermediate type and innermost type differ in signedness,
+              and the outermost type is wider than the intermediate, or
+            - the initial type is a pointer type and the precisions of the
+              intermediate and final types differ, or
+            - the final type is a pointer type and the precisions of the
+              initial and intermediate types differ.  */
+         if (! inside_float && ! inter_float && ! final_float
+             && (inter_prec > inside_prec || inter_prec > final_prec)
+             && ! (inside_int && inter_int
+                   && inter_unsignedp != inside_unsignedp
+                   && inter_prec < final_prec)
+             && ((inter_unsignedp && inter_prec > inside_prec)
+                 == (final_unsignedp && final_prec > inter_prec))
+             && ! (inside_ptr && inter_prec != final_prec)
+             && ! (final_ptr && inside_prec != inter_prec)
+             && ! (final_prec != GET_MODE_BITSIZE (TYPE_MODE (type))
+                   && TYPE_MODE (type) == TYPE_MODE (inter_type))
+             && ! final_ptr)
+           return fold (build1 (code, type,
+                                TREE_OPERAND (TREE_OPERAND (t, 0), 0)));
+       }
+
+      if (TREE_CODE (TREE_OPERAND (t, 0)) == MODIFY_EXPR
+         && TREE_CONSTANT (TREE_OPERAND (TREE_OPERAND (t, 0), 1))
+         /* Detect assigning a bitfield.  */
+         && !(TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 0)) == COMPONENT_REF
+              && DECL_BIT_FIELD (TREE_OPERAND (TREE_OPERAND (TREE_OPERAND (t, 0), 0), 1))))
+       {
+         /* Don't leave an assignment inside a conversion
+            unless assigning a bitfield.  */
+         tree prev = TREE_OPERAND (t, 0);
+         tem = copy_node (t);
+         TREE_OPERAND (tem, 0) = TREE_OPERAND (prev, 1);
+         /* First do the assignment, then return converted constant.  */
+         tem = build2 (COMPOUND_EXPR, TREE_TYPE (tem), prev, fold (tem));
+         TREE_NO_WARNING (tem) = 1;
+         TREE_USED (tem) = 1;
+         return tem;
+       }
+
+      /* Convert (T)(x & c) into (T)x & (T)c, if c is an integer
+        constants (if x has signed type, the sign bit cannot be set
+        in c).  This folds extension into the BIT_AND_EXPR.  */
+      if (INTEGRAL_TYPE_P (type)
+         && TREE_CODE (type) != BOOLEAN_TYPE
+         && TREE_CODE (TREE_OPERAND (t, 0)) == BIT_AND_EXPR
+         && TREE_CODE (TREE_OPERAND (TREE_OPERAND (t, 0), 1)) == INTEGER_CST)
+       {
+         tree and = TREE_OPERAND (t, 0);
+         tree and0 = TREE_OPERAND (and, 0), and1 = TREE_OPERAND (and, 1);
+         int change = 0;
+
+         if (TYPE_UNSIGNED (TREE_TYPE (and))
+             || (TYPE_PRECISION (type)
+                 <= TYPE_PRECISION (TREE_TYPE (and))))
+           change = 1;
+         else if (TYPE_PRECISION (TREE_TYPE (and1))
+                  <= HOST_BITS_PER_WIDE_INT
+                  && host_integerp (and1, 1))
+           {
+             unsigned HOST_WIDE_INT cst;
+
+             cst = tree_low_cst (and1, 1);
+             cst &= (HOST_WIDE_INT) -1
+                    << (TYPE_PRECISION (TREE_TYPE (and1)) - 1);
+             change = (cst == 0);
+#ifdef LOAD_EXTEND_OP
+             if (change
+                 && !flag_syntax_only
+                 && (LOAD_EXTEND_OP (TYPE_MODE (TREE_TYPE (and0)))
+                     == ZERO_EXTEND))
+               {
+                 tree uns = lang_hooks.types.unsigned_type (TREE_TYPE (and0));
+                 and0 = fold_convert (uns, and0);
+                 and1 = fold_convert (uns, and1);
+               }
+#endif
+           }
+         if (change)
+           {
+             tem = build_int_cst_wide (type, TREE_INT_CST_LOW (and1),
+                                       TREE_INT_CST_HIGH (and1));
+             tem = force_fit_type (tem, 0, TREE_OVERFLOW (and1),
+                                   TREE_CONSTANT_OVERFLOW (and1));
+             return fold (build2 (BIT_AND_EXPR, type,
+                                  fold_convert (type, and0), tem));
+           }
+       }
+
+      /* Convert (T1)((T2)X op Y) into (T1)X op Y, for pointer types T1 and
+        T2 being pointers to types of the same size.  */
+      if (POINTER_TYPE_P (TREE_TYPE (t))
+         && BINARY_CLASS_P (arg0)
+         && TREE_CODE (TREE_OPERAND (arg0, 0)) == NOP_EXPR
+         && POINTER_TYPE_P (TREE_TYPE (TREE_OPERAND (arg0, 0))))
+       {
+         tree arg00 = TREE_OPERAND (arg0, 0);
+         tree t0 = TREE_TYPE (t);
+         tree t1 = TREE_TYPE (arg00);
+         tree tt0 = TREE_TYPE (t0);
+         tree tt1 = TREE_TYPE (t1);
+         tree s0 = TYPE_SIZE (tt0);
+         tree s1 = TYPE_SIZE (tt1);
+
+         if (s0 && s1 && operand_equal_p (s0, s1, OEP_ONLY_CONST))
+           return build2 (TREE_CODE (arg0), t0, fold_convert (t0, arg00),
+                          TREE_OPERAND (arg0, 1));
+       }
+
+      tem = fold_convert_const (code, type, arg0);
+      return tem ? tem : t;
+
+    case VIEW_CONVERT_EXPR:
+      if (TREE_CODE (TREE_OPERAND (t, 0)) == VIEW_CONVERT_EXPR)
+       return build1 (VIEW_CONVERT_EXPR, type,
+                      TREE_OPERAND (TREE_OPERAND (t, 0), 0));
+      return t;
+
+    case NEGATE_EXPR:
+      if (negate_expr_p (arg0))
+       return fold_convert (type, negate_expr (arg0));
+      /* Convert - (~A) to A + 1.  */
+      if (INTEGRAL_TYPE_P (type) && TREE_CODE (arg0) == BIT_NOT_EXPR)
+       return fold (build2 (PLUS_EXPR, type, TREE_OPERAND (arg0, 0),
+                            build_int_cst (type, 1)));
+      return t;
+
+    case ABS_EXPR:
+      if (TREE_CODE (arg0) == INTEGER_CST || TREE_CODE (arg0) == REAL_CST)
+       return fold_abs_const (arg0, type);
+      else if (TREE_CODE (arg0) == NEGATE_EXPR)
+       return fold (build1 (ABS_EXPR, type, TREE_OPERAND (arg0, 0)));
+      /* Convert fabs((double)float) into (double)fabsf(float).  */
+      else if (TREE_CODE (arg0) == NOP_EXPR
+              && TREE_CODE (type) == REAL_TYPE)
+       {
+         tree targ0 = strip_float_extensions (arg0);
+         if (targ0 != arg0)
+           return fold_convert (type, fold (build1 (ABS_EXPR,
+                                                    TREE_TYPE (targ0),
+                                                    targ0)));
+       }
+      else if (tree_expr_nonnegative_p (arg0))
+       return arg0;
+
+      /* Strip sign ops from argument.  */
+      if (TREE_CODE (type) == REAL_TYPE)
+       {
+         tem = fold_strip_sign_ops (arg0);
+         if (tem)
+           return fold (build1 (ABS_EXPR, type, fold_convert (type, tem)));
+       }
+      return t;
+
+    case CONJ_EXPR:
+      if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
+       return fold_convert (type, arg0);
+      else if (TREE_CODE (arg0) == COMPLEX_EXPR)
+       return build2 (COMPLEX_EXPR, type,
+                      TREE_OPERAND (arg0, 0),
+                      negate_expr (TREE_OPERAND (arg0, 1)));
+      else if (TREE_CODE (arg0) == COMPLEX_CST)
+       return build_complex (type, TREE_REALPART (arg0),
+                             negate_expr (TREE_IMAGPART (arg0)));
+      else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+       return fold (build2 (TREE_CODE (arg0), type,
+                            fold (build1 (CONJ_EXPR, type,
+                                          TREE_OPERAND (arg0, 0))),
+                            fold (build1 (CONJ_EXPR, type,
+                                          TREE_OPERAND (arg0, 1)))));
+      else if (TREE_CODE (arg0) == CONJ_EXPR)
+       return TREE_OPERAND (arg0, 0);
+      return t;
+
+    case BIT_NOT_EXPR:
+      if (TREE_CODE (arg0) == INTEGER_CST)
+        return fold_not_const (arg0, type);
+      else if (TREE_CODE (arg0) == BIT_NOT_EXPR)
+       return TREE_OPERAND (arg0, 0);
+      /* Convert ~ (-A) to A - 1.  */
+      else if (INTEGRAL_TYPE_P (type) && TREE_CODE (arg0) == NEGATE_EXPR)
+       return fold (build2 (MINUS_EXPR, type, TREE_OPERAND (arg0, 0),
+                            build_int_cst (type, 1)));
+      /* Convert ~ (A - 1) or ~ (A + -1) to -A.  */
+      else if (INTEGRAL_TYPE_P (type)
+              && ((TREE_CODE (arg0) == MINUS_EXPR
+                   && integer_onep (TREE_OPERAND (arg0, 1)))
+                  || (TREE_CODE (arg0) == PLUS_EXPR
+                      && integer_all_onesp (TREE_OPERAND (arg0, 1)))))
+       return fold (build1 (NEGATE_EXPR, type, TREE_OPERAND (arg0, 0)));
+      return t;
+
+    case TRUTH_NOT_EXPR:
+      /* The argument to invert_truthvalue must have Boolean type.  */
+      if (TREE_CODE (TREE_TYPE (arg0)) != BOOLEAN_TYPE)
+          arg0 = fold_convert (boolean_type_node, arg0);
+
+      /* Note that the operand of this must be an int
+        and its values must be 0 or 1.
+        ("true" is a fixed value perhaps depending on the language,
+        but we don't handle values other than 1 correctly yet.)  */
+      tem = invert_truthvalue (arg0);
+      /* Avoid infinite recursion.  */
+      if (TREE_CODE (tem) == TRUTH_NOT_EXPR)
+       return t;
+      return fold_convert (type, tem);
+
+    case REALPART_EXPR:
+      if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
+       return t;
+      else if (TREE_CODE (arg0) == COMPLEX_EXPR)
+       return omit_one_operand (type, TREE_OPERAND (arg0, 0),
+                                TREE_OPERAND (arg0, 1));
+      else if (TREE_CODE (arg0) == COMPLEX_CST)
+       return TREE_REALPART (arg0);
+      else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+       return fold (build2 (TREE_CODE (arg0), type,
+                            fold (build1 (REALPART_EXPR, type,
+                                          TREE_OPERAND (arg0, 0))),
+                            fold (build1 (REALPART_EXPR, type,
+                                          TREE_OPERAND (arg0, 1)))));
+      return t;
+
+    case IMAGPART_EXPR:
+      if (TREE_CODE (TREE_TYPE (arg0)) != COMPLEX_TYPE)
+       return fold_convert (type, integer_zero_node);
+      else if (TREE_CODE (arg0) == COMPLEX_EXPR)
+       return omit_one_operand (type, TREE_OPERAND (arg0, 1),
+                                TREE_OPERAND (arg0, 0));
+      else if (TREE_CODE (arg0) == COMPLEX_CST)
+       return TREE_IMAGPART (arg0);
+      else if (TREE_CODE (arg0) == PLUS_EXPR || TREE_CODE (arg0) == MINUS_EXPR)
+       return fold (build2 (TREE_CODE (arg0), type,
+                            fold (build1 (IMAGPART_EXPR, type,
+                                          TREE_OPERAND (arg0, 0))),
+                            fold (build1 (IMAGPART_EXPR, type,
+                                          TREE_OPERAND (arg0, 1)))));
+      return t;
+
+    default:
+      return t;
+    } /* switch (code) */
+}
+
 /* Perform constant folding and related simplification of EXPR.
    The related simplifications include x*1 => x, x*0 => 0, etc.,
    and application of the associative law.
@@ -6628,6 +7052,17 @@ fold (tree expr)
   if (kind == tcc_constant)
     return t;
 
+  if (IS_EXPR_CODE_CLASS (kind))
+    {
+      switch (TREE_CODE_LENGTH (code))
+       {
+       case 1:
+         return fold_unary (expr);
+       default:
+         break;
+       }
+    }
+
   if (code == NOP_EXPR || code == FLOAT_EXPR || code == CONVERT_EXPR)
     {
       tree subop;