gcc/ChangeLog:
* gimple-ssa-warn-access.cc (check_memop_access): Remove template and
make a member function.
(maybe_check_dealloc_call): Make a pass_waccess member function.
(class pass_waccess): Add and rename members.
(pass_waccess::pass_waccess): Adjust to name change.
(pass_waccess::~pass_waccess): Same.
(check_alloca): Make a member function.
(check_alloc_size_call): Same.
(check_strcat): Same.
(check_strncat): Same.
(check_stxcpy): Same.
(check_stxncpy): Same.
(check_strncmp): Same.
(maybe_warn_rdwr_sizes): Rename...
(pass_waccess::maybe_check_access_sizes): ...to this.
(pass_waccess::check_call): Adjust to name changes.
(pass_waccess::maybe_check_dealloc_call): Make a pass_waccess member
function.
(pass_waccess::execute): Adjust to name changes.
* gimple-ssa-warn-access.h (check_memop_access): Remove.
* pointer-query.cc (access_ref::phi): Handle null pointer.
(access_ref::inform_access): Same.
(pointer_query::put_ref): Modify a cached value, not a copy of it.
(pointer_query::dump): New function.
(compute_objsize_r): Avoid overwriting access_ref::bndrng. Cache
more results.
* pointer-query.h (pointer_query::dump): Declare.
* tree-ssa-strlen.c (printf_strlen_execute): Factor code out into
pointer_query::put_ref.
gcc/testsuite/ChangeLog:
* gcc.dg/Wstringop-overflow-73.c: New test.
@@ -1511,41 +1511,6 @@ check_access (tree expr, tree dstwrite,
mode, pad);
}
-/* Helper to determine and check the sizes of the source and the destination
- of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls. EXP is the
- call expression, DEST is the destination argument, SRC is the source
- argument or null, and LEN is the number of bytes. Use Object Size type-0
- regardless of the OPT_Wstringop_overflow_ setting. Return true on success
- (no overflow or invalid sizes), false otherwise. */
-
-template <class GimpleOrTree>
-static bool
-check_memop_access (GimpleOrTree expr, tree dest, tree src, tree size)
-{
- /* For functions like memset and memcpy that operate on raw memory
- try to determine the size of the largest source and destination
- object using type-0 Object Size regardless of the object size
- type specified by the option. */
- access_data data (expr, access_read_write);
- tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
- tree dstsize = compute_objsize (dest, 0, &data.dst);
-
- return check_access (expr, size, /*maxread=*/NULL_TREE,
- srcsize, dstsize, data.mode, &data);
-}
-
-bool
-check_memop_access (gimple *stmt, tree dest, tree src, tree size)
-{
- return check_memop_access<gimple *>(stmt, dest, src, size);
-}
-
-bool
-check_memop_access (tree expr, tree dest, tree src, tree size)
-{
- return check_memop_access<tree>(expr, dest, src, size);
-}
-
/* A convenience wrapper for check_access above to check access
by a read-only function like puts. */
@@ -2093,135 +2058,6 @@ warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
return true;
}
-/* Issue a warning if a deallocation function such as free, realloc,
- or C++ operator delete is called with an argument not returned by
- a matching allocation function such as malloc or the corresponding
- form of C++ operatorn new. */
-
-static void
-maybe_check_dealloc_call (gcall *call)
-{
- tree fndecl = gimple_call_fndecl (call);
- if (!fndecl)
- return;
-
- unsigned argno = fndecl_dealloc_argno (fndecl);
- if ((unsigned) call_nargs (call) <= argno)
- return;
-
- tree ptr = gimple_call_arg (call, argno);
- if (integer_zerop (ptr))
- return;
-
- access_ref aref;
- if (!compute_objsize (ptr, 0, &aref))
- return;
-
- tree ref = aref.ref;
- if (integer_zerop (ref))
- return;
-
- tree dealloc_decl = fndecl;
- location_t loc = gimple_location (call);
-
- if (DECL_P (ref) || EXPR_P (ref))
- {
- /* Diagnose freeing a declared object. */
- if (aref.ref_declared ()
- && warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on unallocated object %qD",
- dealloc_decl, ref))
- {
- inform (get_location (ref), "declared here");
- return;
- }
-
- /* Diagnose freeing a pointer that includes a positive offset.
- Such a pointer cannot refer to the beginning of an allocated
- object. A negative offset may refer to it. */
- if (aref.sizrng[0] != aref.sizrng[1]
- && warn_dealloc_offset (loc, call, aref))
- return;
- }
- else if (CONSTANT_CLASS_P (ref))
- {
- if (warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on a pointer to an unallocated "
- "object %qE", dealloc_decl, ref))
- {
- if (TREE_CODE (ptr) == SSA_NAME)
- {
- gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
- if (is_gimple_assign (def_stmt))
- {
- location_t loc = gimple_location (def_stmt);
- inform (loc, "assigned here");
- }
- }
- return;
- }
- }
- else if (TREE_CODE (ref) == SSA_NAME)
- {
- /* Also warn if the pointer argument refers to the result
- of an allocation call like alloca or VLA. */
- gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
- if (is_gimple_call (def_stmt))
- {
- bool warned = false;
- if (gimple_call_alloc_p (def_stmt))
- {
- if (matching_alloc_calls_p (def_stmt, dealloc_decl))
- {
- if (warn_dealloc_offset (loc, call, aref))
- return;
- }
- else
- {
- tree alloc_decl = gimple_call_fndecl (def_stmt);
- const opt_code opt =
- (DECL_IS_OPERATOR_NEW_P (alloc_decl)
- || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
- ? OPT_Wmismatched_new_delete
- : OPT_Wmismatched_dealloc);
- warned = warning_at (loc, opt,
- "%qD called on pointer returned "
- "from a mismatched allocation "
- "function", dealloc_decl);
- }
- }
- else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
- || gimple_call_builtin_p (def_stmt,
- BUILT_IN_ALLOCA_WITH_ALIGN))
- warned = warning_at (loc, OPT_Wfree_nonheap_object,
- "%qD called on pointer to "
- "an unallocated object",
- dealloc_decl);
- else if (warn_dealloc_offset (loc, call, aref))
- return;
-
- if (warned)
- {
- tree fndecl = gimple_call_fndecl (def_stmt);
- inform (gimple_location (def_stmt),
- "returned from %qD", fndecl);
- return;
- }
- }
- else if (gimple_nop_p (def_stmt))
- {
- ref = SSA_NAME_VAR (ref);
- /* Diagnose freeing a pointer that includes a positive offset. */
- if (TREE_CODE (ref) == PARM_DECL
- && !aref.deref
- && aref.sizrng[0] != aref.sizrng[1]
- && aref.offrng[0] > 0 && aref.offrng[1] > 0
- && warn_dealloc_offset (loc, call, aref))
- return;
- }
- }
-}
-
namespace {
const pass_data pass_data_waccess = {
@@ -2249,6 +2085,11 @@ class pass_waccess : public gimple_opt_pass
virtual bool gate (function *);
virtual unsigned int execute (function *);
+private:
+ /* Not copyable or assignable. */
+ pass_waccess (pass_waccess &) = delete;
+ void operator= (pass_waccess &) = delete;
+
/* Check a call to a built-in function. */
bool check_builtin (gcall *);
@@ -2259,17 +2100,25 @@ class pass_waccess : public gimple_opt_pass
void check (basic_block);
/* Check a call to a function. */
- void check (gcall *);
+ void check (gcall *);
-private:
- /* Not copyable or assignable. */
- pass_waccess (pass_waccess &) = delete;
- void operator= (pass_waccess &) = delete;
+ /* Check a call to the named built-in function. */
+ void check_alloca (gcall *);
+ void check_alloc_size_call (gcall *);
+ void check_strcat (gcall *);
+ void check_strncat (gcall *);
+ void check_stxcpy (gcall *);
+ void check_stxncpy (gcall *);
+ void check_strncmp (gcall *);
+ void check_memop_access (gimple *, tree, tree, tree);
+
+ void maybe_check_dealloc_call (gcall *);
+ void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
/* A pointer_query object and its cache to store information about
pointers and their targets in. */
- pointer_query ptr_qry;
- pointer_query::cache_type var_cache;
+ pointer_query m_ptr_qry;
+ pointer_query::cache_type m_var_cache;
gimple_ranger *m_ranger;
};
@@ -2278,8 +2127,8 @@ private:
pass_waccess::pass_waccess (gcc::context *ctxt)
: gimple_opt_pass (pass_data_waccess, ctxt),
- ptr_qry (m_ranger, &var_cache),
- var_cache (),
+ m_ptr_qry (m_ranger, &m_var_cache),
+ m_var_cache (),
m_ranger ()
{
}
@@ -2288,7 +2137,7 @@ pass_waccess::pass_waccess (gcc::context *ctxt)
pass_waccess::~pass_waccess ()
{
- ptr_qry.flush_cache ();
+ m_ptr_qry.flush_cache ();
}
/* Return true when any checks performed by the pass are enabled. */
@@ -2476,8 +2325,8 @@ maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
/* Check a call to an alloca function for an excessive size. */
-static void
-check_alloca (gimple *stmt)
+void
+pass_waccess::check_alloca (gcall *stmt)
{
if ((warn_vla_limit >= HOST_WIDE_INT_MAX
&& warn_alloc_size_limit < warn_vla_limit)
@@ -2497,8 +2346,8 @@ check_alloca (gimple *stmt)
/* Check a call to an allocation function for an excessive size. */
-static void
-check_alloc_size_call (gimple *stmt)
+void
+pass_waccess::check_alloc_size_call (gcall *stmt)
{
if (gimple_call_num_args (stmt) < 1)
/* Avoid invalid calls to functions without a prototype. */
@@ -2547,8 +2396,8 @@ check_alloc_size_call (gimple *stmt)
/* Check a call STMT to strcat() for overflow and warn if it does. */
-static void
-check_strcat (gimple *stmt)
+void
+pass_waccess::check_strcat (gcall *stmt)
{
if (!warn_stringop_overflow && !warn_stringop_overread)
return;
@@ -2563,8 +2412,8 @@ check_strcat (gimple *stmt)
access_data data (stmt, access_read_write, NULL_TREE, true,
NULL_TREE, true);
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
- compute_objsize (src, ost, &data.src);
- tree destsize = compute_objsize (dest, ost, &data.dst);
+ compute_objsize (src, ost, &data.src, &m_ptr_qry);
+ tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
src, destsize, data.mode, &data);
@@ -2572,8 +2421,8 @@ check_strcat (gimple *stmt)
/* Check a call STMT to strcat() for overflow and warn if it does. */
-static void
-check_strncat (gimple *stmt)
+void
+pass_waccess::check_strncat (gcall *stmt)
{
if (!warn_stringop_overflow && !warn_stringop_overread)
return;
@@ -2605,7 +2454,8 @@ check_strncat (gimple *stmt)
/* Try to verify that the destination is big enough for the shortest
string. First try to determine the size of the destination object
into which the source is being copied. */
- tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
+ const int ost = warn_stringop_overflow - 1;
+ tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
/* Add one for the terminating nul. */
tree srclen = (maxlen
@@ -2640,8 +2490,8 @@ check_strncat (gimple *stmt)
/* Check a call STMT to stpcpy() or strcpy() for overflow and warn
if it does. */
-static void
-check_stxcpy (gimple *stmt)
+void
+pass_waccess::check_stxcpy (gcall *stmt)
{
tree dst = call_arg (stmt, 0);
tree src = call_arg (stmt, 1);
@@ -2661,8 +2511,8 @@ check_stxcpy (gimple *stmt)
access_data data (stmt, access_read_write, NULL_TREE, true,
NULL_TREE, true);
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
- compute_objsize (src, ost, &data.src);
- tree dstsize = compute_objsize (dst, ost, &data.dst);
+ compute_objsize (src, ost, &data.src, &m_ptr_qry);
+ tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
check_access (stmt, /*dstwrite=*/ NULL_TREE,
/*maxread=*/ NULL_TREE, /*srcstr=*/ src,
dstsize, data.mode, &data);
@@ -2678,8 +2528,8 @@ check_stxcpy (gimple *stmt)
/* Check a call STMT to stpncpy() or strncpy() for overflow and warn
if it does. */
-static void
-check_stxncpy (gimple *stmt)
+void
+pass_waccess::check_stxncpy (gcall *stmt)
{
if (!warn_stringop_overflow)
return;
@@ -2691,8 +2541,8 @@ check_stxncpy (gimple *stmt)
access_data data (stmt, access_read_write, len, true, len, true);
const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
- compute_objsize (src, ost, &data.src);
- tree dstsize = compute_objsize (dst, ost, &data.dst);
+ compute_objsize (src, ost, &data.src, &m_ptr_qry);
+ tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
check_access (stmt, /*dstwrite=*/len,
/*maxread=*/len, src, dstsize, data.mode, &data);
@@ -2701,8 +2551,8 @@ check_stxncpy (gimple *stmt)
/* Check a call STMT to stpncpy() or strncpy() for overflow and warn
if it does. */
-static void
-check_strncmp (gimple *stmt)
+void
+pass_waccess::check_strncmp (gcall *stmt)
{
if (!warn_stringop_overread)
return;
@@ -2757,8 +2607,8 @@ check_strncmp (gimple *stmt)
/* compute_objsize almost never fails (and ultimately should never
fail). Don't bother to handle the rare case when it does. */
- if (!compute_objsize (arg1, 1, &adata1.src)
- || !compute_objsize (arg2, 1, &adata2.src))
+ if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry)
+ || !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
return;
/* Compute the size of the remaining space in each array after
@@ -2792,6 +2642,29 @@ check_strncmp (gimple *stmt)
}
}
+/* Determine and check the sizes of the source and the destination
+ of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls. STMT is
+ the call statement, DEST is the destination argument, SRC is the source
+ argument or null, and SIZE is the number of bytes being accessed. Use
+ Object Size type-0 regardless of the OPT_Wstringop_overflow_ setting.
+ Return true on success (no overflow or invalid sizes), false otherwise. */
+
+void
+pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
+{
+ /* For functions like memset and memcpy that operate on raw memory
+ try to determine the size of the largest source and destination
+ object using type-0 Object Size regardless of the object size
+ type specified by the option. */
+ access_data data (stmt, access_read_write);
+ tree srcsize
+ = src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE;
+ tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry);
+
+ check_access (stmt, size, /*maxread=*/NULL_TREE,
+ srcsize, dstsize, data.mode, &data);
+}
+
/* Check call STMT to a built-in function for invalid accesses. Return
true if a call has been handled. */
@@ -2950,8 +2823,9 @@ append_attrname (const std::pair<int, attr_access> &access,
arguments and diagnose past-the-end accesses and related problems
in the function call EXP. */
-static void
-maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
+void
+pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
+ gimple *stmt)
{
auto_diagnostic_group adg;
@@ -3142,7 +3016,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
NULL_TREE, false);
access_ref* const pobj = (access.second.mode == access_write_only
? &data.dst : &data.src);
- tree objsize = compute_objsize (ptr, 1, pobj);
+ tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
/* The size of the destination or source object. */
tree dstsize = NULL_TREE, srcsize = NULL_TREE;
@@ -3258,7 +3132,7 @@ pass_waccess::check_call (gcall *stmt)
/* Check attribute access arguments. */
tree fndecl = gimple_call_fndecl (stmt);
- maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, stmt);
+ maybe_check_access_sizes (&rdwr_idx, fndecl, fntype, stmt);
check_alloc_size_call (stmt);
return true;
@@ -3276,6 +3150,138 @@ check_nonstring_args (gcall *stmt)
maybe_warn_nonstring_arg (fndecl, stmt);
}
+/* Issue a warning if a deallocation function such as free, realloc,
+ or C++ operator delete is called with an argument not returned by
+ a matching allocation function such as malloc or the corresponding
+ form of C++ operatorn new. */
+
+void
+pass_waccess::maybe_check_dealloc_call (gcall *call)
+{
+ tree fndecl = gimple_call_fndecl (call);
+ if (!fndecl)
+ return;
+
+ unsigned argno = fndecl_dealloc_argno (fndecl);
+ if ((unsigned) call_nargs (call) <= argno)
+ return;
+
+ tree ptr = gimple_call_arg (call, argno);
+ if (integer_zerop (ptr))
+ return;
+
+ access_ref aref;
+ if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
+ return;
+
+ tree ref = aref.ref;
+ if (integer_zerop (ref))
+ return;
+
+ tree dealloc_decl = fndecl;
+ location_t loc = gimple_location (call);
+
+ if (DECL_P (ref) || EXPR_P (ref))
+ {
+ /* Diagnose freeing a declared object. */
+ if (aref.ref_declared ()
+ && warning_at (loc, OPT_Wfree_nonheap_object,
+ "%qD called on unallocated object %qD",
+ dealloc_decl, ref))
+ {
+ inform (get_location (ref), "declared here");
+ return;
+ }
+
+ /* Diagnose freeing a pointer that includes a positive offset.
+ Such a pointer cannot refer to the beginning of an allocated
+ object. A negative offset may refer to it. */
+ if (aref.sizrng[0] != aref.sizrng[1]
+ && warn_dealloc_offset (loc, call, aref))
+ return;
+ }
+ else if (CONSTANT_CLASS_P (ref))
+ {
+ if (warning_at (loc, OPT_Wfree_nonheap_object,
+ "%qD called on a pointer to an unallocated "
+ "object %qE", dealloc_decl, ref))
+ {
+ if (TREE_CODE (ptr) == SSA_NAME)
+ {
+ gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
+ if (is_gimple_assign (def_stmt))
+ {
+ location_t loc = gimple_location (def_stmt);
+ inform (loc, "assigned here");
+ }
+ }
+ return;
+ }
+ }
+ else if (TREE_CODE (ref) == SSA_NAME)
+ {
+ /* Also warn if the pointer argument refers to the result
+ of an allocation call like alloca or VLA. */
+ gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+ if (!def_stmt)
+ return;
+
+ if (is_gimple_call (def_stmt))
+ {
+ bool warned = false;
+ if (gimple_call_alloc_p (def_stmt))
+ {
+ if (matching_alloc_calls_p (def_stmt, dealloc_decl))
+ {
+ if (warn_dealloc_offset (loc, call, aref))
+ return;
+ }
+ else
+ {
+ tree alloc_decl = gimple_call_fndecl (def_stmt);
+ const opt_code opt =
+ (DECL_IS_OPERATOR_NEW_P (alloc_decl)
+ || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
+ ? OPT_Wmismatched_new_delete
+ : OPT_Wmismatched_dealloc);
+ warned = warning_at (loc, opt,
+ "%qD called on pointer returned "
+ "from a mismatched allocation "
+ "function", dealloc_decl);
+ }
+ }
+ else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
+ || gimple_call_builtin_p (def_stmt,
+ BUILT_IN_ALLOCA_WITH_ALIGN))
+ warned = warning_at (loc, OPT_Wfree_nonheap_object,
+ "%qD called on pointer to "
+ "an unallocated object",
+ dealloc_decl);
+ else if (warn_dealloc_offset (loc, call, aref))
+ return;
+
+ if (warned)
+ {
+ tree fndecl = gimple_call_fndecl (def_stmt);
+ inform (gimple_location (def_stmt),
+ "returned from %qD", fndecl);
+ return;
+ }
+ }
+ else if (gimple_nop_p (def_stmt))
+ {
+ ref = SSA_NAME_VAR (ref);
+ /* Diagnose freeing a pointer that includes a positive offset. */
+ if (TREE_CODE (ref) == PARM_DECL
+ && !aref.deref
+ && aref.sizrng[0] != aref.sizrng[1]
+ && aref.offrng[0] > 0 && aref.offrng[1] > 0
+ && warn_dealloc_offset (loc, call, aref))
+ return;
+ }
+ }
+}
+
/* Check call STMT for invalid accesses. */
void
@@ -3317,8 +3323,15 @@ pass_waccess::execute (function *fun)
FOR_EACH_BB_FN (bb, fun)
check (bb);
- /* Release the ranger instance and replace it with a global ranger. */
+ if (dump_file)
+ m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) != 0);
+
+ m_ptr_qry.flush_cache ();
+
+ /* Release the ranger instance and replace it with a global ranger.
+ Also reset the pointer since calling disable_ranger() deletes it. */
disable_ranger (fun);
+ m_ranger = NULL;
return 0;
}
@@ -45,7 +45,6 @@ class access_data;
extern bool check_access (tree, tree, tree, tree, tree, access_mode,
const access_data * = NULL);
-extern bool check_memop_access (tree, tree, tree, tree);
extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
@@ -34,10 +34,13 @@
#include "stringpool.h"
#include "attribs.h"
#include "gimple-fold.h"
+#include "gimple-ssa.h"
#include "intl.h"
#include "attr-fnspec.h"
#include "gimple-range.h"
#include "pointer-query.h"
+#include "tree-pretty-print.h"
+#include "tree-ssanames.h"
static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
pointer_query *);
@@ -628,7 +631,7 @@ access_ref::phi () const
return NULL;
gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
- if (gimple_code (def_stmt) != GIMPLE_PHI)
+ if (!def_stmt || gimple_code (def_stmt) != GIMPLE_PHI)
return NULL;
return as_a <gphi *> (def_stmt);
@@ -1042,6 +1045,9 @@ access_ref::inform_access (access_mode mode) const
if (TREE_CODE (ref) == SSA_NAME)
{
gimple *stmt = SSA_NAME_DEF_STMT (ref);
+ if (!stmt)
+ return;
+
if (is_gimple_call (stmt))
{
loc = gimple_location (stmt);
@@ -1343,7 +1349,7 @@ pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
if (var_cache->access_refs.length () <= cache_idx)
var_cache->access_refs.safe_grow_cleared (cache_idx + 1);
- access_ref cache_ref = var_cache->access_refs[cache_idx - 1];
+ access_ref &cache_ref = var_cache->access_refs[cache_idx];
if (cache_ref.ref)
{
gcc_checking_assert (cache_ref.ref == ref.ref);
@@ -1364,6 +1370,102 @@ pointer_query::flush_cache ()
var_cache->access_refs.release ();
}
+/* Dump statistics and, optionally, cache contents to DUMP_FILE. */
+
+void
+pointer_query::dump (FILE *dump_file, bool contents /* = false */)
+{
+ unsigned nused = 0, nrefs = 0;
+ unsigned nidxs = var_cache->indices.length ();
+ for (unsigned i = 0; i != nidxs; ++i)
+ {
+ unsigned ari = var_cache->indices[i];
+ if (!ari)
+ continue;
+
+ ++nused;
+
+ const access_ref &aref = var_cache->access_refs[ari];
+ if (!aref.ref)
+ continue;
+
+ ++nrefs;
+ }
+
+ fprintf (dump_file, "pointer_query counters:\n"
+ " index cache size: %u\n"
+ " index entries: %u\n"
+ " access cache size: %u\n"
+ " access entries: %u\n"
+ " hits: %u\n"
+ " misses: %u\n"
+ " failures: %u\n"
+ " max_depth: %u\n",
+ nidxs, nused,
+ var_cache->access_refs.length (), nrefs,
+ hits, misses, failures, max_depth);
+
+ if (!contents || !nidxs)
+ return;
+
+ fputs ("\npointer_query cache contents:\n", dump_file);
+
+ for (unsigned i = 0; i != nidxs; ++i)
+ {
+ unsigned ari = var_cache->indices[i];
+ if (!ari)
+ continue;
+
+ const access_ref &aref = var_cache->access_refs[ari];
+ if (!aref.ref)
+ continue;
+
+ /* The level-1 cache index corresponds to the SSA_NAME_VERSION
+ shifted left by one and ORed with the Object Size Type in
+ the lowest bit. Print the two separately. */
+ unsigned ver = i >> 1;
+ unsigned ost = i & 1;
+
+ fprintf (dump_file, " %u.%u[%u]: ", ver, ost, ari);
+ if (tree name = ssa_name (ver))
+ {
+ print_generic_expr (dump_file, name);
+ fputs (" = ", dump_file);
+ }
+ else
+ fprintf (dump_file, " _%u = ", ver);
+
+ if (gphi *phi = aref.phi ())
+ {
+ fputs ("PHI <", dump_file);
+ unsigned nargs = gimple_phi_num_args (phi);
+ for (unsigned i = 0; i != nargs; ++i)
+ {
+ tree arg = gimple_phi_arg_def (phi, i);
+ print_generic_expr (dump_file, arg);
+ if (i + 1 < nargs)
+ fputs (", ", dump_file);
+ }
+ fputc ('>', dump_file);
+ }
+ else
+ print_generic_expr (dump_file, aref.ref);
+
+ if (aref.offrng[0] != aref.offrng[1])
+ fprintf (dump_file, " + [%lli, %lli]",
+ (long long) aref.offrng[0].to_shwi (),
+ (long long) aref.offrng[1].to_shwi ());
+ else if (aref.offrng[0] != 0)
+ fprintf (dump_file, " %c %lli",
+ aref.offrng[0] < 0 ? '-' : '+',
+ (long long) aref.offrng[0].to_shwi ());
+
+ fputc ('\n', dump_file);
+ }
+
+ fputc ('\n', dump_file);
+}
+
/* A helper of compute_objsize_r() to determine the size from an assignment
statement STMT with the RHS of either MIN_EXPR or MAX_EXPR. */
@@ -1778,8 +1880,14 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
if (const access_ref *cache_ref = qry->get_ref (ptr))
{
/* If the pointer is in the cache set *PREF to what it refers
- to and return success. */
+ to and return success.
+ FIXME: BNDRNG is determined by each access and so it doesn't
+ belong in access_ref. Until the design is changed, keep it
+ unchanged here. */
+ const offset_int bndrng[2] = { pref->bndrng[0], pref->bndrng[1] };
*pref = *cache_ref;
+ pref->bndrng[0] = bndrng[0];
+ pref->bndrng[1] = bndrng[1];
return true;
}
}
@@ -1928,9 +2036,13 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
return true;
}
- if (code == ADDR_EXPR
- || code == SSA_NAME)
- return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+ if (code == ADDR_EXPR || code == SSA_NAME)
+ {
+ if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+ return false;
+ qry->put_ref (ptr, *pref);
+ return true;
+ }
/* (This could also be an assignment from a nonlocal pointer.) Save
PTR to mention in diagnostics but otherwise treat it as a pointer
@@ -186,6 +186,9 @@ public:
/* Flush the cache. */
void flush_cache ();
+ /* Dump statistics and optionally cache contents to DUMP_FILE. */
+ void dump (FILE *, bool = false);
+
/* A Ranger instance. May be null to use global ranges. */
range_query *rvals;
/* Cache of SSA_NAMEs. May be null to disable caching. */
new file mode 100644
@@ -0,0 +1,35 @@
+/*
+ { dg-do compile }
+ { dg-options "-Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+int memcmp (const void*, const void*, size_t);
+int strncmp (const char*, const char*, size_t);
+char* stpncpy (char*, const char*, size_t);
+char* strncpy (char*, const char*, size_t);
+
+extern char a4[4], b5[5];
+
+struct A { char a4[4]; };
+
+extern volatile int i;
+extern void* volatile ptr;
+
+void test_stpncpy (struct A *p)
+{
+ ptr = stpncpy (a4, b5, 4);
+ ptr = stpncpy (a4, b5, 5); // { dg-warning "writing 5 bytes" }
+
+ ptr = stpncpy (p->a4, b5, 4);
+ ptr = stpncpy (p->a4, b5, 5); // { dg-warning "writing 5 bytes" }
+}
+
+void test_strncpy (struct A *p)
+{
+ ptr = strncpy (a4, b5, 4);
+ ptr = strncpy (a4, b5, 5); // { dg-warning "writing 5 bytes" }
+
+ ptr = strncpy (p->a4, b5, 4);
+ ptr = strncpy (p->a4, b5, 5); // { dg-warning "writing 5 bytes" }
+}
@@ -5820,27 +5820,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 ();