From aa568e2db30ab4a6793e5fa1398026046897a5b7 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Fri, 15 Mar 2019 10:18:38 -0400
Subject: [PATCH 119/161] FIXME: be more precise about stack depths

---
 gcc/analyzer/constraints.cc | 66 ++++++++++++++++++++++++---------------------
 gcc/analyzer/constraints.h  | 25 ++++++++---------
 gcc/analyzer/engine.cc      | 50 +++++++++++++++++++++-------------
 3 files changed, 80 insertions(+), 61 deletions(-)

diff --git a/gcc/analyzer/constraints.cc b/gcc/analyzer/constraints.cc
index 1ed9f41..d86faaa 100644
--- a/gcc/analyzer/constraints.cc
+++ b/gcc/analyzer/constraints.cc
@@ -234,6 +234,7 @@ equiv_class_id::get_obj (path_state &ps) const
 /* FIXME.  */
 
 path_state::path_state (const path_state &other)
+: m_stack_depth (other.m_stack_depth)
 {
   // FIXME: auto_vec's copy_ctor...
   int i;
@@ -306,28 +307,25 @@ path_state::on_assignment (path_var lhs, path_var rhs)
 
 bool
 path_state::apply_constraints_for_edge (const superedge &edge,
-					const gimple *last_stmt,
-					int stack_depth)
+					const gimple *last_stmt)
 {
   if (edge.m_kind == SUPEREDGE_CALL)
     // FIXME: use as_a<>
-    return apply_constraints_for_call (*edge.dyn_cast_call_superedge (),
-				       stack_depth);
+    return apply_constraints_for_call (*edge.dyn_cast_call_superedge ());
 
   if (edge.m_kind == SUPEREDGE_RETURN)
     // FIXME: use as_a<>
-    return apply_constraints_for_return (*edge.dyn_cast_call_superedge (),
-					 stack_depth);
+    return apply_constraints_for_return (*edge.dyn_cast_call_superedge ());
 
   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, stack_depth);
+    return apply_constraints_for_gcond (edge, cond_stmt);
 
   // 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, stack_depth);
+    return apply_constraints_for_gswitch (edge, switch_stmt);
 
   return true;
 }
