public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] enable ranger and caching in pass_waccess
@ 2021-08-19 23:09 Martin Sebor
  2021-08-20 13:09 ` Andrew MacLeod
  0 siblings, 1 reply; 8+ messages in thread
From: Martin Sebor @ 2021-08-19 23:09 UTC (permalink / raw)
  To: gcc-patches

[-- Attachment #1: Type: text/plain, Size: 991 bytes --]

The attached patch changes the new access warning pass to use
the per-function ranger instance.  To do that it makes a number
of the global static functions members of the pass (that involved
moving one to a later point in the file, increasing the diff;
the body of the function hasn't changed otherwise).  Still more
functions remain.  At the same time, the patch also enables
the simple pointer_query cache to avoid repeatedly recomputing
the properties of related pointers into the same objects, and
makes the cache more effective (trunk fails to cache a bunch of
intermediate results).  Finally, the patch enhances the debugging
support for the cache.

Other than the ranger/caching the changes have no user-visible
effect.

Tested on x86_64-linux.

Martin

Previous patches in this series:
https://gcc.gnu.org/pipermail/gcc-patches/2021-August/577526.html
https://gcc.gnu.org/pipermail/gcc-patches/2021-August/576821.html
https://gcc.gnu.org/pipermail/gcc-patches/2021-July/575377.html

[-- Attachment #2: gcc-warn-access.diff --]
[-- Type: text/x-patch, Size: 29474 bytes --]

gcc/ChangeLog:

	* gimple-ssa-warn-access.cc (check_memop_access): Remove template and
	make a member function.
	(maybe_check_dealloc_call): Make a pass_waccess member function.
	(class pass_waccess): Add and rename members.
	(pass_waccess::pass_waccess): Adjust to name change.
	(pass_waccess::~pass_waccess): Same.
	(check_alloca): Make a member function.
	(check_alloc_size_call): Same.
	(check_strcat): Same.
	(check_strncat): Same.
	(check_stxcpy): Same.
	(check_stxncpy): Same.
	(check_strncmp): Same.
	(maybe_warn_rdwr_sizes): Rename...
	(pass_waccess::maybe_check_access_sizes): ...to this.
	(pass_waccess::check_call): Adjust to name changes.
	(pass_waccess::maybe_check_dealloc_call): Make a pass_waccess member
	function.
	(pass_waccess::execute): Adjust to name changes.
	* gimple-ssa-warn-access.h (check_memop_access): Remove.
	* pointer-query.cc (access_ref::phi): Handle null pointer.
	(access_ref::inform_access): Same.
	(pointer_query::put_ref): Modify a cached value, not a copy of it.
	(pointer_query::dump): New function.
	(compute_objsize_r): Avoid overwriting access_ref::bndrng.  Cache
	more results.
	* pointer-query.h (pointer_query::dump): Declare.
	* tree-ssa-strlen.c (printf_strlen_execute): Factor code out into
	pointer_query::put_ref.

gcc/testsuite/ChangeLog:

	* gcc.dg/Wstringop-overflow-73.c: New test.

diff --git a/gcc/gimple-ssa-warn-access.cc b/gcc/gimple-ssa-warn-access.cc
index 4a2dd9ade77..4473b093f88 100644
--- a/gcc/gimple-ssa-warn-access.cc
+++ b/gcc/gimple-ssa-warn-access.cc
@@ -1511,41 +1511,6 @@ check_access (tree expr, tree dstwrite,
 			    mode, pad);
 }
 
-/* Helper to determine and check the sizes of the source and the destination
-   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  EXP is the
-   call expression, DEST is the destination argument, SRC is the source
-   argument or null, and LEN is the number of bytes.  Use Object Size type-0
-   regardless of the OPT_Wstringop_overflow_ setting.  Return true on success
-   (no overflow or invalid sizes), false otherwise.  */
-
-template <class GimpleOrTree>
-static bool
-check_memop_access (GimpleOrTree expr, tree dest, tree src, tree size)
-{
-  /* 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
-     type specified by the option.  */
-  access_data data (expr, access_read_write);
-  tree srcsize = src ? compute_objsize (src, 0, &data.src) : NULL_TREE;
-  tree dstsize = compute_objsize (dest, 0, &data.dst);
-
-  return check_access (expr, size, /*maxread=*/NULL_TREE,
-		       srcsize, dstsize, data.mode, &data);
-}
-
-bool
-check_memop_access (gimple *stmt, tree dest, tree src, tree size)
-{
-  return check_memop_access<gimple *>(stmt, dest, src, size);
-}
-
-bool
-check_memop_access (tree expr, tree dest, tree src, tree size)
-{
-  return check_memop_access<tree>(expr, dest, src, size);
-}
-
 /* A convenience wrapper for check_access above to check access
    by a read-only function like puts.  */
 
@@ -2093,135 +2058,6 @@ warn_dealloc_offset (location_t loc, gimple *call, const access_ref &aref)
   return true;
 }
 
-/* Issue a warning if a deallocation function such as free, realloc,
-   or C++ operator delete is called with an argument not returned by
-   a matching allocation function such as malloc or the corresponding
-   form of C++ operatorn new.  */
-
-static void
-maybe_check_dealloc_call (gcall *call)
-{
-  tree fndecl = gimple_call_fndecl (call);
-  if (!fndecl)
-    return;
-
-  unsigned argno = fndecl_dealloc_argno (fndecl);
-  if ((unsigned) call_nargs (call) <= argno)
-    return;
-
-  tree ptr = gimple_call_arg (call, argno);
-  if (integer_zerop (ptr))
-    return;
-
-  access_ref aref;
-  if (!compute_objsize (ptr, 0, &aref))
-    return;
-
-  tree ref = aref.ref;
-  if (integer_zerop (ref))
-    return;
-
-  tree dealloc_decl = fndecl;
-  location_t loc = gimple_location (call);
-
-  if (DECL_P (ref) || EXPR_P (ref))
-    {
-      /* Diagnose freeing a declared object.  */
-      if (aref.ref_declared ()
-	  && warning_at (loc, OPT_Wfree_nonheap_object,
-			 "%qD called on unallocated object %qD",
-			 dealloc_decl, ref))
-	{
-	  inform (get_location (ref), "declared here");
-	  return;
-	}
-
-      /* Diagnose freeing a pointer that includes a positive offset.
-	 Such a pointer cannot refer to the beginning of an allocated
-	 object.  A negative offset may refer to it.  */
-      if (aref.sizrng[0] != aref.sizrng[1]
-	  && warn_dealloc_offset (loc, call, aref))
-	return;
-    }
-  else if (CONSTANT_CLASS_P (ref))
-    {
-      if (warning_at (loc, OPT_Wfree_nonheap_object,
-		      "%qD called on a pointer to an unallocated "
-		      "object %qE", dealloc_decl, ref))
-	{
-	  if (TREE_CODE (ptr) == SSA_NAME)
-	    {
-	      gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
-	      if (is_gimple_assign (def_stmt))
-		{
-		  location_t loc = gimple_location (def_stmt);
-		  inform (loc, "assigned here");
-		}
-	    }
-	  return;
-	}
-    }
-  else if (TREE_CODE (ref) == SSA_NAME)
-    {
-      /* Also warn if the pointer argument refers to the result
-	 of an allocation call like alloca or VLA.  */
-      gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
-      if (is_gimple_call (def_stmt))
-	{
-	  bool warned = false;
-	  if (gimple_call_alloc_p (def_stmt))
-	    {
-	      if (matching_alloc_calls_p (def_stmt, dealloc_decl))
-		{
-		  if (warn_dealloc_offset (loc, call, aref))
-		    return;
-		}
-	      else
-		{
-		  tree alloc_decl = gimple_call_fndecl (def_stmt);
-		  const opt_code opt =
-		    (DECL_IS_OPERATOR_NEW_P (alloc_decl)
-		     || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
-		     ? OPT_Wmismatched_new_delete
-		     : OPT_Wmismatched_dealloc);
-		  warned = warning_at (loc, opt,
-				       "%qD called on pointer returned "
-				       "from a mismatched allocation "
-				       "function", dealloc_decl);
-		}
-	    }
-	  else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
-		   || gimple_call_builtin_p (def_stmt,
-					     BUILT_IN_ALLOCA_WITH_ALIGN))
-	    warned = warning_at (loc, OPT_Wfree_nonheap_object,
-				 "%qD called on pointer to "
-				 "an unallocated object",
-				 dealloc_decl);
-	  else if (warn_dealloc_offset (loc, call, aref))
-	    return;
-
-	  if (warned)
-	    {
-	      tree fndecl = gimple_call_fndecl (def_stmt);
-	      inform (gimple_location (def_stmt),
-		      "returned from %qD", fndecl);
-	      return;
-	    }
-	}
-      else if (gimple_nop_p (def_stmt))
-	{
-	  ref = SSA_NAME_VAR (ref);
-	  /* Diagnose freeing a pointer that includes a positive offset.  */
-	  if (TREE_CODE (ref) == PARM_DECL
-	      && !aref.deref
-	      && aref.sizrng[0] != aref.sizrng[1]
-	      && aref.offrng[0] > 0 && aref.offrng[1] > 0
-	      && warn_dealloc_offset (loc, call, aref))
-	    return;
-	}
-    }
-}
-
 namespace {
 
 const pass_data pass_data_waccess = {
@@ -2249,6 +2085,11 @@ class pass_waccess : public gimple_opt_pass
   virtual bool gate (function *);
   virtual unsigned int execute (function *);
 
+private:
+  /* Not copyable or assignable.  */
+  pass_waccess (pass_waccess &) = delete;
+  void operator= (pass_waccess &) = delete;
+
   /* Check a call to a built-in function.  */
   bool check_builtin (gcall *);
 
@@ -2259,17 +2100,25 @@ class pass_waccess : public gimple_opt_pass
   void check (basic_block);
 
   /* Check a call to a function.  */
- void check (gcall *);
+  void check (gcall *);
 
-private:
-  /* Not copyable or assignable.  */
-  pass_waccess (pass_waccess &) = delete;
-  void operator= (pass_waccess &) = delete;
+  /* Check a call to the named built-in function.  */
+  void check_alloca (gcall *);
+  void check_alloc_size_call (gcall *);
+  void check_strcat (gcall *);
+  void check_strncat (gcall *);
+  void check_stxcpy (gcall *);
+  void check_stxncpy (gcall *);
+  void check_strncmp (gcall *);
+  void check_memop_access (gimple *, tree, tree, tree);
+
+  void maybe_check_dealloc_call (gcall *);
+  void maybe_check_access_sizes (rdwr_map *, tree, tree, gimple *);
 
   /* A pointer_query object and its cache to store information about
      pointers and their targets in.  */
-  pointer_query ptr_qry;
-  pointer_query::cache_type var_cache;
+  pointer_query m_ptr_qry;
+  pointer_query::cache_type m_var_cache;
 
   gimple_ranger *m_ranger;
 };
@@ -2278,8 +2127,8 @@ private:
 
 pass_waccess::pass_waccess (gcc::context *ctxt)
   : gimple_opt_pass (pass_data_waccess, ctxt),
-    ptr_qry (m_ranger, &var_cache),
-    var_cache (),
+    m_ptr_qry (m_ranger, &m_var_cache),
+    m_var_cache (),
     m_ranger ()
 {
 }
@@ -2288,7 +2137,7 @@ pass_waccess::pass_waccess (gcc::context *ctxt)
 
 pass_waccess::~pass_waccess ()
 {
-  ptr_qry.flush_cache ();
+  m_ptr_qry.flush_cache ();
 }
 
 /* Return true when any checks performed by the pass are enabled.  */
@@ -2476,8 +2325,8 @@ maybe_warn_alloc_args_overflow (gimple *stmt, const tree args[2],
 
 /* Check a call to an alloca function for an excessive size.  */
 
-static void
-check_alloca (gimple *stmt)
+void
+pass_waccess::check_alloca (gcall *stmt)
 {
   if ((warn_vla_limit >= HOST_WIDE_INT_MAX
        && warn_alloc_size_limit < warn_vla_limit)
@@ -2497,8 +2346,8 @@ check_alloca (gimple *stmt)
 
 /* Check a call to an allocation function for an excessive size.  */
 
-static void
-check_alloc_size_call (gimple *stmt)
+void
+pass_waccess::check_alloc_size_call (gcall *stmt)
 {
   if (gimple_call_num_args (stmt) < 1)
     /* Avoid invalid calls to functions without a prototype.  */
@@ -2547,8 +2396,8 @@ check_alloc_size_call (gimple *stmt)
 
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
 
-static void
-check_strcat (gimple *stmt)
+void
+pass_waccess::check_strcat (gcall *stmt)
 {
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
@@ -2563,8 +2412,8 @@ check_strcat (gimple *stmt)
   access_data data (stmt, access_read_write, NULL_TREE, true,
 		    NULL_TREE, true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src);
-  tree destsize = compute_objsize (dest, ost, &data.dst);
+  compute_objsize (src, ost, &data.src, &m_ptr_qry);
+  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
 
   check_access (stmt, /*dstwrite=*/NULL_TREE, /*maxread=*/NULL_TREE,
 		src, destsize, data.mode, &data);
@@ -2572,8 +2421,8 @@ check_strcat (gimple *stmt)
 
 /* Check a call STMT to strcat() for overflow and warn if it does.  */
 
-static void
-check_strncat (gimple *stmt)
+void
+pass_waccess::check_strncat (gcall *stmt)
 {
   if (!warn_stringop_overflow && !warn_stringop_overread)
     return;
@@ -2605,7 +2454,8 @@ check_strncat (gimple *stmt)
   /* Try to verify that the destination is big enough for the shortest
      string.  First try to determine the size of the destination object
      into which the source is being copied.  */
-  tree destsize = compute_objsize (dest, warn_stringop_overflow - 1, &data.dst);
+  const int ost = warn_stringop_overflow - 1;
+  tree destsize = compute_objsize (dest, ost, &data.dst, &m_ptr_qry);
 
   /* Add one for the terminating nul.  */
   tree srclen = (maxlen
@@ -2640,8 +2490,8 @@ check_strncat (gimple *stmt)
 /* Check a call STMT to stpcpy() or strcpy() for overflow and warn
    if it does.  */
 
-static void
-check_stxcpy (gimple *stmt)
+void
+pass_waccess::check_stxcpy (gcall *stmt)
 {
   tree dst = call_arg (stmt, 0);
   tree src = call_arg (stmt, 1);
@@ -2661,8 +2511,8 @@ check_stxcpy (gimple *stmt)
       access_data data (stmt, access_read_write, NULL_TREE, true,
 			NULL_TREE, true);
       const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-      compute_objsize (src, ost, &data.src);
-      tree dstsize = compute_objsize (dst, ost, &data.dst);
+      compute_objsize (src, ost, &data.src, &m_ptr_qry);
+      tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
       check_access (stmt, /*dstwrite=*/ NULL_TREE,
 		    /*maxread=*/ NULL_TREE, /*srcstr=*/ src,
 		    dstsize, data.mode, &data);
@@ -2678,8 +2528,8 @@ check_stxcpy (gimple *stmt)
 /* Check a call STMT to stpncpy() or strncpy() for overflow and warn
    if it does.  */
 
-static void
-check_stxncpy (gimple *stmt)
+void
+pass_waccess::check_stxncpy (gcall *stmt)
 {
   if (!warn_stringop_overflow)
     return;
@@ -2691,8 +2541,8 @@ check_stxncpy (gimple *stmt)
 
   access_data data (stmt, access_read_write, len, true, len, true);
   const int ost = warn_stringop_overflow ? warn_stringop_overflow - 1 : 1;
-  compute_objsize (src, ost, &data.src);
-  tree dstsize = compute_objsize (dst, ost, &data.dst);
+  compute_objsize (src, ost, &data.src, &m_ptr_qry);
+  tree dstsize = compute_objsize (dst, ost, &data.dst, &m_ptr_qry);
 
   check_access (stmt, /*dstwrite=*/len,
 		/*maxread=*/len, src, dstsize, data.mode, &data);
@@ -2701,8 +2551,8 @@ check_stxncpy (gimple *stmt)
 /* Check a call STMT to stpncpy() or strncpy() for overflow and warn
    if it does.  */
 
-static void
-check_strncmp (gimple *stmt)
+void
+pass_waccess::check_strncmp (gcall *stmt)
 {
   if (!warn_stringop_overread)
     return;
@@ -2757,8 +2607,8 @@ check_strncmp (gimple *stmt)
 
   /* compute_objsize almost never fails (and ultimately should never
      fail).  Don't bother to handle the rare case when it does.  */
-  if (!compute_objsize (arg1, 1, &adata1.src)
-      || !compute_objsize (arg2, 1, &adata2.src))
+  if (!compute_objsize (arg1, 1, &adata1.src, &m_ptr_qry)
+      || !compute_objsize (arg2, 1, &adata2.src, &m_ptr_qry))
     return;
 
   /* Compute the size of the remaining space in each array after
@@ -2792,6 +2642,29 @@ check_strncmp (gimple *stmt)
     }
 }
 
+/* Determine and check the sizes of the source and the destination
+   of calls to __builtin_{bzero,memcpy,mempcpy,memset} calls.  STMT is
+   the call statement, DEST is the destination argument, SRC is the source
+   argument or null, and SIZE is the number of bytes being accessed.  Use
+   Object Size type-0 regardless of the OPT_Wstringop_overflow_ setting.
+   Return true on success (no overflow or invalid sizes), false otherwise.  */
+
+void
+pass_waccess::check_memop_access (gimple *stmt, tree dest, tree src, tree size)
+{
+  /* 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
+     type specified by the option.  */
+  access_data data (stmt, access_read_write);
+  tree srcsize
+    = src ? compute_objsize (src, 0, &data.src, &m_ptr_qry) : NULL_TREE;
+  tree dstsize = compute_objsize (dest, 0, &data.dst, &m_ptr_qry);
+
+  check_access (stmt, size, /*maxread=*/NULL_TREE,
+		srcsize, dstsize, data.mode, &data);
+}
+
 /* Check call STMT to a built-in function for invalid accesses.  Return
    true if a call has been handled.  */
 
@@ -2950,8 +2823,9 @@ append_attrname (const std::pair<int, attr_access> &access,
    arguments and diagnose past-the-end accesses and related problems
    in the function call EXP.  */
 
-static void
-maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
+void
+pass_waccess::maybe_check_access_sizes (rdwr_map *rwm, tree fndecl, tree fntype,
+					gimple *stmt)
 {
   auto_diagnostic_group adg;
 
@@ -3142,7 +3016,7 @@ maybe_warn_rdwr_sizes (rdwr_map *rwm, tree fndecl, tree fntype, gimple *stmt)
 			NULL_TREE, false);
       access_ref* const pobj = (access.second.mode == access_write_only
 				? &data.dst : &data.src);
-      tree objsize = compute_objsize (ptr, 1, pobj);
+      tree objsize = compute_objsize (ptr, 1, pobj, &m_ptr_qry);
 
       /* The size of the destination or source object.  */
       tree dstsize = NULL_TREE, srcsize = NULL_TREE;
@@ -3258,7 +3132,7 @@ pass_waccess::check_call (gcall *stmt)
 
   /* Check attribute access arguments.  */
   tree fndecl = gimple_call_fndecl (stmt);
-  maybe_warn_rdwr_sizes (&rdwr_idx, fndecl, fntype, stmt);
+  maybe_check_access_sizes (&rdwr_idx, fndecl, fntype, stmt);
 
   check_alloc_size_call (stmt);
   return true;
@@ -3276,6 +3150,138 @@ check_nonstring_args (gcall *stmt)
   maybe_warn_nonstring_arg (fndecl, stmt);
 }
 
+/* Issue a warning if a deallocation function such as free, realloc,
+   or C++ operator delete is called with an argument not returned by
+   a matching allocation function such as malloc or the corresponding
+   form of C++ operatorn new.  */
+
+void
+pass_waccess::maybe_check_dealloc_call (gcall *call)
+{
+  tree fndecl = gimple_call_fndecl (call);
+  if (!fndecl)
+    return;
+
+  unsigned argno = fndecl_dealloc_argno (fndecl);
+  if ((unsigned) call_nargs (call) <= argno)
+    return;
+
+  tree ptr = gimple_call_arg (call, argno);
+  if (integer_zerop (ptr))
+    return;
+
+  access_ref aref;
+  if (!compute_objsize (ptr, 0, &aref, &m_ptr_qry))
+    return;
+
+  tree ref = aref.ref;
+  if (integer_zerop (ref))
+    return;
+
+  tree dealloc_decl = fndecl;
+  location_t loc = gimple_location (call);
+
+  if (DECL_P (ref) || EXPR_P (ref))
+    {
+      /* Diagnose freeing a declared object.  */
+      if (aref.ref_declared ()
+	  && warning_at (loc, OPT_Wfree_nonheap_object,
+			 "%qD called on unallocated object %qD",
+			 dealloc_decl, ref))
+	{
+	  inform (get_location (ref), "declared here");
+	  return;
+	}
+
+      /* Diagnose freeing a pointer that includes a positive offset.
+	 Such a pointer cannot refer to the beginning of an allocated
+	 object.  A negative offset may refer to it.  */
+      if (aref.sizrng[0] != aref.sizrng[1]
+	  && warn_dealloc_offset (loc, call, aref))
+	return;
+    }
+  else if (CONSTANT_CLASS_P (ref))
+    {
+      if (warning_at (loc, OPT_Wfree_nonheap_object,
+		      "%qD called on a pointer to an unallocated "
+		      "object %qE", dealloc_decl, ref))
+	{
+	  if (TREE_CODE (ptr) == SSA_NAME)
+	    {
+	      gimple *def_stmt = SSA_NAME_DEF_STMT (ptr);
+	      if (is_gimple_assign (def_stmt))
+		{
+		  location_t loc = gimple_location (def_stmt);
+		  inform (loc, "assigned here");
+		}
+	    }
+	  return;
+	}
+    }
+  else if (TREE_CODE (ref) == SSA_NAME)
+    {
+      /* Also warn if the pointer argument refers to the result
+	 of an allocation call like alloca or VLA.  */
+      gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
+      if (!def_stmt)
+	return;
+
+      if (is_gimple_call (def_stmt))
+	{
+	  bool warned = false;
+	  if (gimple_call_alloc_p (def_stmt))
+	    {
+	      if (matching_alloc_calls_p (def_stmt, dealloc_decl))
+		{
+		  if (warn_dealloc_offset (loc, call, aref))
+		    return;
+		}
+	      else
+		{
+		  tree alloc_decl = gimple_call_fndecl (def_stmt);
+		  const opt_code opt =
+		    (DECL_IS_OPERATOR_NEW_P (alloc_decl)
+		     || DECL_IS_OPERATOR_DELETE_P (dealloc_decl)
+		     ? OPT_Wmismatched_new_delete
+		     : OPT_Wmismatched_dealloc);
+		  warned = warning_at (loc, opt,
+				       "%qD called on pointer returned "
+				       "from a mismatched allocation "
+				       "function", dealloc_decl);
+		}
+	    }
+	  else if (gimple_call_builtin_p (def_stmt, BUILT_IN_ALLOCA)
+		   || gimple_call_builtin_p (def_stmt,
+					     BUILT_IN_ALLOCA_WITH_ALIGN))
+	    warned = warning_at (loc, OPT_Wfree_nonheap_object,
+				 "%qD called on pointer to "
+				 "an unallocated object",
+				 dealloc_decl);
+	  else if (warn_dealloc_offset (loc, call, aref))
+	    return;
+
+	  if (warned)
+	    {
+	      tree fndecl = gimple_call_fndecl (def_stmt);
+	      inform (gimple_location (def_stmt),
+		      "returned from %qD", fndecl);
+	      return;
+	    }
+	}
+      else if (gimple_nop_p (def_stmt))
+	{
+	  ref = SSA_NAME_VAR (ref);
+	  /* Diagnose freeing a pointer that includes a positive offset.  */
+	  if (TREE_CODE (ref) == PARM_DECL
+	      && !aref.deref
+	      && aref.sizrng[0] != aref.sizrng[1]
+	      && aref.offrng[0] > 0 && aref.offrng[1] > 0
+	      && warn_dealloc_offset (loc, call, aref))
+	    return;
+	}
+    }
+}
+
 /* Check call STMT for invalid accesses.  */
 
 void
@@ -3317,8 +3323,15 @@ pass_waccess::execute (function *fun)
   FOR_EACH_BB_FN (bb, fun)
     check (bb);
 
-  /* Release the ranger instance and replace it with a global ranger.  */
+  if (dump_file)
+    m_ptr_qry.dump (dump_file, (dump_flags & TDF_DETAILS) != 0);
+
+  m_ptr_qry.flush_cache ();
+
+  /* Release the ranger instance and replace it with a global ranger.
+     Also reset the pointer since calling disable_ranger() deletes it.  */
   disable_ranger (fun);
+  m_ranger = NULL;
 
   return 0;
 }
diff --git a/gcc/gimple-ssa-warn-access.h b/gcc/gimple-ssa-warn-access.h
index 1cd3a28c421..00f5bb1a7b2 100644
--- a/gcc/gimple-ssa-warn-access.h
+++ b/gcc/gimple-ssa-warn-access.h
@@ -45,7 +45,6 @@ class access_data;
 extern bool check_access (tree, tree, tree, tree, tree, access_mode,
 			  const access_data * = NULL);
 
-extern bool check_memop_access (tree, tree, tree, tree);
 extern bool check_read_access (gimple *, tree, tree = NULL_TREE, int ost = 1);
 extern bool check_read_access (tree, tree, tree = NULL_TREE, int = 1);
 
diff --git a/gcc/pointer-query.cc b/gcc/pointer-query.cc
index 99caf78bfa7..af622ba04b2 100644
--- a/gcc/pointer-query.cc
+++ b/gcc/pointer-query.cc
@@ -34,10 +34,13 @@
 #include "stringpool.h"
 #include "attribs.h"
 #include "gimple-fold.h"
+#include "gimple-ssa.h"
 #include "intl.h"
 #include "attr-fnspec.h"
 #include "gimple-range.h"
 #include "pointer-query.h"
+#include "tree-pretty-print.h"
+#include "tree-ssanames.h"
 
 static bool compute_objsize_r (tree, int, access_ref *, ssa_name_limit_t &,
 			       pointer_query *);
@@ -628,7 +631,7 @@ access_ref::phi () const
     return NULL;
 
   gimple *def_stmt = SSA_NAME_DEF_STMT (ref);
-  if (gimple_code (def_stmt) != GIMPLE_PHI)
+  if (!def_stmt || gimple_code (def_stmt) != GIMPLE_PHI)
     return NULL;
 
   return as_a <gphi *> (def_stmt);
@@ -1042,6 +1045,9 @@ access_ref::inform_access (access_mode mode) const
   if (TREE_CODE (ref) == SSA_NAME)
     {
       gimple *stmt = SSA_NAME_DEF_STMT (ref);
+      if (!stmt)
+	return;
+
       if (is_gimple_call (stmt))
 	{
 	  loc = gimple_location (stmt);
@@ -1343,7 +1349,7 @@ pointer_query::put_ref (tree ptr, const access_ref &ref, int ostype /* = 1 */)
   if (var_cache->access_refs.length () <= cache_idx)
     var_cache->access_refs.safe_grow_cleared (cache_idx + 1);
 
-  access_ref cache_ref = var_cache->access_refs[cache_idx - 1];
+  access_ref &cache_ref = var_cache->access_refs[cache_idx];
   if (cache_ref.ref)
   {
     gcc_checking_assert (cache_ref.ref == ref.ref);
@@ -1364,6 +1370,102 @@ pointer_query::flush_cache ()
   var_cache->access_refs.release ();
 }
 
+/* Dump statistics and, optionally, cache contents to DUMP_FILE.  */
+
+void
+pointer_query::dump (FILE *dump_file, bool contents /* = false */)
+{
+  unsigned nused = 0, nrefs = 0;
+  unsigned nidxs = var_cache->indices.length ();
+  for (unsigned i = 0; i != nidxs; ++i)
+    {
+      unsigned ari = var_cache->indices[i];
+      if (!ari)
+	continue;
+
+      ++nused;
+
+      const access_ref &aref = var_cache->access_refs[ari];
+      if (!aref.ref)
+	continue;
+
+      ++nrefs;
+    }
+
+  fprintf (dump_file, "pointer_query counters:\n"
+	   "  index cache size:   %u\n"
+	   "  index entries:      %u\n"
+	   "  access cache size:  %u\n"
+	   "  access entries:     %u\n"
+	   "  hits:               %u\n"
+	   "  misses:             %u\n"
+	   "  failures:           %u\n"
+	   "  max_depth:          %u\n",
+	   nidxs, nused,
+	   var_cache->access_refs.length (), nrefs,
+	   hits, misses, failures, max_depth);
+
+  if (!contents || !nidxs)
+    return;
+
+  fputs ("\npointer_query cache contents:\n", dump_file);
+
+  for (unsigned i = 0; i != nidxs; ++i)
+    {
+      unsigned ari = var_cache->indices[i];
+      if (!ari)
+	continue;
+
+      const access_ref &aref = var_cache->access_refs[ari];
+      if (!aref.ref)
+	continue;
+
+      /* The level-1 cache index corresponds to the SSA_NAME_VERSION
+	 shifted left by one and ORed with the Object Size Type in
+	 the lowest bit.  Print the two separately.  */
+      unsigned ver = i >> 1;
+      unsigned ost = i & 1;
+
+      fprintf (dump_file, "  %u.%u[%u]: ", ver, ost, ari);
+      if (tree name = ssa_name (ver))
+	{
+	  print_generic_expr (dump_file, name);
+	  fputs (" = ", dump_file);
+	}
+      else
+	fprintf (dump_file, "  _%u = ", ver);
+
+      if (gphi *phi = aref.phi ())
+	{
+	  fputs ("PHI <", dump_file);
+	  unsigned nargs = gimple_phi_num_args (phi);
+	  for (unsigned i = 0; i != nargs; ++i)
+	    {
+	      tree arg = gimple_phi_arg_def (phi, i);
+	      print_generic_expr (dump_file, arg);
+	      if (i + 1 < nargs)
+		fputs (", ", dump_file);
+	    }
+	  fputc ('>', dump_file);
+	}
+      else
+	print_generic_expr (dump_file, aref.ref);
+
+      if (aref.offrng[0] != aref.offrng[1])
+	fprintf (dump_file, " + [%lli, %lli]",
+		 (long long) aref.offrng[0].to_shwi (),
+		 (long long) aref.offrng[1].to_shwi ());
+      else if (aref.offrng[0] != 0)
+	fprintf (dump_file, " %c %lli",
+		 aref.offrng[0] < 0 ? '-' : '+',
+		 (long long) aref.offrng[0].to_shwi ());
+
+      fputc ('\n', dump_file);
+    }
+
+  fputc ('\n', dump_file);
+}
+
 /* A helper of compute_objsize_r() to determine the size from an assignment
    statement STMT with the RHS of either MIN_EXPR or MAX_EXPR.  */
 
@@ -1778,8 +1880,14 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  if (const access_ref *cache_ref = qry->get_ref (ptr))
 	    {
 	      /* If the pointer is in the cache set *PREF to what it refers
-		 to and return success.  */
+		 to and return success.
+		 FIXME: BNDRNG is determined by each access and so it doesn't
+		 belong in access_ref.  Until the design is changed, keep it
+		 unchanged here.  */
+	      const offset_int bndrng[2] = { pref->bndrng[0], pref->bndrng[1] };
 	      *pref = *cache_ref;
+	      pref->bndrng[0] = bndrng[0];
+	      pref->bndrng[1] = bndrng[1];
 	      return true;
 	    }
 	}
@@ -1928,9 +2036,13 @@ compute_objsize_r (tree ptr, int ostype, access_ref *pref,
 	  return true;
 	}
 
-      if (code == ADDR_EXPR
-	  || code == SSA_NAME)
-	return compute_objsize_r (rhs, ostype, pref, snlim, qry);
+      if (code == ADDR_EXPR || code == SSA_NAME)
+	{
+	  if (!compute_objsize_r (rhs, ostype, pref, snlim, qry))
+	    return false;
+	  qry->put_ref (ptr, *pref);
+	  return true;
+	}
 
       /* (This could also be an assignment from a nonlocal pointer.)  Save
 	 PTR to mention in diagnostics but otherwise treat it as a pointer
diff --git a/gcc/pointer-query.h b/gcc/pointer-query.h
index eb7e90dde03..3c8172c652d 100644
--- a/gcc/pointer-query.h
+++ b/gcc/pointer-query.h
@@ -186,6 +186,9 @@ public:
   /* Flush the cache.  */
   void flush_cache ();
 
+  /* Dump statistics and optionally cache contents to DUMP_FILE.  */
+  void dump (FILE *, bool = false);
+
   /* A Ranger instance.  May be null to use global ranges.  */
   range_query *rvals;
   /* Cache of SSA_NAMEs.  May be null to disable caching.  */
diff --git a/gcc/testsuite/gcc.dg/Wstringop-overflow-73.c b/gcc/testsuite/gcc.dg/Wstringop-overflow-73.c
new file mode 100644
index 00000000000..0bb4afecc7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/Wstringop-overflow-73.c
@@ -0,0 +1,35 @@
+/*
+  { dg-do compile }
+  { dg-options "-Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+int memcmp (const void*, const void*, size_t);
+int strncmp (const char*, const char*, size_t);
+char* stpncpy (char*, const char*, size_t);
+char* strncpy (char*, const char*, size_t);
+
+extern char a4[4], b5[5];
+
+struct A { char a4[4]; };
+
+extern volatile int i;
+extern void* volatile ptr;
+
+void test_stpncpy (struct A *p)
+{
+  ptr = stpncpy (a4, b5, 4);
+  ptr = stpncpy (a4, b5, 5);      // { dg-warning "writing 5 bytes" }
+
+  ptr = stpncpy (p->a4, b5, 4);
+  ptr = stpncpy (p->a4, b5, 5);   // { dg-warning "writing 5 bytes" }
+}
+
+void test_strncpy (struct A *p)
+{
+  ptr = strncpy (a4, b5, 4);
+  ptr = strncpy (a4, b5, 5);      // { dg-warning "writing 5 bytes" }
+
+  ptr = strncpy (p->a4, b5, 4);
+  ptr = strncpy (p->a4, b5, 5);   // { dg-warning "writing 5 bytes" }
+}
diff --git a/gcc/tree-ssa-strlen.c b/gcc/tree-ssa-strlen.c
index 15391da8104..11d1712576d 100644
--- a/gcc/tree-ssa-strlen.c
+++ b/gcc/tree-ssa-strlen.c
@@ -5820,27 +5820,7 @@ printf_strlen_execute (function *fun, bool warn_only)
   walker.walk (ENTRY_BLOCK_PTR_FOR_FN (fun));
 
   if (dump_file && (dump_flags & TDF_DETAILS))
-    {
-      unsigned nused = 0;
-      unsigned nidxs = walker.ptr_qry.var_cache->indices.length ();
-      for (unsigned i = 0; i != nidxs; ++i)
-	if (walker.ptr_qry.var_cache->indices[i])
-	  ++nused;
-
-      fprintf (dump_file, "pointer_query counters\n"
-	       "  index cache size:  %u\n"
-	       "  utilization:       %u%%\n"
-	       "  access cache size: %u\n"
-	       "  hits:              %u\n"
-	       "  misses:            %u\n"
-	       "  failures:          %u\n"
-	       "  max_depth:         %u\n",
-	       nidxs,
-	       nidxs == 0 ? 0 : (nused * 100) / nidxs,
-	       walker.ptr_qry.var_cache->access_refs.length (),
-	       walker.ptr_qry.hits, walker.ptr_qry.misses,
-	       walker.ptr_qry.failures, walker.ptr_qry.max_depth);
-    }
+    walker.ptr_qry.dump (dump_file);
 
   ssa_ver_to_stridx.release ();
   strinfo_pool.release ();

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2021-08-31  6:00 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-08-19 23:09 [PATCH] enable ranger and caching in pass_waccess Martin Sebor
2021-08-20 13:09 ` Andrew MacLeod
2021-08-20 22:16   ` Martin Sebor
2021-08-25 15:20     ` [PING][PATCH] " Martin Sebor
2021-08-25 16:14       ` Andrew MacLeod
2021-08-25 21:26         ` Martin Sebor
2021-08-30 14:27           ` [PING #2][PATCH] " Martin Sebor
2021-08-31  6:00             ` Richard Biener

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).