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" } } */