Commit b631bdb3 by Martin Sebor Committed by Martin Sebor

PR tree-optimization/91183 - strlen of a strcpy result with a conditional source not folded

PR tree-optimization/91183 - strlen of a strcpy result with a conditional source not folded
PR tree-optimization/86688 - missing -Wstringop-overflow using a non-string local array in strnlen with excessive bound

gcc/ChangeLog:

	PR tree-optimization/91183
	PR tree-optimization/86688
	* builtins.c (compute_objsize): Handle MEM_REF.
	* tree-ssa-strlen.c (class ssa_name_limit_t): New.
	(get_min_string_length): Remove.
	(count_nonzero_bytes): New function.
	(handle_char_store): Rename...
	(handle_store): to this.  Handle multibyte stores via integer types.
	(strlen_check_and_optimize_stmt): Adjust conditional and the called
	function name.

gcc/testsuite/ChangeLog:

	PR tree-optimization/91183
	PR tree-optimization/86688
	* gcc.dg/Wstringop-overflow-14.c: New test.
	* gcc.dg/attr-nonstring-2.c: Remove xfails.
	* gcc.dg/strlenopt-70.c: New test.
	* gcc.dg/strlenopt-71.c: New test.
	* gcc.dg/strlenopt-72.c: New test.
	* gcc.dg/strlenopt-8.c: Remove xfails.

