On 5/31/19 2:46 PM, Jeff Law wrote:
On 5/22/19 3:34 PM, Martin Sebor wrote:
-Wreturn-local-addr detects a subset of instances of returning
the address of a local object from a function but the warning
doesn't try to handle alloca or VLAs, or some non-trivial cases
of ordinary automatic variables[1].
The attached patch extends the implementation of the warning to
detect those. It still doesn't detect instances where the address
is the result of a built-in such strcpy[2].
Tested on x86_64-linux.
Martin
[1] For example, this is only diagnosed with the patch:
void* f (int i)
{
struct S { int a[2]; } s[2];
return &s->a[i];
}
[2] The following is not diagnosed even with the patch:
void sink (void*);
void* f (int i)
{
char a[6];
char *p = __builtin_strcpy (a, "123");
sink (p);
return p;
}
I would expect detecting to be possible and useful. Maybe as
a follow-up.
gcc-71924.diff
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 c/71924
* gimple-ssa-isolate-paths.c (is_addr_local): New function.
(warn_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 c/71924
* gcc.dg/Wreturn-local-addr-2.c: New test.
* gcc.dg/Walloca-4.c: Prune expected warnings.
* gcc.dg/pr41551.c: Same.
* gcc.dg/pr59523.c: Same.
* gcc.dg/tree-ssa/pr88775-2.c: Same.
* gcc.dg/winline-7.c: Same.
diff --git a/gcc/gimple-ssa-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c
index 33fe352bb23..2933ecf502e 100644
--- a/gcc/gimple-ssa-isolate-paths.c
+++ b/gcc/gimple-ssa-isolate-paths.c
@@ -341,6 +341,135 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
return false;
}
+/* Return true if EXPR is a expression of pointer type that refers
+ to the address of a variable with automatic storage duration.
+ If so, set *PLOC to the location of the object or the call that
+ allocated it (for alloca and VLAs). When PMAYBE is non-null,
+ also consider PHI statements and set *PMAYBE when some but not
+ all arguments of such statements refer to local variables, and
+ to clear it otherwise. */
+
+static bool
+is_addr_local (tree exp, location_t *ploc, bool *pmaybe = NULL,
+ hash_set<gphi *> *visited = NULL)
+{
+ 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 ptr = gimple_assign_rhs1 (def_stmt);
+ return is_addr_local (ptr, ploc, pmaybe, visited);
+ }
+ return false;
+ }
So this is going to recurse on the rhs1 of something like
POINTER_PLUS_EXPR, that's a good thing :-) But isn't it non-selective
about the codes where we recurse?
Consider
ptr = (cond) ? res1 : res2
I think we'll end up recursing on the condition rather than looking at
res1 and res2.
I suspect there are a very limited number of expression codes that
appear on the RHS where we'd want to recurse on one or both operands.
POINTER_PLUS_EXPR, NOP_EXPR, maybe COND_EXPR (where you have to recurse
on both and logically and the result), BIT_AND (maybe we masked off some
bits in an address). That's probably about it :-)
Are there any other codes you've seen or think would be useful in
practice to recurse through? I'd rather list them explicitly rather
than just recurse down through every rhs1 we encounter.
I don't have a list of codes to test for. I initially contemplated
enumerating them but in the end decided the pointer type check would
be sufficient. I wouldn't expect a COND_EXPR here. Don't they get
transformed into PHIs? In all my tests they do and and running
the whole test suite with an assert that it doesn't come up doesn't
expose any either. (I left the assert for COND_EXPR there.) If
a COND_EXPR really can come up in a GIMPLE assignment here can you
please show me how so I can add a test for it?
I've added tests to exercise all C expressions that evaluate to
pointers. I don't know of any others where what you bring up
should be a concern and I don't want to try to hardwire tests for
any that I can't to exercise in the testsuite or don't know how.
If you know of some I'm happy to add them and adjust the code.
+
+ if (code == GIMPLE_PHI && pmaybe)
+ {
+ unsigned count = 0;
+ gphi *phi_stmt = as_a <gphi *> (def_stmt);
+
+ unsigned nargs = gimple_phi_num_args (phi_stmt);
+ for (unsigned i = 0; i < nargs; ++i)
+ {
+ if (!visited->add (phi_stmt))
+ {
+ tree arg = gimple_phi_arg_def (phi_stmt, i);
+ if (is_addr_local (arg, ploc, pmaybe, visited))
+ ++count;
+ }
+ }
So imagine
p = phi (x1, x1, x2)
Where both x1 and x2 would pass is_addr_local. I think this code would
incorrectly set pmaybe.
I suppose it would but this happens readily with or without my
patch, for example here:
int* f (int i)
{
int a[2], b[2];
int *p = i ? a : b;
return p;
}
We end up with two instances of "function may return address
of local variable," one for each local, because
find_implicit_erroneous_behavior only issues the "may return"
kind of warning.
I don't particularly like this -- that's why I added PMAYBE to
the new code. But to avoid mission creep I'd decided not draw
the line there and avoid trying to fix the problem completely.
(I've enhanced this in the attached update.)
We process the first instance of x1, find it is local and bump count.
When we encounter the second instance, it's in our map and we do
nothing. THen we process x2 and bump count again. So count would be 2.
But count is going to be less than nargs so we'll set *pmaybe to true.
ISTM you'd have to record the result for each phi argument and query
that to determine if you should bump the counter if you've already
visited the argument.
I suspect that no solution will be perfect, at a minimum because
multiple return statements sometimes get merged into one, so what
in the source code is a single return that definitely returns
the address a local may end up merged with one that returns
a null (and triggering a maybe kind of warning). I have also
seen warnings in some non-trivial test cases that suggest that
some return statements get split up into two where a "may return"
could turn into a "definitely does return."
It also seems to me that you can break the loop as soon as you've got a
nonzero count and get a false return from is_addr_local. Not sure if
that'll matter in practice or not.
This is only possible if we're willing to lose some detail (i.e.,
if we are willing to only point to one variable when returning
the address of two or more). I don't find that very user-friendly.
To wrap up, while I would have preferred taking a simpler approach
at first and making bigger design changes later, I have redesigned
the warning for better accuracy as you suggested above.
The attached revised patch first collects the return statements
in a hash table along with the locations of the local variables
whose addresses each statement returns, and then issues just one
warning for each statement, listing all the locals it refers to
in subsequent notes.
In addition, since it was nearly trivial, I also added checking
for returning addresses of locals via calls to built-ins like
memcpy.
I have beefed up the test suite to exercise non-trivial functions
involving different kinds of expressions and statements, including
loops. Except for one xfail due to missing location information
(bug 90735 - missing location in -Wreturn-local-addr on a function
with two return statements) I haven't found any issues.
The improved warning does find many more problems than the current
solution.
The other approach here (and I'm not suggesting you implement it) would
be to use the propagation engine. That's probably overkill here since
we'd probably end up computing a whole lot of things we don't need. My
sense is an on-demand approach like you've done is probably less
computationally expensive.
I'm certainly not opposed to making further improvements (as I
mentioned, I'd like to add checking for returning freed pointers
and freeing locals as you suggested). But I would prefer to make
these in incremental steps rather than growing what was at first
meant to be just small bug fix/enhancement for alloca and VLAs.
The attached update has been tested on x86_64-linux.
Martin
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 (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.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/Walloca-4.c: Prune expected warnings.
* gcc.dg/pr41551.c: Same.
* gcc.dg/pr59523.c: Same.
* gcc.dg/tree-ssa/pr88775-2.c: Same.
* gcc.dg/winline-7.c: Same.
diff --git a/gcc/gimple-ssa-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c
index 33fe352bb23..eb8e754870f 100644
--- a/gcc/gimple-ssa-isolate-paths.c
+++ b/gcc/gimple-ssa-isolate-paths.c
@@ -341,6 +341,230 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
return false;
}
+/* Vector of locations of local variables/alloca calls returned from
+ a function. */
+typedef auto_vec <location_t> locvec_t;
+
+/* Describes the property of a return statement that may return
+ the address of one or more local variables. */
+struct args_loc_t
+{
+ /* For a PHI in a return statement its number of arguments. When less
+ than LOCVEC.LENGTH () implies that an address of local may but need
+ not be returned by the statement. Otherwise it implies it definitely
+ is returned. Used to issue "may be" vs "is" diagnostics. */
+ unsigned nargs;
+ /* The locations of local variables/alloca calls returned by the return
+ statement. */
+ locvec_t locvec;
+};
+
+/* 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;
+ if (warning_at (stmtloc, OPT_Wreturn_local_addr,
+ (maybe || argsloc.nargs > argsloc.locvec.length ()
+ ? G_("function may return address of local variable")
+ : G_("function returns address of local variable"))))
+ {
+ for (unsigned i = 0; i != argsloc.locvec.length (); ++i)
+ inform (argsloc.locvec[i], "declared here");
+ }
+ }
+}
+
+/* Return true if EXPR is a expression of pointer type that refers
+ to the address of a variable with automatic storage duration.
+ If so, set *PLOC to the location of the object or the call that
+ allocated it (for alloca and VLAs). When PLOCMAP is non-null,
+ also consider PHI statements and insert INTO PLOCMAP->LOCVEC
+ the locations of the corresponding local variables whose
+ address is returned by the statement. VISITED is a bitmap
+ of PHI nodes already visited by recursive calls. */
+
+static bool
+is_addr_local (gimple *return_stmt, tree exp, location_t *ploc,
+ locmap_t *plocmap = NULL, hash_set<gphi *> *visited = NULL)
+{
+ 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),
+ ploc, plocmap, visited);
+
+ if ((!VAR_P (baseaddr)
+ || is_global_var (baseaddr))
+ && TREE_CODE (baseaddr) != PARM_DECL)
+ return false;
+
+ *ploc = 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);
+ gcc_assert (code != COND_EXPR);
+
+ tree ptr = gimple_assign_rhs1 (def_stmt);
+ return is_addr_local (return_stmt, ptr, ploc, plocmap, visited);
+ }
+ return false;
+ }
+
+ if (code == GIMPLE_CALL
+ && gimple_call_builtin_p (def_stmt))
+ {
+ tree fn = gimple_call_fndecl (def_stmt);
+ int code = DECL_FUNCTION_CODE (fn);
+ if (code == BUILT_IN_ALLOCA
+ || code == BUILT_IN_ALLOCA_WITH_ALIGN)
+ {
+ *ploc = gimple_location (def_stmt);
+ return true;
+ }
+
+ if (gimple_call_num_args (def_stmt) < 1)
+ return false;
+
+ 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_STRCPY:
+ case BUILT_IN_STRCPY_CHK:
+ case BUILT_IN_STRNCAT:
+ case BUILT_IN_STRNCAT_CHK:
+ case BUILT_IN_STRNCPY:
+ case BUILT_IN_STRNCPY_CHK:
+ /* Check both the argument and the return value. */
+ return (is_addr_local (return_stmt,
+ gimple_call_arg (def_stmt, 0),
+ ploc, plocmap, visited)
+ || is_addr_local (return_stmt,
+ gimple_call_lhs (def_stmt),
+ ploc, 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 (return_stmt);
+ if (argsloc->nargs < nargs)
+ 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, ploc, plocmap, visited))
+ {
+ argsloc->locvec.safe_push (*ploc);
+ ++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.
+ Call isolate_path for each returned address.
+ Return DUPLICATE on success and NULL when no such address was found. */
+
+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)
+{
+ location_t origin;
+ if (!is_addr_local (NULL, arg, &origin))
+ return NULL;
+
+ 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 location
+ of the PHI argument to the map. */
+ args_loc_t &argsloc = locmap.get_or_insert (use_stmt);
+ argsloc.nargs = nargs;
+ argsloc.locvec.safe_push (origin);
+
+ 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. */
+ cfg_altered = true;
+ }
+ }
+
+ 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 +576,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,6 +614,12 @@ 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.
@@ -395,63 +627,24 @@ find_implicit_erroneous_behavior (void)
index, 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;
- next_i = i + 1;
+ duplicate = handle_return_addr_local_phi_arg (bb, duplicate, lhs,
+ arg, e, locmap,
+ nargs);
+ next_i = duplicate ? i : i + 1;
- if (TREE_CODE (op) == ADDR_EXPR)
- {
- 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;
- }
- }
- }
- }
-
- 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 +673,52 @@ 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;
+
+ location_t origin;
+ locmap_t locmap;
+
+ locmap.put (return_stmt, args_loc_t ());
+
+ hash_set<gphi *> visited_phis;
+ if (!is_addr_local (return_stmt, val, &origin, &locmap, &visited_phis)
+ /* && !is_addr_freed (val, &origin, &maybe, &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);
+ 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);
+
+ /* 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 +764,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);
}
}
}
diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c
new file mode 100644
index 00000000000..0e3435c8256
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c
@@ -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" } */
+}
diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-3.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-3.c
new file mode 100644
index 00000000000..6dad7af97e6
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-3.c
@@ -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" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-4.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-4.c
new file mode 100644
index 00000000000..0a451efcaf0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-4.c
@@ -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" } */
+}
+
diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-5.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-5.c
new file mode 100644
index 00000000000..1a1d155f575
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-5.c
@@ -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 address of local variable" "missing location" { xfail *-*-* } } */
+ /* { dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */
+
+ return 0;
+}
diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c
new file mode 100644
index 00000000000..4c40cd276ec
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c
@@ -0,0 +1,162 @@
+/* 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);
+
+void sink (void*, ...);
+
+
+void* return_memcpy (const void *s, unsigned n)
+{
+ char a[n];
+ 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];
+ 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];
+ 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];
+ void *p = mempcpy (a, s, n);
+ sink (p);
+ return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
+}
+
+void* return_memmove_cst (unsigned n)
+{
+ char a[n];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ 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];
+ char *p = strncpy (a, s, n);
+ p += strnlen (p, n);
+ sink (p);
+ return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */
+}
diff --git a/gcc/testsuite/gcc.dg/pr41551.c b/gcc/testsuite/gcc.dg/pr41551.c
index 2f2ad2be97e..e1123206cc6 100644
--- a/gcc/testsuite/gcc.dg/pr41551.c
+++ b/gcc/testsuite/gcc.dg/pr41551.c
@@ -10,3 +10,5 @@ int main(void)
int var, *p = &var;
return (double)(uintptr_t)(p);
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
diff --git a/gcc/testsuite/gcc.dg/pr59523.c b/gcc/testsuite/gcc.dg/pr59523.c
index a6c3302a683..49cbe5dd27a 100644
--- a/gcc/testsuite/gcc.dg/pr59523.c
+++ b/gcc/testsuite/gcc.dg/pr59523.c
@@ -16,3 +16,5 @@ foo (int a, int *b, int *c, int *d)
r[i] = 1;
return r;
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c b/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c
index 292ce6edefc..ed5df826432 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c
@@ -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" } */
diff --git a/gcc/testsuite/gcc.dg/winline-7.c b/gcc/testsuite/gcc.dg/winline-7.c
index 34deca42592..239d748926d 100644
--- a/gcc/testsuite/gcc.dg/winline-7.c
+++ b/gcc/testsuite/gcc.dg/winline-7.c
@@ -13,3 +13,5 @@ inline void *t (void)
{
return q (); /* { dg-message "called from here" } */
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */