From f41fb97f13a6af56707cc93ff221d0dfd41a89e5 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 2 Jul 2018 15:36:02 -0400
Subject: [PATCH 13/56] FIXME: record per-pass function state in optimization
 records

---
 gcc/gimple-pretty-print.c |   2 +-
 gcc/gimple-pretty-print.h |   2 +
 gcc/optinfo-emit-json.cc  | 166 ++++++++++++++++++++++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h   |   2 +
 gcc/passes.c              |  17 +++++
 5 files changed, 188 insertions(+), 1 deletion(-)

diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 19cdb40..2554950 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2793,7 +2793,7 @@ dump_implicit_edges (pretty_printer *buffer, basic_block bb, int indent,
 /* Dumps basic block BB to buffer BUFFER with details described by FLAGS and
    indented by INDENT spaces.  */
 
-static void
+void
 gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
 		     dump_flags_t flags)
 {
diff --git a/gcc/gimple-pretty-print.h b/gcc/gimple-pretty-print.h
index 6ae6a3b..e7a5607 100644
--- a/gcc/gimple-pretty-print.h
+++ b/gcc/gimple-pretty-print.h
@@ -34,6 +34,8 @@ extern void print_gimple_expr (FILE *, gimple *, int, dump_flags_t = TDF_NONE);
 extern void pp_gimple_stmt_1 (pretty_printer *, gimple *, int, dump_flags_t);
 extern void gimple_dump_bb (FILE *, basic_block, int, dump_flags_t);
 extern void gimple_dump_bb_for_graph (pretty_printer *, basic_block);
+extern void gimple_dump_bb_buff (pretty_printer *buffer, basic_block bb, int indent,
+				 dump_flags_t flags);
 extern void dump_ssaname_info_to_file (FILE *, tree, int);
 extern void percent_G_format (text_info *);
 
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
index bf1172a..caeaf33 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -56,6 +56,7 @@ public:
   void write () const;
   void add_record (const optinfo *optinfo);
   void pop_scope ();
+  void record_function_state (function *fn, opt_pass *pass);
 
   void add_record (json::object *obj);
   json::object *impl_location_to_json (dump_impl_location_t loc);
@@ -66,6 +67,9 @@ public:
   json::value *inlining_chain_to_json (location_t loc);
   json::object *optinfo_to_json (const optinfo *optinfo);
   void add_pass_list (json::array *arr, opt_pass *pass);
+  void add_bb_to_json (basic_block bb, json::array *cfg_obj);
+  void add_function_state_to_json (function *fun, json::object *fn_obj);
+  json::object *function_state_to_json (function *fn, opt_pass *pass);
 
 private:
   /* The root value for the JSON file.
@@ -453,6 +457,154 @@ optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
   while (pass);
 }
 
+/* Generate a JSON object describing BB, adding it to CFG_OBJ.
+   Compare with dump_bb, dump_bb_info etc.  */
+
+void
+optrecord_json_writer::add_bb_to_json (basic_block bb, json::array *cfg_obj)
+{
+  json::object *bb_obj = new json::object ();
+  cfg_obj->append (bb_obj);
+
+  /* Basic metadata.  Compare with dump_bb_info.  */
+
+  bb_obj->set ("index", new json::number (bb->index));
+  if (bb->count.initialized_p ())
+    bb_obj->set ("count", profile_count_to_json (bb->count));
+
+  /* Flags.  */
+  static const char * const bb_bitnames[] =
+    {
+#define DEF_BASIC_BLOCK_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+      NULL
+#undef DEF_BASIC_BLOCK_FLAG
+    };
+  const unsigned n_bitnames = sizeof (bb_bitnames) / sizeof (char *);
+  json::array *bb_flags = new json::array ();
+  bb_obj->set ("flags", bb_flags);
+  for (unsigned i = 0; i < n_bitnames; i++)
+    if (bb->flags & (1 << i))
+      bb_flags->append (new json::string (bb_bitnames[i]));
+
+  bb_obj->set ("discriminator", new json::number (bb->discriminator));
+
+  if (bb->index >= NUM_FIXED_BLOCKS)
+    {
+      /* For now, just capture all of the statements as one string.  */
+      pretty_printer pp;
+      pp.buffer->stream = NULL;
+      pp.buffer->flush_p = false;
+      pp_needs_newline (&pp) = true;
+      gimple_dump_bb_buff (&pp, bb, 0, TDF_NONE);
+      bb_obj->set ("stmts", new json::string (pp_formatted_text (&pp)));
+    }
+
+  json::array *succs = new json::array ();
+  bb_obj->set ("succs", succs);
+  edge_iterator ei;
+  edge e;
+  FOR_EACH_EDGE (e, ei, bb->succs)
+    {
+      /* compare with dump_edge_info.  */
+      json::object *edge_obj = new json::object ();
+      succs->append (edge_obj);
+      edge_obj->set ("dest", new json::number (e->dest->index));
+
+      /* Edge flags.  */
+      static const char * const bitnames[] =
+	{
+#define DEF_EDGE_FLAG(NAME,IDX) #NAME ,
+#include "cfg-flags.def"
+	  NULL
+#undef DEF_EDGE_FLAG
+	};
+
+      json::array *edge_flags = new json::array ();
+      edge_obj->set ("flags", edge_flags);
+
+      gcc_assert (e->flags <= EDGE_ALL_FLAGS);
+      int flags = e->flags;
+      for (int i = 0; flags; i++)
+	if (flags & (1 << i))
+	  {
+	    flags &= ~(1 << i);
+	    edge_flags->append (new json::string (bitnames[i]));
+	  }
+    }
+}
+
+/* Populate FN_OBJ based on FUN.
+   Compare with dump_function_to_file.  */
+
+void
+optrecord_json_writer::add_function_state_to_json (function *fun,
+						   json::object *fn_obj)
+{
+  tree old_current_fndecl = current_function_decl;
+  basic_block bb;
+  tree fndecl = fun->decl;
+
+  current_function_decl = fndecl;
+
+  if (fun && fun->decl == fndecl
+      && fun->cfg
+      && basic_block_info_for_fn (fun))
+    {
+      json::array *cfg_obj = new json::array ();
+      fn_obj->set ("cfg", cfg_obj);
+
+      FOR_ALL_BB_FN (bb, fun)
+	add_bb_to_json (bb, cfg_obj);
+    }
+
+  current_function_decl = old_current_fndecl;
+}
+
+/* Add a record describing the state of FN after PASS to the queue of
+   records to be written.  */
+
+
+void
+optrecord_json_writer::record_function_state (function *fn,
+					      opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  json::object *obj = function_state_to_json (fn, pass);
+  add_record (obj);
+}
+
+/* Create a JSON object representing the state of FN after PASS.  */
+
+json::object *
+optrecord_json_writer::function_state_to_json (function *fn,
+					       opt_pass *pass)
+{
+  gcc_assert (fn);
+  gcc_assert (pass);
+
+  tree fndecl = fn->decl;
+
+  json::object *obj = new json::object ();
+  obj->set ("kind", new json::string ("state"));
+  obj->set ("pass", get_id_value_for_pass (pass));
+  const char *fnname = get_fnname_from_decl (fndecl);
+  obj->set ("function", new json::string (fnname));
+
+  if (fn->curr_properties & PROP_trees)
+    {
+      add_function_state_to_json (fn, obj);
+    }
+  else
+    {
+      // TODO?  RTL equivalent when dumping is:
+      //print_rtl_with_bb (dump_file, get_insns (), dump_flags);
+    }
+  return obj;
+}
+
 /* File-level interface to rest of compiler (to avoid exposing
    class optrecord_json_writer outside of this file).  */
 
@@ -522,6 +674,20 @@ optimization_records_maybe_pop_dump_scope ()
   the_json_writer->pop_scope ();
 }
 