From-SVN: r273783
parent 7214f11d
......@@ -16,6 +16,19 @@
2019-07-24 Martin Sebor <msebor@redhat.com>
PR tree-optimization/91183
PR tree-optimization/86688
* builtins.c (compute_objsize): Handle MEM_REF.
* tree-ssa-strlen.c (class ssa_name_limit_t): New.
(get_min_string_length): Remove.
(count_nonzero_bytes): New function.
(handle_char_store): Rename...
(handle_store): to this. Handle multibyte stores via integer types.
(strlen_check_and_optimize_stmt): Adjust conditional and the called
function name.
2019-07-24 Martin Sebor <msebor@redhat.com>
PR driver/80545
* diagnostic.c (diagnostic_classify_diagnostic): Use lang_mask.
(diagnostic_report_diagnostic): Same.
......
......@@ -3652,6 +3652,20 @@ compute_objsize (tree dest, int ostype)
if (!ostype)
return NULL_TREE;
if (TREE_CODE (dest) == MEM_REF)
{
tree ref = TREE_OPERAND (dest, 0);
tree off = TREE_OPERAND (dest, 1);
if (tree size = compute_objsize (ref, ostype))
{
if (tree_int_cst_lt (off, size))
return fold_build2 (MINUS_EXPR, size_type_node, size, off);
return integer_zero_node;
}
return NULL_TREE;
}
if (TREE_CODE (dest) != ADDR_EXPR)
return NULL_TREE;
......
2019-07-24 Martin Sebor <msebor@redhat.com>
PR tree-optimization/91183
PR tree-optimization/86688
* gcc/testsuite/c-c++-common/ubsan/object-size-9.c: Disable warnings.
* gcc.dg/Wstringop-overflow-14.c: New test.
* gcc.dg/attr-nonstring-2.c: Remove xfails.
* gcc.dg/strlenopt-70.c: New test.
* gcc.dg/strlenopt-71.c: New test.
* gcc.dg/strlenopt-72.c: New test.
* gcc.dg/strlenopt-8.c: Remove xfails.
2019-07-24 Martin Sebor <msebor@redhat.com>
PR driver/80545
* gcc.misc-tests/help.exp: Add tests.
* lib/options.exp: Handle C++.
......
/* { dg-do run } */
/* { dg-skip-if "" { *-*-* } { "*" } { "-O2" } } */
/* { dg-options "-fsanitize=undefined" } */
/* { dg-options "-Wno-stringop-overflow -fsanitize=undefined" } */
/* Test PARM_DECLs and RESULT_DECLs. */
......
/* Test to verify that past-the-end multibyte writes via lvalues of wider
types than char are diagnosed.
{ dg-do compile }
{ dg-require-effective-target int32plus }
{ dg-options "-O2 -Wall" } */
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
typedef __INT64_TYPE__ int64_t;
typedef __SIZE_TYPE__ size_t;
void* memcpy (void*, const void*, size_t);
char* strcpy (char*, const char*);
char a4[4], a8[8], a16[16];
const char s4[] = "1234";
const char t4[] = "4321";
void test_memcpy_cond (int i)
{
char *p = a4 + 1;
const char *q = i ? s4 : t4;
memcpy (p, q, 4); // { dg-warning "writing 4 bytes into a region of size 3" }
}
void test_int16 (void)
{
char *p = a4 + 1;
*(int16_t*)p = 0;
*(int16_t*)(p + 2) = 0; // { dg-warning "writing 2 bytes into a region of size 1" }
}
void test_int32 (void)
{
char *p = a8 + 3;
*(int32_t*)p = 0;
*(int32_t*)(p + 2) = 0; // { dg-warning "writing 4 bytes into a region of size 3" }
}
void test_int64 (void)
{
char *p = a16 + 5;
*(int64_t*)p = 0;
*(int64_t*)(p + 5) = 0; // { dg-warning "writing 8 bytes into a region of size 6" }
}
......@@ -73,8 +73,8 @@ void test_strnlen_string_cst (void)
T (3, "12", 3, 1);
T (3, "12", 3, 9);
T (3, "123", 3, 1);
T (3, "123", 3, 4); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" "bug 86688" { xfail *-*-* } } */
T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" "bug 86688" { xfail *-*-* } } */
T (3, "123", 3, 4); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */
T (3, "123", 3, 9); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 9" } */
T (5, "1", 2, 1);
T (5, "1", 2, 2);
......@@ -110,6 +110,6 @@ void test_strnlen_string_range (void)
{
T (3, "1", 2, UR (0, 1));
T (3, "1", 2, UR (3, 9));
T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" "bug 86688" { xfail *-*-* } } */
T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" "bug 86688" { xfail *-*-* } } */
T (3, "123", 3, UR (4, 5)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[4, 5]" } */
T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \\\[5, 9]" } */
}
/* PR tree-optimization/91183 - strlen of a strcpy result with a conditional
source not folded
Runtime test to verify that multibyte stores are handled correctly.
{ dg-do run }
{ dg-options "-O2 -Wall" } */
#include "strlenopt.h"
#define CHAR_BIT __CHAR_BIT__
typedef __INT16_TYPE__ int16_t;
typedef __INT32_TYPE__ int32_t;
#define NOIPA __attribute__ ((noclone, noinline, noipa))
/* Prevent the optimizer from detemining invariants from prior tests. */
NOIPA void terminate (void)
{
__builtin_abort ();
}
#define VERIFY(expr, str) \
do { \
const unsigned expect = strlen (str); \
const unsigned len = strlen (expr); \
if (len != expect) \
{ \
__builtin_printf ("line %i: strlen(%s) == %u failed: " \
"got %u with a = \"%.*s\"\n", \
__LINE__, #expr, expect, len, \
(int)sizeof a, a); \
terminate (); \
} \
if (memcmp (a, str, expect + 1)) \
{ \
__builtin_printf ("line %i: expected string \"%s\", " \
"got a = \"%.*s\"\n", \
__LINE__, str, (int)sizeof a, a); \
terminate (); \
} \
} while (0)
#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
# define I16(s) ((s[0] << 8) + s[1])
# define I32(s) ((s[0] << 24) + (s[1] << 16) + (s[2] << 8) + s[3])
#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
# define I16(s) ((s[1] << 8) + s[0])
# define I32(s) ((s[3] << 24) + (s[2] << 16) + (s[1] << 8) + s[0])
#endif
char a[32];
NOIPA void
i16_1 (void)
{
*(int16_t*)a = I16 ("12");
*(int16_t*)(a + 2) = I16 ("3");
VERIFY (a, "123");
*(int16_t*)(a + 1) = I16 ("23");
VERIFY (a, "123");
*(int16_t*)(a) = I16 ("12");
VERIFY (a, "123");
*(int16_t*)(a + 1) = I16 ("2");
VERIFY (a, "12");
*(int16_t*)(a + 3) = I16 ("45");
*(int16_t*)(a + 2) = I16 ("34");
VERIFY (a, "12345");
}
NOIPA void
i16_2 (void)
{
strcpy (a, "12");
strcat (a, "34");
*(int16_t*)a = I16 ("12");
VERIFY (a, "1234");
*(int16_t*)(a + 1) = I16 ("12");
VERIFY (a, "1124");
*(int16_t*)(a + 2) = I16 ("12");
VERIFY (a, "1112");
*(int16_t*)(a + 3) = I16 ("12");
VERIFY (a, "11112");
*(int16_t*)(a + 4) = I16 ("12");
VERIFY (a, "111112");
}
NOIPA void
i32_1 (void)
{
*(int32_t*)a = I32 ("1234");
VERIFY (a, "1234");
*(int32_t*)(a + 1) = I32 ("2345");
VERIFY (a, "12345");
}
NOIPA void
i32_2 (void)
{
strcpy (a, "12");
strcat (a, "34");
*(int32_t*)a = I32 ("1234");
VERIFY (a, "1234");
*(int32_t*)(a + 4) = I32 ("567");
VERIFY (a, "1234567");
*(int32_t*)(a + 7) = I32 ("89\0");
VERIFY (a, "123456789");
*(int32_t*)(a + 3) = I32 ("4567");
VERIFY (a, "123456789");
*(int32_t*)(a + 2) = I32 ("3456");
VERIFY (a, "123456789");
*(int32_t*)(a + 1) = I32 ("2345");
VERIFY (a, "123456789");
}
NOIPA void
i32_3 (void)
{
strcpy (a, "1234");
strcat (a, "5678");
*(int32_t*)a = I32 ("1234");
VERIFY (a, "12345678");
*(int32_t*)(a + 1) = I32 ("234");
VERIFY (a, "1234");
*(int32_t*)(a + 2) = I32 ("3456");
VERIFY (a, "12345678");
*(int32_t*)(a + 3) = I32 ("4567");
VERIFY (a, "12345678");
*(int32_t*)(a + 4) = I32 ("5678");
VERIFY (a, "12345678");
*(int32_t*)(a + 5) = I32 ("6789");
VERIFY (a, "123456789");
*(int32_t*)(a + 6) = I32 ("789A");
VERIFY (a, "123456789A");
}
volatile int vzero = 0;
NOIPA void
i32_4 (void)
{
strcpy (a, "1234");
strcat (a, "5678");
*(int32_t*)a = vzero ? I32 ("1\0\0\0") : I32 ("1234");
VERIFY (a, "12345678");
*(int32_t*)a = vzero ? I32 ("12\0\0") : I32 ("1234");
VERIFY (a, "12345678");
*(int32_t*)a = vzero ? I32 ("123\0") : I32 ("1234");
VERIFY (a, "12345678");
*(int32_t*)a = vzero ? I32 ("1234") : I32 ("1234");
VERIFY (a, "12345678");
*(int32_t*)a = vzero ? I32 ("1235") : I32 ("1234");
VERIFY (a, "12345678");
*(int32_t*)a = vzero ? I32 ("1234") : I32 ("123\0");
VERIFY (a, "123");
*(int32_t*)(a + 3) = vzero ? I32 ("456\0") : I32 ("4567");
VERIFY (a, "12345678");
}
int main ()
{
memset (a, 0, sizeof a);
i16_1 ();
memset (a, 0, sizeof a);
i16_2 ();
memset (a, 0, sizeof a);
i32_1 ();
memset (a, 0, sizeof a);
i32_2 ();
memset (a, 0, sizeof a);
i32_3 ();
memset (a, 0, sizeof a);
i32_4 ();
}
/* PR tree-optimization/91183 - strlen of a strcpy result with a conditional
source not folded
Test to verify that strlen can determine string lengths from wider stores
than narrow characters. This matters because on targets that can handle
unaligned stores and where GCC lowers multi-character stores into smaller
numbers of wider stores.
{ dg-do compile }
{ dg-options "-O2 -fdump-tree-optimized" } */
#include "strlenopt.h"
#define CAT(x, y) x ## y
#define CONCAT(x, y) CAT (x, y)
#define FAILNAME(name) CONCAT (call_ ## name ##_on_line_, __LINE__)
#define FAIL(name) do { \
extern void FAILNAME (name) (void); \
FAILNAME (name)(); \
} while (0)
/* Macros to emit a call to function named
call_failed_to_be_eliminated_on_line_NNN()
for each call that's expected to be eliminated. The dg-final
scan-tree-dump-time directive at the bottom of the test verifies
that no such call appears in output. */
#define ELIM(expr) \
if ((expr)) FAIL (not_eliminated); else (void)0
#undef T
#define T(N, ncpy, expect, assign) do { \
char a[N], b[N]; \
assign; \
memcpy (b, a, ncpy); \
ELIM (!(expect == strlen (b))); \
} while (0)
void test_copy (void)
{
T (2, 1, 0, (a[0] = 0));
T (2, 2, 0, (a[0] = 0, a[1] = 0));
T (2, 2, 1, (a[0] = '1', a[1] = 0));
T (4, 3, 2, (a[0] = '1', a[1] = '2', a[2] = 0));
// Not handled due to pr83821:
// T (4, 3, 1, (a[0] = '1', a[1] = 0, a[2] = '2'));
T (4, 2, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
// Not handled due to pr83821:
// T (4, 3, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
T (4, 4, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
T (4, 3, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0));
T (4, 4, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0));
T (4, 4, 3, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = 0));
T (5, 4, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0));
T (5, 4, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0));
T (5, 4, 3, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = 0));
// Not handled:
// T (5, 5, 1, (a[0] = '1', a[1] = 0, a[2] = 0, a[3] = 0, a[4] = 0));
// T (5, 5, 2, (a[0] = '1', a[1] = '2', a[2] = 0, a[3] = 0, a[4] = 0));
// T (5, 5, 3, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = 0, a[4] = 0));
T (5, 5, 4, (a[0] = '1', a[1] = '2', a[2] = '3', a[3] = '4', a[4] = 0));
}
/* { dg-final { scan-tree-dump-times "strlen" 0 "optimized" } }
{ dg-final { scan-tree-dump-times "_not_eliminated_" 0 "optimized" } } */
......@@ -43,13 +43,7 @@ main ()
return 0;
}
/* On non-strict-align targets we inline the memcpy that strcat is turned
into and end up with a short typed load / store which strlenopt is not
able to analyze. */
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" { xfail non_strict_align } } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 2 "strlen" { target { non_strict_align } } } } */
/* { dg-final { scan-tree-dump-times "memcpy \\(" 4 "strlen" { target { ! non_strict_align } } } } */
/* { dg-final { scan-tree-dump-times "strlen \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcpy \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strcat \\(" 0 "strlen" } } */
/* { dg-final { scan-tree-dump-times "strchr \\(" 0 "strlen" } } */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment