Convert -Wrestrict pass to ranger.
There is one adjustment to a C++ test which now gives a false positive. After talking with Martin Sebor, we've concluded this is expected. There is no way to communicate that libstdc++ allocated objects are always less than PTRDIFF_MAX. gcc/ChangeLog: * calls.c (get_size_range): Adjust to work with ranger. * calls.h (get_size_range): Add ranger argument to prototype. * gimple-ssa-warn-restrict.c (class wrestrict_dom_walker): Remove. (check_call): Pull out of wrestrict_dom_walker into a static function. (wrestrict_dom_walker::before_dom_children): Rename to... (wrestrict_walk): ...this. (pass_wrestrict::execute): Instantiate ranger. (class builtin_memref): Add stmt and query fields. (builtin_access::builtin_access): Add range_query field. (builtin_memref::builtin_memref): Same. (builtin_memref::extend_offset_range): Same. (builtin_access::builtin_access): Make work with ranger. (wrestrict_dom_walker::check_call): Pull out into... (check_call): ...here. (check_bounds_or_overlap): Add range_query argument. * gimple-ssa-warn-restrict.h (check_bounds_or_overlap): Add range_query and gimple stmt arguments. gcc/testsuite/ChangeLog: * gcc.dg/Wrestrict-22.c: New test. * g++.dg/torture/pr92421.C: Adjust for ranger. libstdc++-v3/ChangeLog: * testsuite/21_strings/basic_string/capacity/1.cc: Pass -Wno-stringop-overflow to test.
This commit is contained in:
parent
495ec0b2aa
commit
e864d395b4
7 changed files with 104 additions and 41 deletions
26
gcc/calls.c
26
gcc/calls.c
|
@ -59,6 +59,7 @@ along with GCC; see the file COPYING3. If not see
|
|||
#include "builtins.h"
|
||||
#include "gimple-fold.h"
|
||||
#include "attr-fnspec.h"
|
||||
#include "value-query.h"
|
||||
|
||||
#include "tree-pretty-print.h"
|
||||
|
||||
|
@ -1244,7 +1245,8 @@ alloc_max_size (void)
|
|||
in a multi-range, otherwise to the smallest valid subrange. */
|
||||
|
||||
bool
|
||||
get_size_range (tree exp, tree range[2], int flags /* = 0 */)
|
||||
get_size_range (range_query *query, tree exp, gimple *stmt, tree range[2],
|
||||
int flags /* = 0 */)
|
||||
{
|
||||
if (!exp)
|
||||
return false;
|
||||
|
@ -1263,7 +1265,21 @@ get_size_range (tree exp, tree range[2], int flags /* = 0 */)
|
|||
enum value_range_kind range_type;
|
||||
|
||||
if (integral)
|
||||
range_type = determine_value_range (exp, &min, &max);
|
||||
{
|
||||
value_range vr;
|
||||
if (query && query->range_of_expr (vr, exp, stmt))
|
||||
{
|
||||
range_type = vr.kind ();
|
||||
if (!vr.undefined_p ())
|
||||
{
|
||||
min = wi::to_wide (vr.min ());
|
||||
max = wi::to_wide (vr.max ());
|
||||
}
|
||||
}
|
||||
else
|
||||
range_type = determine_value_range (exp, &min, &max);
|
||||
|
||||
}
|
||||
else
|
||||
range_type = VR_VARYING;
|
||||
|
||||
|
@ -1369,6 +1385,12 @@ get_size_range (tree exp, tree range[2], int flags /* = 0 */)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
get_size_range (tree exp, tree range[2], int flags /* = 0 */)
|
||||
{
|
||||
return get_size_range (/*query=*/NULL, exp, /*stmt=*/NULL, range, flags);
|
||||
}
|
||||
|
||||
/* Diagnose a call EXP to function FN decorated with attribute alloc_size
|
||||
whose argument numbers given by IDX with values given by ARGS exceed
|
||||
the maximum object size or cause an unsigned oveflow (wrapping) when
|
||||
|
|
|
@ -142,6 +142,8 @@ enum size_range_flags
|
|||
SR_USE_LARGEST = 2
|
||||
};
|
||||
extern bool get_size_range (tree, tree[2], int = 0);
|
||||
extern bool get_size_range (class range_query *, tree, gimple *,
|
||||
tree[2], int = 0);
|
||||
extern rtx rtx_for_static_chain (const_tree, bool);
|
||||
extern bool cxx17_empty_base_field_p (const_tree);
|
||||
|
||||
|
|
|
@ -25,7 +25,6 @@
|
|||
#include "backend.h"
|
||||
#include "tree.h"
|
||||
#include "gimple.h"
|
||||
#include "domwalk.h"
|
||||
#include "tree-pass.h"
|
||||
#include "builtins.h"
|
||||
#include "ssa.h"
|
||||
|
@ -41,6 +40,7 @@
|
|||
#include "calls.h"
|
||||
#include "cfgloop.h"
|
||||
#include "intl.h"
|
||||
#include "gimple-range.h"
|
||||
|
||||
namespace {
|
||||
|
||||
|
@ -77,21 +77,10 @@ pass_wrestrict::gate (function *fun ATTRIBUTE_UNUSED)
|
|||
return warn_array_bounds || warn_restrict || warn_stringop_overflow;
|
||||
}
|
||||
|
||||
/* Class to walk the basic blocks of a function in dominator order. */
|
||||
class wrestrict_dom_walker : public dom_walker
|
||||
{
|
||||
public:
|
||||
wrestrict_dom_walker () : dom_walker (CDI_DOMINATORS) {}
|
||||
static void check_call (range_query *, gimple *);
|
||||
|
||||
edge before_dom_children (basic_block) FINAL OVERRIDE;
|
||||
bool handle_gimple_call (gimple_stmt_iterator *);
|
||||
|
||||
private:
|
||||
void check_call (gimple *);
|
||||
};
|
||||
|
||||
edge
|
||||
wrestrict_dom_walker::before_dom_children (basic_block bb)
|
||||
static void
|
||||
wrestrict_walk (range_query *query, basic_block bb)
|
||||
{
|
||||
/* Iterate over statements, looking for function calls. */
|
||||
for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
|
||||
|
@ -101,21 +90,17 @@ wrestrict_dom_walker::before_dom_children (basic_block bb)
|
|||
if (!is_gimple_call (stmt))
|
||||
continue;
|
||||
|
||||
check_call (stmt);
|
||||
check_call (query, stmt);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Execute the pass for function FUN, walking in dominator order. */
|
||||
|
||||
unsigned
|
||||
pass_wrestrict::execute (function *fun)
|
||||
{
|
||||
calculate_dominance_info (CDI_DOMINATORS);
|
||||
|
||||
wrestrict_dom_walker walker;
|
||||
walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
|
||||
gimple_ranger ranger;
|
||||
basic_block bb;
|
||||
FOR_EACH_BB_FN (bb, fun)
|
||||
wrestrict_walk (&ranger, bb);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -159,11 +144,15 @@ public:
|
|||
only the destination reference is. */
|
||||
bool strbounded_p;
|
||||
|
||||
builtin_memref (tree, tree);
|
||||
builtin_memref (range_query *, gimple *, tree, tree);
|
||||
|
||||
tree offset_out_of_bounds (int, offset_int[3]) const;
|
||||
|
||||
private:
|
||||
/* Call statement to the built-in. */
|
||||
gimple *stmt;
|
||||
|
||||
range_query *query;
|
||||
|
||||
/* Ctor helper to set or extend OFFRANGE based on argument. */
|
||||
void extend_offset_range (tree);
|
||||
|
@ -197,7 +186,7 @@ class builtin_access
|
|||
&& detect_overlap != &builtin_access::no_overlap);
|
||||
}
|
||||
|
||||
builtin_access (gimple *, builtin_memref &, builtin_memref &);
|
||||
builtin_access (range_query *, gimple *, builtin_memref &, builtin_memref &);
|
||||
|
||||
/* Entry point to determine overlap. */
|
||||
bool overlap ();
|
||||
|
@ -233,9 +222,10 @@ class builtin_access
|
|||
|
||||
/* Initialize a memory reference representation from a pointer EXPR and
|
||||
a size SIZE in bytes. If SIZE is NULL_TREE then the size is assumed
|
||||
to be unknown. */
|
||||
to be unknown. STMT is the statement in which expr appears in. */
|
||||
|
||||
builtin_memref::builtin_memref (tree expr, tree size)
|
||||
builtin_memref::builtin_memref (range_query *query, gimple *stmt, tree expr,
|
||||
tree size)
|
||||
: ptr (expr),
|
||||
ref (),
|
||||
base (),
|
||||
|
@ -245,7 +235,9 @@ builtin_memref::builtin_memref (tree expr, tree size)
|
|||
offrange (),
|
||||
sizrange (),
|
||||
maxobjsize (tree_to_shwi (max_object_size ())),
|
||||
strbounded_p ()
|
||||
strbounded_p (),
|
||||
stmt (stmt),
|
||||
query (query)
|
||||
{
|
||||
/* Unfortunately, wide_int default ctor is a no-op so array members
|
||||
of the type must be set individually. */
|
||||
|
@ -264,7 +256,7 @@ builtin_memref::builtin_memref (tree expr, tree size)
|
|||
tree range[2];
|
||||
/* Determine the size range, allowing for the result to be [0, 0]
|
||||
for SIZE in the anti-range ~[0, N] where N >= PTRDIFF_MAX. */
|
||||
get_size_range (size, range, SR_ALLOW_ZERO);
|
||||
get_size_range (query, size, stmt, range, SR_ALLOW_ZERO);
|
||||
sizrange[0] = wi::to_offset (range[0]);
|
||||
sizrange[1] = wi::to_offset (range[1]);
|
||||
/* get_size_range returns SIZE_MAX for the maximum size.
|
||||
|
@ -341,7 +333,24 @@ builtin_memref::extend_offset_range (tree offset)
|
|||
/* A pointer offset is represented as sizetype but treated
|
||||
as signed. */
|
||||
wide_int min, max;
|
||||
value_range_kind rng = get_range_info (offset, &min, &max);
|
||||
value_range_kind rng;
|
||||
value_range vr;
|
||||
if (query && query->range_of_expr (vr, offset, stmt))
|
||||
{
|
||||
rng = vr.kind ();
|
||||
if (!vr.undefined_p ())
|
||||
{
|
||||
min = wi::to_wide (vr.min ());
|
||||
max = wi::to_wide (vr.max ());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* There is a global version here because
|
||||
check_bounds_or_overlap may be called from gimple
|
||||
fold during gimple lowering. */
|
||||
rng = get_range_info (offset, &min, &max);
|
||||
}
|
||||
if (rng == VR_ANTI_RANGE && wi::lts_p (max, min))
|
||||
{
|
||||
/* Convert an anti-range whose upper bound is less than
|
||||
|
@ -658,7 +667,8 @@ builtin_memref::offset_out_of_bounds (int strict, offset_int ooboff[3]) const
|
|||
/* Create an association between the memory references DST and SRC
|
||||
for access by a call EXPR to a memory or string built-in funtion. */
|
||||
|
||||
builtin_access::builtin_access (gimple *call, builtin_memref &dst,
|
||||
builtin_access::builtin_access (range_query *query, gimple *call,
|
||||
builtin_memref &dst,
|
||||
builtin_memref &src)
|
||||
: dstref (&dst), srcref (&src), sizrange (), ovloff (), ovlsiz (),
|
||||
dstoff (), srcoff (), dstsiz (), srcsiz ()
|
||||
|
@ -797,7 +807,7 @@ builtin_access::builtin_access (gimple *call, builtin_memref &dst,
|
|||
|
||||
tree size = gimple_call_arg (call, sizeargno);
|
||||
tree range[2];
|
||||
if (get_size_range (size, range, true))
|
||||
if (get_size_range (query, size, call, range, true))
|
||||
{
|
||||
bounds[0] = wi::to_offset (range[0]);
|
||||
bounds[1] = wi::to_offset (range[1]);
|
||||
|
@ -1873,8 +1883,8 @@ maybe_diag_access_bounds (gimple *call, tree func, int strict,
|
|||
/* Check a CALL statement for restrict-violations and issue warnings
|
||||
if/when appropriate. */
|
||||
|
||||
void
|
||||
wrestrict_dom_walker::check_call (gimple *call)
|
||||
static void
|
||||
check_call (range_query *query, gimple *call)
|
||||
{
|
||||
/* Avoid checking the call if it has already been diagnosed for
|
||||
some reason. */
|
||||
|
@ -1964,7 +1974,7 @@ wrestrict_dom_walker::check_call (gimple *call)
|
|||
|| (dstwr && !INTEGRAL_TYPE_P (TREE_TYPE (dstwr))))
|
||||
return;
|
||||
|
||||
if (!check_bounds_or_overlap (call, dst, src, dstwr, NULL_TREE))
|
||||
if (!check_bounds_or_overlap (query, call, dst, src, dstwr, NULL_TREE))
|
||||
return;
|
||||
|
||||
/* Avoid diagnosing the call again. */
|
||||
|
@ -1984,15 +1994,26 @@ int
|
|||
check_bounds_or_overlap (gimple *call, tree dst, tree src, tree dstsize,
|
||||
tree srcsize, bool bounds_only /* = false */,
|
||||
bool do_warn /* = true */)
|
||||
{
|
||||
return check_bounds_or_overlap (/*range_query=*/NULL,
|
||||
call, dst, src, dstsize, srcsize,
|
||||
bounds_only, do_warn);
|
||||
}
|
||||
|
||||
int
|
||||
check_bounds_or_overlap (range_query *query,
|
||||
gimple *call, tree dst, tree src, tree dstsize,
|
||||
tree srcsize, bool bounds_only /* = false */,
|
||||
bool do_warn /* = true */)
|
||||
{
|
||||
tree func = gimple_call_fndecl (call);
|
||||
|
||||
builtin_memref dstref (dst, dstsize);
|
||||
builtin_memref srcref (src, srcsize);
|
||||
builtin_memref dstref (query, call, dst, dstsize);
|
||||
builtin_memref srcref (query, call, src, srcsize);
|
||||
|
||||
/* Create a descriptor of the access. This may adjust both DSTREF
|
||||
and SRCREF based on one another and the kind of the access. */
|
||||
builtin_access acs (call, dstref, srcref);
|
||||
builtin_access acs (query, call, dstref, srcref);
|
||||
|
||||
/* Set STRICT to the value of the -Warray-bounds=N argument for
|
||||
string functions or when N > 1. */
|
||||
|
|
|
@ -22,5 +22,8 @@
|
|||
|
||||
extern int check_bounds_or_overlap (gimple *, tree, tree, tree, tree,
|
||||
bool = false, bool = true);
|
||||
extern int check_bounds_or_overlap (class range_query *, gimple *,
|
||||
tree, tree, tree, tree,
|
||||
bool = false, bool = true);
|
||||
|
||||
#endif /* GIMPLE_SSA_WARN_RESTRICT_H */
|
||||
|
|
|
@ -2,6 +2,10 @@
|
|||
// { dg-do compile }
|
||||
// { dg-additional-options "-Wno-return-type" }
|
||||
|
||||
// VRP jump threading will create additional __builtin___memcpy_chk calls that
|
||||
// may be out of bounds.
|
||||
// { dg-additional-options "-Wno-stringop-overflow" }
|
||||
|
||||
typedef long a;
|
||||
void *b, *c;
|
||||
template <typename, typename> class d {};
|
||||
|
|
9
gcc/testsuite/gcc.dg/Wrestrict-22.c
Normal file
9
gcc/testsuite/gcc.dg/Wrestrict-22.c
Normal file
|
@ -0,0 +1,9 @@
|
|||
/* { dg-do compile } */
|
||||
/* { dg-options "-O2 -Wrestrict" } */
|
||||
|
||||
void test_memcpy_warn (char *d, unsigned n)
|
||||
{
|
||||
for (unsigned i = n; i < 30; ++i)
|
||||
if (i > 10)
|
||||
__builtin_memcpy (d, d + 2, i); /* { dg-warning "overlaps" } */
|
||||
}
|
|
@ -17,6 +17,8 @@
|
|||
// with this library; see the file COPYING3. If not see
|
||||
// <http://www.gnu.org/licenses/>.
|
||||
|
||||
// { dg-options "-Wno-stringop-overflow" }
|
||||
|
||||
// 21.3.3 string capacity
|
||||
|
||||
#include <string>
|
||||
|
|
Loading…
Add table
Reference in a new issue