From 489b834670d2642726e00f6ae3c79e6cf3b8bd3c Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 21 May 2018 16:24:06 -0400
Subject: [PATCH 19/24] WIP on dumping IR

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

diff --git a/gcc/gimple-pretty-print.c b/gcc/gimple-pretty-print.c
index 39edaa1..9e08f40 100644
--- a/gcc/gimple-pretty-print.c
+++ b/gcc/gimple-pretty-print.c
@@ -2813,7 +2813,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 0cda963..305196b 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -406,3 +406,369 @@ optimization_records_maybe_pop_optinfo_scope ()
 
   scopes.pop ();
 }
+
+/* FIXME.  */
+// compare to dump_bb, dump_bb_info etc
+
+static void
+dump_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)
+    {
+      // FIXME, for now, just textual form:
+      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));
+#if 0
+      if (e->probability.initialized_p ())
+	edge_obj->set ("probability", );
+#endif
+
+      /* 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]));
+	  }
+    }
+}
+
+/* FIXME.  */
+// compare with dump_function_to_file
+
+static void
+dump_function_to_json (function *fun, json::object *fn_obj)
+{
+  tree arg, var, old_current_fndecl = current_function_decl;
+  struct function *dsf;
+  bool ignore_topmost_bind = false, any_var = false;
+  basic_block bb;
+  tree chain;
+#if 0
+  bool tmclone = (TREE_CODE (fndecl) == FUNCTION_DECL
+		  && decl_is_tm_clone (fndecl));
+#endif
+  //struct function *fun = DECL_STRUCT_FUNCTION (fndecl);
+  tree fndecl = fun->decl;
+
+#if 0
+  if (DECL_ATTRIBUTES (fndecl) != NULL_TREE)
+    {
+      fprintf (file, "__attribute__((");
+
+      bool first = true;
+      tree chain;
+      for (chain = DECL_ATTRIBUTES (fndecl); chain;
+	   first = false, chain = TREE_CHAIN (chain))
+	{
+	  if (!first)
+	    fprintf (file, ", ");
+
+	  tree name = get_attribute_name (chain);
+	  print_generic_expr (file, name, dump_flags);
+	  if (TREE_VALUE (chain) != NULL_TREE)
+	    {
+	      fprintf (file, " (");
+
+	      if (strstr (IDENTIFIER_POINTER (name), "no_sanitize"))
+		print_no_sanitize_attr_value (file, TREE_VALUE (chain));
+	      else
+		print_generic_expr (file, TREE_VALUE (chain), dump_flags);
+	      fprintf (file, ")");
+	    }
+	}
+
+      fprintf (file, "))\n");
+    }
+#endif
+
+  current_function_decl = fndecl;
+#if 0
+  if (flags & TDF_GIMPLE)
+    {
+      print_generic_expr (file, TREE_TYPE (TREE_TYPE (fndecl)),
+			  dump_flags | TDF_SLIM);
+      fprintf (file, " __GIMPLE ()\n%s (", function_name (fun));
+    }
+  else
+    fprintf (file, "%s %s(", function_name (fun), tmclone ? "[tm-clone] " : "");
+#endif
+
+  json::array *args = new json::array ();
+  fn_obj->set ("args", args);
+  for (tree arg = DECL_ARGUMENTS (fndecl); arg; arg = DECL_CHAIN (arg))
+    {
+      json::object *arg_obj = new json::object ();
+#if 0      
+      print_generic_expr (file, TREE_TYPE (arg), dump_flags);
+      print_generic_expr (file, arg, dump_flags);
+#endif      
+    }
+
+#if 0
+  dsf = DECL_STRUCT_FUNCTION (fndecl);
+  if (dsf && (flags & TDF_EH))
+    dump_eh_tree (file, dsf);
+#endif
+
+#if 0
+  if (flags & TDF_RAW && !gimple_has_body_p (fndecl))
+    {
+      dump_node (fndecl, TDF_SLIM | flags, file);
+      current_function_decl = old_current_fndecl;
+      return;
+    }
+#endif
+
+#if 0
+  /* When GIMPLE is lowered, the variables are no longer available in
+     BIND_EXPRs, so display them separately.  */
+  if (fun && fun->decl == fndecl && (fun->curr_properties & PROP_gimple_lcf))
+    {
+      unsigned ix;
+      ignore_topmost_bind = true;
+
+      fprintf (file, "{\n");
+      if (gimple_in_ssa_p (fun)
+	  && (flags & TDF_ALIAS))
+	{
+	  for (arg = DECL_ARGUMENTS (fndecl); arg != NULL;
+	       arg = DECL_CHAIN (arg))
+	    {
+	      tree def = ssa_default_def (fun, arg);
+	      if (def)
+		dump_default_def (file, def, 2, flags);
+	    }
+
+	  tree res = DECL_RESULT (fun->decl);
+	  if (res != NULL_TREE
+	      && DECL_BY_REFERENCE (res))
+	    {
+	      tree def = ssa_default_def (fun, res);
+	      if (def)
+		dump_default_def (file, def, 2, flags);
+	    }
+
+	  tree static_chain = fun->static_chain_decl;
+	  if (static_chain != NULL_TREE)
+	    {
+	      tree def = ssa_default_def (fun, static_chain);
+	      if (def)
+		dump_default_def (file, def, 2, flags);
+	    }
+	}
+
+      if (!vec_safe_is_empty (fun->local_decls))
+	FOR_EACH_LOCAL_DECL (fun, ix, var)
+	  {
+	    print_generic_decl (file, var, flags);
+	    fprintf (file, "\n");
+
+	    any_var = true;
+	  }
+
+      tree name;
+
+      if (gimple_in_ssa_p (cfun))
+	FOR_EACH_SSA_NAME (ix, name, cfun)
+	  {
+	    if (!SSA_NAME_VAR (name))
+	      {
+		fprintf (file, "  ");
+		print_generic_expr (file, TREE_TYPE (name), flags);
+		fprintf (file, " ");
+		print_generic_expr (file, name, flags);
+		fprintf (file, ";\n");
+
+		any_var = true;
+	      }
+	  }
+    }
+#endif
+
+  if (fun && fun->decl == fndecl
+      && fun->cfg
+      && basic_block_info_for_fn (fun))
+    {
+#if 0
+      /* If the CFG has been built, emit a CFG-based dump.  */
+      if (!ignore_topmost_bind)
+	fprintf (file, "{\n");
+
+      if (any_var && n_basic_blocks_for_fn (fun))
+	fprintf (file, "\n");
+#endif
+
+      json::array *cfg_obj = new json::array ();
+      fn_obj->set ("cfg", cfg_obj);
+
+      FOR_EACH_BB_FN (bb, fun)
+	dump_bb_to_json (bb, cfg_obj);
+    }
+  else if (fun->curr_properties & PROP_gimple_any)
+    {
+#if 0
+      /* The function is now in GIMPLE form but the CFG has not been
+	 built yet.  Emit the single sequence of GIMPLE statements
+	 that make up its body.  */
+      gimple_seq body = gimple_body (fndecl);
+
+      if (gimple_seq_first_stmt (body)
+	  && gimple_seq_first_stmt (body) == gimple_seq_last_stmt (body)
+	  && gimple_code (gimple_seq_first_stmt (body)) == GIMPLE_BIND)
+	print_gimple_seq (file, body, 0, flags);
+      else
+	{
+	  if (!ignore_topmost_bind)
+	    fprintf (file, "{\n");
+
+	  if (any_var)
+	    fprintf (file, "\n");
+
+	  print_gimple_seq (file, body, 2, flags);
+	  fprintf (file, "}\n");
+	}
+#endif
+    }
+  else
+    {
+#if 0
+      int indent;
+
+      /* Make a tree based dump.  */
+      chain = DECL_SAVED_TREE (fndecl);
+      if (chain && TREE_CODE (chain) == BIND_EXPR)
+	{
+	  if (ignore_topmost_bind)
+	    {
+	      chain = BIND_EXPR_BODY (chain);
+	      indent = 2;
+	    }
+	  else
+	    indent = 0;
+	}
+      else
+	{
+	  if (!ignore_topmost_bind)
+	    {
+	      fprintf (file, "{\n");
+	      /* No topmost bind, pretend it's ignored for later.  */
+	      ignore_topmost_bind = true;
+	    }
+	  indent = 2;
+	}
+
+      if (any_var)
+	fprintf (file, "\n");
+
+      print_generic_stmt_indented (file, chain, flags, indent);
+      if (ignore_topmost_bind)
+	fprintf (file, "}\n");
+#endif
+    }
+
+#if 0
+  if (flags & TDF_ENUMERATE_LOCALS)
+    dump_enumerated_decls (file, flags);
+  fprintf (file, "\n\n");
+#endif
+
+  current_function_decl = old_current_fndecl;
+}
+
+
+/* FIXME.  */
+
+void
+optimization_records_maybe_record_function (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", pass_to_json (pass));
+  const char *fnname = get_fnname_from_decl (fndecl);
+  obj->set ("function", new json::string (fnname));
+
+  // TODO: populate with state of the function
+
+  // compare with execute_function_dump
+  // which for gimple calls into tree-cfg.c: dump_function_to_file
+
+  if (fn->curr_properties & PROP_trees)
+    {
+      dump_function_to_json (fn, obj);
+      // FIXME:
+      //dump_function_to_file (fn->decl, dump_file, dump_flags);
+    }
+  else
+    {
+      // FIXME; RTL equivalent is:
+      //print_rtl_with_bb (dump_file, get_insns (), dump_flags);
+    }
+
+
+
+  records->append(obj);
+}
+
+
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
index f228c67..5468946 100644
--- a/gcc/optinfo-emit-json.h
+++ b/gcc/optinfo-emit-json.h
@@ -32,6 +32,8 @@ extern bool optimization_records_enabled_p ();
 
 extern void optimization_records_maybe_record_optinfo (const optinfo *);
 extern void optimization_records_maybe_pop_optinfo_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 62fd413..1a6d648 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;
 
@@ -1785,6 +1787,19 @@ 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;
+
+  if (!optimization_records_enabled_p ())
+    return;
+
+  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.  */
 
@@ -2253,6 +2268,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;
@@ -2554,6 +2571,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

