From 0df33d969fafd7456edfc2a8163d23477979c314 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Wed, 9 May 2018 20:08:02 -0400
Subject: [PATCH 24/33] FIXME: initial implementation of structured
 optimization records

---
 000-README.rst              |  76 ++++++++++++++++++
 gcc/Makefile.in             |   1 +
 gcc/optimization-records.cc | 115 +++++++++++++++++++++++++--
 gcc/optimization-records.h  |   3 +
 gcc/optinfo.cc              | 189 ++++++++++++++++++++++++++++++++++++++++++++
 gcc/optinfo.h               | 161 +++++++++++++++++++++++++++++++++++++
 gcc/tree-vect-loop.c        |  17 ++++
 gcc/tree-vectorizer.c       |   2 +-
 gcc/tree-vectorizer.h       |  17 +---
 9 files changed, 558 insertions(+), 23 deletions(-)
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h

diff --git a/000-README.rst b/000-README.rst
index 038d364..a6ef710 100644
--- a/000-README.rst
+++ b/000-README.rst
@@ -8,9 +8,85 @@ Summary
 Statement of the problem
 ========================
 
+Solution
+========
+
+Consider::
+
+  if (dump_enabled_p ())
+    {
+       dump_printf_loc (MSG_NOTE, vect_location,
+                        "Symbolic number of iterations is ");
+       dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
+       dump_printf (MSG_NOTE, "\n");
+    }
+
+This purely emits text to the destination of `-fopt-info`, e.g.::
+
+  foo.c:15:3: note: Symbolic number of iterations is (unsigned int) n_9(D)
+
+This can be rewritten as::
+
+  if (optinfo_enabled_p ())
+    {
+      emit_optinfo_at_vect_location (OPT_RECORD_REMARK)
+        << "symbolic number of iterations is "
+        << number_of_iterations;
+    }
+
+It can then be emitted in up to three ways:
+
+(a) as before via `-fopt-info`::
+
+    foo.c:15:3: note: symbolic number of iterations is (unsigned int) n_9(D)
+
+  (albeit with capitalization changes)
+
+(b) It can be emitted as a "remark" through the diagnostics subsystem::
+
+   foo.c:15:3: remark: symbolic number of iterations is ‘(unsigned int) n_9(D)’ [pass=‘vect’]
+
+(c) It is emitted in JSON form through `-fsave-optimization-record`:
+
+      {
+          "kind": "remark",
+          "count": {
+              "quality": "guessed_local",
+              "value": 9.5563e+08
+          },
+          "location": {
+              "line": 15,
+              "file": "foo.c",
+              "column": 3
+          },
+          "pass": "vect",
+          "message": [
+              "symbolic number of iterations is ",
+              {
+                  "expr": "(unsigned int) n_9(D)"
+              }
+          ],
+          "function": "compute_sum"
+      },
+
+    Note how this captures metadata such as profile count, function name,
+    pass, etc.
+
+    FIXME: should capture the location of the expressions (so we can do hyperlinks).
+
+
 Known unknowns
 ==============
 
+* Formatting conventions:
+
+  * diagnostics have lowercase start, and no trailing period
+
+  * -fopt-info dumps have uppercase start, and often have a trailing period
+
+* Should optimization info be internationalized/localized? Currently
+  `-fopt-info` isn't.  I say let's not i18n it.
+
 Notes to self
 =============
 
diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 26c0a40..40722b2 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1422,6 +1422,7 @@ OBJS = \
 	omp-grid.o \
 	omp-low.o \
 	omp-simd-clone.o \
+	optinfo.o \
 	optabs.o \
 	optabs-libfuncs.o \
 	optabs-query.o \
diff --git a/gcc/optimization-records.cc b/gcc/optimization-records.cc
index dc47549..3a35816 100644
--- a/gcc/optimization-records.cc
+++ b/gcc/optimization-records.cc
@@ -42,9 +42,12 @@ along with GCC; see the file COPYING3.  If not see
 #include "output.h"
 #include "tree-pass.h"
 
