aboutsummaryrefslogtreecommitdiff
path: root/gcc/tree-ssa-strlen.c
diff options
context:
space:
mode:
authorIan Lance Taylor <iant@golang.org>2021-09-13 10:37:49 -0700
committerIan Lance Taylor <iant@golang.org>2021-09-13 10:37:49 -0700
commite252b51ccde010cbd2a146485d8045103cd99533 (patch)
treee060f101cdc32bf5e520de8e5275db9d4236b74c /gcc/tree-ssa-strlen.c
parentf10c7c4596dda99d2ee872c995ae4aeda65adbdf (diff)
parent104c05c5284b7822d770ee51a7d91946c7e56d50 (diff)
downloadgcc-e252b51ccde010cbd2a146485d8045103cd99533.zip
gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.gz
gcc-e252b51ccde010cbd2a146485d8045103cd99533.tar.bz2
Merge from trunk revision 104c05c5284b7822d770ee51a7d91946c7e56d50.
Diffstat (limited to 'gcc/tree-ssa-strlen.c')
-rw-r--r--gcc/tree-ssa-strlen.c593
1 files changed, 335 insertions, 258 deletions
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index cccd4a0..7c93958 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3. If not see
#include "ssa.h"
#include "cgraph.h"
#include "gimple-pretty-print.h"
+#include "gimple-ssa-warn-access.h"
#include "gimple-ssa-warn-restrict.h"
#include "fold-const.h"
#include "stor-layout.h"
@@ -47,6 +48,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-ssa-strlen.h"
#include "tree-hash-traits.h"
#include "builtins.h"
+#include "pointer-query.h"
#include "target.h"
#include "diagnostic-core.h"
#include "diagnostic.h"
@@ -192,57 +194,35 @@ struct laststmt_struct
static int get_stridx_plus_constant (strinfo *, unsigned HOST_WIDE_INT, tree);
static void handle_builtin_stxncpy_strncat (bool, gimple_stmt_iterator *);
+static bool handle_assign (gimple_stmt_iterator *, tree, bool *,
+ pointer_query &);
/* Sets MINMAX to either the constant value or the range VAL is in
and returns either the constant value or VAL on success or null
when the range couldn't be determined. Uses RVALS when nonnull
- to determine the range, otherwise get_range_info. */
+ to determine the range, otherwise uses CFUN or global range info,
+ whichever is nonnull. */
tree
get_range (tree val, gimple *stmt, wide_int minmax[2],
range_query *rvals /* = NULL */)
{
- if (TREE_CODE (val) == INTEGER_CST)
- {
- minmax[0] = minmax[1] = wi::to_wide (val);
- return val;
- }
+ if (!rvals)
+ rvals = get_range_query (cfun);
- if (TREE_CODE (val) != SSA_NAME)
+ value_range vr;
+ if (!rvals->range_of_expr (vr, val, stmt))
return NULL_TREE;
- if (rvals && stmt)
+ value_range_kind rng = vr.kind ();
+ if (rng == VR_RANGE)
{
- value_range vr;
- if (!rvals->range_of_expr (vr, val, stmt))
- return NULL_TREE;
- value_range_kind rng = vr.kind ();
- if (rng != VR_RANGE)
- return NULL_TREE;
-
+ /* Only handle straight ranges. */
minmax[0] = wi::to_wide (vr.min ());
minmax[1] = wi::to_wide (vr.max ());
return val;
}
- value_range_kind rng = get_range_info (val, minmax, minmax + 1);
- if (rng == VR_RANGE)
- /* This may be an inverted range whose MINMAX[1] < MINMAX[0]. */
- return val;
-
- if (rng == VR_ANTI_RANGE)
- {
- /* An anti-range is the same as an ordinary range with inverted
- bounds (where MINMAX[1] < MINMAX[0] is true) that may result
- from the conversion of a signed anti-range to unsigned. */
- wide_int tmp = minmax[0];
- minmax[0] = minmax[1] + 1;
- minmax[1] = wi::sub (tmp, 1);
- return val;
- }
-
- /* Do not handle anti-ranges and instead make use of the on-demand
- VRP if/when it becomes available (hopefully in GCC 11). */
return NULL_TREE;
}
@@ -929,7 +909,17 @@ dump_strlen_info (FILE *fp, gimple *stmt, range_query *rvals)
rng = VR_UNDEFINED;
}
else
- rng = get_range_info (si->nonzero_chars, &min, &max);
+ {
+ value_range vr;
+ get_range_query (cfun)
+ ->range_of_expr (vr, si->nonzero_chars);
+ rng = vr.kind ();
+ if (!vr.undefined_p ())
+ {
+ min = wi::to_wide (vr.min ());
+ max = wi::to_wide (vr.max ());
+ }
+ }
if (rng == VR_RANGE || rng == VR_ANTI_RANGE)
{
@@ -1266,16 +1256,19 @@ maybe_invalidate (gimple *stmt, bool zero_write = false)
continue;
ao_ref r;
- tree size = NULL_TREE;
- if (si->nonzero_chars)
+ tree size = si->nonzero_chars;
+ ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
+ /* Include the terminating nul in the size of the string
+ to consider when determining possible clobber. But do not
+ add it to 'size' since we don't know whether it would
+ actually fit the allocated area. */
+ if (known_size_p (r.size))
{
- /* Include the terminating nul in the size of the string
- to consider when determining possible clobber. */
- tree type = TREE_TYPE (si->nonzero_chars);
- size = fold_build2 (PLUS_EXPR, type, si->nonzero_chars,
- build_int_cst (type, 1));
+ if (known_le (r.size, HOST_WIDE_INT_MAX - BITS_PER_UNIT))
+ r.max_size += BITS_PER_UNIT;
+ else
+ r.max_size = -1;
}
- ao_ref_init_from_ptr_and_size (&r, si->ptr, size);
if (stmt_may_clobber_ref_p_1 (stmt, &r))
{
if (dump_file && (dump_flags & TDF_DETAILS))
@@ -1809,18 +1802,17 @@ set_strlen_range (tree lhs, wide_int min, wide_int max,
}
else if (TREE_CODE (bound) == SSA_NAME)
{
- wide_int minbound, maxbound;
- // FIXME: Use range_query instead of global ranges.
- value_range_kind rng = get_range_info (bound, &minbound, &maxbound);
- if (rng == VR_RANGE)
+ value_range r;
+ get_range_query (cfun)->range_of_expr (r, bound);
+ if (!r.undefined_p ())
{
/* 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 by callers. */
- if (wi::ltu_p (minbound, min))
- min = minbound;
- if (wi::ltu_p (maxbound, max))
- max = maxbound;
+ if (wi::ltu_p (r.lower_bound (), min))
+ min = r.lower_bound ();
+ if (wi::ltu_p (r.upper_bound (), max))
+ max = r.upper_bound ();
}
}
}
@@ -1909,35 +1901,47 @@ maybe_set_strlen_range (tree lhs, tree src, tree bound)
/* Diagnose buffer overflow by a STMT writing LEN + PLUS_ONE bytes,
either into a region allocated for the object SI when non-null,
or into an object designated by the LHS of STMT otherwise.
+ For a call STMT, when CALL_LHS is set use its left hand side
+ as the destination, otherwise use argument zero.
When nonnull uses RVALS to determine range information.
RAWMEM may be set by memcpy and other raw memory functions
to allow accesses across subobject boundaries. */
static void
-maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
+maybe_warn_overflow (gimple *stmt, bool call_lhs, tree len,
+ pointer_query &ptr_qry,
strinfo *si = NULL, bool plus_one = false,
bool rawmem = false)
{
- if (!len || gimple_no_warning_p (stmt))
+ if (!len || warning_suppressed_p (stmt, OPT_Wstringop_overflow_))
return;
/* The DECL of the function performing the write if it is done
by one. */
tree writefn = NULL_TREE;
- /* The destination expression involved in the store STMT. */
+ /* The destination expression involved in the store or call STMT. */
tree dest = NULL_TREE;
if (is_gimple_assign (stmt))
dest = gimple_assign_lhs (stmt);
else if (is_gimple_call (stmt))
{
- dest = gimple_call_arg (stmt, 0);
+ if (call_lhs)
+ dest = gimple_call_lhs (stmt);
+ else
+ {
+ gcc_assert (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL));
+ dest = gimple_call_arg (stmt, 0);
+ }
+
+ if (!dest)
+ return;
writefn = gimple_call_fndecl (stmt);
}
else
return;
- if (TREE_NO_WARNING (dest))
+ if (warning_suppressed_p (dest, OPT_Wstringop_overflow_))
return;
const int ostype = rawmem ? 0 : 1;
@@ -2002,13 +2006,12 @@ maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
warned = (writefn
? warning_at (loc, OPT_Wstringop_overflow_,
- "%G%qD writing one too many bytes into a region "
+ "%qD writing one too many bytes into a region "
"of a size that depends on %<strlen%>",
- stmt, writefn)
+ writefn)
: warning_at (loc, OPT_Wstringop_overflow_,
- "%Gwriting one too many bytes into a region "
- "of a size that depends on %<strlen%>",
- stmt));
+ "writing one too many bytes into a region "
+ "of a size that depends on %<strlen%>"));
}
else if (lenrng[0] == lenrng[1])
{
@@ -2016,72 +2019,72 @@ maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
warned = (writefn
? warning_n (loc, OPT_Wstringop_overflow_,
lenrng[0].to_uhwi (),
- "%G%qD writing %wu byte into a region "
+ "%qD writing %wu byte into a region "
"of size %wu",
- "%G%qD writing %wu bytes into a region "
+ "%qD writing %wu bytes into a region "
"of size %wu",
- stmt, writefn, lenrng[0].to_uhwi (),
+ writefn, lenrng[0].to_uhwi (),
spcrng[0].to_uhwi ())
: warning_n (loc, OPT_Wstringop_overflow_,
lenrng[0].to_uhwi (),
- "%Gwriting %wu byte into a region "
+ "writing %wu byte into a region "
"of size %wu",
- "%Gwriting %wu bytes into a region "
+ "writing %wu bytes into a region "
"of size %wu",
- stmt, lenrng[0].to_uhwi (),
+ lenrng[0].to_uhwi (),
spcrng[0].to_uhwi ()));
else
warned = (writefn
? warning_n (loc, OPT_Wstringop_overflow_,
lenrng[0].to_uhwi (),
- "%G%qD writing %wu byte into a region "
+ "%qD writing %wu byte into a region "
"of size between %wu and %wu",
- "%G%qD writing %wu bytes into a region "
+ "%qD writing %wu bytes into a region "
"of size between %wu and %wu",
- stmt, writefn, lenrng[0].to_uhwi (),
+ writefn, lenrng[0].to_uhwi (),
spcrng[0].to_uhwi (), spcrng[1].to_uhwi ())
: warning_n (loc, OPT_Wstringop_overflow_,
lenrng[0].to_uhwi (),
- "%Gwriting %wu byte into a region "
+ "writing %wu byte into a region "
"of size between %wu and %wu",
- "%Gwriting %wu bytes into a region "
+ "writing %wu bytes into a region "
"of size between %wu and %wu",
- stmt, lenrng[0].to_uhwi (),
+ lenrng[0].to_uhwi (),
spcrng[0].to_uhwi (), spcrng[1].to_uhwi ()));
}
else if (spcrng[0] == spcrng[1])
warned = (writefn
? warning_at (loc, OPT_Wstringop_overflow_,
- "%G%qD writing between %wu and %wu bytes "
+ "%qD writing between %wu and %wu bytes "
"into a region of size %wu",
- stmt, writefn, lenrng[0].to_uhwi (),
+ writefn, lenrng[0].to_uhwi (),
lenrng[1].to_uhwi (),
spcrng[0].to_uhwi ())
: warning_at (loc, OPT_Wstringop_overflow_,
- "%Gwriting between %wu and %wu bytes "
+ "writing between %wu and %wu bytes "
"into a region of size %wu",
- stmt, lenrng[0].to_uhwi (),
+ lenrng[0].to_uhwi (),
lenrng[1].to_uhwi (),
spcrng[0].to_uhwi ()));
else
warned = (writefn
? warning_at (loc, OPT_Wstringop_overflow_,
- "%G%qD writing between %wu and %wu bytes "
+ "%qD writing between %wu and %wu bytes "
"into a region of size between %wu and %wu",
- stmt, writefn, lenrng[0].to_uhwi (),
+ writefn, lenrng[0].to_uhwi (),
lenrng[1].to_uhwi (),
spcrng[0].to_uhwi (), spcrng[1].to_uhwi ())
: warning_at (loc, OPT_Wstringop_overflow_,
- "%Gwriting between %wu and %wu bytes "
+ "writing between %wu and %wu bytes "
"into a region of size between %wu and %wu",
- stmt, lenrng[0].to_uhwi (),
+ lenrng[0].to_uhwi (),
lenrng[1].to_uhwi (),
spcrng[0].to_uhwi (), spcrng[1].to_uhwi ()));
if (!warned)
return;
- gimple_set_no_warning (stmt, true);
+ suppress_warning (stmt, OPT_Wstringop_overflow_);
aref.inform_access (access_write_only);
}
@@ -2089,12 +2092,12 @@ maybe_warn_overflow (gimple *stmt, tree len, pointer_query &ptr_qry,
/* Convenience wrapper for the above. */
static inline void
-maybe_warn_overflow (gimple *stmt, unsigned HOST_WIDE_INT len,
+maybe_warn_overflow (gimple *stmt, bool call_lhs, unsigned HOST_WIDE_INT len,
pointer_query &ptr_qry, strinfo *si = NULL,
bool plus_one = false, bool rawmem = false)
{
- maybe_warn_overflow (stmt, build_int_cst (size_type_node, len), ptr_qry,
- si, plus_one, rawmem);
+ tree tlen = build_int_cst (size_type_node, len);
+ maybe_warn_overflow (stmt, call_lhs, tlen, ptr_qry, si, plus_one, rawmem);
}
/* Handle a strlen call. If strlen of the argument is known, replace
@@ -2158,8 +2161,7 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
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);
+ gimplify_and_update_call_from_tree (gsi, rhs);
stmt = gsi_stmt (*gsi);
update_stmt (stmt);
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
@@ -2258,8 +2260,7 @@ handle_builtin_strlen (gimple_stmt_iterator *gsi)
}
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);
+ gimplify_and_update_call_from_tree (gsi, ret);
stmt = gsi_stmt (*gsi);
update_stmt (stmt);
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
@@ -2337,8 +2338,7 @@ handle_builtin_strchr (gimple_stmt_iterator *gsi)
TREE_TYPE (rhs)))
rhs = fold_convert_loc (loc, TREE_TYPE (lhs), rhs);
}
- if (!update_call_from_tree (gsi, rhs))
- gimplify_and_update_call_from_tree (gsi, rhs);
+ gimplify_and_update_call_from_tree (gsi, rhs);
stmt = gsi_stmt (*gsi);
update_stmt (stmt);
if (dump_file && (dump_flags & TDF_DETAILS) != 0)
@@ -2427,7 +2427,7 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
else if (idx < 0)
srclen = build_int_cst (size_type_node, ~idx);
- maybe_warn_overflow (stmt, srclen, ptr_qry, olddsi, true);
+ maybe_warn_overflow (stmt, false, srclen, ptr_qry, olddsi, true);
if (olddsi != NULL)
adjust_last_stmt (olddsi, stmt, false, ptr_qry);
@@ -2607,16 +2607,16 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
len = fold_convert_loc (loc, type, unshare_expr (srclen));
len = fold_build2_loc (loc, PLUS_EXPR, type, len, build_int_cst (type, 1));
- /* Set the no-warning bit on the transformed statement? */
- bool set_no_warning = false;
+ /* Disable warning for the transformed statement? */
+ opt_code no_warning_opt = no_warning;
- if (const strinfo *chksi = olddsi ? olddsi : dsi)
- if (si
- && check_bounds_or_overlap (stmt, chksi->ptr, si->ptr, NULL_TREE, len))
- {
- gimple_set_no_warning (stmt, true);
- set_no_warning = true;
- }
+ if (const strinfo *chksi = si ? olddsi ? olddsi : dsi : NULL)
+ {
+ no_warning_opt = check_bounds_or_overlap (stmt, chksi->ptr, si->ptr,
+ NULL_TREE, len);
+ if (no_warning_opt)
+ suppress_warning (stmt, no_warning_opt);
+ }
if (fn == NULL_TREE)
return;
@@ -2650,8 +2650,8 @@ handle_builtin_strcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
fprintf (dump_file, "not possible.\n");
- if (set_no_warning)
- gimple_set_no_warning (stmt, true);
+ if (no_warning_opt)
+ suppress_warning (stmt, no_warning_opt);
}
/* Check the size argument to the built-in forms of stpncpy and strncpy
@@ -2779,16 +2779,19 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
pointer_query *ptr_qry /* = NULL */)
{
gimple *stmt = gsi_stmt (gsi);
- if (gimple_no_warning_p (stmt))
+ if (warning_suppressed_p (stmt, OPT_Wstringop_truncation))
return false;
wide_int cntrange[2];
+ value_range r;
+ if (!get_range_query (cfun)->range_of_expr (r, cnt)
+ || r.varying_p ()
+ || r.undefined_p ())
+ return false;
- // FIXME: Use range_query instead of global ranges.
- enum value_range_kind rng = get_range_info (cnt, cntrange, cntrange + 1);
- if (rng == VR_RANGE)
- ;
- else if (rng == VR_ANTI_RANGE)
+ cntrange[0] = wi::to_wide (r.min ());
+ cntrange[1] = wi::to_wide (r.max ());
+ if (r.kind () == VR_ANTI_RANGE)
{
wide_int maxobjsize = wi::to_wide (TYPE_MAX_VALUE (ptrdiff_type_node));
@@ -2803,8 +2806,6 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
cntrange[0] = wi::zero (TYPE_PRECISION (TREE_TYPE (cnt)));
}
}
- else
- return false;
/* Negative value is the constant string length. If it's less than
the lower bound there is no truncation. Avoid calling get_stridx()
@@ -2967,13 +2968,13 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
if (lenrange[0] == cntrange[1] && cntrange[0] == cntrange[1])
return warning_n (callloc, OPT_Wstringop_truncation,
cntrange[0].to_uhwi (),
- "%G%qD output truncated before terminating "
+ "%qD output truncated before terminating "
"nul copying %E byte from a string of the "
"same length",
- "%G%qD output truncated before terminating nul "
+ "%qD output truncated before terminating nul "
"copying %E bytes from a string of the same "
"length",
- stmt, func, cnt);
+ func, cnt);
else if (!cat_dstlen_bounded)
{
if (wi::geu_p (lenrange[0], cntrange[1]))
@@ -2983,16 +2984,16 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
if (cntrange[0] == cntrange[1])
return warning_n (callloc, OPT_Wstringop_truncation,
cntrange[0].to_uhwi (),
- "%G%qD output truncated copying %E byte "
+ "%qD output truncated copying %E byte "
"from a string of length %wu",
- "%G%qD output truncated copying %E bytes "
+ "%qD output truncated copying %E bytes "
"from a string of length %wu",
- stmt, func, cnt, lenrange[0].to_uhwi ());
+ func, cnt, lenrange[0].to_uhwi ());
return warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD output truncated copying between %wu "
+ "%qD output truncated copying between %wu "
"and %wu bytes from a string of length %wu",
- stmt, func, cntrange[0].to_uhwi (),
+ func, cntrange[0].to_uhwi (),
cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
}
else if (wi::geu_p (lenrange[1], cntrange[1]))
@@ -3002,16 +3003,16 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
if (cntrange[0] == cntrange[1])
return warning_n (callloc, OPT_Wstringop_truncation,
cntrange[0].to_uhwi (),
- "%G%qD output may be truncated copying %E "
+ "%qD output may be truncated copying %E "
"byte from a string of length %wu",
- "%G%qD output may be truncated copying %E "
+ "%qD output may be truncated copying %E "
"bytes from a string of length %wu",
- stmt, func, cnt, lenrange[1].to_uhwi ());
+ func, cnt, lenrange[1].to_uhwi ());
return warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD output may be truncated copying between "
+ "%qD output may be truncated copying between "
"%wu and %wu bytes from a string of length %wu",
- stmt, func, cntrange[0].to_uhwi (),
+ func, cntrange[0].to_uhwi (),
cntrange[1].to_uhwi (), lenrange[1].to_uhwi ());
}
}
@@ -3025,9 +3026,9 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
the lower bound of the specified count but shorter than the
upper bound the copy may (but need not) be truncated. */
return warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD output may be truncated copying between "
+ "%qD output may be truncated copying between "
"%wu and %wu bytes from a string of length %wu",
- stmt, func, cntrange[0].to_uhwi (),
+ func, cntrange[0].to_uhwi (),
cntrange[1].to_uhwi (), lenrange[0].to_uhwi ());
}
}
@@ -3054,8 +3055,8 @@ maybe_diag_stxncpy_trunc (gimple_stmt_iterator gsi, tree src, tree cnt,
if (cntrange[0] == cntrange[1])
return warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD specified bound %E equals destination size",
- stmt, func, cnt);
+ "%qD specified bound %E equals destination size",
+ func, cnt);
}
return false;
@@ -3138,9 +3139,10 @@ handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
else
srclenp1 = NULL_TREE;
- if (check_bounds_or_overlap (stmt, dst, src, dstlenp1, srclenp1))
+ opt_code opt = check_bounds_or_overlap (stmt, dst, src, dstlenp1, srclenp1);
+ if (opt != no_warning)
{
- gimple_set_no_warning (stmt, true);
+ suppress_warning (stmt, opt);
return;
}
@@ -3151,7 +3153,7 @@ handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
if (!pss || pss->first <= 0)
{
if (maybe_diag_stxncpy_trunc (*gsi, src, len))
- gimple_set_no_warning (stmt, true);
+ suppress_warning (stmt, OPT_Wstringop_truncation);
return;
}
@@ -3178,9 +3180,9 @@ handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
&& sisrc == silen
&& is_strlen_related_p (src, len)
&& warning_at (callloc, OPT_Wstringop_truncation,
- "%G%qD output truncated before terminating nul "
+ "%qD output truncated before terminating nul "
"copying as many bytes from a string as its length",
- stmt, func))
+ func))
warned = true;
else if ((append_p || !dstsize || len == dstlenp1)
&& silen && is_strlen_related_p (src, silen->ptr))
@@ -3188,12 +3190,12 @@ handle_builtin_stxncpy_strncat (bool append_p, gimple_stmt_iterator *gsi)
/* Issue -Wstringop-overflow when appending or when writing into
a destination of a known size. Otherwise, when copying into
a destination of an unknown size, it's truncation. */
- int opt = (append_p || dstsize
- ? OPT_Wstringop_overflow_ : OPT_Wstringop_truncation);
+ opt_code opt = (append_p || dstsize
+ ? OPT_Wstringop_overflow_ : OPT_Wstringop_truncation);
warned = warning_at (callloc, opt,
- "%G%qD specified bound depends on the length "
+ "%qD specified bound depends on the length "
"of the source argument",
- stmt, func);
+ func);
}
if (warned)
{
@@ -3230,7 +3232,7 @@ handle_builtin_memcpy (enum built_in_function bcode, gimple_stmt_iterator *gsi,
if (olddsi != NULL
&& !integer_zerop (len))
{
- maybe_warn_overflow (stmt, len, ptr_qry, olddsi, false, true);
+ maybe_warn_overflow (stmt, false, len, ptr_qry, olddsi, false, true);
adjust_last_stmt (olddsi, stmt, false, ptr_qry);
}
@@ -3430,8 +3432,8 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
srclen = get_string_length (si);
}
- /* Set the no-warning bit on the transformed statement? */
- bool set_no_warning = false;
+ /* Disable warning for the transformed statement? */
+ opt_code no_warning_opt = no_warning;
if (dsi == NULL || get_string_length (dsi) == NULL_TREE)
{
@@ -3448,12 +3450,10 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
}
tree sptr = si && si->ptr ? si->ptr : src;
-
- if (check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE, slen))
- {
- gimple_set_no_warning (stmt, true);
- set_no_warning = true;
- }
+ no_warning_opt = check_bounds_or_overlap (stmt, dst, sptr, NULL_TREE,
+ slen);
+ if (no_warning_opt)
+ suppress_warning (stmt, no_warning_opt);
}
/* strcat (p, q) can be transformed into
@@ -3560,11 +3560,10 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
tree dstsize = fold_build2 (PLUS_EXPR, type, dstlen, one);
tree sptr = si && si->ptr ? si->ptr : src;
- if (check_bounds_or_overlap (stmt, dst, sptr, dstsize, srcsize))
- {
- gimple_set_no_warning (stmt, true);
- set_no_warning = true;
- }
+ no_warning_opt = check_bounds_or_overlap (stmt, dst, sptr, dstsize,
+ srcsize);
+ if (no_warning_opt)
+ suppress_warning (stmt, no_warning_opt);
}
tree len = NULL_TREE;
@@ -3630,8 +3629,8 @@ handle_builtin_strcat (enum built_in_function bcode, gimple_stmt_iterator *gsi,
else if (dump_file && (dump_flags & TDF_DETAILS) != 0)
fprintf (dump_file, "not possible.\n");
- if (set_no_warning)
- gimple_set_no_warning (stmt, true);
+ if (no_warning_opt)
+ suppress_warning (stmt, no_warning_opt);
}
/* Handle a call to an allocation function like alloca, malloc or calloc,
@@ -3698,7 +3697,8 @@ handle_builtin_memset (gimple_stmt_iterator *gsi, bool *zero_write,
tree memset_size = gimple_call_arg (memset_stmt, 2);
/* Check for overflow. */
- maybe_warn_overflow (memset_stmt, memset_size, ptr_qry, NULL, false, true);
+ maybe_warn_overflow (memset_stmt, false, memset_size, ptr_qry, NULL,
+ false, true);
/* Bail when there is no statement associated with the destination
(the statement may be null even when SI1->ALLOC is not). */
@@ -3926,13 +3926,12 @@ get_len_or_size (gimple *stmt, tree arg, int idx,
}
else if (TREE_CODE (si->nonzero_chars) == SSA_NAME)
{
- wide_int min, max;
- // FIXME: Use range_query instead of global ranges.
- value_range_kind rng = get_range_info (si->nonzero_chars, &min, &max);
- if (rng == VR_RANGE)
+ value_range r;
+ get_range_query (cfun)->range_of_expr (r, si->nonzero_chars);
+ if (r.kind () == VR_RANGE)
{
- lenrng[0] = min.to_uhwi ();
- lenrng[1] = max.to_uhwi ();
+ lenrng[0] = r.lower_bound ().to_uhwi ();
+ lenrng[1] = r.upper_bound ().to_uhwi ();
*nulterm = si->full_string_p;
}
}
@@ -4126,24 +4125,24 @@ maybe_warn_pointless_strcmp (gimple *stmt, HOST_WIDE_INT bound,
if (siz <= minlen && bound == -1)
warned = warning_at (stmt_loc, OPT_Wstring_compare,
(at_least
- ? G_("%G%qD of a string of length %wu or more and "
+ ? G_("%qD of a string of length %wu or more and "
"an array of size %wu evaluates to nonzero")
- : G_("%G%qD of a string of length %wu and an array "
+ : G_("%qD of a string of length %wu and an array "
"of size %wu evaluates to nonzero")),
- stmt, callee, minlen, siz);
+ callee, minlen, siz);
else if (!at_least && siz <= HOST_WIDE_INT_MAX)
{
if (len[0] != HOST_WIDE_INT_MAX && len[1] != HOST_WIDE_INT_MAX)
warned = warning_at (stmt_loc, OPT_Wstring_compare,
- "%G%qD of strings of length %wu and %wu "
+ "%qD of strings of length %wu and %wu "
"and bound of %wu evaluates to nonzero",
- stmt, callee, len[0], len[1], bound);
+ callee, len[0], len[1], bound);
else
warned = warning_at (stmt_loc, OPT_Wstring_compare,
- "%G%qD of a string of length %wu, an array "
+ "%qD of a string of length %wu, an array "
"of size %wu and bound of %wu evaluates to "
"nonzero",
- stmt, callee, minlen, siz, bound);
+ callee, minlen, siz, bound);
}
if (!warned)
@@ -4360,19 +4359,49 @@ handle_pointer_plus (gimple_stmt_iterator *gsi)
}
}
+/* Set LENRANGE to the number of nonzero bytes for a store of TYPE and
+ clear all flags. Return true on success and false on failure. */
+
+static bool
+nonzero_bytes_for_type (tree type, unsigned lenrange[3],
+ bool *nulterm, bool *allnul, bool *allnonnul)
+{
+ /* Use the size of the type of the expression as the size of the store,
+ and set the upper bound of the length range to that of the size.
+ Nothing is known about the contents so clear all flags. */
+ tree typesize = TYPE_SIZE_UNIT (type);
+ if (!type)
+ return false;
+
+ if (!tree_fits_uhwi_p (typesize))
+ return false;
+
+ unsigned HOST_WIDE_INT sz = tree_to_uhwi (typesize);
+ if (sz > UINT_MAX)
+ return false;
+
+ lenrange[2] = sz;
+ lenrange[1] = lenrange[2] ? lenrange[2] - 1 : 0;
+ lenrange[0] = 0;
+ *nulterm = false;
+ *allnul = false;
+ *allnonnul = false;
+ return true;
+}
+
static bool
count_nonzero_bytes_addr (tree, unsigned HOST_WIDE_INT, unsigned HOST_WIDE_INT,
unsigned [3], bool *, bool *, bool *,
range_query *, ssa_name_limit_t &);
-/* Determines the minimum and maximum number of leading non-zero bytes
- in the representation of EXP and set LENRANGE[0] and LENRANGE[1]
+/* Recursively determine the minimum and maximum number of leading nonzero
+ bytes in the representation of EXP and set LENRANGE[0] and LENRANGE[1]
to each.
Sets LENRANGE[2] to the total size of the access (which may be less
than LENRANGE[1] when what's being referenced by EXP is a pointer
rather than an array).
- Sets *NULTERM if the representation contains a zero byte, and sets
- *ALLNUL if all the bytes are zero.
+ Sets *NULTERM if the representation contains a zero byte, sets *ALLNUL
+ if all the bytes are zero, and *ALLNONNUL is all are nonzero.
OFFSET and NBYTES are the offset into the representation and
the size of the access to it determined from an ADDR_EXPR (i.e.,
a pointer) or MEM_REF or zero for other expressions.
@@ -4408,9 +4437,11 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
if (gimple_assign_single_p (stmt))
{
exp = gimple_assign_rhs1 (stmt);
- if (TREE_CODE (exp) != MEM_REF)
+ if (!DECL_P (exp)
+ && TREE_CODE (exp) != CONSTRUCTOR
+ && TREE_CODE (exp) != MEM_REF)
return false;
- /* Handle MEM_REF below. */
+ /* Handle DECLs, CONSTRUCTOR and MEM_REF below. */
}
else if (gimple_code (stmt) == GIMPLE_PHI)
{
@@ -4434,6 +4465,25 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
}
}
+ if (TREE_CODE (exp) == CONSTRUCTOR)
+ {
+ if (nbytes)
+ /* If NBYTES has already been determined by an outer MEM_REF
+ fail rather than overwriting it (this shouldn't happen). */
+ return false;
+
+ tree type = TREE_TYPE (exp);
+ tree size = TYPE_SIZE_UNIT (type);
+ if (!size || !tree_fits_uhwi_p (size))
+ return false;
+
+ unsigned HOST_WIDE_INT byte_size = tree_to_uhwi (size);
+ if (byte_size < offset)
+ return false;
+
+ nbytes = byte_size - offset;
+ }
+
if (TREE_CODE (exp) == MEM_REF)
{
if (nbytes)
@@ -4469,9 +4519,11 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
if (VAR_P (exp) || TREE_CODE (exp) == CONST_DECL)
{
- exp = ctor_for_folding (exp);
- if (!exp)
- return false;
+ /* If EXP can be folded into a constant use the result. Otherwise
+ proceed to use EXP to determine a range of the result. */
+ if (tree fold_exp = ctor_for_folding (exp))
+ if (fold_exp != error_mark_node)
+ exp = fold_exp;
}
const char *prep = NULL;
@@ -4519,7 +4571,8 @@ count_nonzero_bytes (tree exp, unsigned HOST_WIDE_INT offset,
}
if (!nbytes)
- return false;
+ return nonzero_bytes_for_type (TREE_TYPE (exp), lenrange,
+ nulterm, allnul, allnonnul);
/* Compute the number of leading nonzero bytes in the representation
and update the minimum and maximum. */
@@ -4682,14 +4735,19 @@ count_nonzero_bytes_addr (tree exp, unsigned HOST_WIDE_INT offset,
return true;
}
-/* Same as above except with an implicit SSA_NAME limit. RVALS is used
- to determine ranges of dynamically computed string lengths (the results
- of strlen). */
+/* Same as above except with an implicit SSA_NAME limit. When EXPR_OR_TYPE
+ is a type rather than an expression use its size to compute the range.
+ RVALS is used to determine ranges of dynamically computed string lengths
+ (the results of strlen). */
static bool
-count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
+count_nonzero_bytes (tree expr_or_type, unsigned lenrange[3], bool *nulterm,
bool *allnul, bool *allnonnul, range_query *rvals)
{
+ if (TYPE_P (expr_or_type))
+ return nonzero_bytes_for_type (expr_or_type, lenrange,
+ nulterm, allnul, allnonnul);
+
/* Set to optimistic values so the caller doesn't have to worry about
initializing these and to what. On success, the function will clear
these if it determines their values are different but being recursive
@@ -4700,7 +4758,8 @@ count_nonzero_bytes (tree exp, unsigned lenrange[3], bool *nulterm,
*allnonnul = true;
ssa_name_limit_t snlim;
- return count_nonzero_bytes (exp, 0, 0, lenrange, nulterm, allnul, allnonnul,
+ tree expr = expr_or_type;
+ return count_nonzero_bytes (expr, 0, 0, lenrange, nulterm, allnul, allnonnul,
rvals, snlim);
}
@@ -4714,11 +4773,29 @@ static bool
handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
pointer_query &ptr_qry)
{
- int idx = -1;
- strinfo *si = NULL;
gimple *stmt = gsi_stmt (*gsi);
- tree ssaname = NULL_TREE, lhs = gimple_assign_lhs (stmt);
- tree rhs = gimple_assign_rhs1 (stmt);
+ /* The LHS and RHS of the store. The RHS is null if STMT is a function
+ call. STORETYPE is the type of the store (determined from either
+ the RHS of the assignment statement or the LHS of a function call. */
+ tree lhs, rhs, storetype;
+ if (is_gimple_assign (stmt))
+ {
+ lhs = gimple_assign_lhs (stmt);
+ rhs = gimple_assign_rhs1 (stmt);
+ storetype = TREE_TYPE (rhs);
+ }
+ else if (is_gimple_call (stmt))
+ {
+ lhs = gimple_call_lhs (stmt);
+ rhs = NULL_TREE;
+ storetype = TREE_TYPE (lhs);
+ }
+ else
+ return true;
+
+ tree ssaname = NULL_TREE;
+ strinfo *si = NULL;
+ int idx = -1;
range_query *const rvals = ptr_qry.rvals;
@@ -4742,13 +4819,13 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
ssaname = TREE_OPERAND (lhs, 0);
else if (si == NULL || compare_nonzero_chars (si, offset, rvals) < 0)
{
- *zero_write = initializer_zerop (rhs);
+ *zero_write = rhs ? initializer_zerop (rhs) : false;
bool dummy;
unsigned lenrange[] = { UINT_MAX, 0, 0 };
- if (count_nonzero_bytes (rhs, lenrange, &dummy, &dummy, &dummy,
- rvals))
- maybe_warn_overflow (stmt, lenrange[2], ptr_qry);
+ if (count_nonzero_bytes (rhs ? rhs : storetype, lenrange,
+ &dummy, &dummy, &dummy, rvals))
+ maybe_warn_overflow (stmt, true, lenrange[2], ptr_qry);
return true;
}
@@ -4779,16 +4856,17 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
bool full_string_p;
const bool ranges_valid
- = count_nonzero_bytes (rhs, lenrange, &full_string_p,
+ = count_nonzero_bytes (rhs ? rhs : storetype, lenrange, &full_string_p,
&storing_all_zeros_p, &storing_all_nonzero_p,
rvals);
+
if (ranges_valid)
{
rhs_minlen = lenrange[0];
storing_nonzero_p = lenrange[1] > 0;
*zero_write = storing_all_zeros_p;
- maybe_warn_overflow (stmt, lenrange[2], ptr_qry);
+ maybe_warn_overflow (stmt, true, lenrange[2], ptr_qry);
}
else
{
@@ -4850,7 +4928,7 @@ handle_store (gimple_stmt_iterator *gsi, bool *zero_write,
&& storing_nonzero_p
&& lenrange[0] == lenrange[1]
&& lenrange[0] == lenrange[2]
- && TREE_CODE (TREE_TYPE (rhs)) == INTEGER_TYPE)
+ && TREE_CODE (storetype) == INTEGER_TYPE)
{
/* Handle a store of one or more non-nul characters that ends
before the terminating nul of the destination and so does
@@ -5131,8 +5209,19 @@ strlen_check_and_optimize_call (gimple_stmt_iterator *gsi, bool *zero_write,
if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
{
tree fntype = gimple_call_fntype (stmt);
- if (fntype && lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (fntype)))
- handle_alloc_call (BUILT_IN_NONE, gsi);
+ if (!fntype)
+ return true;
+
+ if (lookup_attribute ("alloc_size", TYPE_ATTRIBUTES (fntype)))
+ {
+ handle_alloc_call (BUILT_IN_NONE, gsi);
+ return true;
+ }
+
+ if (tree lhs = gimple_call_lhs (stmt))
+ handle_assign (gsi, lhs, zero_write, ptr_qry);
+
+ /* Proceed to handle user-defined formatting functions. */
}
/* When not optimizing we must be checking printf calls which
@@ -5304,17 +5393,13 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
/* Reading a character before the final '\0'
character. Just set the value range to ~[0, 0]
if we don't have anything better. */
- wide_int min, max;
- signop sign = TYPE_SIGN (lhs_type);
- int prec = TYPE_PRECISION (lhs_type);
- // FIXME: Use range_query instead of global ranges.
- value_range_kind vr = get_range_info (lhs, &min, &max);
- if (vr == VR_VARYING
- || (vr == VR_RANGE
- && min == wi::min_value (prec, sign)
- && max == wi::max_value (prec, sign)))
- set_range_info (lhs, VR_ANTI_RANGE,
- wi::zero (prec), wi::zero (prec));
+ value_range r;
+ if (!get_range_query (cfun)->range_of_expr (r, lhs)
+ || r.varying_p ())
+ {
+ r.set_nonzero (lhs_type);
+ set_range_info (lhs, r);
+ }
}
}
}
@@ -5352,6 +5437,48 @@ handle_integral_assign (gimple_stmt_iterator *gsi, bool *cleanup_eh,
}
}
+/* Handle assignment statement at *GSI to LHS. Set *ZERO_WRITE if
+ the assignent stores all zero bytes.. */
+
+static bool
+handle_assign (gimple_stmt_iterator *gsi, tree lhs, bool *zero_write,
+ pointer_query &ptr_qry)
+{
+ tree type = TREE_TYPE (lhs);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+
+ bool is_char_store = is_char_type (type);
+ if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
+ {
+ /* To consider stores into char objects via integer types other
+ than char but not those to non-character objects, determine
+ the type of the destination rather than just the type of
+ the access. */
+ for (int i = 0; i != 2; ++i)
+ {
+ tree ref = TREE_OPERAND (lhs, i);
+ type = TREE_TYPE (ref);
+ if (TREE_CODE (type) == POINTER_TYPE)
+ type = TREE_TYPE (type);
+ if (TREE_CODE (type) == ARRAY_TYPE)
+ type = TREE_TYPE (type);
+ if (is_char_type (type))
+ {
+ is_char_store = true;
+ break;
+ }
+ }
+ }
+
+ /* Handle a single or multibyte assignment. */
+ if (is_char_store && !handle_store (gsi, zero_write, ptr_qry))
+ return false;
+
+ return true;
+}
+
+
/* Attempt to check for validity of the performed access a single statement
at *GSI using string length knowledge, and to optimize it.
If the given basic block needs clean-up of EH, CLEANUP_EH is set to
@@ -5397,38 +5524,8 @@ check_and_optimize_stmt (gimple_stmt_iterator *gsi, bool *cleanup_eh,
/* Handle assignment to a character. */
handle_integral_assign (gsi, cleanup_eh, ptr_qry.rvals);
else if (TREE_CODE (lhs) != SSA_NAME && !TREE_SIDE_EFFECTS (lhs))
- {
- tree type = TREE_TYPE (lhs);
- if (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
-
- bool is_char_store = is_char_type (type);
- if (!is_char_store && TREE_CODE (lhs) == MEM_REF)
- {
- /* To consider stores into char objects via integer types
- other than char but not those to non-character objects,
- determine the type of the destination rather than just
- the type of the access. */
- for (int i = 0; i != 2; ++i)
- {
- tree ref = TREE_OPERAND (lhs, i);
- type = TREE_TYPE (ref);
- if (TREE_CODE (type) == POINTER_TYPE)
- type = TREE_TYPE (type);
- if (TREE_CODE (type) == ARRAY_TYPE)
- type = TREE_TYPE (type);
- if (is_char_type (type))
- {
- is_char_store = true;
- break;
- }
- }
- }
-
- /* Handle a single or multibyte assignment. */
- if (is_char_store && !handle_store (gsi, &zero_write, ptr_qry))
- return false;
- }
+ if (!handle_assign (gsi, lhs, &zero_write, ptr_qry))
+ return false;
}
else if (gcond *cond = dyn_cast<gcond *> (stmt))
{
@@ -5691,27 +5788,7 @@ printf_strlen_execute (function *fun, bool warn_only)
walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
if (dump_file && (dump_flags & TDF_DETAILS))
- {
- unsigned nused = 0;
- unsigned nidxs = walker.ptr_qry.var_cache->indices.length ();
- for (unsigned i = 0; i != nidxs; ++i)
- if (walker.ptr_qry.var_cache->indices[i])
- ++nused;
-
- fprintf (dump_file, "pointer_query counters\n"
- " index cache size: %u\n"
- " utilization: %u%%\n"
- " access cache size: %u\n"
- " hits: %u\n"
- " misses: %u\n"
- " failures: %u\n"
- " max_depth: %u\n",
- nidxs,
- nidxs == 0 ? 0 : (nused * 100) / nidxs,
- walker.ptr_qry.var_cache->access_refs.length (),
- walker.ptr_qry.hits, walker.ptr_qry.misses,
- walker.ptr_qry.failures, walker.ptr_qry.max_depth);
- }
+ walker.ptr_qry.dump (dump_file);
ssa_ver_to_stridx.release ();
strinfo_pool.release ();