[TCWG CI] Regression caused by gcc: Add -Wdangling-pointer [PR63272].:
commit 9d6a0f388eb048f8d87f47af78f07b5ce513bfe6
Author: Martin Sebor <mse...@redhat.com>

    Add -Wdangling-pointer [PR63272].

Results regressed to
# reset_artifacts:
-10
# build_abe binutils:
-9
# build_abe stage1:
-5
# build_abe qemu:
-2
# linux_n_obj:
21324
# First few build errors in logs:
# 00:03:31 sound/core/oss/mixer_oss.c:1057:21: error: ‘slot’ is used 
uninitialized [-Werror=uninitialized]
# 00:03:32 sound/core/oss/pcm_oss.c:108:29: error: ‘t’ is used uninitialized 
[-Werror=uninitialized]
# 00:03:32 sound/core/oss/pcm_oss.c:2488:34: error: ‘setup’ is used 
uninitialized [-Werror=uninitialized]
# 00:03:32 sound/core/oss/pcm_oss.c:2998:51: error: ‘template’ is used 
uninitialized [-Werror=uninitialized]
# 00:03:35 make[3]: *** [scripts/Makefile.build:277: 
sound/core/oss/mixer_oss.o] Error 1
# 00:03:35 sound/core/seq/oss/seq_oss_init.c:350:35: error: ‘qinfo’ is used 
uninitialized [-Werror=uninitialized]
# 00:03:35 sound/core/seq/oss/seq_oss_init.c:370:35: error: ‘qinfo’ is used 
uninitialized [-Werror=uninitialized]
# 00:03:36 make[4]: *** [scripts/Makefile.build:277: 
sound/core/seq/oss/seq_oss_init.o] Error 1
# 00:03:40 make[3]: *** [scripts/Makefile.build:277: sound/core/oss/pcm_oss.o] 
Error 1
# 00:03:50 make[3]: *** [scripts/Makefile.build:540: sound/core/seq/oss] Error 2

from
# reset_artifacts:
-10
# build_abe binutils:
-9
# build_abe stage1:
-5
# build_abe qemu:
-2
# linux_n_obj:
21354

THIS IS THE END OF INTERESTING STUFF.  BELOW ARE LINKS TO BUILDS, REPRODUCTION 
INSTRUCTIONS, AND THE RAW COMMIT.

This commit has regressed these CI configurations:
 - tcwg_kernel/gnu-master-aarch64-stable-allmodconfig

First_bad build: 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/build-9d6a0f388eb048f8d87f47af78f07b5ce513bfe6/
Last_good build: 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/build-671a283636de75f7ed638ee6b01ed2d44361b8b6/
Baseline build: 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/build-baseline/
Even more details: 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/

Reproduce builds:
<cut>
mkdir investigate-gcc-9d6a0f388eb048f8d87f47af78f07b5ce513bfe6
cd investigate-gcc-9d6a0f388eb048f8d87f47af78f07b5ce513bfe6

# Fetch scripts
git clone https://git.linaro.org/toolchain/jenkins-scripts

# Fetch manifests and test.sh script
mkdir -p artifacts/manifests
curl -o artifacts/manifests/build-baseline.sh 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/manifests/build-baseline.sh
 --fail
curl -o artifacts/manifests/build-parameters.sh 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/manifests/build-parameters.sh
 --fail
curl -o artifacts/test.sh 
https://ci.linaro.org/job/tcwg_kernel-gnu-bisect-gnu-master-aarch64-stable-allmodconfig/9/artifact/artifacts/test.sh
 --fail
chmod +x artifacts/test.sh

# Reproduce the baseline build (build all pre-requisites)
./jenkins-scripts/tcwg_kernel-build.sh @@ artifacts/manifests/build-baseline.sh

# Save baseline build state (which is then restored in artifacts/test.sh)
mkdir -p ./bisect
rsync -a --del --delete-excluded --exclude /bisect/ --exclude /artifacts/ 
--exclude /gcc/ ./ ./bisect/baseline/

cd gcc

# Reproduce first_bad build
git checkout --detach 9d6a0f388eb048f8d87f47af78f07b5ce513bfe6
../artifacts/test.sh

# Reproduce last_good build
git checkout --detach 671a283636de75f7ed638ee6b01ed2d44361b8b6
../artifacts/test.sh

cd ..
</cut>

