]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
Let -Wuninitialized assume built-ins don't change const arguments [PR101584].
authorMartin Sebor <msebor@redhat.com>
Tue, 27 Jul 2021 22:02:54 +0000 (16:02 -0600)
committerMartin Sebor <msebor@redhat.com>
Tue, 27 Jul 2021 22:02:54 +0000 (16:02 -0600)
PR tree-optimization/101584 - missing -Wuninitialized with an allocated object after a built-in call

gcc/ChangeLog:

PR tree-optimization/101584
* tree-ssa-uninit.c (builtin_call_nomodifying_p): New function.
(check_defs): Call it.

gcc/testsuite/ChangeLog:

PR tree-optimization/101584
* gcc.dg/uninit-38.c: Remove assertions.
* gcc.dg/uninit-41.c: New test.

gcc/testsuite/gcc.dg/uninit-38.c
gcc/testsuite/gcc.dg/uninit-41.c [new file with mode: 0644]
gcc/tree-ssa-uninit.c

index 8dacc8c63a6ee32785ede8fc8499f9da3dc70ffd..0d70bcd8e985b8af6ca998fd8a24988db82443fe 100644 (file)
@@ -1,5 +1,5 @@
-/* Verify that dereferencing uninitialized allocated objects and VLAs
-   correctly reflects offsets into the objects.
+/* Verify that dereferencing uninitialized VLAs correctly reflects
+   offsets into the objects.
    The test's main purpose is to exercise the formatting of MEM_REFs.
    If -Wuninitialized gets smarter and detects uninitialized accesses
    before they're turned into MEM_REFs the test will likely need to
@@ -18,41 +18,6 @@ extern void* malloc (size_t);
 
 void sink (void*, ...);
 
-#undef T
-#define T(Type, idx, off)                      \
-  __attribute__ ((noipa))                      \
-  void UNIQ (test_)(int n)                     \
-  {                                            \
-    void *p = malloc (n);                      \
-    Type *q = (Type*)((char*)p + off);         \
-    sink (p, q[idx]);                          \
-  }                                            \
-  typedef void dummy_type
-
-T (int, 0, 0);      // { dg-warning "'\\*\\(int \\*\\)p' is used uninitialized" }
-T (int, 0, 1);      // { dg-warning "'\\*\\(int \\*\\)\\(\\(char \\*\\)p \\+ 1\\)'" }
-T (int, 0, 2);      // { dg-warning "'\\*\\(int \\*\\)\\(\\(char \\*\\)p \\+ 2\\)'" }
-T (int, 0, 3);      // { dg-warning "'\\*\\(int \\*\\)\\(\\(char \\*\\)p \\+ 3\\)'" }
-T (int, 0, 4);      // { dg-warning "'\\(\\(int \\*\\)p\\)\\\[1]'" }
-T (int, 0, 5);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 1\\)\\)\\\[1]'" }
-T (int, 0, 6);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 2\\)\\)\\\[1]'" }
-T (int, 0, 7);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 3\\)\\)\\\[1]'" }
-T (int, 0, 8);      // { dg-warning "'\\(\\(int \\*\\)p\\)\\\[2]'" }
-T (int, 0, 9);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 1\\)\\)\\\[2]'" }
-
-
-T (int, 1, 0);      // { dg-warning "'\\(\\(int \\*\\)p\\)\\\[1]' is used uninitialized" }
-T (int, 1, 1);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 1\\)\\)\\\[1]'" }
-T (int, 1, 2);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 2\\)\\)\\\[1]'" }
-T (int, 1, 3);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 3\\)\\)\\\[1]'" }
-T (int, 1, 4);      // { dg-warning "'\\(\\(int \\*\\)p\\)\\\[2]'" }
-T (int, 1, 5);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 1\\)\\)\\\[2]'" }
-T (int, 1, 6);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 2\\)\\)\\\[2]'" }
-T (int, 1, 7);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 3\\)\\)\\\[2]'" }
-T (int, 1, 8);      // { dg-warning "'\\(\\(int \\*\\)p\\)\\\[3]'" }
-T (int, 1, 9);      // { dg-warning "'\\(\\(int \\*\\)\\(\\(char \\*\\)p \\+ 1\\)\\)\\\[3]'" }
-
-#undef T
 #define T(Type, idx, off)                      \
   __attribute__ ((noipa))                      \
   void UNIQ (test_)(int n)                     \
diff --git a/gcc/testsuite/gcc.dg/uninit-41.c b/gcc/testsuite/gcc.dg/uninit-41.c
new file mode 100644 (file)
index 0000000..b485611
--- /dev/null
@@ -0,0 +1,121 @@
+/* Verify that calls to non-modifying built-ins aren't considered
+   potentially modifying.
+   { dg-do compile }
+   { dg-options "-O2 -Wall" } */
+
+typedef __SIZE_TYPE__ size_t;
+
+void* alloca (size_t);
+void* calloc (size_t, size_t);
+void* malloc (size_t);
+int printf (const char *, ...);
+int scanf (const char *, ...);
+int sprintf (char *, const char *, ...);
+int snprintf (char *, size_t, const char *, ...);
+int puts (const char *);
+char* strcpy (char*, const char*);
+size_t strlen (const char*);
+
+void noproto ();
+
+void sink (int, ...);
+
+extern char a[];
+
+void nowarn_noproto (const char *fmt)
+{
+  int i;
+  noproto (&i);
+  sink (i);
+}
+
+void nowarn_scanf (const char *fmt)
+{
+  int i;
+  scanf ("%i", &i);
+  sink (i);
+}
+
+void test_puts_sprintf_alloca (const char *fmt)
+{
+  char *p;
+  {
+    p = alloca (8);
+    sprintf (a, fmt, p);                // fmt might contain %n
+    puts (p);
+  }
+
+  {
+    p = alloca (8);
+    snprintf (0, 0, fmt, p);            // same as above
+    puts (p);
+  }
+}
+
+void test_puts_alloca (const char *s)
+{
+  char *p = alloca (8);
+
+  {
+    char a[] = "foo";
+    puts (a);
+  }
+
+  puts (p);                             // { dg-warning "-Wuninitialized" }
+
+  {
+    p = alloca (strlen (s) + 1);
+    strcpy (p, s);
+    puts (p);
+  }
+
+  {
+    /* Verify that the puts() calls above isn't considered to have
+       potentially modified *P, and same for the one below.  */
+    p = alloca (strlen (s));
+    puts (p);                           // { dg-warning "-Wuninitialized" }
+    puts (p + 1);                       // { dg-warning "-Wuninitialized" }
+  }
+}
+
+
+void test_puts_malloc (const char *s, const char *t)
+{
+  char *p;
+
+  {
+    p = malloc (strlen (s) + 1);
+    strcpy (p, s);
+    puts (p);
+  }
+
+  {
+    p = malloc (strlen (t));
+    puts (p);                           // { dg-warning "-Wuninitialized" }
+  }
+}
+
+
+void test_puts_vla (const char *s, const char *t)
+{
+  {
+    char a[strlen (s) + 1];
+    strcpy (a, s);
+    puts (a);
+  }
+
+  {
+    char b[strlen (t)];
+    puts (b);                           // { dg-warning "-Wuninitialized" }
+  }
+}
+
+
+void test_printf_puts (const char *s)
+{
+  char *p = __builtin_malloc (1);
+
+  printf ("%s", s);
+
+  puts (p);                             // { dg-warning "-Wuninitialized" }
+}
index 718b32691c1ac2614cc4cf96a20e4e897209166d..ab64a680568a0af20c0b0d25fe28ed3d236640f1 100644 (file)
@@ -219,6 +219,70 @@ struct check_defs_data
   bool found_may_defs;
 };
 
