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