The attached patch adds code to work harder to determine whether
the destination of an assignment involving MEM_REF is the same
as the destination of a prior strncpy call.  The included test
case demonstrates when this situation comes up.  During ccp,
dstbase and lhsbase returned by get_addr_base_and_unit_offset()
end up looking like this:

  _8 = &pb_3(D)->a;
  _9 = _8;
  _1 = _9;
  strncpy (MEM_REF (&pb_3(D)->a), ...);
  MEM[(struct S *)_1].a[n_7] = 0;

so the loops follow the simple assignments until we get at
the ADDR_EXPR assigned to _8 which is the same as the strncpy
destination.

Tested on x86_64-linux.

Martin
PR tree-optimization/84561 - -Wstringop-truncation with -O2 depends on strncpy's size type

gcc/ChangeLog:

	PR tree-optimization/84561
	* tree-ssa-strlen.c (maybe_diag_stxncpy_trunc): Look harder for
	MEM_REF operand equality.

gcc/testsuite/ChangeLog:

	PR tree-optimization/84561
	* gcc.dg/Wstringop-truncation-6.c: New test.

Index: gcc/tree-ssa-strlen.c
===================================================================
--- gcc/tree-ssa-strlen.c	(revision 263965)
+++ gcc/tree-ssa-strlen.c	(working copy)
@@ -1978,11 +1978,43 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi
 
       poly_int64 lhsoff;
       tree lhsbase = get_addr_base_and_unit_offset (lhs, &lhsoff);
-      if (lhsbase
-	  && dstbase
-	  && known_eq (dstoff, lhsoff)
-	  && operand_equal_p (dstbase, lhsbase, 0))
+      bool eqloff = lhsbase && dstbase && known_eq (dstoff, lhsoff);
+
+      if (eqloff && operand_equal_p (dstbase, lhsbase, 0))
 	return false;
+
+      if (eqloff
+	  && TREE_CODE (dstbase) == MEM_REF
+	  && TREE_CODE (lhsbase) == MEM_REF
+	  && tree_int_cst_equal (TREE_OPERAND (dstbase, 1),
+				 TREE_OPERAND (lhsbase, 1)))
+	{
+	  /* For MEM_REFs with the same offset follow the chain of
+	     SSA_NAME assignments to their source and compare those
+	     for equality.  */
+	  dstbase = TREE_OPERAND (dstbase, 0);
+	  while (TREE_CODE (dstbase) == SSA_NAME)
+	    {
+	      gimple *def = SSA_NAME_DEF_STMT (dstbase);
+	      if (gimple_assign_single_p (def))
+		dstbase = gimple_assign_rhs1 (def);
+	      else
+		break;
+	    }
+
+	  lhsbase = TREE_OPERAND (lhsbase, 0);
+	  while (TREE_CODE (lhsbase) == SSA_NAME)
+	    {
+	      gimple *def = SSA_NAME_DEF_STMT (lhsbase);
+	      if (gimple_assign_single_p (def))
+		lhsbase = gimple_assign_rhs1 (def);
+	      else
+		break;
+	    }
+
+	  if (operand_equal_p (dstbase, lhsbase, 0))
+	    return false;
+	}
     }
 
   int prec = TYPE_PRECISION (TREE_TYPE (cnt));
Index: gcc/testsuite/gcc.dg/Wstringop-truncation-6.c
===================================================================
--- gcc/testsuite/gcc.dg/Wstringop-truncation-6.c	(nonexistent)
+++ gcc/testsuite/gcc.dg/Wstringop-truncation-6.c	(working copy)
@@ -0,0 +1,59 @@
+/* PR tree-optimization/84561 - -Wstringop-truncation with -O2 depends
+   on strncpy's size type
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+enum { N = 3 };
+
+struct S
+{
+  char a[N + 1];
+};
+
+void set (struct S *ps, const char* s, size_t n)
+{
+  if (n > N)
+    n = N;
+
+  __builtin_strncpy (ps->a, s, n);   /* { dg-bogus "\\\[-Wstringop-truncation]" } */
+  ps->a[n] = 0;
+}
+
+struct A
+{
+  struct S str;
+};
+
+void setStringSize_t (struct A *pa, const char *s, size_t n)
+{
+  set (&pa->str, s, n);
+}
+
+void setStringUnsignedInt (struct A *pa, const char* s, unsigned int n)
+{
+  set (&pa->str, s, n);
+}
+
+struct B
+{
+  struct A a;
+};
+
+struct A* getA (struct B *pb)
+{
+  return &pb->a;
+}
+
+void f (struct A *pa)
+{
+  setStringUnsignedInt (pa, "123", N); // No warning here.
+  setStringSize_t (pa, "123", N);      // No warning here.
+}
+
+void g (struct B *pb)
+{
+  setStringUnsignedInt (getA (pb), "123", N);  // Unexpected warning here.
+  setStringSize_t (getA (pb), "123", N);       // No warning here.
+}

Reply via email to