From bd914a1d61dcc522006f20a78612ff847bae2e5b Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 13 Mar 2019 17:57:53 -0400
Subject: [PATCH 103/217] FIXME: start adding path-filtering

---
 gcc/analyzer/constraints.cc                  | 57 ++++++++++++++++-----
 gcc/analyzer/constraints.h                   | 13 +++--
 gcc/analyzer/engine.cc                       | 74 ++++++++++++++++++++++++++--
 gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c |  3 +-
 4 files changed, 126 insertions(+), 21 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index 1f94eb8..a85064e 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -294,17 +294,23 @@ path_state::on_assignment (path_var lhs, path_var rhs)
 
 bool
 path_state::apply_constraints_for_edge (const superedge &edge,
-					const gimple *last_stmt)
+					const gimple *last_stmt,
+					int stack_depth)
 {
+  if (edge.m_kind == SUPEREDGE_CALL)
+    // FIXME: use as_a<>
+    return apply_constraints_for_call (*edge.dyn_cast_call_superedge (),
+				       stack_depth);
+
   if (last_stmt == NULL)
     return true;
 
   if (const gcond *cond_stmt = dyn_cast <const gcond *> (last_stmt))
-    return apply_constraints_for_gcond (edge, cond_stmt);
+    return apply_constraints_for_gcond (edge, cond_stmt, stack_depth);
 
   // FIXME: some linker issues with is_a_helper const
   if (const gswitch *switch_stmt = dyn_cast <gswitch *> (const_cast <gimple *> (last_stmt)))
-    return apply_constraints_for_gswitch (edge, switch_stmt);
+    return apply_constraints_for_gswitch (edge, switch_stmt, stack_depth);
 
   return true;
 }
