analyzer: eliminate non-determinism in logs
authorDavid Malcolm <dmalcolm@redhat.com>
Tue, 27 Oct 2020 13:51:19 +0000 (09:51 -0400)
committerDavid Malcolm <dmalcolm@redhat.com>
Tue, 27 Oct 2020 13:51:19 +0000 (09:51 -0400)
This patch and the followup eliminate various forms of non-determinism
in the analyzer due to changing pointer values.

This patch fixes churn seen when diffing analyzer logs.  The patch
avoids embedding pointers in various places, and adds sorting when
dumping hash_set and hash_map for various analyzer types.  Doing so
requires implementing a way to sort svalue instances, and assigning UIDs
to gimple statements.

Tested both patches together via a script that runs a testcase 100 times,
and then using diff and md5sum to verify that the results are consistent
in the face of address space randomization:

FILENAME=$1
rm $FILENAME.*
for i in `seq 1 100`; do
    echo "iteration: $i"
    ./xgcc -B. -fanalyzer -c ../../src/gcc/testsuite/gcc.dg/analyzer/$FILENAME \
       --Wanalyzer-too-complex \
       -fdump-analyzer-supergraph \
       -fdump-analyzer-exploded-graph \
       -fdump-analyzer \
       -fdump-noaddr \
       -fdump-analyzer-exploded-nodes-2
    mv $FILENAME.supergraph.dot $FILENAME.$i.supergraph.dot
    mv $FILENAME.analyzer.txt $FILENAME.$i.analyzer.txt
    mv $FILENAME.supergraph-eg.dot $FILENAME.$i.supergraph-eg.dot
    mv $FILENAME.eg.txt $FILENAME.$i.eg.txt
    mv $FILENAME.eg.dot $FILENAME.$i.eg.dot
done

gcc/analyzer/ChangeLog:
* engine.cc (setjmp_record::cmp): New.
(supernode_cluster::dump_dot): Avoid embedding pointer in cluster
name.
(supernode_cluster::cmp_ptr_ptr): New.
(function_call_string_cluster::dump_dot): Avoid embedding pointer
in cluster name.  Sort m_map when dumping child clusters.
(function_call_string_cluster::cmp_ptr_ptr): New.
(root_cluster::dump_dot): Sort m_map when dumping child clusters.
* program-point.cc (function_point::cmp): New.
(function_point::cmp_ptr): New.
* program-point.h (function_point::cmp): New decl.
(function_point::cmp_ptr): New decl.
* program-state.cc (sm_state_map::print): Sort the values.  Guard
the printing of pointers with !flag_dump_noaddr.
(program_state::prune_for_point): Sort the regions.
(log_set_of_svalues): Sort the values.  Guard the printing of
pointers with !flag_dump_noaddr.
* region-model-manager.cc (log_uniq_map): Sort the values.
* region-model-reachability.cc (dump_set): New function template.
(reachable_regions::dump_to_pp): Use it.
* region-model.h (svalue::cmp_ptr): New decl.
(svalue::cmp_ptr_ptr): New decl.
(setjmp_record::cmp): New decl.
(placeholder_svalue::get_name): New accessor.
(widening_svalue::get_point): New accessor.
(compound_svalue::get_map): New accessor.
(conjured_svalue::get_stmt): New accessor.
(conjured_svalue::get_id_region): New accessor.
(region::cmp_ptrs): Rename to...
(region::cmp_ptr_ptr): ...this.
* region.cc (region::cmp_ptrs): Rename to...
(region::cmp_ptr_ptr): ...this.
* state-purge.cc
(state_purge_per_ssa_name::state_purge_per_ssa_name): Sort
m_points_needing_name when dumping.
* store.cc (concrete_binding::cmp_ptr_ptr): New.
(symbolic_binding::cmp_ptr_ptr): New.
(binding_map::cmp): New.
(get_sorted_parent_regions): Update for renaming of
region::cmp_ptrs to region::cmp_ptr_ptr.
(store::dump_to_pp): Likewise.
(store::to_json): Likewise.
(store::can_merge_p): Sort the base regions before considering
them.
* store.h (concrete_binding::cmp_ptr_ptr): New decl.
(symbolic_binding::cmp_ptr_ptr): New decl.
(binding_map::cmp): New decl.
* supergraph.cc (supergraph::supergraph): Assign UIDs to the
gimple stmts.
* svalue.cc (cmp_cst): New.
(svalue::cmp_ptr): New.
(svalue::cmp_ptr_ptr): New.

13 files changed:
gcc/analyzer/engine.cc
gcc/analyzer/program-point.cc
gcc/analyzer/program-point.h
gcc/analyzer/program-state.cc
gcc/analyzer/region-model-manager.cc
gcc/analyzer/region-model-reachability.cc
gcc/analyzer/region-model.h
gcc/analyzer/region.cc
gcc/analyzer/state-purge.cc
gcc/analyzer/store.cc
gcc/analyzer/store.h
gcc/analyzer/supergraph.cc
gcc/analyzer/svalue.cc

index be54f0256b7972581b9d482eb46058bf49dbf6be..49cd33e94dad3f9350905a6bfd2db6b011fbf5fa 100644 (file)
@@ -149,6 +149,17 @@ impl_region_model_context::on_escaped_function (tree fndecl)
   m_eg->on_escaped_function (fndecl);
 }
 
