From eabad2f2d026684a7ef20a7c4c0de838b1fc357e Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 26 Jun 2020 09:55:18 -0400
Subject: [PATCH 259/377] FIXME: more known-fn implementations to a new
 region-model2-impl-calls.cc

---
 gcc/Makefile.in                          |   1 +
 gcc/analyzer/region-model2-impl-calls.cc | 312 +++++++++++++++++++++++
 gcc/analyzer/region-model2.cc            | 260 +------------------
 gcc/analyzer/region-model2.h             |  29 +++
 4 files changed, 346 insertions(+), 256 deletions(-)
 create mode 100644 gcc/analyzer/region-model2-impl-calls.cc

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index e731025f0c8..8dbc5e2e536 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1236,6 +1236,7 @@ ANALYZER_OBJS = \
 	analyzer/reachable.o \
 	analyzer/region-model.o \
 	analyzer/region-model2.o \
+	analyzer/region-model2-impl-calls.o \
 	analyzer/sm.o \
 	analyzer/sm-file.o \
 	analyzer/sm-malloc.o \
diff --git a/gcc/analyzer/region-model2-impl-calls.cc b/gcc/analyzer/region-model2-impl-calls.cc
new file mode 100644
index 00000000000..21935490246
--- /dev/null
+++ b/gcc/analyzer/region-model2-impl-calls.cc
@@ -0,0 +1,312 @@
+/* Handling for the known behavior of various specific functions.
+   Copyright (C) 2020 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it
+under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tree.h"
+#include "function.h"
+#include "basic-block.h"
+#include "gimple.h"
+#include "gimple-iterator.h"
+#include "diagnostic-core.h"
+#include "graphviz.h"
+#include "options.h"
+#include "cgraph.h"
+#include "tree-dfa.h"
+#include "stringpool.h"
+#include "convert.h"
+#include "target.h"
+#include "fold-const.h"
+#include "tree-pretty-print.h"
+#include "diagnostic-color.h"
+#include "diagnostic-metadata.h"
+#include "tristate.h"
+#include "bitmap.h"
+#include "selftest.h"
+#include "function.h"
+#include "analyzer/analyzer.h"
+#include "analyzer/analyzer-logging.h"
+#include "ordered-hash-map.h"
+#include "options.h"
+#include "cgraph.h"
+#include "cfg.h"
+#include "digraph.h"
+#include "analyzer/supergraph.h"
+#include "sbitmap.h"
+#include "analyzer/region-model2.h"
+
+#if ENABLE_ANALYZER
+
+namespace ana {
+
+/* class call_details.  */
+
+/* call_details's ctor.  */
+
+call_details::call_details (const gcall *call, region_model2 *model,
+			    region_model2_context *ctxt)
+: m_call (call), m_model (model), m_ctxt (ctxt),
+  m_lhs_type (NULL_TREE), m_lhs_region (NULL)
+{
+  m_lhs_type = NULL_TREE;
+  if (tree lhs = gimple_call_lhs (call))
+    {
+      m_lhs_region = model->get_lvalue (lhs, ctxt);
+      m_lhs_type = TREE_TYPE (lhs);
+    }
+}
+
+/* FIXME.  */
+
+bool
+call_details::maybe_set_lhs (const svalue2 *result) const
+{
+  gcc_assert (result);
+  if (m_lhs_region)
+    {
+      m_model->set_value (m_lhs_region, result, m_ctxt);
+      return true;
+    }
+  else
+    return false;
+}
+
+/* FIXME.  */
+
+tree
+call_details::get_arg_tree (unsigned idx) const
+{
+  return gimple_call_arg (m_call, idx);
+}
+
+/* FIXME.  */
+
+const svalue2 *
+call_details::get_arg_svalue2 (unsigned idx) const
+{
+  tree arg = get_arg_tree (idx);
+  return m_model->get_rvalue (arg, m_ctxt);
+}
+
+/* FIXME.  */
+
+DEBUG_FUNCTION void
+call_details::dump_to_pp (pretty_printer *pp, bool simple) const
+{
+  for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
+    {
+      const svalue2 *arg_sval = get_arg_svalue2 (i);
+      pp_printf (pp, "arg %i: ", i);
+      arg_sval->dump_to_pp (pp, simple);
+      pp_newline (pp);
+    }
+}
+
+/* FIXME.  */
+
+DEBUG_FUNCTION void
+call_details::dump (bool simple) const
+{
+  pretty_printer pp;
+  pp_format_decoder (&pp) = default_tree_printer;
+  pp_show_color (&pp) = pp_show_color (global_dc->printer);
+  pp.buffer->stream = stderr;
+  dump_to_pp (&pp, simple);
+  pp_flush (&pp);
+}
+
+/* Implementations of specific functions.  */
+
+/* Handle the on_call_pre part of "alloca".  */
+
+bool
+region_model2::impl_call_alloca (const call_details &cd)
+{
+  const svalue2 *size_sval = cd.get_arg_svalue2 (0);
+  const region2 *new_reg = create_region_for_alloca (size_sval);
+  const svalue2 *ptr_sval
+    = m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
+  cd.maybe_set_lhs (ptr_sval);
+  return true;
+}
+
+/* FIXME.  */
+
+void
+region_model2::impl_call_analyzer_describe (const gcall *call,
+					    region_model2_context *ctxt)
+{
+  tree t_kind = gimple_call_arg (call, 0);
+  tree t_arg = gimple_call_arg (call, 1);
+  const svalue2 *sval = get_rvalue (t_arg, ctxt);
+  bool simple = zerop (t_kind);
+  label_text desc = sval->get_desc (simple);
+  warning_at (call->location, 0, "%s", desc.m_buffer);
+}
+
+/* Handle the "__analyzer_eval" by evaluating the input
+   and dumping as a dummy warning, so that test cases can use
+   dg-warning to validate the result (and so unexpected warnings will
+   lead to DejaGnu failures).
+   Broken out as a subroutine to make it easier to put a breakpoint on it
+   - though typically this doesn't help, as we have an SSA name as the arg,
+   and what's more interesting is usually the def stmt for that name.  */
+
+void
+region_model2::impl_call_analyzer_eval (const gcall *call,
+					region_model2_context *ctxt)
+{
+  tree t_arg = gimple_call_arg (call, 0);
+  tristate t = eval_condition (t_arg, NE_EXPR, integer_zero_node, ctxt);
+  warning_at (call->location, 0, "%s", t.as_string ());
+}
+
+/* Handle the on_call_pre part of "__builtin_expect" etc.  */
+
+bool
+region_model2::impl_call_builtin_expect (const call_details &cd)
+{
+  /* __builtin_expect's return value is its initial argument.  */
+  const svalue2 *sval = cd.get_arg_svalue2 (0);
+  cd.maybe_set_lhs (sval);
+  return false;
+}
+
+/* Handle the on_call_pre part of "calloc".  */
+
+bool
+region_model2::impl_call_calloc (const call_details &cd)
+{
+  const svalue2 *nmemb_sval = cd.get_arg_svalue2 (0);
+  const svalue2 *size_sval = cd.get_arg_svalue2 (1);
+  /* FIXME: check for overflow here?  */
+  const svalue2 *prod_sval
+    = m_mgr->get_or_create_binop (size_type_node, MULT_EXPR,
+				  nmemb_sval, size_sval);
+  const region2 *new_reg = create_region_for_heap_alloc (prod_sval);
+  zero_fill_region (new_reg);
+  if (cd.get_lhs_type ())
+    {
+      const svalue2 *ptr_sval
+	= m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
+      cd.maybe_set_lhs (ptr_sval);
+    }
+  else
+    {
+      // FIXME: report a leak here?
+    }
+  return true;
+}
+
+/* Handle the on_call_pre part of "malloc".  */
+
+bool
+region_model2::impl_call_malloc (const call_details &cd)
+{
+  const svalue2 *size_sval = cd.get_arg_svalue2 (0);
+  const region2 *new_reg = create_region_for_heap_alloc (size_sval);
+  if (cd.get_lhs_type ())
+    {
+      const svalue2 *ptr_sval
+	= m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
+      cd.maybe_set_lhs (ptr_sval);
+    }
+  else
+    {
+      // FIXME: report a leak here?
+    }
+  return true;
+}
+
+/* Handle the on_call_pre part of "memset" and "__builtin_memset".  */
+
+bool
+region_model2::impl_call_memset (const call_details &cd)
+{
+  const svalue2 *dest_sval = cd.get_arg_svalue2 (0);
+  const svalue2 *fill_value_sval = cd.get_arg_svalue2 (1);
+  const svalue2 *num_bytes_sval = cd.get_arg_svalue2 (2);
+
+  const region2 *dest_reg = deref_rvalue (dest_sval, cd.get_ctxt ());
+
+  if (tree num_bytes = num_bytes_sval->maybe_get_constant ())
+    {
+      /* "memset" of zero size is a no-op.  */
+      if (zerop (num_bytes))
+	return true;
+
+      /* Set with known amount.  */
+      // TODO: handle dynamically-sized regions
+      byte_size_t reg_size;
+      if (dest_reg->get_byte_size (&reg_size))
+	{
+	  /* Check for an exact size match.  */
+	  /* TODO: issue a diagnostic if it's bigger than the region.  */
+	  if (reg_size == wi::to_offset (num_bytes))
+	    {
+	      if (tree cst = fill_value_sval->maybe_get_constant ())
+		{
+		  if (zerop (cst))
+		    {
+		      zero_fill_region (dest_reg);
+		      return true;
+		    }
+		}
+	    }
+	}
+    }
+
+  /* Otherwise, mark region's contents as unknown.  */
+  mark_region_as_unknown (dest_reg);
+  return false;
+}
+
+/* Handle the on_call_pre part of "strlen".  */
+
+bool
+region_model2::impl_call_strlen (const call_details &cd)
+{
+  region_model2_context *ctxt = cd.get_ctxt ();
+  const svalue2 *arg_sval = cd.get_arg_svalue2 (0);
+  const region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
+  if (const string_region2 *str_reg
+      = buf_reg->dyn_cast_string_region2 ())
+    {
+      tree str_cst = str_reg->get_string_cst ();
+      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
+      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
+      int strlen_cst = sizeof_cst - 1;
+      if (cd.get_lhs_type ())
+	{
+	  tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst);
+	  const svalue2 *result_sval
+	    = m_mgr->get_or_create_constant_svalue2 (t_cst);
+	  cd.maybe_set_lhs (result_sval);
+	  return true;
+	}
+    }
+  /* Otherwise an unknown value.  */
+  return true;
+}
+
+} // namespace ana
+
+#endif /* #if ENABLE_ANALYZER */
diff --git a/gcc/analyzer/region-model2.cc b/gcc/analyzer/region-model2.cc
index e5f04b5419a..91a608114ff 100644
--- a/gcc/analyzer/region-model2.cc
+++ b/gcc/analyzer/region-model2.cc
@@ -4738,86 +4738,6 @@ region_model2::on_gcall (const supernode *snode,
 }
 #endif
 
