@@ -181,6 +181,56 @@ initialize_ao_ref_for_dse (gimple *stmt, ao_ref *write, bool maxlen)
return true;
}
+ case BUILT_IN_STRCPY:
+ case BUILT_IN_STRCPY_CHK:
+ {
+ tree dest = gimple_call_arg (stmt, 0);
+ tree src = gimple_call_arg (stmt, 1);
+ tree size = NULL_TREE;
+
+ /* Get the range of the length of the source string. */
+ c_strlen_data src_data = { };
+ if (get_range_strlen (src, &src_data, 1))
+ {
+ size = maxlen ? src_data.maxlen : src_data.minlen;
+
+ /* Adjust the source length to account for NUL terminator. */
+ size = fold_build2 (PLUS_EXPR, TREE_TYPE (size), size, integer_one_node);
+ }
+
+ /* If we are asking for the minimum size and did not get a usable
+ size from get_range_strlen, then the minimum size would be zero
+ and there's nothing we can do. If we were asking for the
+ maximum size, then we can try and get a size from the destination
+ object. */
+ if (!maxlen && !size)
+ return false;
+
+ /* If we did not get a size from the source operand, then try to
+ get it from the size of the destination object's type.
+
+ If we got a size from the source operand, then choose the
+ minimum of the size from the operand and the size we got
+ from the input operand. In theory the latter would be
+ sufficient, but the size from the input operand is a range
+ and could, in theory, be larger than the output buffer
+ due to imprecision in the computation of the size. */
+ tree dest_size = objsize_from_type (dest);
+ if (!size)
+ size = dest_size;
+ else if (dest_size)
+ size = fold_build2 (MIN_EXPR, TREE_TYPE (dest_size),
+ dest_size, size);
+
+ /* If we still don't have size information, then assume we can
+ not analyze this case. */
+ if (!size)
+ return false;
+
+ ao_ref_init_from_ptr_and_size (write, dest, size);
+ return true;
+ }
+
/* A calloc call can never be dead, but it can make
subsequent stores redundant if they store 0 into
the same memory locations. */
@@ -1078,6 +1128,26 @@ dse_dom_walker::dse_optimize_stmt (gimple_stmt_iterator *gsi)
return;
}
+ case BUILT_IN_STRCPY:
+ case BUILT_IN_STRCPY_CHK:
+ {
+ enum dse_store_status store_status;
+ m_byte_tracking_enabled
+ = setup_live_bytes_from_ref (&ref, m_live_bytes);
+ store_status = dse_classify_store (&ref, stmt,
+ m_byte_tracking_enabled,
+ m_live_bytes);
+ if (store_status == DSE_STORE_LIVE)
+ return;
+
+ /* We don't handle trimming these calls, though we might
+ be able to trim a strcpy into a strncpy for example. */
+
+ if (store_status == DSE_STORE_DEAD)
+ delete_dead_or_redundant_call (gsi, "dead");
+ break;
+ }
+
case BUILT_IN_CALLOC:
/* We already know the arguments are integer constants. */
dse_optimize_redundant_stores (stmt);
new file mode 100644
@@ -0,0 +1,40 @@
+/* { dg-options "-O2 -fdump-tree-dse-details -fno-tree-vrp -fno-tree-forwprop" } */
+extern void frob (char *);
+void g (int x)
+{
+ char a[8];
+ __builtin_strcpy (a, x ? "12345" : "56789");
+ __builtin_memset (a, 0, sizeof a);
+ frob (a);
+}
+
+void h (int x)
+{
+ char a[8];
+ __builtin_memset (a, 0, sizeof a);
+ __builtin_strcpy (a, x ? "12345" : "56789");
+ frob (a);
+}
+
+
+void i (int x)
+{
+ char a[8];
+ __builtin_strcpy (a, x ? "12345" : "56");
+ __builtin_memset (a, 0, sizeof a);
+ frob (a);
+}
+
+void j (int x)
+{
+ char a[8];
+ __builtin_memset (a, 0, sizeof a);
+ __builtin_strcpy (a, x ? "12345" : "56");
+ frob (a);
+}
+
+/* We can delete the dead strcpy call in the first and third tests. */
+
+/* { dg-final { scan-tree-dump-times "Deleted dead call" 2 "dse1" } } */
+/* { dg-final { scan-tree-dump-times "Trimming statement " 2 "dse1" } } */
+