From 3faf50952d639f79207de595719f79f22a993eb9 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Tue, 28 Apr 2020 04:44:26 -0400
Subject: [PATCH 084/159] FIXME: handle pure fns, known fns, and fn ptrs with
 handle_unrecognized_call

---
 gcc/analyzer/region-model2.cc                 |  11 +-
 gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c | 120 ++++++++++++++++++
 2 files changed, 130 insertions(+), 1 deletion(-)

diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index ac35ad8750a..23fe704e34e 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -4080,6 +4080,8 @@ region_model2::on_gcall (const supernode *snode,
 {
   region_model2_context *ctxt = NULL; // FIXME
 
+  bool unknown_effects = false;
+
   /* Debugging/test support.  */
   if (is_special_named_call_p (call, "__analyzer_dump", 0))
     {
@@ -4163,9 +4165,16 @@ region_model2::on_gcall (const supernode *snode,
 	  /* Otherwise an unknown value.  */
 	  return true;
 	}
+
+      if (!fndecl_has_gimple_body_p (callee_fndecl)
+	  && !DECL_PURE_P (callee_fndecl))
+	unknown_effects = true;
     }
+  else
+    unknown_effects = true;
 
-  handle_unrecognized_call (call, ctxt);
+  if (unknown_effects)
+    handle_unrecognized_call (call, ctxt);
 
   return true;
 }
diff --git a/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c
index fd99127e3ec..be722c2215b 100644
--- a/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c
+++ b/gcc/testsuite/gcc.dg/analyzer/unknown-fns-2.c
@@ -30,6 +30,30 @@ void test_1 (void)
   __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
 }
 
+/* As test_1, but with an unknown fn_ptr.  */
+
+void test_1a (void (*fn_ptr) (void *))
+{
+  int i;
+
+  i = 42;
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  fn_ptr (NULL);
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+  
+  fn_ptr (&i);
+  __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+  i = 17;
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+  /* Even though we're not passing &i to unknown_fn, it escaped
+     above, so fn_ptr (NULL) could write to it.  */
+  fn_ptr (NULL);
+  __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
+
 int *global_for_test_2;
 
 void test_2 (void)
@@ -119,3 +143,99 @@ void test_4 (struct used_by_test_4 *st4_ptr)
   unknown_fn (NULL);
   __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
 }
+
+static void __attribute__((noinline))
+known_fn (void *ptr)
+{
+  /* Empty.  */
+}
+
+void test_5 (void)
+{
+  int i;
+
+  i = 42;
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  known_fn (&i);
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  i = 17;
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+  /* Ensure that we don't consider &i to have escaped.  */
+  unknown_fn (NULL);
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+extern int __attribute__ ((__pure__))
+unknown_pure_fn (void *);
+
+void test_6 (void)
+{
+  int i;
+
+  i = 42;
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  unknown_pure_fn (&i);
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  i = 17;
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+  /* Ensure that we don't consider &i to have escaped.  */
+  unknown_fn (NULL);
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+extern void unknown_const_fn (const void *);
+
+void test_7 (void)
+{
+  int i;
+
+  i = 42;
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  /* &i is passed as a const void *, so i shouldn't be clobbered by
+     the call.  */
+  unknown_const_fn (&i);
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  i = 17;
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+  /* Ensure that we don't consider &i to have escaped.  */
+  unknown_fn (NULL);
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+}
+
+struct used_by_test_8
+{
+  int *int_ptr;
+};
+
+void test_8 (void)
+{
+  int i;
+
+  i = 42;
+  __analyzer_eval (i == 42); /* { dg-warning "TRUE" } */
+
+  struct used_by_test_8 st8;
+  st8.int_ptr = &i;
+
+  /* Although unknown_const_fn takes a const void *, the
+     int_ptr is a non-const int *, and so &i should be considered
+     writable.  */
+  unknown_const_fn (&st8);
+  __analyzer_eval (i == 42); /* { dg-warning "UNKNOWN" } */
+
+  i = 17;
+  __analyzer_eval (i == 17); /* { dg-warning "TRUE" } */
+
+  /* &i should be considered to have escaped.  */
+  unknown_fn (NULL);
+  __analyzer_eval (i == 17); /* { dg-warning "UNKNOWN" } */
+}
-- 
2.21.0