+/* If optimization records were requested, then  add a record describing the
+   state of FN after PASS to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_function (function *fn,
+					    opt_pass *pass)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->record_function_state (fn, pass);
+}
+
 #if CHECKING_P
 
 namespace selftest {
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
index 3628d56..0f2bfd8 100644
--- a/gcc/optinfo-emit-json.h
+++ b/gcc/optinfo-emit-json.h
@@ -31,6 +31,8 @@ extern bool optimization_records_enabled_p ();
 
 extern void optimization_records_maybe_record_optinfo (const optinfo *);
 extern void optimization_records_maybe_pop_dump_scope ();
+extern void optimization_records_maybe_record_function (function *fun,
+							opt_pass *pass);
 
 
 #endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/passes.c b/gcc/passes.c
index 832f0b3..6cbc5f8 100644
--- a/gcc/passes.c
+++ b/gcc/passes.c
@@ -63,6 +63,8 @@ along with GCC; see the file COPYING3.  If not see
 #include "diagnostic-core.h" /* for fnotice */
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
 
 using namespace gcc;
 
@@ -1734,6 +1736,16 @@ execute_function_dump (function *fn, void *data)
     }
 }
 
+/* Helper function to perform function body dump.  */
+
+static void
+execute_optinfo_function_dump (function *fn, void *data)
+{
+  opt_pass *pass = (opt_pass *)data;
+
+  optimization_records_maybe_record_function (fn, pass);
+}
+
 /* This function is called when an internal compiler error is encountered.
    Ensure that function dump is made available before compiler is aborted.  */
 
@@ -2202,6 +2214,8 @@ execute_one_ipa_transform_pass (struct cgraph_node *node,
 
   if (dump_file)
     do_per_function (execute_function_dump, pass);
+  if (optinfo_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
   pass_fini_dump_file (pass);
 
   current_pass = NULL;
@@ -2503,6 +2517,9 @@ execute_one_pass (opt_pass *pass)
   else if (dump_file)
     do_per_function (execute_function_dump, pass);
 
+  if (optimization_records_enabled_p ())
+    do_per_function (execute_optinfo_function_dump, pass);
+
   if (!current_function_decl)
     symtab->process_new_functions ();
 
-- 
1.8.5.3

