From 8a431c55f62528361e970cdc641f26bcc8850595 Mon Sep 17 00:00:00 2001
From: David Malcolm <dmalcolm@redhat.com>
Date: Mon, 16 Jul 2018 11:41:52 -0400
Subject: [PATCH 21/68] WIP on formatted printing for middle-end

---
 gcc/c-family/c-format.c |  54 +++++++-
 gcc/dump-context.h      |   6 +-
 gcc/dumpfile.c          |  92 ++++++++++++-
 gcc/dumpfile.h          |  20 ++-
 gcc/optinfo.cc          | 347 +++++++++++++++++++++++++++++++++++++++++++-----
 gcc/optinfo.h           |  13 +-
 6 files changed, 482 insertions(+), 50 deletions(-)

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index a0192dd..2bb6237 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -45,6 +45,7 @@ enum format_type { printf_format_type, asm_fprintf_format_type,
 		   gcc_cdiag_format_type,
 		   gcc_cxxdiag_format_type, gcc_gfc_format_type,
 		   gcc_objc_string_format_type,
+		   gcc_dump_printf_format_type,
 		   format_type_error = -1};
 
 struct function_format_info
@@ -461,6 +462,7 @@ static const format_length_info gcc_diag_length_specs[] =
 #define gcc_tdiag_length_specs gcc_diag_length_specs
 #define gcc_cdiag_length_specs gcc_diag_length_specs
 #define gcc_cxxdiag_length_specs gcc_diag_length_specs
+#define gcc_dump_printf_length_specs gcc_diag_length_specs
 
 /* This differs from printf_length_specs only in that "Z" is not accepted.  */
 static const format_length_info scanf_length_specs[] =
@@ -550,6 +552,7 @@ static const format_flag_pair gcc_diag_flag_pairs[] =
 #define gcc_cdiag_flag_pairs gcc_diag_flag_pairs
 #define gcc_cxxdiag_flag_pairs gcc_diag_flag_pairs
 #define gcc_gfc_flag_pairs gcc_diag_flag_pairs
