From: Martin Sebor Date: Mon, 18 Jun 2018 16:32:59 +0000 (+0000) Subject: PR tree-optimization/81384 - built-in form of strnlen missing X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=781ff3d80e88d7d0df019eb3e82ef2a3fb64429c;p=gcc.git PR tree-optimization/81384 - built-in form of strnlen missing gcc/ChangeLog: PR tree-optimization/81384 * builtin-types.def (BT_FN_SIZE_CONST_STRING_SIZE): New. * builtins.c (expand_builtin_strnlen): New function. (expand_builtin): Call it. (fold_builtin_n): Avoid setting TREE_NO_WARNING. * builtins.def (BUILT_IN_STRNLEN): New. * calls.c (maybe_warn_nonstring_arg): Handle BUILT_IN_STRNLEN. Warn for bounds in excess of maximum object size. * tree-ssa-strlen.c (maybe_set_strlen_range): Return tree representing single-value ranges. Handle strnlen. (handle_builtin_strlen): Handle strnlen. (strlen_check_and_optimize_stmt): Same. * doc/extend.texi (Other Builtins): Document strnlen. gcc/testsuite/ChangeLog: PR tree-optimization/81384 * gcc.c-torture/execute/builtins/lib/strnlen.c: New test. * gcc.c-torture/execute/builtins/strnlen-lib.c: New test. * gcc.c-torture/execute/builtins/strnlen.c: New test. * gcc.dg/attr-nonstring-2.c: New test. * gcc.dg/attr-nonstring-3.c: New test. * gcc.dg/attr-nonstring-4.c: New test. * gcc.dg/strlenopt-45.c: New test. * gcc.dg/strlenopt.h (strnlen): Declare. From-SVN: r261705 --- diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 56401fbf9b2..6a386d93691 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,19 @@ +2018-06-18 Martin Sebor + + PR tree-optimization/81384 + * builtin-types.def (BT_FN_SIZE_CONST_STRING_SIZE): New. + * builtins.c (expand_builtin_strnlen): New function. + (expand_builtin): Call it. + (fold_builtin_n): Avoid setting TREE_NO_WARNING. + * builtins.def (BUILT_IN_STRNLEN): New. + * calls.c (maybe_warn_nonstring_arg): Handle BUILT_IN_STRNLEN. + Warn for bounds in excess of maximum object size. + * tree-ssa-strlen.c (maybe_set_strlen_range): Return tree representing + single-value ranges. Handle strnlen. + (handle_builtin_strlen): Handle strnlen. + (strlen_check_and_optimize_stmt): Same. + * doc/extend.texi (Other Builtins): Document strnlen. + 2018-06-18 Prathamesh Kulkarni * tree.c (escaped_string::escape): Replace cast to char * by diff --git a/gcc/builtin-types.def b/gcc/builtin-types.def index 845810c3736..b01095c420f 100644 --- a/gcc/builtin-types.def +++ b/gcc/builtin-types.def @@ -318,6 +318,8 @@ DEF_FUNCTION_TYPE_2 (BT_FN_STRING_CONST_STRING_INT, BT_STRING, BT_CONST_STRING, BT_INT) DEF_FUNCTION_TYPE_2 (BT_FN_STRING_CONST_STRING_SIZE, BT_STRING, BT_CONST_STRING, BT_SIZE) +DEF_FUNCTION_TYPE_2 (BT_FN_SIZE_CONST_STRING_SIZE, + BT_SIZE, BT_CONST_STRING, BT_SIZE) DEF_FUNCTION_TYPE_2 (BT_FN_INT_CONST_STRING_FILEPTR, BT_INT, BT_CONST_STRING, BT_FILEPTR) DEF_FUNCTION_TYPE_2 (BT_FN_INT_INT_FILEPTR, diff --git a/gcc/builtins.c b/gcc/builtins.c index 6b3e6b2ea96..91658e84761 100644 --- a/gcc/builtins.c +++ b/gcc/builtins.c @@ -140,6 +140,7 @@ static rtx expand_builtin_memset (tree, rtx, machine_mode); static rtx expand_builtin_memset_args (tree, tree, tree, rtx, machine_mode, tree); static rtx expand_builtin_bzero (tree); static rtx expand_builtin_strlen (tree, rtx, machine_mode); +static rtx expand_builtin_strnlen (tree, rtx, machine_mode); static rtx expand_builtin_alloca (tree); static rtx expand_builtin_unop (machine_mode, tree, rtx, rtx, optab); static rtx expand_builtin_frame_address (tree, tree); @@ -2820,7 +2821,7 @@ expand_builtin_powi (tree exp, rtx target) } /* Expand expression EXP which is a call to the strlen builtin. Return - NULL_RTX if we failed the caller should emit a normal call, otherwise + NULL_RTX if we failed and the caller should emit a normal call, otherwise try to get the result in TARGET, if convenient. */ static rtx @@ -2925,6 +2926,74 @@ expand_builtin_strlen (tree exp, rtx target, return target; } +/* Expand call EXP to the strnlen built-in, returning the result + and setting it in TARGET. Otherwise return NULL_RTX on failure. */ + +static rtx +expand_builtin_strnlen (tree exp, rtx target, machine_mode target_mode) +{ + if (!validate_arglist (exp, POINTER_TYPE, INTEGER_TYPE, VOID_TYPE)) + return NULL_RTX; + + tree src = CALL_EXPR_ARG (exp, 0); + tree bound = CALL_EXPR_ARG (exp, 1); + + if (!bound) + return NULL_RTX; + + location_t loc = UNKNOWN_LOCATION; + if (EXPR_HAS_LOCATION (exp)) + loc = EXPR_LOCATION (exp); + + tree maxobjsize = max_object_size (); + tree func = get_callee_fndecl (exp); + + tree len = c_strlen (src, 0); + + if (TREE_CODE (bound) == INTEGER_CST) + { + if (!TREE_NO_WARNING (exp) + && tree_int_cst_lt (maxobjsize, bound) + && warning_at (loc, OPT_Wstringop_overflow_, + "%K%qD specified bound %E " + "exceeds maximum object size %E", + exp, func, bound, maxobjsize)) + TREE_NO_WARNING (exp) = true; + + if (!len || TREE_CODE (len) != INTEGER_CST) + return NULL_RTX; + + len = fold_convert_loc (loc, size_type_node, len); + len = fold_build2_loc (loc, MIN_EXPR, size_type_node, len, bound); + return expand_expr (len, target, target_mode, EXPAND_NORMAL); + } + + if (TREE_CODE (bound) != SSA_NAME) + return NULL_RTX; + + wide_int min, max; + enum value_range_type rng = get_range_info (bound, &min, &max); + if (rng != VR_RANGE) + return NULL_RTX; + + if (!TREE_NO_WARNING (exp) + && wi::ltu_p (wi::to_wide (maxobjsize), min) + && warning_at (loc, OPT_Wstringop_overflow_, + "%K%qD specified bound [%wu, %wu] " + "exceeds maximum object size %E", + exp, func, min.to_uhwi (), max.to_uhwi (), maxobjsize)) + TREE_NO_WARNING (exp) = true; + + if (!len || TREE_CODE (len) != INTEGER_CST) + return NULL_RTX; + + if (wi::gtu_p (min, wi::to_wide (len))) + return expand_expr (len, target, target_mode, EXPAND_NORMAL); + + len = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (len), len, bound); + return expand_expr (len, target, target_mode, EXPAND_NORMAL); +} + /* Callback routine for store_by_pieces. Read GET_MODE_BITSIZE (MODE) bytes from constant string DATA + OFFSET and return it as target constant. */ @@ -3121,20 +3190,27 @@ check_access (tree exp, tree, tree, tree dstwrite, object size. */ if (range[0] && tree_int_cst_lt (maxobjsize, range[0])) { + if (TREE_NO_WARNING (exp)) + return false; + location_t loc = tree_nonartificial_location (exp); loc = expansion_point_location_if_in_system_header (loc); + bool warned; if (range[0] == range[1]) - warning_at (loc, opt, - "%K%qD specified size %E " - "exceeds maximum object size %E", - exp, func, range[0], maxobjsize); - else - warning_at (loc, opt, - "%K%qD specified size between %E and %E " - "exceeds maximum object size %E", - exp, func, - range[0], range[1], maxobjsize); + warned = warning_at (loc, opt, + "%K%qD specified size %E " + "exceeds maximum object size %E", + exp, func, range[0], maxobjsize); + else + warned = warning_at (loc, opt, + "%K%qD specified size between %E and %E " + "exceeds maximum object size %E", + exp, func, + range[0], range[1], maxobjsize); + if (warned) + TREE_NO_WARNING (exp) = true; + return false; } @@ -6963,6 +7039,12 @@ expand_builtin (tree exp, rtx target, rtx subtarget, machine_mode mode, return target; break; + case BUILT_IN_STRNLEN: + target = expand_builtin_strnlen (exp, target, target_mode); + if (target) + return target; + break; + case BUILT_IN_STRCAT: target = expand_builtin_strcat (exp, target); if (target) @@ -9174,7 +9256,6 @@ fold_builtin_n (location_t loc, tree fndecl, tree *args, int nargs, bool) { ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret); SET_EXPR_LOCATION (ret, loc); - TREE_NO_WARNING (ret) = 1; return ret; } return NULL_TREE; diff --git a/gcc/builtins.def b/gcc/builtins.def index 9776dfabddf..aacbd513a16 100644 --- a/gcc/builtins.def +++ b/gcc/builtins.def @@ -709,6 +709,7 @@ DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNCASECMP, "strncasecmp", BT_FN_INT_CONST_STR DEF_LIB_BUILTIN (BUILT_IN_STRNCAT, "strncat", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) DEF_LIB_BUILTIN (BUILT_IN_STRNCMP, "strncmp", BT_FN_INT_CONST_STRING_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) DEF_LIB_BUILTIN (BUILT_IN_STRNCPY, "strncpy", BT_FN_STRING_STRING_CONST_STRING_SIZE, ATTR_RET1_NOTHROW_NONNULL_LEAF) +DEF_EXT_LIB_BUILTIN (BUILT_IN_STRNLEN, "strnlen", BT_FN_SIZE_CONST_STRING_SIZE, ATTR_PURE_NOTHROW_NONNULL_LEAF) DEF_LIB_BUILTIN (BUILT_IN_STRPBRK, "strpbrk", BT_FN_STRING_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF) DEF_LIB_BUILTIN (BUILT_IN_STRRCHR, "strrchr", BT_FN_STRING_CONST_STRING_INT, ATTR_PURE_NOTHROW_NONNULL_LEAF) DEF_LIB_BUILTIN (BUILT_IN_STRSPN, "strspn", BT_FN_SIZE_CONST_STRING_CONST_STRING, ATTR_PURE_NOTHROW_NONNULL_LEAF) diff --git a/gcc/calls.c b/gcc/calls.c index 3311d1cbe2b..02562ddc1ee 100644 --- a/gcc/calls.c +++ b/gcc/calls.c @@ -1613,6 +1613,9 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) if (!fndecl || DECL_BUILT_IN_CLASS (fndecl) != BUILT_IN_NORMAL) return; + if (TREE_NO_WARNING (exp)) + return; + unsigned nargs = call_expr_nargs (exp); /* The bound argument to a bounded string function like strncpy. */ @@ -1647,18 +1650,23 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) case BUILT_IN_STPNCPY: case BUILT_IN_STRNCPY: - { - unsigned argno = 2; - if (argno < nargs) - bound = CALL_EXPR_ARG (exp, argno); - break; - } + if (2 < nargs) + bound = CALL_EXPR_ARG (exp, 2); + break; case BUILT_IN_STRNDUP: + if (1 < nargs) + bound = CALL_EXPR_ARG (exp, 1); + break; + + case BUILT_IN_STRNLEN: { - unsigned argno = 1; - if (argno < nargs) - bound = CALL_EXPR_ARG (exp, argno); + tree arg = CALL_EXPR_ARG (exp, 0); + if (!get_attr_nonstring_decl (arg)) + get_range_strlen (arg, lenrng); + + if (1 < nargs) + bound = CALL_EXPR_ARG (exp, 1); break; } @@ -1674,6 +1682,29 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) get_size_range (bound, bndrng); } + location_t loc = EXPR_LOCATION (exp); + + if (bndrng[0]) + { + /* Diagnose excessive bound prior the adjustment below and + regardless of attribute nonstring. */ + tree maxobjsize = max_object_size (); + if (tree_int_cst_lt (maxobjsize, bndrng[0])) + { + if (tree_int_cst_equal (bndrng[0], bndrng[1])) + warning_at (loc, OPT_Wstringop_overflow_, + "%K%qD specified bound %E " + "exceeds maximum object size %E", + exp, fndecl, bndrng[0], maxobjsize); + else + warning_at (loc, OPT_Wstringop_overflow_, + "%K%qD specified bound [%E, %E] " + "exceeds maximum object size %E", + exp, fndecl, bndrng[0], bndrng[1], maxobjsize); + return; + } + } + if (*lenrng) { /* Add one for the nul. */ @@ -1766,8 +1797,6 @@ maybe_warn_nonstring_arg (tree fndecl, tree exp) else if (bound == void_type_node) bound = NULL_TREE; - location_t loc = EXPR_LOCATION (exp); - bool warned = false; if (wi::ltu_p (asize, wibnd)) diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi index ca96c083f9f..cf88175e795 100644 --- a/gcc/doc/extend.texi +++ b/gcc/doc/extend.texi @@ -11255,6 +11255,7 @@ is called and the @var{flag} argument passed to it. @findex strncmp @findex strncpy @findex strndup +@findex strnlen @findex strpbrk @findex strrchr @findex strspn @@ -11338,8 +11339,8 @@ Outside strict ISO C mode (@option{-ansi}, @option{-std=c90}, @code{significandl}, @code{significand}, @code{sincosf}, @code{sincosl}, @code{sincos}, @code{stpcpy}, @code{stpncpy}, @code{strcasecmp}, @code{strdup}, @code{strfmon}, @code{strncasecmp}, -@code{strndup}, @code{toascii}, @code{y0f}, @code{y0l}, @code{y0}, -@code{y1f}, @code{y1l}, @code{y1}, @code{ynf}, @code{ynl} and +@code{strndup}, @code{strnlen}, @code{toascii}, @code{y0f}, @code{y0l}, +@code{y0}, @code{y1f}, @code{y1l}, @code{y1}, @code{ynf}, @code{ynl} and @code{yn} may be handled as built-in functions. All these functions have corresponding versions diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 5672f8b886e..024cf6b2437 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,15 @@ +2018-06-18 Martin Sebor + + PR tree-optimization/81384 + * gcc.c-torture/execute/builtins/lib/strnlen.c: New test. + * gcc.c-torture/execute/builtins/strnlen-lib.c: New test. + * gcc.c-torture/execute/builtins/strnlen.c: New test. + * gcc.dg/attr-nonstring-2.c: New test. + * gcc.dg/attr-nonstring-3.c: New test. + * gcc.dg/attr-nonstring-4.c: New test. + * gcc.dg/strlenopt-45.c: New test. + * gcc.dg/strlenopt.h (strnlen): Declare. + 2018-06-18 Wilco Dijkstra PR tree-optimization/86076 diff --git a/gcc/testsuite/c-c++-common/attr-nonstring-3.c b/gcc/testsuite/c-c++-common/attr-nonstring-3.c index a54e73a3285..b1eb8b63004 100644 --- a/gcc/testsuite/c-c++-common/attr-nonstring-3.c +++ b/gcc/testsuite/c-c++-common/attr-nonstring-3.c @@ -66,7 +66,7 @@ struct MemArrays void sink (int, ...); -#define T(call) sink (0, (call)) +#define T(call) sink (0, call) void test_printf (struct MemArrays *p) { @@ -250,14 +250,14 @@ void test_stpncpy_warn (struct MemArrays *p, unsigned n) T (stpncpy (ptr, str, N + 1)); T (stpncpy (ptr, arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound 5" } */ - T (stpncpy (arr, str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " "bug 82609" { xfail c++ } } */ + T (stpncpy (arr, str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ T (stpncpy (ptr, ptr, N + 1)); T (stpncpy (ptr, parr, N + 1)); T (stpncpy (parr, str, N + 1)); T (stpncpy (ptr, p->arr, N + 1)); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller" } */ - T (stpncpy (p->arr, p->str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows" "bug 82609" { xfail c++ } } */ + T (stpncpy (p->arr, p->str, N + 1)); /* { dg-warning "writing 5 bytes into a region of size 4 overflows " } */ T (stpncpy (p->parr, p->str, N + 1)); } diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c new file mode 100644 index 00000000000..73ada14eb95 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/lib/strnlen.c @@ -0,0 +1,22 @@ +typedef __SIZE_TYPE__ size_t; + +extern void abort (void); +extern int inside_main; + +__attribute__ ((__noinline__)) +size_t +strnlen (const char *s, size_t n) +{ + size_t i; + +#ifdef __OPTIMIZE__ + if (inside_main) + abort (); +#endif + + i = 0; + while (s[i] != 0 && n--) + i++; + + return i; +} diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c new file mode 100644 index 00000000000..bf9194012f1 --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen-lib.c @@ -0,0 +1 @@ +#include "lib/strnlen.c" diff --git a/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c new file mode 100644 index 00000000000..2368d06e95d --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/builtins/strnlen.c @@ -0,0 +1,95 @@ +/* PR tree-optimization/81384 - built-in form of strnlen missing + Test to verify that strnlen built-in expansion works correctly + in the absence of tree strlen optimization. + { dg-do run } + { do-options "-O2 -fno-tree-strlen" } */ + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define SIZE_MAX __SIZE_MAX__ +#define NOIPA __attribute__ ((noipa)) + +typedef __SIZE_TYPE__ size_t; + +extern void abort (void); +extern size_t strnlen (const char *, size_t); + +#define A(expr) \ + ((expr) ? (void)0 \ + : (__builtin_printf ("assertion on line %i failed: %s\n", \ + __LINE__, #expr), \ + abort ())) + +NOIPA void test_strnlen_str_cst (void) +{ + A (strnlen ("", 0) == 0); + A (strnlen ("", 1) == 0); + A (strnlen ("", 9) == 0); + A (strnlen ("", PTRDIFF_MAX) == 0); + A (strnlen ("", SIZE_MAX) == 0); + A (strnlen ("", -1) == 0); + + A (strnlen ("1", 0) == 0); + A (strnlen ("1", 1) == 1); + A (strnlen ("1", 9) == 1); + A (strnlen ("1", PTRDIFF_MAX) == 1); + A (strnlen ("1", SIZE_MAX) == 1); + A (strnlen ("1", -2) == 1); + + A (strnlen ("123", 0) == 0); + A (strnlen ("123", 1) == 1); + A (strnlen ("123", 2) == 2); + A (strnlen ("123", 3) == 3); + A (strnlen ("123", 9) == 3); + A (strnlen ("123", PTRDIFF_MAX) == 3); + A (strnlen ("123", SIZE_MAX) == 3); + A (strnlen ("123", -2) == 3); +} + +NOIPA void test_strnlen_str_range (size_t x) +{ + size_t r_0_3 = x & 3; + size_t r_1_3 = r_0_3 | 1; + size_t r_2_3 = r_0_3 | 2; + + A (strnlen ("", r_0_3) == 0); + A (strnlen ("1", r_0_3) <= 1); + A (strnlen ("12", r_0_3) <= 2); + A (strnlen ("123", r_0_3) <= 3); + A (strnlen ("1234", r_0_3) <= 3); + + A (strnlen ("", r_1_3) == 0); + A (strnlen ("1", r_1_3) == 1); + A (strnlen ("12", r_1_3) <= 2); + A (strnlen ("123", r_1_3) <= 3); + A (strnlen ("1234", r_1_3) <= 3); + + A (strnlen ("", r_2_3) == 0); + A (strnlen ("1", r_2_3) == 1); + A (strnlen ("12", r_2_3) == 2); + A (strnlen ("123", r_2_3) <= 3); + A (strnlen ("1234", r_2_3) <= 3); +} + +NOIPA void test_strnlen_str_range_side_effect (size_t x) +{ + size_t r_0_3 = x & 3; + size_t r_1_3 = r_0_3 | 1; + size_t r_2_3 = r_0_3 | 2; + size_t n = r_2_3; + + int i = 0; + + A (strnlen ("1234" + i++, n) <= 3); + A (i == 1); + + A (strnlen ("1234", n++) <= 3); + A (n == r_2_3 + 1); +} + +void +main_test (void) +{ + test_strnlen_str_cst (); + test_strnlen_str_range ((size_t)""); + test_strnlen_str_range_side_effect ((size_t)""); +} diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-2.c b/gcc/testsuite/gcc.dg/attr-nonstring-2.c new file mode 100644 index 00000000000..e9edb661b9a --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-nonstring-2.c @@ -0,0 +1,115 @@ +/* PR middle-end/81384 - built-in form of strnlen missing + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#include "range.h" + +extern void* memcpy (void*, const void*, size_t); +extern size_t strnlen (const char*, size_t); + +#define NONSTRING __attribute__ ((nonstring)) + +#define _CAT(s, n) s ## n +#define CAT(s, n) _CAT (s, n) +#define UNIQ(n) CAT (n, __LINE__) + +void sink (size_t, ...); + +#define T(expr) sink (expr) + +void test_strnlen_array_cst (void) +{ + NONSTRING char ns3[3]; + sink (0, ns3); // "initialize" ns3 + + T (strnlen (ns3, 0)); + T (strnlen (ns3, 1)); + T (strnlen (ns3, 2)); + T (strnlen (ns3, 3)); + T (strnlen (ns3, 4)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 4" } */ + T (strnlen (ns3, DIFF_MAX)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */ + T (strnlen (ns3, SIZE_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + NONSTRING char ns5[5]; + sink (0, ns5); + + T (strnlen (ns5, 0)); + T (strnlen (ns5, 1)); + T (strnlen (ns5, 2)); + T (strnlen (ns5, 3)); + T (strnlen (ns5, 6)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */ + T (strnlen (ns5, DIFF_MAX)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */ + T (strnlen (ns5, SIZE_MAX)); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ +} + + +void test_strnlen_array_range (void) +{ + NONSTRING char ns3[3]; + sink (0, ns3); // "initialize" ns3 + + T (strnlen (ns3, UR (0, 3))); + T (strnlen (ns3, UR (0, 9))); + T (strnlen (ns3, UR (3, 4))); + T (strnlen (ns3, UR (3, DIFF_MAX))); + T (strnlen (ns3, UR (4, 5))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound \[0-9\]+" } */ + T (strnlen (ns3, UR (DIFF_MAX, SIZE_MAX))); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller " } */ +} + + +#undef T +#define T(N, init, nelts, bound) \ + do { \ + extern NONSTRING char UNIQ (arr)[N]; \ + memcpy (UNIQ (arr), init, nelts); \ + sink (strnlen (UNIQ (arr), bound), UNIQ (arr)); \ + } while (0) + +void test_strnlen_string_cst (void) +{ + T (3, "1", 2, 1); + T (3, "1", 2, 2); + T (3, "1", 2, 3); + 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" } */ + 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); + T (5, "1", 2, 9); + + T (5, "12", 3, 1); + T (5, "12", 3, 9); + T (5, "123", 3, 1); + T (5, "123", 3, 5); + T (5, "123", 3, 6); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 6" } */ + + /* Strnlen shouldn't trigger a warning for arrays of unknown size + (except for accesses to uninitialized elements when those are + detected). */ + T (/* [] */, "1", 1, 1); + T (/* [] */, "1", 1, 2); + T (/* [] */, "1", 2, 1); + T (/* [] */, "1", 2, 2); + T (/* [] */, "1", 2, 3); + T (/* [] */, "1", 2, 9); + T (/* [] */, "1", 2, DIFF_MAX); + T (/* [] */, "1", 2, SIZE_MAX); + + size_t n = DIFF_MAX; + T (/* [] */, "123", 3, n); + T (/* [] */, "123", 3, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */ + n = SIZE_MAX; + T (/* [] */, "123", 3, n); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size " } */ +} + + +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" } */ + T (3, "123", 3, UR (5, 9)); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound 5" } */ +} diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-3.c b/gcc/testsuite/gcc.dg/attr-nonstring-3.c new file mode 100644 index 00000000000..4a104504555 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-nonstring-3.c @@ -0,0 +1,117 @@ +/* PR middle-end/81384 - built-in form of strnlen missing + + Since the strnlen patch affects the handling for strncmp and other + bounded functions, verify that a bound in excess of the maximum + object size specified for strncmp is diagnosed regardless of + attribute nonstring. Also verify that a bound that's greater than + the size of a non-string array is diagnosed, even if it's not in + excess of the maximum object size. + + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#include "range.h" + +extern int strncmp (const char*, const char*, size_t); + +#define STR /* not non-string */ +#define NS __attribute__ ((nonstring)) + +#define _CAT(s, n) s ## n +#define CAT(s, n) _CAT (s, n) +#define UNIQ(n) CAT (n, __LINE__) + +void sink (int); + +#define T(at1, N1, at2, N2, bound) \ + do { \ + extern at1 char UNIQ (a)[N1]; \ + extern at2 char UNIQ (b)[N2]; \ + sink (strncmp (UNIQ (a), UNIQ (b), bound)); \ + } while (0) + +void strncmp_cst (void) +{ + size_t n = DIFF_MAX; + + T (STR, /* [] */, STR, /* [] */, n); + T (STR, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, STR, /* [] */, n); + T (STR, 2, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, STR, 3, n); + T (STR, /* [] */, STR, 4, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, /* [] */, n); + T (STR, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, 5, NS, /* [] */, n); + T (STR, 6, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, 7, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + + T (STR, /* [] */, NS, 8, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, /* [] */, n); + T (NS, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, STR, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, 11, n); + T (NS, /* [] */, STR, 12, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, /* [] */, n); + T (NS, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, 13, NS, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 14, NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, 15, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, /* [] */, NS, 16, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ +} + + +void strncmp_range (void) +{ + size_t n = DIFF_MAX; + n = UR (n, n + 1); + + T (STR, /* [] */, STR, /* [] */, n); + T (STR, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, STR, /* [] */, n); + T (STR, 2, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, STR, 3, n); + T (STR, /* [] */, STR, 4, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, /* [] */, n); + T (STR, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, 5, NS, /* [] */, n); + T (STR, 6, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, /* [] */, NS, 7, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + + T (STR, /* [] */, NS, 8, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, /* [] */, n); + T (NS, /* [] */, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, STR, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, STR, 11, n); + T (NS, /* [] */, STR, 12, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, /* [] */, n); + T (NS, /* [] */, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, 13, NS, /* [] */, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 14, NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, NS, 15, n); /* { dg-warning "argument 2 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, /* [] */, NS, 16, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ +} diff --git a/gcc/testsuite/gcc.dg/attr-nonstring-4.c b/gcc/testsuite/gcc.dg/attr-nonstring-4.c new file mode 100644 index 00000000000..7daff9754c7 --- /dev/null +++ b/gcc/testsuite/gcc.dg/attr-nonstring-4.c @@ -0,0 +1,64 @@ +/* PR middle-end/81384 - built-in form of strnlen missing + + Verify that a strnlen bound in excess of the maximum object size + is diagnosed regardless of attribute nonstring. Also verify that + a bound that's greater than the size of a non-string array is + diagnosed, even if it's not in excess of the maximum object size. + + { dg-do compile } + { dg-options "-O2 -Wall -ftrack-macro-expansion=0" } */ + +#include "range.h" + +extern size_t strnlen (const char*, size_t); + +#define STR /* not non-string */ +#define NS __attribute__ ((nonstring)) + +#define _CAT(s, n) s ## n +#define CAT(s, n) _CAT (s, n) +#define UNIQ(n) CAT (n, __LINE__) + +void sink (size_t); + +#define T(attr, N, bound) \ + do { \ + extern attr char UNIQ (a)[N]; \ + sink (strnlen (UNIQ (a), bound)); \ + } while (0) + +void strnlen_cst (void) +{ + size_t n = DIFF_MAX; + + T (STR, /* [] */, n); + T (STR, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, n); + T (STR, 2, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, n); + T (NS, /* [] */, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, n + 1); /* { dg-warning "specified bound \[0-9\]+ exceeds maximum object size \[0-9\]+" } */ +} + + +void strnlen_range (void) +{ + size_t n = DIFF_MAX; + n = UR (n, n + 1); + + T (STR, /* [] */, n); + T (STR, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (STR, 1, n); + T (STR, 2, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, /* [] */, n); + T (NS, /* [] */, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ + + T (NS, 9, n); /* { dg-warning "argument 1 declared attribute .nonstring. is smaller than the specified bound" } */ + T (NS, 10, n + 1); /* { dg-warning "specified bound \\\[\[0-9\]+, \[0-9\]+] exceeds maximum object size \[0-9\]+" } */ +} diff --git a/gcc/testsuite/gcc.dg/strlenopt-45.c b/gcc/testsuite/gcc.dg/strlenopt-45.c new file mode 100644 index 00000000000..bd9b197a75d --- /dev/null +++ b/gcc/testsuite/gcc.dg/strlenopt-45.c @@ -0,0 +1,335 @@ +/* PR tree-optimization/81384 - built-in form of strnlen missing + Test to verify that strnlen built-in expansion works correctly + in the absence of tree strlen optimization. + { dg-do compile } + { dg-options "-O2 -Wall -fdump-tree-optimized" } */ + +#include "strlenopt.h" + +#define PTRDIFF_MAX __PTRDIFF_MAX__ +#define SIZE_MAX __SIZE_MAX__ + +typedef __SIZE_TYPE__ size_t; + +extern void abort (void); +extern size_t strnlen (const char *, size_t); + +#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) + +/* Macro to emit a call to funcation named + call_in_true_branch_not_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 (in_true_branch_not_eliminated); else (void)0 + +/* Macro to emit a call to a function named + call_made_in_{true,false}_branch_on_line_NNN() + for each call that's expected to be retained. The dg-final + scan-tree-dump-time directive at the bottom of the test verifies + that the expected number of both kinds of calls appears in output + (a pair for each line with the invocation of the KEEP() macro. */ +#define KEEP(expr) \ + if (expr) \ + FAIL (made_in_true_branch); \ + else \ + FAIL (made_in_false_branch) + +extern char c; +extern char a1[1]; +extern char a3[3]; +extern char a5[5]; +extern char a3_7[3][7]; +extern char ax[]; + +void elim_strnlen_arr_cst (void) +{ + /* The length of a string stored in a one-element array must be zero. + The result reported by strnlen() for such an array can be non-zero + only when the bound is equal to 1 (in which case the result must + be one). */ + ELIM (strnlen (&c, 0) == 0); + ELIM (strnlen (&c, 1) < 2); + ELIM (strnlen (&c, 2) == 0); + ELIM (strnlen (&c, 9) == 0); + ELIM (strnlen (&c, PTRDIFF_MAX) == 0); + ELIM (strnlen (&c, SIZE_MAX) == 0); + ELIM (strnlen (&c, -1) == 0); + + ELIM (strnlen (a1, 0) == 0); + ELIM (strnlen (a1, 1) < 2); + ELIM (strnlen (a1, 2) == 0); + ELIM (strnlen (a1, 9) == 0); + ELIM (strnlen (a1, PTRDIFF_MAX) == 0); + ELIM (strnlen (a1, SIZE_MAX) == 0); + ELIM (strnlen (a1, -1) == 0); + + ELIM (strnlen (a3, 0) == 0); + ELIM (strnlen (a3, 1) < 2); + ELIM (strnlen (a3, 2) < 3); + ELIM (strnlen (a3, 3) < 4); + ELIM (strnlen (a3, 9) < 4); + ELIM (strnlen (a3, PTRDIFF_MAX) < 4); + ELIM (strnlen (a3, SIZE_MAX) < 4); + ELIM (strnlen (a3, -1) < 4); + + ELIM (strnlen (a3_7[0], 0) == 0); + ELIM (strnlen (a3_7[0], 1) < 2); + ELIM (strnlen (a3_7[0], 2) < 3); + ELIM (strnlen (a3_7[0], 3) < 4); + ELIM (strnlen (a3_7[0], 9) < 8); + ELIM (strnlen (a3_7[0], PTRDIFF_MAX) < 8); + ELIM (strnlen (a3_7[0], SIZE_MAX) < 8); + ELIM (strnlen (a3_7[0], -1) < 8); + + ELIM (strnlen (a3_7[2], 0) == 0); + ELIM (strnlen (a3_7[2], 1) < 2); + ELIM (strnlen (a3_7[2], 2) < 3); + ELIM (strnlen (a3_7[2], 3) < 4); + ELIM (strnlen (a3_7[2], 9) < 8); + ELIM (strnlen (a3_7[2], PTRDIFF_MAX) < 8); + ELIM (strnlen (a3_7[2], SIZE_MAX) < 8); + ELIM (strnlen (a3_7[2], -1) < 8); + + ELIM (strnlen ((char*)a3_7, 0) == 0); + ELIM (strnlen ((char*)a3_7, 1) < 2); + ELIM (strnlen ((char*)a3_7, 2) < 3); + ELIM (strnlen ((char*)a3_7, 3) < 4); + ELIM (strnlen ((char*)a3_7, 9) < 10); + ELIM (strnlen ((char*)a3_7, 19) < 20); + ELIM (strnlen ((char*)a3_7, 21) < 22); + ELIM (strnlen ((char*)a3_7, 23) < 22); + ELIM (strnlen ((char*)a3_7, PTRDIFF_MAX) < 22); + ELIM (strnlen ((char*)a3_7, SIZE_MAX) < 22); + ELIM (strnlen ((char*)a3_7, -1) < 22); + + ELIM (strnlen (ax, 0) == 0); + ELIM (strnlen (ax, 1) < 2); + ELIM (strnlen (ax, 2) < 3); + ELIM (strnlen (ax, 9) < 10); + ELIM (strnlen (a3, PTRDIFF_MAX) <= PTRDIFF_MAX); + ELIM (strnlen (a3, SIZE_MAX) < PTRDIFF_MAX); + ELIM (strnlen (a3, -1) < PTRDIFF_MAX); +} + +struct MemArrays +{ + char c; + char a0[0]; + char a1[1]; + char a3[3]; + char a5[5]; + char a3_7[3][7]; + char ax[1]; +}; + +void elim_strnlen_memarr_cst (struct MemArrays *p, int i) +{ + ELIM (strnlen (&p->c, 0) == 0); + ELIM (strnlen (&p->c, 1) < 2); + ELIM (strnlen (&p->c, 9) == 0); + ELIM (strnlen (&p->c, PTRDIFF_MAX) == 0); + ELIM (strnlen (&p->c, SIZE_MAX) == 0); + ELIM (strnlen (&p->c, -1) == 0); + + /* Other accesses to internal zero-length arrays are undefined. */ + ELIM (strnlen (p->a0, 0) == 0); + + ELIM (strnlen (p->a1, 0) == 0); + ELIM (strnlen (p->a1, 1) < 2); + ELIM (strnlen (p->a1, 9) == 0); + ELIM (strnlen (p->a1, PTRDIFF_MAX) == 0); + ELIM (strnlen (p->a1, SIZE_MAX) == 0); + ELIM (strnlen (p->a1, -1) == 0); + + ELIM (strnlen (p->a3, 0) == 0); + ELIM (strnlen (p->a3, 1) < 2); + ELIM (strnlen (p->a3, 2) < 3); + ELIM (strnlen (p->a3, 3) < 4); + ELIM (strnlen (p->a3, 9) < 4); + ELIM (strnlen (p->a3, PTRDIFF_MAX) < 4); + ELIM (strnlen (p->a3, SIZE_MAX) < 4); + ELIM (strnlen (p->a3, -1) < 4); + + ELIM (strnlen (p[i].a3, 0) == 0); + ELIM (strnlen (p[i].a3, 1) < 2); + ELIM (strnlen (p[i].a3, 2) < 3); + ELIM (strnlen (p[i].a3, 3) < 4); + ELIM (strnlen (p[i].a3, 9) < 4); + ELIM (strnlen (p[i].a3, PTRDIFF_MAX) < 4); + ELIM (strnlen (p[i].a3, SIZE_MAX) < 4); + ELIM (strnlen (p[i].a3, -1) < 4); + + ELIM (strnlen (p->a3_7[0], 0) == 0); + ELIM (strnlen (p->a3_7[0], 1) < 2); + ELIM (strnlen (p->a3_7[0], 2) < 3); + ELIM (strnlen (p->a3_7[0], 3) < 4); + ELIM (strnlen (p->a3_7[0], 9) < 8); + ELIM (strnlen (p->a3_7[0], PTRDIFF_MAX) < 8); + ELIM (strnlen (p->a3_7[0], SIZE_MAX) < 8); + ELIM (strnlen (p->a3_7[0], -1) < 8); + + ELIM (strnlen (p->a3_7[2], 0) == 0); + ELIM (strnlen (p->a3_7[2], 1) < 2); + ELIM (strnlen (p->a3_7[2], 2) < 3); + ELIM (strnlen (p->a3_7[2], 3) < 4); + ELIM (strnlen (p->a3_7[2], 9) < 8); + ELIM (strnlen (p->a3_7[2], PTRDIFF_MAX) < 8); + ELIM (strnlen (p->a3_7[2], SIZE_MAX) < 8); + ELIM (strnlen (p->a3_7[2], -1) < 8); + + ELIM (strnlen (p->a3_7[i], 0) == 0); + ELIM (strnlen (p->a3_7[i], 1) < 2); + ELIM (strnlen (p->a3_7[i], 2) < 3); + ELIM (strnlen (p->a3_7[i], 3) < 4); + +#if 0 + /* This is tranformed into strnlen ((char*)p + offsetof (a3_7[i]), N) + which makes it impssible to determine the size of the array. */ + ELIM (strnlen (p->a3_7[i], 9) < 8); + ELIM (strnlen (p->a3_7[i], PTRDIFF_MAX) < 8); + ELIM (strnlen (p->a3_7[i], SIZE_MAX) < 8); + ELIM (strnlen (p->a3_7[i], -1) < 8); +#else + ELIM (strnlen (p->a3_7[i], 9) < 10); + ELIM (strnlen (p->a3_7[i], 19) < 20); +#endif + + ELIM (strnlen ((char*)p->a3_7, 0) == 0); + ELIM (strnlen ((char*)p->a3_7, 1) < 2); + ELIM (strnlen ((char*)p->a3_7, 2) < 3); + ELIM (strnlen ((char*)p->a3_7, 3) < 4); + ELIM (strnlen ((char*)p->a3_7, 9) < 10); + ELIM (strnlen ((char*)p->a3_7, 19) < 20); + ELIM (strnlen ((char*)p->a3_7, 21) < 22); + ELIM (strnlen ((char*)p->a3_7, 23) < 22); + ELIM (strnlen ((char*)p->a3_7, PTRDIFF_MAX) < 22); + ELIM (strnlen ((char*)p->a3_7, SIZE_MAX) < 22); + ELIM (strnlen ((char*)p->a3_7, -1) < 22); + + ELIM (strnlen (p->ax, 0) == 0); + ELIM (strnlen (p->ax, 1) < 2); + ELIM (strnlen (p->ax, 2) < 3); + ELIM (strnlen (p->ax, 9) < 10); + ELIM (strnlen (p->a3, PTRDIFF_MAX) <= PTRDIFF_MAX); + ELIM (strnlen (p->a3, SIZE_MAX) < PTRDIFF_MAX); + ELIM (strnlen (p->a3, -1) < PTRDIFF_MAX); +} + + +void elim_strnlen_str_cst (void) +{ + const char *s0 = ""; + const char *s1 = "1"; + const char *s3 = "123"; + + ELIM (strnlen (s0, 0) == 0); + ELIM (strnlen (s0, 1) == 0); + ELIM (strnlen (s0, 9) == 0); + ELIM (strnlen (s0, PTRDIFF_MAX) == 0); + ELIM (strnlen (s0, SIZE_MAX) == 0); + ELIM (strnlen (s0, -1) == 0); + + ELIM (strnlen (s1, 0) == 0); + ELIM (strnlen (s1, 1) == 1); + ELIM (strnlen (s1, 9) == 1); + ELIM (strnlen (s1, PTRDIFF_MAX) == 1); + ELIM (strnlen (s1, SIZE_MAX) == 1); + ELIM (strnlen (s1, -2) == 1); + + ELIM (strnlen (s3, 0) == 0); + ELIM (strnlen (s3, 1) == 1); + ELIM (strnlen (s3, 2) == 2); + ELIM (strnlen (s3, 3) == 3); + ELIM (strnlen (s3, 9) == 3); + ELIM (strnlen (s3, PTRDIFF_MAX) == 3); + ELIM (strnlen (s3, SIZE_MAX) == 3); + ELIM (strnlen (s3, -2) == 3); +} + +void elim_strnlen_range (char *s) +{ + const char *s0 = ""; + const char *s1 = "1"; + const char *s3 = "123"; + + size_t n_0_1 = (size_t)s & 1; + size_t n_0_2 = ((size_t)s & 3) < 3 ? ((size_t)s & 3) : 2; + size_t n_0_3 = (size_t)s & 3; + size_t n_1_2 = n_0_1 + 1; + + ELIM (strnlen (s0, n_0_1) == 0); + ELIM (strnlen (s0, n_0_2) == 0); + ELIM (strnlen (s0, n_1_2) == 0); + + ELIM (strnlen (s1, n_0_1) < 2); + ELIM (strnlen (s1, n_0_2) < 2); + ELIM (strnlen (s1, n_0_3) < 2); + + ELIM (strnlen (s1, n_1_2) > 0); + ELIM (strnlen (s1, n_1_2) < 2); + + ELIM (strnlen (s3, n_0_1) < 2); + ELIM (strnlen (s3, n_0_2) < 3); + ELIM (strnlen (s3, n_0_3) < 4); + + ELIM (strnlen (s3, n_1_2) > 0); + ELIM (strnlen (s3, n_1_2) < 4); +} + + +#line 1000 + +void keep_strnlen_arr_cst (void) +{ + KEEP (strnlen (&c, 1) == 0); + KEEP (strnlen (&c, 1) == 1); + + KEEP (strnlen (a1, 1) == 0); + KEEP (strnlen (a1, 1) == 1); + + KEEP (strnlen (ax, 9) < 9); +} + +struct FlexArrays +{ + char c; + char a0[0]; /* Access to internal zero-length arrays are undefined. */ + char a1[1]; +}; + +void keep_strnlen_memarr_cst (struct FlexArrays *p) +{ + KEEP (strnlen (&p->c, 1) == 0); + KEEP (strnlen (&p->c, 1) == 1); + +#if 0 + /* Accesses to internal zero-length arrays are undefined so avoid + exercising them. */ + KEEP (strnlen (p->a0, 1) == 0); + KEEP (strnlen (p->a0, 1) == 1); + KEEP (strnlen (p->a0, 9) < 9); +#endif + + KEEP (strnlen (p->a1, 1) == 0); + KEEP (strnlen (p->a1, 1) == 1); + + KEEP (strnlen (p->a1, 2) == 0); + KEEP (strnlen (p->a1, 2) == 1); + KEEP (strnlen (p->a1, 2) == 2); + + KEEP (strnlen (p->a1, 9) < 9); +} + +/* { dg-final { scan-tree-dump-times "call_in_true_branch_not_eliminated_" 0 "optimized" } } + + { dg-final { scan-tree-dump-times "call_made_in_true_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 13 "optimized" } } + { dg-final { scan-tree-dump-times "call_made_in_false_branch_on_line_1\[0-9\]\[0-9\]\[0-9\]" 13 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/strlenopt.h b/gcc/testsuite/gcc.dg/strlenopt.h index 8f69940b027..a4044fd28f5 100644 --- a/gcc/testsuite/gcc.dg/strlenopt.h +++ b/gcc/testsuite/gcc.dg/strlenopt.h @@ -9,6 +9,7 @@ void *malloc (size_t); void free (void *); char *strdup (const char *); size_t strlen (const char *); +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); diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c index a4064a54b2e..693d9d3491a 100644 --- a/gcc/tree-ssa-strlen.c +++ b/gcc/tree-ssa-strlen.c @@ -1124,16 +1124,17 @@ adjust_last_stmt (strinfo *si, gimple *stmt, bool is_strcat) update_stmt (last.stmt); } -/* For an LHS that is an SSA_NAME with integer type and for strlen() - argument SRC, set LHS range info to [0, N] if SRC refers to - a character array A[N] with unknown length bounded by N. */ +/* For an LHS that is an SSA_NAME and for strlen() or strnlen() argument + SRC, set LHS range info to [0, min (N, BOUND)] if SRC refers to + a character array A[N] with unknown length bounded by N, and for + strnlen(), by min (N, BOUND). */ -static void -maybe_set_strlen_range (tree lhs, tree src) +static tree +maybe_set_strlen_range (tree lhs, tree src, tree bound) { if (TREE_CODE (lhs) != SSA_NAME || !INTEGRAL_TYPE_P (TREE_TYPE (lhs))) - return; + return NULL_TREE; if (TREE_CODE (src) == SSA_NAME) { @@ -1143,24 +1144,87 @@ maybe_set_strlen_range (tree lhs, tree src) src = gimple_assign_rhs1 (def); } - if (TREE_CODE (src) != ADDR_EXPR) - return; + wide_int max = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node)); + wide_int min = wi::zero (max.get_precision ()); - /* The last array member of a struct can be bigger than its size - suggests if it's treated as a poor-man's flexible array member. */ - src = TREE_OPERAND (src, 0); - if (TREE_CODE (TREE_TYPE (src)) != ARRAY_TYPE - || array_at_struct_end_p (src)) - return; + if (TREE_CODE (src) == ADDR_EXPR) + { + /* The last array member of a struct can be bigger than its size + suggests if it's treated as a poor-man's flexible array member. */ + src = TREE_OPERAND (src, 0); + bool src_is_array = TREE_CODE (TREE_TYPE (src)) == ARRAY_TYPE; + if (src_is_array && !array_at_struct_end_p (src)) + { + tree type = TREE_TYPE (src); + if (tree dom = TYPE_DOMAIN (type)) + { + tree maxval = TYPE_MAX_VALUE (dom); + if (maxval) + max = wi::to_wide (maxval); + else + max = wi::zero (min.get_precision ()); + + /* For strlen() the upper bound above is equal to + the longest string that can be stored in the array + (i.e., it accounts for the terminating nul. For + strnlen() bump up the maximum by one since the array + need not be nul-terminated. */ + if (bound) + ++max; + } + } + else + { + if (TREE_CODE (src) == COMPONENT_REF && !src_is_array) + src = TREE_OPERAND (src, 1); + if (DECL_P (src)) + { + /* Handle the unlikely case of strlen (&c) where c is some + variable. */ + if (tree size = DECL_SIZE_UNIT (src)) + if (TREE_CODE (size) == INTEGER_CST) + max = wi::to_wide (size); + } + } + } - tree type = TREE_TYPE (src); - if (tree dom = TYPE_DOMAIN (type)) - if (tree maxval = TYPE_MAX_VALUE (dom)) - { - wide_int max = wi::to_wide (maxval); - wide_int min = wi::zero (max.get_precision ()); - set_range_info (lhs, VR_RANGE, min, max); - } + if (bound) + { + /* For strnlen, adjust MIN and MAX as necessary. If the bound + is less than the size of the array set MAX to it. It it's + greater than MAX and MAX is non-zero bump MAX down to account + for the necessary terminating nul. Otherwise leave it alone. */ + if (TREE_CODE (bound) == INTEGER_CST) + { + wide_int wibnd = wi::to_wide (bound); + int cmp = wi::cmpu (wibnd, max); + if (cmp < 0) + max = wibnd; + else if (cmp && wi::ne_p (max, min)) + --max; + } + else if (TREE_CODE (bound) == SSA_NAME) + { + wide_int minbound, maxbound; + value_range_type rng = get_range_info (bound, &minbound, &maxbound); + if (rng == VR_RANGE) + { + /* For a bound in a known range, adjust the range determined + above as necessary. For a bound in some anti-range or + in an unknown range, use the range determined above. */ + if (wi::ltu_p (minbound, min)) + min = minbound; + if (wi::ltu_p (maxbound, max)) + max = maxbound; + } + } + } + + if (min == max) + return wide_int_to_tree (size_type_node, min); + + set_range_info (lhs, VR_RANGE, min, max); + return lhs; } /* Handle a strlen call. If strlen of the argument is known, replace @@ -1170,16 +1234,18 @@ maybe_set_strlen_range (tree lhs, tree src) static void handle_builtin_strlen (gimple_stmt_iterator *gsi) { - int idx; - tree src; gimple *stmt = gsi_stmt (*gsi); tree lhs = gimple_call_lhs (stmt); if (lhs == NULL_TREE) return; - src = gimple_call_arg (stmt, 0); - idx = get_stridx (src); + location_t loc = gimple_location (stmt); + tree callee = gimple_call_fndecl (stmt); + tree src = gimple_call_arg (stmt, 0); + tree bound = (DECL_FUNCTION_CODE (callee) == BUILT_IN_STRNLEN + ? gimple_call_arg (stmt, 1) : NULL_TREE); + int idx = get_stridx (src); if (idx) { strinfo *si = NULL; @@ -1203,8 +1269,9 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) } rhs = unshare_expr (rhs); if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (rhs))) - rhs = fold_convert_loc (gimple_location (stmt), - TREE_TYPE (lhs), rhs); + rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs); + if (bound) + rhs = fold_build2_loc (loc, MIN_EXPR, TREE_TYPE (rhs), rhs, bound); if (!update_call_from_tree (gsi, rhs)) gimplify_and_update_call_from_tree (gsi, rhs); stmt = gsi_stmt (*gsi); @@ -1225,10 +1292,8 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) } if (strlen_to_stridx) - { - location_t loc = gimple_location (stmt); - strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); - } + strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); + return; } } @@ -1251,7 +1316,6 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) si->full_string_p = true; if (TREE_CODE (old) == INTEGER_CST) { - location_t loc = gimple_location (stmt); old = fold_convert_loc (loc, TREE_TYPE (lhs), old); tree adj = fold_build2_loc (loc, MINUS_EXPR, TREE_TYPE (lhs), lhs, old); @@ -1274,14 +1338,32 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi) find_equal_ptrs (src, idx); /* For SRC that is an array of N elements, set LHS's range - to [0, N]. */ - maybe_set_strlen_range (lhs, src); + to [0, min (N, BOUND)]. A constant return value means + the range would have consisted of a single value. In + that case, fold the result into the returned constant. */ + if (tree ret = maybe_set_strlen_range (lhs, src, bound)) + if (TREE_CODE (ret) == INTEGER_CST) + { + if (dump_file && (dump_flags & TDF_DETAILS) != 0) + { + fprintf (dump_file, "Optimizing: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); + } + if (!useless_type_conversion_p (TREE_TYPE (lhs), TREE_TYPE (ret))) + ret = fold_convert_loc (loc, TREE_TYPE (lhs), ret); + if (!update_call_from_tree (gsi, ret)) + gimplify_and_update_call_from_tree (gsi, ret); + stmt = gsi_stmt (*gsi); + update_stmt (stmt); + if (dump_file && (dump_flags & TDF_DETAILS) != 0) + { + fprintf (dump_file, "into: "); + print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM); + } + } if (strlen_to_stridx) - { - location_t loc = gimple_location (stmt); - strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); - } + strlen_to_stridx->put (lhs, stridx_strlenloc (idx, loc)); } } @@ -3333,6 +3415,7 @@ strlen_check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh) switch (DECL_FUNCTION_CODE (callee)) { case BUILT_IN_STRLEN: + case BUILT_IN_STRNLEN: handle_builtin_strlen (gsi); break; case BUILT_IN_STRCHR: