This moves the two-conversions-in-a-row foldings from fold_unary to
patterns.  This exposed a bug in the genmatch code-gen which failed
to check whether we may follow SSA use-def chains for the outermost
expression operands which is fixed by this patch as well.

I've XFAILed g++.dg/cpp0x/constexpr-reinterpret1.C as Jason told me
and I've also XFAILed gcc.dg/tree-ssa/pr21031.c for now which is
still optimized but needs the 2nd forwprop pass (due to the fact
that a single-use check is confused by dead code that is now left
around by fold_stmt as opposed to the manual simplification done
by tree-ssa-forwprop.c).  I have opened PR63790 to track them
and the XFAIL of pr21031 should resolve itself after more patterns
are merged from the branch.

Bootstrapped and tested on x86_64-unknown-linux-gnu, applied.

Richard.

2014-11-09  Richard Biener  <rguent...@suse.de>

        * match.pd: Add patterns convering two conversions in a row
        from fold-const.c.
        * fold-const.c (fold_unary_loc): Remove them here.
        * tree-ssa-forwprop.c (combine_conversions): Likewise.
        * genmatch.c (dt_node::gen_kids): Check whether we may
        follow SSA use-def chains.

        * g++.dg/cpp0x/constexpr-reinterpret1.C: XFAIL.
        * gcc.dg/tree-ssa/pr21031.c: XFAIL.

Index: trunk/gcc/fold-const.c
===================================================================
*** trunk.orig/gcc/fold-const.c 2014-11-07 17:52:10.615694814 +0100
--- trunk/gcc/fold-const.c      2014-11-07 18:00:02.757674153 +0100
*************** fold_unary_loc (location_t loc, enum tre
*** 7692,7785 ****
                               constant_boolean_node (false, type));
        }
  
-       /* Handle cases of two conversions in a row.  */
-       if (CONVERT_EXPR_P (op0))
-       {
-         tree inside_type = TREE_TYPE (TREE_OPERAND (op0, 0));
-         tree inter_type = TREE_TYPE (op0);
-         int inside_int = INTEGRAL_TYPE_P (inside_type);
-         int inside_ptr = POINTER_TYPE_P (inside_type);
-         int inside_float = FLOAT_TYPE_P (inside_type);
-         int inside_vec = TREE_CODE (inside_type) == VECTOR_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);
-         int inter_vec = TREE_CODE (inter_type) == VECTOR_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);
-         int final_vec = TREE_CODE (type) == VECTOR_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 || inter_ptr) && final_int)
-                 || (inter_float && final_float))
-             && inter_prec >= final_prec)
-           return fold_build1_loc (loc, code, type, TREE_OPERAND (op0, 0));
- 
-         /* Likewise, if the intermediate and initial types are either both
-            float or both integer, we don't need the middle conversion if the
-            former is wider than the latter and doesn't change the signedness
-            (for integers).  Avoid this if the final type is a pointer since
-            then we sometimes need the middle conversion.  Likewise if the
-            final type has a precision not equal to the size of its mode.  */
-         if (((inter_int && inside_int)
-              || (inter_float && inside_float)
-              || (inter_vec && inside_vec))
-             && inter_prec >= inside_prec
-             && (inter_float || inter_vec
-                 || inter_unsignedp == inside_unsignedp)
-             && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
-                   && TYPE_MODE (type) == TYPE_MODE (inter_type))
-             && ! final_ptr
-             && (! final_vec || inter_prec == inside_prec))
-           return fold_build1_loc (loc, code, type, TREE_OPERAND (op0, 0));
- 
-         /* If we have a sign-extension of a zero-extended value, we can
-            replace that by a single zero-extension.  Likewise if the
-            final conversion does not change precision we can drop the
-            intermediate conversion.  */
-         if (inside_int && inter_int && final_int
-             && ((inside_prec < inter_prec && inter_prec < final_prec
-                  && inside_unsignedp && !inter_unsignedp)
-                 || final_prec == inter_prec))
-           return fold_build1_loc (loc, code, type, TREE_OPERAND (op0, 0));
- 
-         /* Two conversions in a row are not needed unless:
-            - some conversion is floating-point (overstrict for now), or
-            - some conversion is a vector (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
-             && ! inside_vec && ! inter_vec && ! final_vec
-             && (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_PRECISION (TYPE_MODE (type))
-                   && TYPE_MODE (type) == TYPE_MODE (inter_type)))
-           return fold_build1_loc (loc, code, type, TREE_OPERAND (op0, 0));
-       }
- 
        /* Handle (T *)&A.B.C for A being of type T and B and C
         living at offset zero.  This occurs frequently in
         C++ upcasting and then accessing the base.  */
--- 7692,7697 ----
Index: trunk/gcc/match.pd
===================================================================
*** trunk.orig/gcc/match.pd     2014-11-07 17:52:10.615694814 +0100
--- trunk/gcc/match.pd  2014-11-07 18:00:02.758674153 +0100
*************** along with GCC; see the file COPYING3.
*** 319,321 ****
--- 319,414 ----
  (simplify
   (paren (paren@1 @0))
   @1)
