summaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-forwprop.c
diff options
context:
space:
mode:
authorrguenth <rguenth@138bc75d-0d04-0410-961f-82ee72b054a4>2011-05-10 09:57:50 +0000
committerrguenth <rguenth@138bc75d-0d04-0410-961f-82ee72b054a4>2011-05-10 09:57:50 +0000
commit6afd0544e1e12576cc6614e417cbbc01477ac2ca (patch)
tree29206a7d5e93403b2d8ad01b15c16501a9d9603c /gcc/tree-ssa-forwprop.c
parent4e392ca14ab07e198e703b1e35363074ee63b9ed (diff)
downloadgcc-6afd0544e1e12576cc6614e417cbbc01477ac2ca.tar.gz
2011-05-10 Richard Guenther <rguenther@suse.de>
* tree-ssa-forwprop.c (combine_conversions): Pattern-match a series of conversions and apply foldings similar to what fold-const does. (tree_ssa_forward_propagate_single_use_vars): Call it. * gcc.dg/tree-ssa/ssa-fre-2.c: Disable forwprop. * gcc.dg/tree-ssa/ssa-fre-3.c: Likewise. * gcc.dg/tree-ssa/ssa-fre-4.c: Likewise. * gcc.dg/tree-ssa/ssa-fre-5.c: Likewise. * gcc.dg/tree-ssa/scev-cast.c: Adjust. Note what transformation applies. git-svn-id: svn+ssh://gcc.gnu.org/svn/gcc/trunk@173612 138bc75d-0d04-0410-961f-82ee72b054a4
Diffstat (limited to 'gcc/tree-ssa-forwprop.c')
-rw-r--r--gcc/tree-ssa-forwprop.c167
1 files changed, 167 insertions, 0 deletions
diff --git a/gcc/tree-ssa-forwprop.c b/gcc/tree-ssa-forwprop.c
index 7990c5b5598..65e058d7231 100644
--- a/gcc/tree-ssa-forwprop.c
+++ b/gcc/tree-ssa-forwprop.c
@@ -1938,6 +1938,166 @@ out:
return false;
}
+/* Combine two conversions in a row for the second conversion at *GSI.
+ Returns true if there were any changes made. */
+
+static bool
+combine_conversions (gimple_stmt_iterator *gsi)
+{
+ gimple stmt = gsi_stmt (*gsi);
+ gimple def_stmt;
+ tree op0, lhs;
+ enum tree_code code = gimple_assign_rhs_code (stmt);
+
+ gcc_checking_assert (CONVERT_EXPR_CODE_P (code)
+ || code == FLOAT_EXPR
+ || code == FIX_TRUNC_EXPR);
+
+ lhs = gimple_assign_lhs (stmt);
+ op0 = gimple_assign_rhs1 (stmt);
+ if (useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (op0)))
+ {
+ gimple_assign_set_rhs_code (stmt, TREE_CODE (op0));
+ return true;
+ }
+
+ if (TREE_CODE (op0) != SSA_NAME)
+ return false;
+
+ def_stmt = SSA_NAME_DEF_STMT (op0);
+ if (!is_gimple_assign (def_stmt))
+ return false;
+
+ if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (def_stmt)))
+ {
+ tree defop0 = gimple_assign_rhs1 (def_stmt);
+ tree type = TREE_TYPE (lhs);
+ 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);
+
+ /* 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 true;
+ }
+
+ /* 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_BITSIZE (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 true;
+ }
+
+ /* 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)
+ {
+ gimple_assign_set_rhs1 (stmt, defop0);
+ update_stmt (stmt);
+ return true;
+ }
+
+ /* 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_BITSIZE (TYPE_MODE (type))
+ && TYPE_MODE (type) == TYPE_MODE (inter_type)))
+ {
+ gimple_assign_set_rhs1 (stmt, defop0);
+ update_stmt (stmt);
+ return true;
+ }
+
+ /* A truncation to an unsigned type should be canonicalized as
+ bitwise and of a mask. */
+ if (final_int && inter_int && inside_int
+ && final_prec == inside_prec
+ && final_prec > inter_prec
+ && inter_unsignedp)
+ {
+ tree tem;
+ tem = fold_build2 (BIT_AND_EXPR, inside_type,
+ defop0,
+ double_int_to_tree
+ (inside_type, double_int_mask (inter_prec)));
+ if (!useless_type_conversion_p (type, inside_type))
+ {
+ tem = force_gimple_operand_gsi (gsi, tem, true, NULL_TREE, true,
+ GSI_SAME_STMT);
+ gimple_assign_set_rhs1 (stmt, tem);
+ }
+ else
+ gimple_assign_set_rhs_from_tree (gsi, tem);
+ update_stmt (gsi_stmt (*gsi));
+ return true;
+ }
+ }
+
+ return false;
+}
+
/* Main entry point for the forward propagation optimizer. */
static unsigned int
@@ -2061,6 +2221,13 @@ tree_ssa_forward_propagate_single_use_vars (void)
cfg_changed |= associate_plusminus (stmt);
gsi_next (&gsi);
}
+ else if (CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (stmt))
+ || gimple_assign_rhs_code (stmt) == FLOAT_EXPR
+ || gimple_assign_rhs_code (stmt) == FIX_TRUNC_EXPR)
+ {
+ if (!combine_conversions (&gsi))
+ gsi_next (&gsi);
+ }
else
gsi_next (&gsi);
}