Commit 781ff3d8 by Martin Sebor Committed by Martin Sebor

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
parent 7314856c
2018-06-18 Martin Sebor <msebor@redhat.com>
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 <prathamesh.kulkarni@linaro.org>
* tree.c (escaped_string::escape): Replace cast to char * by
......
......@@ -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,
......
......@@ -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;
......
......@@ -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)
......
......@@ -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))
......
......@@ -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
......
2018-06-18 Martin Sebor <msebor@redhat.com>
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 <wdijkstr@arm.com>
PR tree-optimization/86076
......
......@@ -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));
}
......
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;
}
/* 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)"");
}
/* 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" } */
}
/* 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\]+" } */
}
/* 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\]+" } */
}
......@@ -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);
......
......@@ -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:
......
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