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.
+}