+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
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)
{
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.
static void
find_implicit_erroneous_behavior (void)
{
+ locmap_t locmap;
+
basic_block bb;
FOR_EACH_BB_FN (bb, cfun)
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)
}
}
}
+
+ 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
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);
}
}
}
+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.
--- /dev/null
+/* 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));
+}
{
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" } */
--- /dev/null
+/* 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 ();
+}
--- /dev/null
+/* 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" } */
+}
--- /dev/null
+/* 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" } */
+}
+
--- /dev/null
+/* 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" } */
+}
+
--- /dev/null
+/* 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;
+}
--- /dev/null
+/* 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]" } */
+
+}
--- /dev/null
+/* 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 } */
+}
--- /dev/null
+/* 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 } */
--- /dev/null
+/* 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. */
+}
int var, *p = &var;
return (double)(uintptr_t)(p);
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
r[i] = 1;
return r;
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
p = &a[2];
else
p = &i;
- return p;
+ return p; /* { dg-warning "\\\[-Wreturn-local-addr" } */
}
/* { dg-final { scan-tree-dump "Deleted dead store" "dse1" } } */
int c[64] = {}, d[64] = {};
return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[0];
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
{
return q (); /* { dg-message "called from here" } */
}
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
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__