+#define gcc_dump_printf_flag_pairs gcc_diag_flag_pairs
 
 static const format_flag_spec gcc_diag_flag_specs[] =
 {
@@ -565,6 +568,7 @@ static const format_flag_spec gcc_diag_flag_specs[] =
 #define gcc_cdiag_flag_specs gcc_diag_flag_specs
 #define gcc_cxxdiag_flag_specs gcc_diag_flag_specs
 #define gcc_gfc_flag_specs gcc_diag_flag_specs
+#define gcc_dump_printf_flag_specs gcc_diag_flag_specs
 
 static const format_flag_spec scanf_flag_specs[] =
 {
@@ -823,6 +827,40 @@ static const format_char_info gcc_gfc_char_table[] =
   { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
+/* FIXME: this one will need adjusting.  */
+
+static const format_char_info gcc_dump_printf_char_table[] =
+{
+  /* C89 conversion specifiers.  */
+  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
+  { "s",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "pq", "cR", NULL },
+  { "p",   1, STD_C89, { T89_V,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "c",  NULL },
+
+  /* Custom conversion specifiers.  */
+
+  /* These will require a "tree" at runtime.  */
+  { "DFTV", 1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "'",   NULL },
+  { "E",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "",   NULL },
+  { "K",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
+  /* G requires a "gcall*" argument at runtime.  */
+  { "G",   1, STD_C89, { T89_G,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
+  { "v",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q#",  "",   NULL },
+
+  { "r",   1, STD_C89, { T89_C,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",    "/cR",   NULL },
+  { "<",   0, STD_C89, NOARGUMENTS, "",      "<",  NULL },
+  { ">",   0, STD_C89, NOARGUMENTS, "",      ">",  NULL },
+  { "'",   0, STD_C89, NOARGUMENTS, "",      "",   NULL },
+  { "R",   0, STD_C89, NOARGUMENTS, "",     "\\",  NULL },
+  { "m",   0, STD_C89, NOARGUMENTS, "q",     "",   NULL },
+  { "Z",   1, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",    "", &gcc_tdiag_char_table[0] },
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
+};
+
 static const format_char_info scan_char_table[] =
 {
   /* C89 conversion specifiers.  */
@@ -922,6 +960,13 @@ static const format_kind_info format_types_orig[] =
     0, 0, 0, 0, 0, 0,
     NULL, NULL
   },
+  { "gcc_dump_printf",   gcc_dump_printf_length_specs,
+    gcc_dump_printf_char_table, "q+#", NULL,
+    gcc_dump_printf_flag_specs, gcc_dump_printf_flag_pairs,
+    FMT_FLAG_ARG_CONVERT,
+    0, 0, 'p', 0, 'L', 0,
+    NULL, &integer_type_node
+  },
   { "NSString",   NULL,  NULL, NULL, NULL,
     NULL, NULL,
     FMT_FLAG_ARG_CONVERT|FMT_FLAG_PARSE_ARG_CONVERT_EXTERNAL, 0, 0, 0, 0, 0, 0,
@@ -3942,6 +3987,7 @@ init_dynamic_diag_info (void)
 	  dynamic_format_types[gcc_tdiag_format_type].length_char_specs =
 	  dynamic_format_types[gcc_cdiag_format_type].length_char_specs =
 	  dynamic_format_types[gcc_cxxdiag_format_type].length_char_specs =
+	  dynamic_format_types[gcc_dump_printf_format_type].length_char_specs =
 	  diag_ls = (format_length_info *)
 		    xmemdup (gcc_diag_length_specs,
 			     sizeof (gcc_diag_length_specs),
@@ -3968,6 +4014,8 @@ init_dynamic_diag_info (void)
     gcc_cdiag_char_table;
   dynamic_format_types[gcc_cxxdiag_format_type].conversion_specs =
     gcc_cxxdiag_char_table;
+  dynamic_format_types[gcc_dump_printf_format_type].conversion_specs =
+    gcc_dump_printf_char_table;
 }
 
 #ifdef TARGET_FORMAT_TYPES
@@ -4122,7 +4170,8 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
       || info.format_type == gcc_diag_format_type
       || info.format_type == gcc_tdiag_format_type
       || info.format_type == gcc_cdiag_format_type
-      || info.format_type == gcc_cxxdiag_format_type)
+      || info.format_type == gcc_cxxdiag_format_type
+      || info.format_type == gcc_dump_printf_format_type)
     {
       /* Our first time through, we have to make sure that our
 	 format_type data is allocated dynamically and is modifiable.  */
@@ -4144,7 +4193,8 @@ handle_format_attribute (tree *node, tree ARG_UNUSED (name), tree args,
       else if (info.format_type == gcc_diag_format_type
 	       || info.format_type == gcc_tdiag_format_type
 	       || info.format_type == gcc_cdiag_format_type
-	       || info.format_type == gcc_cxxdiag_format_type)
+	       || info.format_type == gcc_cxxdiag_format_type
+	       || info.format_type == gcc_dump_printf_format_type)
 	init_dynamic_diag_info ();
       else
 	gcc_unreachable ();
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
index f6df0b4..5f83ef5 100644
--- a/gcc/dump-context.h
+++ b/gcc/dump-context.h
@@ -22,6 +22,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_DUMP_CONTEXT_H
 #define GCC_DUMP_CONTEXT_H 1
 
+#include "dumpfile.h" // for ATTRIBUTE_GCC_DUMP_PRINTF
+
 /* A class for handling the various dump_* calls.
 
    In particular, this class has responsibility for consolidating
@@ -69,11 +71,11 @@ class dump_context
 			      tree t);
 
   void dump_printf_va (dump_flags_t dump_kind, const char *format,
-		       va_list ap) ATTRIBUTE_PRINTF (3, 0);
+		       va_list ap) ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
 
   void dump_printf_loc_va (dump_flags_t dump_kind, const dump_location_t &loc,
 			   const char *format, va_list ap)
-    ATTRIBUTE_PRINTF (4, 0);
+    ATTRIBUTE_GCC_DUMP_PRINTF (4, 0);
 
   template<unsigned int N, typename C>
   void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index cbd9168..c8d58ba 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cgraph.h"
 #include "tree-pass.h" /* for "current_pass".  */
 #include "optinfo-emit-json.h"
+#include "stringpool.h" /* for get_identifier.  */
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -600,7 +601,8 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
   dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
-/* Output a formatted message using FORMAT on appropriate dump streams.  */
+/* Output a formatted message using FORMAT on appropriate dump streams.
+   Use optinfo::add_printf_va internally. */
 
 void
 dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
@@ -610,7 +612,10 @@ dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
     {
       va_list aq;
       va_copy (aq, ap);
-      vfprintf (dump_file, format, aq);
+      optinfo info (dump_location_t::from_location_t (UNKNOWN_LOCATION),
+		    OPTINFO_KIND_NOTE, current_pass);
+      info.add_printf_va (format, &aq);
+      info.write_to_dump_file (dump_file);
       va_end (aq);
     }
 
@@ -618,16 +623,21 @@ dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
     {
       va_list aq;
       va_copy (aq, ap);
-      vfprintf (alt_dump_file, format, aq);
+      optinfo info (dump_location_t::from_location_t (UNKNOWN_LOCATION),
+		    OPTINFO_KIND_NOTE, current_pass);
+      info.add_printf_va (format, &aq);
+      info.write_to_dump_file (alt_dump_file);
       va_end (aq);
     }
 
+  // FIXME: maybe don't repeat so much work ^^^
+
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       va_list aq;
       va_copy (aq, ap);
-      info.add_printf_va (format, aq);
+      info.add_printf_va (format, &aq);
       va_end (aq);
     }
 }
@@ -1614,6 +1624,13 @@ test_capture_of_dump_calls (const line_table_case &case_)
 
   dump_location_t loc = dump_location_t::from_location_t (where);
 
+  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			       get_identifier ("test_decl"),
+			       integer_type_node);
+
+  greturn *stmt = gimple_build_return (NULL);
+  gimple_set_location (stmt, where);
+
   /* Test of dump_printf.  */
   {
     temp_dump_context tmp (true);
@@ -1626,6 +1643,70 @@ test_capture_of_dump_calls (const line_table_case &case_)
     ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
   }
 
+  /* Test of dump_printf with %T.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    ASSERT_IS_TEXT (info->get_item (0), "tree: ");
+    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
+  }
+
+  /* Test of dump_printf with %E.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf (MSG_NOTE, "gimple: %E", stmt);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    ASSERT_IS_TEXT (info->get_item (0), "gimple: ");
+    ASSERT_IS_GIMPLE (info->get_item (1), where, "return;");
+  }
+
+  /* Test of dump_printf with %G.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf (MSG_NOTE, "gimple: %G", stmt);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 2);
+    ASSERT_IS_TEXT (info->get_item (0), "gimple: ");
+    ASSERT_IS_GIMPLE (info->get_item (1), where, "return;\n");
+  }
+
+  /* dump_print_loc with multiple format codes.  This tests various
+     things:
+     - intermingling of text, format codes handled by the base pretty_printer,
+       and dump-specific format codes
+     - multiple dump-specific format codes: some consecutive, others
+       separated by text, trailing text after the final one.  */
+  {
+    temp_dump_context tmp (true);
+    dump_printf (MSG_NOTE, "before %T and %T %i consecutive %E%E after\n",
+		 integer_zero_node, test_decl, 42, stmt, stmt);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 8);
+    ASSERT_IS_TEXT (info->get_item (0), "before ");
+    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
+    ASSERT_IS_TEXT (info->get_item (2), " and ");
+    ASSERT_IS_TREE (info->get_item (3), UNKNOWN_LOCATION, "test_decl");
+    ASSERT_IS_TEXT (info->get_item (4), " 42 consecutive ");
+    ASSERT_IS_GIMPLE (info->get_item (5), where, "return;");
+    ASSERT_IS_GIMPLE (info->get_item (6), where, "return;");
+    ASSERT_IS_TEXT (info->get_item (7), " after\n");
+  }
+
   /* Tree, via dump_generic_expr.  */
   {
     temp_dump_context tmp (true);
@@ -1656,9 +1737,6 @@ test_capture_of_dump_calls (const line_table_case &case_)
 
   /* Gimple.  */
   {
-    greturn *stmt = gimple_build_return (NULL);
-    gimple_set_location (stmt, where);
-
     /* dump_gimple_stmt_loc.  */
     {
       temp_dump_context tmp (true);
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 5db5526..315f815 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -23,6 +23,19 @@ along with GCC; see the file COPYING3.  If not see
 
 #include "profile-count.h"
 
+/* An attribute for annotating formatting printing functions that use
+   the dumpfile/optinfo formatting codes.  These are the pretty_printer
+   format codes (see pretty-print.c), with additional codes for middle-end
+   specific entities (see optinfo.cc).  */
+
+#if GCC_VERSION >= 3005
+#define ATTRIBUTE_GCC_DUMP_PRINTF(m, n) \
+  __attribute__ ((__format__ (__gcc_dump_printf__, m ,n))) \
+  ATTRIBUTE_NONNULL(m)
+#else
+#define ATTRIBUTE_GCC_DUMP_PRINTF(m, n) ATTRIBUTE_NONNULL(m)
+#endif
+
 /* Different tree dump places.  When you add new tree dump places,
    extend the DUMP_FILES array in dumpfile.c.  */
 enum tree_dump_index
@@ -466,9 +479,12 @@ dump_enabled_p (void)
    to minimize the work done for the common case where dumps
    are disabled.  */
 
-extern void dump_printf (dump_flags_t, const char *, ...) ATTRIBUTE_PRINTF_2;
+extern void dump_printf (dump_flags_t, const char *, ...)
+  ATTRIBUTE_GCC_DUMP_PRINTF (2, 3);
+
 extern void dump_printf_loc (dump_flags_t, const dump_location_t &,
-			     const char *, ...) ATTRIBUTE_PRINTF_3;
+			     const char *, ...)
+  ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
 extern void dump_function (int phase, tree fn);
 extern void dump_basic_block (dump_flags_t, basic_block, int);
 extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
index 77770de..e2b869f 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -35,6 +35,289 @@ along with GCC; see the file COPYING3.  If not see
 #include "cgraph.h"
 #include "selftest.h"
 
+/* Subroutine for implementing dump format code %E, dump_gimple_expr,
+   and dump_gimple_expr_loc.  */
+
+static optinfo_item *
+make_item_for_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags)
+{
+  dump_flags |= TDF_RHS_ONLY;
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
+			xstrdup (pp_formatted_text (&pp)), true);
+  return item;
+}
+
+/* Subroutine for implementing dump format code %G and dump_gimple_stmt.  */
+
+static optinfo_item *
+make_item_for_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags)
+{
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
+  pp_newline (&pp);
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
+			xstrdup (pp_formatted_text (&pp)), true);
+  return item;
+}
+
+/* Subroutine for implementing dump format code %T and dump_generic_expr.  */
+
+static optinfo_item *
+make_item_for_tree (tree node, dump_flags_t dump_flags)
+{
+  pretty_printer pp;
+  pp_needs_newline (&pp) = true;
+  pp_translate_identifiers (&pp) = false;
+  dump_generic_node (&pp, node, 0, dump_flags, false);
+
+  location_t loc = UNKNOWN_LOCATION;
+  if (EXPR_HAS_LOCATION (node))
+    loc = EXPR_LOCATION (node);
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc,
+			xstrdup (pp_formatted_text (&pp)), true);
+  return item;
+}
+
+/* A subclass of pretty_printer for implementing optinfo::add_printf_va.
+
+   In particular, the formatted chunks are captured as optinfo_item instances,
+   thus retaining metadata about the entities being dumped (e.g. source
+   locations), rather than just as plain text.  */
+
+class optinfo_pretty_printer : public pretty_printer
+{
+public:
+  optinfo_pretty_printer ();
+
+  void extract_items_to_optinfo (optinfo *dest);
+
+private:
+  /* Information on an optinfo_item that was generated during phase 2 of
+     formatting.  */
+  struct stashed_item
+  {
+    stashed_item (const char **buffer_ptr_, optinfo_item *item_)
+      : buffer_ptr (buffer_ptr_), item (item_) {}
+    const char **buffer_ptr;
+    optinfo_item *item;
+  };
+
+  static bool format_decoder_cb (pretty_printer *pp, text_info *text,
+				 const char *spec, int /*precision*/,
+				 bool /*wide*/, bool /*set_locus*/,
+				 bool /*verbose*/, bool */*quoted*/,
+				 const char **buffer_ptr);
+
+  bool decode_format (text_info *text, const char *spec,
+		      const char **buffer_ptr);
+
+  void stash_item (const char **buffer_ptr, optinfo_item *item);
+
+  void flush_any_pending_textual_chunks (optinfo *dest);
+
+  auto_vec<stashed_item> m_stashed_items;
+};
+
+/* optinfo_pretty_printer's ctor.  */
+
+optinfo_pretty_printer::optinfo_pretty_printer ()
+: pretty_printer (), m_stashed_items ()
+{
+  pp_format_decoder (this) = format_decoder_cb;
+}
+
+/* Phase 3 of formatting; compare with pp_output_formatted_text.
+
+   Append optinfo_item instances to DEST for the various formatted chunks
+   from phases 1 and 2 (pp_format).
+
+   Some chunks may already have had their items built (during decode_format).
+   These chunks have been stashed into m_stashed_items; we append them here.
+
+   For all other purely textual chunks, they are printed into
+   buffer->formatted_obstack, and then flushed into a textual optinfo_item.
+   This consolidates multiple adjacent text chunks into a single text
+   optinfo_item.  */
+
+void
+optinfo_pretty_printer::extract_items_to_optinfo (optinfo *dest)
+{
+  gcc_assert (dest);
+
+  output_buffer *buffer = pp_buffer (this);
+  struct chunk_info *chunk_array = buffer->cur_chunk_array;
+  const char **args = chunk_array->args;
+
+  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
+  gcc_assert (buffer->line_length == 0);
+
+  unsigned stashed_item_idx = 0;
+  for (unsigned chunk = 0; args[chunk]; chunk++)
+    {
+      if (stashed_item_idx < m_stashed_items.length ()
+	  && args[chunk] == *m_stashed_items[stashed_item_idx].buffer_ptr)
+	{
+	  flush_any_pending_textual_chunks (dest);
+	  /* This chunk has a stashed item: use it.  */
+	  dest->add_item (m_stashed_items[stashed_item_idx++].item);
+	}
+      else
+	/* This chunk is purely textual.  Print it (to
+	   buffer->formatted_obstack), so that we can consolidate adjacent
+	   chunks into one textual optinfo_item.  */
+	pp_string (this, args[chunk]);
+    }
+
+  flush_any_pending_textual_chunks (dest);
+
+  /* Ensure that we consumed all of stashed_items.  */
+  gcc_assert (stashed_item_idx == m_stashed_items.length ());
+
+  /* Deallocate the chunk structure and everything after it (i.e. the
+     associated series of formatted strings).  */
+  buffer->cur_chunk_array = chunk_array->prev;
+  obstack_free (&buffer->chunk_obstack, chunk_array);
+}
+
+/* Subroutine of optinfo_pretty_printer::extract_items_to_optinfo
+   for consolidating multiple adjacent pure-text chunks into single
+   optinfo_items (in phase 3).  */
+
+void
+optinfo_pretty_printer::flush_any_pending_textual_chunks (optinfo *dest)
+{
+  gcc_assert (dest);
+  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
+
+  /* Don't add an item if the pending text is empty.  */
+  if (output_buffer_last_position_in_text (buffer) == NULL)
+    return;
+
+  char *formatted_text = xstrdup (pp_formatted_text (this));
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			formatted_text, true);
+  dest->add_item (item);
+
+  /* Clear the pending text by unwind formatted_text back to the start
+     of the buffer (without deallocating).  */
+  obstack_free (&buffer->formatted_obstack,
+		buffer->formatted_obstack.object_base);
+}
+
+/* Record that ITEM (generated in phase 2 of formatting) is to be used for
+   the chunk at BUFFER_PTR in phase 3 (by extract_items_to_optinfo).  */
+
+void
+optinfo_pretty_printer::stash_item (const char **buffer_ptr, optinfo_item *item)
+{
+  gcc_assert (buffer_ptr);
+  gcc_assert (item);
+
+  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
+}
+
+/* pp_format_decoder callback for optinfo::add_printf_va, and thus for
+   dump_printf and dump_printf_loc.
+
+   A wrapper around decode_format, for type-safety.  */
+
+bool
+optinfo_pretty_printer::format_decoder_cb (pretty_printer *pp, text_info *text,
+					   const char *spec, int /*precision*/,
+					   bool /*wide*/, bool /*set_locus*/,
+					   bool /*verbose*/, bool */*quoted*/,
+					   const char **buffer_ptr)
+{
+  optinfo_pretty_printer *opp = static_cast <optinfo_pretty_printer *> (pp);
+  return opp->decode_format (text, spec, buffer_ptr);
+}
+
+/* Format decoder for optinfo::add_printf_va, and thus for dump_printf and
+   dump_printf_loc.
+
+   Supported format codes (in addition to the standard pretty_printer ones)
+   are:
+
+   %E: gimple *:
+       Equivalent to: dump_gimple_expr (MSG_*, TDF_SLIM, stmt, 0)
+   %G: gimple *:
+       Equivalent to: dump_gimple_stmt (MSG_*, TDF_SLIM, stmt, 0)
+   %T: tree:
+       Equivalent to: dump_generic_expr (arg, TDF_SLIM).
+
+   These format codes build optinfo_item instances, thus capturing metadata
+   about the arguments being dumped, as well as the textual output.  */
+
+bool
+optinfo_pretty_printer::decode_format (text_info *text, const char *spec,
+				       const char **buffer_ptr)
+{
+  /* Various format codes that imply making an optinfo_item and stashed it
+     for later use (to capture metadata, rather than plain text).
+     We can't keep args[chunk] as NULL, as it uses NULL as a terminating
+     value. */
+  switch (*spec)
+    {
+    case 'E':
+      {
+	gimple *stmt = va_arg (*text->args_ptr, gimple *);
+
+	/* Make an item for the stmt, and stash it.  */
+	optinfo_item *item = make_item_for_gimple_expr (stmt, 0, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	pp_string (this, "");
+	return true;
+      }
+
+    case 'G':
+      {
+	gimple *stmt = va_arg (*text->args_ptr, gimple *);
+
+	/* Make an item for the stmt, and stash it.  */
+	optinfo_item *item = make_item_for_gimple_stmt (stmt, 0, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	pp_string (this, "");
+	return true;
+      }
+
+    case 'T':
+      {
+	tree t = va_arg (*text->args_ptr, tree);
+
+	/* Make an item for the tree, and stash it.  */
+	optinfo_item *item = make_item_for_tree (t, TDF_SLIM);
+
+	// FIXME: do something with "item"
+	// where to stash it?
+	//pp_string (pp, item->get_text ());
+	//delete item;
+	// maybe keep args[chunk] as NULL
+	// and stash the items for such chunks in a separate list?
+	// No, because NULL is used as a terminating value.
+	// We need to be able to record that a given chunk needs to
+	// be handled via this item
+	stash_item (buffer_ptr, item);
+	pp_string (this, "");
+
+	return true;
+      }
+
+    default:
+      return false;
+    }
+}
+
 /* optinfo_item's ctor.  */
 
 optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
@@ -112,6 +395,18 @@ optinfo::~optinfo ()
     delete item;
 }
 
+/* Implementation detail of dump_context::dump_printf_va, for writing
+   to dump_file and alt_dump_file.  */
+
+void
+optinfo::write_to_dump_file (FILE *out) const
+{
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    fprintf (out, "%s", item->get_text ());
+}
+
 /* Emit the optinfo to all of the active destinations.  */
 
 void
@@ -174,20 +469,27 @@ optinfo::add_printf (const char *format, ...)
 {
   va_list ap;
   va_start (ap, format);
-  add_printf_va (format, ap);
+  add_printf_va (format, &ap);
   va_end (ap);
 }
 
 /* Append printf-formatted text to this optinfo.  */
 
 void
-optinfo::add_printf_va (const char *format, va_list ap)
+optinfo::add_printf_va (const char *format, va_list *ap)
 {
-  char *formatted_text = xvasprintf (format, ap);
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
-			formatted_text, true);
-  m_items.safe_push (item);
+  /* Phases 1 and 2, using pp_format.  */
+  optinfo_pretty_printer pp;
+
+  text_info text;
+  text.err_no = errno;
+  text.args_ptr = ap;
+  text.format_spec = format;
+
+  pp_format (&pp, &text);
+
+  /* Phase 3.  */
+  pp.extract_items_to_optinfo (this);
 }
 
 /* Append a gimple statement to this optinfo, equivalent to
@@ -196,14 +498,7 @@ optinfo::add_printf_va (const char *format, va_list ap)
 void
 optinfo::add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags)
 {
-  pretty_printer pp;
-  pp_needs_newline (&pp) = true;
-  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
-  pp_newline (&pp);
-
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
-			xstrdup (pp_formatted_text (&pp)), true);
+  optinfo_item *item = make_item_for_gimple_stmt (stmt, spc, dump_flags);
   m_items.safe_push (item);
 }
 
@@ -213,14 +508,7 @@ optinfo::add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags)
 void
 optinfo::add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags)
 {
-  dump_flags |= TDF_RHS_ONLY;
-  pretty_printer pp;
-  pp_needs_newline (&pp) = true;
-  pp_gimple_stmt_1 (&pp, stmt, spc, dump_flags);
-
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_GIMPLE, gimple_location (stmt),
-			xstrdup (pp_formatted_text (&pp)), true);
+  optinfo_item *item = make_item_for_gimple_expr (stmt, spc, dump_flags);
   m_items.safe_push (item);
 }
 
@@ -229,18 +517,7 @@ optinfo::add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags)
 void
 optinfo::add_tree (tree node, dump_flags_t dump_flags)
 {
-  pretty_printer pp;
-  pp_needs_newline (&pp) = true;
-  pp_translate_identifiers (&pp) = false;
-  dump_generic_node (&pp, node, 0, dump_flags, false);
-
-  location_t loc = UNKNOWN_LOCATION;
-  if (EXPR_HAS_LOCATION (node))
-    loc = EXPR_LOCATION (node);
-
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_TREE, loc,
-			xstrdup (pp_formatted_text (&pp)), true);
+  optinfo_item *item = make_item_for_tree (node, dump_flags);
   m_items.safe_push (item);
 }
 
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index aed323a..9afe010 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -21,6 +21,8 @@ along with GCC; see the file COPYING3.  If not see
 #ifndef GCC_OPTINFO_H
 #define GCC_OPTINFO_H
 
+#include "dumpfile.h" // for ATTRIBUTE_GCC_DUMP_PRINTF
+
 /* An "optinfo" is a bundle of information describing part of an
    optimization, which can be emitted to zero or more of several
    destinations, such as:
@@ -122,6 +124,11 @@ class optinfo
   location_t get_location_t () const { return m_loc.get_location_t (); }
   profile_count get_count () const { return m_loc.get_count (); }
 
+  void write_to_dump_file (FILE *) const;
+
+  /* Take ownership of ITEM and add it to this optinfo.  */
+  void add_item (optinfo_item *item) { m_items.safe_push (item); }
+
  private:
   void emit ();
 
@@ -129,8 +136,10 @@ class optinfo
      dump_context.  */
   void handle_dump_file_kind (dump_flags_t);
   void add_string (const char *str);
-  void add_printf (const char *format, ...) ATTRIBUTE_PRINTF_2;
-  void add_printf_va (const char *format, va_list ap) ATTRIBUTE_PRINTF (2, 0);
+  void add_printf (const char *format, ...)
+    ATTRIBUTE_GCC_DUMP_PRINTF(2,3);
+  void add_printf_va (const char *format, va_list *ap)
+    ATTRIBUTE_GCC_DUMP_PRINTF(2,0);
   void add_gimple_stmt (gimple *stmt, int spc, dump_flags_t dump_flags);
   void add_gimple_expr (gimple *stmt, int spc, dump_flags_t dump_flags);
   void add_tree (tree node, dump_flags_t dump_flags);
-- 
1.8.5.3

