PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result

PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result
PR middle-end/90549 - missing -Wreturn-local-addr maybe returning an address of a local array plus offset

gcc/ChangeLog:

	PR middle-end/71924
	PR middle-end/90549
	* gimple-ssa-isolate-paths.c (isolate_path): Add attribute.  Update
	comment.
	(args_loc_t): New type.
	(args_loc_t, locmap_t): same.
	(diag_returned_locals): New function.
	(is_addr_local): Same.
	(handle_return_addr_local_phi_arg, warn_return_addr_local): Same.
	(find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg.
	(find_explicit_erroneous_behavior): Call warn_return_addr_local.

gcc/testsuite/ChangeLog:

	PR middle-end/71924
	PR middle-end/90549
	* gcc.c-torture/execute/return-addr.c: New test.
	* gcc.dg/Wreturn-local-addr-2.c: New test.
	* gcc.dg/Wreturn-local-addr-4.c: New test.
	* gcc.dg/Wreturn-local-addr-5.c: New test.
	* gcc.dg/Wreturn-local-addr-6.c: New test.
	* gcc.dg/Wreturn-local-addr-7.c: New test.
	* gcc.dg/Wreturn-local-addr-8.c: New test.
	* gcc.dg/Wreturn-local-addr-9.c: New test.
	* gcc.dg/Wreturn-local-addr-10.c: New test.
	* gcc.dg/Walloca-4.c: Handle expected warnings.
	* gcc.dg/pr41551.c: Same.
	* gcc.dg/pr59523.c: Same.
	* gcc.dg/tree-ssa/pr88775-2.c: Same.
	* gcc.dg/tree-ssa/alias-37.c: Same.
	* gcc.dg/winline-7.c: Same.

From-SVN: r273261
This commit is contained in:
Martin Sebor 2019-07-09 04:15:42 +00:00 committed by Martin Sebor
parent 7d64aec499
commit aac9480da1
20 changed files with 1989 additions and 95 deletions

View File

@ -1,3 +1,17 @@
2019-07-08 Martin Sebor <msebor@redhat.com>
PR middle-end/71924
PR middle-end/90549
* gimple-ssa-isolate-paths.c (isolate_path): Add attribute. Update
comment.
(args_loc_t): New type.
(args_loc_t, locmap_t): same.
(diag_returned_locals): New function.
(is_addr_local): Same.
(handle_return_addr_local_phi_arg, warn_return_addr_local): Same.
(find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg.
(find_explicit_erroneous_behavior): Call warn_return_addr_local.
2019-07-08 Jakub Jelinek <jakub@redhat.com>
* tree-vect-stmts.c (scan_operand_equal_p): Look through MEM_REF

View File

@ -128,9 +128,9 @@ insert_trap (gimple_stmt_iterator *si_p, tree op)
DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
Return BB'. */
Return BB' (which may be equal to DUPLICATE). */
basic_block
ATTRIBUTE_RETURNS_NONNULL basic_block
isolate_path (basic_block bb, basic_block duplicate,
edge e, gimple *stmt, tree op, bool ret_zero)
{
@ -341,6 +341,322 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
return false;
}
/* Describes the property of a return statement that may return
the address of one or more local variables. The type must
be safely assignable and copyable so that it can be stored in
a hash_map. */
class args_loc_t
{
public:
args_loc_t (): nargs (), locvec (), ptr (&ptr)
{
locvec.create (4);
}
args_loc_t (const args_loc_t &rhs)
: nargs (rhs.nargs), locvec (rhs.locvec.copy ()), ptr (&ptr) { }
args_loc_t& operator= (const args_loc_t &rhs)
{
nargs = rhs.nargs;
locvec.release ();
locvec = rhs.locvec.copy ();
return *this;
}
~args_loc_t ()
{
locvec.release ();
gcc_assert (ptr == &ptr);
}
/* For a PHI in a return statement its number of arguments. When greater
than LOCVEC.LENGTH () implies that an address of one of the locals in
LOCVEC may but need not be returned by the statement. Otherwise,
unless both are zero, it implies it definitely is returned. */
unsigned nargs;
/* The locations of local variables/alloca calls returned by the return
statement. Avoid using auto_vec here since it's not safe to copy due
to pr90904. */
vec <location_t> locvec;
void *ptr;
};
/* A mapping from a return statement to the locations of local variables
whose addresses it may return. */
typedef hash_map <gimple *, args_loc_t> locmap_t;
/* Given the LOCMAP mapping, issue diagnostics about returning addresses
of local variables. When MAYBE is set, all diagnostics will be of
the "may return" kind. Otherwise each will be determined based on
the equality of the corresponding NARGS and LOCVEC.LENGTH () values. */
static void
diag_returned_locals (bool maybe, const locmap_t &locmap)
{
for (locmap_t::iterator it = locmap.begin (); it != locmap.end (); ++it)
{
gimple *stmt = (*it).first;
const args_loc_t &argsloc = (*it).second;
location_t stmtloc = gimple_location (stmt);
auto_diagnostic_group d;
unsigned nargs = argsloc.locvec.length ();
if (warning_at (stmtloc, OPT_Wreturn_local_addr,
(maybe || argsloc.nargs > nargs
? G_("function may return address of local variable")
: G_("function returns address of local variable"))))
{
for (unsigned i = 0; i != nargs; ++i)
inform (argsloc.locvec[i], "declared here");
}
}
}
/* Return true if EXPR is an expression of pointer type that refers
to the address of one or more variables with automatic storage
duration. If so, add an entry to *PLOCMAP and insert into
PLOCMAP->LOCVEC the locations of the corresponding local variables
whose address is returned by the RETURN_STMT (which may be set to
(gimple*)-1 as a placeholder for such a statement). VISITED is
a bitmap of PHI nodes already visited by recursive calls. When
null, PHI expressions are not considered. */
static bool
is_addr_local (gimple *return_stmt, tree exp, locmap_t *plocmap,
hash_set<gphi *> *visited)
{
if (TREE_CODE (exp) == ADDR_EXPR)
{
tree baseaddr = get_base_address (TREE_OPERAND (exp, 0));
if (TREE_CODE (baseaddr) == MEM_REF)
return is_addr_local (return_stmt, TREE_OPERAND (baseaddr, 0),
plocmap, visited);
if ((!VAR_P (baseaddr)
|| is_global_var (baseaddr))
&& TREE_CODE (baseaddr) != PARM_DECL)
return false;
args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
argsloc.locvec.safe_push (DECL_SOURCE_LOCATION (baseaddr));
return true;
}
if (!POINTER_TYPE_P (TREE_TYPE (exp)))
return false;
if (TREE_CODE (exp) == SSA_NAME)
{
gimple *def_stmt = SSA_NAME_DEF_STMT (exp);
enum gimple_code code = gimple_code (def_stmt);
if (is_gimple_assign (def_stmt))
{
tree type = TREE_TYPE (gimple_assign_lhs (def_stmt));
if (POINTER_TYPE_P (type))
{
tree_code code = gimple_assign_rhs_code (def_stmt);
tree ptr1 = NULL_TREE, ptr2 = NULL_TREE;
/* Set to the number of arguments examined that should
be added to ARGSLOC->NARGS to identify expressions
only some but not all of whose operands refer to local
addresses. */
unsigned nargs = 0;
if (code == COND_EXPR)
{
ptr1 = gimple_assign_rhs2 (def_stmt);
ptr2 = gimple_assign_rhs3 (def_stmt);
nargs = 2;
}
else if (code == MAX_EXPR || code == MIN_EXPR)
{
ptr1 = gimple_assign_rhs1 (def_stmt);
ptr2 = gimple_assign_rhs2 (def_stmt);
nargs = 2;
}
else if (code == ADDR_EXPR
|| code == NOP_EXPR
|| code == POINTER_PLUS_EXPR)
/* Leave NARGS at zero and let the recursive call set it. */
ptr1 = gimple_assign_rhs1 (def_stmt);
/* Avoid short-circuiting the logical OR result in case
both operands refer to local variables, in which case
both should be considered and identified in the warning. */
bool res1 = false, res2 = false;
if (ptr1)
res1 = is_addr_local (return_stmt, ptr1, plocmap, visited);
if (ptr2)
res2 = is_addr_local (return_stmt, ptr2, plocmap, visited);
if (nargs)
if (args_loc_t *argsloc = plocmap->get (return_stmt))
argsloc->nargs += nargs;
return res1 || res2;
}
return false;
}
if (code == GIMPLE_CALL
&& gimple_call_builtin_p (def_stmt))
{
/* Handle alloca and friends that return pointers to automatic
storage. */
tree fn = gimple_call_fndecl (def_stmt);
int code = DECL_FUNCTION_CODE (fn);
if (code == BUILT_IN_ALLOCA
|| code == BUILT_IN_ALLOCA_WITH_ALIGN
|| code == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX)
{
args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
argsloc.locvec.safe_push (gimple_location (def_stmt));
return true;
}
if (gimple_call_num_args (def_stmt) < 1)
return false;
/* Recursively examine the first argument of calls to built-ins
that return it. */
switch (code)
{
case BUILT_IN_MEMCPY:
case BUILT_IN_MEMCPY_CHK:
case BUILT_IN_MEMPCPY:
case BUILT_IN_MEMPCPY_CHK:
case BUILT_IN_MEMMOVE:
case BUILT_IN_MEMMOVE_CHK:
case BUILT_IN_STPCPY:
case BUILT_IN_STPCPY_CHK:
case BUILT_IN_STPNCPY:
case BUILT_IN_STPNCPY_CHK:
case BUILT_IN_STRCAT:
case BUILT_IN_STRCAT_CHK:
case BUILT_IN_STRCHR:
case BUILT_IN_STRCPY:
case BUILT_IN_STRCPY_CHK:
case BUILT_IN_STRNCAT:
case BUILT_IN_STRNCAT_CHK:
case BUILT_IN_STRNCPY:
case BUILT_IN_STRNCPY_CHK:
case BUILT_IN_STRRCHR:
case BUILT_IN_STRSTR:
return is_addr_local (return_stmt,
gimple_call_arg (def_stmt, 0),
plocmap, visited);
default:
return false;
}
}
if (code == GIMPLE_PHI && visited)
{
gphi *phi_stmt = as_a <gphi *> (def_stmt);
if (visited->add (phi_stmt))
return false;
unsigned count = 0;
unsigned nargs = gimple_phi_num_args (phi_stmt);
args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
/* Bump up the number of operands examined by the number of
operands of this PHI. */
argsloc.nargs += nargs;
for (unsigned i = 0; i < gimple_phi_num_args (phi_stmt); ++i)
{
tree arg = gimple_phi_arg_def (phi_stmt, i);
if (is_addr_local (return_stmt, arg, plocmap, visited))
++count;
}
return count != 0;
}
}
return false;
}
/* Detect returning the address of a local variable in a PHI result LHS
and argument ARG and PHI edge E in basic block BB. Add an entry for
each use to LOCMAP, setting its NARGS member to the NARGS argument
(the number of PHI operands) plus the number of arguments in binary
expressions refereced by ARG. Call isolate_path for each returned
address and set *ISOLATED to true if called.
Return either DUPLICATE or the most recent result of isolate_path. */
static basic_block
handle_return_addr_local_phi_arg (basic_block bb, basic_block duplicate,
tree lhs, tree arg, edge e, locmap_t &locmap,
unsigned nargs, bool *isolated)
{
/* Use (gimple*)-1 as a temporary placeholder and replace it with
the return statement below once it is known. Using a null doesn't
work because it's used by the hash_map to mean "no-entry." Pass
null instead of a visited_phis bitmap to avoid descending into
PHIs since they are being processed by the caller. Those that
remain will be checked again later. */
if (!is_addr_local ((gimple*)-1, arg, &locmap, NULL))
{
/* Remove the placeholder regardless of success or failure. */
locmap.remove ((gimple*)-1);
return duplicate;
}
const args_loc_t* const placeargsloc = locmap.get ((gimple*)-1);
const unsigned nlocs = placeargsloc->locvec.length ();
gcc_assert (nlocs);
/* Add to the number of PHI arguments determined by the caller
the number of operands of the expressions referenced by ARG.
This lets the caller determine whether it's dealing with
a "may return" or "definitely returns." */
nargs += placeargsloc->nargs;
/* Set to true if any expressions referenced by ARG involve
multiple addresses only some of which are those of locals. */
bool maybe = placeargsloc->nargs > placeargsloc->locvec.length ();
gimple *use_stmt;
imm_use_iterator iter;
/* Look for uses of the PHI result LHS in return statements. */
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
{
greturn *return_stmt = dyn_cast <greturn *> (use_stmt);
if (!return_stmt)
continue;
if (gimple_return_retval (return_stmt) != lhs)
continue;
/* Add an entry for the return statement and the locations
oof the PHI arguments obtained above to the map. */
args_loc_t &argsloc = locmap.get_or_insert (use_stmt);
argsloc.nargs = nargs;
unsigned nelts = argsloc.locvec.length () + nlocs;
argsloc.locvec.reserve (nelts);
argsloc.locvec.splice (placeargsloc->locvec);
if (!maybe
&& (flag_isolate_erroneous_paths_dereference
|| flag_isolate_erroneous_paths_attribute)
&& gimple_bb (use_stmt) == bb)
{
duplicate = isolate_path (bb, duplicate, e,
use_stmt, lhs, true);
/* Let caller know the path has been isolated. */
*isolated = true;
}
}
locmap.remove ((gimple*)-1);
return duplicate;
}
/* Look for PHI nodes which feed statements in the same block where
the value of the PHI node implies the statement is erroneous.
@ -352,6 +668,8 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
static void
find_implicit_erroneous_behavior (void)
{
locmap_t locmap;
basic_block bb;
FOR_EACH_BB_FN (bb, cfun)
@ -388,70 +706,46 @@ find_implicit_erroneous_behavior (void)
gphi *phi = si.phi ();
tree lhs = gimple_phi_result (phi);
/* Initial number of PHI arguments. The result may change
from one iteration of the loop below to the next in
response to changes to the CFG but only the initial
value is stored below for use by diagnostics. */
unsigned nargs = gimple_phi_num_args (phi);
/* PHI produces a pointer result. See if any of the PHI's
arguments are NULL.
When we remove an edge, we want to reprocess the current
index, hence the ugly way we update I for each iteration. */
index since the argument at that index will have been
removed, hence the ugly way we update I for each iteration. */
basic_block duplicate = NULL;
for (unsigned i = 0, next_i = 0;
i < gimple_phi_num_args (phi);
i = next_i)
i < gimple_phi_num_args (phi); i = next_i)
{
tree op = gimple_phi_arg_def (phi, i);
tree arg = gimple_phi_arg_def (phi, i);
edge e = gimple_phi_arg_edge (phi, i);
imm_use_iterator iter;
gimple *use_stmt;
/* Advance the argument index unless a path involving
the current argument has been isolated. */
next_i = i + 1;
if (TREE_CODE (op) == ADDR_EXPR)
bool isolated = false;
duplicate = handle_return_addr_local_phi_arg (bb, duplicate, lhs,
arg, e, locmap,
nargs, &isolated);
if (isolated)
{
tree valbase = get_base_address (TREE_OPERAND (op, 0));
if ((VAR_P (valbase) && !is_global_var (valbase))
|| TREE_CODE (valbase) == PARM_DECL)
{
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
{
greturn *return_stmt
= dyn_cast <greturn *> (use_stmt);
if (!return_stmt)
continue;
if (gimple_return_retval (return_stmt) != lhs)
continue;
{
auto_diagnostic_group d;
if (warning_at (gimple_location (use_stmt),
OPT_Wreturn_local_addr,
"function may return address "
"of local variable"))
inform (DECL_SOURCE_LOCATION(valbase),
"declared here");
}
if ((flag_isolate_erroneous_paths_dereference
|| flag_isolate_erroneous_paths_attribute)
&& gimple_bb (use_stmt) == bb)
{
duplicate = isolate_path (bb, duplicate, e,
use_stmt, lhs, true);
/* When we remove an incoming edge, we need to
reprocess the Ith element. */
next_i = i;
cfg_altered = true;
}
}
}
cfg_altered = true;
next_i = i;
}
if (!integer_zerop (op))
if (!integer_zerop (arg))
continue;
location_t phi_arg_loc = gimple_phi_arg_location (phi, i);
imm_use_iterator iter;
gimple *use_stmt;
/* We've got a NULL PHI argument. Now see if the
PHI's result is dereferenced within BB. */
FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
@ -480,6 +774,57 @@ find_implicit_erroneous_behavior (void)
}
}
}
diag_returned_locals (false, locmap);
}
/* Detect and diagnose returning the address of a local variable
in RETURN_STMT in basic block BB. This only becomes undefined
behavior if the result is used, so we do not insert a trap and
only return NULL instead. */
static void
warn_return_addr_local (basic_block bb, greturn *return_stmt)
{
tree val = gimple_return_retval (return_stmt);
if (!val)
return;
locmap_t locmap;
hash_set<gphi *> visited_phis;
if (!is_addr_local (return_stmt, val, &locmap, &visited_phis))
return;
/* We only need it for this particular case. */
calculate_dominance_info (CDI_POST_DOMINATORS);
const args_loc_t *argsloc = locmap.get (return_stmt);
gcc_assert (argsloc);
bool maybe = argsloc->nargs > argsloc->locvec.length ();
if (!maybe)
maybe = !dominated_by_p (CDI_POST_DOMINATORS,
single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
diag_returned_locals (maybe, locmap);
/* Bail if the statement isn't certain to return the address
of a local (e.g., if it involves a conditional expression
that wasn't trasnformed into a PHI or if it involves
a MAX_EXPR or MIN_EXPR only one of whose operands is a local
(even though such an expression isn't valid in C or has
defined semantics in C++). */
if (maybe)
return;
/* Do not modify code if the user only asked for warnings. */
if (flag_isolate_erroneous_paths_dereference
|| flag_isolate_erroneous_paths_attribute)
{
tree zero = build_zero_cst (TREE_TYPE (val));
gimple_return_set_retval (return_stmt, zero);
update_stmt (return_stmt);
}
}
/* Look for statements which exhibit erroneous behavior. For example
@ -525,49 +870,10 @@ find_explicit_erroneous_behavior (void)
break;
}
/* Detect returning the address of a local variable. This only
becomes undefined behavior if the result is used, so we do not
insert a trap and only return NULL instead. */
/* Look for a return statement that returns the address
of a local variable or the result of alloca. */
if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
{
tree val = gimple_return_retval (return_stmt);
if (val && TREE_CODE (val) == ADDR_EXPR)
{
tree valbase = get_base_address (TREE_OPERAND (val, 0));
if ((VAR_P (valbase) && !is_global_var (valbase))
|| TREE_CODE (valbase) == PARM_DECL)
{
/* We only need it for this particular case. */
calculate_dominance_info (CDI_POST_DOMINATORS);
const char* msg;
bool always_executed = dominated_by_p
(CDI_POST_DOMINATORS,
single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
if (always_executed)
msg = N_("function returns address of local variable");
else
msg = N_("function may return address of "
"local variable");
{
auto_diagnostic_group d;
if (warning_at (gimple_location (stmt),
OPT_Wreturn_local_addr, msg))
inform (DECL_SOURCE_LOCATION(valbase),
"declared here");
}
/* Do not modify code if the user only asked for
warnings. */
if (flag_isolate_erroneous_paths_dereference
|| flag_isolate_erroneous_paths_attribute)
{
tree zero = build_zero_cst (TREE_TYPE (val));
gimple_return_set_retval (return_stmt, zero);
update_stmt (stmt);
}
}
}
}
warn_return_addr_local (bb, return_stmt);
}
}
}