-/* Helper class for handling calls.  */
-
-class call_details
-{
-public:
-  call_details (const gcall *call, region_model2 *model,
-		region_model2_context *ctxt)
-    : m_call (call), m_model (model), m_ctxt (ctxt),
-      m_lhs_type (NULL_TREE), m_lhs_region (NULL)
-  {
-    m_lhs_type = NULL_TREE;
-    if (tree lhs = gimple_call_lhs (call))
-      {
-	m_lhs_region = model->get_lvalue (lhs, ctxt);
-	m_lhs_type = TREE_TYPE (lhs);
-      }
-  }
-
-  region_model2_context *get_ctxt () const { return m_ctxt; }
-
-  tree get_lhs_type () const { return m_lhs_type; }
-  const region2 *get_lhs_region () const { return m_lhs_region; }
-
-  bool maybe_set_lhs (const svalue2 *result) const
-  {
-    gcc_assert (result);
-    if (m_lhs_region)
-      {
-	m_model->set_value (m_lhs_region, result, m_ctxt);
-	return true;
-      }
-    else
-      return false;
-  }
-
-  tree get_arg_tree (unsigned idx) const { return gimple_call_arg (m_call, idx); }
-  const svalue2 *get_arg_svalue2 (unsigned idx) const
-  {
-    tree arg = get_arg_tree (idx);
-    return m_model->get_rvalue (arg, m_ctxt);
-  }
-
-  void dump_to_pp (pretty_printer *pp, bool simple) const;
-  void dump (bool simple) const;
-
-private:
-  const gcall *m_call;
-  region_model2 *m_model;
-  region_model2_context *m_ctxt;
-  tree m_lhs_type;
-  const region2 *m_lhs_region;
-};
-
-/* FIXME.  */
-
-DEBUG_FUNCTION void
-call_details::dump_to_pp (pretty_printer *pp, bool simple) const
-{
-  for (unsigned i = 0; i < gimple_call_num_args (m_call); i++)
-    {
-      const svalue2 *arg_sval = get_arg_svalue2 (i);
-      pp_printf (pp, "arg %i: ", i);
-      arg_sval->dump_to_pp (pp, simple);
-      pp_newline (pp);
-    }
-}
-
-/* FIXME.  */
-
-DEBUG_FUNCTION void
-call_details::dump (bool simple) const
-{
-  pretty_printer pp;
-  pp_format_decoder (&pp) = default_tree_printer;
-  pp_show_color (&pp) = pp_show_color (global_dc->printer);
-  pp.buffer->stream = stderr;
-  dump_to_pp (&pp, simple);
-  pp_flush (&pp);
-}
-
 /* A subclass of pending_diagnostic for complaining about uses of
    poisoned values.  */
 