Full commit (up to 1000 lines):
<cut>
commit 9d6a0f388eb048f8d87f47af78f07b5ce513bfe6
Author: Martin Sebor <mse...@redhat.com>
Date:   Sat Jan 15 16:41:40 2022 -0700

    Add -Wdangling-pointer [PR63272].
    
    Resolves:
    PR c/63272 - GCC should warn when using pointer to dead scoped variable with
    in the same function
    
    gcc/c-family/ChangeLog:
    
            PR c/63272
            * c.opt (-Wdangling-pointer): New option.
    
    gcc/ChangeLog:
    
            PR c/63272
            * diagnostic-spec.c (nowarn_spec_t::nowarn_spec_t): Handle
            -Wdangling-pointer.
            * doc/invoke.texi (-Wdangling-pointer): Document new option.
            * gimple-ssa-warn-access.cc (pass_waccess::clone): Set new member.
            (pass_waccess::check_pointer_uses): New function.
            (pass_waccess::gimple_call_return_arg): New function.
            (pass_waccess::gimple_call_return_arg_ref): New function.
            (pass_waccess::check_call_dangling): New function.
            (pass_waccess::check_dangling_uses): New function overloads.
            (pass_waccess::check_dangling_stores): New function.
            (pass_waccess::check_dangling_stores): New function.
            (pass_waccess::m_clobbers): New data member.
            (pass_waccess::m_func): New data member.
            (pass_waccess::m_run_number): New data member.
            (pass_waccess::m_check_dangling_p): New data member.
            (pass_waccess::check_alloca): Check m_early_checks_p.
            (pass_waccess::check_alloc_size_call): Same.
            (pass_waccess::check_strcat): Same.
            (pass_waccess::check_strncat): Same.
            (pass_waccess::check_stxcpy): Same.
            (pass_waccess::check_stxncpy): Same.
            (pass_waccess::check_strncmp): Same.
            (pass_waccess::check_memop_access): Same.
            (pass_waccess::check_read_access): Same.
            (pass_waccess::check_builtin): Call check_pointer_uses.
            (pass_waccess::warn_invalid_pointer): Add arguments.
            (is_auto_decl): New function.
            (pass_waccess::check_stmt): New function.
            (pass_waccess::check_block): Call check_stmt.
            (pass_waccess::execute): Call check_dangling_uses,
            check_dangling_stores.  Empty m_clobbers.
            * passes.def (pass_warn_access): Invoke pass two more times.
    
    gcc/testsuite/ChangeLog:
    
            PR c/63272
            * g++.dg/warn/Wfree-nonheap-object-6.C: Disable valid warnings.
            * g++.dg/warn/ref-temp1.C: Prune expected warning.
            * gcc.dg/uninit-pr50476.c: Expect a new warning.
            * c-c++-common/Wdangling-pointer-2.c: New test.
            * c-c++-common/Wdangling-pointer-3.c: New test.
            * c-c++-common/Wdangling-pointer-4.c: New test.
            * c-c++-common/Wdangling-pointer-5.c: New test.
            * c-c++-common/Wdangling-pointer-6.c: New test.
            * c-c++-common/Wdangling-pointer.c: New test.
            * g++.dg/warn/Wdangling-pointer-2.C: New test.
            * g++.dg/warn/Wdangling-pointer.C: New test.
            * gcc.dg/Wdangling-pointer-2.c: New test.
            * gcc.dg/Wdangling-pointer.c: New test.
---
 gcc/c-family/c.opt                                 |   8 +
 gcc/diagnostic-spec.c                              |   1 +
 gcc/doc/invoke.texi                                |  62 +-
 gcc/gimple-ssa-warn-access.cc                      | 635 +++++++++++++++++++--
 gcc/passes.def                                     |   5 +-
 gcc/testsuite/c-c++-common/Wdangling-pointer-2.c   | 437 ++++++++++++++
 gcc/testsuite/c-c++-common/Wdangling-pointer-3.c   |  64 +++
 gcc/testsuite/c-c++-common/Wdangling-pointer-4.c   |  73 +++
 gcc/testsuite/c-c++-common/Wdangling-pointer-5.c   |  90 +++
 gcc/testsuite/c-c++-common/Wdangling-pointer-6.c   |  32 ++
 gcc/testsuite/c-c++-common/Wdangling-pointer.c     | 434 ++++++++++++++
 gcc/testsuite/g++.dg/warn/Wdangling-pointer-2.C    |  23 +
 gcc/testsuite/g++.dg/warn/Wdangling-pointer.C      |  74 +++
 gcc/testsuite/g++.dg/warn/Wfree-nonheap-object-6.C |   4 +-
 gcc/testsuite/g++.dg/warn/ref-temp1.C              |   3 +
 gcc/testsuite/gcc.dg/Wdangling-pointer-2.c         |  82 +++
 gcc/testsuite/gcc.dg/Wdangling-pointer.c           |  75 +++
 gcc/testsuite/gcc.dg/uninit-pr50476.c              |   2 +-
 18 files changed, 2043 insertions(+), 61 deletions(-)

diff --git a/gcc/c-family/c.opt b/gcc/c-family/c.opt
index 28363643664..db65c14a7a5 100644
--- a/gcc/c-family/c.opt
+++ b/gcc/c-family/c.opt
@@ -548,6 +548,14 @@ Wdangling-else
 C ObjC C++ ObjC++ Var(warn_dangling_else) Warning LangEnabledBy(C ObjC C++ 
ObjC++,Wparentheses)
 Warn about dangling else.
 
+Wdangling-pointer
+C ObjC C++ LTO ObjC++ Alias(Wdangling-pointer=, 2, 0) Warning
+Warn for uses of pointers to auto variables whose lifetime has ended.
+
+Wdangling-pointer=
+C ObjC C++ ObjC++ Joined RejectNegative UInteger Var(warn_dangling_pointer) 
Warning LangEnabledBy(C ObjC C++ ObjC++,Wall, 2, 0) IntegerRange(0, 2)
+Warn for uses of pointers to auto variables whose lifetime has ended.
+
 Wdate-time
 C ObjC C++ ObjC++ CPP(warn_date_time) CppReason(CPP_W_DATE_TIME) 
Var(cpp_warn_date_time) Init(0) Warning
 Warn about __TIME__, __DATE__ and __TIMESTAMP__ usage.
diff --git a/gcc/diagnostic-spec.c b/gcc/diagnostic-spec.c
index c9e1c1be91d..a8af229d677 100644
--- a/gcc/diagnostic-spec.c
+++ b/gcc/diagnostic-spec.c
@@ -99,6 +99,7 @@ nowarn_spec_t::nowarn_spec_t (opt_code opt)
        m_bits = NW_UNINIT;
       break;
 