+/* struct setjmp_record.  */
+
+int
+setjmp_record::cmp (const setjmp_record &rec1, const setjmp_record &rec2)
+{
+  if (int cmp_enode = rec1.m_enode->m_index - rec2.m_enode->m_index)
+    return cmp_enode;
+  gcc_assert (&rec1 == &rec2);
+  return 0;
+}
+
 /* class setjmp_svalue : public svalue.  */
 
 /* Implementation of svalue::accept vfunc for setjmp_svalue.  */
@@ -3506,8 +3517,7 @@ public:
 
   void dump_dot (graphviz_out *gv, const dump_args_t &args) const FINAL OVERRIDE
   {
-    gv->println ("subgraph \"cluster_supernode_%p\" {",
-                (const void *)this);
+    gv->println ("subgraph \"cluster_supernode_%i\" {", m_supernode->m_index);
     gv->indent ();
     gv->println ("style=\"dashed\";");
     gv->println ("label=\"SN: %i (bb: %i; scc: %i)\";",
@@ -3529,6 +3539,17 @@ public:
     m_enodes.safe_push (en);
   }
 
+  /* Comparator for use by auto_vec<supernode_cluster *>::qsort.  */
+
+  static int cmp_ptr_ptr (const void *p1, const void *p2)
+  {
+    const supernode_cluster *c1
+      = *(const supernode_cluster * const *)p1;
+    const supernode_cluster *c2
+      = *(const supernode_cluster * const *)p2;
+    return c1->m_supernode->m_index - c2->m_supernode->m_index;
+  }
+
 private:
   const supernode *m_supernode;
   auto_vec <exploded_node *> m_enodes;
@@ -3555,7 +3576,8 @@ public:
   {
     const char *funcname = function_name (m_fun);
 
-    gv->println ("subgraph \"cluster_function_%p\" {", (const void *)this);
+    gv->println ("subgraph \"cluster_function_%s\" {",
+                IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (m_fun->decl)));
     gv->indent ();
     gv->write_indent ();
     gv->print ("label=\"call string: ");
@@ -3563,10 +3585,19 @@ public:
     gv->print (" function: %s \";", funcname);
     gv->print ("\n");
 
+    /* Dump m_map, sorting it to avoid churn when comparing dumps.  */
+    auto_vec<supernode_cluster *> child_clusters (m_map.elements ());
     for (map_t::iterator iter = m_map.begin ();
         iter != m_map.end ();
         ++iter)
-      (*iter).second->dump_dot (gv, args);
+      child_clusters.quick_push ((*iter).second);
+
+    child_clusters.qsort (supernode_cluster::cmp_ptr_ptr);
+
+    unsigned i;
+    supernode_cluster *child_cluster;
+    FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
+      child_cluster->dump_dot (gv, args);
 
     /* Terminate subgraph.  */
     gv->outdent ();
@@ -3588,6 +3619,21 @@ public:
       }
   }
 
+  /* Comparator for use by auto_vec<function_call_string_cluster *>.  */
+
+  static int cmp_ptr_ptr (const void *p1, const void *p2)
+  {
+    const function_call_string_cluster *c1
+      = *(const function_call_string_cluster * const *)p1;
+    const function_call_string_cluster *c2
+      = *(const function_call_string_cluster * const *)p2;
+    if (int cmp_names
+       = strcmp (IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c1->m_fun->decl)),
+                 IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (c2->m_fun->decl))))
+      return cmp_names;
+    return call_string::cmp (c1->m_cs, c2->m_cs);
+  }
+
 private:
   function *m_fun;
   call_string m_cs;
@@ -3680,10 +3726,18 @@ public:
     FOR_EACH_VEC_ELT (m_functionless_enodes, i, enode)
       enode->dump_dot (gv, args);
 
+    /* Dump m_map, sorting it to avoid churn when comparing dumps.  */
+    auto_vec<function_call_string_cluster *> child_clusters (m_map.elements ());
     for (map_t::iterator iter = m_map.begin ();
         iter != m_map.end ();
         ++iter)
-      (*iter).second->dump_dot (gv, args);
+      child_clusters.quick_push ((*iter).second);
+
+    child_clusters.qsort (function_call_string_cluster::cmp_ptr_ptr);
+
+    function_call_string_cluster *child_cluster;
+    FOR_EACH_VEC_ELT (child_clusters, i, child_cluster)
+      child_cluster->dump_dot (gv, args);
   }
 
   void add_node (exploded_node *en) FINAL OVERRIDE
index 0aadd73a272d444d21404f49bec58118a31df2b7..d8fde6033747849f8ece19f2eeb68a45ebfe1e16 100644 (file)
@@ -558,6 +558,33 @@ function_point::cmp_within_supernode (const function_point &point_a,
   return result;
 }
 