@@ -4909,7 +4829,7 @@ private:
    Return true if the function call has unknown side effects (it wasn't
    recognized and we don't have a body for it, or are unable to tell which
    fndecl it is).  */
-#if 1
+
 bool
 region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 {
@@ -4928,6 +4848,9 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
     {
       call_details cd (call, this, ctxt);
 
+      /* The various impl_call_* member functions are implemented
+	 in region-model2-impl-calls.cc.  */
+
       /* FIXME: should we be returning bools from these,
 	 or dropping through?
 	 Existing behavior was to return "true", but that looks wrong.  */
@@ -4981,7 +4904,6 @@ region_model2::on_call_pre (const gcall *call, region_model2_context *ctxt)
 
   return unknown_side_effects;
 }
-#endif
 
 /* Update this model for the CALL stmt, using CTXT to report any
    diagnostics - the second half.
@@ -5036,180 +4958,6 @@ region_model2::on_call_post (const gcall *call,
     handle_unrecognized_call (call, ctxt);
 }
 
-/* Handling for the known behavior of various specific functions.  */
-
-/* Handle the on_call_pre part of "alloca".  */
-
-bool
-region_model2::impl_call_alloca (const call_details &cd)
-{
-  const svalue2 *size_sval = cd.get_arg_svalue2 (0);
-  const region2 *new_reg = create_region_for_alloca (size_sval);
-  const svalue2 *ptr_sval
-    = m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
-  cd.maybe_set_lhs (ptr_sval);
-  return true;
-}
-
-/* FIXME.  */
-
-void
-region_model2::impl_call_analyzer_describe (const gcall *call,
-					    region_model2_context *ctxt)
-{
-  tree t_kind = gimple_call_arg (call, 0);
-  tree t_arg = gimple_call_arg (call, 1);
-  const svalue2 *sval = get_rvalue (t_arg, ctxt);
-  bool simple = zerop (t_kind);
-  label_text desc = sval->get_desc (simple);
-  warning_at (call->location, 0, "%s", desc.m_buffer);
-}
-
-/* Handle the "__analyzer_eval" by evaluating the input
-   and dumping as a dummy warning, so that test cases can use
-   dg-warning to validate the result (and so unexpected warnings will
-   lead to DejaGnu failures).
-   Broken out as a subroutine to make it easier to put a breakpoint on it
-   - though typically this doesn't help, as we have an SSA name as the arg,
-   and what's more interesting is usually the def stmt for that name.  */
-
-void
-region_model2::impl_call_analyzer_eval (const gcall *call,
-					region_model2_context *ctxt)
-{
-  tree t_arg = gimple_call_arg (call, 0);
-  tristate t = eval_condition (t_arg, NE_EXPR, integer_zero_node, ctxt);
-  warning_at (call->location, 0, "%s", t.as_string ());
-}
-
-/* Handle the on_call_pre part of "__builtin_expect" etc.  */
-
-bool
-region_model2::impl_call_builtin_expect (const call_details &cd)
-{
-  /* __builtin_expect's return value is its initial argument.  */
-  const svalue2 *sval = cd.get_arg_svalue2 (0);
-  cd.maybe_set_lhs (sval);
-  return false;
-}
-
-/* Handle the on_call_pre part of "calloc".  */
-
-bool
-region_model2::impl_call_calloc (const call_details &cd)
-{
-  const svalue2 *nmemb_sval = cd.get_arg_svalue2 (0);
-  const svalue2 *size_sval = cd.get_arg_svalue2 (1);
-  /* FIXME: check for overflow here?  */
-  const svalue2 *prod_sval
-    = m_mgr->get_or_create_binop (size_type_node, MULT_EXPR,
-				  nmemb_sval, size_sval);
-  const region2 *new_reg = create_region_for_heap_alloc (prod_sval);
-  zero_fill_region (new_reg);
-  if (cd.get_lhs_type ())
-    {
-      const svalue2 *ptr_sval
-	= m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
-      cd.maybe_set_lhs (ptr_sval);
-    }
-  else
-    {
-      // FIXME: report a leak here?
-    }
-  return true;
-}
-
-/* Handle the on_call_pre part of "malloc".  */
-
-bool
-region_model2::impl_call_malloc (const call_details &cd)
-{
-  const svalue2 *size_sval = cd.get_arg_svalue2 (0);
-  const region2 *new_reg = create_region_for_heap_alloc (size_sval);
-  if (cd.get_lhs_type ())
-    {
-      const svalue2 *ptr_sval
-	= m_mgr->get_ptr_svalue2 (cd.get_lhs_type (), new_reg);
-      cd.maybe_set_lhs (ptr_sval);
-    }
-  else
-    {
-      // FIXME: report a leak here?
-    }
-  return true;
-}
-
-/* Handle the on_call_pre part of "memset".  */
-
-bool
-region_model2::impl_call_memset (const call_details &cd)
-{
-  const svalue2 *dest_sval = cd.get_arg_svalue2 (0);
-  const svalue2 *fill_value_sval = cd.get_arg_svalue2 (1);
-  const svalue2 *num_bytes_sval = cd.get_arg_svalue2 (2);
-
-  const region2 *dest_reg = deref_rvalue (dest_sval, cd.get_ctxt ());
-
-  if (tree num_bytes = num_bytes_sval->maybe_get_constant ())
-    {
-      /* "memset" of zero size is a no-op.  */
-      if (zerop (num_bytes))
-	return true;
-
-      /* Set with known amount.  */
-      // TODO: handle dynamically-sized regions
-      byte_size_t reg_size;
-      if (dest_reg->get_byte_size (&reg_size))
-	{
-	  /* Check for an exact size match.  */
-	  /* TODO: issue a diagnostic if it's bigger than the region.  */
-	  if (reg_size == wi::to_offset (num_bytes))
-	    {
-	      if (tree cst = fill_value_sval->maybe_get_constant ())
-		{
-		  if (zerop (cst))
-		    {
-		      zero_fill_region (dest_reg);
-		      return true;
-		    }
-		}
-	    }
-	}
-    }
-
-  /* Otherwise, mark region's contents as unknown.  */
-  mark_region_as_unknown (dest_reg);
-  return false;
-}
-
-/* Handle the on_call_pre part of "strlen".  */
-
-bool
-region_model2::impl_call_strlen (const call_details &cd)
-{
-  region_model2_context *ctxt = cd.get_ctxt ();
-  const svalue2 *arg_sval = cd.get_arg_svalue2 (0);
-  const region2 *buf_reg = deref_rvalue (arg_sval, ctxt);
-  if (const string_region2 *str_reg
-      = buf_reg->dyn_cast_string_region2 ())
-    {
-      tree str_cst = str_reg->get_string_cst ();
-      /* TREE_STRING_LENGTH is sizeof, not strlen.  */
-      int sizeof_cst = TREE_STRING_LENGTH (str_cst);
-      int strlen_cst = sizeof_cst - 1;
-      if (cd.get_lhs_type ())
-	{
-	  tree t_cst = build_int_cst (cd.get_lhs_type (), strlen_cst);
-	  const svalue2 *result_sval
-	    = m_mgr->get_or_create_constant_svalue2 (t_cst);
-	  cd.maybe_set_lhs (result_sval);
-	  return true;
-	}
-    }
-  /* Otherwise an unknown value.  */
-  return true;
-}
-
 /* Handle a call CALL to a function with unknown behavior.
 
    Traverse the region2s in this model, determining what region2s are
diff --git a/gcc/analyzer/region-model2.h b/gcc/analyzer/region-model2.h
index 32ba5f8df1c..d441ddc1b2c 100644
--- a/gcc/analyzer/region-model2.h
+++ b/gcc/analyzer/region-model2.h
@@ -2102,6 +2102,35 @@ private:
 
 struct append_ssa_names_cb_data;
 
+/* Helper class for handling calls to functions with known behavior.
+   Implemented in region-model2-impl-calls.c.  */
+
+class call_details
+{
+public:
+  call_details (const gcall *call, region_model2 *model,
+		region_model2_context *ctxt);
+
+  region_model2_context *get_ctxt () const { return m_ctxt; }
+  tree get_lhs_type () const { return m_lhs_type; }
+  const region2 *get_lhs_region () const { return m_lhs_region; }
+
+  bool maybe_set_lhs (const svalue2 *result) const;
+
+  tree get_arg_tree (unsigned idx) const;
+  const svalue2 *get_arg_svalue2 (unsigned idx) const;
+
+  void dump_to_pp (pretty_printer *pp, bool simple) const;
+  void dump (bool simple) const;
+
+private:
+  const gcall *m_call;
+  region_model2 *m_model;
+  region_model2_context *m_ctxt;
+  tree m_lhs_type;
+  const region2 *m_lhs_region;
+};
+
 /* A region_model2 encapsulates a representation of the state of memory, with
    a tree of region2s, along with their associated values.
    The representation is graph-like because values can be pointers to
-- 
2.26.2