+/* Return true if STMT is a call to built-in function all of whose
+   by-reference arguments are const-qualified (i.e., the function can
+   be assumed not to modify them).  */
+
+static bool
+builtin_call_nomodifying_p (gimple *stmt)
+{
+  if (!gimple_call_builtin_p (stmt, BUILT_IN_NORMAL))
+    return false;
+
+  tree fndecl = gimple_call_fndecl (stmt);
+  if (!fndecl)
+    return false;
+
+  tree fntype = TREE_TYPE (fndecl);
+  if (!fntype)
+    return false;
+
+  /* Check the called function's signature for non-constc pointers.
+     If one is found, return false.  */
+  unsigned argno = 0;
+  tree argtype;
+  function_args_iterator it;
+  FOREACH_FUNCTION_ARGS (fntype, argtype, it)
+    {
+      if (VOID_TYPE_P (argtype))
+       return true;
+
+      ++argno;
+
+      if (!POINTER_TYPE_P (argtype))
+       continue;
+
+      if (TYPE_READONLY (TREE_TYPE (argtype)))
+       continue;
+
+      return false;
+    }
+
+  /* If the number of actual arguments to the call is less than or
+     equal to the number of parameters, return false.  */
+  unsigned nargs = gimple_call_num_args (stmt);
+  if (nargs <= argno)
+    return false;
+
+  /* Check arguments passed through the ellipsis in calls to variadic
+     functions for pointers.  If one is found that's a non-constant
+     pointer, return false.  */
+  for (; argno < nargs; ++argno)
+    {
+      tree arg = gimple_call_arg (stmt, argno);
+      argtype = TREE_TYPE (arg);
+      if (!POINTER_TYPE_P (argtype))
+       continue;
+
+      if (TYPE_READONLY (TREE_TYPE (argtype)))
+       continue;
+
+      return false;
+    }
+
+  return true;
+}
+
 /* Callback for walk_aliased_vdefs.  */
 
 static bool
@@ -261,6 +325,9 @@ check_defs (ao_ref *ref, tree vdef, void *data_)
       return false;
     }
 
+  if (builtin_call_nomodifying_p (def_stmt))
+    return false;
+
   /* Found a may-def on this path.  */
   data->found_may_defs = true;
   return true;