diff mbox series

[RFA,PR,tree-optimization/80576] Handle strcpy and strcpy_chk in DSE

Message ID f48546c8-50e1-7a3c-4d77-cac31e600726@redhat.com
State New
Headers show
Series [RFA,PR,tree-optimization/80576] Handle strcpy and strcpy_chk in DSE | expand

Commit Message

Jeff Law Aug. 19, 2019, 1:11 a.m. UTC
So this builds on the previous DSE patch to add handling of strcpy and
resolves the remainder of 80576.

Recall there's two cases to consider.

If the strcpy is the first store (ie, potentially dead), then the
conservative choice when setting up the ao_ref is to take the smaller of
the destination object's size and the maximum length of the source string.

For the second store (ie, the killing store) we want the minimum of the
length of the source string when setting up the ao_ref.

We do not handle trimming in the case of a partially dead call to
strcpy.  We only handle cases where it's fully dead.


Bootstrapped and regression tested on x86_64, i686, aarch64, ppc64,
ppc64le, s390x & sparc64.

Also built and tested the various *-elf targets with no regressions.  I
also verified the new test passes on all those targets.

Several other targets have been bootstrapped (alpha, m68k, various arm*
things, mipsisa32r2, riscv64 and others).


OK for the trunk?

Jeff
PR tree-optimization/80576
	* tree-ssa-dse.c (initialize_ao_ref_for_dse): Handle strcpy and
	strcpy_chk.
	(dse_dom_walker::dse_optimize_stmt): Similarly.
 
	* gcc.dg/tree-ssa/ssa-dse-41.c: New test.

commit 24f60672cacb4da2ae898ec7e90bd55f52073954
Author: Jeff Law <law@torsion.usersys.redhat.com>
Date:   Fri Aug 9 18:13:50 2019 -0400

    MOre DSE improvements
    
    Add test
diff mbox series

Patch

diff --git a/gcc/tree-ssa-dse.c b/gcc/tree-ssa-dse.c
index ae03980f792..47907617aaf 100644
--- a/gcc/tree-ssa-dse.c
+++ b/gcc/tree-ssa-dse.c
@@ -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);
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-41.c b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-41.c
new file mode 100644
index 00000000000..0f64c265c29
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ssa-dse-41.c
@@ -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" } } */
+