@@ -335,11 +333,11 @@ 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)
+path_state::apply_constraints_for_call (const call_superedge& edge)
 {
-  /* Map args to parms at next stack_depth.  */
-  int callee_stack_depth = caller_stack_depth + 1;
+  /* Map args to parms at next stack_depth, incrementing stack depth.  */
+  int caller_stack_depth = m_stack_depth;
+  int callee_stack_depth = ++m_stack_depth;
 
   tree callee = edge.get_callee_decl ();
   gcall *call_stmt = edge.get_call_stmt ();
@@ -360,24 +358,27 @@ path_state::apply_constraints_for_call (const call_superedge& edge,
 /* FIXME.  */
 
 bool
-path_state::apply_constraints_for_return (const call_superedge&,
-					  int callee_stack_depth)
+path_state::apply_constraints_for_return (const call_superedge&)
 {
-  purge_stack_frame (callee_stack_depth);
+  pop_stack_frame ();
   return true;
 }
 
-/* Purge vars at callee_stack_depth (and beyond).
+/* Decrement the stack depth, purging vars at the old stack depth (and beyond).
    This is broken out as a subroutine so it can be unit-tested.  */
 
 void
-path_state::purge_stack_frame (int callee_stack_depth)
+path_state::pop_stack_frame ()
 {
+  int callee_stack_depth = m_stack_depth--;
+
   int i;
   equiv_class *ec;
   FOR_EACH_VEC_ELT (m_equiv_classes, i, ec)
     {
       ec->purge_stack_frame (callee_stack_depth);
+      /* This can lead to empty equiv classes (possibly without
+	 constants).  Perhaps we ought to purge them?  */
     }
 }
 
@@ -386,8 +387,7 @@ path_state::purge_stack_frame (int callee_stack_depth)
 
 bool
 path_state::apply_constraints_for_gcond (const superedge &edge,
-					 const gcond *cond_stmt,
-					 int stack_depth)
+					 const gcond *cond_stmt)
 {
   ::edge cfg_edge = edge.get_any_cfg_edge ();
   gcc_assert (cfg_edge != NULL);
@@ -398,23 +398,24 @@ 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 */);
-  return add_constraint (path_var (lhs, stack_depth), op, path_var (rhs, stack_depth));
+  return add_constraint (path_var (lhs, m_stack_depth),
+			 op,
+			 path_var (rhs, m_stack_depth));
 }
 
 /* FIXME.  */
 
 bool
 path_state::apply_constraints_for_gswitch (const superedge &edge,
-					   const gswitch *switch_stmt,
-					   int stack_depth)
+					   const gswitch *switch_stmt)
 {
   // 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), stack_depth);
+  path_var index (gimple_switch_index (switch_stmt), m_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), stack_depth);
-  path_var upper_bound (CASE_HIGH (case_label), stack_depth);
+  path_var lower_bound (CASE_LOW (case_label), m_stack_depth);
+  path_var upper_bound (CASE_HIGH (case_label), m_stack_depth);
   if (lower_bound.m_tree)
     {
       if (upper_bound.m_tree)
@@ -438,8 +439,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), stack_depth);
-	  path_var other_upper_bound (CASE_HIGH (other_label), stack_depth);
+	  path_var other_lower_bound (CASE_LOW (other_label), m_stack_depth);
+	  path_var other_upper_bound (CASE_HIGH (other_label), m_stack_depth);
 	  gcc_assert (other_lower_bound.m_tree);
 	  if (other_upper_bound.m_tree)
 	    {
@@ -615,9 +616,10 @@ path_state::get_or_add_equiv_class (path_var pv)
 tristate
 path_state::dump_condition (tree lhs, enum tree_code op, tree rhs)
 {
-  // FIXME: stack depths?
-  equiv_class_id lhs_ec_id = get_or_add_equiv_class (path_var (lhs, 0));
-  equiv_class_id rhs_ec_id = get_or_add_equiv_class (path_var (rhs, 0));
+  equiv_class_id lhs_ec_id
+    = get_or_add_equiv_class (path_var (lhs, m_stack_depth));
+  equiv_class_id rhs_ec_id
+    = get_or_add_equiv_class (path_var (rhs, m_stack_depth));
 
   return eval_condition (lhs_ec_id, op, rhs_ec_id);
 }
@@ -1210,12 +1212,14 @@ test_stack_frames ()
 
   {
     path_state ps;
+    ps.m_stack_depth = 1;
+
     /* x == a.  */
     ADD_SAT_CONSTRAINT (ps, x, EQ_EXPR, a);
     ASSERT_CONDITION_TRUE (ps, x, EQ_EXPR, a);
 
     /* Pop the stack.  */
-    ps.purge_stack_frame (1);
+    ps.pop_stack_frame ();
     ASSERT_CONDITION_UNKNOWN (ps, x, EQ_EXPR, a);
   }
 }
diff --git a/gcc/analyzer/constraints.h b/gcc/analyzer/constraints.h
index 915b88d..adf5269 100644
--- a/gcc/analyzer/constraints.h
+++ b/gcc/analyzer/constraints.h
@@ -129,7 +129,7 @@ class constraint {
 class path_state
 {
 public:
-  path_state () {}
+  path_state () : m_stack_depth (0) {}
   path_state (const path_state &other);
   ~path_state ();
 
@@ -144,29 +144,29 @@ public:
     return *m_equiv_classes[idx];
   }
 
+  path_var make_path_var (tree expr) const
+  {
+    return path_var (expr, m_stack_depth);
+  }
+
   void on_assignment (path_var lhs, path_var rhs);
 
   bool apply_constraints_for_edge (const superedge &edge,
-				   const gimple *last_stmt,
-				   int stack_depth);
+				   const gimple *last_stmt);
 
   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_call (const call_superedge& edge);
 
-  bool apply_constraints_for_return (const call_superedge& edge,
-				     int stack_depth);
+  bool apply_constraints_for_return (const call_superedge& edge);
 
-  void purge_stack_frame (int callee_stack_depth);
+  void pop_stack_frame ();
 
   bool apply_constraints_for_gcond (const superedge &edge,
-				    const gcond *cond_stmt,
-				    int stack_depth);
+				    const gcond *cond_stmt);
   bool apply_constraints_for_gswitch (const superedge &edge,
-				      const gswitch *switch_stmt,
-				      int stack_depth);
+				      const gswitch *switch_stmt);
   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,
