aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-forwprop.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/tree-ssa-forwprop.cc')
-rw-r--r--gcc/tree-ssa-forwprop.cc421
1 files changed, 234 insertions, 187 deletions
diff --git a/gcc/tree-ssa-forwprop.cc b/gcc/tree-ssa-forwprop.cc
index 43b1c9d..3d38d88 100644
--- a/gcc/tree-ssa-forwprop.cc
+++ b/gcc/tree-ssa-forwprop.cc
@@ -1190,117 +1190,55 @@ constant_pointer_difference (tree p1, tree p2)
return NULL_TREE;
}
-
-/* Optimize
- a = {};
- b = a;
- into
- a = {};
- b = {};
- Similarly for memset (&a, ..., sizeof (a)); instead of a = {};
- and/or memcpy (&b, &a, sizeof (a)); instead of b = a; */
-
+/* Helper function for optimize_aggr_zeroprop.
+ Props the zeroing (memset, VAL) that was done in DEST+OFFSET:LEN
+ (DEFSTMT) into the STMT. Returns true if the STMT was updated. */
static bool
-optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree len)
+optimize_aggr_zeroprop_1 (gimple *defstmt, gimple *stmt,
+ tree dest, poly_int64 offset, tree val,
+ poly_offset_int len)
{
- ao_ref read;
- gimple *stmt = gsi_stmt (*gsip);
- if (gimple_has_volatile_ops (stmt))
- return false;
-
- tree src2 = NULL_TREE, len2 = NULL_TREE;
- poly_int64 offset, offset2;
- tree val = integer_zero_node;
- bool len_was_null = len == NULL_TREE;
- if (len == NULL_TREE)
- len = (TREE_CODE (src) == COMPONENT_REF
- ? DECL_SIZE_UNIT (TREE_OPERAND (src, 1))
- : TYPE_SIZE_UNIT (TREE_TYPE (src)));
- if (len == NULL_TREE
- || !poly_int_tree_p (len))
- return false;
+ tree src2;
+ tree len2 = NULL_TREE;
+ poly_int64 offset2;
- ao_ref_init (&read, src);
- tree vuse = gimple_vuse (stmt);
- gimple *defstmt;
- unsigned limit = param_sccvn_max_alias_queries_per_access;
- do {
- /* If the vuse is the default definition, then there is no stores beforhand. */
- if (SSA_NAME_IS_DEFAULT_DEF (vuse))
- return false;
- defstmt = SSA_NAME_DEF_STMT (vuse);
- if (is_a <gphi*>(defstmt))
- return false;
- if (limit-- == 0)
- return false;
- /* If the len was null, then we can use TBBA. */
- if (stmt_may_clobber_ref_p_1 (defstmt, &read,
- /* tbaa_p = */ len_was_null))
- break;
- vuse = gimple_vuse (defstmt);
- } while (true);
-
- if (gimple_store_p (defstmt)
- && gimple_assign_single_p (defstmt)
- && TREE_CODE (gimple_assign_rhs1 (defstmt)) == STRING_CST
- && !gimple_clobber_p (defstmt))
+ if (gimple_call_builtin_p (stmt, BUILT_IN_MEMCPY)
+ && TREE_CODE (gimple_call_arg (stmt, 1)) == ADDR_EXPR
+ && poly_int_tree_p (gimple_call_arg (stmt, 2)))
{
- tree str = gimple_assign_rhs1 (defstmt);
- src2 = gimple_assign_lhs (defstmt);
- /* The string must contain all null char's for now. */
- for (int i = 0; i < TREE_STRING_LENGTH (str); i++)
- {
- if (TREE_STRING_POINTER (str)[i] != 0)
- {
- src2 = NULL_TREE;
- break;
- }
- }
- }
- else if (gimple_store_p (defstmt)
- && gimple_assign_single_p (defstmt)
- && TREE_CODE (gimple_assign_rhs1 (defstmt)) == CONSTRUCTOR
- && !gimple_clobber_p (defstmt))
- src2 = gimple_assign_lhs (defstmt);
- else if (gimple_call_builtin_p (defstmt, BUILT_IN_MEMSET)
- && TREE_CODE (gimple_call_arg (defstmt, 0)) == ADDR_EXPR
- && TREE_CODE (gimple_call_arg (defstmt, 1)) == INTEGER_CST)
- {
- src2 = TREE_OPERAND (gimple_call_arg (defstmt, 0), 0);
- len2 = gimple_call_arg (defstmt, 2);
- val = gimple_call_arg (defstmt, 1);
- /* For non-0 val, we'd have to transform stmt from assignment
- into memset (only if dest is addressable). */
- if (!integer_zerop (val) && is_gimple_assign (stmt))
- src2 = NULL_TREE;
+ src2 = TREE_OPERAND (gimple_call_arg (stmt, 1), 0);
+ len2 = gimple_call_arg (stmt, 2);
}
+ else if (gimple_assign_load_p (stmt) && gimple_store_p (stmt))
+ {
+ src2 = gimple_assign_rhs1 (stmt);
+ len2 = (TREE_CODE (src2) == COMPONENT_REF
+ ? DECL_SIZE_UNIT (TREE_OPERAND (src2, 1))
+ : TYPE_SIZE_UNIT (TREE_TYPE (src2)));
+ /* Can only handle zero memsets. */
+ if (!integer_zerop (val))
+ return false;
+ }
+ else
+ return false;
- if (src2 == NULL_TREE)
- return false;
-
- if (len2 == NULL_TREE)
- len2 = (TREE_CODE (src2) == COMPONENT_REF
- ? DECL_SIZE_UNIT (TREE_OPERAND (src2, 1))
- : TYPE_SIZE_UNIT (TREE_TYPE (src2)));
if (len2 == NULL_TREE
|| !poly_int_tree_p (len2))
return false;
- src = get_addr_base_and_unit_offset (src, &offset);
src2 = get_addr_base_and_unit_offset (src2, &offset2);
- if (src == NULL_TREE
- || src2 == NULL_TREE
- || maybe_lt (offset, offset2))
+ if (src2 == NULL_TREE
+ || maybe_lt (offset2, offset))
return false;
- if (!operand_equal_p (src, src2, 0))
+ if (!operand_equal_p (dest, src2, 0))
return false;
- /* [ src + offset2, src + offset2 + len2 - 1 ] is set to val.
+ /* [ dest + offset, dest + offset + len - 1 ] is set to val.
Make sure that
- [ src + offset, src + offset + len - 1 ] is a subset of that. */
- if (maybe_gt (wi::to_poly_offset (len) + (offset - offset2),
- wi::to_poly_offset (len2)))
+ [ dest + offset2, dest + offset2 + len2 - 1 ] is a subset of that. */
+ if (maybe_gt (wi::to_poly_offset (len2) + (offset2 - offset),
+ len))
return false;
if (dump_file && (dump_flags & TDF_DETAILS))
@@ -1310,7 +1248,7 @@ optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree
fprintf (dump_file, "after previous\n ");
print_gimple_stmt (dump_file, defstmt, 0, dump_flags);
}
-
+ gimple *orig_stmt = stmt;
/* For simplicity, don't change the kind of the stmt,
turn dest = src; into dest = {}; and memcpy (&dest, &src, len);
into memset (&dest, val, len);
@@ -1320,8 +1258,10 @@ optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree
of dest, dest isn't volatile. */
if (is_gimple_assign (stmt))
{
- tree ctor = build_constructor (TREE_TYPE (dest), NULL);
- gimple_assign_set_rhs_from_tree (gsip, ctor);
+ tree ctor_type = TREE_TYPE (gimple_assign_lhs (stmt));
+ tree ctor = build_constructor (ctor_type, NULL);
+ gimple_stmt_iterator gsi = gsi_for_stmt (stmt);
+ gimple_assign_set_rhs_from_tree (&gsi, ctor);
update_stmt (stmt);
statistics_counter_event (cfun, "copy zeroing propagation of aggregate", 1);
}
@@ -1341,89 +1281,210 @@ optimize_memcpy_to_memset (gimple_stmt_iterator *gsip, tree dest, tree src, tree
fprintf (dump_file, "into\n ");
print_gimple_stmt (dump_file, stmt, 0, dump_flags);
}
+
+ /* Mark the bb for eh cleanup if needed. */
+ if (maybe_clean_or_replace_eh_stmt (orig_stmt, stmt))
+ bitmap_set_bit (to_purge, gimple_bb (stmt)->index);
+
return true;
}
-/* Optimizes
- a = c;
+
+/* Optimize
+ a = {}; // DEST = value ;; LEN(nullptr)
b = a;
into
- a = c;
- b = c;
- GSIP is the second statement and SRC is the common
- between the statements.
-*/
+ a = {};
+ b = {};
+ Similarly for memset (&a, ..., sizeof (a)); instead of a = {};
+ and/or memcpy (&b, &a, sizeof (a)); instead of b = a; */
+
static bool
-optimize_agr_copyprop (gimple_stmt_iterator *gsip)
+optimize_aggr_zeroprop (gimple_stmt_iterator *gsip)
{
+ ao_ref read;
gimple *stmt = gsi_stmt (*gsip);
if (gimple_has_volatile_ops (stmt))
return false;
- tree dest = gimple_assign_lhs (stmt);
- tree src = gimple_assign_rhs1 (stmt);
- /* If the statement is `src = src;` then ignore it. */
- if (operand_equal_p (dest, src, 0))
- return false;
+ tree dest = NULL_TREE;
+ tree val = integer_zero_node;
+ tree len = NULL_TREE;
+ bool can_use_tbba = true;
+ bool changed = false;
+
+ if (gimple_call_builtin_p (stmt, BUILT_IN_MEMSET)
+ && TREE_CODE (gimple_call_arg (stmt, 0)) == ADDR_EXPR
+ && TREE_CODE (gimple_call_arg (stmt, 1)) == INTEGER_CST
+ && poly_int_tree_p (gimple_call_arg (stmt, 2)))
+ {
+ dest = TREE_OPERAND (gimple_call_arg (stmt, 0), 0);
+ len = gimple_call_arg (stmt, 2);
+ val = gimple_call_arg (stmt, 1);
+ ao_ref_init_from_ptr_and_size (&read, gimple_call_arg (stmt, 0), len);
+ can_use_tbba = false;
+ }
+ else if (gimple_store_p (stmt)
+ && gimple_assign_single_p (stmt)
+ && TREE_CODE (gimple_assign_rhs1 (stmt)) == STRING_CST)
+ {
+ tree str = gimple_assign_rhs1 (stmt);
+ dest = gimple_assign_lhs (stmt);
+ ao_ref_init (&read, dest);
+ /* The string must contain all null char's for now. */
+ for (int i = 0; i < TREE_STRING_LENGTH (str); i++)
+ {
+ if (TREE_STRING_POINTER (str)[i] != 0)
+ {
+ dest = NULL_TREE;
+ break;
+ }
+ }
+ }
+ else if (gimple_store_p (stmt)
+ && gimple_assign_single_p (stmt)
+ && TREE_CODE (gimple_assign_rhs1 (stmt)) == CONSTRUCTOR
+ && !gimple_clobber_p (stmt))
+ {
+ dest = gimple_assign_lhs (stmt);
+ ao_ref_init (&read, dest);
+ }
- tree vuse = gimple_vuse (stmt);
- /* If the vuse is the default definition, then there is no store beforehand. */
- if (SSA_NAME_IS_DEFAULT_DEF (vuse))
+ if (dest == NULL_TREE)
return false;
- gimple *defstmt = SSA_NAME_DEF_STMT (vuse);
- if (!gimple_assign_load_p (defstmt)
- || !gimple_store_p (defstmt))
+
+ if (len == NULL_TREE)
+ len = (TREE_CODE (dest) == COMPONENT_REF
+ ? DECL_SIZE_UNIT (TREE_OPERAND (dest, 1))
+ : TYPE_SIZE_UNIT (TREE_TYPE (dest)));
+ if (len == NULL_TREE
+ || !poly_int_tree_p (len))
return false;
- if (gimple_has_volatile_ops (defstmt))
+
+ /* This store needs to be on the byte boundary and pointing to an object. */
+ poly_int64 offset;
+ tree dest_base = get_addr_base_and_unit_offset (dest, &offset);
+ if (dest_base == NULL_TREE)
return false;
- tree dest2 = gimple_assign_lhs (defstmt);
- tree src2 = gimple_assign_rhs1 (defstmt);
+ /* Setup the worklist. */
+ auto_vec<std::pair<tree, unsigned>> worklist;
+ unsigned limit = param_sccvn_max_alias_queries_per_access;
+ worklist.safe_push (std::make_pair (gimple_vdef (stmt), limit));
- /* If the original store is `src2 = src2;` skip over it. */
- if (operand_equal_p (src2, dest2, 0))
- return false;
- if (!operand_equal_p (src, dest2, 0))
+ while (!worklist.is_empty ())
+ {
+ std::pair<tree, unsigned> top = worklist.pop ();
+ tree vdef = top.first;
+ limit = top.second;
+ gimple *use_stmt;
+ imm_use_iterator iter;
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, vdef)
+ {
+ /* Handling PHI nodes might not be worth it so don't. */
+ if (is_a <gphi*> (use_stmt))
+ continue;
+
+ /* If this statement does not clobber add the vdef stmt to the
+ worklist. */
+ if (gimple_vdef (use_stmt)
+ && !stmt_may_clobber_ref_p_1 (use_stmt, &read,
+ /* tbaa_p = */ can_use_tbba)
+ && limit != 0)
+ worklist.safe_push (std::make_pair (gimple_vdef (use_stmt),
+ limit - 1));
+
+ if (optimize_aggr_zeroprop_1 (stmt, use_stmt, dest_base, offset,
+ val, wi::to_poly_offset (len)))
+ changed = true;
+ }
+ }
+
+ return changed;
+}
+/* Optimizes
+ DEST = SRC;
+ DEST2 = DEST; # DEST2 = SRC2;
+ into
+ DEST = SRC;
+ DEST2 = SRC;
+ GSIP is the first statement and SRC is the common
+ between the statements.
+*/
+static bool
+optimize_agr_copyprop (gimple_stmt_iterator *gsip)
+{
+ gimple *stmt = gsi_stmt (*gsip);
+ if (gimple_has_volatile_ops (stmt))
return false;
+ /* Can't prop if the statement could throw. */
+ if (stmt_could_throw_p (cfun, stmt))
+ return false;
- /* For 2 memory refences and using a temporary to do the copy,
- don't remove the temporary as the 2 memory references might overlap.
- Note t does not need to be decl as it could be field.
- See PR 22237 for full details.
- E.g.
- t = *a;
- *b = t;
- Cannot be convert into
- t = *a;
- *b = *a;
- Though the following is allowed to be done:
- t = *a;
- *a = t;
- And convert it into:
- t = *a;
- *a = *a;
- */
- if (!operand_equal_p (src2, dest, 0)
- && !DECL_P (dest) && !DECL_P (src2))
+ tree dest = gimple_assign_lhs (stmt);
+ tree src = gimple_assign_rhs1 (stmt);
+ /* If the statement is `src = src;` then ignore it. */
+ if (operand_equal_p (dest, src, 0))
return false;
- if (dump_file && (dump_flags & TDF_DETAILS))
+ tree vdef = gimple_vdef (stmt);
+ imm_use_iterator iter;
+ gimple *use_stmt;
+ bool changed = false;
+ FOR_EACH_IMM_USE_STMT (use_stmt, iter, vdef)
{
- fprintf (dump_file, "Simplified\n ");
- print_gimple_stmt (dump_file, stmt, 0, dump_flags);
- fprintf (dump_file, "after previous\n ");
- print_gimple_stmt (dump_file, defstmt, 0, dump_flags);
- }
- gimple_assign_set_rhs_from_tree (gsip, unshare_expr (src2));
- update_stmt (stmt);
+ if (!gimple_assign_load_p (use_stmt)
+ || !gimple_store_p (use_stmt))
+ continue;
+ if (gimple_has_volatile_ops (use_stmt))
+ continue;
+ tree dest2 = gimple_assign_lhs (use_stmt);
+ tree src2 = gimple_assign_rhs1 (use_stmt);
+ /* If the new store is `src2 = src2;` skip over it. */
+ if (operand_equal_p (src2, dest2, 0))
+ continue;
+ if (!operand_equal_p (dest, src2, 0))
+ continue;
+ /* For 2 memory refences and using a temporary to do the copy,
+ don't remove the temporary as the 2 memory references might overlap.
+ Note t does not need to be decl as it could be field.
+ See PR 22237 for full details.
+ E.g.
+ t = *a; #DEST = SRC;
+ *b = t; #DEST2 = SRC2;
+ Cannot be convert into
+ t = *a;
+ *b = *a;
+ Though the following is allowed to be done:
+ t = *a;
+ *a = t;
+ And convert it into:
+ t = *a;
+ *a = *a;
+ */
+ if (!operand_equal_p (dest2, src, 0)
+ && !DECL_P (dest2) && !DECL_P (src))
+ continue;
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "Simplified\n ");
+ print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
+ fprintf (dump_file, "after previous\n ");
+ print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+ }
+ gimple_stmt_iterator gsi = gsi_for_stmt (use_stmt);
+ gimple_assign_set_rhs_from_tree (&gsi, unshare_expr (src));
+ update_stmt (use_stmt);
- if (dump_file && (dump_flags & TDF_DETAILS))
- {
- fprintf (dump_file, "into\n ");
- print_gimple_stmt (dump_file, stmt, 0, dump_flags);
+ if (dump_file && (dump_flags & TDF_DETAILS))
+ {
+ fprintf (dump_file, "into\n ");
+ print_gimple_stmt (dump_file, use_stmt, 0, dump_flags);
+ }
+ statistics_counter_event (cfun, "copy prop for aggregate", 1);
+ changed = true;
}
- statistics_counter_event (cfun, "copy prop for aggregate", 1);
- return true;
+ return changed;
}
/* *GSI_P is a GIMPLE_CALL to a builtin function.
@@ -1463,22 +1524,6 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2)
switch (DECL_FUNCTION_CODE (callee2))
{
- case BUILT_IN_MEMCPY:
- if (gimple_call_num_args (stmt2) == 3)
- {
- tree dest = gimple_call_arg (stmt2, 0);
- tree src = gimple_call_arg (stmt2, 1);
- tree len = gimple_call_arg (stmt2, 2);
- /* Try to optimize the memcpy to memset if src
- and dest are addresses. */
- if (TREE_CODE (dest) == ADDR_EXPR
- && TREE_CODE (src) == ADDR_EXPR
- && TREE_CODE (len) == INTEGER_CST
- && optimize_memcpy_to_memset (gsi_p, TREE_OPERAND (dest, 0),
- TREE_OPERAND (src, 0), len))
- return true;
- }
- break;
case BUILT_IN_MEMCHR:
if (gimple_call_num_args (stmt2) == 3
&& (res = gimple_call_lhs (stmt2)) != nullptr
@@ -1540,6 +1585,13 @@ simplify_builtin_call (gimple_stmt_iterator *gsi_p, tree callee2)
break;
case BUILT_IN_MEMSET:
+ if (gimple_call_num_args (stmt2) == 3)
+ {
+ /* Try to prop the zeroing/value of the memset to memcpy
+ if the dest is an address and the value is a constant. */
+ if (optimize_aggr_zeroprop (gsi_p))
+ return true;
+ }
if (gimple_call_num_args (stmt2) != 3
|| gimple_call_lhs (stmt2)
|| CHAR_BIT != 8
@@ -4858,21 +4910,16 @@ pass_forwprop::execute (function *fun)
{
tree rhs1 = gimple_assign_rhs1 (stmt);
enum tree_code code = gimple_assign_rhs_code (stmt);
- if (gimple_assign_load_p (stmt) && gimple_store_p (stmt))
+ if (gimple_store_p (stmt) && optimize_aggr_zeroprop (&gsi))
{
- if (optimize_memcpy_to_memset (&gsi,
- gimple_assign_lhs (stmt),
- gimple_assign_rhs1 (stmt),
- /* len = */NULL_TREE))
- {
- changed = true;
- break;
- }
- if (optimize_agr_copyprop (&gsi))
- {
- changed = true;
- break;
- }
+ changed = true;
+ break;
+ }
+ if (gimple_assign_load_p (stmt) && gimple_store_p (stmt)
+ && optimize_agr_copyprop (&gsi))
+ {
+ changed = true;
+ break;
}
if (TREE_CODE_CLASS (code) == tcc_comparison)