@@ -312,8 +318,35 @@ path_state::apply_constraints_for_edge (const superedge &edge,
 /* FIXME.  */
 
 bool
+path_state::apply_constraints_for_call (const call_superedge& edge,
+					int caller_stack_depth)
+{
+  /* Map args to parms at next stack_depth.  */
+  int callee_stack_depth = caller_stack_depth + 1;
+
+  tree callee = edge.get_callee_decl ();
+  gcall *call_stmt = edge.get_call_stmt ();
+
+  int i = 0;
+  for (tree parm = DECL_ARGUMENTS (callee); parm;
+       parm = DECL_CHAIN (parm), ++i)
+    {
+      tree parm_default_ssa = ssa_default_def (edge.m_dest->m_fun, parm);
+      add_constraint (path_var (parm_default_ssa, callee_stack_depth),
+		      EQ_EXPR,
+		      path_var (gimple_call_arg (call_stmt, i),
+				caller_stack_depth));
+    }
+  return true;
+}
+
+
+/* FIXME.  */
+
+bool
 path_state::apply_constraints_for_gcond (const superedge &edge,
-					 const gcond *cond_stmt)
+					 const gcond *cond_stmt,
+					 int stack_depth)
 {
   ::edge cfg_edge = edge.get_any_cfg_edge ();
   gcc_assert (cfg_edge != NULL);
@@ -324,23 +357,23 @@ path_state::apply_constraints_for_gcond (const superedge &edge,
   tree rhs = gimple_cond_rhs (cond_stmt);
   if (cfg_edge->flags & EDGE_FALSE_VALUE)
     op = invert_tree_comparison (op, false /* honor_nans */);
-  // FIXME: stack depth:
-  return add_constraint (path_var (lhs, 0), op, path_var (rhs, 0));
+  return add_constraint (path_var (lhs, stack_depth), op, path_var (rhs, stack_depth));
 }
 
 /* FIXME.  */
 
 bool
 path_state::apply_constraints_for_gswitch (const superedge &edge,
-					   const gswitch *switch_stmt)
+					   const gswitch *switch_stmt,
+					   int stack_depth)
 {
   // FIXME: use an as_a for this cast:
   const switch_cfg_superedge &switch_edge = (const switch_cfg_superedge &)edge;
-  path_var index (gimple_switch_index (switch_stmt), 0); // FIXME: depth
+  path_var index (gimple_switch_index (switch_stmt), stack_depth);
   tree case_label = switch_edge.get_case_label ();
   gcc_assert (TREE_CODE (case_label) == CASE_LABEL_EXPR);
-  path_var lower_bound (CASE_LOW (case_label), 0); // FIXME: depth
-  path_var upper_bound (CASE_HIGH (case_label), 0); // FIXME: depth
+  path_var lower_bound (CASE_LOW (case_label), stack_depth);
+  path_var upper_bound (CASE_HIGH (case_label), stack_depth);
   if (lower_bound.m_tree)
     {
       if (upper_bound.m_tree)
@@ -364,8 +397,8 @@ path_state::apply_constraints_for_gswitch (const superedge &edge,
 	{
 	  tree other_label = gimple_switch_label (switch_stmt,
 						  other_idx);
-	  path_var other_lower_bound (CASE_LOW (other_label), 0); // FIXME: depth
-	  path_var other_upper_bound (CASE_HIGH (other_label), 0); // FIXME: depth
+	  path_var other_lower_bound (CASE_LOW (other_label), stack_depth);
+	  path_var other_upper_bound (CASE_HIGH (other_label), stack_depth);
 	  gcc_assert (other_lower_bound.m_tree);
 	  if (other_upper_bound.m_tree)
 	    {
diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
index ae8011d..ef2117c 100644
--- a/gcc/analyzer/constraints.h
+++ b/gcc/analyzer/constraints.h
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #define GCC_ANALYZER_CONSTRAINTS_H
 
 class superedge;
+class call_superedge;
 
 /* A tree, extended with stack frame information for locals, so that
    we can distinguish between different values of locals within a potentially
@@ -144,15 +145,21 @@ public:
   void on_assignment (path_var lhs, path_var rhs);
 
   bool apply_constraints_for_edge (const superedge &edge,
-				   const gimple *last_stmt);
+				   const gimple *last_stmt,
+				   int stack_depth);
 
   bool add_constraint (path_var lhs, enum tree_code op, path_var rhs);
 
   //private:
+  bool apply_constraints_for_call (const call_superedge& edge,
+				   int stack_depth);
+
   bool apply_constraints_for_gcond (const superedge &edge,
-				    const gcond *cond_stmt);
+				    const gcond *cond_stmt,
+				    int stack_depth);
   bool apply_constraints_for_gswitch (const superedge &edge,
-				      const gswitch *switch_stmt);
+				      const gswitch *switch_stmt,
+				      int stack_depth);
   bool get_equiv_class (path_var pv, equiv_class_id *out) const;
   equiv_class_id get_or_add_equiv_class (path_var vr);
   tristate eval_condition (equiv_class_id lhs,
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index dec3f55..8763ca1 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -904,6 +904,7 @@ public:
   checker_path *expand_path (const checker_path &path,
 			     tree var, state_machine::state_t state);
   void prune_path (checker_path *path, tree var, state_machine::state_t state);
+  bool is_valid_path (const checker_path &path);
   void maybe_emit (const candidate_diagnostic &cd);
 
   void dump_analyzer_summary (const supernode *node) const;
@@ -1431,7 +1432,7 @@ engine::dfs_traverse (sm_instance sm_inst,
 	      are impossible.  */
 	   path_state new_ps (ps);
 	   const gimple *last_stmt = node->get_last_stmt ();
-	   if (!new_ps.apply_constraints_for_edge (*succ, last_stmt))
+	   if (!new_ps.apply_constraints_for_edge (*succ, last_stmt, 0)) // FIXME: stack depth
 	     {
 	       log ("skipping impossible edge to SN: %i",
 		    succ->m_dest->m_index);
@@ -2444,8 +2445,19 @@ engine::expand_call_summary (const call_summary *summary_event,
 	  break;
 	if (node->m_succs.length () > 0)
 	  {
-	    node = node->m_succs[0]->m_dest;
+	    const superedge *succ = node->m_succs[0];
 	    // FIXME: must be in same function, etc etc
+	    if (succ->m_kind == SUPEREDGE_CFG_EDGE)
+	      {
+		checker_event *event
+		  = new edge_event (succ, node->get_end_location (),
+				    node->m_fun->decl,
+				    callee_stack_depth,
+				    print ("taking edge SN:%i -> SN:%i",
+					   node->m_index, succ->m_dest->m_index));
+		result->add_event (event);
+	      }
+	    node = succ->m_dest;
 	  }
 	else
 	  break;
@@ -2745,6 +2757,53 @@ engine::prune_path (checker_path *path,
 }
 
 /* FIXME.  */
+// FIXME: needs to happen before pruning
+
+bool
+engine::is_valid_path (const checker_path &path)
+{
+  JIT_LOG_FUNC (get_logger ());
+  path.maybe_log (get_logger (), "path");
+
+  path_state ps;
+  for (int idx = 0; idx < path.m_events.length (); idx++)
+    {
+      checker_event *base_event = path.m_events[idx];
+      log ("event %i: %s",
+	   idx, base_event->to_str ().c_str ());
+      log ("  ps was: %s", ps.to_str ().c_str ());
+
+      //FIXME:
+      switch (base_event->m_kind)
+	{
+	case EK_EDGE:
+	  {
+	    edge_event *edge_ev = (edge_event *)base_event;
+	    if (!ps.apply_constraints_for_edge (*edge_ev->m_edge,
+						edge_ev->m_edge->m_src->get_last_stmt (),
+						base_event->m_depth))
+	      {
+		log ("  constraints for edge aren't satisfiable");
+		return false;
+	      }
+	  }
+
+	default:
+	  break;
+	}
+
+      log ("  ps after: %s", ps.to_str ().c_str ());
+
+      // FIXME: we're going to need all the statements in order to track things
+      // ...as a subclass, I think
+      // also, we might need the call summaries to be expanded,
+      // and then filtered away, to ensure that we respect their effects
+    }
+
+  return true; // TODO
+}
+
+/* FIXME.  */
 
 void
 engine::maybe_emit (const candidate_diagnostic &cd)
@@ -2752,7 +2811,6 @@ engine::maybe_emit (const candidate_diagnostic &cd)
   JIT_LOG_FUNC (get_logger ());
   cd.m_path.maybe_log (get_logger (), "path");
 
-  gcc_rich_location rich_loc (cd.m_stmt->location);
 #if 0
   backtrace full_backtrace;
   if (!make_full_backtrace (&full_backtrace))
@@ -2769,6 +2827,14 @@ engine::maybe_emit (const candidate_diagnostic &cd)
 #endif
 
   checker_path *expanded_path = expand_path (path, cd.m_var, cd.m_state);
+
+  if (!is_valid_path (*expanded_path))
+    {
+      log ("rejecting non-valid path");
+      delete expanded_path;
+      return;
+    }
+
   prune_path (expanded_path, cd.m_var, cd.m_state);
 
   char *msg = print ("here (%qE is in state %qs)", cd.m_var,
@@ -2781,9 +2847,9 @@ engine::maybe_emit (const candidate_diagnostic &cd)
   expanded_path->add_event (end_of_path);
   // FIXME: ideally would show the full message here, too?
 
-
   expanded_path->maybe_log (get_logger (), "path for emission");
 
+  gcc_rich_location rich_loc (cd.m_stmt->location);
   rich_loc.set_path (expanded_path);
 
   /* Push/pop cfun to ensure we get "In function " announcements
diff --git a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c
index 4c0eaf6..a266386 100644
--- a/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c
+++ b/gcc/testsuite/gcc.dg/analyzer/malloc-ipa-7.c
@@ -15,8 +15,7 @@ void test_1(void *p)
 static void maybe_calls_free_2(int *q, int flag)
 {
   if (flag)
-    free(q); /* { dg-bogus "double-free" "" { xfail *-*-* } } */
-  /* TODO: (xfail): need to filter out impossible paths.  */
+    free(q); /* { dg-bogus "double-free" } */
 }
 
 void test_2(void *p)
-- 
1.8.5.3