+/* Comparator for imposing an order on function_points.  */
+
+int
+function_point::cmp (const function_point &point_a,
+                    const function_point &point_b)
+{
+  int idx_a = point_a.m_supernode ? point_a.m_supernode->m_index : -1;
+  int idx_b = point_b.m_supernode ? point_b.m_supernode->m_index : -1;
+  if (int cmp_idx = idx_a - idx_b)
+    return cmp_idx;
+  gcc_assert (point_a.m_supernode == point_b.m_supernode);
+  if (point_a.m_supernode)
+    return cmp_within_supernode (point_a, point_b);
+  else
+    return 0;
+}
+
+/* Comparator for use by vec<function_point>::qsort.  */
+
+int
+function_point::cmp_ptr (const void *p1, const void *p2)
+{
+  const function_point *fp1 = (const function_point *)p1;
+  const function_point *fp2 = (const function_point *)p2;
+  return cmp (*fp1, *fp2);
+}
+
 /* For PK_BEFORE_STMT, go to next stmt (or to PK_AFTER_SUPERNODE).  */
 
 void
index d804621a7159a3fd72733daaa8f12edc59b618ba..2d1c3aba7cbd31e3a83d9bade223999b8a378ab8 100644 (file)
@@ -138,6 +138,9 @@ public:
                                     const function_point &point_b);
   static int cmp_within_supernode (const function_point &point_a,
                                   const function_point &point_b);
+  static int cmp (const function_point &point_a,
+                 const function_point &point_b);
+  static int cmp_ptr (const void *p1, const void *p2);
 
   /* For before_stmt, go to next stmt.  */
   void next_stmt ();
index 5bb8907e340789cd0a1e1ca64fd113fcef9f64ad..2313a662e360d084c6876298ed0bd9a4317b137d 100644 (file)
@@ -170,21 +170,29 @@ sm_state_map::print (const region_model *model,
        pp_newline (pp);
       first = false;
     }
+  auto_vec <const svalue *> keys (m_map.elements ());
   for (map_t::iterator iter = m_map.begin ();
        iter != m_map.end ();
        ++iter)
