+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
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,
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);
}
/* 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
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. */
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;
}
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)
{
ret = build1 (NOP_EXPR, TREE_TYPE (ret), ret);
SET_EXPR_LOCATION (ret, loc);
- TREE_NO_WARNING (ret) = 1;
return ret;
}
return NULL_TREE;
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)
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. */
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;
}
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. */
else if (bound == void_type_node)
bound = NULL_TREE;
- location_t loc = EXPR_LOCATION (exp);
-
bool warned = false;
if (wi::ltu_p (asize, wibnd))
@findex strncmp
@findex strncpy
@findex strndup
+@findex strnlen
@findex strpbrk
@findex strrchr
@findex strspn
@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
void sink (int, ...);
-#define T(call) sink (0, (call))
+#define T(call) sink (0, call)
void test_printf (struct MemArrays *p)
{
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));
}
--- /dev/null
+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;
+}
--- /dev/null
+#include "lib/strnlen.c"
--- /dev/null
+/* 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)"");
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* 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\]+" } */
+}
--- /dev/null
+/* 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\]+" } */
+}
--- /dev/null
+/* 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" } } */
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);
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)
{
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
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;
}
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);
}
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;
}
}
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);
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));
}
}
switch (DECL_FUNCTION_CODE (callee))
{
case BUILT_IN_STRLEN:
+ case BUILT_IN_STRNLEN:
handle_builtin_strlen (gsi);
break;
case BUILT_IN_STRCHR: