PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result
authorMartin Sebor <msebor@redhat.com>
Tue, 9 Jul 2019 04:15:42 +0000 (04:15 +0000)
committerMartin Sebor <msebor@gcc.gnu.org>
Tue, 9 Jul 2019 04:15:42 +0000 (22:15 -0600)
PR middle-end/71924 - missing -Wreturn-local-addr returning alloca result
PR middle-end/90549 - missing -Wreturn-local-addr maybe returning an address of a local array plus offset

gcc/ChangeLog:

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

gcc/testsuite/ChangeLog:

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

From-SVN: r273261

20 files changed:
gcc/ChangeLog
gcc/gimple-ssa-isolate-paths.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.c-torture/execute/return-addr.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Walloca-4.c
gcc/testsuite/gcc.dg/Wreturn-local-addr-10.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-2.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-3.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-4.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-5.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-6.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-7.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-8.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/Wreturn-local-addr-9.c [new file with mode: 0644]
gcc/testsuite/gcc.dg/pr41551.c
gcc/testsuite/gcc.dg/pr59523.c
gcc/testsuite/gcc.dg/tree-ssa/alias-37.c
gcc/testsuite/gcc.dg/tree-ssa/pr88775-2.c
gcc/testsuite/gcc.dg/winline-7.c
libgcc/generic-morestack.c

index 543337667dd302c323669f8f7cba22b4f9f95d3c..a6c99aca4b236bc9e7baf98a32b6ed3a4d412e4a 100644 (file)
@@ -1,3 +1,17 @@
+2019-07-08  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/71924
+       PR middle-end/90549
+       * gimple-ssa-isolate-paths.c (isolate_path): Add attribute.  Update
+       comment.
+       (args_loc_t): New type.
+       (args_loc_t, locmap_t): same.
+       (diag_returned_locals): New function.
+       (is_addr_local): Same.
+       (handle_return_addr_local_phi_arg, warn_return_addr_local): Same.
+       (find_implicit_erroneous_behavior): Call warn_return_addr_local_phi_arg.
+       (find_explicit_erroneous_behavior): Call warn_return_addr_local.
+
 2019-07-08  Jakub Jelinek  <jakub@redhat.com>
 
        * tree-vect-stmts.c (scan_operand_equal_p): Look through MEM_REF
index 33fe352bb239d82586f49a5464cbc2e470eeb0be..72e6c779c34e0ef00730eb1fa91b53e99d98d623 100644 (file)
@@ -128,9 +128,9 @@ insert_trap (gimple_stmt_iterator *si_p, tree op)
 
    DUPLICATE is a pre-existing duplicate, use it as BB' if it exists.
 
-   Return BB'.  */
+   Return BB' (which may be equal to DUPLICATE).  */
 
-basic_block
+ATTRIBUTE_RETURNS_NONNULL basic_block
 isolate_path (basic_block bb, basic_block duplicate,
              edge e, gimple *stmt, tree op, bool ret_zero)
 {
@@ -341,6 +341,322 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
   return false;
 }
 