@@ -178,6 +178,7 @@ public:
 
   auto_vec<equiv_class *> m_equiv_classes;
   auto_vec<constraint> m_constraints;
+  int m_stack_depth;
 };
 
 
diff --git a/gcc/analyzer/engine.cc b/gcc/analyzer/engine.cc
index afa1540..de8d48e 100644
--- a/gcc/analyzer/engine.cc
+++ b/gcc/analyzer/engine.cc
@@ -975,7 +975,7 @@ public:
 		const backtrace &b, checker_path *path, path_state *ps);
   void on_assignment (const supernode *node, const gimple *stmt,
 		      sm_instance *sm_inst, const sm_instance *old_sm_inst,
-		      tree lhs, tree rhs, const backtrace &b,
+		      path_var lhs, path_var rhs, const backtrace &b,
 		      path_state *ps);
 
   void relax (backtrace b);
@@ -1547,7 +1547,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, 0)) // FIXME: stack depth
+	   if (!new_ps.apply_constraints_for_edge (*succ, last_stmt))
 	     {
 	       log ("skipping impossible edge to SN: %i",
 		    succ->m_dest->m_index);
@@ -1599,7 +1599,8 @@ void
 engine::on_phi (const supernode *node, const gphi *phi,
 		sm_instance *sm_inst, const sm_instance *old_sm_inst,
 		const backtrace &b, /* FIXME: <<< this is purely so that we can generate full paths for debugging */
-		checker_path *path, path_state *ps,
+		checker_path *path ATTRIBUTE_UNUSED,
+		path_state *ps,
 		const superedge *last_edge)
 {
   JIT_LOG_FUNC (get_logger ());
@@ -1631,7 +1632,8 @@ engine::on_phi (const supernode *node, const gphi *phi,
   size_t index = last_cfg_superedge->m_cfg_edge->dest_idx;
   tree src = gimple_phi_arg_def (const_cast <gphi *> (phi), index); // FIXME const_cast
   tree lhs = gimple_phi_result (phi);
-  on_assignment (node, phi, sm_inst, old_sm_inst, lhs, src, b, ps);
+  on_assignment (node, phi, sm_inst, old_sm_inst, ps->make_path_var (lhs),
+		 ps->make_path_var (src), b, ps);
 }
 
 void
@@ -1675,7 +1677,9 @@ engine::on_stmt (const supernode *node, const gimple *stmt,
 	case INTEGER_CST:
 	  {
 	    /* Direct assignment of the form LHS = RHS;  */
-	    on_assignment (node, assign, sm_inst, old_sm_inst, lhs, rhs1,
+	    on_assignment (node, assign, sm_inst, old_sm_inst,
+			   ps->make_path_var (lhs),
+			   ps->make_path_var (rhs1),
 			   b, ps);
 	  }
 	  break;
@@ -1733,7 +1737,9 @@ engine::on_stmt (const supernode *node, const gimple *stmt,
       tree lhs = DECL_RESULT (callee);
       tree rhs = gimple_return_retval (return_);
       if (rhs)
-	on_assignment (node, return_, sm_inst, old_sm_inst, lhs, rhs, b, ps);
+	on_assignment (node, return_, sm_inst, old_sm_inst,
+		       ps->make_path_var (lhs),
+		       ps->make_path_var (rhs), b, ps);
     }
 
   if (const gcall *call = dyn_cast <const gcall *> (stmt))
@@ -1786,15 +1792,16 @@ engine::on_stmt (const supernode *node, const gimple *stmt,
 void
 engine::on_assignment (const supernode *node, const gimple *stmt,
 		       sm_instance *sm_inst, const sm_instance *old_sm_inst,
-		       tree lhs, tree rhs, const backtrace &b, path_state *ps)
+		       path_var lhs, path_var rhs, const backtrace &b,
+		       path_state *ps)
 {
   JIT_LOG_SCOPE (get_logger ());
   if (get_logger ())
     {
       log ("sm_inst: %s", sm_inst->to_str (m_sm).c_str ());
       log ("old_sm_inst: %s", old_sm_inst->to_str (m_sm).c_str ());
-      log ("lhs: %s", expr_to_string (lhs).c_str ());
-      log ("rhs: %s", expr_to_string (rhs).c_str ());
+      log ("lhs: %s", lhs.to_str ().c_str ());
+      log ("rhs: %s", rhs.to_str ().c_str ());
       if (ps)
 	log ("old ps: %s", ps->to_str ().c_str ());
     }
@@ -1802,22 +1809,22 @@ engine::on_assignment (const supernode *node, const gimple *stmt,
   // FIXME: should ps always be non-NULL?
   // FIXME: this isn't right; this assumes LHS = RHS; what if there are ops???
   if (ps)
-    ps->on_assignment (path_var (lhs, 0), path_var (rhs, 0)); // FIXME stack depths
+    ps->on_assignment (lhs, rhs);
 
   /* Copy the state of RHS to LHS.  */
   for (set_of_vars::iterator iter = old_sm_inst->m_active_vars.begin ();
        iter != old_sm_inst->m_active_vars.end ();
        ++iter)
     {
-      if ((*iter).m_var == rhs)
+      if ((*iter).m_var == rhs.m_tree)
 	{
 	  log ("got match");
 	  engine_sm_context ctxt (this, b, sm_inst, old_sm_inst,
 				  NULL /* FIXME */, ps);
 	  // FIXME: we want to overwrite any old states
 	  // (but respect the gstate, I think):
-	  ctxt.on_transition (node, stmt, lhs, 0/* FIXME */, (*iter).m_state,
-			      rhs);
+	  ctxt.on_transition (node, stmt, lhs.m_tree, 0/* FIXME */,
+			      (*iter).m_state, rhs.m_tree);
 	}
     }
 
@@ -1950,6 +1957,9 @@ engine::refine_for_call (sm_instance sm_inst, cgraph_edge *edge,
   tree callee = edge->callee->get_fun ()->decl;
   gcall *call = edge->call_stmt;
 
+  int caller_stack_depth = b.m_path.m_stack_depth;
+  int callee_stack_depth = caller_stack_depth + 1;
+
   int i = 0;
   for (tree parm = DECL_ARGUMENTS (callee); parm;
        parm = DECL_CHAIN (parm), ++i)
@@ -1965,7 +1975,9 @@ engine::refine_for_call (sm_instance sm_inst, cgraph_edge *edge,
       if (parm_default_ssa)
 	on_assignment (caller_node, call,
 		       &sm_inst_for_call, &sm_inst /* FIXME.  */,
-		       parm_default_ssa, arg, b, &ps);
+		       path_var (parm_default_ssa, callee_stack_depth),
+		       path_var (arg, caller_stack_depth),
+		       b, &ps);
 #if 0
       /* Use: parm, arg, next_sm_inst, to map from args in caller
 	 to parms in callee */
@@ -2016,9 +2028,12 @@ engine::restore_after_call (sm_instance sm_inst_in_callee,
   if (lhs)
     {
       path_state ps; // FIXME
+      int callee_stack_depth = b.m_path.m_stack_depth;
+      int caller_stack_depth = callee_stack_depth - 1;
       on_assignment (rest_of_caller_node, call_stmt,
 		     &sm_inst_in_caller, &sm_inst_in_callee /* FIXME.  */,
-		     lhs, result, b, &ps);
+		     path_var (lhs, caller_stack_depth),
+		     path_var (result, callee_stack_depth), b, &ps);
     }
 
   // TODO: map formal params back to actual args:
@@ -2726,7 +2741,7 @@ engine::expand_path (const checker_path &in_path,
 
 void
 engine::prune_path (checker_path *path,
-		    tree var, state_machine::state_t state)
+		    tree var, state_machine::state_t state ATTRIBUTE_UNUSED)
 {
   JIT_LOG_FUNC (get_logger ());
   path->maybe_log (get_logger (), "path");
@@ -2893,8 +2908,7 @@ engine::is_valid_path (const checker_path &path)
 	  {
 	    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))
+						edge_ev->m_edge->m_src->get_last_stmt ()))
 	      {
 		log ("  constraints for edge aren't satisfiable");
 		return false;
-- 
1.8.5.3