+    keys.quick_push ((*iter).first);
+  keys.qsort (svalue::cmp_ptr_ptr);
+  unsigned i;
+  const svalue *sval;
+  FOR_EACH_VEC_ELT (keys, i, sval)
     {
       if (multiline)
        pp_string (pp, "  ");
       else if (!first)
        pp_string (pp, ", ");
       first = false;
-      const svalue *sval = (*iter).first;
-      pp_pointer (pp, sval);
-      pp_string (pp, ": ");
+      if (!flag_dump_noaddr)
+       {
+         pp_pointer (pp, sval);
+         pp_string (pp, ": ");
+       }
       sval->dump_to_pp (pp, simple);
 
-      entry_t e = (*iter).second;
+      entry_t e = *const_cast <map_t &> (m_map).get (sval);
       pp_string (pp, ": ");
       e.m_state->dump_to_pp (pp);
       if (model)
@@ -916,6 +924,7 @@ program_state::prune_for_point (exploded_graph &eg,
       auto_vec<const decl_region *> ssa_name_regs;
       new_state.m_region_model->get_ssa_name_regions_for_current_frame
        (&ssa_name_regs);
+      ssa_name_regs.qsort (region::cmp_ptr_ptr);
       unsigned i;
       const decl_region *reg;
       FOR_EACH_VEC_ELT (ssa_name_regs, i, reg)
@@ -1032,18 +1041,26 @@ program_state::validate (const extrinsic_state &ext_state) const
 
 static void
 log_set_of_svalues (logger *logger, const char *name,
-                    const svalue_set &set)
+                   const svalue_set &set)
 {
   logger->log (name);
   logger->inc_indent ();
+  auto_vec<const svalue *> sval_vecs (set.elements ());
   for (svalue_set::iterator iter = set.begin ();
        iter != set.end (); ++iter)
+    sval_vecs.quick_push (*iter);
+  sval_vecs.qsort (svalue::cmp_ptr_ptr);
+  unsigned i;
+  const svalue *sval;
+  FOR_EACH_VEC_ELT (sval_vecs, i, sval)
     {
       logger->start_log_line ();
       pretty_printer *pp = logger->get_printer ();
-      const svalue *sval = (*iter);
-      pp_pointer (pp, sval);
-      pp_string (pp, ": ");
+      if (!flag_dump_noaddr)
+       {
+         pp_pointer (pp, sval);
+         pp_string (pp, ": ");
+       }
       sval->dump_to_pp (pp, false);
       logger->end_log_line ();
     }
index 8dd3ad0020a0a7698a5d8057893a66f6c02c4831..f7fa33b028c37cfa8eccb095c1a55158e86bc40a 100644 (file)
@@ -1033,13 +1033,19 @@ log_uniq_map (logger *logger, bool show_objs, const char *title,
              const hash_map<K, T*> &uniq_map)
 {
   logger->log ("  # %s: %li", title, uniq_map.elements ());
-  if (show_objs)
-    for (typename hash_map<K, T*>::iterator iter = uniq_map.begin ();
-        iter != uniq_map.end (); ++iter)
-      {
-       T *managed_obj = (*iter).second;
-       log_managed_object<T> (logger, managed_obj);
-      }
+  if (!show_objs)
+    return;
+  auto_vec<const T *> vec_objs (uniq_map.elements ());
+  for (typename hash_map<K, T*>::iterator iter = uniq_map.begin ();
+       iter != uniq_map.end (); ++iter)
+    vec_objs.quick_push ((*iter).second);
+
+  vec_objs.qsort (T::cmp_ptr_ptr);
+
+  unsigned i;
+  const T *obj;
+  FOR_EACH_VEC_ELT (vec_objs, i, obj)
+    log_managed_object<T> (logger, obj);
 }
 
 /* Dump the number of objects that were managed by MAP to LOGGER.
@@ -1051,13 +1057,20 @@ log_uniq_map (logger *logger, bool show_objs, const char *title,
              const consolidation_map<T> &map)
 {
   logger->log ("  # %s: %li", title, map.elements ());
-  if (show_objs)
-    for (typename consolidation_map<T>::iterator iter = map.begin ();
-        iter != map.end (); ++iter)
-      {
-       T *managed_obj = (*iter).second;
-       log_managed_object<T> (logger, managed_obj);
-      }
+  if (!show_objs)
+    return;
+
+  auto_vec<const T *> vec_objs (map.elements ());
+  for (typename consolidation_map<T>::iterator iter = map.begin ();
+       iter != map.end (); ++iter)
+    vec_objs.quick_push ((*iter).second);
+
+  vec_objs.qsort (T::cmp_ptr_ptr);
+
+  unsigned i;
+  const T *obj;
+  FOR_EACH_VEC_ELT (vec_objs, i, obj)
+    log_managed_object<T> (logger, obj);
 }
 
 /* Dump the number of objects of each class that were managed by this
index 3a6b312a8dfca88dbbdd8b915af805c54ea56fb6..52525e1144be6342d48870faf7a47783cca5010b 100644 (file)
@@ -227,6 +227,29 @@ reachable_regions::mark_escaped_clusters (region_model_context *ctxt)
     }
 }
 
+/* Dump SET to PP, sorting it to avoid churn when comparing dumps.  */
+
+template <typename T>
+static void
+dump_set (const hash_set<const T *> &set, pretty_printer *pp)
+{
+  auto_vec<const T *> elements (set.elements ());
+  for (typename hash_set<const T *>::iterator iter = set.begin ();
+       iter != set.end (); ++iter)
+    elements.quick_push (*iter);
+
+  elements.qsort (T::cmp_ptr_ptr);
+
+  unsigned i;
+  const T *element;
+  FOR_EACH_VEC_ELT (elements, i, element)
+    {
+      pp_string (pp, "  ");
+      element->dump_to_pp (pp, true);
+      pp_newline (pp);
+    }
+}
+
 /* Dump a multiline representation of this object to PP.  */
 
 void
@@ -234,40 +257,19 @@ reachable_regions::dump_to_pp (pretty_printer *pp) const
 {
   pp_string (pp, "reachable clusters: ");
   pp_newline (pp);
-  for (hash_set<const region *>::iterator iter = m_reachable_base_regs.begin ();
-       iter != m_reachable_base_regs.end (); ++iter)
-    {
-      pp_string (pp, "  ");
-      (*iter)->dump_to_pp (pp, true);
-      pp_newline (pp);
-    }
+  dump_set (m_reachable_base_regs, pp);
+
   pp_string (pp, "mutable clusters: ");
   pp_newline (pp);
-  for (hash_set<const region *>::iterator iter = m_mutable_base_regs.begin ();
-       iter != m_mutable_base_regs.end (); ++iter)
-    {
-      pp_string (pp, "  ");
-      (*iter)->dump_to_pp (pp, true);
-      pp_newline (pp);
-    }
+  dump_set (m_mutable_base_regs, pp);
+
   pp_string (pp, "reachable svals: ");
   pp_newline (pp);
-  for (svalue_set::iterator iter = m_reachable_svals.begin ();
-       iter != m_reachable_svals.end (); ++iter)
-    {
-      pp_string (pp, "  ");
-      (*iter)->dump_to_pp (pp, true);
-      pp_newline (pp);
-    }
+  dump_set (m_reachable_svals, pp);
+
   pp_string (pp, "mutable svals: ");
   pp_newline (pp);
-  for (svalue_set::iterator iter = m_mutable_svals.begin ();
-       iter != m_mutable_svals.end (); ++iter)
-    {
-      pp_string (pp, "  ");
-      (*iter)->dump_to_pp (pp, true);
-      pp_newline (pp);
-    }
+  dump_set (m_mutable_svals, pp);
 }
 
 /* Dump a multiline representation of this object to stderr.  */
index 3298d05ffdaa8d3b1c2b02b21524c5ff79e18931..a67d6f4336f14301dfc9de4db180380f71071150 100644 (file)
@@ -300,6 +300,9 @@ public:
   virtual bool implicitly_live_p (const svalue_set &live_svalues,
                                  const region_model *model) const;
 
+  static int cmp_ptr (const svalue *, const svalue *);
+  static int cmp_ptr_ptr (const void *, const void *);
+
  protected:
   svalue (complexity c, tree type)
   : m_complexity (c), m_type (type)
@@ -552,6 +555,8 @@ struct setjmp_record
     hstate->add_ptr (m_setjmp_call);
   }
 
+  static int cmp (const setjmp_record &rec1, const setjmp_record &rec2);
+
   const exploded_node *m_enode;
   const gcall *m_setjmp_call;
 };
@@ -987,6 +992,8 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   void accept (visitor *v) const FINAL OVERRIDE;
 
+  const char *get_name () const { return m_name; }
+
  private:
   const char *m_name;
 };
@@ -1078,6 +1085,7 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   void accept (visitor *v) const FINAL OVERRIDE;
 