+    case OPT_Wdangling_pointer_:
     case OPT_Wreturn_local_addr:
     case OPT_Wuse_after_free_:
       m_bits = NW_DANGLING;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 121c8ea827f..7f2205e4a85 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -341,7 +341,8 @@ Objective-C and Objective-C++ Dialects}.
 -Wchar-subscripts @gol
 -Wclobbered  -Wcomment @gol
 -Wconversion  -Wno-coverage-mismatch  -Wno-cpp @gol
--Wdangling-else  -Wdate-time @gol
+-Wdangling-else  -Wdangling-pointer  -Wdangling-pointer=@var{n}  @gol
+-Wdate-time @gol
 -Wno-deprecated  -Wno-deprecated-declarations  -Wno-designated-init @gol
 -Wdisabled-optimization @gol
 -Wno-discarded-array-qualifiers  -Wno-discarded-qualifiers @gol
@@ -4389,6 +4390,8 @@ Warn about overriding virtual functions that are not 
marked with the
 @opindex Wno-use-after-free
 Warn about uses of pointers to dynamically allocated objects that have
 been rendered indeterminate by a call to a deallocation function.
+The warning is enabled at all optimization levels but may yield different
+results with optimization than without.
 
 @table @gcctabopt
 @item -Wuse-after-free=1
@@ -5714,6 +5717,7 @@ Options} and @ref{Objective-C and Objective-C++ Dialect 
Options}.
 -Wcatch-value @r{(C++ and Objective-C++ only)}  @gol
 -Wchar-subscripts  @gol
 -Wcomment  @gol
+-Wdangling-pointer=2  @gol
 -Wduplicate-decl-specifier @r{(C and Objective-C only)} @gol
 -Wenum-compare @r{(in C/ObjC; this is on by default in C++)} @gol
 -Wformat   @gol
@@ -8587,6 +8591,62 @@ looks like this:
 
 This warning is enabled by @option{-Wparentheses}.
 
+@item -Wdangling-pointer
+@itemx -Wdangling-pointer=@var{n}
+@opindex Wdangling-pointer
+@opindex Wno-dangling-pointer
+Warn about uses of pointers (or C++ references) to objects with automatic
+storage duration after their lifetime has ended.  This includes local
+variables declared in nested blocks, compound literals and other unnamed
+temporary objects.  In addition, warn about storing the address of such
+objects in escaped pointers.  The warning is enabled at all optimization
+levels but may yield different results with optimization than without.
+
+@table @gcctabopt
+@item -Wdangling-pointer=1
+At level 1 the warning diagnoses only unconditional uses of dangling pointers.
+For example
+@smallexample
+int f (int c1, int c2, x)
+@{
+  char *p = strchr ((char[])@{ c1, c2 @}, c3);
+  return p ? *p : 'x';   // warning: dangling pointer to a compound literal
+@}
+@end smallexample
+In the following function the store of the address of the local variable
+@code{x} in the escaped pointer @code{*p} also triggers the warning.
+@smallexample
+void g (int **p)
+@{
+  int x = 7;
+  *p = &x;   // warning: storing the address of a local variable in *p
+@}
+@end smallexample
+
+@item -Wdangling-pointer=2
+At level 2, in addition to unconditional uses the warning also diagnoses
+conditional uses of dangling pointers.
+
+For example, because the array @var{a} in the following function is out of
+scope when the pointer @var{s} that was set to point is used, the warning
+triggers at this level.
+
+@smallexample
+void f (char *s)
+@{
+  if (!s)
+    @{
+      char a[12] = "tmpname";
+      s = a;
+    @}
+  strcat (s, ".tmp");   // warning: dangling pointer to a may be used
+  ...
+@}
+@end smallexample
+@end table
+
+@option{-Wdangling-pointer=2} is included in @option{-Wall}.
+
 @item -Wdate-time
 @opindex Wdate-time
 @opindex Wno-date-time
diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 882129143a1..f639807a78a 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -2069,10 +2069,12 @@ class pass_waccess : public gimple_opt_pass
 
   ~pass_waccess ();
 
-  opt_pass *clone () { return new pass_waccess (m_ctxt); }
+  opt_pass *clone ();
 
   virtual bool gate (function *);
 
+  void set_pass_param (unsigned, bool);
+
   virtual unsigned int execute (function *);
 
 private:
@@ -2089,6 +2091,9 @@ private:
   /* Check a call to an ordinary function for invalid accesses.  */
   bool check_call_access (gcall *);
 
+  /* Check a non-call statement.  */
+  void check_stmt (gimple *);
+
   /* Check statements in a basic block.  */
   void check_block (basic_block);
 
@@ -2112,26 +2117,41 @@ private:
   void check_atomic_memmodel (gimple *, tree, tree, const unsigned char *);
 
   /* Check for uses of indeterminate pointers.  */
-  void check_pointer_uses (gimple *, tree);
+  void check_pointer_uses (gimple *, tree, tree = NULL_TREE, bool = false);
 
   /* Return the argument that a call returns.  */
   tree gimple_call_return_arg (gcall *);
+  tree gimple_call_return_arg_ref (gcall *);
+
+  /* Check a call for uses of a dangling pointer arguments.  */
+  void check_call_dangling (gcall *);
+
+  /* Check uses of a dangling pointer or those derived from it.  */
+  void check_dangling_uses (tree, tree, bool = false, bool = false);
+  void check_dangling_uses ();
+  void check_dangling_stores ();
+  void check_dangling_stores (basic_block, hash_set<tree> &, auto_bitmap &);
 
-  void warn_invalid_pointer (tree, gimple *, gimple *, bool, bool = false);
+  void warn_invalid_pointer (tree, gimple *, gimple *, tree, bool, bool = 
false);
 
   /* Return true if use follows an invalidating statement.  */