View File

@ -1,3 +1,23 @@
2019-07-08 Martin Sebor <msebor@redhat.com>
PR middle-end/71924
PR middle-end/90549
* gcc.c-torture/execute/return-addr.c: New test.
* gcc.dg/Wreturn-local-addr-2.c: New test.
* gcc.dg/Wreturn-local-addr-4.c: New test.
* gcc.dg/Wreturn-local-addr-5.c: New test.
* gcc.dg/Wreturn-local-addr-6.c: New test.
* gcc.dg/Wreturn-local-addr-7.c: New test.
* gcc.dg/Wreturn-local-addr-8.c: New test.
* gcc.dg/Wreturn-local-addr-9.c: New test.
* gcc.dg/Wreturn-local-addr-10.c: New test.
* gcc.dg/Walloca-4.c: Handle expected warnings.
* gcc.dg/pr41551.c: Same.
* gcc.dg/pr59523.c: Same.
* gcc.dg/tree-ssa/pr88775-2.c: Same.
* gcc.dg/tree-ssa/alias-37.c: Same.
* gcc.dg/winline-7.c: Same.
2019-07-08 Jakub Jelinek <jakub@redhat.com>
* g++.dg/vect/simd-6.cc: Replace xfail with target x86.

View File

@ -0,0 +1,122 @@
/* Test to verify that a function that returns either the address
of a local variable or a non-local via a MAX_EXPR or MIN_EXPR
doesn't return null when the result of the expression is
the latter. */
#define NOIPA __attribute__ ((noclone, noinline, noipa))
#define A(expr) \
((expr) \
? (void)0 \
: (__builtin_printf ("assertion failed on line %i: %s\n", \
__LINE__, #expr), \
__builtin_abort ()))
typedef __UINTPTR_TYPE__ uintptr_t;
/* Return a bigger value than P. The address still points (just
past) the local variable pointed to by P so the caller does
return the address of a local variable but that's hidden from
GCC by the attribute and the point of the test is to verify
that the address in the return statement in the caller isn't
replaced by null when GCC cannot prove the address doesn't
reference a non-local variable. */
NOIPA char* get_max_2 (char *p)
{
return p + 1;
}
NOIPA char* get_max_3 (char *p, char *q)
{
return p < q ? q + 1 : p + 1;
}
/* Analogous to the above. The expressions are undefined because
they form an address prior to the beginning of the object but
it's hidden from GCC by the attributes. */
NOIPA char* get_min_2 (char *p)
{
return p - 1;
}
NOIPA char* get_min_3 (char *p, char *q)
{
return p < q ? p - 1 : q - 1;
}
NOIPA void* test_max_2 (void)
{
char c;
char *p = get_max_2 (&c);
void *q = p > &c ? p : &c; /* MAX_EXPR */
return q;
}
NOIPA void* test_max_3 (void)
{
char c;
char d;
char *p = get_max_3 (&c, &d);
void *q = p < &c ? &c < &d ? &d : &c : p;
return q;
}
NOIPA void* test_min_2 (void)
{
char c;
char *p = get_min_2 (&c);
void *q = p < &c ? p : &c; /* MIN_EXPR" */
return q;
}
NOIPA void* test_min_3 (void)
{
char c;
char d;
char *p = get_min_3 (&c, &d);
void *q = p > &c ? &c > &d ? &d : &c : p;
return q;
}
NOIPA void* test_min_3_phi (int i)
{
char a, b;
char *p0 = &a;
char *p1 = &b;
char *p2 = get_min_3 (&a, &b);
char *p3 = get_min_3 (&a, &b);
char *p4 = p2 < p0 ? p2 : p0;
char *p5 = p3 < p1 ? p3 : p1;
__builtin_printf ("%p %p %p %p\n", p2, p3, p4, p5);
if (i == 1)
return p4;
else
return p5;
}
int main ()
{
A (0 != test_max_2 ());
A (0 != test_max_3 ());
A (0 != test_min_2 ());
A (0 != test_min_3 ());
A (0 != test_min_3_phi (0));
}

View File

@ -7,11 +7,12 @@
{
char *src;
_Bool
use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
if (use_alloca)
_Bool use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
if (use_alloca)
src = (char *) __builtin_alloca ((rear_ptr - w) * sizeof (char));
else
src = (char *) __builtin_malloc ((rear_ptr - w) * sizeof (char));
return src;
}
/* { dg-prune-output "-Wreturn-local-addr" } */

View File

@ -0,0 +1,56 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
Test reduced from libstdc++-v3/testsuite/ext/ext_pointer/1.cc.
It verifies that iteration in find_implicit_erroneous_behavior
in gimple-ssa-isolate-path.c terminates under specific conditions.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
typedef __UINTPTR_TYPE__ uintptr_t;
struct A { int i; };
struct P { uintptr_t d; };
static inline struct A* get (const struct P *p)
{
if (p->d == 1)
return 0;
return (struct A*)((uintptr_t)p + p->d);
}
static inline void set (struct P *p, struct A* q)
{
/* The basic block below would cause an infinite loop in
find_implicit_erroneous_behavior due to assuming the DUPLICATE
pointer returned from isolate_path would distinct from the one
passed to it. (Replacing the if statement with the ternary ?:
expression did not have this effect (it gets optimized early
on).
<bb 4> [local count: 1073741823]:
# _14 = PHI <0B(2), &MEM <struct A[2]> [(void *)&a + 4B](3)>
_2 = _14->i;
if (_2 != 2)
goto <bb 5>; [0.00%]
else
goto <bb 6>; [100.00%]
*/
if (!q)
p->d = 1;
else
p->d = (uintptr_t)(q) - (uintptr_t)(p);
}
void f (void)
{
struct A a[2] = { { 1 }, { 2 } };
struct P p, q;
set (&p, a);
set (&q, get (&p));
set (&q, get (&q) + 0);
set (&q, get (&q) + 1);
if (get (&q)[0].i != get (&p)[1].i)
__builtin_abort ();
}

View File

@ -0,0 +1,293 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
#define ATTR(...) __attribute__ ((__VA_ARGS__))
struct A { int a, b, c; };
struct B { int a, b, c[]; };
void sink (void*, ...);
ATTR (noipa) void*
return_alloca (int n)
{
void *p = __builtin_alloca (n);
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_index_cst (int n)
{
int *p = (int*)__builtin_alloca (n);
p = &p[1];
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_plus_cst (int n)
{
int *p = (int*)__builtin_alloca (n);
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_plus_var (int n, int i)
{
char *p = (char*)__builtin_alloca (n);
p += i;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_member_1 (int n)
{
struct A *p = (struct A*)__builtin_alloca (n);
sink (&p->a);
return &p->a; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_member_2 (int n)
{
struct A *p = (struct A*)__builtin_alloca (n);
sink (&p->b);
return &p->b; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_flexarray (int n)
{
struct B *p = (struct B*)__builtin_alloca (n);
sink (p->c);
return p->c; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_array (void)
{
int a[32];
void *p = a;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_array_index_cst (void)
{
int a[32];
void *p = &a[2];
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_array_plus_cst (void)
{
int a[32];
void *p = a + 2;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_array_plus_var (int i)
{
int a[32];
void *p = a + i;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_array_member_1 (void)
{
struct A a[2];
int *p = &a[1].a;
sink (a, p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_array_member_2 (void)
{
struct A a[32];
int *p = &a[1].b;
sink (a, p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla (int n)
{
char a[n];
void *p = a;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla_index_cst (int n)
{
char a[n];
char *p = &a[3];
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla_plus_cst (int n)
{
char a[n];
char *p = a + 3;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla_index_var (int n, int i)
{
char a[n];
char *p = &a[i];
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla_plus_var (int n, int i)
{
char a[n];
char *p = a + i;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla_member_1 (int n, int i)
{
struct A a[n];
void *p = &a[i].a;
sink (a, p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_vla_member_2 (int n, int i)
{
struct A a[n];
void *p = &a[i].b;
sink (a, p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_or_alloca (int n, int i)
{
void *p = i ? __builtin_alloca (n * i) : __builtin_alloca (n);
sink (p);
/* The warning here should really be "function returns". */
return p; /* { dg-warning "function (returns|may return) address of local" } */
}
ATTR (noipa) void*
return_alloca_or_alloca_2 (int n, int i)
{
void *p0 = __builtin_alloca (n);
void *p1 = __builtin_alloca (n * 2);
void *p = i ? p0 : p1;
sink (p0, p1, p);
/* Same as above. */
return p; /* { dg-warning "function (returns|may return) address of local" } */
}
ATTR (noipa) void*
return_array_or_array (int i)
{
int a[5];
int b[7];
void *p = i ? a : b;
sink (a, b, p);
/* The warning here should really be "function returns". */
return p; /* { dg-warning "function (returns|may return) address of local" } */
}
ATTR (noipa) void*
return_array_or_array_plus_var (int i, int j)
{
int a[5];
int b[7];
void *p0 = a + i;
void *p1 = b + j;
void *p = i < j ? p0 : p1;
sink (a, b, p0, p1, p);
/* The warning here should really be "function returns". */
return p; /* { dg-warning "function (returns|may return) address of local" } */
}
extern int global[32];
ATTR (noipa) void*
may_return_global_or_alloca (int n, int i)
{
void *p = i ? global : __builtin_alloca (n);
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
may_return_global_or_alloca_plus_cst (int n, int i)
{
int *p = i ? global : (int*)__builtin_alloca (n);
p += 7;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
may_return_global_or_array (int n, int i)
{
int a[32];
void *p = i ? global : a;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
may_return_global_or_array_plus_cst (int n, int i)
{
int a[32];
int *p = i ? global : a;
p += 4;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
may_return_global_or_vla (int n, int i)
{
int a[n];
void *p = i ? global : a;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
may_return_global_or_vla_plus_cst (int n, int i)
{
int a[n];
int *p = i ? global : a;
p += 4;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}

View File

@ -0,0 +1,248 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
#define ATTR(...) __attribute__ ((__VA_ARGS__))
typedef __INTPTR_TYPE__ intptr_t;
struct A { int a, b, c; };
struct B { int a, b, c[]; };
extern int g1[5], g2[5], g3[5], g4[5], g5[5];
void sink (void*, ...);
/* Verify that a pointer difference expression is handled correctly
even when converted to a pointer. */
ATTR (noipa) void*
return_local_diff_cst (void)
{
int a[5];
void *p = (void*)(&a[4] - &a[1]);
return p;
}
ATTR (noipa) void*
return_local_diff_var (int i, int j)
{
int a[5];
void *p = (void*)(&a[j] - &a[i]);
return p;
}
ATTR (noipa) void*
return_2_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
void *p = i < 0 ? a : b;
return p; /* { dg-warning "function returns address of local" } */
}
/* Verify that returning the address of a local converted to intptr_t
is not diagnosed (see bug 90737 for a case the front-end gets wrong). */
ATTR (noipa) intptr_t
return_int_2_locals (int i)
{
int a[1];
int b[2];
void *p = i < 0 ? a : b;
return (intptr_t)p;
}
/* Verify that a conditional expression with a pointer first operand
is handled correctly. */
ATTR (noipa) void*
return_2_locals_ptrcond (void *q)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
void *p = q ? a : b;
return p; /* { dg-warning "function returns address of local" } */
}
/* Verify that a preincrement expression with a pointer operand is
handled correctly. */
ATTR (noipa) void*
return_2_locals_ptrinc (void *q)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = q ? a : b;
return ++p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_3_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
void *p = i < 0 ? a : 0 < i ? c : b;
return p; /* { dg-warning "function returns address of local" } */
}
/* Verify that a conditional expression with a pointer first operand
is handled correctly. */
ATTR (noipa) void*
return_3_locals_ptrcond (void *p, void *q)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
void *r = q ? r ? a : b : c;
return r; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_5_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int d[4]; /* { dg-message "declared here" } */
int e[5]; /* { dg-message "declared here" } */
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? e : 0 < i ? d : c;
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_1_global_4_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int d[4]; /* { dg-message "declared here" } */
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? d : c;
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_2_globals_3_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : c;
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_3_globals_2_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
void *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : g3;
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_4_globals_1_local (int i)
{
int a[1]; /* { dg-message "declared here" } */
void *p = i < -1 ? a : i < 0 ? g1 : 1 < i ? g2 : 0 < i ? g4 : g3;
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_all_globals (int i)
{
void *p = i < -1 ? g1 : i < 0 ? g2 : 1 < i ? g3 : 0 < i ? g5 : g4;
return p;
}
ATTR (noipa) void*
return_2_alloca_local_cstoff (int n, int i)
{
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
int *p = i < 0 ? a : b;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_local_cstoff (int n, int i)
{
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = i < 0 ? a : b;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_local_alloca_cstoff (int n, int i)
{
int a[2]; /* { dg-message "declared here" } */
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
int *p = i < 0 ? a : b;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_2_locals_cstoff (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = i < 0 ? a : b;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_2_globals_3_locals_cstoff (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : c;
p += 1;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_3_globals_alloca_local_varoff (int n, int i, int j)
{
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : g3;
p += j;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_3_globals_2_locals_varoff (int i, int j)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = i < -1 ? a : i < 0 ? b : 1 < i ? g1 : 0 < i ? g2 : g3;
p += j;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}

View File

@ -0,0 +1,370 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
#define ATTR(...) __attribute__ ((__VA_ARGS__))
struct A { int a, b, c; };
struct B { int a, b, c[]; };
extern int g1[5], g2[5], g3[5], g4[5], g5[5];
void sink (void*, ...);
ATTR (noipa) void*
return_2_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
void *p = b;
if (i < 0)
p = a;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_2_locals_after_2_globals (int i, int j)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p;
if (i < 0)
p = g1;
else
p = g2;
sink (p);
if (j < 0)
p = a;
else
p = b;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_3_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
void *p = b + 1;
if (i < 0)
p = a;
else if (0 < i)
p = c + 2;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_5_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int d[4]; /* { dg-message "declared here" } */
int e[5]; /* { dg-message "declared here" } */
void *p = &c[2];
if (i < -1)
p = a;
else if (i < 0)
p = &b[1];
else if (1 < i)
p = &e[4];
else if (0 < i)
p = &d[3];
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_5_locals_switch (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int d[4]; /* { dg-message "declared here" } */
int e[5]; /* { dg-message "declared here" } */
void *p = 0;
switch (i)
{
case 0: p = &a[1]; break;
case 1: p = &b[2]; break;
case 2: p = &c[3]; break;
case 3: p = &d[4]; break;
default: p = &e[5]; break;
}
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_1_global_4_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int d[4]; /* { dg-message "declared here" } */
void *p = c;
if (i < -1)
sink (p = a);
else if (i < 0)
sink (p = b);
else if (1 < i)
sink (p = g1);
else if (0 < i)
sink (p = d);
sink (p, a, b, c, d);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_1_global_4_locals_switch (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int d[4]; /* { dg-message "declared here" } */
void *p = 0;
switch (i)
{
case 0: p = &a[0]; break;
case 1: p = &b[1]; break;
case 2: p = &c[2]; break;
case 3: p = &d[3]; break;
}
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_2_globals_3_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
void *p = c;
if (i < -1)
p = a;
else if (i < 0)
p = b;
else if (1 < i)
p = g1;
else if (0 < i)
p = g2;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_3_globals_2_locals (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
void *p = g3;
if (i < -1)
p = a;
else if (i < 0)
p = b;
else if (1 < i)
p = g1;
else if (0 < i)
p = g2;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_4_globals_1_local (int i)
{
int a[1]; /* { dg-message "declared here" } */
void *p = g3;
if (i < -1)
p = a;
else if (i < 0)
p = g1;
else if (1 < i)
p = g2;
else if (0 < i)
p = g4;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_all_globals (int i)
{
void *p = g4;
if (i < -1)
p = g1;
else if (i < 0)
p = g2;
else if (1 < i)
p = g3;
else if (0 < i)
p = g5;
return p;
}
ATTR (noipa) void*
return_2_alloca_local_cstoff (int n, int i)
{
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
int *p = i < 0 ? a : b;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_alloca_local_cstoff (int n, int i)
{
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = b;
if (i < 0)
p = a;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_local_alloca_cstoff (int n, int i)
{
int a[2]; /* { dg-message "declared here" } */
int *b = __builtin_alloca (n); /* { dg-message "declared here" } */
int *p = b;
if (i < 0)
p = a;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_2_locals_cstoff (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = b;
if (i < 0)
p = a;
p += 1;
sink (p);
return p; /* { dg-warning "function returns address of local" } */
}
ATTR (noipa) void*
return_2_globals_3_locals_cstoff (int i)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int c[3]; /* { dg-message "declared here" } */
int *p = c;
if (i < -1)
p = a;
else if (i < 0)
p = b;
else if (1 < i)
p = g1;
else if (0 < i)
p = g2;
p += 1;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_3_globals_alloca_local_varoff (int n, int i, int j)
{
int *a = __builtin_alloca (n); /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = g3;
if (i < -1)
p = a;
else if (i < 0)
p = b;
else if (1 < i)
p = g1;
else if (0 < i)
p = g2;
p += j;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}
ATTR (noipa) void*
return_3_globals_2_locals_varoff (int i, int j)
{
int a[1]; /* { dg-message "declared here" } */
int b[2]; /* { dg-message "declared here" } */
int *p = g3;
if (i < -1)
p = a;
else if (i < 0)
p = b;
else if (1 < i)
p = g1;
else if (0 < i)
p = g2;
p += j;
sink (p);
return p; /* { dg-warning "function may return address of local" } */
}

View File

@ -0,0 +1,40 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
void sink (void*);
void* loop_idx (int x)
{
char a[32]; /* { dg-message "declared here" } */
char *p = a;
sink (a);
int i;
for (i = 0; i != 32; ++i)
if (p[i] == x)
break;
p = i < 32 ? &p[i] : 0;
return p; /* { dg-warning "may return address of local variable" } */
}
void* loop_ptr (int i, int x)
{
char a[32]; /* { dg-message "declared here" } */
char *p;
sink (a);
/* The warning for the statement below would ideally be a "returns"
because it definitely returns the address of a, but when both
returns get merged into one we end up with a "may return". */
for (p = a; *p; ++p)
if (*p == x)
return p; /* { dg-warning "(returns|may return) address of local variable" "missing location" { xfail *-*-* } } */
/* { dg-warning "(returns|may return) address of local variable" "pr90735" { target *-*-* } 0 } */
return 0;
}

View File

@ -0,0 +1,203 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
typedef __SIZE_TYPE__ size_t;
void* memcpy (void*, const void*, size_t);
void* mempcpy (void*, const void*, size_t);
void* memmove (void*, const void*, size_t);
char* stpcpy (char*, const char*);
char* stpncpy (char*, const char*, size_t);
size_t strlen (const char*);
size_t strnlen (const char*, size_t);
char* strcat (char*, const char*);
char* strncat (char*, const char*, size_t);
char* strcpy (char*, const char*);
char* strncpy (char*, const char*, size_t);
char* strdup (const char*);
char* strchr (const char*, int);
char* strrchr (const char*, int);
char* strstr (const char*, const char*);
void sink (void*, ...);
void* return_memcpy (const void *s, unsigned n)
{
char a[n]; /* { dg-message "declared here" } */
void *p = memcpy (a, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
void* return_memcpy_cst (const void *s, unsigned n)
{
char a[n]; /* { dg-message "declared here" } */
void *p = memcpy (a + 1, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
void* return_memcpy_var (const void *s, unsigned n, int i)
{
char a[n]; /* { dg-message "declared here" } */
void *p = memcpy (a + i, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
void* return_mempcpy (const void *s, unsigned n)
{
char a[n]; /* { dg-message "declared here" } */
void *p = mempcpy (a, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
void* return_memmove_cst (unsigned n)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
void *p = memmove (a + 1, a, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
void* return_memmove_var (unsigned n, int i)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
void *p = memmove (a + i, a, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_stpcpy (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
char *p = stpcpy (a, s);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_stpncpy (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
char *p = stpncpy (a, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strcat (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strcat (a, s);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strncat (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strncat (a, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strcpy (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
char *p = strcpy (a, s);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strcpy_plus_strlen (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
char *p = strcpy (a, s);
sink (p);
p += strlen (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strcpy_cst_plus_strlen (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strcpy (a + 1, s);
sink (p);
p += strlen (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strcpy_var_plus_strlen (unsigned n, const char *s, int i)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strcpy (a + i, s);
sink (p);
p += strlen (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strncpy (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
char *p = strncpy (a, s, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strncpy_plus_strnlen (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
char *p = strncpy (a, s, n);
p += strnlen (p, n);
sink (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strdup (unsigned n)
{
char a[n];
sink (a);
char *p = strdup (a);
return p;
}
char* return_strchr (unsigned n, int c)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strchr (a, c);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strstr (unsigned n, const char *s)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strstr (a, s);
if (p)
p += strlen (p);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}
char* return_strrchr (unsigned n, int c)
{
char a[n]; /* { dg-message "declared here" } */
sink (a);
char *p = strrchr (a, c);
return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
}

View File

@ -0,0 +1,50 @@
/* Test to verify that a PHI with a COND_EXPR argument in a return
statement is handled correctly.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
extern struct S s;
void* f (int n)
{
void *p;
int x = 0;
for (int i = n; i >= 0; i--)
{
p = &s;
if (p == (void*)-1)
x = 1;
else if (p)
return p;
}
/* The return statement below ends up with the following IL:
<bb 6> [local count: 59055800]:
# x_10 = PHI <1(5), 0(2)>
_5 = x_10 != 0 ? -1B : 0B;
<bb 7> [local count: 114863532]:
# _3 = PHI <&s(4), _5(6), &s(3)>
return _3; */
return x ? (void*)-1 : 0;
}
void* g (int n)
{
void *p;
int x = 0; /* { dg-message "declared here" } */
for (int i = n; i >= 0; i--)
{
p = &s;
if (p == (void*)-1)
x = 1;
else if (p)
return p;
}
/* The return statement below does not reference a COND_EXPR argument. */
return x ? &x : 0; /* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
/* { dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */
}

View File

@ -0,0 +1,88 @@
/* Test to verify that a MAX_EXPR and MIN_EXPR in a return statement
is handled correctly and that all local variables whose address
is or may be returned are identified.
{ dg-do compile }
{ dg-options "-O2 -Wall" } */
char* sink (char*, ...);
void* test_max_2 (void)
{
char c; /* { dg-message "declared here" } */
char *p = sink (&c);
void *q = p > &c ? p : &c; /* MAX_EXPR */
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
}
void* test_max_3 (void)
{
char c; /* { dg-message "declared here" } */
char d; /* { dg-message "declared here" } */
char *p = sink (&c, &d);
void *q = p < &c ? &c < &d ? &d : &c : p;
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
}
void* test_min_2 (void)
{
char c; /* { dg-message "declared here" } */
char *p = sink (&c);
void *q = p < &c ? p : &c; /* MIN_EXPR" */
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
}
void* test_min_3 (void)
{
char c; /* { dg-message "declared here" } */
char d; /* { dg-message "declared here" } */
char *p = sink (&c, &d);
void *q = p > &c ? &c > &d ? &d : &c : p;
return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */
}
void* test_min_2_phi (int i)
{
char a; /* { dg-message "declared here" } */
char *p = &a;
char *q = sink (&a);
p = p < q ? p : q;
if (i == 1)
return p;
/* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
else
return q;
}
void* test_min_3_phi (int i)
{
char a; /* { dg-message "declared here" } */
char b; /* { dg-message "declared here" } */
char *p0 = &a;
char *p1 = &b;
char *p2 = sink (&a, &b);
char *p3 = sink (&a, &b);
char *p4 = p2 < p0 ? p2 : p0;
char *p5 = p3 < p1 ? p3 : p1;
if (i == 1)
/* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
return p4;
else
/* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
return p5;
}
/* The directive below "swallows" warnings for both test_min_2_phi
and test_min_3_phi.
{ dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */

View File

@ -0,0 +1,73 @@
/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
Test derived from gcc.c-torture/execute/20071108-1.c. It shows
a false positive at -Os caused by the jump threading/vrp1 pass.
{ dg-do compile }
{ dg-options "-Os -fdump-tree-optimized" } */
struct S
{
int i;
};
void* f (void);
__attribute__ ((noinline))
struct S* g (int i)
{
struct S *p = f (), q;
if (p == 0)
p = &q;
p->i = i;
if (p == &q)
p = 0;
/* With -Os the warning pass sees:
...
<bb 4>
# p_1 = PHI <&q(2), p_5(3)>
p_1->i = i_6(D);
if (&q == p_1)
goto <bb 6>; [14.90%]
else
goto <bb 5>; [85.10%]
<bb 5>
<bb 6>
# p_2 = PHI <0B(4), p_1(5)>
q ={v} {CLOBBER};
return p_2;
}
which leads to: */
return p; /* { dg-bogus "may return address of local variable" "" { xfail *-*-* } } */
/* Whereas as -O2 the pass sees:
<bb 2>
p_5 = f ();
if (p_5 == 0B)
goto <bb 4>; [30.00%]
else
goto <bb 3>; [70.00%]
<bb 3>
# p_2 = PHI <0B(5), p_5(4)>
q ={v} {CLOBBER};
return p_2;
<bb 4>
p_5->i = i_6(D);
goto <bb 3>; [100.00%]
<bb 5>
q.i = i_6(D);
goto <bb 3>; [100.00%]
}
and no warning. */
}

View File

@ -10,3 +10,5 @@ int main(void)
int var, *p = &var;
return (double)(uintptr_t)(p);
}
/* { dg-prune-output "-Wreturn-local-addr" } */

View File

@ -16,3 +16,5 @@ foo (int a, int *b, int *c, int *d)
r[i] = 1;
return r;
}
/* { dg-prune-output "-Wreturn-local-addr" } */

View File

@ -12,7 +12,7 @@ int *foo (int bogus, int n)
p = &a[2];
else
p = &i;
return p;
return p; /* { dg-warning "\\\[-Wreturn-local-addr" } */
}
/* { dg-final { scan-tree-dump "Deleted dead store" "dse1" } } */

View File

@ -41,3 +41,5 @@ f5 (void)
int c[64] = {}, d[64] = {};
return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[0];
}
/* { dg-prune-output "-Wreturn-local-addr" } */

View File

@ -13,3 +13,5 @@ inline void *t (void)
{
return q (); /* { dg-message "called from here" } */
}
/* { dg-prune-output "-Wreturn-local-addr" } */

View File

@ -23,6 +23,8 @@ a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#pragma GCC optimize ("no-isolate-erroneous-paths-dereference")
/* powerpc 32-bit not supported. */
#if !defined __powerpc__ || defined __powerpc64__