+/* Describes the property of a return statement that may return
+   the address of one or more local variables.  The type must
+   be safely assignable and copyable so that it can be stored in
+   a hash_map.  */
+class args_loc_t
+{
+ public:
+
+  args_loc_t (): nargs (), locvec (), ptr (&ptr)
+  {
+    locvec.create (4);
+  }
+
+  args_loc_t (const args_loc_t &rhs)
+    : nargs (rhs.nargs), locvec (rhs.locvec.copy ()), ptr (&ptr) { }
+
+  args_loc_t& operator= (const args_loc_t &rhs)
+  {
+    nargs = rhs.nargs;
+    locvec.release ();
+    locvec = rhs.locvec.copy ();
+    return *this;
+  }
+
+  ~args_loc_t ()
+  {
+    locvec.release ();
+    gcc_assert (ptr == &ptr);
+  }
+
+  /* For a PHI in a return statement its number of arguments.  When greater
+     than LOCVEC.LENGTH () implies that an address of one of the locals in
+     LOCVEC may but need not be returned by the statement.  Otherwise,
+     unless both are zero, it implies it definitely is returned.  */
+  unsigned nargs;
+  /* The locations of local variables/alloca calls returned by the return
+     statement.  Avoid using auto_vec here since it's not safe to copy due
+     to pr90904.  */
+  vec <location_t> locvec;
+  void *ptr;
+};
+
+/* A mapping from a return statement to the locations of local variables
+   whose addresses it may return.  */
+typedef hash_map <gimple *, args_loc_t> locmap_t;
+
+/* Given the LOCMAP mapping, issue diagnostics about returning addresses
+   of local variables.  When MAYBE is set, all diagnostics will be of
+   the "may return" kind.  Otherwise each will be determined based on
+   the equality of the corresponding NARGS and LOCVEC.LENGTH () values.  */
+
+static void
+diag_returned_locals (bool maybe, const locmap_t &locmap)
+{
+  for (locmap_t::iterator it = locmap.begin (); it != locmap.end (); ++it)
+    {
+      gimple *stmt = (*it).first;
+      const args_loc_t &argsloc = (*it).second;
+      location_t stmtloc = gimple_location (stmt);
+
+      auto_diagnostic_group d;
+      unsigned nargs = argsloc.locvec.length ();
+      if (warning_at (stmtloc, OPT_Wreturn_local_addr,
+                     (maybe || argsloc.nargs > nargs
+                      ? G_("function may return address of local variable")
+                      : G_("function returns address of local variable"))))
+       {
+         for (unsigned i = 0; i != nargs; ++i)
+           inform (argsloc.locvec[i], "declared here");
+       }
+    }
+}
+
+/* Return true if EXPR is an expression of pointer type that refers
+   to the address of one or more variables with automatic storage
+   duration.  If so, add an entry to *PLOCMAP and insert into
+   PLOCMAP->LOCVEC the locations of the corresponding local variables
+   whose address is returned by the RETURN_STMT (which may be set to
+   (gimple*)-1 as a placeholder for such a statement).  VISITED is
+   a bitmap of PHI nodes already visited by recursive calls.  When
+   null, PHI expressions are not considered.  */
+
+static bool
+is_addr_local (gimple *return_stmt, tree exp, locmap_t *plocmap,
+              hash_set<gphi *> *visited)
+{
+  if (TREE_CODE (exp) == ADDR_EXPR)
+    {
+      tree baseaddr = get_base_address (TREE_OPERAND (exp, 0));
+      if (TREE_CODE (baseaddr) == MEM_REF)
+       return is_addr_local (return_stmt, TREE_OPERAND (baseaddr, 0),
+                             plocmap, visited);
+
+      if ((!VAR_P (baseaddr)
+          || is_global_var (baseaddr))
+         && TREE_CODE (baseaddr) != PARM_DECL)
+       return false;
+
+      args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
+      argsloc.locvec.safe_push (DECL_SOURCE_LOCATION (baseaddr));
+      return true;
+    }
+
+  if (!POINTER_TYPE_P (TREE_TYPE (exp)))
+    return false;
+
+  if (TREE_CODE (exp) == SSA_NAME)
+    {
+      gimple *def_stmt = SSA_NAME_DEF_STMT (exp);
+      enum gimple_code code = gimple_code (def_stmt);
+
+      if (is_gimple_assign (def_stmt))
+       {
+         tree type = TREE_TYPE (gimple_assign_lhs (def_stmt));
+         if (POINTER_TYPE_P (type))
+           {
+             tree_code code = gimple_assign_rhs_code (def_stmt);
+             tree ptr1 = NULL_TREE, ptr2 = NULL_TREE;
+
+             /* Set to the number of arguments examined that should
+                be added to ARGSLOC->NARGS to identify expressions
+                only some but not all of whose operands refer to local
+                addresses.  */
+             unsigned nargs = 0;
+             if (code == COND_EXPR)
+               {
+                 ptr1 = gimple_assign_rhs2 (def_stmt);
+                 ptr2 = gimple_assign_rhs3 (def_stmt);
+                 nargs = 2;
+               }
+             else if (code == MAX_EXPR || code == MIN_EXPR)
+               {
+                 ptr1 = gimple_assign_rhs1 (def_stmt);
+                 ptr2 = gimple_assign_rhs2 (def_stmt);
+                 nargs = 2;
+               }
+             else if (code == ADDR_EXPR
+                      || code == NOP_EXPR
+                      || code == POINTER_PLUS_EXPR)
+               /* Leave NARGS at zero and let the recursive call set it.  */
+               ptr1 = gimple_assign_rhs1 (def_stmt);
+
+             /* Avoid short-circuiting the logical OR result in case
+                both operands refer to local variables, in which case
+                both should be considered and identified in the warning.  */
+             bool res1 = false, res2 = false;
+             if (ptr1)
+               res1 = is_addr_local (return_stmt, ptr1, plocmap, visited);
+             if (ptr2)
+               res2 = is_addr_local (return_stmt, ptr2, plocmap, visited);
+
+             if (nargs)
+               if (args_loc_t *argsloc = plocmap->get (return_stmt))
+                 argsloc->nargs += nargs;
+
+             return res1 || res2;
+           }
+         return false;
+       }
+
+      if (code == GIMPLE_CALL
+         && gimple_call_builtin_p (def_stmt))
+       {
+         /* Handle alloca and friends that return pointers to automatic
+            storage.  */
+         tree fn = gimple_call_fndecl (def_stmt);
+         int code = DECL_FUNCTION_CODE (fn);
+         if (code == BUILT_IN_ALLOCA
+             || code == BUILT_IN_ALLOCA_WITH_ALIGN
+             || code == BUILT_IN_ALLOCA_WITH_ALIGN_AND_MAX)
+           {
+             args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
+             argsloc.locvec.safe_push (gimple_location (def_stmt));
+             return true;
+           }
+
+         if (gimple_call_num_args (def_stmt) < 1)
+           return false;
+
+         /* Recursively examine the first argument of calls to built-ins
+            that return it.  */
+         switch (code)
+           {
+           case BUILT_IN_MEMCPY:
+           case BUILT_IN_MEMCPY_CHK:
+           case BUILT_IN_MEMPCPY:
+           case BUILT_IN_MEMPCPY_CHK:
+           case BUILT_IN_MEMMOVE:
+           case BUILT_IN_MEMMOVE_CHK:
+           case BUILT_IN_STPCPY:
+           case BUILT_IN_STPCPY_CHK:
+           case BUILT_IN_STPNCPY:
+           case BUILT_IN_STPNCPY_CHK:
+           case BUILT_IN_STRCAT:
+           case BUILT_IN_STRCAT_CHK:
+           case BUILT_IN_STRCHR:
+           case BUILT_IN_STRCPY:
+           case BUILT_IN_STRCPY_CHK:
+           case BUILT_IN_STRNCAT:
+           case BUILT_IN_STRNCAT_CHK:
+           case BUILT_IN_STRNCPY:
+           case BUILT_IN_STRNCPY_CHK:
+           case BUILT_IN_STRRCHR:
+           case BUILT_IN_STRSTR:
+             return is_addr_local (return_stmt,
+                                   gimple_call_arg (def_stmt, 0),
+                                   plocmap, visited);
+           default:
+             return false;
+           }
+       }
+
+      if (code == GIMPLE_PHI && visited)
+       {
+         gphi *phi_stmt = as_a <gphi *> (def_stmt);
+         if (visited->add (phi_stmt))
+           return false;
+
+         unsigned count = 0;
+         unsigned nargs = gimple_phi_num_args (phi_stmt);
+         args_loc_t &argsloc = plocmap->get_or_insert (return_stmt);
+         /* Bump up the number of operands examined by the number of
+            operands of this PHI.  */
+         argsloc.nargs += nargs;
+         for (unsigned i = 0; i < gimple_phi_num_args (phi_stmt); ++i)
+           {
+             tree arg = gimple_phi_arg_def (phi_stmt, i);
+             if (is_addr_local (return_stmt, arg, plocmap, visited))
+               ++count;
+           }
+         return count != 0;
+       }
+    }
+
+  return false;
+}
+
+/* Detect returning the address of a local variable in a PHI result LHS
+   and argument ARG and PHI edge E in basic block BB.  Add an entry for
+   each use to LOCMAP, setting its NARGS member to the NARGS argument
+   (the number of PHI operands) plus the number of arguments in binary
+   expressions refereced by ARG.  Call isolate_path for each returned
+   address and set *ISOLATED to true if called.
+   Return either DUPLICATE or the most recent result of isolate_path.  */
+
+static basic_block
+handle_return_addr_local_phi_arg (basic_block bb, basic_block duplicate,
+                                 tree lhs, tree arg, edge e, locmap_t &locmap,
+                                 unsigned nargs, bool *isolated)
+{
+  /* Use (gimple*)-1 as a temporary placeholder and replace it with
+     the return statement below once it is known.  Using a null doesn't
+     work because it's used by the hash_map to mean "no-entry."  Pass
+     null instead of a visited_phis bitmap to avoid descending into
+     PHIs since they are being processed by the caller.  Those that
+     remain will be checked again later.  */
+  if (!is_addr_local ((gimple*)-1, arg, &locmap, NULL))
+    {
+      /* Remove the placeholder regardless of success or failure.  */
+      locmap.remove ((gimple*)-1);
+      return duplicate;
+    }
+
+  const args_loc_t* const placeargsloc = locmap.get ((gimple*)-1);
+  const unsigned nlocs = placeargsloc->locvec.length ();
+  gcc_assert (nlocs);
+
+  /* Add to the number of PHI arguments determined by the caller
+     the number of operands of the expressions referenced by ARG.
+     This lets the caller determine whether it's dealing with
+     a "may return" or "definitely returns."  */
+  nargs += placeargsloc->nargs;
+
+  /* Set to true if any expressions referenced by ARG involve
+     multiple addresses only some of which are those of locals.  */
+  bool maybe = placeargsloc->nargs > placeargsloc->locvec.length ();
+
+  gimple *use_stmt;
+  imm_use_iterator iter;
+
+  /* Look for uses of the PHI result LHS in return statements.  */
+  FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
+    {
+      greturn *return_stmt = dyn_cast <greturn *> (use_stmt);
+      if (!return_stmt)
+       continue;
+
+      if (gimple_return_retval (return_stmt) != lhs)
+       continue;
+
+      /* Add an entry for the return statement and the locations
+        oof the PHI arguments obtained above to the map.  */
+      args_loc_t &argsloc = locmap.get_or_insert (use_stmt);
+      argsloc.nargs = nargs;
+      unsigned nelts = argsloc.locvec.length () + nlocs;
+      argsloc.locvec.reserve (nelts);
+      argsloc.locvec.splice (placeargsloc->locvec);
+
+      if (!maybe
+         && (flag_isolate_erroneous_paths_dereference
+             || flag_isolate_erroneous_paths_attribute)
+         && gimple_bb (use_stmt) == bb)
+       {
+         duplicate = isolate_path (bb, duplicate, e,
+                                   use_stmt, lhs, true);
+
+         /* Let caller know the path has been isolated.  */
+         *isolated = true;
+       }
+    }
+
+  locmap.remove ((gimple*)-1);
+
+  return duplicate;
+}
+
 /* Look for PHI nodes which feed statements in the same block where
    the value of the PHI node implies the statement is erroneous.
 
@@ -352,6 +668,8 @@ stmt_uses_0_or_null_in_undefined_way (gimple *stmt)
 static void
 find_implicit_erroneous_behavior (void)
 {
+  locmap_t locmap;
+
   basic_block bb;
 
   FOR_EACH_BB_FN (bb, cfun)
@@ -388,70 +706,46 @@ find_implicit_erroneous_behavior (void)
          gphi *phi = si.phi ();
          tree lhs = gimple_phi_result (phi);
 
+         /* Initial number of PHI arguments.  The result may change
+            from one iteration of the loop below to the next in
+            response to changes to the CFG but only the initial
+            value is stored below for use by diagnostics.  */
+         unsigned nargs = gimple_phi_num_args (phi);
+
          /* PHI produces a pointer result.  See if any of the PHI's
             arguments are NULL.
 
             When we remove an edge, we want to reprocess the current
-            index, hence the ugly way we update I for each iteration.  */
+            index since the argument at that index will have been
+            removed, hence the ugly way we update I for each iteration.  */
          basic_block duplicate = NULL;
          for (unsigned i = 0, next_i = 0;
-              i < gimple_phi_num_args (phi);
-              i = next_i)
+              i < gimple_phi_num_args (phi); i = next_i)
            {
-             tree op = gimple_phi_arg_def (phi, i);
+             tree arg = gimple_phi_arg_def (phi, i);
              edge e = gimple_phi_arg_edge (phi, i);
-             imm_use_iterator iter;
-             gimple *use_stmt;
 
+             /* Advance the argument index unless a path involving
+                the current argument has been isolated.  */
              next_i = i + 1;
-
-             if (TREE_CODE (op) == ADDR_EXPR)
+             bool isolated = false;
+             duplicate = handle_return_addr_local_phi_arg (bb, duplicate, lhs,
+                                                           arg, e, locmap,
+                                                           nargs, &isolated);
+             if (isolated)
                {
-                 tree valbase = get_base_address (TREE_OPERAND (op, 0));
-                 if ((VAR_P (valbase) && !is_global_var (valbase))
-                     || TREE_CODE (valbase) == PARM_DECL)
-                   {
-                     FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
-                       {
-                         greturn *return_stmt
-                           = dyn_cast <greturn *> (use_stmt);
-                         if (!return_stmt)
-                           continue;
-
-                         if (gimple_return_retval (return_stmt) != lhs)
-                           continue;
-
-                         {
-                           auto_diagnostic_group d;
-                           if (warning_at (gimple_location (use_stmt),
-                                             OPT_Wreturn_local_addr,
-                                             "function may return address "
-                                             "of local variable"))
-                             inform (DECL_SOURCE_LOCATION(valbase),
-                                       "declared here");
-                         }
-
-                         if ((flag_isolate_erroneous_paths_dereference
-                              || flag_isolate_erroneous_paths_attribute)
-                             && gimple_bb (use_stmt) == bb)
-                           {
-                             duplicate = isolate_path (bb, duplicate, e,
-                                                       use_stmt, lhs, true);
-
-                             /* When we remove an incoming edge, we need to
-                                reprocess the Ith element.  */
-                             next_i = i;
-                             cfg_altered = true;
-                           }
-                       }
-                   }
+                 cfg_altered = true;
+                 next_i = i;
                }
 
-             if (!integer_zerop (op))
+             if (!integer_zerop (arg))
                continue;
 
              location_t phi_arg_loc = gimple_phi_arg_location (phi, i);
 
+             imm_use_iterator iter;
+             gimple *use_stmt;
+
              /* We've got a NULL PHI argument.  Now see if the
                 PHI's result is dereferenced within BB.  */
              FOR_EACH_IMM_USE_STMT (use_stmt, iter, lhs)
@@ -480,6 +774,57 @@ find_implicit_erroneous_behavior (void)
            }
        }
     }
+
+  diag_returned_locals (false, locmap);
+}
+
+/* Detect and diagnose returning the address of a local variable
+   in RETURN_STMT in basic block BB.  This only becomes undefined
+   behavior if the result is used, so we do not insert a trap and
+   only return NULL instead.  */
+
+static void
+warn_return_addr_local (basic_block bb, greturn *return_stmt)
+{
+  tree val = gimple_return_retval (return_stmt);
+  if (!val)
+    return;
+
+  locmap_t locmap;
+  hash_set<gphi *> visited_phis;
+  if (!is_addr_local (return_stmt, val, &locmap, &visited_phis))
+    return;
+
+  /* We only need it for this particular case.  */
+  calculate_dominance_info (CDI_POST_DOMINATORS);
+
+  const args_loc_t *argsloc = locmap.get (return_stmt);
+  gcc_assert (argsloc);
+
+  bool maybe = argsloc->nargs > argsloc->locvec.length ();
+  if (!maybe)
+    maybe = !dominated_by_p (CDI_POST_DOMINATORS,
+                            single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
+
+  diag_returned_locals (maybe, locmap);
+
+  /* Bail if the statement isn't certain to return the address
+     of a local (e.g., if it involves a conditional expression
+     that wasn't trasnformed into a PHI or if it involves
+     a MAX_EXPR or MIN_EXPR only one of whose operands is a local
+     (even though such an expression isn't valid in C or has
+     defined semantics in C++).  */
+  if (maybe)
+    return;
+
+  /* Do not modify code if the user only asked for warnings.  */
+  if (flag_isolate_erroneous_paths_dereference
+      || flag_isolate_erroneous_paths_attribute)
+    {
+      tree zero = build_zero_cst (TREE_TYPE (val));
+      gimple_return_set_retval (return_stmt, zero);
+      update_stmt (return_stmt);
+    }
 }
 
 /* Look for statements which exhibit erroneous behavior.  For example
@@ -525,49 +870,10 @@ find_explicit_erroneous_behavior (void)
              break;
            }
 
-         /* Detect returning the address of a local variable.  This only
-            becomes undefined behavior if the result is used, so we do not
-            insert a trap and only return NULL instead.  */
+         /* Look for a return statement that returns the address
+            of a local variable or the result of alloca.  */
          if (greturn *return_stmt = dyn_cast <greturn *> (stmt))
-           {
-             tree val = gimple_return_retval (return_stmt);
-             if (val && TREE_CODE (val) == ADDR_EXPR)
-               {
-                 tree valbase = get_base_address (TREE_OPERAND (val, 0));
-                 if ((VAR_P (valbase) && !is_global_var (valbase))
-                     || TREE_CODE (valbase) == PARM_DECL)
-                   {
-                     /* We only need it for this particular case.  */
-                     calculate_dominance_info (CDI_POST_DOMINATORS);
-                     const char* msg;
-                     bool always_executed = dominated_by_p
-                       (CDI_POST_DOMINATORS,
-                        single_succ (ENTRY_BLOCK_PTR_FOR_FN (cfun)), bb);
-                     if (always_executed)
-                       msg = N_("function returns address of local variable");
-                     else
-                       msg = N_("function may return address of "
-                                "local variable");
-                     {
-                       auto_diagnostic_group d;
-                       if (warning_at (gimple_location (stmt),
-                                         OPT_Wreturn_local_addr, msg))
-                         inform (DECL_SOURCE_LOCATION(valbase),
-                                 "declared here");
-                     }
-
-                     /* Do not modify code if the user only asked for
-                        warnings.  */
-                     if (flag_isolate_erroneous_paths_dereference
-                         || flag_isolate_erroneous_paths_attribute)
-                       {
-                         tree zero = build_zero_cst (TREE_TYPE (val));
-                         gimple_return_set_retval (return_stmt, zero);
-                         update_stmt (stmt);
-                       }
-                   }
-               }
-           }
+           warn_return_addr_local (bb, return_stmt);
        }
     }
 }
