PR middle-end/93519 - bogus -Wrestrict for strcpy(d, s) call guarded by d != s gcc/testsuite/ChangeLog: PR middle-end/93519 * gcc.dg/Wrestrict-14.c: Remove an xfail. * gcc.dg/Wrestrict-21.c: New test. * gcc.dg/Wrestrict-22.c: New test. * gcc.dg/strlenopt-92.c: New test. * gcc.dg/strlenopt.h: Declare more functions. gcc/ChangeLog: PR middle-end/93519 * builtins.c (expand_builtin_mempcpy): Diagnose perfectly overlapping copies and return destination. (expand_builtin_strcpy): Same. Defer declaring locals until their initial value is known for better readability. (expand_builtin_strncpy): Same. * gimple-fold.c (gimple_fold_builtin_memory_op): Avoid folding perfectly overlapping calls to mempcpy. (gimple_fold_builtin_strcpy): Avoid folding perfectly overlapping calls.. * gimple-ssa-warn-restrict.c (maybe_diag_equal_operands): New function. (check_bounds_or_overlap): Call it. * gimple-ssa-warn-restrict.h (maybe_diag_equal_operands): Declare. * tree-ssa-strlen.c (maybe_handle_store_to_self): New function. (handle_builtin_strcpy): Call it. (handle_builtin_memcpy): Same. diff --git a/gcc/builtins.c b/gcc/builtins.c index e4a8694054e..46cb3f8ae10 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -4253,6 +4253,22 @@ expand_builtin_mempcpy (tree exp, rtx target) if (!check_memop_access (exp, dest, src, len)) return NULL_RTX; + if (operand_equal_p (dest, src, 0)) + { + if (!TREE_NO_WARNING (exp)) + { + location_t loc = EXPR_LOCATION (exp); + tree func = get_callee_fndecl (exp); + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + } + + /* Replace perfectly overlapping calls with DST + LEN. */ + tree res = fold_build2 (POINTER_PLUS_EXPR, TREE_TYPE (dest), dest, len); + return expand_normal (res); + } + return expand_builtin_mempcpy_args (dest, src, len, target, exp, /*retmode=*/ RETURN_END); } @@ -4474,6 +4490,21 @@ expand_builtin_strcpy (tree exp, rtx target) src, destsize); } + if (operand_equal_p (dest, src, 0)) + { + if (!TREE_NO_WARNING (exp)) + { + location_t loc = EXPR_LOCATION (exp); + tree func = get_callee_fndecl (exp); + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + } + + /* Replace perfectly overlapping calls with the destination. */ + return expand_normal (dest); + } + if (rtx ret = expand_builtin_strcpy_args (exp, dest, src, target)) { /* Check to see if the argument was declared attribute nonstring @@ -4810,6 +4841,22 @@ expand_builtin_strncpy (tree exp, rtx target) return NULL_RTX; tree dest = CALL_EXPR_ARG (exp, 0); tree src = CALL_EXPR_ARG (exp, 1); + + if (operand_equal_p (dest, src, 0)) + { + if (!TREE_NO_WARNING (exp)) + { + location_t loc = EXPR_LOCATION (exp); + tree func = get_callee_fndecl (exp); + warning_at (loc, OPT_Wrestrict, + "%qD source argument is the same as destination", + func); + } + + /* Replace perfectly overlapping calls with the destination. */ + return expand_normal (dest); + } + /* The number of bytes to write (not the maximum). */ tree len = CALL_EXPR_ARG (exp, 2); diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index ed225922269..50b1d627760 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -725,6 +725,12 @@ gimple_fold_builtin_memory_op (gimple_stmt_iterator *gsi, DEST{,+LEN,+LEN-1}. */ if (operand_equal_p (src, dest, 0)) { + /* Fail for mempcpy (but not memcpy, and certainly not memmove) + if SRC and DEST are the same. Handle the overlap later. */ + if ((code == BUILT_IN_MEMPCPY || code == BUILT_IN_MEMPCPY_CHK) + && operand_equal_p (src, dest, 0)) + return false; + /* Avoid diagnosing exact overlap in calls to __builtin_memcpy. It's safe and may even be emitted by GCC itself (see bug 32667). */ @@ -1747,37 +1753,17 @@ static bool gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi, tree dest, tree src) { - gimple *stmt = gsi_stmt (*gsi); - location_t loc = gimple_location (stmt); - tree fn; - - /* If SRC and DEST are the same (and not volatile), return DEST. */ + /* Fail if SRC and DEST are the same. Handle the overlap later. */ if (operand_equal_p (src, dest, 0)) - { - /* Issue -Wrestrict unless the pointers are null (those do - not point to objects and so do not indicate an overlap; - such calls could be the result of sanitization and jump - threading). */ - if (!integer_zerop (dest) && !gimple_no_warning_p (stmt)) - { - tree func = gimple_call_fndecl (stmt); - - warning_at (loc, OPT_Wrestrict, - "%qD source argument is the same as destination", - func); - } - - replace_call_with_value (gsi, dest); - return true; - } - - if (optimize_function_for_size_p (cfun)) return false; - fn = builtin_decl_implicit (BUILT_IN_MEMCPY); + tree fn = builtin_decl_implicit (BUILT_IN_MEMCPY); if (!fn) return false; + gimple *stmt = gsi_stmt (*gsi); + location_t loc = gimple_location (stmt); + /* Set to non-null if ARG refers to an unterminated array. */ tree nonstr = NULL; tree len = get_maxval_strlen (src, SRK_STRLEN, &nonstr); @@ -1794,6 +1780,9 @@ gimple_fold_builtin_strcpy (gimple_stmt_iterator *gsi, if (!len) return false; + if (optimize_function_for_size_p (cfun)) + return false; + len = fold_convert_loc (loc, size_type_node, len); len = size_binop_loc (loc, PLUS_EXPR, len, build_int_cst (size_type_node, 1)); len = force_gimple_operand_gsi (gsi, len, true, diff --git a/gcc/gimple-ssa-warn-restrict.c b/gcc/gimple-ssa-warn-restrict.c index 2c582a670eb..46e9cc8e75e 100644 --- a/gcc/gimple-ssa-warn-restrict.c +++ b/gcc/gimple-ssa-warn-restrict.c @@ -2006,6 +2006,56 @@ wrestrict_dom_walker::check_call (gimple *call) } /* anonymous namespace */ + +/* If DST and SRC point to the same location issues -Wrestrict. */ + +bool +maybe_diag_equal_operands (gimple *stmt, tree dst, tree src) +{ + if (integer_zerop (dst) || gimple_no_warning_p (stmt)) + return false; + + location_t loc = gimple_location (stmt); + tree func = gimple_call_fndecl (stmt); + if (!warning_at (loc, OPT_Wrestrict, + "%G%qD source argument is the same as destination", + stmt, func)) + return false; + + gimple_set_no_warning (stmt, true); + + /* The operands may be entirely different in the source code but + end up being equal as a result of inlining. To make the warning + easier to understand reference the underlying decl they point to + in a note. */ + tree base = src; + if (TREE_CODE (base) == ADDR_EXPR) + base = TREE_OPERAND (base, 0); + + poly_int64 poff; + base = get_addr_base_and_unit_offset (base, &poff); + + /* Use the underlying SSA_NAME variable to point to the function + argument the operands were derived from. */ + if (TREE_CODE (base) == SSA_NAME) + { + base = SSA_NAME_VAR (base); + if (!base) + return true; + } + if (TREE_CODE (base) == PARM_DECL || DECL_P (base)) + loc = DECL_SOURCE_LOCATION (base); + else if (EXPR_HAS_LOCATION (base)) + loc = EXPR_LOCATION (base); + else + return true; + + if (loc != UNKNOWN_LOCATION) + inform (loc, "accessing %qE declared here", base); + + return true; +} + /* Attempt to detect and diagnose invalid offset bounds and (except for memmove) overlapping copy in a call expression EXPR from SRC to DST and DSTSIZE and SRCSIZE bytes, respectively. Both DSTSIZE and @@ -2072,14 +2122,8 @@ check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize, not point to objects and so do not indicate an overlap; such calls could be the result of sanitization and jump threading). */ - if (!integer_zerop (dst) && !gimple_no_warning_p (call)) - { - warning_at (loc, OPT_Wrestrict, - "%G%qD source argument is the same as destination", - call, func); - gimple_set_no_warning (call, true); - return OPT_Wrestrict; - } + if (maybe_diag_equal_operands (call, dst, src)) + return OPT_Wrestrict; return 0; } diff --git a/gcc/gimple-ssa-warn-restrict.h b/gcc/gimple-ssa-warn-restrict.h index 7bae95a9ad1..91d9da9f126 100644 --- a/gcc/gimple-ssa-warn-restrict.h +++ b/gcc/gimple-ssa-warn-restrict.h @@ -23,4 +23,6 @@ extern int check_bounds_or_overlap (gimple *, tree, tree, tree, tree, bool = false, bool = true); +extern bool maybe_diag_equal_operands (gimple *, tree, tree); + #endif /* GIMPLE_SSA_WARN_RESTRICT_H */ diff --git a/gcc/testsuite/gcc.dg/Wrestrict-14.c b/gcc/testsuite/gcc.dg/Wrestrict-14.c index b919fa644d3..58c502a8731 100644 --- a/gcc/testsuite/gcc.dg/Wrestrict-14.c +++ b/gcc/testsuite/gcc.dg/Wrestrict-14.c @@ -69,7 +69,7 @@ void test_mempcpy (char *p, struct S *q, size_t n) mempcpy (q->p, q->p, 1); /* { dg-warning "\\\[-Wrestrict]" } */ sink (q); - mempcpy (&q->a[0], q->a, n); /* { dg-warning "\\\[-Wrestrict]" "bug ????" { xfail *-*-* } } */ + mempcpy (&q->a[0], q->a, n); /* { dg-warning "\\\[-Wrestrict]" } */ sink (q); mempcpy (q, q->a, n); /* { dg-warning "\\\[-Wrestrict]" "bug ????" { xfail *-*-* } } */ diff --git a/gcc/testsuite/gcc.dg/Wrestrict-21.c b/gcc/testsuite/gcc.dg/Wrestrict-21.c new file mode 100644 index 00000000000..4af814ff430 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wrestrict-21.c @@ -0,0 +1,185 @@ +/* PR middle-end/93519 -bogus -Wrestrict for strcpy(d, s) call guarded by d != s + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +static inline void memcpy_nowarn (void *d, const void *s) +{ + if (s != d) + __builtin_memcpy (d, s, 32); +} + +static inline void memcpy_warn (void *d, const void *s) +{ + /* Not diagnosed because GCC itself emits such overlapping calls and, + for the most part, handles them gracefully. (Calls made by programs + should still be diagnosed but they're indistinguishable from GCC's + own.) */ + __builtin_memcpy (d, s, 32); // { dg-warning "\\\[-Wrestrict]" "" { xfail *-*-* } } +} + +void test_memcpy (void *s) // { dg-message "declared here" "" { xfail *-*-* } } +{ + memcpy_nowarn (s, s); + + memcpy_warn (s, s); + + extern char memcpy_buf[]; // { dg-message "declared here" "" { xfail *-*-* } } + void *d = memcpy_buf; + s = memcpy_buf; + + memcpy_warn (d, s); +} + + +static inline void* mempcpy_nowarn (void *d, const void *s) +{ + if (s != d) + return __builtin_mempcpy (d, s, 32); + return 0; +} + +static inline void* mempcpy_warn (void *d, const void *s) +{ + return __builtin_mempcpy (d, s, 32); // { dg-warning "\\\[-Wrestrict]" } +} + +void test_mempcpy (void *s) // { dg-message "declared here" } +{ + mempcpy_nowarn (s, s); + + mempcpy_warn (s, s); + + extern char mempcpy_buf[]; // { dg-message "declared here" } + void *d = mempcpy_buf; + s = mempcpy_buf; + + mempcpy_warn (d, s); +} + + +static inline void strcat_nowarn (char *d, const char *s) +{ + if (s != d) + __builtin_strcat (d, s); +} + +static inline void strcat_warn (char *d, const char *s) +{ + __builtin_strcat (d, s); // { dg-warning "\\\[-Wrestrict]" } +} + +void test_strcat (char *s) // { dg-message "declared here" } +{ + strcat_nowarn (s, s); + + strcat_warn (s, s); + + extern char strcat_buf[]; // { dg-message "declared here" } + char *d = strcat_buf; + s = strcat_buf; + + strcat_warn (d, s); +} + + +static inline char* stpcpy_nowarn (char *d, const char *s) +{ + if (s != d) + return __builtin_stpcpy (d, s); + return 0; +} + +static inline char* stpcpy_warn (char *d, const char *s) +{ + return __builtin_stpcpy (d, s); // { dg-warning "\\\[-Wrestrict]" } +} + +void test_stpcpy (char *s) // { dg-message "declared here" } +{ + stpcpy_nowarn (s, s); + + stpcpy_warn (s, s); + + extern char stpcpy_buf[]; // { dg-message "declared here" } + char *d = stpcpy_buf; + s = stpcpy_buf; + + stpcpy_warn (d, s); +} + + +static inline char* stpncpy_nowarn (char *d, const char *s) +{ + if (s != d) + return __builtin_stpncpy (d, s, 32); + return 0; +} + +static inline char* stpncpy_warn (char *d, const char *s) +{ + return __builtin_stpncpy (d, s, 32); // { dg-warning "\\\[-Wrestrict]" } +} + +void test_stpncpy (char *s) // { dg-message "declared here" } +{ + stpncpy_nowarn (s, s); + + stpncpy_warn (s, s); + + extern char stpncpy_buf[]; // { dg-message "declared here" } + char *d = stpncpy_buf; + s = stpncpy_buf; + + stpncpy_warn (d, s); +} + + +static inline void strcpy_nowarn (char *d, const char *s) +{ + if (s != d) + __builtin_strcpy (d, s); // { dg-bogus "\\\[-Wrestrict]" } +} + +static inline void strcpy_warn (char *d, const char *s) +{ + __builtin_strcpy (d, s); // { dg-warning "\\\[-Wrestrict]" } +} + +void test_strcpy (char *s) // { dg-message "declared here" } +{ + strcpy_nowarn (s, s); + + strcpy_warn (s, s); + + extern char strcpy_buf[]; // { dg-message "declared here" } + char *d = strcpy_buf; + s = strcpy_buf; + + strcpy_warn (d, s); +} + + +static inline void strncpy_nowarn (char *d, const char *s) +{ + if (s != d) + __builtin_strncpy (d, s, 32); +} + +static inline void strncpy_warn (char *d, const char *s) +{ + __builtin_strncpy (d, s, 32); // { dg-warning "\\\[-Wrestrict]" } +} + +void test_strncpy (char *s) // { dg-message "declared here" } +{ + strncpy_nowarn (s, s); + + strncpy_warn (s, s); + + extern char strncpy_buf[]; // { dg-message "declared here" } + char *d = strncpy_buf; + s = strncpy_buf; + + strncpy_nowarn (d, s); + strncpy_warn (d, s); +} diff --git a/gcc/testsuite/gcc.dg/Wrestrict-22.c b/gcc/testsuite/gcc.dg/Wrestrict-22.c new file mode 100644 index 00000000000..cd2577a415b --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wrestrict-22.c @@ -0,0 +1,116 @@ +/* { dg-do compile } + { dg-options "-O2 -Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +extern void* memcpy (void*, const void*, size_t); +extern void* mempcpy (void*, const void*, size_t); + +struct A { char a[3]; }; +struct B { struct A a; char b[2]; }; +struct C { struct B b; char c[2]; }; + + +void* memcpy_member_to_A (struct A *p) +{ + char *q = p->a; + memcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + +void* memcpy_member_to_B (struct B *p) +{ + void *q = &p->a; + memcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + +void memcpy_member_to_C (struct C *p) +{ + void *q = &p->b; + memcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} + + +void* memcpy_member_plus_to_A (struct A *p) +{ + char *q = p->a + 1; + memcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + +void* memcpy_member_plus_to_B (struct B *p) +{ + char *q = (char*)&p->a + 1; + memcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + +void* memcpy_member_plus_to_C (struct C *p) +{ + char *q = (char*)&p->b + 1; + memcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + + +void* memcpy_member_to_A_plus (struct A *p) +{ + char *q = (char*)p + 1; + memcpy (q, p->a, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + + +void* memcpy_member_to_B_plus (struct B *p) +{ + char *q = (char*)p + 1; + memcpy (q, &p->a, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + +void* memcpy_member_to_C_plus (struct C *p) +{ + char *q = (char*)p + 1; + memcpy (p, &p->b, sizeof *p); // { dg-warning "\\\[-Wrestrict" } + return q; +} + + + +void* mempcpy_member_to_A (struct A *p) +{ + char *q = p->a; + return mempcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} + +void* mempcpy_member_to_B (struct B *p) +{ + void *q = &p->a; + return mempcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} + +void* mempcpy_member_to_C (struct C *p) +{ + void *q = &p->b; + return mempcpy (p, q, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} + + +void* mempcpy_member_to_A_plus (struct A *p) +{ + char *q = (char*)p + 1; + return mempcpy (q, p->a, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} + +void* mempcpy_member_to_B_plus (struct B *p) +{ + char *q = (char*)p + 1; + return mempcpy (q, &p->a, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} + +void* mempcpy_member_to_C_plus (struct C *p) +{ + char *q = (char*)p + 1; + return mempcpy (q, &p->b, sizeof *p); // { dg-warning "\\\[-Wrestrict" } +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-92.c b/gcc/testsuite/gcc.dg/strlenopt-92.c new file mode 100644 index 00000000000..97082388ce1 --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-92.c @@ -0,0 +1,131 @@ +/* Verify that perfectly overlapping calls to raw memory and string + functions are eliminated (they are undefined but there's no point + in making the library calls). + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-optimized" } */ + +#define USE_GNU +#include "strlenopt.h" + +void use (char*); + +extern char acpy[3]; + +void test_mem_cpy_self_use (void) +{ + char *d = acpy, *s = acpy; + use (memcpy (d, s, sizeof acpy)); +} + +/* { dg-final { scan-tree-dump-times "use \\\(\\\&acpy" 1 "optimized" } } */ + +char* test_mem_cpy_self_ret (unsigned n) +{ + char *d = acpy, *s = acpy; + return memcpy (d, s, n); +} + +/* { dg-final { scan-tree-dump-times "return \\\&acpy;" 1 "optimized" } } + { dg-final { scan-tree-dump-not "memcpy \\\(" "optimized" } } */ + + +extern char apcpy[3]; + +void test_mem_pcpy_self_use (void) +{ + char *d = apcpy, *s = apcpy; + use (mempcpy (d, s, sizeof apcpy)); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "use \\\(\\\&apcpy" 1 "optimized" { xfail *-*-* } } } */ + +char* test_mem_pcpy_self_ret (unsigned n) +{ + char *d = apcpy, *s = apcpy; + return mempcpy (d, s, n); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "return \\\&apcpy;" 1 "optimized" { xfail *-*-* } } } + { dg-final { scan-tree-dump-not "mempcpy \\\(" "optimized" { xfail *-*-* } } } + { dg-final { scan-assembler-not "mempcpy" } } */ + + +extern char ascpy[3]; + +void test_str_cpy_use (void) +{ + char *d = ascpy, *s = ascpy; + use (strcpy (d, s)); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "use \\\(\\\&ascpy" 1 "optimized" } } */ + +char* test_str_cpy_ret (void) +{ + char *d = ascpy, *s = ascpy; + return strcpy (d, s); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "return \\\&ascpy;" 1 "optimized" } } + { dg-final { scan-tree-dump-not "strcpy \\\(" "optimized" } } */ + + +extern char asncpy[3]; + +void test_str_ncpy_use (void) +{ + char *d = asncpy, *s = asncpy; + use (strncpy (d, s, sizeof asncpy)); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "use \\\(\\\&asncpy" 1 "optimized" { xfail *-*-* } } } */ + +char* test_str_ncpy_ret (unsigned n) +{ + char *d = asncpy, *s = asncpy; + return strncpy (d, s, n); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "return \\\&asncpy;" 1 "optimized" { xfail *-*-* } } } + { dg-final { scan-tree-dump-not "strncpy \\\(" "optimized" { xfail *-*-* } } } + { dg-final { scan-assembler-not "strncpy" } } */ + + +extern char aspcpy[3]; + +void test_stp_cpy_use (void) +{ + char *d = aspcpy, *s = aspcpy; + use (stpcpy (d, s)); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "use \\\(\\\&aspcpy" 1 "optimized" { xfail *-*-*} } } */ + +char* test_stp_cpy_ret (void) +{ + char *d = aspcpy, *s = aspcpy; + return stpcpy (d, s); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "return \\\&aspcpy;" 1 "optimized" { xfail *-*-* } } } + { dg-final { scan-tree-dump-not "stpcpy \\\(" "optimized" { xfail *-*-* } } } */ + + +extern char aspncpy[3]; + +void test_stp_ncpy_use (void) +{ + char *d = aspcpy, *s = aspcpy; + use (stpncpy (d, s, 3)); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "use \\\(\\\&aspncpy" 1 "optimized" { xfail *-*-*} } } */ + +char* test_stp_ncpy_ret (unsigned n) +{ + char *d = aspcpy, *s = aspcpy; + return stpncpy (d, s, n); // { dg-warning "\\\[-Wrestrict" } +} + +/* { dg-final { scan-tree-dump-times "return \\\&aspncpy;" 1 "optimized" { xfail *-*-* } } } + { dg-final { scan-tree-dump-not "stpncpy \\\(" "optimized" { xfail *-*-* } } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt.h b/gcc/testsuite/gcc.dg/strlenopt.h index 518d0cf08b2..ca13dc7a31a 100644 --- a/gcc/testsuite/gcc.dg/strlenopt.h +++ b/gcc/testsuite/gcc.dg/strlenopt.h @@ -13,6 +13,7 @@ size_t strnlen (const char *, size_t); void *memcpy (void *__restrict, const void *__restrict, size_t); void *memmove (void *, const void *, size_t); char *strcpy (char *__restrict, const char *__restrict); +char *strncpy (char *__restrict, const char *__restrict, size_t); char *strcat (char *__restrict, const char *__restrict); char *strchr (const char *, int); int strcmp (const char *, const char *); @@ -23,6 +24,7 @@ int strcmp (const char *, const char *); #ifdef USE_GNU void *mempcpy (void *__restrict, const void *__restrict, size_t); char *stpcpy (char *__restrict, const char *__restrict); +char *stpncpy (char *__restrict, const char *__restrict, size_t); #endif int sprintf (char * __restrict, const char *__restrict, ...); diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index ad9e98973b1..7a94c9a2d90 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -2312,6 +2312,41 @@ maybe_warn_overflow (gimple *stmt, unsigned HOST_WIDE_INT len, si, plus_one, rawmem); } +/* If DST is the same as SRC, diagnoses the overlapping call at GSI. + If SUB is not ERROR_MARK_NODE, replaces the call if its result is + used with SUB if nonnull or with DST otherwise. + Returns true if the call has been eliminated. */ + +static bool +maybe_handle_store_to_self (gimple_stmt_iterator *gsi, tree dst, tree src, + tree sub = NULL_TREE) +{ + if (!operand_equal_p (dst, src, 0)) + return false; + + gimple *stmt = gsi_stmt (*gsi); + + maybe_diag_equal_operands (stmt, dst, src); + + if (gimple_call_lhs (stmt)) + { + if (sub == error_mark_node) + return false; + + /* Replace the call with SUB if non-null or with DST otherwise. */ + replace_call_with_value (gsi, sub ? sub : dst); + } + else + { + /* Eliminate the call. */ + unlink_stmt_vdef (stmt); + gsi_remove (gsi, true); + release_defs (stmt); + } + + return true; +} + /* Handle a strlen call. If strlen of the argument is known, replace the strlen call with the known value, otherwise remember that strlen of the argument is stored in the lhs SSA_NAME. */ @@ -2610,24 +2645,25 @@ static void handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, const vr_values *rvals) { - int idx, didx; - tree src, dst, srclen, len, lhs, type, fn, oldlen; - bool success; gimple *stmt = gsi_stmt (*gsi); - strinfo *si, *dsi, *olddsi, *zsi; - location_t loc; + tree src = gimple_call_arg (stmt, 1); + tree dst = gimple_call_arg (stmt, 0); - src = gimple_call_arg (stmt, 1); - dst = gimple_call_arg (stmt, 0); - lhs = gimple_call_lhs (stmt); - idx = get_stridx (src); - si = NULL; - if (idx > 0) - si = get_strinfo (idx); + /* Diagnose exactly overlapping strcpy and either replace it with DST + or eliminate it. Only diagnose overlapping stpcpy for now without + replacing its LHS (but eliminate it if the result is unused). + FIXME: Handle overlapping stpcpy result by replacing it with + DST + strlen (DST). */ + tree sub = (bcode == BUILT_IN_STRCPY || bcode == BUILT_IN_STRCPY_CHK + ? NULL_TREE : error_mark_node); + if (maybe_handle_store_to_self (gsi, src, dst, sub)) + return; - didx = get_stridx (dst); - olddsi = NULL; - oldlen = NULL_TREE; + int idx = get_stridx (src); + strinfo *si = idx > 0 ? get_strinfo (idx) : NULL; + int didx = get_stridx (dst); + strinfo *olddsi = NULL; + tree oldlen = NULL_TREE; if (didx > 0) olddsi = get_strinfo (didx); else if (didx < 0) @@ -2636,7 +2672,7 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, if (olddsi != NULL) adjust_last_stmt (olddsi, stmt, false); - srclen = NULL_TREE; + tree srclen = NULL_TREE; if (si != NULL) srclen = get_string_length (si); else if (idx < 0) @@ -2647,7 +2683,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, if (olddsi != NULL) adjust_last_stmt (olddsi, stmt, false); - loc = gimple_location (stmt); + tree lhs = gimple_call_lhs (stmt); + location_t loc = gimple_location (stmt); if (srclen == NULL_TREE) switch (bcode) { @@ -2678,6 +2715,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, if (didx == 0) return; } + + strinfo *dsi; if (olddsi != NULL) { oldlen = olddsi->nonzero_chars; @@ -2769,8 +2808,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, if (si != NULL) si->dont_invalidate = true; - fn = NULL_TREE; - zsi = NULL; + tree fn = NULL_TREE; + strinfo *zsi = NULL; switch (bcode) { case BUILT_IN_STRCPY: @@ -2811,15 +2850,14 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, if (zsi != NULL) zsi->dont_invalidate = true; + tree type = size_type_node; if (fn) { tree args = TYPE_ARG_TYPES (TREE_TYPE (fn)); type = TREE_VALUE (TREE_CHAIN (TREE_CHAIN (args))); } - else - type = size_type_node; - len = fold_convert_loc (loc, type, unshare_expr (srclen)); + tree len = fold_convert_loc (loc, type, unshare_expr (srclen)); len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1)); /* Set the no-warning bit on the transformed statement? */ @@ -2843,6 +2881,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, fprintf (dump_file, "Optimizing: "); print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); } + + bool success; if (gimple_call_num_args (stmt) == 2) success = update_gimple_call (gsi, fn, 3, dst, src, len); else @@ -3421,6 +3461,17 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi, tree src = gimple_call_arg (stmt, 1); tree dst = gimple_call_arg (stmt, 0); + /* Diagnose exactly overlapping memcpy and either replace it with DST + or eliminate it. Only diagnose overlapping mempcpy for now without + replacing its LHS (but eliminate it if the result is unused). + FIXME: Handle overlapping mempcpy result by replacing it with + DST + LEN. + Also consider diagnosing overflow before overlapping copies. */ + tree sub = (bcode == BUILT_IN_MEMCPY || bcode == BUILT_IN_MEMCPY_CHK + ? NULL_TREE : error_mark_node); + if (maybe_handle_store_to_self (gsi, src, dst, sub)) + return; + int didx = get_stridx (dst); strinfo *olddsi = NULL; if (didx > 0)