-  bool use_after_inval_p (gimple *, gimple *);
+  bool use_after_inval_p (gimple *, gimple *, bool = false);
 
   /* A pointer_query object and its cache to store information about
      pointers and their targets in.  */
   pointer_query m_ptr_qry;
   pointer_query::cache_type m_var_cache;
-
+  /* Mapping from DECLs and their clobber statements in the function.  */
+  hash_map<tree, gimple *> m_clobbers;
   /* A bit is set for each basic block whose statements have been assigned
      valid UIDs.  */
   bitmap m_bb_uids_set;
   /* The current function.  */
   function *m_func;
+  /* True to run checks for uses of dangling pointers.  */
+  bool m_check_dangling_p;
+  /* True to run checks early on in the optimization pipeline.  */
+  bool m_early_checks_p;
 };
 
 /* Construct the pass.  */
@@ -2140,11 +2160,22 @@ pass_waccess::pass_waccess (gcc::context *ctxt)
   : gimple_opt_pass (pass_data_waccess, ctxt),
     m_ptr_qry (NULL, &m_var_cache),
     m_var_cache (),
+    m_clobbers (),
     m_bb_uids_set (),
-    m_func ()
+    m_func (),
+    m_check_dangling_p (),
+    m_early_checks_p ()
 {
 }
 
+/* Return a copy of the pass with RUN_NUMBER one greater than THIS.  */
+
+opt_pass*
+pass_waccess::clone ()
+{
+  return new pass_waccess (m_ctxt);
+}
+
 /* Release pointer_query cache.  */
 
 pass_waccess::~pass_waccess ()
@@ -2152,6 +2183,14 @@ pass_waccess::~pass_waccess ()
   m_ptr_qry.flush_cache ();
 }
 
+void
+pass_waccess::set_pass_param (unsigned int n, bool early)
+{
+  gcc_assert (n == 0);
+
+  m_early_checks_p = early;
+}
+
 /* Return true when any checks performed by the pass are enabled.  */
 
 bool