+#include "optinfo.h"
 #include "optimization-records.h"
 #include "json.h"
 #include "tree-scalar-evolution.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
 
 /* FIXME.  */
 
@@ -139,22 +142,61 @@ optimization_record_enabled_p ()
 /* FIXME.  */
 
 void
-maybe_record_optimization_va (enum opt_record_kind kind,
-			      gimple *stmt,
-			      const char *fmt, va_list *ap)
+maybe_record_optimization (const optinfo *optinfo)
 {
   /* Bail immediately if recording not enabled.  */
   if (!records)
     return;
 
-  /* Formatted output.  */
-  char *msg;
-  vasprintf (&msg, fmt, *ap);
+  gimple *stmt = optinfo->get_location ().m_stmt;//get_best_stmt ();
 
   json::object *obj = new json::object ();
 
-  obj->set ("kind", new json::string (opt_record_kind_to_string (kind)));
-  obj->set ("message", new json::string (msg));
+  const char *kind_str = opt_record_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    const optinfo_item_text *text = (const optinfo_item_text *)item;
+	    message->append (new json::string (text->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    const optinfo_item_tree *tree = (const optinfo_item_tree *)item;
+	    json::object *obj = new json::object ();
+	    /* TODO: capture output of:
+	         dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
+	       into a text buffer, and add it as a attribute of OBJ.
+	       This is:
+	         print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+	       which is:
+		 maybe_init_pretty_print (file);
+		 dump_generic_node (tree_pp, t, 0, flags, false);
+		 pp_flush (tree_pp);
+	    */
+	    pretty_printer pp;
+	    pp_needs_newline (&pp) = true;
+	    pp_translate_identifiers (&pp) = false;
+
+	    dump_generic_node (&pp, tree->get_node (), 0, TDF_DETAILS,
+			       false);
+
+	    obj->set ("expr", new json::string (pp_formatted_text (&pp)));
+
+	    message->append (obj);
+	  }
+	  break;
+	}
+   }
 
   if (current_pass)
     obj->set ("pass", new json::string (current_pass->name));
@@ -182,14 +224,71 @@ maybe_record_optimization_va (enum opt_record_kind kind,
 
   records->append (obj);
 
+  // FIXME:
+#if 1
   // Also send it through the diagnostics subsystem:
   if (1)
     {
+      pretty_printer pp;
+      pp_needs_newline (&pp) = true;
+      pp_translate_identifiers (&pp) = false;
+
+      bool show_color = true; // FIXME
+
+      for (unsigned i = 0; i < optinfo->num_items (); i++)
+	{
+	  const optinfo_item *item = optinfo->get_item (i);
+	  switch (item->get_kind ())
+	    {
+	    default:
+	      gcc_unreachable ();
+	    case OPTINFO_ITEM_KIND_TEXT:
+	      {
+		const optinfo_item_text *text = (const optinfo_item_text *)item;
+		pp_string (&pp, text->get_text ());
+	      }
+	      break;
+	    case OPTINFO_ITEM_KIND_TREE:
+	      {
+		const optinfo_item_tree *tree = (const optinfo_item_tree *)item;
+		pp_begin_quote (&pp, show_color);
+		dump_generic_node (&pp, tree->get_node (), 0, TDF_DETAILS,
+				   false);
+		pp_end_quote (&pp, show_color);
+	      }
+	      break;
+	    }
+	}
+
+      const char *msg = pp_formatted_text (&pp);
+
       if (current_pass)
 	remark (loc, 0, "%s [pass=%qs]", msg, current_pass->name);
       else
 	remark (loc, 0, "%s", msg);
     }
+#endif
+}
+
+/* FIXME.  */
+
+void
+maybe_record_optimization_va (enum opt_record_kind kind,
+			      gimple *stmt,
+			      const char *fmt, va_list *ap)
+{
+  /* Bail immediately if recording not enabled.  */
+  if (!records)
+    return;
+
+  /* Formatted output.  */
+  char *msg;
+  vasprintf (&msg, fmt, *ap);
+
+  // FIXME: need to separate the pending from the info
+  optinfo optinfo (kind, optinfo_location (stmt));
+  optinfo << msg;
+  optinfo.emit ();
 
   free (msg);
 }