+ 
+ /* Handle cases of two conversions in a row.  */
+ (for ocvt (convert float fix_trunc)
+  (for icvt (convert float)
+   (simplify
+    (ocvt (icvt@1 @0))
+    (with
+     {
+       tree inside_type = TREE_TYPE (@0);
+       tree inter_type = TREE_TYPE (@1);
+       int inside_int = INTEGRAL_TYPE_P (inside_type);
+       int inside_ptr = POINTER_TYPE_P (inside_type);
+       int inside_float = FLOAT_TYPE_P (inside_type);
+       int inside_vec = TREE_CODE (inside_type) == VECTOR_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);
+       int inter_vec = TREE_CODE (inter_type) == VECTOR_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);
+       int final_vec = TREE_CODE (type) == VECTOR_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 (((GIMPLE && useless_type_conversion_p (type, inside_type))
+        || (GENERIC
+            && TYPE_MAIN_VARIANT (type) == TYPE_MAIN_VARIANT (inside_type)))
+       && (((inter_int || inter_ptr) && final_int)
+           || (inter_float && final_float))
+       && inter_prec >= final_prec)
+     (ocvt @0))
+ 
+    /* Likewise, if the intermediate and initial types are either both
+       float or both integer, we don't need the middle conversion if the
+       former is wider than the latter and doesn't change the signedness
+       (for integers).  Avoid this if the final type is a pointer since
+       then we sometimes need the middle conversion.  Likewise if the
+       final type has a precision not equal to the size of its mode.  */
+    (if (((inter_int && inside_int)
+        || (inter_float && inside_float)
+        || (inter_vec && inside_vec))
+       && inter_prec >= inside_prec
+       && (inter_float || inter_vec
+           || inter_unsignedp == inside_unsignedp)
+       && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
+             && TYPE_MODE (type) == TYPE_MODE (inter_type))
+       && ! final_ptr
+       && (! final_vec || inter_prec == inside_prec))
+     (ocvt @0))
+ 
+    /* If we have a sign-extension of a zero-extended value, we can
+       replace that by a single zero-extension.  Likewise if the
+       final conversion does not change precision we can drop the
+       intermediate conversion.  */
+    (if (inside_int && inter_int && final_int
+       && ((inside_prec < inter_prec && inter_prec < final_prec
+            && inside_unsignedp && !inter_unsignedp)
+           || final_prec == inter_prec))
+     (ocvt @0))
+ 
+    /* Two conversions in a row are not needed unless:
+       - some conversion is floating-point (overstrict for now), or
+       - some conversion is a vector (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
+       && ! inside_vec && ! inter_vec && ! final_vec
+       && (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_PRECISION (TYPE_MODE (type))
+             && TYPE_MODE (type) == TYPE_MODE (inter_type)))
+     (ocvt @0))))))
Index: trunk/gcc/tree-ssa-forwprop.c
===================================================================
*** trunk.orig/gcc/tree-ssa-forwprop.c  2014-11-07 17:52:10.615694814 +0100
--- trunk/gcc/tree-ssa-forwprop.c       2014-11-07 18:00:02.759674153 +0100
*************** combine_conversions (gimple_stmt_iterato
*** 2386,2494 ****
        tree inside_type = TREE_TYPE (defop0);
        tree inter_type = TREE_TYPE (op0);
        int inside_int = INTEGRAL_TYPE_P (inside_type);
-       int inside_ptr = POINTER_TYPE_P (inside_type);
-       int inside_float = FLOAT_TYPE_P (inside_type);
-       int inside_vec = TREE_CODE (inside_type) == VECTOR_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);
-       int inter_vec = TREE_CODE (inter_type) == VECTOR_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);
-       int final_vec = TREE_CODE (type) == VECTOR_TYPE;
        unsigned int final_prec = TYPE_PRECISION (type);
-       int final_unsignedp = TYPE_UNSIGNED (type);
  
        /* Don't propagate ssa names that occur in abnormal phis.  */
        if (TREE_CODE (defop0) == SSA_NAME
          && SSA_NAME_OCCURS_IN_ABNORMAL_PHI (defop0))
        return 0;
  
-       /* 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 (useless_type_conversion_p (type, inside_type)
-         && (((inter_int || inter_ptr) && final_int)
-             || (inter_float && final_float))
-         && inter_prec >= final_prec)
-       {
-         gimple_assign_set_rhs1 (stmt, unshare_expr (defop0));
-         gimple_assign_set_rhs_code (stmt, TREE_CODE (defop0));
-         update_stmt (stmt);
-         return remove_prop_source_from_use (op0) ? 2 : 1;
-       }
- 
-       /* Likewise, if the intermediate and initial types are either both
-        float or both integer, we don't need the middle conversion if the
-        former is wider than the latter and doesn't change the signedness
-        (for integers).  Avoid this if the final type is a pointer since
-        then we sometimes need the middle conversion.  Likewise if the
-        final type has a precision not equal to the size of its mode.  */
-       if (((inter_int && inside_int)
-          || (inter_float && inside_float)
-          || (inter_vec && inside_vec))
-         && inter_prec >= inside_prec
-         && (inter_float || inter_vec
-             || inter_unsignedp == inside_unsignedp)
-         && ! (final_prec != GET_MODE_PRECISION (TYPE_MODE (type))
-               && TYPE_MODE (type) == TYPE_MODE (inter_type))
-         && ! final_ptr
-         && (! final_vec || inter_prec == inside_prec))
-       {
-         gimple_assign_set_rhs1 (stmt, defop0);
-         update_stmt (stmt);
-         return remove_prop_source_from_use (op0) ? 2 : 1;
-       }
- 
-       /* If we have a sign-extension of a zero-extended value, we can
-        replace that by a single zero-extension.  Likewise if the
-        final conversion does not change precision we can drop the
-        intermediate conversion.  */
-       if (inside_int && inter_int && final_int
-         && ((inside_prec < inter_prec && inter_prec < final_prec
-              && inside_unsignedp && !inter_unsignedp)
-             || final_prec == inter_prec))
-       {
-         gimple_assign_set_rhs1 (stmt, defop0);
-         update_stmt (stmt);
-         return remove_prop_source_from_use (op0) ? 2 : 1;
-       }
- 
-       /* Two conversions in a row are not needed unless:
-        - some conversion is floating-point (overstrict for now), or
-        - some conversion is a vector (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
-         && ! inside_vec && ! inter_vec && ! final_vec
-         && (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_PRECISION (TYPE_MODE (type))
-               && TYPE_MODE (type) == TYPE_MODE (inter_type)))
-       {
-         gimple_assign_set_rhs1 (stmt, defop0);
-         update_stmt (stmt);
-         return remove_prop_source_from_use (op0) ? 2 : 1;
-       }
- 
        /* A truncation to an unsigned type should be canonicalized as
         bitwise and of a mask.  */
        if (final_int && inter_int && inside_int
--- 2386,2405 ----
Index: trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C
===================================================================
*** trunk.orig/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C      
2014-11-07 17:52:10.615694814 +0100
--- trunk/gcc/testsuite/g++.dg/cpp0x/constexpr-reinterpret1.C   2014-11-07 
18:00:02.759674153 +0100
*************** public:
*** 18,24 ****
    {
      /* I am surprised this is considered a constexpr */
      return *((Inner *)4);
!   } // { dg-error "reinterpret_cast" }
  };
  
  B B::instance;
--- 18,24 ----
    {
      /* I am surprised this is considered a constexpr */
      return *((Inner *)4);
!   } // { dg-error "reinterpret_cast" "" { xfail *-*-* } }
  };
  
  B B::instance;