+  const function_point &get_point () const { return m_point; }
   const svalue *get_base_svalue () const { return m_base_sval; }
   const svalue *get_iter_svalue () const { return m_iter_sval; }
 
@@ -1172,6 +1180,8 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   void accept (visitor *v) const FINAL OVERRIDE;
 
+  const binding_map &get_map () const { return m_map; }
+
   iterator_t begin () const { return m_map.begin (); }
   iterator_t end () const { return m_map.end (); }
 
@@ -1280,6 +1290,9 @@ public:
   void dump_to_pp (pretty_printer *pp, bool simple) const FINAL OVERRIDE;
   void accept (visitor *v) const FINAL OVERRIDE;
 
+  const gimple *get_stmt () const { return m_stmt; }
+  const region *get_id_region () const { return m_id_reg; }
+
  private:
   const gimple *m_stmt;
   const region *m_id_reg;
@@ -1418,7 +1431,7 @@ public:
 
   bool non_null_p () const;
 
-  static int cmp_ptrs (const void *, const void *);
+  static int cmp_ptr_ptr (const void *, const void *);
 
   region_offset get_offset () const;
   bool get_byte_size (byte_size_t *out) const;
index adf0e2c3ce35bbde99c9a8f73ab5b4b23ce9af4c..3a88a5fbc67de0c0ac7ef1bf32f3e70f65788a50 100644 (file)
@@ -517,10 +517,11 @@ region::region (complexity c, unsigned id, const region *parent, tree type)
   gcc_assert (type == NULL_TREE || TYPE_P (type));
 }
 
-/* Comparator for regions, using their IDs to order them.  */
+/* Comparator for use by vec<const region *>::qsort,
+   using their IDs to order them.  */
 
 int
-region::cmp_ptrs (const void *p1, const void *p2)
+region::cmp_ptr_ptr (const void *p1, const void *p2)
 {
   const region * const *reg1 = (const region * const *)p1;
   const region * const *reg2 = (const region * const *)p2;
index e4942a692fa145de8a12cc4de93453bbb703e010..418d80c608b188cce33276a36f707ec8b232161f 100644 (file)
@@ -209,13 +209,21 @@ state_purge_per_ssa_name::state_purge_per_ssa_name (const state_purge_map &map,
   if (map.get_logger ())
     {
       map.log ("%qE in %qD is needed to process:", name, fun->decl);
+      /* Log m_points_needing_name, sorting it to avoid churn when comparing
+        dumps.  */
+      auto_vec<function_point> points;
       for (point_set_t::iterator iter = m_points_needing_name.begin ();
           iter != m_points_needing_name.end ();
           ++iter)
+       points.safe_push (*iter);
+      points.qsort (function_point::cmp_ptr);
+      unsigned i;
+      function_point *point;
+      FOR_EACH_VEC_ELT (points, i, point)
        {
          map.start_log_line ();
          map.get_logger ()->log_partial ("  point: ");
-         (*iter).print (map.get_logger ()->get_printer (), format (false));
+         point->print (map.get_logger ()->get_printer (), format (false));
          map.end_log_line ();
        }
     }
index 7e91addd035f8163f8dbc0e5543334cc31e37b1c..05dd47d94a13f0b161f1602627af860ce4838357 100644 (file)
@@ -208,6 +208,23 @@ concrete_binding::overlaps_p (const concrete_binding &other) const
   return false;
 }
 
+/* Comparator for use by vec<const concrete_binding *>::qsort.  */
+
+int
+concrete_binding::cmp_ptr_ptr (const void *p1, const void *p2)
+{
+  const concrete_binding *b1 = *(const concrete_binding * const *)p1;
+  const concrete_binding *b2 = *(const concrete_binding * const *)p2;
+
+  if (int kind_cmp = b1->get_kind () - b2->get_kind ())
+    return kind_cmp;
+
+  if (int start_cmp = wi::cmps (b1->m_start_bit_offset, b2->m_start_bit_offset))
+    return start_cmp;
+
+  return wi::cmpu (b1->m_size_in_bits, b2->m_size_in_bits);
+}
+
 /* class symbolic_binding : public binding_key.  */
 
 void
@@ -218,6 +235,20 @@ symbolic_binding::dump_to_pp (pretty_printer *pp, bool simple) const
   m_region->dump_to_pp (pp, simple);
 }
 