index 31ba4200c041c889361023ec26ba64934aa9b362..2941e438c43c877c4fea5f3328d3f8f7af308475 100644 (file)
@@ -1,3 +1,23 @@
+2019-07-08  Martin Sebor  <msebor@redhat.com>
+
+       PR middle-end/71924
+       PR middle-end/90549
+       * gcc.c-torture/execute/return-addr.c: New test.
+       * gcc.dg/Wreturn-local-addr-2.c: New test.
+       * gcc.dg/Wreturn-local-addr-4.c: New test.
+       * gcc.dg/Wreturn-local-addr-5.c: New test.
+       * gcc.dg/Wreturn-local-addr-6.c: New test.
+       * gcc.dg/Wreturn-local-addr-7.c: New test.
+       * gcc.dg/Wreturn-local-addr-8.c: New test.
+       * gcc.dg/Wreturn-local-addr-9.c: New test.
+       * gcc.dg/Wreturn-local-addr-10.c: New test.
+       * gcc.dg/Walloca-4.c: Handle expected warnings.
+       * gcc.dg/pr41551.c: Same.
+       * gcc.dg/pr59523.c: Same.
+       * gcc.dg/tree-ssa/pr88775-2.c: Same.
+       * gcc.dg/tree-ssa/alias-37.c: Same.
+       * gcc.dg/winline-7.c: Same.
+
 2019-07-08  Jakub Jelinek  <jakub@redhat.com>
 
        * g++.dg/vect/simd-6.cc: Replace xfail with target x86.
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 (file)
index 0000000..7981818
--- /dev/null
@@ -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));
+}
index 85dcb7b9bb99d7d22bf78c8b69b0036c412ba977..1fbed597b98a573b156c9d87c5b22aa8b00ca29d 100644 (file)
@@ -7,11 +7,12 @@
 {
 
   char *src;
- _Bool 
-      use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
- if (use_alloca)
+  _Bool use_alloca = (((rear_ptr - w) * sizeof (char)) < 4096U);
+  if (use_alloca)
     src = (char *) __builtin_alloca ((rear_ptr - w) * sizeof (char));
   else
     src = (char *) __builtin_malloc ((rear_ptr - w) * sizeof (char));
   return src;
 }
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
diff --git a/gcc/testsuite/gcc.dg/Wreturn-local-addr-10.c b/gcc/testsuite/gcc.dg/Wreturn-local-addr-10.c
new file mode 100644 (file)
index 0000000..ddd2c36
--- /dev/null
@@ -0,0 +1,56 @@
+/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
+   Test reduced from libstdc++-v3/testsuite/ext/ext_pointer/1.cc.
+   It verifies that iteration in find_implicit_erroneous_behavior
+   in gimple-ssa-isolate-path.c terminates under specific conditions.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __UINTPTR_TYPE__ uintptr_t;
+
+struct A { int i; };
+struct P { uintptr_t d; };
+
+static inline struct A* get (const struct P *p)
+{
+  if (p->d == 1)
+    return 0;
+
+  return (struct A*)((uintptr_t)p + p->d);
+}
+
+static inline void set (struct P *p, struct A* q)
+{
+  /* The basic block below would cause an infinite loop in
+     find_implicit_erroneous_behavior due to assuming the DUPLICATE
+     pointer returned from isolate_path would distinct from the one
+     passed to it.  (Replacing the if statement with the ternary ?:
+     expression did not have this effect (it gets optimized early
+     on).
+    <bb 4> [local count: 1073741823]:
+    # _14 = PHI <0B(2), &MEM <struct A[2]> [(void *)&a + 4B](3)>
+    _2 = _14->i;
+    if (_2 != 2)
+      goto <bb 5>; [0.00%]
+    else
+      goto <bb 6>; [100.00%]
+  */
+  if (!q)
+    p->d = 1;
+  else
+    p->d = (uintptr_t)(q) - (uintptr_t)(p);
+}
+
+void f (void)
+{
+  struct A a[2] = { { 1 }, { 2 } };
+
+  struct P p, q;
+  set (&p, a);
+  set (&q, get (&p));
+
+  set (&q, get (&q) + 0);
+  set (&q, get (&q) + 1);
+
+  if (get (&q)[0].i != get (&p)[1].i)
+    __builtin_abort ();
+}
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 (file)
index 0000000..0e3435c
--- /dev/null
@@ -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 (file)
index 0000000..6dad7af
--- /dev/null
@@ -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 (file)
index 0000000..0a451ef
--- /dev/null
@@ -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 (file)
index 0000000..bdf1cd4
--- /dev/null
@@ -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 (file)
index 0000000..70138b3
--- /dev/null
@@ -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 (file)
index 0000000..ac1fb76
--- /dev/null
@@ -0,0 +1,50 @@
+/* Test to verify that a PHI with a COND_EXPR argument in a return
+   statement is handled correctly.
+  { dg-do compile }
+  { dg-options "-O2 -Wall" } */
+
+extern struct S s;
+
+void* f (int n)
+{
+  void *p;
+  int x = 0;
+
+  for (int i = n; i >= 0; i--)
+    {
+      p = &s;
+      if (p == (void*)-1)
+         x = 1;
+      else if (p)
+         return p;
+    }
+
+  /* The return statement below ends up with the following IL:
+     <bb 6> [local count: 59055800]:
+     # x_10 = PHI <1(5), 0(2)>
+     _5 = x_10 != 0 ? -1B : 0B;
+
+     <bb 7> [local count: 114863532]:
+     # _3 = PHI <&s(4), _5(6), &s(3)>
+     return _3;  */
+  return x ? (void*)-1 : 0;
+}
+
+void* g (int n)
+{
+  void *p;
+  int x = 0;                  /* { dg-message "declared here" } */
+
+  for (int i = n; i >= 0; i--)
+    {
+      p = &s;
+      if (p == (void*)-1)
+         x = 1;
+      else if (p)
+         return p;
+    }
+
+  /* The return statement below does not reference a COND_EXPR argument.  */
+  return x ? &x : 0;          /* { dg-warning "may return address of local variable" "missing location" { xfail *-*-* } } */
+  /* { dg-warning "may return address of local variable" "pr90735" { target *-*-* } 0 } */
+}
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 (file)
index 0000000..98a3805
--- /dev/null
@@ -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 (file)
index 0000000..d24f911
--- /dev/null
@@ -0,0 +1,73 @@
+/* PR c/71924 - missing -Wreturn-local-addr returning alloca result
+   Test derived from gcc.c-torture/execute/20071108-1.c.  It shows
+   a false positive at -Os caused by the jump threading/vrp1 pass.
+   { dg-do compile }
+   { dg-options "-Os -fdump-tree-optimized" } */
+
+struct S
+{
+  int i;
+};
+
+void* f (void);
+
+__attribute__ ((noinline))
+struct S* g (int i)
+{
+  struct S *p = f (), q;
+
+  if (p == 0)
+    p = &q;
+
+  p->i = i;
+
+  if (p == &q)
+    p = 0;
+
+  /* With -Os the warning pass sees:
+
+       ...
+       <bb 4>
+       # p_1 = PHI <&q(2), p_5(3)>
+       p_1->i = i_6(D);
+       if (&q == p_1)
+         goto <bb 6>; [14.90%]
+       else
+         goto <bb 5>; [85.10%]
+
+       <bb 5>
+
+       <bb 6>
+       # p_2 = PHI <0B(4), p_1(5)>
+       q ={v} {CLOBBER};
+       return p_2;
+     }
+
+     which leads to:  */
+  return p;         /* { dg-bogus "may return address of local variable" "" { xfail *-*-* } } */
+
+  /* Whereas as -O2 the pass sees:
+
+       <bb 2>
+       p_5 = f ();
+       if (p_5 == 0B)
+         goto <bb 4>; [30.00%]
+       else
+         goto <bb 3>; [70.00%]
+
+       <bb 3>
+       # p_2 = PHI <0B(5), p_5(4)>
+       q ={v} {CLOBBER};
+       return p_2;
+
+       <bb 4>
+       p_5->i = i_6(D);
+       goto <bb 3>; [100.00%]
+
+       <bb 5>
+       q.i = i_6(D);
+       goto <bb 3>; [100.00%]
+     }
+
+     and no warning.  */
+}
index 2f2ad2be97e19f0089080b760c04e9f19e1edb47..e1123206cc61684634a0c5d3bac5abeb30d52e69 100644 (file)
@@ -10,3 +10,5 @@ int main(void)
  int var, *p = &var;
  return (double)(uintptr_t)(p);
 }
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
index a6c3302a6831d8ee840da4b39982e9b3ddb1a89f..49cbe5dd27a61faed54c933cf86f8e53c6a79437 100644 (file)
@@ -16,3 +16,5 @@ foo (int a, int *b, int *c, int *d)
       r[i] = 1;
   return r;
 }
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
index 2fb0e0c3188f6d3791333f081134eb0f3a909b3b..c913d26d1323216245ee3cb420cb5669bdc27f61 100644 (file)
@@ -12,7 +12,7 @@ int *foo (int bogus, int n)
     p = &a[2];
   else
     p = &i;