@@ -2340,6 +2379,9 @@ maybe_warn_alloc_args_overflow (gimple *stmt, const tree 
args[2],
 void
 pass_waccess::check_alloca (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   if ((warn_vla_limit >= HOST_WIDE_INT_MAX
        && warn_alloc_size_limit < warn_vla_limit)
       || (warn_alloca_limit >= HOST_WIDE_INT_MAX
@@ -2361,6 +2403,13 @@ pass_waccess::check_alloca (gcall *stmt)
 void
 pass_waccess::check_alloc_size_call (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
+  if (gimple_call_num_args (stmt) < 1)
+    /* Avoid invalid calls to functions without a prototype.  */
+    return;
+
   tree fndecl = gimple_call_fndecl (stmt);
   if (fndecl && gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     {
@@ -2413,6 +2462,9 @@ pass_waccess::check_alloc_size_call (gcall *stmt)
 void
 pass_waccess::check_strcat (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
@@ -2438,6 +2490,9 @@ pass_waccess::check_strcat (gcall *stmt)
 void
 pass_waccess::check_strncat (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
 
@@ -2507,6 +2562,9 @@ pass_waccess::check_strncat (gcall *stmt)
 void
 pass_waccess::check_stxcpy (gcall *stmt)
 {
+  if (m_early_checks_p)
+    return;
+
   tree dst = call_arg (stmt, 0);
   tree src = call_arg (stmt, 1);
 
@@ -2545,7 +2603,7 @@ pass_waccess::check_stxcpy (gcall *stmt)
 void
 pass_waccess::check_stxncpy (gcall *stmt)
 {
-  if (!warn_stringop_overflow)
+  if (m_early_checks_p || !warn_stringop_overflow)
     return;
 
   tree dst = call_arg (stmt, 0);
@@ -2569,7 +2627,7 @@ pass_waccess::check_stxncpy (gcall *stmt)
 void
 pass_waccess::check_strncmp (gcall *stmt)
 {
-  if (!warn_stringop_overread)
+  if (m_early_checks_p || !warn_stringop_overread)
     return;
 
   tree arg1 = call_arg (stmt, 0);
@@ -2674,6 +2732,9 @@ pass_waccess::check_strncmp (gcall *stmt)
 void
 pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
 {
+  if (m_early_checks_p)
+    return;
+
   /* For functions like memset and memcpy that operate on raw memory
      try to determine the size of the largest source and destination
      object using type-0 Object Size regardless of the object size
@@ -2695,7 +2756,7 @@ pass_waccess::check_read_access (gimple *stmt, tree src,
                                 tree bound /* = NULL_TREE */,
                                 int ost /* = 1 */)
 {
-  if (!warn_stringop_overread)
+  if (m_early_checks_p || !warn_stringop_overread)
     return;
 
   if (bound && !useless_type_conversion_p (size_type_node, TREE_TYPE (bound)))
@@ -2938,7 +2999,7 @@ pass_waccess::check_atomic_memmodel (gimple *stmt, tree 
ord_sucs,
   if (warning_suppressed_p (stmt, OPT_Winvalid_memory_model))
     return;
 
-  if (maybe_warn_memmodel (stmt, ord_sucs, ord_fail, valid))
+  if (!maybe_warn_memmodel (stmt, ord_sucs, ord_fail, valid))
     return;
 
   suppress_warning (stmt, OPT_Winvalid_memory_model);
@@ -3094,11 +3155,12 @@ pass_waccess::check_builtin (gcall *stmt)
 
     case BUILT_IN_FREE:
     case BUILT_IN_REALLOC:
-      {
-       tree arg = call_arg (stmt, 0);
-       if (TREE_CODE (arg) == SSA_NAME)
-         check_pointer_uses (stmt, arg);
-      }
+      if (!m_early_checks_p)
+       {
+         tree arg = call_arg (stmt, 0);
+         if (TREE_CODE (arg) == SSA_NAME)
+           check_pointer_uses (stmt, arg);
+       }
       return true;
 
     case BUILT_IN_GETTEXT:
@@ -3725,16 +3787,67 @@ pass_waccess::maybe_check_dealloc_call (gcall *call)
 
 /* Return true if either USE_STMT's basic block (that of a pointer's use)
    is dominated by INVAL_STMT's (that of a pointer's invalidating statement,
-   or if they're in the same block, USE_STMT follows INVAL_STMT.  */
+   which is either a clobber or a deallocation call), or if they're in
+   the same block, USE_STMT follows INVAL_STMT.  */
 
 bool
-pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt)
+pass_waccess::use_after_inval_p (gimple *inval_stmt, gimple *use_stmt,
+                                bool last_block /* = false */)
 {
+  tree clobvar =
+    gimple_clobber_p (inval_stmt) ? gimple_assign_lhs (inval_stmt) : NULL_TREE;
+
   basic_block inval_bb = gimple_bb (inval_stmt);
   basic_block use_bb = gimple_bb (use_stmt);
 
+  if (!inval_bb || !use_bb)
+    return false;
+
   if (inval_bb != use_bb)
-    return dominated_by_p (CDI_DOMINATORS, use_bb, inval_bb);
+    {
+      if (dominated_by_p (CDI_DOMINATORS, use_bb, inval_bb))
+       return true;
+
+      if (!clobvar || !last_block)
+       return false;
+
+      /* Proceed only when looking for uses of dangling pointers.  */
+      auto gsi = gsi_for_stmt (use_stmt);
+
+      auto_bitmap visited;
+
+      /* A use statement in the last basic block in a function or one that
+        falls through to it is after any other prior clobber of the used
+        variable unless it's followed by a clobber of the same variable. */
+      basic_block bb = use_bb;
+      while (bb != inval_bb
+            && single_succ_p (bb)
+            && !(single_succ_edge (bb)->flags & (EDGE_EH|EDGE_DFS_BACK)))
+       {
+         if (!bitmap_set_bit (visited, bb->index))
+           /* Avoid cycles. */
+           return true;
+
+         for (; !gsi_end_p (gsi); gsi_next_nondebug (&gsi))
+           {
+             gimple *stmt = gsi_stmt (gsi);
+             if (gimple_clobber_p (stmt))
+               {
+                 if (clobvar == gimple_assign_lhs (stmt))
+                   /* The use is followed by a clobber.  */
+                   return false;
+               }
+           }
+
+         bb = single_succ (bb);
+         gsi = gsi_start_bb (bb);
+       }
+
+      /* The use is one of a dangling pointer if a clobber of the variable
+        [the pointer points to] has not been found before the function exit
+        point.  */
+      return bb == EXIT_BLOCK_PTR_FOR_FN (cfun);
+    }
 
   if (bitmap_set_bit (m_bb_uids_set, inval_bb->index))
     /* The first time this basic block is visited assign increasing ids
@@ -3752,27 +3865,30 @@ pass_waccess::use_after_inval_p (gimple *inval_stmt, 
gimple *use_stmt)
   return gimple_uid (inval_stmt) < gimple_uid (use_stmt);
 }
 
-/* Issue a warning for the USE_STMT of pointer PTR rendered invalid
-   by INVAL_STMT.  PTR may be null when it's been optimized away.
-   MAYBE is true to issue the "maybe" kind of warning.  EQUALITY is
-   true when the pointer is used in an equality expression.  */
+/* Issue a warning for the USE_STMT of pointer or reference REF rendered
+   invalid by INVAL_STMT.  REF may be null when it's been optimized away.
+   When nonnull, INVAL_STMT is the deallocation function that rendered
+   the pointer or reference dangling.  Otherwise, VAR is the auto variable
+   (including an unnamed temporary such as a compound literal) whose
+   lifetime's rended it dangling.  MAYBE is true to issue the "maybe"
+   kind of warning.  EQUALITY is true when the pointer is used in
+   an equality expression.  */
 
 void
-pass_waccess::warn_invalid_pointer (tree ptr, gimple *use_stmt,
-                                   gimple *inval_stmt,
-                                   bool maybe,
-                                   bool equality /* = false */)
+pass_waccess::warn_invalid_pointer (tree ref, gimple *use_stmt,
+                                   gimple *inval_stmt, tree var,
+                                   bool maybe, bool equality /* = false */)
 {
   /* Avoid printing the unhelpful "<unknown>" in the diagnostics.  */
-  if (ptr && TREE_CODE (ptr) == SSA_NAME
-      && (!SSA_NAME_VAR (ptr) || DECL_ARTIFICIAL (SSA_NAME_VAR (ptr))))
-    ptr = NULL_TREE;
+  if (ref && TREE_CODE (ref) == SSA_NAME
+      && (!SSA_NAME_VAR (ref) || DECL_ARTIFICIAL (SSA_NAME_VAR (ref))))
+    ref = NULL_TREE;
 
   location_t use_loc = gimple_location (use_stmt);
   if (use_loc == UNKNOWN_LOCATION)
     {
-      use_loc = cfun->function_end_locus;
-      if (!ptr)
+      use_loc = m_func->function_end_locus;
+      if (!ref)
        /* Avoid issuing a warning with no context other than
           the function.  That would make it difficult to debug
           in any but very simple cases.  */
@@ -3788,12 +3904,12 @@ pass_waccess::warn_invalid_pointer (tree ptr, gimple 
*use_stmt,
 
       const tree inval_decl = gimple_call_fndecl (inval_stmt);
 
-      if ((ptr && warning_at (use_loc, OPT_Wuse_after_free,
+      if ((ref && warning_at (use_loc, OPT_Wuse_after_free,
                              (maybe
                               ? G_("pointer %qE may be used after %qD")
                               : G_("pointer %qE used after %qD")),
-                             ptr, inval_decl))
-         || (!ptr && warning_at (use_loc, OPT_Wuse_after_free,
+                             ref, inval_decl))
+         || (!ref && warning_at (use_loc, OPT_Wuse_after_free,
                              (maybe
                               ? G_("pointer may be used after %qD")
                               : G_("pointer used after %qD")),
@@ -3805,6 +3921,52 @@ pass_waccess::warn_invalid_pointer (tree ptr, gimple 
*use_stmt,
        }
       return;
     }
+
+  if ((maybe && warn_dangling_pointer < 2)
+      || warning_suppressed_p (use_stmt, OPT_Wdangling_pointer_))
+    return;
+
+  if (DECL_NAME (var))
+    {
+      if ((ref
+          && warning_at (use_loc, OPT_Wdangling_pointer_,
+                         (maybe
+                          ? G_("dangling pointer %qE to %qD may be used")
+                          : G_("using dangling pointer %qE to %qD")),
+                         ref, var))
+         || (!ref
+             && warning_at (use_loc, OPT_Wdangling_pointer_,
+                            (maybe
+                             ? G_("dangling pointer to %qD may be used")
+                             : G_("using a dangling pointer to %qD")),
+                            var)))
+       inform (DECL_SOURCE_LOCATION (var),
+               "%qD declared here", var);
+      suppress_warning (use_stmt, OPT_Wdangling_pointer_);
+      return;
+    }
+
+  if ((ref
+       && warning_at (use_loc, OPT_Wdangling_pointer_,
+                     (maybe
+                      ? G_("dangling pointer %qE to an unnamed temporary "
+                           "may be used")
+                      : G_("using dangling pointer %qE to an unnamed "
+                           "temporary")),
+                     ref, var))
+      || (!ref
+         && warning_at (use_loc, OPT_Wdangling_pointer_,
+                        (maybe
+                         ? G_("dangling pointer to an unnamed temporary "
+                              "may be used")
+                         : G_("using a dangling pointer to an unnamed "
+                              "temporary")),
+                        var)))
+    {
+      inform (DECL_SOURCE_LOCATION (var),
+             "unnamed temporary defined here");
+      suppress_warning (use_stmt, OPT_Wdangling_pointer_);
+    }
 }
 
 /* If STMT is a call to either the standard realloc or to a user-defined
@@ -3927,10 +4089,14 @@ pointers_related_p (gimple *stmt, tree p, tree q, 
pointer_query &qry)
 
 /* For a STMT either a call to a deallocation function or a clobber, warn
    for uses of the pointer PTR it was called with (including its copies
-   or others derived from it by pointer arithmetic).  */
+   or others derived from it by pointer arithmetic).  If STMT is a clobber,
+   VAR is the decl of the clobbered variable.  When MAYBE is true use
+   a "maybe" form of diagnostic.  */
 
 void
-pass_waccess::check_pointer_uses (gimple *stmt, tree ptr)
+pass_waccess::check_pointer_uses (gimple *stmt, tree ptr,
+                                 tree var /* = NULL_TREE */,
+                                 bool maybe /* = false */)
 {
   gcc_assert (TREE_CODE (ptr) == SSA_NAME);
 
@@ -4013,18 +4179,25 @@ pass_waccess::check_pointer_uses (gimple *stmt, tree 
ptr)
          /* Warn if USE_STMT is dominated by the deallocation STMT.
             Otherwise, add the pointer to POINTERS so that the uses
             of any other pointers derived from it can be checked.  */
-         if (use_after_inval_p (stmt, use_stmt))
+         if (use_after_inval_p (stmt, use_stmt, check_dangling))
            {
-             /* TODO: Handle PHIs but careful of false positives.  */
-             if (gimple_code (use_stmt) != GIMPLE_PHI)
+             if (gimple_code (use_stmt) == GIMPLE_PHI)
                {
-                 basic_block use_bb = gimple_bb (use_stmt);
-                 bool this_maybe
-                   = !dominated_by_p (CDI_POST_DOMINATORS, use_bb, stmt_bb);
-                 warn_invalid_pointer (*use_p->use, use_stmt, stmt,
-                                       this_maybe, equality);
-                 continue;
+                 tree lhs = gimple_phi_result (use_stmt);
+                 if (TREE_CODE (lhs) == SSA_NAME)
+                   {
+                     pointers.safe_push (lhs);
+                     continue;
+                   }
                }
+
+             basic_block use_bb = gimple_bb (use_stmt);
+             bool this_maybe
+               = (maybe
+                  || !dominated_by_p (CDI_POST_DOMINATORS, use_bb, stmt_bb));
+             warn_invalid_pointer (*use_p->use, use_stmt, stmt, var,
+                                   this_maybe, equality);
+             continue;
            }
 
          if (is_gimple_assign (use_stmt))
@@ -4059,26 +4232,100 @@ pass_waccess::check_call (gcall *stmt)
   if (gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
     check_builtin (stmt);
 
-  if (tree callee = gimple_call_fndecl (stmt))
-    {
-      /* Check for uses of the pointer passed to either a standard
-        or a user-defined deallocation function.  */
-      unsigned argno = fndecl_dealloc_argno (callee);
-      if (argno < (unsigned) call_nargs (stmt))
-       {
-         tree arg = call_arg (stmt, argno);
-         if (TREE_CODE (arg) == SSA_NAME)
-           check_pointer_uses (stmt, arg);
-       }
-    }
+  if (!m_early_checks_p)
+    if (tree callee = gimple_call_fndecl (stmt))
+      {
+       /* Check for uses of the pointer passed to either a standard
+          or a user-defined deallocation function.  */
+       unsigned argno = fndecl_dealloc_argno (callee);
+       if (argno < (unsigned) call_nargs (stmt))
+         {
+           tree arg = call_arg (stmt, argno);
+           if (TREE_CODE (arg) == SSA_NAME)
+             check_pointer_uses (stmt, arg);
+         }
+      }
 
   check_call_access (stmt);
+  check_call_dangling (stmt);
+
+  if (m_early_checks_p)
+    return;
 
   maybe_check_dealloc_call (stmt);
   check_nonstring_args (stmt);
 }
 
 
+/* Return true of X is a DECL with automatic storage duration.  */
+
+static inline bool
+is_auto_decl (tree x)
+{
+  return DECL_P (x) && !DECL_EXTERNAL (x) && !TREE_STATIC (x);
+}
+
+/* Check non-call STMT for invalid accesses.  */
+
+void
+pass_waccess::check_stmt (gimple *stmt)
+{
+  if (m_check_dangling_p && gimple_clobber_p (stmt))
+    {
+      /* Ignore clobber statemts in blocks with exceptional edges.  */
+      basic_block bb = gimple_bb (stmt);
+      edge e = EDGE_PRED (bb, 0);
+      if (e->flags & EDGE_EH)
+       return;
+
+      tree var = gimple_assign_lhs (stmt);
+      m_clobbers.put (var, stmt);
+      return;
+    }
+
+  if (is_gimple_assign (stmt))
+    {
+      /* Clobbered unnamed temporaries such as compound literals can be
+        revived.  Check for an assignment to one and remove it from
+        M_CLOBBERS.  */
+      tree lhs = gimple_assign_lhs (stmt);
+      while (handled_component_p (lhs))
+       lhs = TREE_OPERAND (lhs, 0);
+
+      if (is_auto_decl (lhs))
+       m_clobbers.remove (lhs);
+      return;
+    }
+
+  if (greturn *ret = dyn_cast <greturn *> (stmt))
+    {
+      if (optimize && flag_isolate_erroneous_paths_dereference)
+       /* Avoid interfering with -Wreturn-local-addr (which runs only
+          with optimization enabled).  */
+       return;
+
+      tree arg = gimple_return_retval (ret);
+      if (!arg || TREE_CODE (arg) != ADDR_EXPR)
+       return;
+
+      arg = TREE_OPERAND (arg, 0);
+      while (handled_component_p (arg))
+       arg = TREE_OPERAND (arg, 0);
+
+      if (!is_auto_decl (arg))
+       return;
+
+      gimple **pclobber = m_clobbers.get (arg);
+      if (!pclobber)
+       return;
+
+      if (!use_after_inval_p (*pclobber, stmt))
+       return;
+
+      warn_invalid_pointer (NULL_TREE, stmt, *pclobber, arg, false);
+    }
+}
+
 /* Check basic block BB for invalid accesses.  */
 
 void
@@ -4091,6 +4338,8 @@ pass_waccess::check_block (basic_block bb)
       gimple *stmt = gsi_stmt (si);
       if (gcall *call = dyn_cast <gcall *> (stmt))
        check_call (call);
+      else
+       check_stmt (stmt);
     }
 }
 
@@ -4139,6 +4388,262 @@ pass_waccess::gimple_call_return_arg (gcall *call)
   return gimple_call_arg (call, argno);
 }
 
+/* Return the decl referenced by the argument that the call STMT to
+   a built-in function returns (including with an offset) or null if
+   it doesn't.  */
+
+tree
+pass_waccess::gimple_call_return_arg_ref (gcall *call)
+{
+  if (tree arg = gimple_call_return_arg (call))
+    {
+      access_ref aref;
+      if (m_ptr_qry.get_ref (arg, call, &aref, 0)
+         && DECL_P (aref.ref))
+       return aref.ref;
+    }
+
+  return NULL_TREE;
+}
+
+/* Check for and diagnose all uses of the dangling pointer VAR to the auto
+   object DECL whose lifetime has ended.  OBJREF is true when VAR denotes
+   an access to a DECL that may have been clobbered.  */
+
+void
+pass_waccess::check_dangling_uses (tree var, tree decl, bool maybe /* = false 
*/,
+                                  bool objref /* = false */)
+{
+  if (!decl || !is_auto_decl (decl))
+    return;
+
+  gimple **pclob = m_clobbers.get (decl);
+  if (!pclob)
+    return;
+
+  if (!objref)
+    {
+      check_pointer_uses (*pclob, var, decl, maybe);
+      return;
+    }
+
+  gimple *use_stmt = SSA_NAME_DEF_STMT (var);
+  if (!use_after_inval_p (*pclob, use_stmt, true))
+    return;
+
+  basic_block use_bb = gimple_bb (use_stmt);
+  basic_block clob_bb = gimple_bb (*pclob);
+  maybe = maybe || !dominated_by_p (CDI_POST_DOMINATORS, use_bb, clob_bb);
+  warn_invalid_pointer (var, use_stmt, *pclob, decl, maybe, false);
+}
+
+/* Diagnose stores in BB and (recursively) its predecessors of the addresses
+   of local variables into nonlocal pointers that are left dangling after
+   the function returns.  BBS is a bitmap of basic blocks visited.  */
+
+void
+pass_waccess::check_dangling_stores (basic_block bb,
+                                    hash_set<tree> &stores,
+                                    auto_bitmap &bbs)
+{
+  if (!bitmap_set_bit (bbs, bb->index))
+    /* Avoid cycles. */
+    return;
+
+  /* Iterate backwards over the statements looking for a store of
+     the address of a local variable into a nonlocal pointer.  */
+  for (auto gsi = gsi_last_nondebug_bb (bb); ; gsi_prev_nondebug (&gsi))
+    {
+      gimple *stmt = gsi_stmt (gsi);
+      if (!stmt)
+       break;
+
+      if (is_gimple_call (stmt)
+         && !(gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE)))
+       /* Avoid looking before nonconst, nonpure calls since those might
+          use the escaped locals.  */
+       return;
+
+      if (!is_gimple_assign (stmt) || gimple_clobber_p (stmt))
+       continue;
+
+      access_ref lhs_ref;
+      tree lhs = gimple_assign_lhs (stmt);
+      if (!m_ptr_qry.get_ref (lhs, stmt, &lhs_ref, 0))
+       continue;
+
+      if (is_auto_decl (lhs_ref.ref))
+       continue;
+
+      if (DECL_P (lhs_ref.ref))
+       {
+         if (!POINTER_TYPE_P (TREE_TYPE (lhs_ref.ref))
+             || lhs_ref.deref > 0)
+           continue;
+       }
+      else if (TREE_CODE (lhs_ref.ref) == SSA_NAME)
+       {
+         /* Avoid looking at or before stores into unknown objects.  */
+         gimple *def_stmt = SSA_NAME_DEF_STMT (lhs_ref.ref);
+         if (!gimple_nop_p (def_stmt))
+           return;
+       }
+      else if (TREE_CODE (lhs_ref.ref) == MEM_REF)
+       {
+         tree arg = TREE_OPERAND (lhs_ref.ref, 0);
+         if (TREE_CODE (arg) == SSA_NAME)
+           {
+             gimple *def_stmt = SSA_NAME_DEF_STMT (arg);
+             if (!gimple_nop_p (def_stmt))
+               return;
+           }
+       }
+      else
+       continue;
+
+      if (stores.add (lhs_ref.ref))
+       continue;
+
+      /* FIXME: Handle stores of alloca() and VLA.  */
+      access_ref rhs_ref;
+      tree rhs = gimple_assign_rhs1 (stmt);
+      if (!m_ptr_qry.get_ref (rhs, stmt, &rhs_ref, 0)
+         || rhs_ref.deref != -1)
+       continue;
+
+      if (!is_auto_decl (rhs_ref.ref))
+       continue;
+
+      location_t loc = gimple_location (stmt);
+      if (warning_at (loc, OPT_Wdangling_pointer_,
+                     "storing the address of local variable %qD in %qE",
+                     rhs_ref.ref, lhs))
+       {
+         location_t loc = DECL_SOURCE_LOCATION (rhs_ref.ref);
+         inform (loc, "%qD declared here", rhs_ref.ref);
+
+         if (DECL_P (lhs_ref.ref))
+           loc = DECL_SOURCE_LOCATION (lhs_ref.ref);
+         else if (EXPR_HAS_LOCATION (lhs_ref.ref))
+           loc = EXPR_LOCATION (lhs_ref.ref);
+
+         if (loc != UNKNOWN_LOCATION)
+           inform (loc, "%qE declared here", lhs_ref.ref);
+       }
+    }
+
+  edge e;
+  edge_iterator ei;
+  FOR_EACH_EDGE (e, ei, bb->preds)
+    {
+      basic_block pred = e->src;
+      check_dangling_stores (pred, stores, bbs);
+    }
+}
+
+/* Diagnose stores of the addresses of local variables into nonlocal
+   pointers that are left dangling after the function returns.  */
+
+void
+pass_waccess::check_dangling_stores ()
+{
+  auto_bitmap bbs;
+  hash_set<tree> stores;
+  check_dangling_stores (EXIT_BLOCK_PTR_FOR_FN (m_func), stores, bbs);
+}
+
+/* Check for and diagnose uses of dangling pointers to auto objects
+   whose lifetime has ended.  */
+
+void
+pass_waccess::check_dangling_uses ()
+{
+  tree var;
+  unsigned i;
+  FOR_EACH_SSA_NAME (i, var, m_func)
+    {
+      /* For each SSA_NAME pointer VAR find the DECL it points to.
+        If the DECL is a clobbered local variable, check to see
+        if any of VAR's uses (or those of other pointers derived
+        from VAR) happens after the clobber.  If so, warn.  */
+      tree decl = NULL_TREE;
+
+      gimple *def_stmt = SSA_NAME_DEF_STMT (var);
+      if (is_gimple_assign (def_stmt))
+       {
+         tree rhs = gimple_assign_rhs1 (def_stmt);
+         if (TREE_CODE (rhs) == ADDR_EXPR)
+           {
+             if (!POINTER_TYPE_P (TREE_TYPE (var)))
+               continue;
+             decl = TREE_OPERAND (rhs, 0);
+           }
+         else
+           {
+             /* For other expressions, check the base DECL to see
+                if it's been clobbered, most likely as a result of
</cut>
_______________________________________________
linaro-toolchain mailing list -- linaro-toolchain@lists.linaro.org
To unsubscribe send an email to linaro-toolchain-le...@lists.linaro.org

Reply via email to