+/* Comparator for use by vec<const symbolic_binding *>::qsort.  */
+
+int
+symbolic_binding::cmp_ptr_ptr (const void *p1, const void *p2)
+{
+  const symbolic_binding *b1 = *(const symbolic_binding * const *)p1;
+  const symbolic_binding *b2 = *(const symbolic_binding * const *)p2;
+
+  if (int kind_cmp = b1->get_kind () - b2->get_kind ())
+    return kind_cmp;
+
+  return region::cmp_ids (b1->get_region (), b2->get_region ());
+}
+
 /* The store is oblivious to the types of the svalues bound within
    it: any type can get bound at any location.
    Simplify any casts before binding.
@@ -409,6 +440,40 @@ binding_map::to_json () const
   return map_obj;
 }
 
+/* Comparator for imposing an order on binding_maps.  */
+
+int
+binding_map::cmp (const binding_map &map1, const binding_map &map2)
+{
+  if (int count_cmp = map1.elements () - map2.elements ())
+    return count_cmp;
+
+  auto_vec <const binding_key *> keys1 (map1.elements ());
+  for (map_t::iterator iter = map1.begin ();
+       iter != map1.end (); ++iter)
+    keys1.quick_push ((*iter).first);
+  keys1.qsort (binding_key::cmp_ptrs);
+
+  auto_vec <const binding_key *> keys2 (map2.elements ());
+  for (map_t::iterator iter = map2.begin ();
+       iter != map2.end (); ++iter)
+    keys2.quick_push ((*iter).first);
+  keys2.qsort (binding_key::cmp_ptrs);
+
+  for (size_t i = 0; i < keys1.length (); i++)
+    {
+      const binding_key *k1 = keys1[i];
+      const binding_key *k2 = keys2[i];
+      if (int key_cmp = binding_key::cmp (k1, k2))
+       return key_cmp;
+      gcc_assert (k1 == k2);
+      if (int sval_cmp = svalue::cmp_ptr (map1.get (k1), map2.get (k2)))
+       return sval_cmp;
+    }
+
+  return 0;
+}
+
 /* Get the child region of PARENT_REG based upon INDEX within a
    CONSTRUCTOR.   */
 
@@ -1512,7 +1577,7 @@ get_sorted_parent_regions (auto_vec<const region *> *out,
     out->safe_push (*iter);
 
   /* Sort OUT.  */
-  out->qsort (region::cmp_ptrs);
+  out->qsort (region::cmp_ptr_ptr);
 }
 
 /* Dump a representation of this store to PP, using SIMPLE to control how
@@ -1532,7 +1597,7 @@ store::dump_to_pp (pretty_printer *pp, bool simple, bool multiline,
       const region *base_reg = (*iter).first;
       base_regions.safe_push (base_reg);
     }
-  base_regions.qsort (region::cmp_ptrs);
+  base_regions.qsort (region::cmp_ptr_ptr);
 
   /* Gather clusters, organize by parent region, so that we can group
      together locals, globals, etc.  */
@@ -1653,7 +1718,7 @@ store::to_json () const
       const region *base_reg = (*iter).first;
       base_regions.safe_push (base_reg);
     }
-  base_regions.qsort (region::cmp_ptrs);
+  base_regions.qsort (region::cmp_ptr_ptr);
 
   /* Gather clusters, organize by parent region, so that we can group
      together locals, globals, etc.  */
@@ -1991,10 +2056,19 @@ store::can_merge_p (const store *store_a, const store *store_b,
       base_regions.add (base_reg_b);
     }
 
+  /* Sort the base regions before considering them.  This ought not to
+     affect the results, but can affect which types UNKNOWN_REGIONs are
+     created for in a run; sorting them thus avoids minor differences
+     in logfiles.  */
+  auto_vec<const region *> vec_base_regions (base_regions.elements ());
   for (hash_set<const region *>::iterator iter = base_regions.begin ();
        iter != base_regions.end (); ++iter)
+    vec_base_regions.quick_push (*iter);
+  vec_base_regions.qsort (region::cmp_ptr_ptr);
+  unsigned i;
+  const region *base_reg;
+  FOR_EACH_VEC_ELT (vec_base_regions, i, base_reg)
     {
-      const region *base_reg = *iter;
       const binding_cluster *cluster_a = store_a->get_cluster (base_reg);
       const binding_cluster *cluster_b = store_b->get_cluster (base_reg);
       /* At least one of cluster_a and cluster_b must be non-NULL.  */
index 0f4e7ab2a56f4755481893ac8deb9e0f3966ecf1..466c1830c0f361a89635801494f031841b3476b6 100644 (file)
@@ -237,6 +237,8 @@ public:
 
   bool overlaps_p (const concrete_binding &other) const;
 
+  static int cmp_ptr_ptr (const void *, const void *);
+
 private:
   bit_offset_t m_start_bit_offset;
   bit_size_t m_size_in_bits;
@@ -282,6 +284,8 @@ public:
 
   const region *get_region () const { return m_region; }
 
+  static int cmp_ptr_ptr (const void *, const void *);
+
 private:
   const region *m_region;
 };
@@ -346,6 +350,8 @@ public:
   bool apply_ctor_to_region (const region *parent_reg, tree ctor,
                             region_model_manager *mgr);
 
