PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result PR middle-end/90549 - missing -Wreturn-local-addr maybe returning an address of a local array plus offset gcc/ChangeLog: PR middle-end/71924 PR middle-end/90549 * gimple-ssa-isolate-paths.c (isolate_path): Add attribute. Update comment. (args_loc_t): New type. (args_loc_t, locmap_t): same. (diag_returned_locals): New function. (is_addr_local): Same. (handle_return_addr_local_phi_arg, warn_return_addr_local): Same. (find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg. (find_explicit_erroneous_behavior): Call warn_return_addr_local. libgcc/ChangeLog: * generic-morestack.c: Disable -fisolate-erroneous-paths-dereference to get around PR libgcc/90918. gcc/testsuite/ChangeLog: PR middle-end/71924 PR middle-end/90549 * gcc.c-torture/execute/return-addr.c: New test. * gcc.dg/Wreturn-local-addr-2.c: New test. * gcc.dg/Wreturn-local-addr-4.c: New test. * gcc.dg/Wreturn-local-addr-5.c: New test. * gcc.dg/Wreturn-local-addr-6.c: New test. * gcc.dg/Wreturn-local-addr-7.c: New test. * gcc.dg/Wreturn-local-addr-8.c: New test. * gcc.dg/Wreturn-local-addr-9.c: New test. * gcc.dg/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. diff --git a/gcc/gimple-ssa-isolate-paths.c b/gcc/gimple-ssa-isolate-paths.c index 33fe352bb23..764c1c00f0e 100644 --- a/gcc/gimple-ssa-isolate-paths.c +++ b/gcc/gimple-ssa-isolate-paths.c @@ -128,9 +128,9 @@ insert_trap (gimple_stmt_iterator *si_p, tree op) DUPLICATE is a pre-existing duplicate, use it as BB' if it exists. - Return BB'. */ + Return BB' (which may be equal to DUPLICATE). */ -basic_block +ATTRIBUTE_RETURNS_NONNULL basic_block isolate_path (basic_block bb, basic_block duplicate, edge e, gimple *stmt, tree op, bool ret_zero) { @@ -341,6 +341,322 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt) return false; } +/* Describes the property of a return statement that may return + the address of one or more local variables. The type must + be safely assignable and copyable so that it can be stored in + a hash_map. */ +class args_loc_t +{ + public: + + args_loc_t (): nargs (), locvec (), ptr (&ptr) + { + locvec.create (4); + } + + args_loc_t (const args_loc_t &rhs) + : nargs (rhs.nargs), locvec (rhs.locvec.copy ()), ptr (&ptr) { } + + args_loc_t& operator= (const args_loc_t &rhs) + { + nargs = rhs.nargs; + locvec.release (); + locvec = rhs.locvec.copy (); + return *this; + } + + ~args_loc_t () + { + locvec.release (); + gcc_assert (ptr == &ptr); + } + + /* For a PHI in a return statement its number of arguments. When greater + than LOCVEC.LENGTH () implies that an address of one of the locals in + LOCVEC may but need not be returned by the statement. Otherwise, + unless both are zero, it implies it definitely is returned. */ + unsigned nargs; + /* The locations of local variables/alloca calls returned by the return + statement. Avoid using auto_vec here since it's not safe to copy due + to pr90904. */ + vec locvec; + void *ptr; +}; + +/* A mapping from a return statement to the locations of local variables + whose addresses it may return. */ +typedef hash_map 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 *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 (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 (use_stmt); + if (!return_stmt) + continue; + + if (gimple_return_retval (return_stmt) != lhs) + continue; + + /* Add an entry for the return statement and the locations + oof the PHI arguments obtained above to the map. */ + args_loc_t &argsloc = locmap.get_or_insert (use_stmt); + argsloc.nargs = nargs; + unsigned nelts = argsloc.locvec.length () + nlocs; + argsloc.locvec.reserve (nelts); + argsloc.locvec.splice (placeargsloc->locvec); + + if (!maybe + && (flag_isolate_erroneous_paths_dereference + || flag_isolate_erroneous_paths_attribute) + && gimple_bb (use_stmt) == bb) + { + duplicate = isolate_path (bb, duplicate, e, + use_stmt, lhs, true); + + /* Let caller know the path has been isolated. */ + *isolated = true; + } + } + + locmap.remove ((gimple*)-1); + + return duplicate; +} + /* Look for PHI nodes which feed statements in the same block where the value of the PHI node implies the statement is erroneous. @@ -352,6 +668,8 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt) static void find_implicit_erroneous_behavior (void) { + locmap_t locmap; + basic_block bb; FOR_EACH_BB_FN (bb, cfun) @@ -388,70 +706,46 @@ find_implicit_erroneous_behavior (void) gphi *phi = si.phi (); tree lhs = gimple_phi_result (phi); + /* Initial number of PHI arguments. The result may change + from one iteration of the loop below to the next in + response to changes to the CFG but only the initial + value is stored below for use by diagnostics. */ + unsigned nargs = gimple_phi_num_args (phi); + /* PHI produces a pointer result. See if any of the PHI's arguments are NULL. When we remove an edge, we want to reprocess the current - index, hence the ugly way we update I for each iteration. */ + index since the argument at that index will have been + removed, hence the ugly way we update I for each iteration. */ basic_block duplicate = NULL; for (unsigned i = 0, next_i = 0; - i < gimple_phi_num_args (phi); - i = next_i) + i < gimple_phi_num_args (phi); i = next_i) { - tree op = gimple_phi_arg_def (phi, i); + tree arg = gimple_phi_arg_def (phi, i); edge e = gimple_phi_arg_edge (phi, i); - imm_use_iterator iter; - gimple *use_stmt; + /* Advance the argument index unless a path involving + the current argument has been isolated. */ next_i = i + 1; - - if (TREE_CODE (op) == ADDR_EXPR) + bool isolated = false; + duplicate = handle_return_addr_local_phi_arg (bb, duplicate, lhs, + arg, e, locmap, + nargs, &isolated); + if (isolated) { - tree valbase = get_base_address (TREE_OPERAND (op, 0)); - if ((VAR_P (valbase) && !is_global_var (valbase)) - || TREE_CODE (valbase) == PARM_DECL) - { - FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) - { - greturn *return_stmt - = dyn_cast (use_stmt); - if (!return_stmt) - continue; - - if (gimple_return_retval (return_stmt) != lhs) - continue; - - { - auto_diagnostic_group d; - if (warning_at (gimple_location (use_stmt), - OPT_Wreturn_local_addr, - "function may return address " - "of local variable")) - inform (DECL_SOURCE_LOCATION(valbase), - "declared here"); - } - - if ((flag_isolate_erroneous_paths_dereference - || flag_isolate_erroneous_paths_attribute) - && gimple_bb (use_stmt) == bb) - { - duplicate = isolate_path (bb, duplicate, e, - use_stmt, lhs, true); - - /* When we remove an incoming edge, we need to - reprocess the Ith element. */ - next_i = i; - cfg_altered = true; - } - } - } + cfg_altered = true; + next_i = i; } - if (!integer_zerop (op)) + if (!integer_zerop (arg)) continue; location_t phi_arg_loc = gimple_phi_arg_location (phi, i); + imm_use_iterator iter; + gimple *use_stmt; + /* We've got a NULL PHI argument. Now see if the PHI's result is dereferenced within BB. */ FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs) @@ -480,6 +774,57 @@ find_implicit_erroneous_behavior (void) } } } + + diag_returned_locals (false, locmap); +} + +/* Detect and diagnose returning the address of a local variable + in RETURN_STMT in basic block BB. This only becomes undefined + behavior if the result is used, so we do not insert a trap and + only return NULL instead. */ + +static void +warn_return_addr_local (basic_block bb, greturn *return_stmt) +{ + tree val = gimple_return_retval (return_stmt); + if (!val) + return; + + locmap_t locmap; + hash_set visited_phis; + if (!is_addr_local (return_stmt, val, &locmap, &visited_phis)) + return; + + /* We only need it for this particular case. */ + calculate_dominance_info (CDI_POST_DOMINATORS); + + const args_loc_t *argsloc = locmap.get (return_stmt); + gcc_assert (argsloc); + + bool maybe = argsloc->nargs > argsloc->locvec.length (); + if (!maybe) + maybe = !dominated_by_p (CDI_POST_DOMINATORS, + single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb); + + diag_returned_locals (maybe, locmap); + + /* Bail if the statement isn't certain to return the address + of a local (e.g., if it involves a conditional expression + that wasn't trasnformed into a PHI or if it involves + a MAX_EXPR or MIN_EXPR only one of whose operands is a local + (even though such an expression isn't valid in C or has + defined semantics in C++). */ + if (maybe) + return; + + /* Do not modify code if the user only asked for warnings. */ + if (flag_isolate_erroneous_paths_dereference + || flag_isolate_erroneous_paths_attribute) + { + tree zero = build_zero_cst (TREE_TYPE (val)); + gimple_return_set_retval (return_stmt, zero); + update_stmt (return_stmt); + } } /* Look for statements which exhibit erroneous behavior. For example @@ -525,49 +870,10 @@ find_explicit_erroneous_behavior (void) break; } - /* Detect returning the address of a local variable. This only - becomes undefined behavior if the result is used, so we do not - insert a trap and only return NULL instead. */ + /* Look for a return statement that returns the address + of a local variable or the result of alloca. */ if (greturn *return_stmt = dyn_cast (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/hash-map.h b/gcc/hash-map.h index 588dfda04fa..71cc1dead1d 100644 --- a/gcc/hash-map.h +++ b/gcc/hash-map.h @@ -21,8 +21,12 @@ along with GCC; see the file COPYING3. If not see #ifndef hash_map_h #define hash_map_h -template +/* KeyId must be a trivial (POD) type. Value may be non-trivial + (non-POD). Ctors and dtors are invoked as necessary on + inserted and removed elements. On hash_map destruction all + elements are removed. */ + +template class GTY((user)) hash_map { typedef typename Traits::key_type Key; @@ -151,12 +155,16 @@ public: { hash_entry *e = m_table.find_slot_with_hash (k, Traits::hash (k), INSERT); - bool existed = !hash_entry::is_empty (*e); - if (!existed) - e->m_key = k; + bool ins = hash_entry::is_empty (*e); + if (ins) + { + e->m_key = k; + new ((void *) &e->m_value) Value (v); + } + else + e->m_value = v; - e->m_value = v; - return existed; + return !ins; } /* if the passed in key is in the map return its value otherwise NULL. */ @@ -168,8 +176,8 @@ public: } /* Return a reference to the value for the passed in key, creating the entry - if it doesn't already exist. If existed is not NULL then it is set to false - if the key was not previously in the map, and true otherwise. */ + if it doesn't already exist. If existed is not NULL then it is set to + false if the key was not previously in the map, and true otherwise. */ Value &get_or_insert (const Key &k, bool *existed = NULL) { @@ -177,7 +185,10 @@ public: INSERT); bool ins = Traits::is_empty (*e); if (ins) - e->m_key = k; + { + e->m_key = k; + new ((void *)&e->m_value) Value (); + } if (existed != NULL) *existed = !ins; diff --git a/gcc/hash-set.h b/gcc/hash-set.h index d891ed78297..5eb4bd768d9 100644 --- a/gcc/hash-set.h +++ b/gcc/hash-set.h @@ -21,6 +21,11 @@ along with GCC; see the file COPYING3. If not see #ifndef hash_set_h #define hash_set_h +/* KeyId must be a trivial (POD) type. Traits::value_type may be + non-trivial (non-POD). Ctors and dtors are invoked as necessary + on inserted and removed elements. On hash_set destruction all + elements are removed. */ + template > class hash_set @@ -48,7 +53,7 @@ public: Key *e = m_table.find_slot_with_hash (k, Traits::hash (k), INSERT); bool existed = !Traits::is_empty (*e); if (!existed) - *e = k; + new (e) Key (k); return existed; } diff --git a/gcc/testsuite/gcc.c-torture/execute/return-addr.c b/gcc/testsuite/gcc.c-torture/execute/return-addr.c new file mode 100644 index 00000000000..7981818b3be --- /dev/null +++ b/gcc/testsuite/gcc.c-torture/execute/return-addr.c @@ -0,0 +1,122 @@ +/* Test to verify that a function that returns either the address + of a local variable or a non-local via a MAX_EXPR or MIN_EXPR + doesn't return null when the result of the expression is + the latter. */ + +#define NOIPA __attribute__ ((noclone, noinline, noipa)) + +#define A(expr) \ + ((expr) \ + ? (void)0 \ + : (__builtin_printf ("assertion failed on line %i: %s\n", \ + __LINE__, #expr), \ + __builtin_abort ())) + + +typedef __UINTPTR_TYPE__ uintptr_t; + +/* Return a bigger value than P. The address still points (just + past) the local variable pointed to by P so the caller does + return the address of a local variable but that's hidden from + GCC by the attribute and the point of the test is to verify + that the address in the return statement in the caller isn't + replaced by null when GCC cannot prove the address doesn't + reference a non-local variable. */ + +NOIPA char* get_max_2 (char *p) +{ + return p + 1; +} + +NOIPA char* get_max_3 (char *p, char *q) +{ + return p < q ? q + 1 : p + 1; +} + +/* Analogous to the above. The expressions are undefined because + they form an address prior to the beginning of the object but + it's hidden from GCC by the attributes. */ + +NOIPA char* get_min_2 (char *p) +{ + return p - 1; +} + +NOIPA char* get_min_3 (char *p, char *q) +{ + return p < q ? p - 1 : q - 1; +} + + +NOIPA void* test_max_2 (void) +{ + char c; + + char *p = get_max_2 (&c); + + void *q = p > &c ? p : &c; /* MAX_EXPR */ + return q; +} + +NOIPA void* test_max_3 (void) +{ + char c; + char d; + + char *p = get_max_3 (&c, &d); + + void *q = p < &c ? &c < &d ? &d : &c : p; + return q; +} + +NOIPA void* test_min_2 (void) +{ + char c; + + char *p = get_min_2 (&c); + + void *q = p < &c ? p : &c; /* MIN_EXPR" */ + return q; +} + +NOIPA void* test_min_3 (void) +{ + char c; + char d; + + char *p = get_min_3 (&c, &d); + + void *q = p > &c ? &c > &d ? &d : &c : p; + return q; +} + +NOIPA void* test_min_3_phi (int i) +{ + char a, b; + + char *p0 = &a; + char *p1 = &b; + char *p2 = get_min_3 (&a, &b); + char *p3 = get_min_3 (&a, &b); + + char *p4 = p2 < p0 ? p2 : p0; + char *p5 = p3 < p1 ? p3 : p1; + + __builtin_printf ("%p %p %p %p\n", p2, p3, p4, p5); + + if (i == 1) + return p4; + else + return p5; +} + +int main () +{ + A (0 != test_max_2 ()); + A (0 != test_max_3 ()); + + A (0 != test_min_2 ()); + A (0 != test_min_3 ()); + + A (0 != test_min_3_phi (0)); +} diff --git a/gcc/testsuite/gcc.dg/Walloca-4.c b/gcc/testsuite/gcc.dg/Walloca-4.c index 85dcb7b9bb9..1fbed597b98 100644 --- a/gcc/testsuite/gcc.dg/Walloca-4.c +++ b/gcc/testsuite/gcc.dg/Walloca-4.c @@ -7,11 +7,12 @@ { char *src; - _Bool - use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U); - if (use_alloca) + _Bool use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U); + if (use_alloca) src = (char *) __builtin_alloca ((rear_ptr - w) * sizeof (char)); else src = (char *) __builtin_malloc ((rear_ptr - w) * sizeof (char)); return src; } + +/* { dg-prune-output "-Wreturn-local-addr" } */ 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..bdf1cd40c1b --- /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|may return) address of local variable" "missing location" { xfail *-*-* } } */ + /* { dg-warning "(returns|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..70138b3eff8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c @@ -0,0 +1,203 @@ +/* PR c/71924 - missing -Wreturn-local-addr returning alloca result + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +typedef __SIZE_TYPE__ size_t; + +void* memcpy (void*, const void*, size_t); +void* mempcpy (void*, const void*, size_t); +void* memmove (void*, const void*, size_t); + +char* stpcpy (char*, const char*); +char* stpncpy (char*, const char*, size_t); + +size_t strlen (const char*); +size_t strnlen (const char*, size_t); + +char* strcat (char*, const char*); +char* strncat (char*, const char*, size_t); + +char* strcpy (char*, const char*); +char* strncpy (char*, const char*, size_t); + +char* strdup (const char*); + +char* strchr (const char*, int); +char* strrchr (const char*, int); +char* strstr (const char*, const char*); + +void sink (void*, ...); + + +void* return_memcpy (const void *s, unsigned n) +{ + char a[n]; /* { dg-message "declared here" } */ + void *p = memcpy (a, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +void* return_memcpy_cst (const void *s, unsigned n) +{ + char a[n]; /* { dg-message "declared here" } */ + void *p = memcpy (a + 1, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +void* return_memcpy_var (const void *s, unsigned n, int i) +{ + char a[n]; /* { dg-message "declared here" } */ + void *p = memcpy (a + i, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +void* return_mempcpy (const void *s, unsigned n) +{ + char a[n]; /* { dg-message "declared here" } */ + void *p = mempcpy (a, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +void* return_memmove_cst (unsigned n) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + void *p = memmove (a + 1, a, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +void* return_memmove_var (unsigned n, int i) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + void *p = memmove (a + i, a, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_stpcpy (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + char *p = stpcpy (a, s); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_stpncpy (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + char *p = stpncpy (a, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strcat (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strcat (a, s); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strncat (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strncat (a, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ + +} +char* return_strcpy (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + char *p = strcpy (a, s); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strcpy_plus_strlen (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + char *p = strcpy (a, s); + sink (p); + p += strlen (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strcpy_cst_plus_strlen (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strcpy (a + 1, s); + sink (p); + p += strlen (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strcpy_var_plus_strlen (unsigned n, const char *s, int i) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strcpy (a + i, s); + sink (p); + p += strlen (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strncpy (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + char *p = strncpy (a, s, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strncpy_plus_strnlen (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + char *p = strncpy (a, s, n); + p += strnlen (p, n); + sink (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strdup (unsigned n) +{ + char a[n]; + sink (a); + char *p = strdup (a); + return p; +} + +char* return_strchr (unsigned n, int c) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strchr (a, c); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strstr (unsigned n, const char *s) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strstr (a, s); + if (p) + p += strlen (p); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ +} + +char* return_strrchr (unsigned n, int c) +{ + char a[n]; /* { dg-message "declared here" } */ + sink (a); + char *p = strrchr (a, c); + return p; /* { dg-warning "\\\[-Wreturn-local-addr]" } */ + +} diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-7.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-7.c new file mode 100644 index 00000000000..ac1fb769ba8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-7.c @@ -0,0 +1,50 @@ +/* Test to verify that a PHI with a COND_EXPR argument in a return + statement is handled correctly. + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +extern struct S s; + +void* f (int n) +{ + void *p; + int x = 0; + + for (int i = n; i >= 0; i--) + { + p = &s; + if (p == (void*)-1) + x = 1; + else if (p) + return p; + } + + /* The return statement below ends up with the following IL: + [local count: 59055800]: + # x_10 = PHI <1(5), 0(2)> + _5 = x_10 != 0 ? -1B : 0B; + + [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 } */ +} diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-8.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-8.c new file mode 100644 index 00000000000..98a3805b5f0 --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-8.c @@ -0,0 +1,88 @@ +/* Test to verify that a MAX_EXPR and MIN_EXPR in a return statement + is handled correctly and that all local variables whose address + is or may be returned are identified. + { dg-do compile } + { dg-options "-O2 -Wall" } */ + +char* sink (char*, ...); + +void* test_max_2 (void) +{ + char c; /* { dg-message "declared here" } */ + + char *p = sink (&c); + + void *q = p > &c ? p : &c; /* MAX_EXPR */ + return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */ +} + +void* test_max_3 (void) +{ + char c; /* { dg-message "declared here" } */ + char d; /* { dg-message "declared here" } */ + + char *p = sink (&c, &d); + + void *q = p < &c ? &c < &d ? &d : &c : p; + return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */ +} + +void* test_min_2 (void) +{ + char c; /* { dg-message "declared here" } */ + + char *p = sink (&c); + + void *q = p < &c ? p : &c; /* MIN_EXPR" */ + return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */ +} + +void* test_min_3 (void) +{ + char c; /* { dg-message "declared here" } */ + char d; /* { dg-message "declared here" } */ + + char *p = sink (&c, &d); + + void *q = p > &c ? &c > &d ? &d : &c : p; + return q; /* { dg-warning "\\\[-Wreturn-local-addr" } */ +} + +void* test_min_2_phi (int i) +{ + char a; /* { dg-message "declared here" } */ + + char *p = &a; + char *q = sink (&a); + p = p < q ? p : q; + if (i == 1) + return p; + /* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */ + else + return q; +} + +void* test_min_3_phi (int i) +{ + char a; /* { dg-message "declared here" } */ + char b; /* { dg-message "declared here" } */ + + char *p0 = &a; + char *p1 = &b; + char *p2 = sink (&a, &b); + char *p3 = sink (&a, &b); + + char *p4 = p2 < p0 ? p2 : p0; + char *p5 = p3 < p1 ? p3 : p1; + + if (i == 1) + /* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */ + return p4; + else + /* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */ + return p5; +} + +/* The directive below "swallows" warnings for both test_min_2_phi + and test_min_3_phi. + { dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */ diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c new file mode 100644 index 00000000000..d24f911e9cd --- /dev/null +++ b/gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c @@ -0,0 +1,73 @@ +/* PR c/71924 - missing -Wreturn-local-addr returning alloca result + Test derived from gcc.c-torture/execute/20071108-1.c. It shows + a false positive at -Os caused by the jump threading/vrp1 pass. + { dg-do compile } + { dg-options "-Os -fdump-tree-optimized" } */ + +struct S +{ + int i; +}; + +void* f (void); + +__attribute__ ((noinline)) +struct S* g (int i) +{ + struct S *p = f (), q; + + if (p == 0) + p = &q; + + p->i = i; + + if (p == &q) + p = 0; + + /* With -Os the warning pass sees: + + ... + + # p_1 = PHI <&q(2), p_5(3)> + p_1->i = i_6(D); + if (&q == p_1) + goto ; [14.90%] + else + goto ; [85.10%] + + + + + # 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: + + + p_5 = f (); + if (p_5 == 0B) + goto ; [30.00%] + else + goto ; [70.00%] + + + # p_2 = PHI <0B(5), p_5(4)> + q ={v} {CLOBBER}; + return p_2; + + + p_5->i = i_6(D); + goto ; [100.00%] + + + q.i = i_6(D); + goto ; [100.00%] + } + + and no warning. */ +} 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/alias-37.c b/gcc/testsuite/gcc.dg/tree-ssa/alias-37.c index 37eaaa66a10..6956209575a 100644 --- a/gcc/testsuite/gcc.dg/tree-ssa/alias-37.c +++ b/gcc/testsuite/gcc.dg/tree-ssa/alias-37.c @@ -11,7 +11,7 @@ int *foo (int bogus, int n) p = &a[2]; else p = &i; - return p; + return p; /* { dg-warning "\\\[-Wreturn-local-addr" } */ } /* { dg-final { scan-tree-dump "Deleted dead store" "dse1" } } */ 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" } */ diff --git a/libgcc/generic-morestack.c b/libgcc/generic-morestack.c index 0f6f0005f99..2dc373305fb 100644 --- a/libgcc/generic-morestack.c +++ b/libgcc/generic-morestack.c @@ -23,6 +23,8 @@ a copy of the GCC Runtime Library Exception along with this program; see the files COPYING3 and COPYING.RUNTIME respectively. If not, see . */ +#pragma GCC optimize ("no-isolate-erroneous-paths-dereference") + /* powerpc 32-bit not supported. */ #if !defined __powerpc__ || defined __powerpc64__