diff --git a/gcc/optimization-records.h b/gcc/optimization-records.h
index b82a17f..02b6a96 100644
--- a/gcc/optimization-records.h
+++ b/gcc/optimization-records.h
@@ -22,6 +22,7 @@ along with GCC; see the file COPYING3.  If not see
 #define GCC_OPTIMIZATION_RECORDS_H
 
 struct loop;
+class optinfo;
 
 /* FIXME.  */
 
@@ -40,6 +41,8 @@ extern const char *opt_record_kind_to_string (enum opt_record_kind kind);
 // FIXME: maybe make this inline?
 extern bool optimization_record_enabled_p ();
 
+extern void maybe_record_optimization (const optinfo *);
+
 extern void maybe_record_optimization_va (enum opt_record_kind kind,
 					  gimple *stmt,
 					  const char *fmt, va_list *ap)
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..a1f136d
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,189 @@
+/* FIXME.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+/* FIXME: trim these.  */
+#include "backend.h"
+#include "rtl.h"
+#include "tree.h"
+#include "gimple.h"
+#include "cfghooks.h"
+#include "cgraph.h"
+#include "coverage.h"
+#include "diagnostic-core.h"
+#include "cfganal.h"
+#include "value-prof.h"
+#include "gimple-iterator.h"
+#include "tree-cfg.h"
+#include "dumpfile.h"
+#include "cfgloop.h"
+
+#include "profile.h"
+#include "output.h"
+#include "tree-pass.h"
+
+#include "optinfo.h"
+#include "optimization-records.h"
+#include "json.h"
+#include "tree-scalar-evolution.h"
+
+
+/* FIXME.  */
+
+void optinfo_item_text::flush () const
+{
+  // FIXME
+}
+
+/* FIXME.  */
+
+void optinfo_item_tree::flush () const
+{
+  // FIXME
+  dump_generic_expr (MSG_NOTE, TDF_DETAILS, m_node);
+}
+
+/* FIXME.  */
+
+optinfo_location::optinfo_location (struct loop *loop)
+  : m_stmt (NULL)
+{
+  if (loop)
+    {
+      m_stmt = get_loop_exit_condition (loop);
+      if (m_stmt)
+	return;
+
+      gimple_stmt_iterator si;
+      for (si = gsi_start_bb (loop->header); !gsi_end_p (si); gsi_next (&si))
+	{
+	  gimple *stmt = gsi_stmt (si);
+	  if (LOCATION_LOCUS (gimple_location (stmt)) > BUILTINS_LOCATION)
+	    {
+	      m_stmt = stmt;
+	      return;
+	    }
+	}
+    }
+}
+
+/* FIXME.  */
+
+optinfo::~optinfo ()
+{
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* FIXME.  */
+
+optinfo&
+optinfo::operator<< (const char *msg)
+{
+  m_items.safe_push (new optinfo_item_text (msg));
+  return *this;
+}
+
+/* FIXME.  */
+
+optinfo&
+optinfo::operator<< (tree node)
+{
+  m_items.safe_push (new optinfo_item_tree (node));
+  return *this;
+}
+
+/* FIXME.  */
+
+void
+optinfo::emit () const
+{
+  /* -fsave-optimization-record and remarks.  */
+  maybe_record_optimization (this);
+
+  /* -fopt-info.  */
+  if (1)
+    {
+      // FIXME: flag!
+      // FIXME: location!
+      dump_printf_loc (MSG_NOTE, UNKNOWN_LOCATION, "");
+      for (unsigned i = 0; i < num_items (); i++)
+	{
+	  const optinfo_item *item = get_item (i);
+	  switch (item->get_kind ())
+	    {
+	    default:
+	      gcc_unreachable ();
+	    case OPTINFO_ITEM_KIND_TEXT:
+	      {
+		const optinfo_item_text *text = (const optinfo_item_text *)item;
+		dump_printf (MSG_NOTE, "%s", text->get_text ());
+	      }
+	      break;
+	    case OPTINFO_ITEM_KIND_TREE:
+	      {
+		const optinfo_item_tree *tree = (const optinfo_item_tree *)item;
+		dump_generic_expr (MSG_NOTE, TDF_DETAILS, tree->get_node ());
+	      }
+	      break;
+	    }
+	}
+      dump_printf (MSG_NOTE, "\n");
+    }
+}
+
+pending_optinfo::pending_optinfo (/*optinfo_emitter *emitter, */enum opt_record_kind kind,
+				  optinfo_location loc)
+{
+  m_optinfo = new optinfo (kind, loc);
+}
+
+pending_optinfo::~pending_optinfo ()
+{
+  /* Flush the pending information.  */
+  m_optinfo->emit ();
+
+  /* Cleanup.  */
+  delete m_optinfo;
+}
+
+/* FIXME.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (const char *msg)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_text (msg));
+  return *this;
+}
+
+/* FIXME.  */
+
+pending_optinfo&
+pending_optinfo::operator<< (tree node)
+{
+  m_optinfo->m_items.safe_push (new optinfo_item_tree (node));
+  return *this;
+}
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..af04c40
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,161 @@
+/* FIXME.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_OPTINFO_H
+#define GCC_OPTINFO_H
+
+#include "optimization-records.h"
+
+/* FIXME: this is flexible, but probably inefficient.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+};
+
+/* FIXME.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+
+  // FIXME: is this used anywhere?
+  virtual void flush () const = 0;
+};
+
+/* FIXME.  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (const char *text) : m_text (text) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+  void flush () const FINAL OVERRIDE;
+
+  const char *get_text () const { return m_text; }
+
+ private:
+  const char *m_text;
+};
+
+/* FIXME.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (tree node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+  void flush () const FINAL OVERRIDE;
+
+  tree get_node () const { return m_node; }
+
+ private:
+  tree m_node;
+};
+
+#if 0
+/* FIXME.  */
+
+class optinfo_emitter
+{
+};
+#endif
+
+/* FIXME.  */
+
+class optinfo_location
+{
+ public:
+  optinfo_location () : m_stmt (NULL) /*, m_loop (NULL) */  {}
+  optinfo_location (gimple *stmt) : m_stmt (stmt) /*, m_loop (NULL) */ {}
+#if 1
+  optinfo_location (struct loop *loop); // : m_stmt (NULL), m_loop (loop) {}
+#endif
+
+#if 0
+  gimple *get_best_stmt () const;
+#endif
+
+  // This probably ought to be a GTY:
+  gimple *m_stmt;
+  // This would have to be a GTY:
+#if 0
+  struct loop *m_loop;
+#endif
+};
+
+class pending_optinfo;
+
+/* FIXME.  */
+
+class optinfo
+{
+  friend class pending_optinfo;
+ public:
+  optinfo (/*optinfo_emitter *emitter, */enum opt_record_kind kind,
+	   optinfo_location loc)
+    : /*m_emitter (emitter), */m_kind (kind), m_loc (loc), m_items () {}
+  ~optinfo ();
+
+  enum opt_record_kind get_kind () const { return m_kind; }
+  optinfo_location get_location () const { return m_loc; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  optinfo& operator<< (const char *);
+  optinfo& operator<< (tree);
+
+  void emit () const;
+
+ private:
+  /*optinfo_emitter *m_emitter;*/
+  enum opt_record_kind m_kind;
+  optinfo_location m_loc;
+  auto_vec <optinfo_item *> m_items;
+};
+
+/* Emit the info when this goes out of scope.  */
+
+class pending_optinfo
+{
+ public:
+  pending_optinfo (/*optinfo_emitter *emitter, */enum opt_record_kind kind,
+		   optinfo_location loc);
+  ~pending_optinfo ();
+
+  pending_optinfo& operator<< (const char *);
+  pending_optinfo& operator<< (tree);
+
+ private:
+  optinfo *m_optinfo;
+};
+
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 55d7aaf..49b2f8a 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "vec-perm-indices.h"
 #include "tree-eh.h"
 #include "optimization-records.h"
+#include "optinfo.h"
 
 /* Loop Vectorization Pass.
 
@@ -195,10 +196,12 @@ void vect_location_writeln (enum opt_record_kind kind,
 
   if (optimization_record_enabled_p ())
     {
+#if 0
       if (vect_location_thing.m_loop != NULL)
 	maybe_record_optimization_va (kind, vect_location_thing.m_loop,
 				      fmt, &ap);
       else
+#endif
 	maybe_record_optimization_va (kind, vect_location_thing.m_stmt,
 				      fmt, &ap);
     }
@@ -1707,6 +1710,14 @@ vect_analyze_loop_form_1 (struct loop *loop, gcond **loop_cond,
   return true;
 }
 
+/* FIXME.  */
+
+pending_optinfo
+emit_optinfo_at_vect_location (enum opt_record_kind kind)
+{
+  return pending_optinfo (/*emitter, */kind, vect_location_thing);
+}
+
 /* Analyze LOOP form and return a loop_vec_info if it is of suitable form.  */
 
 loop_vec_info
@@ -1742,10 +1753,16 @@ vect_analyze_loop_form (struct loop *loop)
     {
       if (thing_enabled_p ())
         {
+#if 1
+	  emit_optinfo_at_vect_location (OPT_RECORD_REMARK)
+	    << "symbolic number of iterations is "
+	    << number_of_iterations;
+#else
           vect_location_writeln (OPT_RECORD_REMARK,
 			   "Symbolic number of iterations is ");
 	  dump_generic_expr (MSG_NOTE, TDF_DETAILS, number_of_iterations);
           dump_printf (MSG_NOTE, "\n");
+#endif
         }
     }
 
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index 8d6efa7..abab532 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -83,7 +83,7 @@ along with GCC; see the file COPYING3.  If not see
 
 /* Loop or bb location.  */
 source_location vect_location;
-vect_location_thing_t vect_location_thing;
+optinfo_location vect_location_thing;
 
 /* Vector mapping GIMPLE stmt to stmt_vec_info. */
 vec<stmt_vec_info> stmt_vec_info_vec;
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index b10cca7..9360308 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -25,6 +25,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "tree-hash-traits.h"
 #include "target.h"
 #include "optimization-records.h"
+#include "optinfo.h"
 
 /* Used for naming of new temporaries.  */
 enum vect_var_kind {
@@ -1385,20 +1386,8 @@ vect_get_scalar_dr_size (struct data_reference *dr)
 extern source_location vect_location;
 
 /* FIXME: update this whenever vect_location changes.
-   FIXME: or merge it all together into this class.
-   FIXME: need a non-placeholder name.  */
-class vect_location_thing_t
-{
- public:
-  vect_location_thing_t () : m_stmt (NULL), m_loop (NULL) {}
-  vect_location_thing_t (gimple *stmt) : m_stmt (stmt), m_loop (NULL) {}
-  vect_location_thing_t (struct loop *loop) : m_stmt (NULL), m_loop (loop) {}
-
-  gimple *m_stmt;
-  struct loop *m_loop;
-};
-
-extern vect_location_thing_t vect_location_thing;
+   FIXME: or merge it all together into this class.  */
+extern optinfo_location vect_location_thing;
 
 extern void vect_location_writeln (enum opt_record_kind kind,
 				   const char *fmt, ...)
-- 
1.8.5.3