Index: trunk/gcc/genmatch.c
===================================================================
*** trunk.orig/gcc/genmatch.c   2014-11-06 10:46:26.541593521 +0100
--- trunk/gcc/genmatch.c        2014-11-07 21:42:15.948090689 +0100
*************** dt_operand::gen_gimple_expr (FILE *f)
*** 1615,1621 ****
        else
        fprintf (f, "tree %s = gimple_call_arg (def_stmt, %u);\n",
                 child_opname, i);
!       fprintf (f, "if ((%s = do_valueize (valueize, %s)) != 0)\n",
               child_opname, child_opname);
        fprintf (f, "{\n");
      }
--- 1615,1621 ----
        else
        fprintf (f, "tree %s = gimple_call_arg (def_stmt, %u);\n",
                 child_opname, i);
!       fprintf (f, "if ((%s = do_valueize (valueize, %s)))\n",
               child_opname, child_opname);
        fprintf (f, "{\n");
      }
*************** dt_node::gen_kids (FILE *f, bool gimple)
*** 1726,1731 ****
--- 1726,1732 ----
    if (exprs_len || fns_len)
      {
        fprintf (f, "case SSA_NAME:\n");
+       fprintf (f, "if (do_valueize (valueize, %s) != NULL_TREE)\n", 
kid_opname);
        fprintf (f, "{\n");
        fprintf (f, "gimple def_stmt = SSA_NAME_DEF_STMT (%s);\n", kid_opname);
  
Index: trunk/gcc/testsuite/gcc.dg/tree-ssa/pr21031.c
===================================================================
*** trunk.orig/gcc/testsuite/gcc.dg/tree-ssa/pr21031.c  2006-09-18 
16:11:20.000000000 +0200
--- trunk/gcc/testsuite/gcc.dg/tree-ssa/pr21031.c       2014-11-09 
12:07:45.990037324 +0100
*************** foo (int a)
*** 16,20 ****
      return 0;
  }
  
! /* { dg-final { scan-tree-dump-times "Replaced" 2 "forwprop1"} } */
  /* { dg-final { cleanup-tree-dump "forwprop1" } } */
--- 16,20 ----
      return 0;
  }
  
! /* { dg-final { scan-tree-dump-times "Replaced" 2 "forwprop1" { xfail *-*-* } 
} } */
  /* { dg-final { cleanup-tree-dump "forwprop1" } } */

Reply via email to