Use ranges for lengths and object sizes in strncat and snprintf to determine if they can be transformed into simpler operations.
gcc/ChangeLog: * gimple-fold.c (gimple_fold_builtin_strncat): Use ranges to determine if it is safe to transform to strcat. (gimple_fold_builtin_snprintf): Likewise. gcc/testsuite/ChangeLog: * gcc.dg/fold-stringops-2.c: Define size_t. (safe1): Adjust. (safe4): New test. * gcc.dg/fold-stringops-3.c: New test. Signed-off-by: Siddhesh Poyarekar <siddh...@gotplt.org> --- gcc/gimple-fold.c | 102 ++++++++++++------------ gcc/testsuite/gcc.dg/fold-stringops-2.c | 16 +++- gcc/testsuite/gcc.dg/fold-stringops-3.c | 18 +++++ 3 files changed, 82 insertions(+), 54 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/fold-stringops-3.c diff --git a/gcc/gimple-fold.c b/gcc/gimple-fold.c index f3362287c0d..50b9ba8d558 100644 --- a/gcc/gimple-fold.c +++ b/gcc/gimple-fold.c @@ -2485,72 +2485,73 @@ gimple_fold_builtin_strncat (gimple_stmt_iterator *gsi) tree dst = gimple_call_arg (stmt, 0); tree src = gimple_call_arg (stmt, 1); tree len = gimple_call_arg (stmt, 2); - - const char *p = c_getstr (src); + tree src_len = c_strlen (src, 1); /* If the requested length is zero, or the src parameter string length is zero, return the dst parameter. */ - if (integer_zerop (len) || (p && *p == '\0')) + if (integer_zerop (len) || (src_len && integer_zerop (src_len))) { replace_call_with_value (gsi, dst); return true; } - if (TREE_CODE (len) != INTEGER_CST || !p) - return false; - - unsigned srclen = strlen (p); - - int cmpsrc = compare_tree_int (len, srclen); - /* Return early if the requested len is less than the string length. Warnings will be issued elsewhere later. */ - if (cmpsrc < 0) + if (!src_len || known_lower (stmt, len, src_len, true)) return false; unsigned HOST_WIDE_INT dstsize; + bool found_dstsize = compute_builtin_object_size (dst, 1, &dstsize); - bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_); - - if (!nowarn && compute_builtin_object_size (dst, 1, &dstsize)) + /* Warn on constant LEN. */ + if (TREE_CODE (len) == INTEGER_CST) { - int cmpdst = compare_tree_int (len, dstsize); + bool nowarn = warning_suppressed_p (stmt, OPT_Wstringop_overflow_); - if (cmpdst >= 0) + if (!nowarn && found_dstsize) { - tree fndecl = gimple_call_fndecl (stmt); + int cmpdst = compare_tree_int (len, dstsize); + + if (cmpdst >= 0) + { + tree fndecl = gimple_call_fndecl (stmt); + + /* Strncat copies (at most) LEN bytes and always appends + the terminating NUL so the specified bound should never + be equal to (or greater than) the size of the destination. + If it is, the copy could overflow. */ + location_t loc = gimple_location (stmt); + nowarn = warning_at (loc, OPT_Wstringop_overflow_, + cmpdst == 0 + ? G_("%qD specified bound %E equals " + "destination size") + : G_("%qD specified bound %E exceeds " + "destination size %wu"), + fndecl, len, dstsize); + if (nowarn) + suppress_warning (stmt, OPT_Wstringop_overflow_); + } + } - /* Strncat copies (at most) LEN bytes and always appends - the terminating NUL so the specified bound should never - be equal to (or greater than) the size of the destination. - If it is, the copy could overflow. */ + if (!nowarn && TREE_CODE (src_len) == INTEGER_CST + && tree_int_cst_compare (src_len, len) == 0) + { + tree fndecl = gimple_call_fndecl (stmt); location_t loc = gimple_location (stmt); - nowarn = warning_at (loc, OPT_Wstringop_overflow_, - cmpdst == 0 - ? G_("%qD specified bound %E equals " - "destination size") - : G_("%qD specified bound %E exceeds " - "destination size %wu"), - fndecl, len, dstsize); - if (nowarn) + + /* To avoid possible overflow the specified bound should also + not be equal to the length of the source, even when the size + of the destination is unknown (it's not an uncommon mistake + to specify as the bound to strncpy the length of the source). */ + if (warning_at (loc, OPT_Wstringop_overflow_, + "%qD specified bound %E equals source length", + fndecl, len)) suppress_warning (stmt, OPT_Wstringop_overflow_); } } - if (!nowarn && cmpsrc == 0) - { - tree fndecl = gimple_call_fndecl (stmt); - location_t loc = gimple_location (stmt); - - /* To avoid possible overflow the specified bound should also - not be equal to the length of the source, even when the size - of the destination is unknown (it's not an uncommon mistake - to specify as the bound to strncpy the length of the source). */ - if (warning_at (loc, OPT_Wstringop_overflow_, - "%qD specified bound %E equals source length", - fndecl, len)) - suppress_warning (stmt, OPT_Wstringop_overflow_); - } + if (!known_lower (stmt, src_len, len)) + return false; tree fn = builtin_decl_implicit (BUILT_IN_STRCAT); @@ -3623,10 +3624,6 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi) if (gimple_call_num_args (stmt) == 4) orig = gimple_call_arg (stmt, 3); - if (!tree_fits_uhwi_p (destsize)) - return false; - unsigned HOST_WIDE_INT destlen = tree_to_uhwi (destsize); - /* Check whether the format is a literal string constant. */ fmt_str = c_getstr (fmt); if (fmt_str == NULL) @@ -3646,6 +3643,8 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi) if (orig) return false; + tree len = build_int_cstu (TREE_TYPE (destsize), strlen (fmt_str)); + /* We could expand this as memcpy (str, fmt, cst - 1); str[cst - 1] = '\0'; or to @@ -3653,8 +3652,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi) but in the former case that might increase code size and in the latter case grow .rodata section too much. So punt for now. */ - size_t len = strlen (fmt_str); - if (len >= destlen) + if (!known_lower (stmt, len, destsize, true)) return false; gimple_seq stmts = NULL; @@ -3663,7 +3661,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi) if (tree lhs = gimple_call_lhs (stmt)) { repl = gimple_build_assign (lhs, - build_int_cst (TREE_TYPE (lhs), len)); + fold_convert (TREE_TYPE (lhs), len)); gimple_seq_add_stmt_without_update (&stmts, repl); gsi_replace_with_seq_vops (gsi, stmts); /* gsi now points at the assignment to the lhs, get a @@ -3694,8 +3692,6 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi) return false; tree orig_len = get_maxval_strlen (orig, SRK_STRLEN); - if (!orig_len || TREE_CODE (orig_len) != INTEGER_CST) - return false; /* We could expand this as memcpy (str1, str2, cst - 1); str1[cst - 1] = '\0'; @@ -3704,7 +3700,7 @@ gimple_fold_builtin_snprintf (gimple_stmt_iterator *gsi) but in the former case that might increase code size and in the latter case grow .rodata section too much. So punt for now. */ - if (compare_tree_int (orig_len, destlen) >= 0) + if (!known_lower (stmt, orig_len, destsize, true)) return false; /* Convert snprintf (str1, cst, "%s", str2) into diff --git a/gcc/testsuite/gcc.dg/fold-stringops-2.c b/gcc/testsuite/gcc.dg/fold-stringops-2.c index 0b415dfaf57..ac7d29eac50 100644 --- a/gcc/testsuite/gcc.dg/fold-stringops-2.c +++ b/gcc/testsuite/gcc.dg/fold-stringops-2.c @@ -1,10 +1,12 @@ /* { dg-do compile } */ /* { dg-options "-O2" } */ +typedef __SIZE_TYPE__ size_t; + #define bos(__d) __builtin_object_size ((__d), 0) char * -safe1 (const char *src, int cond, __SIZE_TYPE__ len) +safe1 (const char *src, int cond, size_t len) { char *dst; @@ -44,6 +46,18 @@ safe3 (const char *src, int cond, unsigned char len) return __builtin___snprintf_chk (dst, len, 0, bos (dst), "%s", src); } +char dst[1024]; + +void +safe4 (size_t len) +{ + len = len > sizeof (dst) - 1 ? sizeof (dst) - 1 : len; + len = len < sizeof (dst) / 2 ? sizeof (dst) / 2 : len; + + __builtin_strncat (dst, "hello", len); +} + /* { dg-final { scan-assembler-not "__memcpy_chk" } } */ /* { dg-final { scan-assembler-not "__strncpy_chk" } } */ /* { dg-final { scan-assembler-not "__snprintf_chk" } } */ +/* { dg-final { scan-assembler-not "strncat" } } */ diff --git a/gcc/testsuite/gcc.dg/fold-stringops-3.c b/gcc/testsuite/gcc.dg/fold-stringops-3.c new file mode 100644 index 00000000000..ae2efbf9967 --- /dev/null +++ b/gcc/testsuite/gcc.dg/fold-stringops-3.c @@ -0,0 +1,18 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +typedef __SIZE_TYPE__ size_t; + +char dst[1024]; + +void +safe1 (size_t len) +{ + len = len > sizeof (dst) ? sizeof (dst) : len; + len = len < sizeof (dst) / 2 ? sizeof (dst) / 2 : len; + + __builtin_snprintf (dst, len, "hello"); + __builtin_snprintf (dst + 5, len, "%s", " world"); +} + +/* { dg-final { scan-assembler-not "snprintf" } } */ -- 2.31.1