+  static int cmp (const binding_map &map1, const binding_map &map2);
+
 private:
   bool apply_ctor_val_to_range (const region *parent_reg,
                                region_model_manager *mgr,
index 735c4a30e09a4fe90f476ed98c0db852b7193d4b..37f143c6fca007aa4ba604837531f55619a908ec 100644 (file)
@@ -90,7 +90,8 @@ supergraph_call_edge (function *fun, gimple *stmt)
 /* supergraph's ctor.  Walk the callgraph, building supernodes for each
    CFG basic block, splitting the basic blocks at callsites.  Join
    together the supernodes with interprocedural and intraprocedural
-   superedges as appropriate.  */
+   superedges as appropriate.
+   Assign UIDs to the gimple stmts.  */
 
 supergraph::supergraph (logger *logger)
 {
@@ -98,8 +99,10 @@ supergraph::supergraph (logger *logger)
 
   LOG_FUNC (logger);
 
-  /* First pass: make supernodes.  */
+  /* First pass: make supernodes (and assign UIDs to the gimple stmts).  */
   {
+    unsigned next_uid = 0;
+
     /* Sort the cgraph_nodes?  */
     cgraph_node *node;
     FOR_EACH_FUNCTION_WITH_GIMPLE_BODY (node)
@@ -124,6 +127,7 @@ supergraph::supergraph (logger *logger)
            {
              gimple *stmt = gsi_stmt (gpi);
              m_stmt_to_node_t.put (stmt, node_for_stmts);
+             stmt->uid = next_uid++;
            }
 
          /* Append statements from BB to the current supernode, splitting
@@ -135,6 +139,7 @@ supergraph::supergraph (logger *logger)
              gimple *stmt = gsi_stmt (gsi);
              node_for_stmts->m_stmts.safe_push (stmt);
              m_stmt_to_node_t.put (stmt, node_for_stmts);
+             stmt->uid = next_uid++;
              if (cgraph_edge *edge = supergraph_call_edge (fun, stmt))
                {
                  m_cgraph_edge_to_caller_prev_node.put(edge, node_for_stmts);
index ae3b6783e9cd1ee574f83fb64f2047bc4ad6e321..8eece134e109c98e140711c8634c13484bd967d4 100644 (file)
@@ -298,6 +298,211 @@ svalue::implicitly_live_p (const svalue_set &, const region_model *) const
   return false;
 }
 
+/* Comparator for imposing a deterministic order on constants that are
+   of the same type.  */
+
+static int
+cmp_cst (const_tree cst1, const_tree cst2)
+{
+  gcc_assert (TREE_TYPE (cst1) == TREE_TYPE (cst2));
+  gcc_assert (TREE_CODE (cst1) == TREE_CODE (cst2));
+  switch (TREE_CODE (cst1))
+    {
+    default:
+      gcc_unreachable ();
+    case INTEGER_CST:
+      return tree_int_cst_compare (cst1, cst2);
+    case STRING_CST:
+      return strcmp (TREE_STRING_POINTER (cst1),
+                    TREE_STRING_POINTER (cst2));
+    case REAL_CST:
+      /* Impose an arbitrary but deterministic order.  */
+      return memcmp (TREE_REAL_CST_PTR (cst1),
+                    TREE_REAL_CST_PTR (cst2),
+                    sizeof (real_value));
+    case VECTOR_CST:
+      if (int cmp_log2_npatterns
+           = ((int)VECTOR_CST_LOG2_NPATTERNS (cst1)
+              - (int)VECTOR_CST_LOG2_NPATTERNS (cst2)))
+       return cmp_log2_npatterns;
+      if (int cmp_nelts_per_pattern
+           = ((int)VECTOR_CST_NELTS_PER_PATTERN (cst1)
+              - (int)VECTOR_CST_NELTS_PER_PATTERN (cst2)))
+       return cmp_nelts_per_pattern;
+      unsigned encoded_nelts = vector_cst_encoded_nelts (cst1);
+      for (unsigned i = 0; i < encoded_nelts; i++)
+       if (int el_cmp = cmp_cst (VECTOR_CST_ENCODED_ELT (cst1, i),
+                                 VECTOR_CST_ENCODED_ELT (cst2, i)))
+         return el_cmp;
+      return 0;
+    }
+}
+
+/* Comparator for imposing a deterministic order on svalues.  */
+
+int
+svalue::cmp_ptr (const svalue *sval1, const svalue *sval2)
+{
+  if (sval1 == sval2)
+    return 0;
+  if (int cmp_kind = sval1->get_kind () - sval2->get_kind ())
+    return cmp_kind;
+  int t1 = sval1->get_type () ? TYPE_UID (sval1->get_type ()) : -1;
+  int t2 = sval2->get_type () ? TYPE_UID (sval2->get_type ()) : -1;
+  if (int cmp_type = t1 - t2)
+    return cmp_type;
+  switch (sval1->get_kind ())
+    {
+    default:
+      gcc_unreachable ();
+    case SK_REGION:
+      {
+       const region_svalue *region_sval1 = (const region_svalue *)sval1;
+       const region_svalue *region_sval2 = (const region_svalue *)sval2;
+       return region::cmp_ids (region_sval1->get_pointee (),
+                               region_sval2->get_pointee ());
+      }
+      break;
+    case SK_CONSTANT:
+      {
+       const constant_svalue *constant_sval1 = (const constant_svalue *)sval1;
+       const constant_svalue *constant_sval2 = (const constant_svalue *)sval2;
+       const_tree cst1 = constant_sval1->get_constant ();
+       const_tree cst2 = constant_sval2->get_constant ();
+       return cmp_cst (cst1, cst2);
+      }
+      break;
+    case SK_UNKNOWN:
+      {
+       gcc_assert (sval1 == sval2);
+       return 0;
+      }
+      break;
+    case SK_POISONED:
+      {
+       const poisoned_svalue *poisoned_sval1 = (const poisoned_svalue *)sval1;
+       const poisoned_svalue *poisoned_sval2 = (const poisoned_svalue *)sval2;
+       return (poisoned_sval1->get_poison_kind ()
+               - poisoned_sval2->get_poison_kind ());
+      }
+      break;
+    case SK_SETJMP:
+      {
+       const setjmp_svalue *setjmp_sval1 = (const setjmp_svalue *)sval1;
+       const setjmp_svalue *setjmp_sval2 = (const setjmp_svalue *)sval2;
+       const setjmp_record &rec1 = setjmp_sval1->get_setjmp_record ();
+       const setjmp_record &rec2 = setjmp_sval2->get_setjmp_record ();
+       return setjmp_record::cmp (rec1, rec2);
+      }
+      break;
+    case SK_INITIAL:
+      {
+       const initial_svalue *initial_sval1 = (const initial_svalue *)sval1;
+       const initial_svalue *initial_sval2 = (const initial_svalue *)sval2;
+       return region::cmp_ids (initial_sval1->get_region (),
+                               initial_sval2->get_region ());
+      }
+      break;
+    case SK_UNARYOP:
+      {
+       const unaryop_svalue *unaryop_sval1 = (const unaryop_svalue *)sval1;
+       const unaryop_svalue *unaryop_sval2 = (const unaryop_svalue *)sval2;
+       if (int op_cmp = unaryop_sval1->get_op () - unaryop_sval2->get_op ())
+         return op_cmp;
+       return svalue::cmp_ptr (unaryop_sval1->get_arg (),
+                               unaryop_sval2->get_arg ());
+      }
+      break;
+    case SK_BINOP:
+      {
+       const binop_svalue *binop_sval1 = (const binop_svalue *)sval1;
+       const binop_svalue *binop_sval2 = (const binop_svalue *)sval2;
+       if (int op_cmp = binop_sval1->get_op () - binop_sval2->get_op ())
+         return op_cmp;
+       if (int arg0_cmp = svalue::cmp_ptr (binop_sval1->get_arg0 (),
+                                           binop_sval2->get_arg0 ()))
+         return arg0_cmp;
+       return svalue::cmp_ptr (binop_sval1->get_arg1 (),
+                               binop_sval2->get_arg1 ());
+      }
+      break;
+    case SK_SUB:
+      {
+       const sub_svalue *sub_sval1 = (const sub_svalue *)sval1;
+       const sub_svalue *sub_sval2 = (const sub_svalue *)sval2;
+       if (int parent_cmp = svalue::cmp_ptr (sub_sval1->get_parent (),
+                                             sub_sval2->get_parent ()))
+         return parent_cmp;
+       return region::cmp_ids (sub_sval1->get_subregion (),
+                               sub_sval2->get_subregion ());
+      }
+      break;
+    case SK_UNMERGEABLE:
+      {
+       const unmergeable_svalue *unmergeable_sval1
+         = (const unmergeable_svalue *)sval1;
+       const unmergeable_svalue *unmergeable_sval2
+         = (const unmergeable_svalue *)sval2;
+       return svalue::cmp_ptr (unmergeable_sval1->get_arg (),
+                               unmergeable_sval2->get_arg ());
+      }
+      break;
+    case SK_PLACEHOLDER:
+      {
+       const placeholder_svalue *placeholder_sval1
+         = (const placeholder_svalue *)sval1;
+       const placeholder_svalue *placeholder_sval2
+         = (const placeholder_svalue *)sval2;
+       return strcmp (placeholder_sval1->get_name (),
+                      placeholder_sval2->get_name ());
+      }
+      break;
+    case SK_WIDENING:
+      {
+       const widening_svalue *widening_sval1 = (const widening_svalue *)sval1;
+       const widening_svalue *widening_sval2 = (const widening_svalue *)sval2;
+       if (int point_cmp = function_point::cmp (widening_sval1->get_point (),
+                                                widening_sval2->get_point ()))
+         return point_cmp;
+       if (int base_cmp = svalue::cmp_ptr (widening_sval1->get_base_svalue (),
+                                           widening_sval2->get_base_svalue ()))
+         return base_cmp;
+       return svalue::cmp_ptr (widening_sval1->get_iter_svalue (),
+                               widening_sval2->get_iter_svalue ());
+      }
+      break;
+    case SK_COMPOUND:
+      {
+       const compound_svalue *compound_sval1 = (const compound_svalue *)sval1;
+       const compound_svalue *compound_sval2 = (const compound_svalue *)sval2;
+       return binding_map::cmp (compound_sval1->get_map (),
+                                compound_sval2->get_map ());
+      }
+      break;
+    case SK_CONJURED:
+      {
+       const conjured_svalue *conjured_sval1 = (const conjured_svalue *)sval1;
+       const conjured_svalue *conjured_sval2 = (const conjured_svalue *)sval2;
+       if (int stmt_cmp = (conjured_sval1->get_stmt ()->uid
+                           - conjured_sval2->get_stmt ()->uid))
+         return stmt_cmp;
+       return region::cmp_ids (conjured_sval1->get_id_region (),
+                               conjured_sval2->get_id_region ());
+      }
+      break;
+    }
+}
+
+/* Comparator for use by vec<const svalue *>::qsort.  */
+
+int
+svalue::cmp_ptr_ptr (const void *p1, const void *p2)
+{
+  const svalue *sval1 = *(const svalue * const *)p1;
+  const svalue *sval2 = *(const svalue * const *)p2;
+  return cmp_ptr (sval1, sval2);
+}
+
 /* class region_svalue : public svalue.  */
 
 /* Implementation of svalue::dump_to_pp vfunc for region_svalue.  */