mirror of
git://gcc.gnu.org/git/gcc.git
synced 2025-03-24 19:01:17 +08:00
tree-optimization: Fix tree dse of strncpy PR93249
As the testcase shows, tail trimming of strncpy in tree-ssa-dse.c is fine, we just copy or clear fewer bytes in the destination, but unlike memcpy/memset etc., head trimming is problematic in certain cases. If we can prove that there are no zero bytes among initial head_trim bytes, it is ok to trim it, if we can prove there is at least one zero byte among initial head_trim bytes, we could (not implemented in the patch) turn the strncpy into memset 0, but otherwise we need to avoid the head trimming, because the presence or absence of NUL byte there changes the behavior for subsequent bytes, whether further bytes from src are copied or if further bytes are cleared. 2020-01-15 Jakub Jelinek <jakub@redhat.com> PR tree-optimization/93249 * tree-ssa-dse.c: Include builtins.h and gimple-fold.h. (maybe_trim_memstar_call): Move head_trim and tail_trim vars to function body scope, reindent. For BUILTIN_IN_STRNCPY*, don't perform head trim unless we can prove there are no '\0' chars from the source among the first head_trim chars. * gcc.c-torture/execute/pr93249.c: New test.
This commit is contained in:
parent
d8998708ca
commit
623c6fddd6
@ -1,3 +1,12 @@
|
||||
2020-01-15 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR tree-optimization/93249
|
||||
* tree-ssa-dse.c: Include builtins.h and gimple-fold.h.
|
||||
(maybe_trim_memstar_call): Move head_trim and tail_trim vars to
|
||||
function body scope, reindent. For BUILTIN_IN_STRNCPY*, don't
|
||||
perform head trim unless we can prove there are no '\0' chars
|
||||
from the source among the first head_trim chars.
|
||||
|
||||
2020-01-14 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* Makefile.in (ANALYZER_OBJS): Add analyzer/function-set.o.
|
||||
|
@ -1,3 +1,8 @@
|
||||
2020-01-15 Jakub Jelinek <jakub@redhat.com>
|
||||
|
||||
PR tree-optimization/93249
|
||||
* gcc.c-torture/execute/pr93249.c: New test.
|
||||
|
||||
2020-01-14 David Malcolm <dmalcolm@redhat.com>
|
||||
|
||||
* gcc.dg/analyzer/signal-5.c: New test.
|
||||
|
40
gcc/testsuite/gcc.c-torture/execute/pr93249.c
Normal file
40
gcc/testsuite/gcc.c-torture/execute/pr93249.c
Normal file
@ -0,0 +1,40 @@
|
||||
/* PR tree-optimization/93249 */
|
||||
|
||||
char a[2], b[4], c[6];
|
||||
|
||||
void
|
||||
foo (void)
|
||||
{
|
||||
char d[2] = { 0x00, 0x11 };
|
||||
__builtin_strncpy (&b[2], d, 2);
|
||||
__builtin_strncpy (&b[1], a, 2);
|
||||
if (b[0] || b[1] || b[2] || b[3])
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
void
|
||||
bar (void)
|
||||
{
|
||||
__builtin_strncpy (&b[2], "\0\x11", 2);
|
||||
__builtin_strncpy (&b[1], a, 2);
|
||||
if (b[0] || b[1] || b[2] || b[3])
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
void
|
||||
baz (void)
|
||||
{
|
||||
__builtin_strncpy (&c[2], "\x11\x11\0\x11", 4);
|
||||
__builtin_strncpy (&c[1], a, 2);
|
||||
if (c[0] || c[1] || c[2] || c[3] != 0x11 || c[4] || c[5])
|
||||
__builtin_abort ();
|
||||
}
|
||||
|
||||
int
|
||||
main ()
|
||||
{
|
||||
foo ();
|
||||
bar ();
|
||||
baz ();
|
||||
return 0;
|
||||
}
|
@ -36,6 +36,8 @@ along with GCC; see the file COPYING3. If not see
|
||||
#include "alias.h"
|
||||
#include "tree-ssa-loop.h"
|
||||
#include "tree-ssa-dse.h"
|
||||
#include "builtins.h"
|
||||
#include "gimple-fold.h"
|
||||
|
||||
/* This file implements dead store elimination.
|
||||
|
||||
@ -456,56 +458,83 @@ increment_start_addr (gimple *stmt, tree *where, int increment)
|
||||
static void
|
||||
maybe_trim_memstar_call (ao_ref *ref, sbitmap live, gimple *stmt)
|
||||
{
|
||||
int head_trim, tail_trim;
|
||||
switch (DECL_FUNCTION_CODE (gimple_call_fndecl (stmt)))
|
||||
{
|
||||
case BUILT_IN_STRNCPY:
|
||||
case BUILT_IN_STRNCPY_CHK:
|
||||
compute_trims (ref, live, &head_trim, &tail_trim, stmt);
|
||||
if (head_trim)
|
||||
{
|
||||
/* Head trimming of strncpy is only possible if we can
|
||||
prove all bytes we would trim are non-zero (or we could
|
||||
turn the strncpy into memset if there must be zero
|
||||
among the head trimmed bytes). If we don't know anything
|
||||
about those bytes, the presence or absence of '\0' bytes
|
||||
in there will affect whether it acts for the non-trimmed
|
||||
bytes as memset or memcpy/strncpy. */
|
||||
c_strlen_data lendata = { };
|
||||
int orig_head_trim = head_trim;
|
||||
tree srcstr = gimple_call_arg (stmt, 1);
|
||||
if (!get_range_strlen (srcstr, &lendata, /*eltsize=*/1)
|
||||
|| !tree_fits_uhwi_p (lendata.minlen))
|
||||
head_trim = 0;
|
||||
else if (tree_to_uhwi (lendata.minlen) < (unsigned) head_trim)
|
||||
{
|
||||
head_trim = tree_to_uhwi (lendata.minlen);
|
||||
if ((orig_head_trim & (UNITS_PER_WORD - 1)) == 0)
|
||||
head_trim &= ~(UNITS_PER_WORD - 1);
|
||||
}
|
||||
if (orig_head_trim != head_trim
|
||||
&& dump_file
|
||||
&& (dump_flags & TDF_DETAILS))
|
||||
fprintf (dump_file,
|
||||
" Adjusting strncpy trimming to (head = %d,"
|
||||
" tail = %d)\n", head_trim, tail_trim);
|
||||
}
|
||||
goto do_memcpy;
|
||||
|
||||
case BUILT_IN_MEMCPY:
|
||||
case BUILT_IN_MEMMOVE:
|
||||
case BUILT_IN_STRNCPY:
|
||||
case BUILT_IN_MEMCPY_CHK:
|
||||
case BUILT_IN_MEMMOVE_CHK:
|
||||
case BUILT_IN_STRNCPY_CHK:
|
||||
{
|
||||
int head_trim, tail_trim;
|
||||
compute_trims (ref, live, &head_trim, &tail_trim, stmt);
|
||||
compute_trims (ref, live, &head_trim, &tail_trim, stmt);
|
||||
|
||||
/* Tail trimming is easy, we can just reduce the count. */
|
||||
if (tail_trim)
|
||||
decrement_count (stmt, tail_trim);
|
||||
do_memcpy:
|
||||
/* Tail trimming is easy, we can just reduce the count. */
|
||||
if (tail_trim)
|
||||
decrement_count (stmt, tail_trim);
|
||||
|
||||
/* Head trimming requires adjusting all the arguments. */
|
||||
if (head_trim)
|
||||
{
|
||||
tree *dst = gimple_call_arg_ptr (stmt, 0);
|
||||
increment_start_addr (stmt, dst, head_trim);
|
||||
tree *src = gimple_call_arg_ptr (stmt, 1);
|
||||
increment_start_addr (stmt, src, head_trim);
|
||||
decrement_count (stmt, head_trim);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Head trimming requires adjusting all the arguments. */
|
||||
if (head_trim)
|
||||
{
|
||||
tree *dst = gimple_call_arg_ptr (stmt, 0);
|
||||
increment_start_addr (stmt, dst, head_trim);
|
||||
tree *src = gimple_call_arg_ptr (stmt, 1);
|
||||
increment_start_addr (stmt, src, head_trim);
|
||||
decrement_count (stmt, head_trim);
|
||||
}
|
||||
break;
|
||||
|
||||
case BUILT_IN_MEMSET:
|
||||
case BUILT_IN_MEMSET_CHK:
|
||||
{
|
||||
int head_trim, tail_trim;
|
||||
compute_trims (ref, live, &head_trim, &tail_trim, stmt);
|
||||
compute_trims (ref, live, &head_trim, &tail_trim, stmt);
|
||||
|
||||
/* Tail trimming is easy, we can just reduce the count. */
|
||||
if (tail_trim)
|
||||
decrement_count (stmt, tail_trim);
|
||||
/* Tail trimming is easy, we can just reduce the count. */
|
||||
if (tail_trim)
|
||||
decrement_count (stmt, tail_trim);
|
||||
|
||||
/* Head trimming requires adjusting all the arguments. */
|
||||
if (head_trim)
|
||||
{
|
||||
tree *dst = gimple_call_arg_ptr (stmt, 0);
|
||||
increment_start_addr (stmt, dst, head_trim);
|
||||
decrement_count (stmt, head_trim);
|
||||
}
|
||||
break;
|
||||
}
|
||||
/* Head trimming requires adjusting all the arguments. */
|
||||
if (head_trim)
|
||||
{
|
||||
tree *dst = gimple_call_arg_ptr (stmt, 0);
|
||||
increment_start_addr (stmt, dst, head_trim);
|
||||
decrement_count (stmt, head_trim);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user