-  return p;
+  return p;         /* { dg-warning "\\\[-Wreturn-local-addr" } */
 }
 
 /* { dg-final { scan-tree-dump "Deleted dead store" "dse1" } } */
index 292ce6edefc9913320eaadd745ad2368b4ecf528..ed5df8264327e47ea71ebb677dcf6649c6656fc2 100644 (file)
@@ -41,3 +41,5 @@ f5 (void)
   int c[64] = {}, d[64] = {};
   return (__UINTPTR_TYPE__) &c[64] != (__UINTPTR_TYPE__) &d[0];
 }
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
index 34deca42592dfc5a33c0fa8fe4f24fe3d967f024..239d748926d8dbe59107c50603f83e18bcec1b49 100644 (file)
@@ -13,3 +13,5 @@ inline void *t (void)
 {
        return q ();             /* { dg-message "called from here" } */
 }
+
+/* { dg-prune-output "-Wreturn-local-addr" } */
index 0f6f0005f994f384468d5b60eed7067a0eb5fac7..2dc373305fb53f80318bd9d50cbf5d1ce694783a 100644 (file)
@@ -23,6 +23,8 @@ a copy of the GCC Runtime Library Exception along with this program;
 see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 <http://www.gnu.org/licenses/>.  */
 
+#pragma GCC optimize ("no-isolate-erroneous-paths-dereference")
+
 /* powerpc 32-bit not supported.  */
 #if !defined __powerpc__ || defined __powerpc64__