public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
  2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
@ 2018-07-27 21:47 ` David Malcolm
  2018-07-31 12:54   ` Richard Biener
  2018-07-27 21:47 ` [PATCH 4/5] c-family: clean up the data tables in c-format.c David Malcolm
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-07-27 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

With the addition of optinfo, the various dump_* calls had three parts:
- optionally print to dump_file
- optionally print to alt_dump_file
- optionally make an optinfo_item and add it to the pending optinfo,
  creating it for dump_*_loc calls.

However, this split makes it difficult to implement the formatted dumps
later in patch kit, so as enabling work towards that, this patch removes
the above split, so that all dumping within the dump_* API goes through
optinfo_item.

In order to ensure that the dumps to dump_file and alt_dump_file are
processed immediately (rather than being buffered within the pending
optinfo for consolidation), this patch introduces the idea of "immediate"
optinfo_item destinations vs "non-immediate" destinations.

The patch also adds selftest coverage of what's printed, and of scopes.

This adds two allocations per dump_* call when dumping is enabled.
I'm assuming that this isn't a problem, as dump_enabled_p is normally
false.  There are ways of optimizing it if it is an issue (by making
optinfo_item instances become temporaries that borrow the underlying
buffer), but they require nontrivial changes, so I'd prefer to leave
that for another patch kit, if it becomes necessary.

gcc/ChangeLog:
	* dump-context.h: Include "pretty-print.h".
	(dump_context::refresh_dumps_are_enabled): New decl.
	(dump_context::emit_item): New decl.
	(class dump_context): Add fields "m_test_pp" and
	"m_test_pp_flags".
	(temp_dump_context::temp_dump_context): Add param "test_pp_flags".
	(temp_dump_context::get_dumped_text): New decl.
	(class temp_dump_context): Add field "m_pp".
	* dumpfile.c (refresh_dumps_are_enabled): Convert to...
	(dump_context::refresh_dumps_are_enabled): ...and add a test for
	m_test_pp.
	(set_dump_file): Update for above change.
	(set_alt_dump_file): Likewise.
	(dump_loc): New overload, taking a pretty_printer *.
	(dump_context::dump_loc): Call end_any_optinfo.  Dump the location
	to any test pretty-printer.
	(make_item_for_dump_gimple_stmt): New function, adapted from
	optinfo::add_gimple_stmt.
	(dump_context::dump_gimple_stmt): Call it, and use the result,
	eliminating the direct usage of dump_file and alt_dump_file in
	favor of indirectly using them via emit_item.
	(make_item_for_dump_gimple_expr): New function, adapted from
	optinfo::add_gimple_expr.
	(dump_context::dump_gimple_expr): Call it, and use the result,
	eliminating the direct usage of dump_file and alt_dump_file in
	favor of indirectly using them via emit_item.
	(make_item_for_dump_generic_expr): New function, adapted from
	optinfo::add_tree.
	(dump_context::dump_generic_expr): Call it, and use the result,
	eliminating the direct usage of dump_file and alt_dump_file in
	favor of indirectly using them via emit_item.
	(make_item_for_dump_printf_va): New function, adapted from
	optinfo::add_printf_va.
	(make_item_for_dump_printf): New function.
	(dump_context::dump_printf_va): Call make_item_for_dump_printf_va,
	and use the result, eliminating the direct usage of dump_file and
	alt_dump_file in favor of indirectly using them via emit_item.
	(make_item_for_dump_dec): New function.
	(dump_context::dump_dec): Call it, and use the result,
	eliminating the direct usage of dump_file and alt_dump_file in
	favor of indirectly using them via emit_item.
	(make_item_for_dump_symtab_node): New function, adapted from
	optinfo::add_symtab_node.
	(dump_context::dump_symtab_node): Call it, and use the result,
	eliminating the direct usage of dump_file and alt_dump_file in
	favor of indirectly using them via emit_item.
	(dump_context::begin_scope): Reimplement, avoiding direct usage
	of dump_file and alt_dump_file in favor of indirectly using them
	via emit_item.
	(dump_context::emit_item): New member function.
	(temp_dump_context::temp_dump_context): Add param "test_pp_flags".
	Set up test pretty-printer on the underlying context.  Call
	refresh_dumps_are_enabled.
	(temp_dump_context::~temp_dump_context): Call
	refresh_dumps_are_enabled.
	(temp_dump_context::get_dumped_text): New member function.
	(selftest::verify_dumped_text): New function.
	(ASSERT_DUMPED_TEXT_EQ): New macro.
	(selftest::test_capture_of_dump_calls): Run all tests twice, with
	and then without optinfo enabled.  Add uses of
	ASSERT_DUMPED_TEXT_EQ to all tests.  Add test of nested scopes.
	* dumpfile.h: Update comment for the dump_* API.
	* optinfo-emit-json.cc
	(selftest::test_building_json_from_dump_calls): Update for new
	param for temp_dump_context ctor.
	* optinfo.cc (optinfo_item::optinfo_item): Remove "owned" param
	and "m_owned" field.
	(optinfo_item::~optinfo_item): Likewise.
	(optinfo::add_item): New member function.
	(optinfo::emit): Update comment.
	(optinfo::add_string): Delete.
	(optinfo::add_printf): Delete.
	(optinfo::add_printf_va): Delete.
	(optinfo::add_gimple_stmt): Delete.
	(optinfo::add_gimple_expr): Delete.
	(optinfo::add_tree): Delete.
	(optinfo::add_symtab_node): Delete.
	(optinfo::add_dec): Delete.
	* optinfo.h (class dump_context): New forward decl.
	(optinfo::add_item): New decl.
	(optinfo::add_string): Delete.
	(optinfo::add_printf): Delete.
	(optinfo::add_printf_va): Delete.
	(optinfo::add_gimple_stmt): Delete.
	(optinfo::add_gimple_expr): Delete.
	(optinfo::add_tree): Delete.
	(optinfo::add_symtab_node): Delete.
	(optinfo::add_dec): Delete.
	(optinfo::add_poly_int): Delete.
	(optinfo_item::optinfo_item): Remove "owned" param.
	(class optinfo_item): Remove field "m_owned".
---
 gcc/dump-context.h       |  16 +-
 gcc/dumpfile.c           | 620 ++++++++++++++++++++++++++++++++++-------------
 gcc/dumpfile.h           |  34 ++-
 gcc/optinfo-emit-json.cc |   2 +-
 gcc/optinfo.cc           | 135 ++---------
 gcc/optinfo.h            |  38 +--
 6 files changed, 505 insertions(+), 340 deletions(-)

diff --git a/gcc/dump-context.h b/gcc/dump-context.h
index f6df0b4..f40ea14 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 "pretty-print.h"
+
 /* A class for handling the various dump_* calls.
 
    In particular, this class has responsibility for consolidating
@@ -39,6 +41,8 @@ class dump_context
 
   ~dump_context ();
 
+  void refresh_dumps_are_enabled ();
+
   void dump_loc (dump_flags_t dump_kind, const dump_location_t &loc);
 
   void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
@@ -93,6 +97,8 @@ class dump_context
 
   void end_any_optinfo ();
 
+  void emit_item (optinfo_item *item, dump_flags_t dump_kind);
+
  private:
   optinfo &ensure_pending_optinfo ();
   optinfo &begin_next_optinfo (const dump_location_t &loc);
@@ -108,6 +114,11 @@ class dump_context
      if any.  */
   optinfo *m_pending;
 
+  /* For use in selftests: if non-NULL, then items are to be printed
+     to this, using the given flags.  */
+  pretty_printer *m_test_pp;
+  dump_flags_t m_test_pp_flags;
+
   /* The currently active dump_context, for use by the dump_* API calls.  */
   static dump_context *s_current;
 
@@ -123,13 +134,16 @@ class dump_context
 class temp_dump_context
 {
  public:
-  temp_dump_context (bool forcibly_enable_optinfo);
+  temp_dump_context (bool forcibly_enable_optinfo,
+		     dump_flags_t test_pp_flags);
   ~temp_dump_context ();
 
   /* Support for selftests.  */
   optinfo *get_pending_optinfo () const { return m_context.m_pending; }
+  const char *get_dumped_text ();
 
  private:
+  pretty_printer m_pp;
   dump_context m_context;
   dump_context *m_saved;
   bool m_saved_flag_remarks;
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 3c8bc38..10e9cab 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -63,15 +63,6 @@ dump_flags_t dump_flags;
 bool dumps_are_enabled = false;
 
 
-/* Update the "dumps_are_enabled" global; to be called whenever dump_file
-   or alt_dump_file change.  */
-
-static void
-refresh_dumps_are_enabled ()
-{
-  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
-}
-
 /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
    global.  */
 
@@ -80,7 +71,7 @@ set_dump_file (FILE *new_dump_file)
 {
   dumpfile_ensure_any_optinfo_are_flushed ();
   dump_file = new_dump_file;
-  refresh_dumps_are_enabled ();
+  dump_context::get ().refresh_dumps_are_enabled ();
 }
 
 /* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
@@ -91,7 +82,7 @@ set_alt_dump_file (FILE *new_alt_dump_file)
 {
   dumpfile_ensure_any_optinfo_are_flushed ();
   alt_dump_file = new_alt_dump_file;
-  refresh_dumps_are_enabled ();
+  dump_context::get ().refresh_dumps_are_enabled ();
 }
 
 #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
@@ -465,6 +456,27 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
     }
 }
 
+/* Print source location to PP if enabled.  */
+
+static void
+dump_loc (dump_flags_t dump_kind, pretty_printer *pp, source_location loc)
+{
+  if (dump_kind)
+    {
+      if (LOCATION_LOCUS (loc) > BUILTINS_LOCATION)
+	pp_printf (pp, "%s:%d:%d: note: ", LOCATION_FILE (loc),
+		   LOCATION_LINE (loc), LOCATION_COLUMN (loc));
+      else if (current_function_decl)
+	pp_printf (pp, "%s:%d:%d: note: ",
+		   DECL_SOURCE_FILE (current_function_decl),
+		   DECL_SOURCE_LINE (current_function_decl),
+		   DECL_SOURCE_COLUMN (current_function_decl));
+      /* Indentation based on scope depth.  */
+      for (unsigned i = 0; i < get_dump_scope_depth (); i++)
+	pp_character (pp, ' ');
+    }
+}
+
 /* Implementation of dump_context member functions.  */
 
 /* dump_context's dtor.  */
@@ -474,12 +486,24 @@ dump_context::~dump_context ()
   delete m_pending;
 }
 
+/* Update the "dumps_are_enabled" global; to be called whenever dump_file
+   or alt_dump_file change, or when changing dump_context in selftests.  */
+
+void
+dump_context::refresh_dumps_are_enabled ()
+{
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ()
+		       || m_test_pp);
+}
+
 /* Print LOC to the appropriate dump destinations, given DUMP_KIND.
    If optinfos are enabled, begin a new optinfo.  */
 
 void
 dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
 {
+  end_any_optinfo ();
+
   location_t srcloc = loc.get_location_t ();
 
   if (dump_file && (dump_kind & pflags))
@@ -488,6 +512,10 @@ dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
   if (alt_dump_file && (dump_kind & alt_flags))
     ::dump_loc (dump_kind, alt_dump_file, srcloc);
 
+  /* Support for temp_dump_context in selftests.  */
+  if (m_test_pp && (dump_kind & m_test_pp_flags))
+    ::dump_loc (dump_kind, m_test_pp, srcloc);
+
   if (optinfo_enabled_p ())
     {
       optinfo &info = begin_next_optinfo (loc);
@@ -495,6 +523,22 @@ dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
     }
 }
 
+/* Make an item for the given dump call, equivalent to print_gimple_stmt.  */
+
+static optinfo_item *
+make_item_for_dump_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)));
+  return item;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
@@ -503,18 +547,18 @@ dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
 				dump_flags_t extra_dump_flags,
 				gimple *gs, int spc)
 {
-  if (dump_file && (dump_kind & pflags))
-    print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+  optinfo_item *item
+    = make_item_for_dump_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
+  emit_item (item, dump_kind);
 
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
+      info.add_item (item);
     }
+  else
+    delete item;
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
@@ -529,6 +573,22 @@ dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
   dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
 }
 
+/* Make an item for the given dump call, equivalent to print_gimple_expr.  */
+
+static optinfo_item *
+make_item_for_dump_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)));
+  return item;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
    Do not terminate with a newline or semicolon.  */
@@ -538,18 +598,18 @@ dump_context::dump_gimple_expr (dump_flags_t dump_kind,
 				dump_flags_t extra_dump_flags,
 				gimple *gs, int spc)
 {
-  if (dump_file && (dump_kind & pflags))
-    print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags);
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
+  optinfo_item *item
+    = make_item_for_dump_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
+  emit_item (item, dump_kind);
 
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
+      info.add_item (item);
     }
+  else
+    delete item;
 }
 
 /* Similar to dump_gimple_expr, except additionally print source location.  */
@@ -565,6 +625,25 @@ dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
   dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
 }
 
+/* Make an item for the given dump call, equivalent to print_generic_expr.  */
+
+static optinfo_item *
+make_item_for_dump_generic_expr (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)));
+  return item;
+}
 
 /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
    DUMP_KIND is enabled.  */
@@ -574,18 +653,18 @@ dump_context::dump_generic_expr (dump_flags_t dump_kind,
 				 dump_flags_t extra_dump_flags,
 				 tree t)
 {
-  if (dump_file && (dump_kind & pflags))
-      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
+  optinfo_item *item
+    = make_item_for_dump_generic_expr (t, dump_flags | extra_dump_flags);
+  emit_item (item, dump_kind);
 
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_tree (t, dump_flags | extra_dump_flags);
+      info.add_item (item);
     }
+  else
+    delete item;
 }
 
 
@@ -602,36 +681,56 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
   dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
+/* Make an item for the given dump call.  */
+
+static optinfo_item *
+make_item_for_dump_printf_va (const char *format, va_list ap)
+  ATTRIBUTE_PRINTF (1, 0);
+
+static optinfo_item *
+make_item_for_dump_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);
+  return item;
+}
+
+/* Make an item for the given dump call.  */
+
+static optinfo_item *
+make_item_for_dump_printf (const char *format, ...)
+  ATTRIBUTE_PRINTF (1, 2);
+
+static optinfo_item *
+make_item_for_dump_printf (const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  optinfo_item *item
+    = make_item_for_dump_printf_va (format, ap);
+  va_end (ap);
+  return item;
+}
+
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
 dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
 			      va_list ap)
 {
-  if (dump_file && (dump_kind & pflags))
-    {
-      va_list aq;
-      va_copy (aq, ap);
-      vfprintf (dump_file, format, aq);
-      va_end (aq);
-    }
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    {
-      va_list aq;
-      va_copy (aq, ap);
-      vfprintf (alt_dump_file, format, aq);
-      va_end (aq);
-    }
+  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
+  emit_item (item, dump_kind);
 
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
-      va_list aq;
-      va_copy (aq, ap);
-      info.add_printf_va (format, aq);
-      va_end (aq);
+      info.handle_dump_file_kind (dump_kind);
+      info.add_item (item);
     }
+  else
+    delete item;
 }
 
 /* Similar to dump_printf, except source location is also printed, and
@@ -646,26 +745,64 @@ dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
   dump_printf_va (dump_kind, format, ap);
 }
 
-/* Output VALUE in decimal to appropriate dump streams.  */
+/* Make an item for the given dump call, equivalent to print_dec.  */
 
 template<unsigned int N, typename C>
-void
-dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+static optinfo_item *
+make_item_for_dump_dec (const poly_int<N, C> &value)
 {
   STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
   signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
-  if (dump_file && (dump_kind & pflags))
-    print_dec (value, dump_file, sgn);
 
-  if (alt_dump_file && (dump_kind & alt_flags))
-    print_dec (value, alt_dump_file, sgn);
+  pretty_printer pp;
+
+  if (value.is_constant ())
+    pp_wide_int (&pp, value.coeffs[0], sgn);
+  else
+    {
+      pp_character (&pp, '[');
+      for (unsigned int i = 0; i < N; ++i)
+	{
+	  pp_wide_int (&pp, value.coeffs[i], sgn);
+	  pp_character (&pp, i == N - 1 ? ']' : ',');
+	}
+    }
+
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			xstrdup (pp_formatted_text (&pp)));
+  return item;
+}
+
+/* Output VALUE in decimal to appropriate dump streams.  */
+
+template<unsigned int N, typename C>
+void
+dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+{
+  optinfo_item *item = make_item_for_dump_dec (value);
+  emit_item (item, dump_kind);
 
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_poly_int<N,C> (value);
+      info.add_item (item);
     }
+  else
+    delete item;
+}
+
+/* Make an item for the given dump call.  */
+
+static optinfo_item *
+make_item_for_dump_symtab_node (symtab_node *node)
+{
+  location_t loc = DECL_SOURCE_LOCATION (node->decl);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
+			xstrdup (node->dump_name ()));
+  return item;
 }
 
 /* Output the name of NODE on appropriate dump streams.  */
@@ -673,18 +810,17 @@ dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 void
 dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
 {
-  if (dump_file && (dump_kind & pflags))
-    fprintf (dump_file, "%s", node->dump_name ());
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    fprintf (alt_dump_file, "%s", node->dump_name ());
+  optinfo_item *item = make_item_for_dump_symtab_node (node);
+  emit_item (item, dump_kind);
 
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_symtab_node (node);
+      info.add_item (item);
     }
+  else
+    delete item;
 }
 
 /* Get the current dump scope-nesting depth.
@@ -705,28 +841,28 @@ dump_context::get_scope_depth () const
 void
 dump_context::begin_scope (const char *name, const dump_location_t &loc)
 {
-  /* Specialcase, to avoid going through dump_printf_loc,
-     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
-
   if (dump_file)
-    {
-      ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
-      fprintf (dump_file, "=== %s ===\n", name);
-    }
+    ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
 
   if (alt_dump_file)
-    {
-      ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
-      fprintf (alt_dump_file, "=== %s ===\n", name);
-    }
+    ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+
+  /* Support for temp_dump_context in selftests.  */
+  if (m_test_pp)
+    ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
+
+  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n", name);
+  emit_item (item, MSG_NOTE);
 
   if (optinfo_enabled_p ())
     {
+      optinfo &info = begin_next_optinfo (loc);
+      info.m_kind = OPTINFO_KIND_SCOPE;
+      info.add_item (item);
       end_any_optinfo ();
-      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
-      info.add_printf ("=== %s ===", name);
-      info.emit ();
     }
+  else
+    delete item;
 
   m_scope_depth++;
 }
@@ -776,6 +912,23 @@ dump_context::end_any_optinfo ()
   m_pending = NULL;
 }
 
+/* Emit ITEM to all item destinations (those that don't require
+   consolidation into optinfo instances).  */
+
+void
+dump_context::emit_item (optinfo_item *item, dump_flags_t dump_kind)
+{
+  if (dump_file && (dump_kind & pflags))
+    fprintf (dump_file, "%s", item->get_text ());
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    fprintf (alt_dump_file, "%s", item->get_text ());
+
+  /* Support for temp_dump_context in selftests.  */
+  if (m_test_pp && (dump_kind & m_test_pp_flags))
+    pp_string (m_test_pp, item->get_text ());
+}
+
 /* The current singleton dump_context, and its default.  */
 
 dump_context *dump_context::s_current = &dump_context::s_default;
@@ -1510,12 +1663,18 @@ enable_rtl_dump_file (void)
 /* temp_dump_context's ctor.  Temporarily override the dump_context
    (to forcibly enable optinfo-generation).  */
 
-temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
+temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo,
+				      dump_flags_t test_pp_flags)
+
 : m_context (),
   m_saved (&dump_context ().get ())
 {
   dump_context::s_current = &m_context;
   m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
+  m_context.m_test_pp = &m_pp;
+  m_context.m_test_pp_flags = test_pp_flags;
+
+  dump_context::get ().refresh_dumps_are_enabled ();
 }
 
 /* temp_dump_context's dtor.  Restore the saved dump_context.  */
@@ -1523,6 +1682,16 @@ temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
 temp_dump_context::~temp_dump_context ()
 {
   dump_context::s_current = m_saved;
+
+  dump_context::get ().refresh_dumps_are_enabled ();
+}
+
+/* 0-terminate the text dumped so far, and return it.  */
+
+const char *
+temp_dump_context::get_dumped_text ()
+{
+  return pp_formatted_text (&m_pp);
 }
 
 namespace selftest {
@@ -1561,6 +1730,29 @@ test_impl_location ()
 #endif
 }
 
+/* Verify that the text dumped so far in CONTEXT equals
+   EXPECTED_TEXT, using LOC for the location of any failure.
+   As a side-effect, the internal buffer is 0-terminated.  */
+
+static void
+verify_dumped_text (const location &loc,
+		    temp_dump_context *context,
+		    const char *expected_text)
+{
+  gcc_assert (context);
+  ASSERT_STREQ_AT (loc, context->get_dumped_text (),
+		   expected_text);
+}
+
+/* Verify that the text dumped so far in CONTEXT equals
+   EXPECTED_TEXT.
+   As a side-effect, the internal buffer is 0-terminated.  */
+
+#define ASSERT_DUMPED_TEXT_EQ(CONTEXT, EXPECTED_TEXT)			\
+  SELFTEST_BEGIN_STMT							\
+    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT), (EXPECTED_TEXT)); \
+  SELFTEST_END_STMT
+
 /* Verify that ITEM has the expected values.  */
 
 static void
@@ -1611,116 +1803,198 @@ test_capture_of_dump_calls (const line_table_case &case_)
   linemap_line_start (line_table, 5, 100);
   linemap_add (line_table, LC_LEAVE, false, NULL, 0);
   location_t where = linemap_position_for_column (line_table, 10);
+  if (where > LINE_MAP_MAX_LOCATION_WITH_COLS)
+    return;
 
   dump_location_t loc = dump_location_t::from_location_t (where);
 
-  /* Test of dump_printf.  */
-  {
-    temp_dump_context tmp (true);
-    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
-
-    optinfo *info = tmp.get_pending_optinfo ();
-    ASSERT_TRUE (info != NULL);
-    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
-    ASSERT_EQ (info->num_items (), 1);
-    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
-  }
-
-  /* Tree, via dump_generic_expr.  */
-  {
-    temp_dump_context tmp (true);
-    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
-    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
-
-    optinfo *info = tmp.get_pending_optinfo ();
-    ASSERT_TRUE (info != NULL);
-    ASSERT_EQ (info->get_location_t (), where);
-    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
-    ASSERT_EQ (info->num_items (), 2);
-    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
-    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
-  }
-
-  /* Tree, via dump_generic_expr_loc.  */
-  {
-    temp_dump_context tmp (true);
-    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
-
-    optinfo *info = tmp.get_pending_optinfo ();
-    ASSERT_TRUE (info != NULL);
-    ASSERT_EQ (info->get_location_t (), where);
-    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
-    ASSERT_EQ (info->num_items (), 1);
-    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
-  }
-
-  /* Gimple.  */
-  {
-    greturn *stmt = gimple_build_return (NULL);
-    gimple_set_location (stmt, where);
-
-    /* dump_gimple_stmt_loc.  */
-    {
-      temp_dump_context tmp (true);
-      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
-
-      optinfo *info = tmp.get_pending_optinfo ();
-      ASSERT_TRUE (info != NULL);
-      ASSERT_EQ (info->num_items (), 1);
-      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
-    }
-
-    /* dump_gimple_stmt.  */
-    {
-      temp_dump_context tmp (true);
-      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
+  greturn *stmt = gimple_build_return (NULL);
+  gimple_set_location (stmt, where);
 
-      optinfo *info = tmp.get_pending_optinfo ();
-      ASSERT_TRUE (info != NULL);
-      ASSERT_EQ (info->num_items (), 1);
-      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
-    }
-
-    /* dump_gimple_expr_loc.  */
+  /* Run all tests twice, with and then without optinfo enabled, to ensure
+     that immediate destinations vs optinfo-based destinations both
+     work, independently of each other, with no leaks.  */
+  for (int i = 0 ; i < 2; i++)
     {
-      temp_dump_context tmp (true);
-      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
+      bool with_optinfo = (i == 0);
+
+      /* Test of dump_printf.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "int: 42 str: foo");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+	    ASSERT_EQ (info->num_items (), 1);
+	    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
+	  }
+      }
+
+      /* Tree, via dump_generic_expr.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
+	dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: test of tree: 0");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->get_location_t (), where);
+	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+	    ASSERT_EQ (info->num_items (), 2);
+	    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
+	    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
+	  }
+      }
+
+      /* Tree, via dump_generic_expr_loc.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: 1");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->get_location_t (), where);
+	    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
+	    ASSERT_EQ (info->num_items (), 1);
+	    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
+	  }
+      }
+
+      /* Gimple.  */
+      {
+	/* dump_gimple_stmt_loc.  */
+	{
+	  temp_dump_context tmp (with_optinfo, MSG_ALL);
+	  dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
+
+	  ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: return;\n");
+	  if (with_optinfo)
+	    {
+	      optinfo *info = tmp.get_pending_optinfo ();
+	      ASSERT_TRUE (info != NULL);
+	      ASSERT_EQ (info->num_items (), 1);
+	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
+	    }
+	}
 
-      optinfo *info = tmp.get_pending_optinfo ();
-      ASSERT_TRUE (info != NULL);
-      ASSERT_EQ (info->num_items (), 1);
-      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
-    }
+	/* dump_gimple_stmt.  */
+	{
+	  temp_dump_context tmp (with_optinfo, MSG_ALL);
+	  dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
+
+	  ASSERT_DUMPED_TEXT_EQ (tmp, "return;\n");
+	  if (with_optinfo)
+	    {
+	      optinfo *info = tmp.get_pending_optinfo ();
+	      ASSERT_TRUE (info != NULL);
+	      ASSERT_EQ (info->num_items (), 1);
+	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
+	    }
+	}
 
-    /* dump_gimple_expr.  */
-    {
-      temp_dump_context tmp (true);
-      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
+	/* dump_gimple_expr_loc.  */
+	{
+	  temp_dump_context tmp (with_optinfo, MSG_ALL);
+	  dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
+
+	  ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: return;");
+	  if (with_optinfo)
+	    {
+	      optinfo *info = tmp.get_pending_optinfo ();
+	      ASSERT_TRUE (info != NULL);
+	      ASSERT_EQ (info->num_items (), 1);
+	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+	    }
+	}
 
-      optinfo *info = tmp.get_pending_optinfo ();
-      ASSERT_TRUE (info != NULL);
-      ASSERT_EQ (info->num_items (), 1);
-      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+	/* dump_gimple_expr.  */
+	{
+	  temp_dump_context tmp (with_optinfo, MSG_ALL);
+	  dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
+
+	  ASSERT_DUMPED_TEXT_EQ (tmp, "return;");
+	  if (with_optinfo)
+	    {
+	      optinfo *info = tmp.get_pending_optinfo ();
+	      ASSERT_TRUE (info != NULL);
+	      ASSERT_EQ (info->num_items (), 1);
+	      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
+	    }
+	}
+      }
+
+      /* poly_int.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_dec (MSG_NOTE, poly_int64 (42));
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "42");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->num_items (), 1);
+	    ASSERT_IS_TEXT (info->get_item (0), "42");
+	  }
+      }
+
+      /* scopes.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");
+	{
+	  AUTO_DUMP_SCOPE ("outer scope", stmt);
+	  dump_printf_loc (MSG_NOTE, stmt, "msg 2\n");
+	  {
+	    AUTO_DUMP_SCOPE ("middle scope", stmt);
+	    dump_printf_loc (MSG_NOTE, stmt, "msg 3\n");
+	    {
+	      AUTO_DUMP_SCOPE ("inner scope", stmt);
+	      dump_printf_loc (MSG_NOTE, stmt, "msg 4\n");
+	    }
+	    dump_printf_loc (MSG_NOTE, stmt, "msg 5\n");
+	  }
+	  dump_printf_loc (MSG_NOTE, stmt, "msg 6\n");
+	}
+	dump_printf_loc (MSG_NOTE, stmt, "msg 7\n");
+
+	ASSERT_DUMPED_TEXT_EQ (tmp,
+			       "test.txt:5:10: note: msg 1\n"
+			       "test.txt:5:10: note: === outer scope ===\n"
+			       "test.txt:5:10: note:  msg 2\n"
+			       "test.txt:5:10: note:  === middle scope ===\n"
+			       "test.txt:5:10: note:   msg 3\n"
+			       "test.txt:5:10: note:   === inner scope ===\n"
+			       "test.txt:5:10: note:    msg 4\n"
+			       "test.txt:5:10: note:   msg 5\n"
+			       "test.txt:5:10: note:  msg 6\n"
+			       "test.txt:5:10: note: msg 7\n");
+	if (with_optinfo)
+	  {
+	    optinfo *info = tmp.get_pending_optinfo ();
+	    ASSERT_TRUE (info != NULL);
+	    ASSERT_EQ (info->num_items (), 1);
+	    ASSERT_IS_TEXT (info->get_item (0), "msg 7\n");
+	  }
+      }
     }
-  }
-
-  /* poly_int.  */
-  {
-    temp_dump_context tmp (true);
-    dump_dec (MSG_NOTE, poly_int64 (42));
-
-    optinfo *info = tmp.get_pending_optinfo ();
-    ASSERT_TRUE (info != NULL);
-    ASSERT_EQ (info->num_items (), 1);
-    ASSERT_IS_TEXT (info->get_item (0), "42");
-  }
 
   /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
      above.  */
   {
     /* MSG_OPTIMIZED_LOCATIONS.  */
     {
-      temp_dump_context tmp (true);
+      temp_dump_context tmp (true, MSG_ALL);
       dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
       ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
 		 OPTINFO_KIND_SUCCESS);
@@ -1728,7 +2002,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
 
     /* MSG_MISSED_OPTIMIZATION.  */
     {
-      temp_dump_context tmp (true);
+      temp_dump_context tmp (true, MSG_ALL);
       dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
       ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
 		 OPTINFO_KIND_FAILURE);
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 1dbe3b8..2b174e5 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -442,19 +442,27 @@ dump_enabled_p (void)
 }
 
 /* The following API calls (which *don't* take a "FILE *")
-   write the output to zero or more locations:
-   (a) the active dump_file, if any
-   (b) the -fopt-info destination, if any
-   (c) to the "optinfo" destinations, if any:
-       (c.1) as optimization records
-
-   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
-                                   |
-                                   +--> (b) alt_dump_file
-                                   |
-                                   `--> (c) optinfo
-                                            `---> optinfo destinations
-                                                  (c.1) optimization records
+   write the output to zero or more locations.
+
+   Some destinations are written to immediately as dump_* calls
+   are made; for others, the output is consolidated into an "optinfo"
+   instance (with its own metadata), and only emitted once the optinfo
+   is complete.
+
+   The destinations are:
+
+   (a) the "immediate" destinations:
+       (a.1) the active dump_file, if any
+       (a.2) the -fopt-info destination, if any
+   (b) the "optinfo" destinations, if any:
+       (b.1) as optimization records
+
+   dump_* (MSG_*) --> dumpfile.c --> items --> (a.1) dump_file
+                                       |   `-> (a.2) alt_dump_file
+                                       |
+                                       `--> (b) optinfo
+                                                `---> optinfo destinations
+                                                      (b.1) optimization records
 
    For optinfos, the dump_*_loc mark the beginning of an optinfo
    instance: all subsequent dump_* calls are consolidated into
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
index 2199d52..992960e 100644
--- a/gcc/optinfo-emit-json.cc
+++ b/gcc/optinfo-emit-json.cc
@@ -537,7 +537,7 @@ namespace selftest {
 static void
 test_building_json_from_dump_calls ()
 {
-  temp_dump_context tmp (true);
+  temp_dump_context tmp (true, MSG_NOTE);
   dump_location_t loc;
   dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
   dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
index 93de9d9..b858c3c 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -34,11 +34,11 @@ along with GCC; see the file COPYING3.  If not see
 #include "cgraph.h"
 #include "selftest.h"
 
-/* optinfo_item's ctor.  */
+/* optinfo_item's ctor.  Takes ownership of TEXT.  */
 
 optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
-			    char *text, bool owned)
-: m_kind (kind), m_location (location), m_text (text), m_owned (owned)
+			    char *text)
+: m_kind (kind), m_location (location), m_text (text)
 {
 }
 
@@ -46,8 +46,7 @@ optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
 
 optinfo_item::~optinfo_item ()
 {
-  if (m_owned)
-    free (m_text);
+  free (m_text);
 }
 
 /* Get a string from KIND.  */
@@ -81,7 +80,17 @@ optinfo::~optinfo ()
     delete item;
 }
 
-/* Emit the optinfo to all of the active destinations.  */
+/* Add ITEM to this optinfo.  */
+
+void
+optinfo::add_item (optinfo_item *item)
+{
+  gcc_assert (item);
+  m_items.safe_push (item);
+}
+
+/* Emit the optinfo to all of the "non-immediate" destinations
+   (emission to "immediate" destinations is done by emit_item).  */
 
 void
 optinfo::emit ()
@@ -103,120 +112,6 @@ optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
     m_kind = OPTINFO_KIND_NOTE;
 }
 
-/* Append a string literal to this optinfo.  */
-
-void
-optinfo::add_string (const char *str)
-{
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
-			const_cast <char *> (str), false);
-  m_items.safe_push (item);
-}
-
-/* Append printf-formatted text to this optinfo.  */
-
-void
-optinfo::add_printf (const char *format, ...)
-{
-  va_list ap;
-  va_start (ap, format);
-  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)
-{
-  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);
-}
-
-/* Append a gimple statement to this optinfo, equivalent to
-   print_gimple_stmt.  */
-
-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);
-  m_items.safe_push (item);
-}
-
-/* Append a gimple statement to this optinfo, equivalent to
-   print_gimple_expr.  */
-
-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);
-  m_items.safe_push (item);
-}
-
-/* Append a tree node to this optinfo, equivalent to print_generic_expr.  */
-
-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);
-  m_items.safe_push (item);
-}
-
-/* Append a symbol table node to this optinfo.  */
-
-void
-optinfo::add_symtab_node (symtab_node *node)
-{
-  location_t loc = DECL_SOURCE_LOCATION (node->decl);
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
-			xstrdup (node->dump_name ()), true);
-  m_items.safe_push (item);
-}
-
-/* Append the decimal represenation of a wide_int_ref to this
-   optinfo.  */
-
-void
-optinfo::add_dec (const wide_int_ref &wi, signop sgn)
-{
-  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
-  print_dec (wi, buf, sgn);
-  optinfo_item *item
-    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
-			xstrdup (buf), true);
-  m_items.safe_push (item);
-}
-
 /* Should optinfo instances be created?
    All creation of optinfos should be guarded by this predicate.
    Return true if any optinfo destinations are active.  */
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index c4cf8ad..8ac961c 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -92,6 +92,8 @@ enum optinfo_kind
 
 extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
 
+class dump_context;
+
 /* A bundle of information describing part of an optimization.  */
 
 class optinfo
@@ -120,41 +122,14 @@ 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 add_item (optinfo_item *item);
+
  private:
   void emit ();
 
   /* Pre-canned ways of manipulating the optinfo, for use by friend class
      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_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);
-  void add_symtab_node (symtab_node *node);
-  void add_dec (const wide_int_ref &wi, signop sgn);
-
-  template<unsigned int N, typename C>
-  void add_poly_int (const poly_int<N, C> &value)
-  {
-    /* Compare with dump_dec (MSG_NOTE, ).  */
-
-    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
-    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
-
-    if (value.is_constant ())
-      add_dec (value.coeffs[0], sgn);
-    else
-      {
-	add_string ("[");
-	for (unsigned int i = 0; i < N; ++i)
-	  {
-	    add_dec (value.coeffs[i], sgn);
-	    add_string (i == N - 1 ? "]" : ",");
-	  }
-      }
-  }
 
  private:
   dump_location_t m_loc;
@@ -179,7 +154,7 @@ class optinfo_item
 {
  public:
   optinfo_item (enum optinfo_item_kind kind, location_t location,
-		char *text, bool owned);
+		char *text);
   ~optinfo_item ();
 
   enum optinfo_item_kind get_kind () const { return m_kind; }
@@ -191,9 +166,8 @@ class optinfo_item
   enum optinfo_item_kind m_kind;
   location_t m_location;
 
-  /* The textual form of the item.  */
+  /* The textual form of the item, owned by the item.  */
   char *m_text;
-  bool m_owned;
 };
 
 #endif /* #ifndef GCC_OPTINFO_H */
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 3/5] C++: clean up cp_printer
  2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
                   ` (3 preceding siblings ...)
  2018-07-27 21:47 ` [PATCH 5/5] Formatted printing for dump_* in the middle-end David Malcolm
@ 2018-07-27 21:47 ` David Malcolm
  2018-07-28 14:06   ` Jason Merrill
  2018-07-31 12:50 ` [PATCH 0/5] dump_printf support for middle-end types Richard Biener
  5 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-07-27 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This makes it easier to compare cp_printer with gcc_cxxdiag_char_table
in c-format.c.

No functional change intended.

gcc/cp/ChangeLog:
	* error.c (cp_printer): In the leading comment, move "%H" and "I"
	into alphabetical order, and add missing "%G" and "K".  Within the
	switch statement, move cases 'G', 'H', 'I' and 'K" so that the
	cases are in alphabetical order.
---
 gcc/cp/error.c | 46 ++++++++++++++++++++--------------------------
 1 file changed, 20 insertions(+), 26 deletions(-)

diff --git a/gcc/cp/error.c b/gcc/cp/error.c
index b0d8e32..7a644fd 100644
--- a/gcc/cp/error.c
+++ b/gcc/cp/error.c
@@ -4025,6 +4025,10 @@ defer_phase_2_of_type_diff (deferred_printed_type *deferred,
    %D   declaration.
    %E   expression.
    %F   function declaration.
+   %G   gcall *
+   %H   type difference (from).
+   %I   type difference (to).
+   %K   tree
    %L	language as used in extern "lang".
    %O	binary operator.
    %P   function parameter whose position is indicated by an integer.
@@ -4032,9 +4036,7 @@ defer_phase_2_of_type_diff (deferred_printed_type *deferred,
    %S   substitution (template + args)
    %T   type.
    %V   cv-qualifier.
-   %X   exception-specification.
-   %H   type difference (from)
-   %I   type difference (to).  */
+   %X   exception-specification.  */
 static bool
 cp_printer (pretty_printer *pp, text_info *text, const char *spec,
 	    int precision, bool wide, bool set_locus, bool verbose,
@@ -4076,6 +4078,21 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
       break;
     case 'E': result = expr_to_string (next_tree);		break;
     case 'F': result = fndecl_to_string (next_tree, verbose);	break;
+    case 'G':
+      percent_G_format (text);
+      return true;
+    case 'H':
+      defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree,
+				  buffer_ptr, verbose, *quoted);
+      return true;
+    case 'I':
+      defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree,
+				  buffer_ptr, verbose, *quoted);
+      return true;
+    case 'K':
+      t = va_arg (*text->args_ptr, tree);
+      percent_K_format (text, t);
+      return true;
     case 'L': result = language_to_string (next_lang);		break;
     case 'O': result = op_to_string (false, next_tcode);	break;
     case 'P': result = parm_to_string (next_int);		break;
@@ -4090,29 +4107,6 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
     case 'V': result = cv_to_string (next_tree, verbose);	break;
     case 'X': result = eh_spec_to_string (next_tree, verbose);  break;
 
-    case 'G':
-      percent_G_format (text);
-      return true;
-
-    case 'K':
-      t = va_arg (*text->args_ptr, tree);
-      percent_K_format (text, t);
-      return true;
-
-    case 'H':
-      {
-	defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree,
-				    buffer_ptr, verbose, *quoted);
-	return true;
-      }
-
-    case 'I':
-      {
-	defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree,
-				    buffer_ptr, verbose, *quoted);
-	return true;
-      }
-
     default:
       return false;
     }
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 5/5] Formatted printing for dump_* in the middle-end
  2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
                   ` (2 preceding siblings ...)
  2018-07-27 21:47 ` [PATCH 1/5] Simplify dump_context by adding a dump_loc member function David Malcolm
@ 2018-07-27 21:47 ` David Malcolm
  2018-07-31 13:03   ` Richard Biener
  2018-07-27 21:47 ` [PATCH 3/5] C++: clean up cp_printer David Malcolm
  2018-07-31 12:50 ` [PATCH 0/5] dump_printf support for middle-end types Richard Biener
  5 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-07-27 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch converts dump_print and dump_printf_loc from using
printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
based on pp_format, which supports formatting middle-end types.

In particular, the following codes are implemented (in addition
to the standard pretty_printer ones):

   %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 (MSG_*, arg, TDF_SLIM).

Hence it becomes possible to convert e.g.:

  if (dump_enabled_p ())
    {
      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                       "not vectorized: different sized vector "
                       "types in statement, ");
      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, nunits_vectype);
      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
    }

into a one-liner:

  if (dump_enabled_p ())
    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                     "not vectorized: different sized vector "
                     "types in statement, %T and %T\n",
                     vectype, nunits_vectype);

Unlike regular pretty-printers, this one captures optinfo_item
instances for the formatted chunks as appropriate, so that when
written out to a JSON optimization record, the relevant parts of
the message are labelled by type, and by source location (so that
e.g. %G is entirely equivalent to using dump_gimple_stmt).

dump_printf and dump_printf_loc become marked with
ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.

gcc/c-family/ChangeLog:
	* c-format.c (enum format_type): Add gcc_dump_printf_format_type.
	(local_gimple_ptr_node): New decl.
	(gcc_dump_printf_length_specs): New.
	(gcc_dump_printf_flag_pairs): New.
	(gcc_dump_printf_flag_specs): New.
	(gcc_dump_printf_char_table): New.
	(format_types_orig): Add entry for "gcc_dump_printf".
	(init_dynamic_diag_info): Create local_gimple_ptr_node.
	Set up length_char_specs and conversion_specs for
	gcc_dump_printf_format_type.
	(handle_format_attribute): Handle gcc_dump_printf_format_type.
	* c-format.h (T89_GIMPLE): New macro.

gcc/ChangeLog:
	* dump-context.h: Include "dumpfile.h".
	(dump_context::dump_printf_va): Convert final param from va_list
	to va_list *.  Convert from ATTRIBUTE_PRINTF to
	ATTRIBUTE_GCC_DUMP_PRINTF.
	(dump_context::dump_printf_loc_va): Likewise.
	* dumpfile.c: Include "stringpool.h".
	(make_item_for_dump_printf_va): Delete.
	(make_item_for_dump_printf): Delete.
	(class dump_pretty_printer): New class.
	(dump_pretty_printer::dump_pretty_printer): New ctor.
	(dump_pretty_printer::emit_items): New member function.
	(dump_pretty_printer::emit_any_pending_textual_chunks): New member
	function.
	(dump_pretty_printer::emit_item): New member function.
	(dump_pretty_printer::stash_item): New member function.
	(dump_pretty_printer::format_decoder_cb): New member function.
	(dump_pretty_printer::decode_format): New member function.
	(dump_context::dump_printf_va): Reimplement in terms of
	dump_pretty_printer.
	(dump_context::dump_printf_loc_va): Convert final param from va_list
	to va_list *.
	(dump_context::begin_scope): Reimplement call to
	make_item_for_dump_printf.
	(dump_printf): Update for change to dump_printf_va.
	(dump_printf_loc): Likewise.
	(selftest::test_capture_of_dump_calls): Convert "stmt" from
	greturn * to gimple *.  Add a test_decl.  Add tests of dump_printf
	with %T, %E, and %G.
	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
	(dump_printf): Replace ATTRIBUTE_PRINTF_2 with
	ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
	(dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
	ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).

gcc/testsuite/ChangeLog:
	* gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage for
	gcc_dump_printf.
	* gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add test
	coverage for gcc_dump_printf.
---
 gcc/c-family/c-format.c                   |  60 ++++-
 gcc/c-family/c-format.h                   |   1 +
 gcc/dump-context.h                        |   7 +-
 gcc/dumpfile.c                            | 358 +++++++++++++++++++++++++++---
 gcc/dumpfile.h                            |  20 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  15 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  26 +++
 7 files changed, 448 insertions(+), 39 deletions(-)

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index 82841e4..b524468 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -44,6 +44,7 @@ enum format_type { printf_format_type, asm_fprintf_format_type,
 		   gcc_diag_format_type, gcc_tdiag_format_type,
 		   gcc_cdiag_format_type,
 		   gcc_cxxdiag_format_type, gcc_gfc_format_type,
+		   gcc_dump_printf_format_type,
 		   gcc_objc_string_format_type,
 		   format_type_error = -1};
 
@@ -56,6 +57,7 @@ struct function_format_info
 
 /* Initialized in init_dynamic_diag_info.  */
 static GTY(()) tree local_tree_type_node;
+static GTY(()) tree local_gimple_ptr_node;
 static GTY(()) tree local_gcall_ptr_node;
 static GTY(()) tree locus;
 
@@ -461,6 +463,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 +553,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 +569,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[] =
 {
@@ -786,6 +791,22 @@ static const format_char_info gcc_gfc_char_table[] =
   { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
+static const format_char_info gcc_dump_printf_char_table[] =
+{
+  /* The conversion specifiers implemented within pp_format.  */
+  PP_FORMAT_CHAR_TABLE,
+
+  /* Custom conversion specifiers implemented by dump_pretty_printer.  */
+
+  /* E and G require a "gimple *" argument at runtime.  */
+  { "EG",   1, STD_C89, { T89_GIMPLE,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
+  /* T requires a "tree" at runtime.  */
+  { "T",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
+};
+
 static const format_char_info scan_char_table[] =
 {
   /* C89 conversion specifiers.  */
@@ -885,6 +906,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,
@@ -3838,6 +3866,29 @@ init_dynamic_diag_info (void)
 	local_tree_type_node = void_type_node;
     }
 
+  /* Similar to the above but for gimple *.  */
+  if (!local_gimple_ptr_node
+      || local_gimple_ptr_node == void_type_node)
+    {
+      if ((local_gimple_ptr_node = maybe_get_identifier ("gimple")))
+	{
+	  local_gimple_ptr_node
+	    = identifier_global_value (local_gimple_ptr_node);
+	  if (local_gimple_ptr_node)
+	    {
+	      if (TREE_CODE (local_gimple_ptr_node) != TYPE_DECL)
+		{
+		  error ("%<gimple%> is not defined as a type");
+		  local_gimple_ptr_node = 0;
+		}
+	      else
+		local_gimple_ptr_node = TREE_TYPE (local_gimple_ptr_node);
+	    }
+	}
+      else
+	local_gimple_ptr_node = void_type_node;
+    }
+
   /* Similar to the above but for gcall*.  */
   if (!local_gcall_ptr_node
       || local_gcall_ptr_node == void_type_node)
@@ -3905,6 +3956,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),
@@ -3931,6 +3983,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
@@ -4085,7 +4139,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.  */
@@ -4107,7 +4162,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/c-family/c-format.h b/gcc/c-family/c-format.h
index f828e77..4dfdbdb 100644
--- a/gcc/c-family/c-format.h
+++ b/gcc/c-family/c-format.h
@@ -298,6 +298,7 @@ struct format_kind_info
 #define T_UC	&unsigned_char_type_node
 #define T99_UC	{ STD_C99, NULL, T_UC }
 #define T_V	&void_type_node
+#define T89_GIMPLE   { STD_C89, NULL, &local_gimple_ptr_node }
 #define T89_G   { STD_C89, NULL, &local_gcall_ptr_node }
 #define T89_T   { STD_C89, NULL, &local_tree_type_node }
 #define T89_V	{ STD_C89, NULL, T_V }
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
index f40ea14..54095d3 100644
--- a/gcc/dump-context.h
+++ b/gcc/dump-context.h
@@ -22,6 +22,7 @@ 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
 #include "pretty-print.h"
 
 /* A class for handling the various dump_* calls.
@@ -73,11 +74,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);
+			   const char *format, va_list *ap)
+    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 10e9cab..7131f56 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.  */
@@ -681,56 +682,262 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
   dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
-/* Make an item for the given dump call.  */
+/* A subclass of pretty_printer for implementing dump_context::dump_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.  */
 
-static optinfo_item *
-make_item_for_dump_printf_va (const char *format, va_list ap)
-  ATTRIBUTE_PRINTF (1, 0);
+class dump_pretty_printer : public pretty_printer
+{
+public:
+  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);
 
-static optinfo_item *
-make_item_for_dump_printf_va (const char *format, va_list ap)
+  void emit_items (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 emit_any_pending_textual_chunks (optinfo *dest);
+
+  void emit_item (optinfo_item *item, optinfo *dest);
+
+  dump_context *m_context;
+  dump_flags_t m_dump_kind;
+  auto_vec<stashed_item> m_stashed_items;
+};
+
+/* dump_pretty_printer's ctor.  */
+
+dump_pretty_printer::dump_pretty_printer (dump_context *context,
+					  dump_flags_t dump_kind)
+: pretty_printer (), m_context (context), m_dump_kind (dump_kind),
+  m_stashed_items ()
+{
+  pp_format_decoder (this) = format_decoder_cb;
+}
+
+/* Phase 3 of formatting; compare with pp_output_formatted_text.
+
+   Emit optinfo_item instances for the various formatted chunks from phases
+   1 and 2 (i.e. pp_format).
+
+   Some chunks may already have had their items built (during decode_format).
+   These chunks have been stashed into m_stashed_items; we emit them here.
+
+   For all other purely textual chunks, they are printed into
+   buffer->formatted_obstack, and then emitted as a textual optinfo_item.
+   This consolidates multiple adjacent text chunks into a single text
+   optinfo_item.  */
+
+void
+dump_pretty_printer::emit_items (optinfo *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)
+	{
+	  emit_any_pending_textual_chunks (dest);
+	  /* This chunk has a stashed item: use it.  */
+	  emit_item (m_stashed_items[stashed_item_idx++].item, dest);
+	}
+      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]);
+    }
+
+  emit_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 dump_pretty_printer::emit_items
+   for consolidating multiple adjacent pure-text chunks into single
+   optinfo_items (in phase 3).  */
+
+void
+dump_pretty_printer::emit_any_pending_textual_chunks (optinfo *dest)
 {
-  char *formatted_text = xvasprintf (format, ap);
+  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
+
+  /* Don't emit 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);
-  return item;
+  emit_item (item, dest);
+
+  /* Clear the pending text by unwinding formatted_text back to the start
+     of the buffer (without deallocating).  */
+  obstack_free (&buffer->formatted_obstack,
+		buffer->formatted_obstack.object_base);
 }
 
-/* Make an item for the given dump call.  */
+/* Emit ITEM and take ownership of it.  If DEST is non-NULL, add ITEM
+   to DEST; otherwise delete ITEM.  */
 
-static optinfo_item *
-make_item_for_dump_printf (const char *format, ...)
-  ATTRIBUTE_PRINTF (1, 2);
+void
+dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest)
+{
+  m_context->emit_item (item, m_dump_kind);
+  if (dest)
+    dest->add_item (item);
+  else
+    delete item;
+}
 
-static optinfo_item *
-make_item_for_dump_printf (const char *format, ...)
+/* Record that ITEM (generated in phase 2 of formatting) is to be used for
+   the chunk at BUFFER_PTR in phase 3 (by emit_items).  */
+
+void
+dump_pretty_printer::stash_item (const char **buffer_ptr, optinfo_item *item)
 {
-  va_list ap;
-  va_start (ap, format);
-  optinfo_item *item
-    = make_item_for_dump_printf_va (format, ap);
-  va_end (ap);
-  return item;
+  gcc_assert (buffer_ptr);
+  gcc_assert (item);
+
+  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
+}
+
+/* pp_format_decoder callback for dump_pretty_printer, and thus for
+   dump_printf and dump_printf_loc.
+
+   A wrapper around decode_format, for type-safety.  */
+
+bool
+dump_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)
+{
+  dump_pretty_printer *opp = static_cast <dump_pretty_printer *> (pp);
+  return opp->decode_format (text, spec, buffer_ptr);
+}
+
+/* Format decoder for dump_pretty_printer, 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 (MSG_*, arg, TDF_SLIM).
+
+   FIXME: add symtab_node?
+
+   These format codes build optinfo_item instances, thus capturing metadata
+   about the arguments being dumped, as well as the textual output.  */
+
+bool
+dump_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).  */
+  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_dump_gimple_expr (stmt, 0, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	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_dump_gimple_stmt (stmt, 0, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	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_dump_generic_expr (t, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	return true;
+      }
+
+    default:
+      return false;
+    }
 }
 
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
 dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
-			      va_list ap)
+			      va_list *ap)
 {
-  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
-  emit_item (item, dump_kind);
+  dump_pretty_printer pp (this, dump_kind);
 
+  text_info text;
+  text.err_no = errno;
+  text.args_ptr = ap;
+  text.format_spec = format;
+
+  /* Phases 1 and 2, using pp_format.  */
+  pp_format (&pp, &text);
+
+  /* Phase 3.  */
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_item (item);
+      pp.emit_items (&info);
     }
   else
-    delete item;
+    pp.emit_items (NULL);
 }
 
 /* Similar to dump_printf, except source location is also printed, and
@@ -739,7 +946,7 @@ dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
 void
 dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
 				  const dump_location_t &loc,
-				  const char *format, va_list ap)
+				  const char *format, va_list *ap)
 {
   dump_loc (dump_kind, loc);
   dump_printf_va (dump_kind, format, ap);
@@ -851,7 +1058,11 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
   if (m_test_pp)
     ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
 
-  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n", name);
+  pretty_printer pp;
+  pp_printf (&pp, "=== %s ===\n", name);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			xstrdup (pp_formatted_text (&pp)));
   emit_item (item, MSG_NOTE);
 
   if (optinfo_enabled_p ())
@@ -859,7 +1070,6 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
       optinfo &info = begin_next_optinfo (loc);
       info.m_kind = OPTINFO_KIND_SCOPE;
       info.add_item (item);
-      end_any_optinfo ();
     }
   else
     delete item;
@@ -1006,7 +1216,7 @@ dump_printf (dump_flags_t dump_kind, const char *format, ...)
 {
   va_list ap;
   va_start (ap, format);
-  dump_context::get ().dump_printf_va (dump_kind, format, ap);
+  dump_context::get ().dump_printf_va (dump_kind, format, &ap);
   va_end (ap);
 }
 
@@ -1019,7 +1229,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 {
   va_list ap;
   va_start (ap, format);
-  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, &ap);
   va_end (ap);
 }
 
@@ -1808,9 +2018,12 @@ test_capture_of_dump_calls (const line_table_case &case_)
 
   dump_location_t loc = dump_location_t::from_location_t (where);
 
-  greturn *stmt = gimple_build_return (NULL);
+  gimple *stmt = gimple_build_return (NULL);
   gimple_set_location (stmt, where);
 
+  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			       get_identifier ("test_decl"),
+			       integer_type_node);
   /* Run all tests twice, with and then without optinfo enabled, to ensure
      that immediate destinations vs optinfo-based destinations both
      work, independently of each other, with no leaks.  */
@@ -1834,6 +2047,89 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	  }
       }
 
+      /* Test of dump_printf with %T.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "tree: 0");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "gimple: %E", stmt);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "gimple: %G", stmt);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;\n");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
+	dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
+			 " %i consecutive %E%E after\n",
+			 integer_zero_node, test_decl, 42, stmt, stmt);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp,
+			       "test.txt:5:10: note: before 0 and test_decl"
+			       " 42 consecutive return;return; after\n");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 2b174e5..1fe64cb 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 dumpfile.c).  */
+
+#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
@@ -476,9 +489,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/testsuite/gcc.dg/format/gcc_diag-1.c b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
index 034e097..8761456 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
@@ -1,4 +1,4 @@
-/* Test for GCC diagnositc formats.  */
+/* Test for GCC diagnostic formats.  */
 /* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
 /* { dg-do compile } */
 /* { dg-options "-Wformat" } */
@@ -24,6 +24,7 @@ extern int diag (const char *, ...) ATTRIBUTE_DIAG(__gcc_diag__);
 extern int tdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_tdiag__);
 extern int cdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cdiag__);
 extern int cxxdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cxxdiag__);
+extern int dump (const char *, ...) ATTRIBUTE_DIAG(__gcc_dump_printf__);
 
 void
 foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
@@ -39,36 +40,44 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   tdiag ("%%");
   cdiag ("%%");
   cxxdiag ("%%");
+  dump ("%%");
   diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   tdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
+  dump ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   diag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   tdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   cdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   cxxdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
+  dump ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   tdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
+  dump ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   tdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
+  dump ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   tdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
+  dump ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   diag ("%.*s", i, s);
   tdiag ("%.*s", i, s);
   cdiag ("%.*s", i, s);
   cxxdiag ("%.*s", i, s);
+  dump ("%.*s", i, s);
 
   /* Extensions provided in the diagnostic framework.  */
   diag ("%m");
   tdiag ("%m");
   cdiag ("%m");
   cxxdiag ("%m");
+  dump ("%m");
 
   /* Quote directives to avoid "warning: conversion used unquoted." */
   tdiag ("%<%D%F%T%V%>", t1, t1, t1, t1);
@@ -94,20 +103,24 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   tdiag ("%Z", v, v_len);
   cdiag ("%Z", v, v_len);
   cxxdiag ("%Z", v, v_len);
+  dump ("%Z", v, v_len);
 
   /* Bad stuff with extensions.  */
   diag ("%m", i); /* { dg-warning "format" "extra arg" } */
   tdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
   cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
   cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
+  dump ("%m", i); /* { dg-warning "format" "extra arg" } */
   diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   tdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
+  dump ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   tdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
+  dump ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
   tdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
   tdiag ("%E", t1);
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
index ab9bc2f..8b5aadf1 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
@@ -19,12 +19,16 @@ typedef union tree_node *tree;
    the C test to find the symbol.  */
 typedef struct gcall gcall;
 
+/* Likewise for gimple.  */
+typedef struct gimple gimple;
+
 #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__, 1, 2)))
 
 void diag (const char*, ...) FORMAT (diag);
 void cdiag (const char*, ...) FORMAT (cdiag);
 void tdiag (const char*, ...) FORMAT (tdiag);
 void cxxdiag (const char*, ...) FORMAT (cxxdiag);
+void dump (const char*, ...) FORMAT (dump_printf);
 
 void test_diag (tree t, gcall *gc)
 {
@@ -157,3 +161,25 @@ void test_cxxdiag (tree t, gcall *gc)
   cxxdiag ("%<%V%>", t);
   cxxdiag ("%<%X%>", t);
 }
+
+void test_dump (tree t, gimple *stmt)
+{
+  dump ("%<");   /* { dg-warning "unterminated quoting directive" } */
+  dump ("%>");   /* { dg-warning "unmatched quoting directive " } */
+  dump ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting directive" } */
+
+  dump ("%R");       /* { dg-warning "unmatched color reset directive" } */
+  dump ("%r", "");   /* { dg-warning "unterminated color directive" } */
+  dump ("%r%r", "", "");   /* { dg-warning "unterminated color directive" } */
+  dump ("%r%R", "");
+  dump ("%r%r%R", "", "");
+  dump ("%r%R%r%R", "", "");
+
+  dump ("%<%R%>");      /* { dg-warning "unmatched color reset directive" } */
+  dump ("%<%r%>", "");  /* { dg-warning "unterminated color directive" } */
+  dump ("%<%r%R%>", "");
+
+  dump ("%E", stmt);
+  dump ("%T", t);
+  dump ("%G", stmt);
+}
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 4/5] c-family: clean up the data tables in c-format.c
  2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
  2018-07-27 21:47 ` [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file David Malcolm
@ 2018-07-27 21:47 ` David Malcolm
  2018-07-31 12:56   ` Richard Biener
  2018-07-27 21:47 ` [PATCH 1/5] Simplify dump_context by adding a dump_loc member function David Malcolm
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-07-27 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

The format_char_info tables in c-format.c for our own formats contain
a lot of repetition.

This patch adds a macro to express the conversion specifiers implemented
within pp_format, making it clearer which are custom ones added by the
various diagnostic_format_decoder callbacks.

Doing so uncovered a few mistakes in the data (based on comparison with
the source of the diagnostic_format_decoder callbacks, and the notes
below), which the patch fixes:

- gcc_diag_char_table didn't have 'Z', but it *is* implemented by pp_format.

- removed erroneous 'G' and 'K' entries from gcc_diag_char_table: they're
  implemented by default_tree_printer (and thus in "tdiag") and by the
  C/C++ FEs, but not in pp_format.

- removed "v" (lower case) from gcc_tdiag_char_table and
  gcc_cxxdiag_char_table

Notes:

pretty-print.h uses this for ATTRIBUTE_GCC_PPDIAG, used by pp_printf
and pp_verbatim:

whereas diagnostic-core.h uses this for ATTRIBUTE_GCC_DIAG, used by
the various diagnostic functions:

/* If we haven't already defined a front-end-specific diagnostics
   style, use the generic one.  */

Hence I'm assuming that __gcc_diag__ is for use for when we don't
know what kind of diagnostic_format_decoder we have, and we can
only rely on pp_format's core functionality, where __gcc_tdiag__
is allowed to assume default_tree_printer.

gcc/c-family/ChangeLog:
	* c-format.c (PP_FORMAT_CHAR_TABLE): New macro, based on existing
	table entries for gcc_diag_char_table, and the 'Z' entry from
	gcc_tdiag_char_table, changing the "chain" entry for 'Z' from
	&gcc_tdiag_char_table[0] to &gcc_diag_char_table[0].
	(gcc_diag_char_table): Use PP_FORMAT_CHAR_TABLE, implicitly
	adding missing "Z" for this table.  Remove erroneous "G" and "K"
	entries.
	(gcc_tdiag_char_table): Use PP_FORMAT_CHAR_TABLE.  Remove "v".
	(gcc_cdiag_char_table): Use PP_FORMAT_CHAR_TABLE.
	(gcc_cxxdiag_char_table): Use PP_FORMAT_CHAR_TABLE.  Remove "v".

gcc/testsuite/ChangeLog:
	* gcc.dg/format/gcc_diag-1.c (foo): Update the %v tests for
	tdiag and cxxdiag.
	* gcc.dg/format/gcc_diag-10.c (test_diag): Update tests of %G
	and %K.
---
 gcc/c-family/c-format.c                   | 99 ++++++++++---------------------
 gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  4 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  7 +--
 3 files changed, 35 insertions(+), 75 deletions(-)

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index a0192dd..82841e4 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -679,43 +679,40 @@ static const format_char_info asm_fprintf_char_table[] =
   { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
+/* GCC-specific format_char_info arrays.  */
+
+/* The conversion specifiers implemented within pp_format, and thus supported
+   by all pretty_printer instances within GCC.  */
+
+#define PP_FORMAT_CHAR_TABLE \
+  { "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 }, \
+  { "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_diag_char_table[0] }
+
 static const format_char_info gcc_diag_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.  */
+  /* The conversion specifiers implemented within pp_format.  */
+  PP_FORMAT_CHAR_TABLE,
 
-  /* 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 },
-  /* K requires a "tree" argument at runtime.  */
-  { "K",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",    "\"",   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 },
   { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
 static const format_char_info gcc_tdiag_char_table[] =
 {
-  /* C89 conversion specifiers.  */
-  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
-  { "s",   1, STD_C89, { T89_C,   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  }, "q",  "c",  NULL },
+  /* The conversion specifiers implemented within pp_format.  */
+  PP_FORMAT_CHAR_TABLE,
 
-  /* Custom conversion specifiers.  */
+  /* Custom conversion specifiers implemented by default_tree_printer.  */
 
   /* These will require a "tree" at runtime.  */
   { "DFTV", 1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "'",   NULL },
@@ -725,29 +722,15 @@ static const format_char_info gcc_tdiag_char_table[] =
   /* G requires a "gcall*" argument at runtime.  */
   { "G", 1, STD_C89, { T89_G,   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 gcc_cdiag_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 },
+  /* The conversion specifiers implemented within pp_format.  */
+  PP_FORMAT_CHAR_TABLE,
 
-  /* Custom conversion specifiers.  */
+  /* Custom conversion specifiers implemented by c_tree_printer.  */
 
   /* 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 },
@@ -759,33 +742,20 @@ static const format_char_info gcc_cdiag_char_table[] =
 
   { "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 gcc_cxxdiag_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 },
+  /* The conversion specifiers implemented within pp_format.  */
+  PP_FORMAT_CHAR_TABLE,
 
-  /* Custom conversion specifiers.  */
+  /* Custom conversion specifiers implemented by cp_printer.  */
 
   /* These will require a "tree" at runtime.  */
   { "ADFHISTVX",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 },
-  { "v", 0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q#",  "",   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 },
@@ -793,13 +763,6 @@ static const format_char_info gcc_cxxdiag_char_table[] =
   /* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.)  */
   { "CLOPQ",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 }
 };
 
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
index 4dcdb05..034e097 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
@@ -87,9 +87,9 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   cxxdiag ("%<%+#A%+#D%+#E%+#F%+#T%+#V%>", t1, t1, t1, t1, t1, t1);
   cxxdiag ("%C%L%O%P%Q", i, i, i, i, i);
 
-  tdiag ("%v%qv%#v", i, i, i);
+  tdiag ("%v", i); /* { dg-warning "format" } */
   cdiag ("%v%qv%#v", i, i, i);
-  cxxdiag ("%v%qv%#v", i, i, i);
+  cxxdiag ("%v", i); /* { dg-warning "format" } */
 
   tdiag ("%Z", v, v_len);
   cdiag ("%Z", v, v_len);
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
index 9bda73b..ab9bc2f 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
@@ -32,8 +32,8 @@ void test_diag (tree t, gcall *gc)
   diag ("%>");   /* { dg-warning "unmatched quoting directive " } */
   diag ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting directive" } */
 
-  diag ("%G", gc);
-  diag ("%K", t);
+  diag ("%G", gc); /* { dg-warning "format" } */
+  diag ("%K", t); /* { dg-warning "format" } */
 
   diag ("%R");       /* { dg-warning "unmatched color reset directive" } */
   diag ("%r", "");   /* { dg-warning "unterminated color directive" } */
@@ -42,9 +42,6 @@ void test_diag (tree t, gcall *gc)
   diag ("%r%r%R", "", "");
   diag ("%r%R%r%R", "", "");
 
-  diag ("%<%G%>", gc);  /* { dg-warning ".G. conversion used within a quoted sequence" } */
-  diag ("%<%K%>", t);   /* { dg-warning ".K. conversion used within a quoted sequence" } */
-
   diag ("%<%R%>");      /* { dg-warning "unmatched color reset directive" } */
   diag ("%<%r%>", "");  /* { dg-warning "unterminated color directive" } */
   diag ("%<%r%R%>", "");
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 1/5] Simplify dump_context by adding a dump_loc member function
  2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
  2018-07-27 21:47 ` [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file David Malcolm
  2018-07-27 21:47 ` [PATCH 4/5] c-family: clean up the data tables in c-format.c David Malcolm
@ 2018-07-27 21:47 ` David Malcolm
  2018-07-31 12:51   ` Richard Biener
  2018-07-27 21:47 ` [PATCH 5/5] Formatted printing for dump_* in the middle-end David Malcolm
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-07-27 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch removes some duplicated code in dumpfile.c by
reimplementing the various dump_foo_loc calls in terms of dump_foo.

gcc/ChangeLog:
	* dump-context.h (dump_context::dump_loc): New decl.
	* dumpfile.c (dump_context::dump_loc): New member function.
	(dump_context::dump_gimple_stmt_loc): Reimplement using dump_loc
	and dump_gimple_stmt.
	(dump_context::dump_gimple_expr_loc): Likewise, using
	dump_gimple_expr.
	(dump_context::dump_generic_expr_loc): Likewise, using
	dump_generic_expr.
	(dump_context::dump_printf_loc_va): Likewise, using
	dump_printf_va.
	(dump_context::begin_scope): Explicitly using the global function
	"dump_loc", rather than the member function.
---
 gcc/dump-context.h |   2 +
 gcc/dumpfile.c     | 119 ++++++++++++++---------------------------------------
 2 files changed, 33 insertions(+), 88 deletions(-)

diff --git a/gcc/dump-context.h b/gcc/dump-context.h
index a191e3a..f6df0b4 100644
--- a/gcc/dump-context.h
+++ b/gcc/dump-context.h
@@ -39,6 +39,8 @@ class dump_context
 
   ~dump_context ();
 
+  void dump_loc (dump_flags_t dump_kind, const dump_location_t &loc);
+
   void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
 			 gimple *gs, int spc);
 
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 176c9b8..3c8bc38 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -474,6 +474,27 @@ dump_context::~dump_context ()
   delete m_pending;
 }
 
+/* Print LOC to the appropriate dump destinations, given DUMP_KIND.
+   If optinfos are enabled, begin a new optinfo.  */
+
+void
+dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
+{
+  location_t srcloc = loc.get_location_t ();
+
+  if (dump_file && (dump_kind & pflags))
+    ::dump_loc (dump_kind, dump_file, srcloc);
+
+  if (alt_dump_file && (dump_kind & alt_flags))
+    ::dump_loc (dump_kind, alt_dump_file, srcloc);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = begin_next_optinfo (loc);
+      info.handle_dump_file_kind (dump_kind);
+    }
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
@@ -504,25 +525,8 @@ dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
 				    dump_flags_t extra_dump_flags,
 				    gimple *gs, int spc)
 {
-  location_t srcloc = loc.get_location_t ();
-  if (dump_file && (dump_kind & pflags))
-    {
-      dump_loc (dump_kind, dump_file, srcloc);
-      print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
-    }
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    {
-      dump_loc (dump_kind, alt_dump_file, srcloc);
-      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
-    }
-
-  if (optinfo_enabled_p ())
-    {
-      optinfo &info = begin_next_optinfo (loc);
-      info.handle_dump_file_kind (dump_kind);
-      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
-    }
+  dump_loc (dump_kind, loc);
+  dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
 }
 
 /* Dump gimple statement GS with SPC indentation spaces and
@@ -557,25 +561,8 @@ dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
 				    gimple *gs,
 				    int spc)
 {
-  location_t srcloc = loc.get_location_t ();
-  if (dump_file && (dump_kind & pflags))
-    {
-      dump_loc (dump_kind, dump_file, srcloc);
-      print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags);
-    }
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    {
-      dump_loc (dump_kind, alt_dump_file, srcloc);
-      print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
-    }
-
-  if (optinfo_enabled_p ())
-    {
-      optinfo &info = begin_next_optinfo (loc);
-      info.handle_dump_file_kind (dump_kind);
-      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
-    }
+  dump_loc (dump_kind, loc);
+  dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
 }
 
 
@@ -611,25 +598,8 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
 				     dump_flags_t extra_dump_flags,
 				     tree t)
 {
-  location_t srcloc = loc.get_location_t ();
-  if (dump_file && (dump_kind & pflags))
-    {
-      dump_loc (dump_kind, dump_file, srcloc);
-      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
-    }
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    {
-      dump_loc (dump_kind, alt_dump_file, srcloc);
-      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
-    }
-
-  if (optinfo_enabled_p ())
-    {
-      optinfo &info = begin_next_optinfo (loc);
-      info.handle_dump_file_kind (dump_kind);
-      info.add_tree (t, dump_flags | extra_dump_flags);
-    }
+  dump_loc (dump_kind, loc);
+  dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
@@ -672,35 +642,8 @@ dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
 				  const dump_location_t &loc,
 				  const char *format, va_list ap)
 {
-  location_t srcloc = loc.get_location_t ();
-
-  if (dump_file && (dump_kind & pflags))
-    {
-      dump_loc (dump_kind, dump_file, srcloc);
-      va_list aq;
-      va_copy (aq, ap);
-      vfprintf (dump_file, format, aq);
-      va_end (aq);
-    }
-
-  if (alt_dump_file && (dump_kind & alt_flags))
-    {
-      dump_loc (dump_kind, alt_dump_file, srcloc);
-      va_list aq;
-      va_copy (aq, ap);
-      vfprintf (alt_dump_file, format, aq);
-      va_end (aq);
-    }
-
-  if (optinfo_enabled_p ())
-    {
-      optinfo &info = begin_next_optinfo (loc);
-      info.handle_dump_file_kind (dump_kind);
-      va_list aq;
-      va_copy (aq, ap);
-      info.add_printf_va (format, aq);
-      va_end (aq);
-    }
+  dump_loc (dump_kind, loc);
+  dump_printf_va (dump_kind, format, ap);
 }
 
 /* Output VALUE in decimal to appropriate dump streams.  */
@@ -767,13 +710,13 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
 
   if (dump_file)
     {
-      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
+      ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
       fprintf (dump_file, "=== %s ===\n", name);
     }
 
   if (alt_dump_file)
     {
-      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
       fprintf (alt_dump_file, "=== %s ===\n", name);
     }
 
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH 0/5] dump_printf support for middle-end types
@ 2018-07-27 21:47 David Malcolm
  2018-07-27 21:47 ` [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file David Malcolm
                   ` (5 more replies)
  0 siblings, 6 replies; 29+ messages in thread
From: David Malcolm @ 2018-07-27 21:47 UTC (permalink / raw)
  To: gcc-patches; +Cc: David Malcolm

This patch kit converts dump_print and dump_printf_loc from using
fprintf etc internally to using a new pretty-printer
based on pp_format, which supports formatting middle-end types.

In particular, the following codes are implemented (in addition
to the standard pretty_printer ones):

   %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 (MSG_*, arg, TDF_SLIM).

Hence it becomes possible to convert e.g.:

  if (dump_enabled_p ())
    {
      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                       "not vectorized: different sized vector "
                       "types in statement, ");
      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, nunits_vectype);
      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
    }
  return false;

into a single call to dump_printf_loc:

  if (dump_enabled_p ())
    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                     "not vectorized: different sized vector "
                     "types in statement, %T and %T\n",
                     vectype, nunits_vectype);
  return false;

Unlike regular pretty-printers, this captures metadata for the
formatted chunks as appropriate, so that when written out to a
JSON optimization record, the relevant parts of the message are
labelled by type, and by source location (so that
e.g. %G is entirely equivalent to using dump_gimple_stmt).

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

I'm hoping to use this in a v3 of:
  "[PATCH 0/5] [RFC v2] Higher-level reporting of vectorization problems"
     https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html
where the above might become:
  return opt_result::failure_at (stmt,
                                 "not vectorized: different sized vector "
                                 "types in statement, %T and %T\n",
                                 vectype, nunits_vectype);
where opt_result::failure_at would encapsulate the "false", and
capture an opt_problem * (when dumps are enabled), for the reasons
discussed in that other kit.

David Malcolm (5):
  Simplify dump_context by adding a dump_loc member function
  dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
  C++: clean up cp_printer
  c-family: clean up the data tables in c-format.c
  Formatted printing for dump_* in the middle-end

 gcc/c-family/c-format.c                   |  159 +++--
 gcc/c-family/c-format.h                   |    1 +
 gcc/cp/error.c                            |   46 +-
 gcc/dump-context.h                        |   25 +-
 gcc/dumpfile.c                            | 1011 ++++++++++++++++++++++-------
 gcc/dumpfile.h                            |   54 +-
 gcc/optinfo-emit-json.cc                  |    2 +-
 gcc/optinfo.cc                            |  135 +---
 gcc/optinfo.h                             |   38 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |   19 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-10.c |   33 +-
 11 files changed, 998 insertions(+), 525 deletions(-)

-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 3/5] C++: clean up cp_printer
  2018-07-27 21:47 ` [PATCH 3/5] C++: clean up cp_printer David Malcolm
@ 2018-07-28 14:06   ` Jason Merrill
  0 siblings, 0 replies; 29+ messages in thread
From: Jason Merrill @ 2018-07-28 14:06 UTC (permalink / raw)
  To: David Malcolm; +Cc: gcc-patches List

OK.

On Sat, Jul 28, 2018 at 8:02 AM, David Malcolm <dmalcolm@redhat.com> wrote:
> This makes it easier to compare cp_printer with gcc_cxxdiag_char_table
> in c-format.c.
>
> No functional change intended.
>
> gcc/cp/ChangeLog:
>         * error.c (cp_printer): In the leading comment, move "%H" and "I"
>         into alphabetical order, and add missing "%G" and "K".  Within the
>         switch statement, move cases 'G', 'H', 'I' and 'K" so that the
>         cases are in alphabetical order.
> ---
>  gcc/cp/error.c | 46 ++++++++++++++++++++--------------------------
>  1 file changed, 20 insertions(+), 26 deletions(-)
>
> diff --git a/gcc/cp/error.c b/gcc/cp/error.c
> index b0d8e32..7a644fd 100644
> --- a/gcc/cp/error.c
> +++ b/gcc/cp/error.c
> @@ -4025,6 +4025,10 @@ defer_phase_2_of_type_diff (deferred_printed_type *deferred,
>     %D   declaration.
>     %E   expression.
>     %F   function declaration.
> +   %G   gcall *
> +   %H   type difference (from).
> +   %I   type difference (to).
> +   %K   tree
>     %L  language as used in extern "lang".
>     %O  binary operator.
>     %P   function parameter whose position is indicated by an integer.
> @@ -4032,9 +4036,7 @@ defer_phase_2_of_type_diff (deferred_printed_type *deferred,
>     %S   substitution (template + args)
>     %T   type.
>     %V   cv-qualifier.
> -   %X   exception-specification.
> -   %H   type difference (from)
> -   %I   type difference (to).  */
> +   %X   exception-specification.  */
>  static bool
>  cp_printer (pretty_printer *pp, text_info *text, const char *spec,
>             int precision, bool wide, bool set_locus, bool verbose,
> @@ -4076,6 +4078,21 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
>        break;
>      case 'E': result = expr_to_string (next_tree);             break;
>      case 'F': result = fndecl_to_string (next_tree, verbose);  break;
> +    case 'G':
> +      percent_G_format (text);
> +      return true;
> +    case 'H':
> +      defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree,
> +                                 buffer_ptr, verbose, *quoted);
> +      return true;
> +    case 'I':
> +      defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree,
> +                                 buffer_ptr, verbose, *quoted);
> +      return true;
> +    case 'K':
> +      t = va_arg (*text->args_ptr, tree);
> +      percent_K_format (text, t);
> +      return true;
>      case 'L': result = language_to_string (next_lang);         break;
>      case 'O': result = op_to_string (false, next_tcode);       break;
>      case 'P': result = parm_to_string (next_int);              break;
> @@ -4090,29 +4107,6 @@ cp_printer (pretty_printer *pp, text_info *text, const char *spec,
>      case 'V': result = cv_to_string (next_tree, verbose);      break;
>      case 'X': result = eh_spec_to_string (next_tree, verbose);  break;
>
> -    case 'G':
> -      percent_G_format (text);
> -      return true;
> -
> -    case 'K':
> -      t = va_arg (*text->args_ptr, tree);
> -      percent_K_format (text, t);
> -      return true;
> -
> -    case 'H':
> -      {
> -       defer_phase_2_of_type_diff (&postprocessor->m_type_a, next_tree,
> -                                   buffer_ptr, verbose, *quoted);
> -       return true;
> -      }
> -
> -    case 'I':
> -      {
> -       defer_phase_2_of_type_diff (&postprocessor->m_type_b, next_tree,
> -                                   buffer_ptr, verbose, *quoted);
> -       return true;
> -      }
> -
>      default:
>        return false;
>      }
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 0/5] dump_printf support for middle-end types
  2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
                   ` (4 preceding siblings ...)
  2018-07-27 21:47 ` [PATCH 3/5] C++: clean up cp_printer David Malcolm
@ 2018-07-31 12:50 ` Richard Biener
  2018-07-31 14:01   ` David Malcolm
  5 siblings, 1 reply; 29+ messages in thread
From: Richard Biener @ 2018-07-31 12:50 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Fri, Jul 27, 2018 at 11:47 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch kit converts dump_print and dump_printf_loc from using
> fprintf etc internally to using a new pretty-printer
> based on pp_format, which supports formatting middle-end types.
>
> In particular, the following codes are implemented (in addition
> to the standard pretty_printer ones):
>
>    %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 (MSG_*, arg, TDF_SLIM).
>
> Hence it becomes possible to convert e.g.:
>
>   if (dump_enabled_p ())
>     {
>       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
>                        "not vectorized: different sized vector "
>                        "types in statement, ");
>       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
>       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
>       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, nunits_vectype);
>       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
>     }
>   return false;
>
> into a single call to dump_printf_loc:
>
>   if (dump_enabled_p ())
>     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
>                      "not vectorized: different sized vector "
>                      "types in statement, %T and %T\n",
>                      vectype, nunits_vectype);
>   return false;
>
> Unlike regular pretty-printers, this captures metadata for the
> formatted chunks as appropriate, so that when written out to a
> JSON optimization record, the relevant parts of the message are
> labelled by type, and by source location (so that
> e.g. %G is entirely equivalent to using dump_gimple_stmt).
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

Nice!  I'm somehow missing 3/5?  Will look into the other ones now.

Richard.

> I'm hoping to use this in a v3 of:
>   "[PATCH 0/5] [RFC v2] Higher-level reporting of vectorization problems"
>      https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html
> where the above might become:
>   return opt_result::failure_at (stmt,
>                                  "not vectorized: different sized vector "
>                                  "types in statement, %T and %T\n",
>                                  vectype, nunits_vectype);
> where opt_result::failure_at would encapsulate the "false", and
> capture an opt_problem * (when dumps are enabled), for the reasons
> discussed in that other kit.
>
> David Malcolm (5):
>   Simplify dump_context by adding a dump_loc member function
>   dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
>   C++: clean up cp_printer
>   c-family: clean up the data tables in c-format.c
>   Formatted printing for dump_* in the middle-end
>
>  gcc/c-family/c-format.c                   |  159 +++--
>  gcc/c-family/c-format.h                   |    1 +
>  gcc/cp/error.c                            |   46 +-
>  gcc/dump-context.h                        |   25 +-
>  gcc/dumpfile.c                            | 1011 ++++++++++++++++++++++-------
>  gcc/dumpfile.h                            |   54 +-
>  gcc/optinfo-emit-json.cc                  |    2 +-
>  gcc/optinfo.cc                            |  135 +---
>  gcc/optinfo.h                             |   38 +-
>  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |   19 +-
>  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |   33 +-
>  11 files changed, 998 insertions(+), 525 deletions(-)
>
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 1/5] Simplify dump_context by adding a dump_loc member function
  2018-07-27 21:47 ` [PATCH 1/5] Simplify dump_context by adding a dump_loc member function David Malcolm
@ 2018-07-31 12:51   ` Richard Biener
  0 siblings, 0 replies; 29+ messages in thread
From: Richard Biener @ 2018-07-31 12:51 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Fri, Jul 27, 2018 at 11:48 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch removes some duplicated code in dumpfile.c by
> reimplementing the various dump_foo_loc calls in terms of dump_foo.

OK.

Richard.

> gcc/ChangeLog:
>         * dump-context.h (dump_context::dump_loc): New decl.
>         * dumpfile.c (dump_context::dump_loc): New member function.
>         (dump_context::dump_gimple_stmt_loc): Reimplement using dump_loc
>         and dump_gimple_stmt.
>         (dump_context::dump_gimple_expr_loc): Likewise, using
>         dump_gimple_expr.
>         (dump_context::dump_generic_expr_loc): Likewise, using
>         dump_generic_expr.
>         (dump_context::dump_printf_loc_va): Likewise, using
>         dump_printf_va.
>         (dump_context::begin_scope): Explicitly using the global function
>         "dump_loc", rather than the member function.
> ---
>  gcc/dump-context.h |   2 +
>  gcc/dumpfile.c     | 119 ++++++++++++++---------------------------------------
>  2 files changed, 33 insertions(+), 88 deletions(-)
>
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> index a191e3a..f6df0b4 100644
> --- a/gcc/dump-context.h
> +++ b/gcc/dump-context.h
> @@ -39,6 +39,8 @@ class dump_context
>
>    ~dump_context ();
>
> +  void dump_loc (dump_flags_t dump_kind, const dump_location_t &loc);
> +
>    void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>                          gimple *gs, int spc);
>
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 176c9b8..3c8bc38 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -474,6 +474,27 @@ dump_context::~dump_context ()
>    delete m_pending;
>  }
>
> +/* Print LOC to the appropriate dump destinations, given DUMP_KIND.
> +   If optinfos are enabled, begin a new optinfo.  */
> +
> +void
> +dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
> +{
> +  location_t srcloc = loc.get_location_t ();
> +
> +  if (dump_file && (dump_kind & pflags))
> +    ::dump_loc (dump_kind, dump_file, srcloc);
> +
> +  if (alt_dump_file && (dump_kind & alt_flags))
> +    ::dump_loc (dump_kind, alt_dump_file, srcloc);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.handle_dump_file_kind (dump_kind);
> +    }
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>
> @@ -504,25 +525,8 @@ dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
>                                     dump_flags_t extra_dump_flags,
>                                     gimple *gs, int spc)
>  {
> -  location_t srcloc = loc.get_location_t ();
> -  if (dump_file && (dump_kind & pflags))
> -    {
> -      dump_loc (dump_kind, dump_file, srcloc);
> -      print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    {
> -      dump_loc (dump_kind, alt_dump_file, srcloc);
> -      print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (optinfo_enabled_p ())
> -    {
> -      optinfo &info = begin_next_optinfo (loc);
> -      info.handle_dump_file_kind (dump_kind);
> -      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> -    }
> +  dump_loc (dump_kind, loc);
> +  dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
>  }
>
>  /* Dump gimple statement GS with SPC indentation spaces and
> @@ -557,25 +561,8 @@ dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
>                                     gimple *gs,
>                                     int spc)
>  {
> -  location_t srcloc = loc.get_location_t ();
> -  if (dump_file && (dump_kind & pflags))
> -    {
> -      dump_loc (dump_kind, dump_file, srcloc);
> -      print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    {
> -      dump_loc (dump_kind, alt_dump_file, srcloc);
> -      print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (optinfo_enabled_p ())
> -    {
> -      optinfo &info = begin_next_optinfo (loc);
> -      info.handle_dump_file_kind (dump_kind);
> -      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> -    }
> +  dump_loc (dump_kind, loc);
> +  dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
>  }
>
>
> @@ -611,25 +598,8 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
>                                      dump_flags_t extra_dump_flags,
>                                      tree t)
>  {
> -  location_t srcloc = loc.get_location_t ();
> -  if (dump_file && (dump_kind & pflags))
> -    {
> -      dump_loc (dump_kind, dump_file, srcloc);
> -      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    {
> -      dump_loc (dump_kind, alt_dump_file, srcloc);
> -      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> -    }
> -
> -  if (optinfo_enabled_p ())
> -    {
> -      optinfo &info = begin_next_optinfo (loc);
> -      info.handle_dump_file_kind (dump_kind);
> -      info.add_tree (t, dump_flags | extra_dump_flags);
> -    }
> +  dump_loc (dump_kind, loc);
> +  dump_generic_expr (dump_kind, extra_dump_flags, t);
>  }
>
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
> @@ -672,35 +642,8 @@ dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
>                                   const dump_location_t &loc,
>                                   const char *format, va_list ap)
>  {
> -  location_t srcloc = loc.get_location_t ();
> -
> -  if (dump_file && (dump_kind & pflags))
> -    {
> -      dump_loc (dump_kind, dump_file, srcloc);
> -      va_list aq;
> -      va_copy (aq, ap);
> -      vfprintf (dump_file, format, aq);
> -      va_end (aq);
> -    }
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    {
> -      dump_loc (dump_kind, alt_dump_file, srcloc);
> -      va_list aq;
> -      va_copy (aq, ap);
> -      vfprintf (alt_dump_file, format, aq);
> -      va_end (aq);
> -    }
> -
> -  if (optinfo_enabled_p ())
> -    {
> -      optinfo &info = begin_next_optinfo (loc);
> -      info.handle_dump_file_kind (dump_kind);
> -      va_list aq;
> -      va_copy (aq, ap);
> -      info.add_printf_va (format, aq);
> -      va_end (aq);
> -    }
> +  dump_loc (dump_kind, loc);
> +  dump_printf_va (dump_kind, format, ap);
>  }
>
>  /* Output VALUE in decimal to appropriate dump streams.  */
> @@ -767,13 +710,13 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
>
>    if (dump_file)
>      {
> -      dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> +      ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
>        fprintf (dump_file, "=== %s ===\n", name);
>      }
>
>    if (alt_dump_file)
>      {
> -      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
>        fprintf (alt_dump_file, "=== %s ===\n", name);
>      }
>
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
  2018-07-27 21:47 ` [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file David Malcolm
@ 2018-07-31 12:54   ` Richard Biener
  2018-07-31 15:34     ` David Malcolm
  0 siblings, 1 reply; 29+ messages in thread
From: Richard Biener @ 2018-07-31 12:54 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Fri, Jul 27, 2018 at 11:48 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> With the addition of optinfo, the various dump_* calls had three parts:
> - optionally print to dump_file
> - optionally print to alt_dump_file
> - optionally make an optinfo_item and add it to the pending optinfo,
>   creating it for dump_*_loc calls.
>
> However, this split makes it difficult to implement the formatted dumps
> later in patch kit, so as enabling work towards that, this patch removes
> the above split, so that all dumping within the dump_* API goes through
> optinfo_item.
>
> In order to ensure that the dumps to dump_file and alt_dump_file are
> processed immediately (rather than being buffered within the pending
> optinfo for consolidation), this patch introduces the idea of "immediate"
> optinfo_item destinations vs "non-immediate" destinations.
>
> The patch also adds selftest coverage of what's printed, and of scopes.
>
> This adds two allocations per dump_* call when dumping is enabled.
> I'm assuming that this isn't a problem, as dump_enabled_p is normally
> false.  There are ways of optimizing it if it is an issue (by making
> optinfo_item instances become temporaries that borrow the underlying
> buffer), but they require nontrivial changes, so I'd prefer to leave
> that for another patch kit, if it becomes necessary.

Yeah, I guess that's OK given we can consolidate quite some calls after
your patch anyways.  Using alloca + placement new would be possible
as well I guess?

OK.

Richard.

> gcc/ChangeLog:
>         * dump-context.h: Include "pretty-print.h".
>         (dump_context::refresh_dumps_are_enabled): New decl.
>         (dump_context::emit_item): New decl.
>         (class dump_context): Add fields "m_test_pp" and
>         "m_test_pp_flags".
>         (temp_dump_context::temp_dump_context): Add param "test_pp_flags".
>         (temp_dump_context::get_dumped_text): New decl.
>         (class temp_dump_context): Add field "m_pp".
>         * dumpfile.c (refresh_dumps_are_enabled): Convert to...
>         (dump_context::refresh_dumps_are_enabled): ...and add a test for
>         m_test_pp.
>         (set_dump_file): Update for above change.
>         (set_alt_dump_file): Likewise.
>         (dump_loc): New overload, taking a pretty_printer *.
>         (dump_context::dump_loc): Call end_any_optinfo.  Dump the location
>         to any test pretty-printer.
>         (make_item_for_dump_gimple_stmt): New function, adapted from
>         optinfo::add_gimple_stmt.
>         (dump_context::dump_gimple_stmt): Call it, and use the result,
>         eliminating the direct usage of dump_file and alt_dump_file in
>         favor of indirectly using them via emit_item.
>         (make_item_for_dump_gimple_expr): New function, adapted from
>         optinfo::add_gimple_expr.
>         (dump_context::dump_gimple_expr): Call it, and use the result,
>         eliminating the direct usage of dump_file and alt_dump_file in
>         favor of indirectly using them via emit_item.
>         (make_item_for_dump_generic_expr): New function, adapted from
>         optinfo::add_tree.
>         (dump_context::dump_generic_expr): Call it, and use the result,
>         eliminating the direct usage of dump_file and alt_dump_file in
>         favor of indirectly using them via emit_item.
>         (make_item_for_dump_printf_va): New function, adapted from
>         optinfo::add_printf_va.
>         (make_item_for_dump_printf): New function.
>         (dump_context::dump_printf_va): Call make_item_for_dump_printf_va,
>         and use the result, eliminating the direct usage of dump_file and
>         alt_dump_file in favor of indirectly using them via emit_item.
>         (make_item_for_dump_dec): New function.
>         (dump_context::dump_dec): Call it, and use the result,
>         eliminating the direct usage of dump_file and alt_dump_file in
>         favor of indirectly using them via emit_item.
>         (make_item_for_dump_symtab_node): New function, adapted from
>         optinfo::add_symtab_node.
>         (dump_context::dump_symtab_node): Call it, and use the result,
>         eliminating the direct usage of dump_file and alt_dump_file in
>         favor of indirectly using them via emit_item.
>         (dump_context::begin_scope): Reimplement, avoiding direct usage
>         of dump_file and alt_dump_file in favor of indirectly using them
>         via emit_item.
>         (dump_context::emit_item): New member function.
>         (temp_dump_context::temp_dump_context): Add param "test_pp_flags".
>         Set up test pretty-printer on the underlying context.  Call
>         refresh_dumps_are_enabled.
>         (temp_dump_context::~temp_dump_context): Call
>         refresh_dumps_are_enabled.
>         (temp_dump_context::get_dumped_text): New member function.
>         (selftest::verify_dumped_text): New function.
>         (ASSERT_DUMPED_TEXT_EQ): New macro.
>         (selftest::test_capture_of_dump_calls): Run all tests twice, with
>         and then without optinfo enabled.  Add uses of
>         ASSERT_DUMPED_TEXT_EQ to all tests.  Add test of nested scopes.
>         * dumpfile.h: Update comment for the dump_* API.
>         * optinfo-emit-json.cc
>         (selftest::test_building_json_from_dump_calls): Update for new
>         param for temp_dump_context ctor.
>         * optinfo.cc (optinfo_item::optinfo_item): Remove "owned" param
>         and "m_owned" field.
>         (optinfo_item::~optinfo_item): Likewise.
>         (optinfo::add_item): New member function.
>         (optinfo::emit): Update comment.
>         (optinfo::add_string): Delete.
>         (optinfo::add_printf): Delete.
>         (optinfo::add_printf_va): Delete.
>         (optinfo::add_gimple_stmt): Delete.
>         (optinfo::add_gimple_expr): Delete.
>         (optinfo::add_tree): Delete.
>         (optinfo::add_symtab_node): Delete.
>         (optinfo::add_dec): Delete.
>         * optinfo.h (class dump_context): New forward decl.
>         (optinfo::add_item): New decl.
>         (optinfo::add_string): Delete.
>         (optinfo::add_printf): Delete.
>         (optinfo::add_printf_va): Delete.
>         (optinfo::add_gimple_stmt): Delete.
>         (optinfo::add_gimple_expr): Delete.
>         (optinfo::add_tree): Delete.
>         (optinfo::add_symtab_node): Delete.
>         (optinfo::add_dec): Delete.
>         (optinfo::add_poly_int): Delete.
>         (optinfo_item::optinfo_item): Remove "owned" param.
>         (class optinfo_item): Remove field "m_owned".
> ---
>  gcc/dump-context.h       |  16 +-
>  gcc/dumpfile.c           | 620 ++++++++++++++++++++++++++++++++++-------------
>  gcc/dumpfile.h           |  34 ++-
>  gcc/optinfo-emit-json.cc |   2 +-
>  gcc/optinfo.cc           | 135 ++---------
>  gcc/optinfo.h            |  38 +--
>  6 files changed, 505 insertions(+), 340 deletions(-)
>
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> index f6df0b4..f40ea14 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 "pretty-print.h"
> +
>  /* A class for handling the various dump_* calls.
>
>     In particular, this class has responsibility for consolidating
> @@ -39,6 +41,8 @@ class dump_context
>
>    ~dump_context ();
>
> +  void refresh_dumps_are_enabled ();
> +
>    void dump_loc (dump_flags_t dump_kind, const dump_location_t &loc);
>
>    void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> @@ -93,6 +97,8 @@ class dump_context
>
>    void end_any_optinfo ();
>
> +  void emit_item (optinfo_item *item, dump_flags_t dump_kind);
> +
>   private:
>    optinfo &ensure_pending_optinfo ();
>    optinfo &begin_next_optinfo (const dump_location_t &loc);
> @@ -108,6 +114,11 @@ class dump_context
>       if any.  */
>    optinfo *m_pending;
>
> +  /* For use in selftests: if non-NULL, then items are to be printed
> +     to this, using the given flags.  */
> +  pretty_printer *m_test_pp;
> +  dump_flags_t m_test_pp_flags;
> +
>    /* The currently active dump_context, for use by the dump_* API calls.  */
>    static dump_context *s_current;
>
> @@ -123,13 +134,16 @@ class dump_context
>  class temp_dump_context
>  {
>   public:
> -  temp_dump_context (bool forcibly_enable_optinfo);
> +  temp_dump_context (bool forcibly_enable_optinfo,
> +                    dump_flags_t test_pp_flags);
>    ~temp_dump_context ();
>
>    /* Support for selftests.  */
>    optinfo *get_pending_optinfo () const { return m_context.m_pending; }
> +  const char *get_dumped_text ();
>
>   private:
> +  pretty_printer m_pp;
>    dump_context m_context;
>    dump_context *m_saved;
>    bool m_saved_flag_remarks;
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 3c8bc38..10e9cab 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -63,15 +63,6 @@ dump_flags_t dump_flags;
>  bool dumps_are_enabled = false;
>
>
> -/* Update the "dumps_are_enabled" global; to be called whenever dump_file
> -   or alt_dump_file change.  */
> -
> -static void
> -refresh_dumps_are_enabled ()
> -{
> -  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
> -}
> -
>  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
>     global.  */
>
> @@ -80,7 +71,7 @@ set_dump_file (FILE *new_dump_file)
>  {
>    dumpfile_ensure_any_optinfo_are_flushed ();
>    dump_file = new_dump_file;
> -  refresh_dumps_are_enabled ();
> +  dump_context::get ().refresh_dumps_are_enabled ();
>  }
>
>  /* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the "dumps_are_enabled"
> @@ -91,7 +82,7 @@ set_alt_dump_file (FILE *new_alt_dump_file)
>  {
>    dumpfile_ensure_any_optinfo_are_flushed ();
>    alt_dump_file = new_alt_dump_file;
> -  refresh_dumps_are_enabled ();
> +  dump_context::get ().refresh_dumps_are_enabled ();
>  }
>
>  #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
> @@ -465,6 +456,27 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>      }
>  }
>
> +/* Print source location to PP if enabled.  */
> +
> +static void
> +dump_loc (dump_flags_t dump_kind, pretty_printer *pp, source_location loc)
> +{
> +  if (dump_kind)
> +    {
> +      if (LOCATION_LOCUS (loc) > BUILTINS_LOCATION)
> +       pp_printf (pp, "%s:%d:%d: note: ", LOCATION_FILE (loc),
> +                  LOCATION_LINE (loc), LOCATION_COLUMN (loc));
> +      else if (current_function_decl)
> +       pp_printf (pp, "%s:%d:%d: note: ",
> +                  DECL_SOURCE_FILE (current_function_decl),
> +                  DECL_SOURCE_LINE (current_function_decl),
> +                  DECL_SOURCE_COLUMN (current_function_decl));
> +      /* Indentation based on scope depth.  */
> +      for (unsigned i = 0; i < get_dump_scope_depth (); i++)
> +       pp_character (pp, ' ');
> +    }
> +}
> +
>  /* Implementation of dump_context member functions.  */
>
>  /* dump_context's dtor.  */
> @@ -474,12 +486,24 @@ dump_context::~dump_context ()
>    delete m_pending;
>  }
>
> +/* Update the "dumps_are_enabled" global; to be called whenever dump_file
> +   or alt_dump_file change, or when changing dump_context in selftests.  */
> +
> +void
> +dump_context::refresh_dumps_are_enabled ()
> +{
> +  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ()
> +                      || m_test_pp);
> +}
> +
>  /* Print LOC to the appropriate dump destinations, given DUMP_KIND.
>     If optinfos are enabled, begin a new optinfo.  */
>
>  void
>  dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
>  {
> +  end_any_optinfo ();
> +
>    location_t srcloc = loc.get_location_t ();
>
>    if (dump_file && (dump_kind & pflags))
> @@ -488,6 +512,10 @@ dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
>    if (alt_dump_file && (dump_kind & alt_flags))
>      ::dump_loc (dump_kind, alt_dump_file, srcloc);
>
> +  /* Support for temp_dump_context in selftests.  */
> +  if (m_test_pp && (dump_kind & m_test_pp_flags))
> +    ::dump_loc (dump_kind, m_test_pp, srcloc);
> +
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = begin_next_optinfo (loc);
> @@ -495,6 +523,22 @@ dump_context::dump_loc (dump_flags_t dump_kind, const dump_location_t &loc)
>      }
>  }
>
> +/* Make an item for the given dump call, equivalent to print_gimple_stmt.  */
> +
> +static optinfo_item *
> +make_item_for_dump_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)));
> +  return item;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>
> @@ -503,18 +547,18 @@ dump_context::dump_gimple_stmt (dump_flags_t dump_kind,
>                                 dump_flags_t extra_dump_flags,
>                                 gimple *gs, int spc)
>  {
> -  if (dump_file && (dump_kind & pflags))
> -    print_gimple_stmt (dump_file, gs, spc, dump_flags | extra_dump_flags);
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    print_gimple_stmt (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> +  optinfo_item *item
> +    = make_item_for_dump_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> +  emit_item (item, dump_kind);
>
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
>        info.handle_dump_file_kind (dump_kind);
> -      info.add_gimple_stmt (gs, spc, dump_flags | extra_dump_flags);
> +      info.add_item (item);
>      }
> +  else
> +    delete item;
>  }
>
>  /* Similar to dump_gimple_stmt, except additionally print source location.  */
> @@ -529,6 +573,22 @@ dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
>    dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
>  }
>
> +/* Make an item for the given dump call, equivalent to print_gimple_expr.  */
> +
> +static optinfo_item *
> +make_item_for_dump_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)));
> +  return item;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
>     Do not terminate with a newline or semicolon.  */
> @@ -538,18 +598,18 @@ dump_context::dump_gimple_expr (dump_flags_t dump_kind,
>                                 dump_flags_t extra_dump_flags,
>                                 gimple *gs, int spc)
>  {
> -  if (dump_file && (dump_kind & pflags))
> -    print_gimple_expr (dump_file, gs, spc, dump_flags | extra_dump_flags);
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    print_gimple_expr (alt_dump_file, gs, spc, dump_flags | extra_dump_flags);
> +  optinfo_item *item
> +    = make_item_for_dump_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> +  emit_item (item, dump_kind);
>
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
>        info.handle_dump_file_kind (dump_kind);
> -      info.add_gimple_expr (gs, spc, dump_flags | extra_dump_flags);
> +      info.add_item (item);
>      }
> +  else
> +    delete item;
>  }
>
>  /* Similar to dump_gimple_expr, except additionally print source location.  */
> @@ -565,6 +625,25 @@ dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
>    dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
>  }
>
> +/* Make an item for the given dump call, equivalent to print_generic_expr.  */
> +
> +static optinfo_item *
> +make_item_for_dump_generic_expr (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)));
> +  return item;
> +}
>
>  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
>     DUMP_KIND is enabled.  */
> @@ -574,18 +653,18 @@ dump_context::dump_generic_expr (dump_flags_t dump_kind,
>                                  dump_flags_t extra_dump_flags,
>                                  tree t)
>  {
> -  if (dump_file && (dump_kind & pflags))
> -      print_generic_expr (dump_file, t, dump_flags | extra_dump_flags);
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -      print_generic_expr (alt_dump_file, t, dump_flags | extra_dump_flags);
> +  optinfo_item *item
> +    = make_item_for_dump_generic_expr (t, dump_flags | extra_dump_flags);
> +  emit_item (item, dump_kind);
>
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
>        info.handle_dump_file_kind (dump_kind);
> -      info.add_tree (t, dump_flags | extra_dump_flags);
> +      info.add_item (item);
>      }
> +  else
> +    delete item;
>  }
>
>
> @@ -602,36 +681,56 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
>    dump_generic_expr (dump_kind, extra_dump_flags, t);
>  }
>
> +/* Make an item for the given dump call.  */
> +
> +static optinfo_item *
> +make_item_for_dump_printf_va (const char *format, va_list ap)
> +  ATTRIBUTE_PRINTF (1, 0);
> +
> +static optinfo_item *
> +make_item_for_dump_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);
> +  return item;
> +}
> +
> +/* Make an item for the given dump call.  */
> +
> +static optinfo_item *
> +make_item_for_dump_printf (const char *format, ...)
> +  ATTRIBUTE_PRINTF (1, 2);
> +
> +static optinfo_item *
> +make_item_for_dump_printf (const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  optinfo_item *item
> +    = make_item_for_dump_printf_va (format, ap);
> +  va_end (ap);
> +  return item;
> +}
> +
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
>  dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
>                               va_list ap)
>  {
> -  if (dump_file && (dump_kind & pflags))
> -    {
> -      va_list aq;
> -      va_copy (aq, ap);
> -      vfprintf (dump_file, format, aq);
> -      va_end (aq);
> -    }
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    {
> -      va_list aq;
> -      va_copy (aq, ap);
> -      vfprintf (alt_dump_file, format, aq);
> -      va_end (aq);
> -    }
> +  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> +  emit_item (item, dump_kind);
>
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
> -      va_list aq;
> -      va_copy (aq, ap);
> -      info.add_printf_va (format, aq);
> -      va_end (aq);
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_item (item);
>      }
> +  else
> +    delete item;
>  }
>
>  /* Similar to dump_printf, except source location is also printed, and
> @@ -646,26 +745,64 @@ dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
>    dump_printf_va (dump_kind, format, ap);
>  }
>
> -/* Output VALUE in decimal to appropriate dump streams.  */
> +/* Make an item for the given dump call, equivalent to print_dec.  */
>
>  template<unsigned int N, typename C>
> -void
> -dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +static optinfo_item *
> +make_item_for_dump_dec (const poly_int<N, C> &value)
>  {
>    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
>    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> -  if (dump_file && (dump_kind & pflags))
> -    print_dec (value, dump_file, sgn);
>
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    print_dec (value, alt_dump_file, sgn);
> +  pretty_printer pp;
> +
> +  if (value.is_constant ())
> +    pp_wide_int (&pp, value.coeffs[0], sgn);
> +  else
> +    {
> +      pp_character (&pp, '[');
> +      for (unsigned int i = 0; i < N; ++i)
> +       {
> +         pp_wide_int (&pp, value.coeffs[i], sgn);
> +         pp_character (&pp, i == N - 1 ? ']' : ',');
> +       }
> +    }
> +
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +                       xstrdup (pp_formatted_text (&pp)));
> +  return item;
> +}
> +
> +/* Output VALUE in decimal to appropriate dump streams.  */
> +
> +template<unsigned int N, typename C>
> +void
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +{
> +  optinfo_item *item = make_item_for_dump_dec (value);
> +  emit_item (item, dump_kind);
>
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
>        info.handle_dump_file_kind (dump_kind);
> -      info.add_poly_int<N,C> (value);
> +      info.add_item (item);
>      }
> +  else
> +    delete item;
> +}
> +
> +/* Make an item for the given dump call.  */
> +
> +static optinfo_item *
> +make_item_for_dump_symtab_node (symtab_node *node)
> +{
> +  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> +                       xstrdup (node->dump_name ()));
> +  return item;
>  }
>
>  /* Output the name of NODE on appropriate dump streams.  */
> @@ -673,18 +810,17 @@ dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>  void
>  dump_context::dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
>  {
> -  if (dump_file && (dump_kind & pflags))
> -    fprintf (dump_file, "%s", node->dump_name ());
> -
> -  if (alt_dump_file && (dump_kind & alt_flags))
> -    fprintf (alt_dump_file, "%s", node->dump_name ());
> +  optinfo_item *item = make_item_for_dump_symtab_node (node);
> +  emit_item (item, dump_kind);
>
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
>        info.handle_dump_file_kind (dump_kind);
> -      info.add_symtab_node (node);
> +      info.add_item (item);
>      }
> +  else
> +    delete item;
>  }
>
>  /* Get the current dump scope-nesting depth.
> @@ -705,28 +841,28 @@ dump_context::get_scope_depth () const
>  void
>  dump_context::begin_scope (const char *name, const dump_location_t &loc)
>  {
> -  /* Specialcase, to avoid going through dump_printf_loc,
> -     so that we can create a optinfo of kind OPTINFO_KIND_SCOPE.  */
> -
>    if (dump_file)
> -    {
> -      ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> -      fprintf (dump_file, "=== %s ===\n", name);
> -    }
> +    ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
>
>    if (alt_dump_file)
> -    {
> -      ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> -      fprintf (alt_dump_file, "=== %s ===\n", name);
> -    }
> +    ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +
> +  /* Support for temp_dump_context in selftests.  */
> +  if (m_test_pp)
> +    ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
> +
> +  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n", name);
> +  emit_item (item, MSG_NOTE);
>
>    if (optinfo_enabled_p ())
>      {
> +      optinfo &info = begin_next_optinfo (loc);
> +      info.m_kind = OPTINFO_KIND_SCOPE;
> +      info.add_item (item);
>        end_any_optinfo ();
> -      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> -      info.add_printf ("=== %s ===", name);
> -      info.emit ();
>      }
> +  else
> +    delete item;
>
>    m_scope_depth++;
>  }
> @@ -776,6 +912,23 @@ dump_context::end_any_optinfo ()
>    m_pending = NULL;
>  }
>
> +/* Emit ITEM to all item destinations (those that don't require
> +   consolidation into optinfo instances).  */
> +
> +void
> +dump_context::emit_item (optinfo_item *item, dump_flags_t dump_kind)
> +{
> +  if (dump_file && (dump_kind & pflags))
> +    fprintf (dump_file, "%s", item->get_text ());
> +
> +  if (alt_dump_file && (dump_kind & alt_flags))
> +    fprintf (alt_dump_file, "%s", item->get_text ());
> +
> +  /* Support for temp_dump_context in selftests.  */
> +  if (m_test_pp && (dump_kind & m_test_pp_flags))
> +    pp_string (m_test_pp, item->get_text ());
> +}
> +
>  /* The current singleton dump_context, and its default.  */
>
>  dump_context *dump_context::s_current = &dump_context::s_default;
> @@ -1510,12 +1663,18 @@ enable_rtl_dump_file (void)
>  /* temp_dump_context's ctor.  Temporarily override the dump_context
>     (to forcibly enable optinfo-generation).  */
>
> -temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
> +temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo,
> +                                     dump_flags_t test_pp_flags)
> +
>  : m_context (),
>    m_saved (&dump_context ().get ())
>  {
>    dump_context::s_current = &m_context;
>    m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> +  m_context.m_test_pp = &m_pp;
> +  m_context.m_test_pp_flags = test_pp_flags;
> +
> +  dump_context::get ().refresh_dumps_are_enabled ();
>  }
>
>  /* temp_dump_context's dtor.  Restore the saved dump_context.  */
> @@ -1523,6 +1682,16 @@ temp_dump_context::temp_dump_context (bool forcibly_enable_optinfo)
>  temp_dump_context::~temp_dump_context ()
>  {
>    dump_context::s_current = m_saved;
> +
> +  dump_context::get ().refresh_dumps_are_enabled ();
> +}
> +
> +/* 0-terminate the text dumped so far, and return it.  */
> +
> +const char *
> +temp_dump_context::get_dumped_text ()
> +{
> +  return pp_formatted_text (&m_pp);
>  }
>
>  namespace selftest {
> @@ -1561,6 +1730,29 @@ test_impl_location ()
>  #endif
>  }
>
> +/* Verify that the text dumped so far in CONTEXT equals
> +   EXPECTED_TEXT, using LOC for the location of any failure.
> +   As a side-effect, the internal buffer is 0-terminated.  */
> +
> +static void
> +verify_dumped_text (const location &loc,
> +                   temp_dump_context *context,
> +                   const char *expected_text)
> +{
> +  gcc_assert (context);
> +  ASSERT_STREQ_AT (loc, context->get_dumped_text (),
> +                  expected_text);
> +}
> +
> +/* Verify that the text dumped so far in CONTEXT equals
> +   EXPECTED_TEXT.
> +   As a side-effect, the internal buffer is 0-terminated.  */
> +
> +#define ASSERT_DUMPED_TEXT_EQ(CONTEXT, EXPECTED_TEXT)                  \
> +  SELFTEST_BEGIN_STMT                                                  \
> +    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT), (EXPECTED_TEXT)); \
> +  SELFTEST_END_STMT
> +
>  /* Verify that ITEM has the expected values.  */
>
>  static void
> @@ -1611,116 +1803,198 @@ test_capture_of_dump_calls (const line_table_case &case_)
>    linemap_line_start (line_table, 5, 100);
>    linemap_add (line_table, LC_LEAVE, false, NULL, 0);
>    location_t where = linemap_position_for_column (line_table, 10);
> +  if (where > LINE_MAP_MAX_LOCATION_WITH_COLS)
> +    return;
>
>    dump_location_t loc = dump_location_t::from_location_t (where);
>
> -  /* Test of dump_printf.  */
> -  {
> -    temp_dump_context tmp (true);
> -    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> -
> -    optinfo *info = tmp.get_pending_optinfo ();
> -    ASSERT_TRUE (info != NULL);
> -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> -    ASSERT_EQ (info->num_items (), 1);
> -    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
> -  }
> -
> -  /* Tree, via dump_generic_expr.  */
> -  {
> -    temp_dump_context tmp (true);
> -    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> -    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> -
> -    optinfo *info = tmp.get_pending_optinfo ();
> -    ASSERT_TRUE (info != NULL);
> -    ASSERT_EQ (info->get_location_t (), where);
> -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> -    ASSERT_EQ (info->num_items (), 2);
> -    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> -    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
> -  }
> -
> -  /* Tree, via dump_generic_expr_loc.  */
> -  {
> -    temp_dump_context tmp (true);
> -    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
> -
> -    optinfo *info = tmp.get_pending_optinfo ();
> -    ASSERT_TRUE (info != NULL);
> -    ASSERT_EQ (info->get_location_t (), where);
> -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> -    ASSERT_EQ (info->num_items (), 1);
> -    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
> -  }
> -
> -  /* Gimple.  */
> -  {
> -    greturn *stmt = gimple_build_return (NULL);
> -    gimple_set_location (stmt, where);
> -
> -    /* dump_gimple_stmt_loc.  */
> -    {
> -      temp_dump_context tmp (true);
> -      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> -
> -      optinfo *info = tmp.get_pending_optinfo ();
> -      ASSERT_TRUE (info != NULL);
> -      ASSERT_EQ (info->num_items (), 1);
> -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> -    }
> -
> -    /* dump_gimple_stmt.  */
> -    {
> -      temp_dump_context tmp (true);
> -      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> +  greturn *stmt = gimple_build_return (NULL);
> +  gimple_set_location (stmt, where);
>
> -      optinfo *info = tmp.get_pending_optinfo ();
> -      ASSERT_TRUE (info != NULL);
> -      ASSERT_EQ (info->num_items (), 1);
> -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> -    }
> -
> -    /* dump_gimple_expr_loc.  */
> +  /* Run all tests twice, with and then without optinfo enabled, to ensure
> +     that immediate destinations vs optinfo-based destinations both
> +     work, independently of each other, with no leaks.  */
> +  for (int i = 0 ; i < 2; i++)
>      {
> -      temp_dump_context tmp (true);
> -      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +      bool with_optinfo = (i == 0);
> +
> +      /* Test of dump_printf.  */
> +      {
> +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> +       dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "int: 42 str: foo");
> +       if (with_optinfo)
> +         {
> +           optinfo *info = tmp.get_pending_optinfo ();
> +           ASSERT_TRUE (info != NULL);
> +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +           ASSERT_EQ (info->num_items (), 1);
> +           ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
> +         }
> +      }
> +
> +      /* Tree, via dump_generic_expr.  */
> +      {
> +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> +       dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> +       dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: test of tree: 0");
> +       if (with_optinfo)
> +         {
> +           optinfo *info = tmp.get_pending_optinfo ();
> +           ASSERT_TRUE (info != NULL);
> +           ASSERT_EQ (info->get_location_t (), where);
> +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +           ASSERT_EQ (info->num_items (), 2);
> +           ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> +           ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
> +         }
> +      }
> +
> +      /* Tree, via dump_generic_expr_loc.  */
> +      {
> +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> +       dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM, integer_one_node);
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: 1");
> +       if (with_optinfo)
> +         {
> +           optinfo *info = tmp.get_pending_optinfo ();
> +           ASSERT_TRUE (info != NULL);
> +           ASSERT_EQ (info->get_location_t (), where);
> +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> +           ASSERT_EQ (info->num_items (), 1);
> +           ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
> +         }
> +      }
> +
> +      /* Gimple.  */
> +      {
> +       /* dump_gimple_stmt_loc.  */
> +       {
> +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> +         dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +
> +         ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: return;\n");
> +         if (with_optinfo)
> +           {
> +             optinfo *info = tmp.get_pending_optinfo ();
> +             ASSERT_TRUE (info != NULL);
> +             ASSERT_EQ (info->num_items (), 1);
> +             ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> +           }
> +       }
>
> -      optinfo *info = tmp.get_pending_optinfo ();
> -      ASSERT_TRUE (info != NULL);
> -      ASSERT_EQ (info->num_items (), 1);
> -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> -    }
> +       /* dump_gimple_stmt.  */
> +       {
> +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> +         dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> +
> +         ASSERT_DUMPED_TEXT_EQ (tmp, "return;\n");
> +         if (with_optinfo)
> +           {
> +             optinfo *info = tmp.get_pending_optinfo ();
> +             ASSERT_TRUE (info != NULL);
> +             ASSERT_EQ (info->num_items (), 1);
> +             ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> +           }
> +       }
>
> -    /* dump_gimple_expr.  */
> -    {
> -      temp_dump_context tmp (true);
> -      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> +       /* dump_gimple_expr_loc.  */
> +       {
> +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> +         dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> +
> +         ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: return;");
> +         if (with_optinfo)
> +           {
> +             optinfo *info = tmp.get_pending_optinfo ();
> +             ASSERT_TRUE (info != NULL);
> +             ASSERT_EQ (info->num_items (), 1);
> +             ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +           }
> +       }
>
> -      optinfo *info = tmp.get_pending_optinfo ();
> -      ASSERT_TRUE (info != NULL);
> -      ASSERT_EQ (info->num_items (), 1);
> -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +       /* dump_gimple_expr.  */
> +       {
> +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> +         dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> +
> +         ASSERT_DUMPED_TEXT_EQ (tmp, "return;");
> +         if (with_optinfo)
> +           {
> +             optinfo *info = tmp.get_pending_optinfo ();
> +             ASSERT_TRUE (info != NULL);
> +             ASSERT_EQ (info->num_items (), 1);
> +             ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> +           }
> +       }
> +      }
> +
> +      /* poly_int.  */
> +      {
> +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> +       dump_dec (MSG_NOTE, poly_int64 (42));
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "42");
> +       if (with_optinfo)
> +         {
> +           optinfo *info = tmp.get_pending_optinfo ();
> +           ASSERT_TRUE (info != NULL);
> +           ASSERT_EQ (info->num_items (), 1);
> +           ASSERT_IS_TEXT (info->get_item (0), "42");
> +         }
> +      }
> +
> +      /* scopes.  */
> +      {
> +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> +       dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");
> +       {
> +         AUTO_DUMP_SCOPE ("outer scope", stmt);
> +         dump_printf_loc (MSG_NOTE, stmt, "msg 2\n");
> +         {
> +           AUTO_DUMP_SCOPE ("middle scope", stmt);
> +           dump_printf_loc (MSG_NOTE, stmt, "msg 3\n");
> +           {
> +             AUTO_DUMP_SCOPE ("inner scope", stmt);
> +             dump_printf_loc (MSG_NOTE, stmt, "msg 4\n");
> +           }
> +           dump_printf_loc (MSG_NOTE, stmt, "msg 5\n");
> +         }
> +         dump_printf_loc (MSG_NOTE, stmt, "msg 6\n");
> +       }
> +       dump_printf_loc (MSG_NOTE, stmt, "msg 7\n");
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp,
> +                              "test.txt:5:10: note: msg 1\n"
> +                              "test.txt:5:10: note: === outer scope ===\n"
> +                              "test.txt:5:10: note:  msg 2\n"
> +                              "test.txt:5:10: note:  === middle scope ===\n"
> +                              "test.txt:5:10: note:   msg 3\n"
> +                              "test.txt:5:10: note:   === inner scope ===\n"
> +                              "test.txt:5:10: note:    msg 4\n"
> +                              "test.txt:5:10: note:   msg 5\n"
> +                              "test.txt:5:10: note:  msg 6\n"
> +                              "test.txt:5:10: note: msg 7\n");
> +       if (with_optinfo)
> +         {
> +           optinfo *info = tmp.get_pending_optinfo ();
> +           ASSERT_TRUE (info != NULL);
> +           ASSERT_EQ (info->num_items (), 1);
> +           ASSERT_IS_TEXT (info->get_item (0), "msg 7\n");
> +         }
> +      }
>      }
> -  }
> -
> -  /* poly_int.  */
> -  {
> -    temp_dump_context tmp (true);
> -    dump_dec (MSG_NOTE, poly_int64 (42));
> -
> -    optinfo *info = tmp.get_pending_optinfo ();
> -    ASSERT_TRUE (info != NULL);
> -    ASSERT_EQ (info->num_items (), 1);
> -    ASSERT_IS_TEXT (info->get_item (0), "42");
> -  }
>
>    /* Verify that MSG_* affects optinfo->get_kind (); we tested MSG_NOTE
>       above.  */
>    {
>      /* MSG_OPTIMIZED_LOCATIONS.  */
>      {
> -      temp_dump_context tmp (true);
> +      temp_dump_context tmp (true, MSG_ALL);
>        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
>        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
>                  OPTINFO_KIND_SUCCESS);
> @@ -1728,7 +2002,7 @@ test_capture_of_dump_calls (const line_table_case &case_)
>
>      /* MSG_MISSED_OPTIMIZATION.  */
>      {
> -      temp_dump_context tmp (true);
> +      temp_dump_context tmp (true, MSG_ALL);
>        dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
>        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
>                  OPTINFO_KIND_FAILURE);
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 1dbe3b8..2b174e5 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -442,19 +442,27 @@ dump_enabled_p (void)
>  }
>
>  /* The following API calls (which *don't* take a "FILE *")
> -   write the output to zero or more locations:
> -   (a) the active dump_file, if any
> -   (b) the -fopt-info destination, if any
> -   (c) to the "optinfo" destinations, if any:
> -       (c.1) as optimization records
> -
> -   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> -                                   |
> -                                   +--> (b) alt_dump_file
> -                                   |
> -                                   `--> (c) optinfo
> -                                            `---> optinfo destinations
> -                                                  (c.1) optimization records
> +   write the output to zero or more locations.
> +
> +   Some destinations are written to immediately as dump_* calls
> +   are made; for others, the output is consolidated into an "optinfo"
> +   instance (with its own metadata), and only emitted once the optinfo
> +   is complete.
> +
> +   The destinations are:
> +
> +   (a) the "immediate" destinations:
> +       (a.1) the active dump_file, if any
> +       (a.2) the -fopt-info destination, if any
> +   (b) the "optinfo" destinations, if any:
> +       (b.1) as optimization records
> +
> +   dump_* (MSG_*) --> dumpfile.c --> items --> (a.1) dump_file
> +                                       |   `-> (a.2) alt_dump_file
> +                                       |
> +                                       `--> (b) optinfo
> +                                                `---> optinfo destinations
> +                                                      (b.1) optimization records
>
>     For optinfos, the dump_*_loc mark the beginning of an optinfo
>     instance: all subsequent dump_* calls are consolidated into
> diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> index 2199d52..992960e 100644
> --- a/gcc/optinfo-emit-json.cc
> +++ b/gcc/optinfo-emit-json.cc
> @@ -537,7 +537,7 @@ namespace selftest {
>  static void
>  test_building_json_from_dump_calls ()
>  {
> -  temp_dump_context tmp (true);
> +  temp_dump_context tmp (true, MSG_NOTE);
>    dump_location_t loc;
>    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
>    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> index 93de9d9..b858c3c 100644
> --- a/gcc/optinfo.cc
> +++ b/gcc/optinfo.cc
> @@ -34,11 +34,11 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cgraph.h"
>  #include "selftest.h"
>
> -/* optinfo_item's ctor.  */
> +/* optinfo_item's ctor.  Takes ownership of TEXT.  */
>
>  optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
> -                           char *text, bool owned)
> -: m_kind (kind), m_location (location), m_text (text), m_owned (owned)
> +                           char *text)
> +: m_kind (kind), m_location (location), m_text (text)
>  {
>  }
>
> @@ -46,8 +46,7 @@ optinfo_item::optinfo_item (enum optinfo_item_kind kind, location_t location,
>
>  optinfo_item::~optinfo_item ()
>  {
> -  if (m_owned)
> -    free (m_text);
> +  free (m_text);
>  }
>
>  /* Get a string from KIND.  */
> @@ -81,7 +80,17 @@ optinfo::~optinfo ()
>      delete item;
>  }
>
> -/* Emit the optinfo to all of the active destinations.  */
> +/* Add ITEM to this optinfo.  */
> +
> +void
> +optinfo::add_item (optinfo_item *item)
> +{
> +  gcc_assert (item);
> +  m_items.safe_push (item);
> +}
> +
> +/* Emit the optinfo to all of the "non-immediate" destinations
> +   (emission to "immediate" destinations is done by emit_item).  */
>
>  void
>  optinfo::emit ()
> @@ -103,120 +112,6 @@ optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
>      m_kind = OPTINFO_KIND_NOTE;
>  }
>
> -/* Append a string literal to this optinfo.  */
> -
> -void
> -optinfo::add_string (const char *str)
> -{
> -  optinfo_item *item
> -    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> -                       const_cast <char *> (str), false);
> -  m_items.safe_push (item);
> -}
> -
> -/* Append printf-formatted text to this optinfo.  */
> -
> -void
> -optinfo::add_printf (const char *format, ...)
> -{
> -  va_list ap;
> -  va_start (ap, format);
> -  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)
> -{
> -  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);
> -}
> -
> -/* Append a gimple statement to this optinfo, equivalent to
> -   print_gimple_stmt.  */
> -
> -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);
> -  m_items.safe_push (item);
> -}
> -
> -/* Append a gimple statement to this optinfo, equivalent to
> -   print_gimple_expr.  */
> -
> -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);
> -  m_items.safe_push (item);
> -}
> -
> -/* Append a tree node to this optinfo, equivalent to print_generic_expr.  */
> -
> -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);
> -  m_items.safe_push (item);
> -}
> -
> -/* Append a symbol table node to this optinfo.  */
> -
> -void
> -optinfo::add_symtab_node (symtab_node *node)
> -{
> -  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> -  optinfo_item *item
> -    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> -                       xstrdup (node->dump_name ()), true);
> -  m_items.safe_push (item);
> -}
> -
> -/* Append the decimal represenation of a wide_int_ref to this
> -   optinfo.  */
> -
> -void
> -optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> -{
> -  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> -  print_dec (wi, buf, sgn);
> -  optinfo_item *item
> -    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> -                       xstrdup (buf), true);
> -  m_items.safe_push (item);
> -}
> -
>  /* Should optinfo instances be created?
>     All creation of optinfos should be guarded by this predicate.
>     Return true if any optinfo destinations are active.  */
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> index c4cf8ad..8ac961c 100644
> --- a/gcc/optinfo.h
> +++ b/gcc/optinfo.h
> @@ -92,6 +92,8 @@ enum optinfo_kind
>
>  extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
>
> +class dump_context;
> +
>  /* A bundle of information describing part of an optimization.  */
>
>  class optinfo
> @@ -120,41 +122,14 @@ 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 add_item (optinfo_item *item);
> +
>   private:
>    void emit ();
>
>    /* Pre-canned ways of manipulating the optinfo, for use by friend class
>       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_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);
> -  void add_symtab_node (symtab_node *node);
> -  void add_dec (const wide_int_ref &wi, signop sgn);
> -
> -  template<unsigned int N, typename C>
> -  void add_poly_int (const poly_int<N, C> &value)
> -  {
> -    /* Compare with dump_dec (MSG_NOTE, ).  */
> -
> -    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> -    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> -
> -    if (value.is_constant ())
> -      add_dec (value.coeffs[0], sgn);
> -    else
> -      {
> -       add_string ("[");
> -       for (unsigned int i = 0; i < N; ++i)
> -         {
> -           add_dec (value.coeffs[i], sgn);
> -           add_string (i == N - 1 ? "]" : ",");
> -         }
> -      }
> -  }
>
>   private:
>    dump_location_t m_loc;
> @@ -179,7 +154,7 @@ class optinfo_item
>  {
>   public:
>    optinfo_item (enum optinfo_item_kind kind, location_t location,
> -               char *text, bool owned);
> +               char *text);
>    ~optinfo_item ();
>
>    enum optinfo_item_kind get_kind () const { return m_kind; }
> @@ -191,9 +166,8 @@ class optinfo_item
>    enum optinfo_item_kind m_kind;
>    location_t m_location;
>
> -  /* The textual form of the item.  */
> +  /* The textual form of the item, owned by the item.  */
>    char *m_text;
> -  bool m_owned;
>  };
>
>  #endif /* #ifndef GCC_OPTINFO_H */
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 4/5] c-family: clean up the data tables in c-format.c
  2018-07-27 21:47 ` [PATCH 4/5] c-family: clean up the data tables in c-format.c David Malcolm
@ 2018-07-31 12:56   ` Richard Biener
  2018-07-31 13:08     ` Marek Polacek
  0 siblings, 1 reply; 29+ messages in thread
From: Richard Biener @ 2018-07-31 12:56 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Fri, Jul 27, 2018 at 11:48 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> The format_char_info tables in c-format.c for our own formats contain
> a lot of repetition.
>
> This patch adds a macro to express the conversion specifiers implemented
> within pp_format, making it clearer which are custom ones added by the
> various diagnostic_format_decoder callbacks.
>
> Doing so uncovered a few mistakes in the data (based on comparison with
> the source of the diagnostic_format_decoder callbacks, and the notes
> below), which the patch fixes:
>
> - gcc_diag_char_table didn't have 'Z', but it *is* implemented by pp_format.
>
> - removed erroneous 'G' and 'K' entries from gcc_diag_char_table: they're
>   implemented by default_tree_printer (and thus in "tdiag") and by the
>   C/C++ FEs, but not in pp_format.
>
> - removed "v" (lower case) from gcc_tdiag_char_table and
>   gcc_cxxdiag_char_table
>
> Notes:
>
> pretty-print.h uses this for ATTRIBUTE_GCC_PPDIAG, used by pp_printf
> and pp_verbatim:
>
> whereas diagnostic-core.h uses this for ATTRIBUTE_GCC_DIAG, used by
> the various diagnostic functions:
>
> /* If we haven't already defined a front-end-specific diagnostics
>    style, use the generic one.  */
>
> Hence I'm assuming that __gcc_diag__ is for use for when we don't
> know what kind of diagnostic_format_decoder we have, and we can
> only rely on pp_format's core functionality, where __gcc_tdiag__
> is allowed to assume default_tree_printer.

OK if nobody objects.

Thanks,
Richard.

> gcc/c-family/ChangeLog:
>         * c-format.c (PP_FORMAT_CHAR_TABLE): New macro, based on existing
>         table entries for gcc_diag_char_table, and the 'Z' entry from
>         gcc_tdiag_char_table, changing the "chain" entry for 'Z' from
>         &gcc_tdiag_char_table[0] to &gcc_diag_char_table[0].
>         (gcc_diag_char_table): Use PP_FORMAT_CHAR_TABLE, implicitly
>         adding missing "Z" for this table.  Remove erroneous "G" and "K"
>         entries.
>         (gcc_tdiag_char_table): Use PP_FORMAT_CHAR_TABLE.  Remove "v".
>         (gcc_cdiag_char_table): Use PP_FORMAT_CHAR_TABLE.
>         (gcc_cxxdiag_char_table): Use PP_FORMAT_CHAR_TABLE.  Remove "v".
>
> gcc/testsuite/ChangeLog:
>         * gcc.dg/format/gcc_diag-1.c (foo): Update the %v tests for
>         tdiag and cxxdiag.
>         * gcc.dg/format/gcc_diag-10.c (test_diag): Update tests of %G
>         and %K.
> ---
>  gcc/c-family/c-format.c                   | 99 ++++++++++---------------------
>  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  4 +-
>  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  7 +--
>  3 files changed, 35 insertions(+), 75 deletions(-)
>
> diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
> index a0192dd..82841e4 100644
> --- a/gcc/c-family/c-format.c
> +++ b/gcc/c-family/c-format.c
> @@ -679,43 +679,40 @@ static const format_char_info asm_fprintf_char_table[] =
>    { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
>  };
>
> +/* GCC-specific format_char_info arrays.  */
> +
> +/* The conversion specifiers implemented within pp_format, and thus supported
> +   by all pretty_printer instances within GCC.  */
> +
> +#define PP_FORMAT_CHAR_TABLE \
> +  { "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 }, \
> +  { "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_diag_char_table[0] }
> +
>  static const format_char_info gcc_diag_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.  */
> +  /* The conversion specifiers implemented within pp_format.  */
> +  PP_FORMAT_CHAR_TABLE,
>
> -  /* 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 },
> -  /* K requires a "tree" argument at runtime.  */
> -  { "K",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "",    "\"",   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 },
>    { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
>  };
>
>  static const format_char_info gcc_tdiag_char_table[] =
>  {
> -  /* C89 conversion specifiers.  */
> -  { "di",  0, STD_C89, { T89_I,   BADLEN,  BADLEN,  T89_L,   T9L_LL,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
> -  { "ox",  0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
> -  { "u",   0, STD_C89, { T89_UI,  BADLEN,  BADLEN,  T89_UL,  T9L_ULL, BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
> -  { "c",   0, STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q",  "",   NULL },
> -  { "s",   1, STD_C89, { T89_C,   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  }, "q",  "c",  NULL },
> +  /* The conversion specifiers implemented within pp_format.  */
> +  PP_FORMAT_CHAR_TABLE,
>
> -  /* Custom conversion specifiers.  */
> +  /* Custom conversion specifiers implemented by default_tree_printer.  */
>
>    /* These will require a "tree" at runtime.  */
>    { "DFTV", 1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q+", "'",   NULL },
> @@ -725,29 +722,15 @@ static const format_char_info gcc_tdiag_char_table[] =
>    /* G requires a "gcall*" argument at runtime.  */
>    { "G", 1, STD_C89, { T89_G,   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 gcc_cdiag_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 },
> +  /* The conversion specifiers implemented within pp_format.  */
> +  PP_FORMAT_CHAR_TABLE,
>
> -  /* Custom conversion specifiers.  */
> +  /* Custom conversion specifiers implemented by c_tree_printer.  */
>
>    /* 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 },
> @@ -759,33 +742,20 @@ static const format_char_info gcc_cdiag_char_table[] =
>
>    { "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 gcc_cxxdiag_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 },
> +  /* The conversion specifiers implemented within pp_format.  */
> +  PP_FORMAT_CHAR_TABLE,
>
> -  /* Custom conversion specifiers.  */
> +  /* Custom conversion specifiers implemented by cp_printer.  */
>
>    /* These will require a "tree" at runtime.  */
>    { "ADFHISTVX",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 },
> -  { "v", 0,STD_C89, { T89_I,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "q#",  "",   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 },
> @@ -793,13 +763,6 @@ static const format_char_info gcc_cxxdiag_char_table[] =
>    /* These accept either an 'int' or an 'enum tree_code' (which is handled as an 'int'.)  */
>    { "CLOPQ",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 }
>  };
>
> diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> index 4dcdb05..034e097 100644
> --- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> @@ -87,9 +87,9 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
>    cxxdiag ("%<%+#A%+#D%+#E%+#F%+#T%+#V%>", t1, t1, t1, t1, t1, t1);
>    cxxdiag ("%C%L%O%P%Q", i, i, i, i, i);
>
> -  tdiag ("%v%qv%#v", i, i, i);
> +  tdiag ("%v", i); /* { dg-warning "format" } */
>    cdiag ("%v%qv%#v", i, i, i);
> -  cxxdiag ("%v%qv%#v", i, i, i);
> +  cxxdiag ("%v", i); /* { dg-warning "format" } */
>
>    tdiag ("%Z", v, v_len);
>    cdiag ("%Z", v, v_len);
> diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> index 9bda73b..ab9bc2f 100644
> --- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> @@ -32,8 +32,8 @@ void test_diag (tree t, gcall *gc)
>    diag ("%>");   /* { dg-warning "unmatched quoting directive " } */
>    diag ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting directive" } */
>
> -  diag ("%G", gc);
> -  diag ("%K", t);
> +  diag ("%G", gc); /* { dg-warning "format" } */
> +  diag ("%K", t); /* { dg-warning "format" } */
>
>    diag ("%R");       /* { dg-warning "unmatched color reset directive" } */
>    diag ("%r", "");   /* { dg-warning "unterminated color directive" } */
> @@ -42,9 +42,6 @@ void test_diag (tree t, gcall *gc)
>    diag ("%r%r%R", "", "");
>    diag ("%r%R%r%R", "", "");
>
> -  diag ("%<%G%>", gc);  /* { dg-warning ".G. conversion used within a quoted sequence" } */
> -  diag ("%<%K%>", t);   /* { dg-warning ".K. conversion used within a quoted sequence" } */
> -
>    diag ("%<%R%>");      /* { dg-warning "unmatched color reset directive" } */
>    diag ("%<%r%>", "");  /* { dg-warning "unterminated color directive" } */
>    diag ("%<%r%R%>", "");
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 5/5] Formatted printing for dump_* in the middle-end
  2018-07-27 21:47 ` [PATCH 5/5] Formatted printing for dump_* in the middle-end David Malcolm
@ 2018-07-31 13:03   ` Richard Biener
  2018-07-31 14:19     ` David Malcolm
  0 siblings, 1 reply; 29+ messages in thread
From: Richard Biener @ 2018-07-31 13:03 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Fri, Jul 27, 2018 at 11:49 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch converts dump_print and dump_printf_loc from using
> printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
> based on pp_format, which supports formatting middle-end types.
>
> In particular, the following codes are implemented (in addition
> to the standard pretty_printer ones):
>
>    %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 (MSG_*, arg, TDF_SLIM).
>
> Hence it becomes possible to convert e.g.:
>
>   if (dump_enabled_p ())
>     {
>       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
>                        "not vectorized: different sized vector "
>                        "types in statement, ");
>       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
>       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
>       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, nunits_vectype);
>       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
>     }
>
> into a one-liner:
>
>   if (dump_enabled_p ())
>     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
>                      "not vectorized: different sized vector "
>                      "types in statement, %T and %T\n",
>                      vectype, nunits_vectype);
>
> Unlike regular pretty-printers, this one captures optinfo_item
> instances for the formatted chunks as appropriate, so that when
> written out to a JSON optimization record, the relevant parts of
> the message are labelled by type, and by source location (so that
> e.g. %G is entirely equivalent to using dump_gimple_stmt).
>
> dump_printf and dump_printf_loc become marked with
> ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.
>
> gcc/c-family/ChangeLog:
>         * c-format.c (enum format_type): Add gcc_dump_printf_format_type.
>         (local_gimple_ptr_node): New decl.
>         (gcc_dump_printf_length_specs): New.
>         (gcc_dump_printf_flag_pairs): New.
>         (gcc_dump_printf_flag_specs): New.
>         (gcc_dump_printf_char_table): New.
>         (format_types_orig): Add entry for "gcc_dump_printf".
>         (init_dynamic_diag_info): Create local_gimple_ptr_node.
>         Set up length_char_specs and conversion_specs for
>         gcc_dump_printf_format_type.
>         (handle_format_attribute): Handle gcc_dump_printf_format_type.
>         * c-format.h (T89_GIMPLE): New macro.

Iff the c-family changes are neccessary (are they?) then how does this
work for non-c-family languages which do not link c-family/c-format.o?

> gcc/ChangeLog:
>         * dump-context.h: Include "dumpfile.h".
>         (dump_context::dump_printf_va): Convert final param from va_list
>         to va_list *.  Convert from ATTRIBUTE_PRINTF to
>         ATTRIBUTE_GCC_DUMP_PRINTF.
>         (dump_context::dump_printf_loc_va): Likewise.
>         * dumpfile.c: Include "stringpool.h".
>         (make_item_for_dump_printf_va): Delete.
>         (make_item_for_dump_printf): Delete.
>         (class dump_pretty_printer): New class.
>         (dump_pretty_printer::dump_pretty_printer): New ctor.
>         (dump_pretty_printer::emit_items): New member function.
>         (dump_pretty_printer::emit_any_pending_textual_chunks): New member
>         function.
>         (dump_pretty_printer::emit_item): New member function.
>         (dump_pretty_printer::stash_item): New member function.
>         (dump_pretty_printer::format_decoder_cb): New member function.
>         (dump_pretty_printer::decode_format): New member function.
>         (dump_context::dump_printf_va): Reimplement in terms of
>         dump_pretty_printer.
>         (dump_context::dump_printf_loc_va): Convert final param from va_list
>         to va_list *.
>         (dump_context::begin_scope): Reimplement call to
>         make_item_for_dump_printf.
>         (dump_printf): Update for change to dump_printf_va.
>         (dump_printf_loc): Likewise.
>         (selftest::test_capture_of_dump_calls): Convert "stmt" from
>         greturn * to gimple *.  Add a test_decl.  Add tests of dump_printf
>         with %T, %E, and %G.
>         * dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
>         (dump_printf): Replace ATTRIBUTE_PRINTF_2 with
>         ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
>         (dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
>         ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
>
> gcc/testsuite/ChangeLog:
>         * gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage for
>         gcc_dump_printf.
>         * gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add test
>         coverage for gcc_dump_printf.
> ---
>  gcc/c-family/c-format.c                   |  60 ++++-
>  gcc/c-family/c-format.h                   |   1 +
>  gcc/dump-context.h                        |   7 +-
>  gcc/dumpfile.c                            | 358 +++++++++++++++++++++++++++---
>  gcc/dumpfile.h                            |  20 +-
>  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  15 +-
>  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  26 +++
>  7 files changed, 448 insertions(+), 39 deletions(-)
>
> diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
> index 82841e4..b524468 100644
> --- a/gcc/c-family/c-format.c
> +++ b/gcc/c-family/c-format.c
> @@ -44,6 +44,7 @@ enum format_type { printf_format_type, asm_fprintf_format_type,
>                    gcc_diag_format_type, gcc_tdiag_format_type,
>                    gcc_cdiag_format_type,
>                    gcc_cxxdiag_format_type, gcc_gfc_format_type,
> +                  gcc_dump_printf_format_type,
>                    gcc_objc_string_format_type,
>                    format_type_error = -1};
>
> @@ -56,6 +57,7 @@ struct function_format_info
>
>  /* Initialized in init_dynamic_diag_info.  */
>  static GTY(()) tree local_tree_type_node;
> +static GTY(()) tree local_gimple_ptr_node;
>  static GTY(()) tree local_gcall_ptr_node;
>  static GTY(()) tree locus;
>
> @@ -461,6 +463,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 +553,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 +569,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[] =
>  {
> @@ -786,6 +791,22 @@ static const format_char_info gcc_gfc_char_table[] =
>    { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
>  };
>
> +static const format_char_info gcc_dump_printf_char_table[] =
> +{
> +  /* The conversion specifiers implemented within pp_format.  */
> +  PP_FORMAT_CHAR_TABLE,
> +
> +  /* Custom conversion specifiers implemented by dump_pretty_printer.  */
> +
> +  /* E and G require a "gimple *" argument at runtime.  */
> +  { "EG",   1, STD_C89, { T89_GIMPLE,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> +
> +  /* T requires a "tree" at runtime.  */
> +  { "T",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> +
> +  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> +};
> +
>  static const format_char_info scan_char_table[] =
>  {
>    /* C89 conversion specifiers.  */
> @@ -885,6 +906,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,
> @@ -3838,6 +3866,29 @@ init_dynamic_diag_info (void)
>         local_tree_type_node = void_type_node;
>      }
>
> +  /* Similar to the above but for gimple *.  */
> +  if (!local_gimple_ptr_node
> +      || local_gimple_ptr_node == void_type_node)
> +    {
> +      if ((local_gimple_ptr_node = maybe_get_identifier ("gimple")))
> +       {
> +         local_gimple_ptr_node
> +           = identifier_global_value (local_gimple_ptr_node);
> +         if (local_gimple_ptr_node)
> +           {
> +             if (TREE_CODE (local_gimple_ptr_node) != TYPE_DECL)
> +               {
> +                 error ("%<gimple%> is not defined as a type");
> +                 local_gimple_ptr_node = 0;
> +               }
> +             else
> +               local_gimple_ptr_node = TREE_TYPE (local_gimple_ptr_node);
> +           }
> +       }
> +      else
> +       local_gimple_ptr_node = void_type_node;
> +    }
> +
>    /* Similar to the above but for gcall*.  */
>    if (!local_gcall_ptr_node
>        || local_gcall_ptr_node == void_type_node)
> @@ -3905,6 +3956,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),
> @@ -3931,6 +3983,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
> @@ -4085,7 +4139,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.  */
> @@ -4107,7 +4162,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/c-family/c-format.h b/gcc/c-family/c-format.h
> index f828e77..4dfdbdb 100644
> --- a/gcc/c-family/c-format.h
> +++ b/gcc/c-family/c-format.h
> @@ -298,6 +298,7 @@ struct format_kind_info
>  #define T_UC   &unsigned_char_type_node
>  #define T99_UC { STD_C99, NULL, T_UC }
>  #define T_V    &void_type_node
> +#define T89_GIMPLE   { STD_C89, NULL, &local_gimple_ptr_node }
>  #define T89_G   { STD_C89, NULL, &local_gcall_ptr_node }
>  #define T89_T   { STD_C89, NULL, &local_tree_type_node }
>  #define T89_V  { STD_C89, NULL, T_V }
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> index f40ea14..54095d3 100644
> --- a/gcc/dump-context.h
> +++ b/gcc/dump-context.h
> @@ -22,6 +22,7 @@ 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
>  #include "pretty-print.h"
>
>  /* A class for handling the various dump_* calls.
> @@ -73,11 +74,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);
> +                          const char *format, va_list *ap)
> +    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 10e9cab..7131f56 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.  */
> @@ -681,56 +682,262 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
>    dump_generic_expr (dump_kind, extra_dump_flags, t);
>  }
>
> -/* Make an item for the given dump call.  */
> +/* A subclass of pretty_printer for implementing dump_context::dump_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.  */
>
> -static optinfo_item *
> -make_item_for_dump_printf_va (const char *format, va_list ap)
> -  ATTRIBUTE_PRINTF (1, 0);
> +class dump_pretty_printer : public pretty_printer
> +{
> +public:
> +  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);
>
> -static optinfo_item *
> -make_item_for_dump_printf_va (const char *format, va_list ap)
> +  void emit_items (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 emit_any_pending_textual_chunks (optinfo *dest);
> +
> +  void emit_item (optinfo_item *item, optinfo *dest);
> +
> +  dump_context *m_context;
> +  dump_flags_t m_dump_kind;
> +  auto_vec<stashed_item> m_stashed_items;
> +};
> +
> +/* dump_pretty_printer's ctor.  */
> +
> +dump_pretty_printer::dump_pretty_printer (dump_context *context,
> +                                         dump_flags_t dump_kind)
> +: pretty_printer (), m_context (context), m_dump_kind (dump_kind),
> +  m_stashed_items ()
> +{
> +  pp_format_decoder (this) = format_decoder_cb;
> +}
> +
> +/* Phase 3 of formatting; compare with pp_output_formatted_text.
> +
> +   Emit optinfo_item instances for the various formatted chunks from phases
> +   1 and 2 (i.e. pp_format).
> +
> +   Some chunks may already have had their items built (during decode_format).
> +   These chunks have been stashed into m_stashed_items; we emit them here.
> +
> +   For all other purely textual chunks, they are printed into
> +   buffer->formatted_obstack, and then emitted as a textual optinfo_item.
> +   This consolidates multiple adjacent text chunks into a single text
> +   optinfo_item.  */
> +
> +void
> +dump_pretty_printer::emit_items (optinfo *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)
> +       {
> +         emit_any_pending_textual_chunks (dest);
> +         /* This chunk has a stashed item: use it.  */
> +         emit_item (m_stashed_items[stashed_item_idx++].item, dest);
> +       }
> +      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]);
> +    }
> +
> +  emit_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 dump_pretty_printer::emit_items
> +   for consolidating multiple adjacent pure-text chunks into single
> +   optinfo_items (in phase 3).  */
> +
> +void
> +dump_pretty_printer::emit_any_pending_textual_chunks (optinfo *dest)
>  {
> -  char *formatted_text = xvasprintf (format, ap);
> +  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
> +
> +  /* Don't emit 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);
> -  return item;
> +  emit_item (item, dest);
> +
> +  /* Clear the pending text by unwinding formatted_text back to the start
> +     of the buffer (without deallocating).  */
> +  obstack_free (&buffer->formatted_obstack,
> +               buffer->formatted_obstack.object_base);
>  }
>
> -/* Make an item for the given dump call.  */
> +/* Emit ITEM and take ownership of it.  If DEST is non-NULL, add ITEM
> +   to DEST; otherwise delete ITEM.  */
>
> -static optinfo_item *
> -make_item_for_dump_printf (const char *format, ...)
> -  ATTRIBUTE_PRINTF (1, 2);
> +void
> +dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest)
> +{
> +  m_context->emit_item (item, m_dump_kind);
> +  if (dest)
> +    dest->add_item (item);
> +  else
> +    delete item;
> +}
>
> -static optinfo_item *
> -make_item_for_dump_printf (const char *format, ...)
> +/* Record that ITEM (generated in phase 2 of formatting) is to be used for
> +   the chunk at BUFFER_PTR in phase 3 (by emit_items).  */
> +
> +void
> +dump_pretty_printer::stash_item (const char **buffer_ptr, optinfo_item *item)
>  {
> -  va_list ap;
> -  va_start (ap, format);
> -  optinfo_item *item
> -    = make_item_for_dump_printf_va (format, ap);
> -  va_end (ap);
> -  return item;
> +  gcc_assert (buffer_ptr);
> +  gcc_assert (item);
> +
> +  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
> +}
> +
> +/* pp_format_decoder callback for dump_pretty_printer, and thus for
> +   dump_printf and dump_printf_loc.
> +
> +   A wrapper around decode_format, for type-safety.  */
> +
> +bool
> +dump_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)
> +{
> +  dump_pretty_printer *opp = static_cast <dump_pretty_printer *> (pp);
> +  return opp->decode_format (text, spec, buffer_ptr);
> +}
> +
> +/* Format decoder for dump_pretty_printer, 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 (MSG_*, arg, TDF_SLIM).
> +
> +   FIXME: add symtab_node?
> +
> +   These format codes build optinfo_item instances, thus capturing metadata
> +   about the arguments being dumped, as well as the textual output.  */
> +
> +bool
> +dump_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).  */
> +  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_dump_gimple_expr (stmt, 0, TDF_SLIM);
> +       stash_item (buffer_ptr, item);
> +       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_dump_gimple_stmt (stmt, 0, TDF_SLIM);
> +       stash_item (buffer_ptr, item);
> +       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_dump_generic_expr (t, TDF_SLIM);
> +       stash_item (buffer_ptr, item);
> +       return true;
> +      }
> +
> +    default:
> +      return false;
> +    }
>  }
>
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
>  dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
> -                             va_list ap)
> +                             va_list *ap)
>  {
> -  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> -  emit_item (item, dump_kind);
> +  dump_pretty_printer pp (this, dump_kind);
>
> +  text_info text;
> +  text.err_no = errno;
> +  text.args_ptr = ap;
> +  text.format_spec = format;
> +
> +  /* Phases 1 and 2, using pp_format.  */
> +  pp_format (&pp, &text);
> +
> +  /* Phase 3.  */
>    if (optinfo_enabled_p ())
>      {
>        optinfo &info = ensure_pending_optinfo ();
>        info.handle_dump_file_kind (dump_kind);
> -      info.add_item (item);
> +      pp.emit_items (&info);
>      }
>    else
> -    delete item;
> +    pp.emit_items (NULL);
>  }
>
>  /* Similar to dump_printf, except source location is also printed, and
> @@ -739,7 +946,7 @@ dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
>  void
>  dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
>                                   const dump_location_t &loc,
> -                                 const char *format, va_list ap)
> +                                 const char *format, va_list *ap)
>  {
>    dump_loc (dump_kind, loc);
>    dump_printf_va (dump_kind, format, ap);
> @@ -851,7 +1058,11 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
>    if (m_test_pp)
>      ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
>
> -  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n", name);
> +  pretty_printer pp;
> +  pp_printf (&pp, "=== %s ===\n", name);
> +  optinfo_item *item
> +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> +                       xstrdup (pp_formatted_text (&pp)));
>    emit_item (item, MSG_NOTE);
>
>    if (optinfo_enabled_p ())
> @@ -859,7 +1070,6 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
>        optinfo &info = begin_next_optinfo (loc);
>        info.m_kind = OPTINFO_KIND_SCOPE;
>        info.add_item (item);
> -      end_any_optinfo ();
>      }
>    else
>      delete item;
> @@ -1006,7 +1216,7 @@ dump_printf (dump_flags_t dump_kind, const char *format, ...)
>  {
>    va_list ap;
>    va_start (ap, format);
> -  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> +  dump_context::get ().dump_printf_va (dump_kind, format, &ap);
>    va_end (ap);
>  }
>
> @@ -1019,7 +1229,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>  {
>    va_list ap;
>    va_start (ap, format);
> -  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, &ap);
>    va_end (ap);
>  }
>
> @@ -1808,9 +2018,12 @@ test_capture_of_dump_calls (const line_table_case &case_)
>
>    dump_location_t loc = dump_location_t::from_location_t (where);
>
> -  greturn *stmt = gimple_build_return (NULL);
> +  gimple *stmt = gimple_build_return (NULL);
>    gimple_set_location (stmt, where);
>
> +  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> +                              get_identifier ("test_decl"),
> +                              integer_type_node);
>    /* Run all tests twice, with and then without optinfo enabled, to ensure
>       that immediate destinations vs optinfo-based destinations both
>       work, independently of each other, with no leaks.  */
> @@ -1834,6 +2047,89 @@ test_capture_of_dump_calls (const line_table_case &case_)
>           }
>        }
>
> +      /* Test of dump_printf with %T.  */
> +      {
> +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> +       dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "tree: 0");
> +       if (with_optinfo)
> +         {
> +           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 (with_optinfo, MSG_ALL);
> +       dump_printf (MSG_NOTE, "gimple: %E", stmt);
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;");
> +       if (with_optinfo)
> +         {
> +           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 (with_optinfo, MSG_ALL);
> +       dump_printf (MSG_NOTE, "gimple: %G", stmt);
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;\n");
> +       if (with_optinfo)
> +         {
> +           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 (with_optinfo, MSG_ALL);
> +       dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
> +                        " %i consecutive %E%E after\n",
> +                        integer_zero_node, test_decl, 42, stmt, stmt);
> +
> +       ASSERT_DUMPED_TEXT_EQ (tmp,
> +                              "test.txt:5:10: note: before 0 and test_decl"
> +                              " 42 consecutive return;return; after\n");
> +       if (with_optinfo)
> +         {
> +           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 (with_optinfo, MSG_ALL);
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 2b174e5..1fe64cb 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 dumpfile.c).  */
> +
> +#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
> @@ -476,9 +489,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/testsuite/gcc.dg/format/gcc_diag-1.c b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> index 034e097..8761456 100644
> --- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> @@ -1,4 +1,4 @@
> -/* Test for GCC diagnositc formats.  */
> +/* Test for GCC diagnostic formats.  */
>  /* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
>  /* { dg-do compile } */
>  /* { dg-options "-Wformat" } */
> @@ -24,6 +24,7 @@ extern int diag (const char *, ...) ATTRIBUTE_DIAG(__gcc_diag__);
>  extern int tdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_tdiag__);
>  extern int cdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cdiag__);
>  extern int cxxdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cxxdiag__);
> +extern int dump (const char *, ...) ATTRIBUTE_DIAG(__gcc_dump_printf__);
>
>  void
>  foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
> @@ -39,36 +40,44 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
>    tdiag ("%%");
>    cdiag ("%%");
>    cxxdiag ("%%");
> +  dump ("%%");
>    diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
>    tdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
>    cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
>    cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> +  dump ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
>    diag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
>    tdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
>    cdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
>    cxxdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
> +  dump ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
>    diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
>    tdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
>    cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
>    cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> +  dump ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
>    diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
>    tdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
>    cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
>    cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> +  dump ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
>    diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
>    tdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
>    cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
>    cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> +  dump ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
>    diag ("%.*s", i, s);
>    tdiag ("%.*s", i, s);
>    cdiag ("%.*s", i, s);
>    cxxdiag ("%.*s", i, s);
> +  dump ("%.*s", i, s);
>
>    /* Extensions provided in the diagnostic framework.  */
>    diag ("%m");
>    tdiag ("%m");
>    cdiag ("%m");
>    cxxdiag ("%m");
> +  dump ("%m");
>
>    /* Quote directives to avoid "warning: conversion used unquoted." */
>    tdiag ("%<%D%F%T%V%>", t1, t1, t1, t1);
> @@ -94,20 +103,24 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
>    tdiag ("%Z", v, v_len);
>    cdiag ("%Z", v, v_len);
>    cxxdiag ("%Z", v, v_len);
> +  dump ("%Z", v, v_len);
>
>    /* Bad stuff with extensions.  */
>    diag ("%m", i); /* { dg-warning "format" "extra arg" } */
>    tdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
>    cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
>    cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> +  dump ("%m", i); /* { dg-warning "format" "extra arg" } */
>    diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
>    tdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
>    cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
>    cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> +  dump ("%#m"); /* { dg-warning "format" "bogus modifier" } */
>    diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
>    tdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
>    cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
>    cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> +  dump ("%+m"); /* { dg-warning "format" "bogus modifier" } */
>    diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
>    tdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
>    tdiag ("%E", t1);
> diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> index ab9bc2f..8b5aadf1 100644
> --- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> @@ -19,12 +19,16 @@ typedef union tree_node *tree;
>     the C test to find the symbol.  */
>  typedef struct gcall gcall;
>
> +/* Likewise for gimple.  */
> +typedef struct gimple gimple;
> +
>  #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__, 1, 2)))
>
>  void diag (const char*, ...) FORMAT (diag);
>  void cdiag (const char*, ...) FORMAT (cdiag);
>  void tdiag (const char*, ...) FORMAT (tdiag);
>  void cxxdiag (const char*, ...) FORMAT (cxxdiag);
> +void dump (const char*, ...) FORMAT (dump_printf);
>
>  void test_diag (tree t, gcall *gc)
>  {
> @@ -157,3 +161,25 @@ void test_cxxdiag (tree t, gcall *gc)
>    cxxdiag ("%<%V%>", t);
>    cxxdiag ("%<%X%>", t);
>  }
> +
> +void test_dump (tree t, gimple *stmt)
> +{
> +  dump ("%<");   /* { dg-warning "unterminated quoting directive" } */
> +  dump ("%>");   /* { dg-warning "unmatched quoting directive " } */
> +  dump ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting directive" } */
> +
> +  dump ("%R");       /* { dg-warning "unmatched color reset directive" } */
> +  dump ("%r", "");   /* { dg-warning "unterminated color directive" } */
> +  dump ("%r%r", "", "");   /* { dg-warning "unterminated color directive" } */
> +  dump ("%r%R", "");
> +  dump ("%r%r%R", "", "");
> +  dump ("%r%R%r%R", "", "");
> +
> +  dump ("%<%R%>");      /* { dg-warning "unmatched color reset directive" } */
> +  dump ("%<%r%>", "");  /* { dg-warning "unterminated color directive" } */
> +  dump ("%<%r%R%>", "");
> +
> +  dump ("%E", stmt);
> +  dump ("%T", t);
> +  dump ("%G", stmt);
> +}
> --
> 1.8.5.3
>

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 4/5] c-family: clean up the data tables in c-format.c
  2018-07-31 12:56   ` Richard Biener
@ 2018-07-31 13:08     ` Marek Polacek
  0 siblings, 0 replies; 29+ messages in thread
From: Marek Polacek @ 2018-07-31 13:08 UTC (permalink / raw)
  To: Richard Biener; +Cc: David Malcolm, GCC Patches

On Tue, Jul 31, 2018 at 02:56:29PM +0200, Richard Biener wrote:
> On Fri, Jul 27, 2018 at 11:48 PM David Malcolm <dmalcolm@redhat.com> wrote:
> >
> > The format_char_info tables in c-format.c for our own formats contain
> > a lot of repetition.
> >
> > This patch adds a macro to express the conversion specifiers implemented
> > within pp_format, making it clearer which are custom ones added by the
> > various diagnostic_format_decoder callbacks.
> >
> > Doing so uncovered a few mistakes in the data (based on comparison with
> > the source of the diagnostic_format_decoder callbacks, and the notes
> > below), which the patch fixes:
> >
> > - gcc_diag_char_table didn't have 'Z', but it *is* implemented by pp_format.
> >
> > - removed erroneous 'G' and 'K' entries from gcc_diag_char_table: they're
> >   implemented by default_tree_printer (and thus in "tdiag") and by the
> >   C/C++ FEs, but not in pp_format.
> >
> > - removed "v" (lower case) from gcc_tdiag_char_table and
> >   gcc_cxxdiag_char_table
> >
> > Notes:
> >
> > pretty-print.h uses this for ATTRIBUTE_GCC_PPDIAG, used by pp_printf
> > and pp_verbatim:
> >
> > whereas diagnostic-core.h uses this for ATTRIBUTE_GCC_DIAG, used by
> > the various diagnostic functions:
> >
> > /* If we haven't already defined a front-end-specific diagnostics
> >    style, use the generic one.  */
> >
> > Hence I'm assuming that __gcc_diag__ is for use for when we don't
> > know what kind of diagnostic_format_decoder we have, and we can
> > only rely on pp_format's core functionality, where __gcc_tdiag__
> > is allowed to assume default_tree_printer.
> 
> OK if nobody objects.

Looks fine to me, too.

Marek

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 0/5] dump_printf support for middle-end types
  2018-07-31 12:50 ` [PATCH 0/5] dump_printf support for middle-end types Richard Biener
@ 2018-07-31 14:01   ` David Malcolm
  0 siblings, 0 replies; 29+ messages in thread
From: David Malcolm @ 2018-07-31 14:01 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Tue, 2018-07-31 at 14:50 +0200, Richard Biener wrote:
> On Fri, Jul 27, 2018 at 11:47 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > This patch kit converts dump_print and dump_printf_loc from using
> > fprintf etc internally to using a new pretty-printer
> > based on pp_format, which supports formatting middle-end types.
> > 
> > In particular, the following codes are implemented (in addition
> > to the standard pretty_printer ones):
> > 
> >    %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 (MSG_*, arg, TDF_SLIM).
> > 
> > Hence it becomes possible to convert e.g.:
> > 
> >   if (dump_enabled_p ())
> >     {
> >       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> >                        "not vectorized: different sized vector "
> >                        "types in statement, ");
> >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > vectype);
> >       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
> >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > nunits_vectype);
> >       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
> >     }
> >   return false;
> > 
> > into a single call to dump_printf_loc:
> > 
> >   if (dump_enabled_p ())
> >     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> >                      "not vectorized: different sized vector "
> >                      "types in statement, %T and %T\n",
> >                      vectype, nunits_vectype);
> >   return false;
> > 
> > Unlike regular pretty-printers, this captures metadata for the
> > formatted chunks as appropriate, so that when written out to a
> > JSON optimization record, the relevant parts of the message are
> > labelled by type, and by source location (so that
> > e.g. %G is entirely equivalent to using dump_gimple_stmt).
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > 
> > OK for trunk?
> 
> Nice!  I'm somehow missing 3/5?  Will look into the other ones now.

Thanks.

FWIW, 3/5 was:
  "[PATCH 3/5] C++: clean up cp_printer"
     https://gcc.gnu.org/ml/gcc-patches/2018-07/msg01766.html

which Jason has already approved (and I've committed as r263046, after
a fresh bootstrap&regrtest).

Dave

> Richard.
> 
> > I'm hoping to use this in a v3 of:
> >   "[PATCH 0/5] [RFC v2] Higher-level reporting of vectorization
> > problems"
> >      https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html
> > where the above might become:
> >   return opt_result::failure_at (stmt,
> >                                  "not vectorized: different sized
> > vector "
> >                                  "types in statement, %T and %T\n",
> >                                  vectype, nunits_vectype);
> > where opt_result::failure_at would encapsulate the "false", and
> > capture an opt_problem * (when dumps are enabled), for the reasons
> > discussed in that other kit.
> > 
> > David Malcolm (5):
> >   Simplify dump_context by adding a dump_loc member function
> >   dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
> >   C++: clean up cp_printer
> >   c-family: clean up the data tables in c-format.c
> >   Formatted printing for dump_* in the middle-end
> > 
> >  gcc/c-family/c-format.c                   |  159 +++--
> >  gcc/c-family/c-format.h                   |    1 +
> >  gcc/cp/error.c                            |   46 +-
> >  gcc/dump-context.h                        |   25 +-
> >  gcc/dumpfile.c                            | 1011
> > ++++++++++++++++++++++-------
> >  gcc/dumpfile.h                            |   54 +-
> >  gcc/optinfo-emit-json.cc                  |    2 +-
> >  gcc/optinfo.cc                            |  135 +---
> >  gcc/optinfo.h                             |   38 +-
> >  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |   19 +-
> >  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |   33 +-
> >  11 files changed, 998 insertions(+), 525 deletions(-)
> > 
> > --
> > 1.8.5.3
> > 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 5/5] Formatted printing for dump_* in the middle-end
  2018-07-31 13:03   ` Richard Biener
@ 2018-07-31 14:19     ` David Malcolm
  2018-07-31 14:21       ` Richard Biener
  2018-07-31 19:56       ` Joseph Myers
  0 siblings, 2 replies; 29+ messages in thread
From: David Malcolm @ 2018-07-31 14:19 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Tue, 2018-07-31 at 15:03 +0200, Richard Biener wrote:
> On Fri, Jul 27, 2018 at 11:49 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > This patch converts dump_print and dump_printf_loc from using
> > printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
> > based on pp_format, which supports formatting middle-end types.
> > 
> > In particular, the following codes are implemented (in addition
> > to the standard pretty_printer ones):
> > 
> >    %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 (MSG_*, arg, TDF_SLIM).
> > 
> > Hence it becomes possible to convert e.g.:
> > 
> >   if (dump_enabled_p ())
> >     {
> >       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> >                        "not vectorized: different sized vector "
> >                        "types in statement, ");
> >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > vectype);
> >       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
> >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > nunits_vectype);
> >       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
> >     }
> > 
> > into a one-liner:
> > 
> >   if (dump_enabled_p ())
> >     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> >                      "not vectorized: different sized vector "
> >                      "types in statement, %T and %T\n",
> >                      vectype, nunits_vectype);
> > 
> > Unlike regular pretty-printers, this one captures optinfo_item
> > instances for the formatted chunks as appropriate, so that when
> > written out to a JSON optimization record, the relevant parts of
> > the message are labelled by type, and by source location (so that
> > e.g. %G is entirely equivalent to using dump_gimple_stmt).
> > 
> > dump_printf and dump_printf_loc become marked with
> > ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.
> > 
> > gcc/c-family/ChangeLog:
> >         * c-format.c (enum format_type): Add
> > gcc_dump_printf_format_type.
> >         (local_gimple_ptr_node): New decl.
> >         (gcc_dump_printf_length_specs): New.
> >         (gcc_dump_printf_flag_pairs): New.
> >         (gcc_dump_printf_flag_specs): New.
> >         (gcc_dump_printf_char_table): New.
> >         (format_types_orig): Add entry for "gcc_dump_printf".
> >         (init_dynamic_diag_info): Create local_gimple_ptr_node.
> >         Set up length_char_specs and conversion_specs for
> >         gcc_dump_printf_format_type.
> >         (handle_format_attribute): Handle
> > gcc_dump_printf_format_type.
> >         * c-format.h (T89_GIMPLE): New macro.
> 
> Iff the c-family changes are neccessary (are they?) then how does
> this
> work for non-c-family languages which do not link c-family/c-
> format.o?

The c-family changes are necessary for bootstrap, so that -Wformat
works cleanly after changing dump_printf_loc etc from
  ATTRIBUTE_PRINTF_3;
to
  ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
i.e. they're just the changes to -Wformat to teach it how to verify the
new:
  __attribute__ ((__format__ (__gcc_dump_printf__, m ,n)))
(hence the cleanups to c-format.c earlier in the patch kit, to avoid
yet more copy-and-paste there for the new format decoder callback).

The implementation itself is all within dumpfile.c, hence the non-c-
family languages ought to work.  My testing was with:
  --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto,jit,brig
(and with cloog and isl, fwiw).

(I kept the alphabetization of the ChangeLog files from my generate-
changelog.py script, which put the gcc/c-family/ChangeLog before the
gcc/ChangeLog and thus may have made this confusing to read, sorry).

I didn't exhaustively check every callsite to the changed calls; I'm
assuming that -Wformat during bootstrap has effectively checked that
for me.  Though now I think about it, I note that we use
HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to be a
valid input to pp_format on all of our configurations?

Dave

> 
> > gcc/ChangeLog:
> >         * dump-context.h: Include "dumpfile.h".
> >         (dump_context::dump_printf_va): Convert final param from
> > va_list
> >         to va_list *.  Convert from ATTRIBUTE_PRINTF to
> >         ATTRIBUTE_GCC_DUMP_PRINTF.
> >         (dump_context::dump_printf_loc_va): Likewise.
> >         * dumpfile.c: Include "stringpool.h".
> >         (make_item_for_dump_printf_va): Delete.
> >         (make_item_for_dump_printf): Delete.
> >         (class dump_pretty_printer): New class.
> >         (dump_pretty_printer::dump_pretty_printer): New ctor.
> >         (dump_pretty_printer::emit_items): New member function.
> >         (dump_pretty_printer::emit_any_pending_textual_chunks): New
> > member
> >         function.
> >         (dump_pretty_printer::emit_item): New member function.
> >         (dump_pretty_printer::stash_item): New member function.
> >         (dump_pretty_printer::format_decoder_cb): New member
> > function.
> >         (dump_pretty_printer::decode_format): New member function.
> >         (dump_context::dump_printf_va): Reimplement in terms of
> >         dump_pretty_printer.
> >         (dump_context::dump_printf_loc_va): Convert final param
> > from va_list
> >         to va_list *.
> >         (dump_context::begin_scope): Reimplement call to
> >         make_item_for_dump_printf.
> >         (dump_printf): Update for change to dump_printf_va.
> >         (dump_printf_loc): Likewise.
> >         (selftest::test_capture_of_dump_calls): Convert "stmt" from
> >         greturn * to gimple *.  Add a test_decl.  Add tests of
> > dump_printf
> >         with %T, %E, and %G.
> >         * dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
> >         (dump_printf): Replace ATTRIBUTE_PRINTF_2 with
> >         ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
> >         (dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
> >         ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
> > 
> > gcc/testsuite/ChangeLog:
> >         * gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage
> > for
> >         gcc_dump_printf.
> >         * gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add
> > test
> >         coverage for gcc_dump_printf.
> > ---
> >  gcc/c-family/c-format.c                   |  60 ++++-
> >  gcc/c-family/c-format.h                   |   1 +
> >  gcc/dump-context.h                        |   7 +-
> >  gcc/dumpfile.c                            | 358
> > +++++++++++++++++++++++++++---
> >  gcc/dumpfile.h                            |  20 +-
> >  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  15 +-
> >  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  26 +++
> >  7 files changed, 448 insertions(+), 39 deletions(-)
> > 
> > diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
> > index 82841e4..b524468 100644
> > --- a/gcc/c-family/c-format.c
> > +++ b/gcc/c-family/c-format.c
> > @@ -44,6 +44,7 @@ enum format_type { printf_format_type,
> > asm_fprintf_format_type,
> >                    gcc_diag_format_type, gcc_tdiag_format_type,
> >                    gcc_cdiag_format_type,
> >                    gcc_cxxdiag_format_type, gcc_gfc_format_type,
> > +                  gcc_dump_printf_format_type,
> >                    gcc_objc_string_format_type,
> >                    format_type_error = -1};
> > 
> > @@ -56,6 +57,7 @@ struct function_format_info
> > 
> >  /* Initialized in init_dynamic_diag_info.  */
> >  static GTY(()) tree local_tree_type_node;
> > +static GTY(()) tree local_gimple_ptr_node;
> >  static GTY(()) tree local_gcall_ptr_node;
> >  static GTY(()) tree locus;
> > 
> > @@ -461,6 +463,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 +553,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 +569,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[] =
> >  {
> > @@ -786,6 +791,22 @@ static const format_char_info
> > gcc_gfc_char_table[] =
> >    { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> >  };
> > 
> > +static const format_char_info gcc_dump_printf_char_table[] =
> > +{
> > +  /* The conversion specifiers implemented within pp_format.  */
> > +  PP_FORMAT_CHAR_TABLE,
> > +
> > +  /* Custom conversion specifiers implemented by
> > dump_pretty_printer.  */
> > +
> > +  /* E and G require a "gimple *" argument at runtime.  */
> > +  { "EG",   1, STD_C89, {
> > T89_GIMPLE,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN, 
> >  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> > +
> > +  /* T requires a "tree" at runtime.  */
> > +  { "T",   1, STD_C89, {
> > T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADL
> > EN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> > +
> > +  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> > +};
> > +
> >  static const format_char_info scan_char_table[] =
> >  {
> >    /* C89 conversion specifiers.  */
> > @@ -885,6 +906,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,
> > @@ -3838,6 +3866,29 @@ init_dynamic_diag_info (void)
> >         local_tree_type_node = void_type_node;
> >      }
> > 
> > +  /* Similar to the above but for gimple *.  */
> > +  if (!local_gimple_ptr_node
> > +      || local_gimple_ptr_node == void_type_node)
> > +    {
> > +      if ((local_gimple_ptr_node = maybe_get_identifier
> > ("gimple")))
> > +       {
> > +         local_gimple_ptr_node
> > +           = identifier_global_value (local_gimple_ptr_node);
> > +         if (local_gimple_ptr_node)
> > +           {
> > +             if (TREE_CODE (local_gimple_ptr_node) != TYPE_DECL)
> > +               {
> > +                 error ("%<gimple%> is not defined as a type");
> > +                 local_gimple_ptr_node = 0;
> > +               }
> > +             else
> > +               local_gimple_ptr_node = TREE_TYPE
> > (local_gimple_ptr_node);
> > +           }
> > +       }
> > +      else
> > +       local_gimple_ptr_node = void_type_node;
> > +    }
> > +
> >    /* Similar to the above but for gcall*.  */
> >    if (!local_gcall_ptr_node
> >        || local_gcall_ptr_node == void_type_node)
> > @@ -3905,6 +3956,7 @@ init_dynamic_diag_info (void)
> >           dynamic_format_types[gcc_tdiag_format_type].length_char_s
> > pecs =
> >           dynamic_format_types[gcc_cdiag_format_type].length_char_s
> > pecs =
> >           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),
> > @@ -3931,6 +3983,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_spe
> > cs =
> > +    gcc_dump_printf_char_table;
> >  }
> > 
> >  #ifdef TARGET_FORMAT_TYPES
> > @@ -4085,7 +4139,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.  */
> > @@ -4107,7 +4162,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/c-family/c-format.h b/gcc/c-family/c-format.h
> > index f828e77..4dfdbdb 100644
> > --- a/gcc/c-family/c-format.h
> > +++ b/gcc/c-family/c-format.h
> > @@ -298,6 +298,7 @@ struct format_kind_info
> >  #define T_UC   &unsigned_char_type_node
> >  #define T99_UC { STD_C99, NULL, T_UC }
> >  #define T_V    &void_type_node
> > +#define T89_GIMPLE   { STD_C89, NULL, &local_gimple_ptr_node }
> >  #define T89_G   { STD_C89, NULL, &local_gcall_ptr_node }
> >  #define T89_T   { STD_C89, NULL, &local_tree_type_node }
> >  #define T89_V  { STD_C89, NULL, T_V }
> > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > index f40ea14..54095d3 100644
> > --- a/gcc/dump-context.h
> > +++ b/gcc/dump-context.h
> > @@ -22,6 +22,7 @@ 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
> >  #include "pretty-print.h"
> > 
> >  /* A class for handling the various dump_* calls.
> > @@ -73,11 +74,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);
> > +                          const char *format, va_list *ap)
> > +    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 10e9cab..7131f56 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.  */
> > @@ -681,56 +682,262 @@ dump_context::dump_generic_expr_loc
> > (dump_flags_t dump_kind,
> >    dump_generic_expr (dump_kind, extra_dump_flags, t);
> >  }
> > 
> > -/* Make an item for the given dump call.  */
> > +/* A subclass of pretty_printer for implementing
> > dump_context::dump_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.  */
> > 
> > -static optinfo_item *
> > -make_item_for_dump_printf_va (const char *format, va_list ap)
> > -  ATTRIBUTE_PRINTF (1, 0);
> > +class dump_pretty_printer : public pretty_printer
> > +{
> > +public:
> > +  dump_pretty_printer (dump_context *context, dump_flags_t
> > dump_kind);
> > 
> > -static optinfo_item *
> > -make_item_for_dump_printf_va (const char *format, va_list ap)
> > +  void emit_items (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 emit_any_pending_textual_chunks (optinfo *dest);
> > +
> > +  void emit_item (optinfo_item *item, optinfo *dest);
> > +
> > +  dump_context *m_context;
> > +  dump_flags_t m_dump_kind;
> > +  auto_vec<stashed_item> m_stashed_items;
> > +};
> > +
> > +/* dump_pretty_printer's ctor.  */
> > +
> > +dump_pretty_printer::dump_pretty_printer (dump_context *context,
> > +                                         dump_flags_t dump_kind)
> > +: pretty_printer (), m_context (context), m_dump_kind (dump_kind),
> > +  m_stashed_items ()
> > +{
> > +  pp_format_decoder (this) = format_decoder_cb;
> > +}
> > +
> > +/* Phase 3 of formatting; compare with pp_output_formatted_text.
> > +
> > +   Emit optinfo_item instances for the various formatted chunks
> > from phases
> > +   1 and 2 (i.e. pp_format).
> > +
> > +   Some chunks may already have had their items built (during
> > decode_format).
> > +   These chunks have been stashed into m_stashed_items; we emit
> > them here.
> > +
> > +   For all other purely textual chunks, they are printed into
> > +   buffer->formatted_obstack, and then emitted as a textual
> > optinfo_item.
> > +   This consolidates multiple adjacent text chunks into a single
> > text
> > +   optinfo_item.  */
> > +
> > +void
> > +dump_pretty_printer::emit_items (optinfo *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)
> > +       {
> > +         emit_any_pending_textual_chunks (dest);
> > +         /* This chunk has a stashed item: use it.  */
> > +         emit_item (m_stashed_items[stashed_item_idx++].item,
> > dest);
> > +       }
> > +      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]);
> > +    }
> > +
> > +  emit_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 dump_pretty_printer::emit_items
> > +   for consolidating multiple adjacent pure-text chunks into
> > single
> > +   optinfo_items (in phase 3).  */
> > +
> > +void
> > +dump_pretty_printer::emit_any_pending_textual_chunks (optinfo
> > *dest)
> >  {
> > -  char *formatted_text = xvasprintf (format, ap);
> > +  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
> > +
> > +  /* Don't emit 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);
> > -  return item;
> > +  emit_item (item, dest);
> > +
> > +  /* Clear the pending text by unwinding formatted_text back to
> > the start
> > +     of the buffer (without deallocating).  */
> > +  obstack_free (&buffer->formatted_obstack,
> > +               buffer->formatted_obstack.object_base);
> >  }
> > 
> > -/* Make an item for the given dump call.  */
> > +/* Emit ITEM and take ownership of it.  If DEST is non-NULL, add
> > ITEM
> > +   to DEST; otherwise delete ITEM.  */
> > 
> > -static optinfo_item *
> > -make_item_for_dump_printf (const char *format, ...)
> > -  ATTRIBUTE_PRINTF (1, 2);
> > +void
> > +dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest)
> > +{
> > +  m_context->emit_item (item, m_dump_kind);
> > +  if (dest)
> > +    dest->add_item (item);
> > +  else
> > +    delete item;
> > +}
> > 
> > -static optinfo_item *
> > -make_item_for_dump_printf (const char *format, ...)
> > +/* Record that ITEM (generated in phase 2 of formatting) is to be
> > used for
> > +   the chunk at BUFFER_PTR in phase 3 (by emit_items).  */
> > +
> > +void
> > +dump_pretty_printer::stash_item (const char **buffer_ptr,
> > optinfo_item *item)
> >  {
> > -  va_list ap;
> > -  va_start (ap, format);
> > -  optinfo_item *item
> > -    = make_item_for_dump_printf_va (format, ap);
> > -  va_end (ap);
> > -  return item;
> > +  gcc_assert (buffer_ptr);
> > +  gcc_assert (item);
> > +
> > +  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
> > +}
> > +
> > +/* pp_format_decoder callback for dump_pretty_printer, and thus
> > for
> > +   dump_printf and dump_printf_loc.
> > +
> > +   A wrapper around decode_format, for type-safety.  */
> > +
> > +bool
> > +dump_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)
> > +{
> > +  dump_pretty_printer *opp = static_cast <dump_pretty_printer *>
> > (pp);
> > +  return opp->decode_format (text, spec, buffer_ptr);
> > +}
> > +
> > +/* Format decoder for dump_pretty_printer, 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 (MSG_*, arg, TDF_SLIM).
> > +
> > +   FIXME: add symtab_node?
> > +
> > +   These format codes build optinfo_item instances, thus capturing
> > metadata
> > +   about the arguments being dumped, as well as the textual
> > output.  */
> > +
> > +bool
> > +dump_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).  */
> > +  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_dump_gimple_expr (stmt,
> > 0, TDF_SLIM);
> > +       stash_item (buffer_ptr, item);
> > +       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_dump_gimple_stmt (stmt,
> > 0, TDF_SLIM);
> > +       stash_item (buffer_ptr, item);
> > +       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_dump_generic_expr (t,
> > TDF_SLIM);
> > +       stash_item (buffer_ptr, item);
> > +       return true;
> > +      }
> > +
> > +    default:
> > +      return false;
> > +    }
> >  }
> > 
> >  /* Output a formatted message using FORMAT on appropriate dump
> > streams.  */
> > 
> >  void
> >  dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > *format,
> > -                             va_list ap)
> > +                             va_list *ap)
> >  {
> > -  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> > -  emit_item (item, dump_kind);
> > +  dump_pretty_printer pp (this, dump_kind);
> > 
> > +  text_info text;
> > +  text.err_no = errno;
> > +  text.args_ptr = ap;
> > +  text.format_spec = format;
> > +
> > +  /* Phases 1 and 2, using pp_format.  */
> > +  pp_format (&pp, &text);
> > +
> > +  /* Phase 3.  */
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> >        info.handle_dump_file_kind (dump_kind);
> > -      info.add_item (item);
> > +      pp.emit_items (&info);
> >      }
> >    else
> > -    delete item;
> > +    pp.emit_items (NULL);
> >  }
> > 
> >  /* Similar to dump_printf, except source location is also printed,
> > and
> > @@ -739,7 +946,7 @@ dump_context::dump_printf_va (dump_flags_t
> > dump_kind, const char *format,
> >  void
> >  dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
> >                                   const dump_location_t &loc,
> > -                                 const char *format, va_list ap)
> > +                                 const char *format, va_list *ap)
> >  {
> >    dump_loc (dump_kind, loc);
> >    dump_printf_va (dump_kind, format, ap);
> > @@ -851,7 +1058,11 @@ dump_context::begin_scope (const char *name,
> > const dump_location_t &loc)
> >    if (m_test_pp)
> >      ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
> > 
> > -  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n",
> > name);
> > +  pretty_printer pp;
> > +  pp_printf (&pp, "=== %s ===\n", name);
> > +  optinfo_item *item
> > +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > +                       xstrdup (pp_formatted_text (&pp)));
> >    emit_item (item, MSG_NOTE);
> > 
> >    if (optinfo_enabled_p ())
> > @@ -859,7 +1070,6 @@ dump_context::begin_scope (const char *name,
> > const dump_location_t &loc)
> >        optinfo &info = begin_next_optinfo (loc);
> >        info.m_kind = OPTINFO_KIND_SCOPE;
> >        info.add_item (item);
> > -      end_any_optinfo ();
> >      }
> >    else
> >      delete item;
> > @@ -1006,7 +1216,7 @@ dump_printf (dump_flags_t dump_kind, const
> > char *format, ...)
> >  {
> >    va_list ap;
> >    va_start (ap, format);
> > -  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> > +  dump_context::get ().dump_printf_va (dump_kind, format, &ap);
> >    va_end (ap);
> >  }
> > 
> > @@ -1019,7 +1229,7 @@ dump_printf_loc (dump_flags_t dump_kind,
> > const dump_location_t &loc,
> >  {
> >    va_list ap;
> >    va_start (ap, format);
> > -  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > ap);
> > +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > &ap);
> >    va_end (ap);
> >  }
> > 
> > @@ -1808,9 +2018,12 @@ test_capture_of_dump_calls (const
> > line_table_case &case_)
> > 
> >    dump_location_t loc = dump_location_t::from_location_t (where);
> > 
> > -  greturn *stmt = gimple_build_return (NULL);
> > +  gimple *stmt = gimple_build_return (NULL);
> >    gimple_set_location (stmt, where);
> > 
> > +  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> > +                              get_identifier ("test_decl"),
> > +                              integer_type_node);
> >    /* Run all tests twice, with and then without optinfo enabled,
> > to ensure
> >       that immediate destinations vs optinfo-based destinations
> > both
> >       work, independently of each other, with no leaks.  */
> > @@ -1834,6 +2047,89 @@ test_capture_of_dump_calls (const
> > line_table_case &case_)
> >           }
> >        }
> > 
> > +      /* Test of dump_printf with %T.  */
> > +      {
> > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +       dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "tree: 0");
> > +       if (with_optinfo)
> > +         {
> > +           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 (with_optinfo, MSG_ALL);
> > +       dump_printf (MSG_NOTE, "gimple: %E", stmt);
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;");
> > +       if (with_optinfo)
> > +         {
> > +           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 (with_optinfo, MSG_ALL);
> > +       dump_printf (MSG_NOTE, "gimple: %G", stmt);
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;\n");
> > +       if (with_optinfo)
> > +         {
> > +           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 (with_optinfo, MSG_ALL);
> > +       dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
> > +                        " %i consecutive %E%E after\n",
> > +                        integer_zero_node, test_decl, 42, stmt,
> > stmt);
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp,
> > +                              "test.txt:5:10: note: before 0 and
> > test_decl"
> > +                              " 42 consecutive return;return;
> > after\n");
> > +       if (with_optinfo)
> > +         {
> > +           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 (with_optinfo, MSG_ALL);
> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > index 2b174e5..1fe64cb 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 dumpfile.c).  */
> > +
> > +#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
> > @@ -476,9 +489,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/testsuite/gcc.dg/format/gcc_diag-1.c
> > b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > index 034e097..8761456 100644
> > --- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > @@ -1,4 +1,4 @@
> > -/* Test for GCC diagnositc formats.  */
> > +/* Test for GCC diagnostic formats.  */
> >  /* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
> >  /* { dg-do compile } */
> >  /* { dg-options "-Wformat" } */
> > @@ -24,6 +24,7 @@ extern int diag (const char *, ...)
> > ATTRIBUTE_DIAG(__gcc_diag__);
> >  extern int tdiag (const char *, ...)
> > ATTRIBUTE_DIAG(__gcc_tdiag__);
> >  extern int cdiag (const char *, ...)
> > ATTRIBUTE_DIAG(__gcc_cdiag__);
> >  extern int cxxdiag (const char *, ...)
> > ATTRIBUTE_DIAG(__gcc_cxxdiag__);
> > +extern int dump (const char *, ...)
> > ATTRIBUTE_DIAG(__gcc_dump_printf__);
> > 
> >  void
> >  foo (int i, int i1, int i2, unsigned int u, double d, char *s,
> > void *p,
> > @@ -39,36 +40,44 @@ foo (int i, int i1, int i2, unsigned int u,
> > double d, char *s, void *p,
> >    tdiag ("%%");
> >    cdiag ("%%");
> >    cxxdiag ("%%");
> > +  dump ("%%");
> >    diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> >    tdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> >    cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> >    cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > +  dump ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> >    diag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > p);
> >    tdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > p);
> >    cdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > p);
> >    cxxdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i,
> > s, p);
> > +  dump ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > p);
> >    diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> >    tdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> >    cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> >    cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > +  dump ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> >    diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> >    tdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> >    cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> >    cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > +  dump ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> >    diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> >    tdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> >    cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> >    cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > +  dump ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> >    diag ("%.*s", i, s);
> >    tdiag ("%.*s", i, s);
> >    cdiag ("%.*s", i, s);
> >    cxxdiag ("%.*s", i, s);
> > +  dump ("%.*s", i, s);
> > 
> >    /* Extensions provided in the diagnostic framework.  */
> >    diag ("%m");
> >    tdiag ("%m");
> >    cdiag ("%m");
> >    cxxdiag ("%m");
> > +  dump ("%m");
> > 
> >    /* Quote directives to avoid "warning: conversion used
> > unquoted." */
> >    tdiag ("%<%D%F%T%V%>", t1, t1, t1, t1);
> > @@ -94,20 +103,24 @@ foo (int i, int i1, int i2, unsigned int u,
> > double d, char *s, void *p,
> >    tdiag ("%Z", v, v_len);
> >    cdiag ("%Z", v, v_len);
> >    cxxdiag ("%Z", v, v_len);
> > +  dump ("%Z", v, v_len);
> > 
> >    /* Bad stuff with extensions.  */
> >    diag ("%m", i); /* { dg-warning "format" "extra arg" } */
> >    tdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> >    cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> >    cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > +  dump ("%m", i); /* { dg-warning "format" "extra arg" } */
> >    diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> >    tdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> >    cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> >    cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > +  dump ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> >    diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> >    tdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> >    cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> >    cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > +  dump ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> >    diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
> >    tdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
> >    tdiag ("%E", t1);
> > diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > index ab9bc2f..8b5aadf1 100644
> > --- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > @@ -19,12 +19,16 @@ typedef union tree_node *tree;
> >     the C test to find the symbol.  */
> >  typedef struct gcall gcall;
> > 
> > +/* Likewise for gimple.  */
> > +typedef struct gimple gimple;
> > +
> >  #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__,
> > 1, 2)))
> > 
> >  void diag (const char*, ...) FORMAT (diag);
> >  void cdiag (const char*, ...) FORMAT (cdiag);
> >  void tdiag (const char*, ...) FORMAT (tdiag);
> >  void cxxdiag (const char*, ...) FORMAT (cxxdiag);
> > +void dump (const char*, ...) FORMAT (dump_printf);
> > 
> >  void test_diag (tree t, gcall *gc)
> >  {
> > @@ -157,3 +161,25 @@ void test_cxxdiag (tree t, gcall *gc)
> >    cxxdiag ("%<%V%>", t);
> >    cxxdiag ("%<%X%>", t);
> >  }
> > +
> > +void test_dump (tree t, gimple *stmt)
> > +{
> > +  dump ("%<");   /* { dg-warning "unterminated quoting directive"
> > } */
> > +  dump ("%>");   /* { dg-warning "unmatched quoting directive " }
> > */
> > +  dump ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting
> > directive" } */
> > +
> > +  dump ("%R");       /* { dg-warning "unmatched color reset
> > directive" } */
> > +  dump ("%r", "");   /* { dg-warning "unterminated color
> > directive" } */
> > +  dump ("%r%r", "", "");   /* { dg-warning "unterminated color
> > directive" } */
> > +  dump ("%r%R", "");
> > +  dump ("%r%r%R", "", "");
> > +  dump ("%r%R%r%R", "", "");
> > +
> > +  dump ("%<%R%>");      /* { dg-warning "unmatched color reset
> > directive" } */
> > +  dump ("%<%r%>", "");  /* { dg-warning "unterminated color
> > directive" } */
> > +  dump ("%<%r%R%>", "");
> > +
> > +  dump ("%E", stmt);
> > +  dump ("%T", t);
> > +  dump ("%G", stmt);
> > +}
> > --
> > 1.8.5.3
> > 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 5/5] Formatted printing for dump_* in the middle-end
  2018-07-31 14:19     ` David Malcolm
@ 2018-07-31 14:21       ` Richard Biener
  2018-07-31 14:33         ` Richard Biener
  2018-07-31 19:56       ` Joseph Myers
  1 sibling, 1 reply; 29+ messages in thread
From: Richard Biener @ 2018-07-31 14:21 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jul 31, 2018 at 4:19 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Tue, 2018-07-31 at 15:03 +0200, Richard Biener wrote:
> > On Fri, Jul 27, 2018 at 11:49 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > This patch converts dump_print and dump_printf_loc from using
> > > printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
> > > based on pp_format, which supports formatting middle-end types.
> > >
> > > In particular, the following codes are implemented (in addition
> > > to the standard pretty_printer ones):
> > >
> > >    %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 (MSG_*, arg, TDF_SLIM).
> > >
> > > Hence it becomes possible to convert e.g.:
> > >
> > >   if (dump_enabled_p ())
> > >     {
> > >       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> > >                        "not vectorized: different sized vector "
> > >                        "types in statement, ");
> > >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > > vectype);
> > >       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
> > >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > > nunits_vectype);
> > >       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
> > >     }
> > >
> > > into a one-liner:
> > >
> > >   if (dump_enabled_p ())
> > >     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> > >                      "not vectorized: different sized vector "
> > >                      "types in statement, %T and %T\n",
> > >                      vectype, nunits_vectype);
> > >
> > > Unlike regular pretty-printers, this one captures optinfo_item
> > > instances for the formatted chunks as appropriate, so that when
> > > written out to a JSON optimization record, the relevant parts of
> > > the message are labelled by type, and by source location (so that
> > > e.g. %G is entirely equivalent to using dump_gimple_stmt).
> > >
> > > dump_printf and dump_printf_loc become marked with
> > > ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.
> > >
> > > gcc/c-family/ChangeLog:
> > >         * c-format.c (enum format_type): Add
> > > gcc_dump_printf_format_type.
> > >         (local_gimple_ptr_node): New decl.
> > >         (gcc_dump_printf_length_specs): New.
> > >         (gcc_dump_printf_flag_pairs): New.
> > >         (gcc_dump_printf_flag_specs): New.
> > >         (gcc_dump_printf_char_table): New.
> > >         (format_types_orig): Add entry for "gcc_dump_printf".
> > >         (init_dynamic_diag_info): Create local_gimple_ptr_node.
> > >         Set up length_char_specs and conversion_specs for
> > >         gcc_dump_printf_format_type.
> > >         (handle_format_attribute): Handle
> > > gcc_dump_printf_format_type.
> > >         * c-format.h (T89_GIMPLE): New macro.
> >
> > Iff the c-family changes are neccessary (are they?) then how does
> > this
> > work for non-c-family languages which do not link c-family/c-
> > format.o?
>
> The c-family changes are necessary for bootstrap, so that -Wformat
> works cleanly after changing dump_printf_loc etc from
>   ATTRIBUTE_PRINTF_3;
> to
>   ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
> i.e. they're just the changes to -Wformat to teach it how to verify the
> new:
>   __attribute__ ((__format__ (__gcc_dump_printf__, m ,n)))
> (hence the cleanups to c-format.c earlier in the patch kit, to avoid
> yet more copy-and-paste there for the new format decoder callback).

Ah, thanks for clarifying.

> The implementation itself is all within dumpfile.c, hence the non-c-
> family languages ought to work.  My testing was with:
>   --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto,jit,brig
> (and with cloog and isl, fwiw).
>
> (I kept the alphabetization of the ChangeLog files from my generate-
> changelog.py script, which put the gcc/c-family/ChangeLog before the
> gcc/ChangeLog and thus may have made this confusing to read, sorry).
>
> I didn't exhaustively check every callsite to the changed calls; I'm
> assuming that -Wformat during bootstrap has effectively checked that
> for me.  Though now I think about it, I note that we use
> HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to be a
> valid input to pp_format on all of our configurations?

I hope so ... returning to the patch now.

Richard.

> Dave
>
> >
> > > gcc/ChangeLog:
> > >         * dump-context.h: Include "dumpfile.h".
> > >         (dump_context::dump_printf_va): Convert final param from
> > > va_list
> > >         to va_list *.  Convert from ATTRIBUTE_PRINTF to
> > >         ATTRIBUTE_GCC_DUMP_PRINTF.
> > >         (dump_context::dump_printf_loc_va): Likewise.
> > >         * dumpfile.c: Include "stringpool.h".
> > >         (make_item_for_dump_printf_va): Delete.
> > >         (make_item_for_dump_printf): Delete.
> > >         (class dump_pretty_printer): New class.
> > >         (dump_pretty_printer::dump_pretty_printer): New ctor.
> > >         (dump_pretty_printer::emit_items): New member function.
> > >         (dump_pretty_printer::emit_any_pending_textual_chunks): New
> > > member
> > >         function.
> > >         (dump_pretty_printer::emit_item): New member function.
> > >         (dump_pretty_printer::stash_item): New member function.
> > >         (dump_pretty_printer::format_decoder_cb): New member
> > > function.
> > >         (dump_pretty_printer::decode_format): New member function.
> > >         (dump_context::dump_printf_va): Reimplement in terms of
> > >         dump_pretty_printer.
> > >         (dump_context::dump_printf_loc_va): Convert final param
> > > from va_list
> > >         to va_list *.
> > >         (dump_context::begin_scope): Reimplement call to
> > >         make_item_for_dump_printf.
> > >         (dump_printf): Update for change to dump_printf_va.
> > >         (dump_printf_loc): Likewise.
> > >         (selftest::test_capture_of_dump_calls): Convert "stmt" from
> > >         greturn * to gimple *.  Add a test_decl.  Add tests of
> > > dump_printf
> > >         with %T, %E, and %G.
> > >         * dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
> > >         (dump_printf): Replace ATTRIBUTE_PRINTF_2 with
> > >         ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
> > >         (dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
> > >         ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
> > >
> > > gcc/testsuite/ChangeLog:
> > >         * gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage
> > > for
> > >         gcc_dump_printf.
> > >         * gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add
> > > test
> > >         coverage for gcc_dump_printf.
> > > ---
> > >  gcc/c-family/c-format.c                   |  60 ++++-
> > >  gcc/c-family/c-format.h                   |   1 +
> > >  gcc/dump-context.h                        |   7 +-
> > >  gcc/dumpfile.c                            | 358
> > > +++++++++++++++++++++++++++---
> > >  gcc/dumpfile.h                            |  20 +-
> > >  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  15 +-
> > >  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  26 +++
> > >  7 files changed, 448 insertions(+), 39 deletions(-)
> > >
> > > diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
> > > index 82841e4..b524468 100644
> > > --- a/gcc/c-family/c-format.c
> > > +++ b/gcc/c-family/c-format.c
> > > @@ -44,6 +44,7 @@ enum format_type { printf_format_type,
> > > asm_fprintf_format_type,
> > >                    gcc_diag_format_type, gcc_tdiag_format_type,
> > >                    gcc_cdiag_format_type,
> > >                    gcc_cxxdiag_format_type, gcc_gfc_format_type,
> > > +                  gcc_dump_printf_format_type,
> > >                    gcc_objc_string_format_type,
> > >                    format_type_error = -1};
> > >
> > > @@ -56,6 +57,7 @@ struct function_format_info
> > >
> > >  /* Initialized in init_dynamic_diag_info.  */
> > >  static GTY(()) tree local_tree_type_node;
> > > +static GTY(()) tree local_gimple_ptr_node;
> > >  static GTY(()) tree local_gcall_ptr_node;
> > >  static GTY(()) tree locus;
> > >
> > > @@ -461,6 +463,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 +553,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 +569,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[] =
> > >  {
> > > @@ -786,6 +791,22 @@ static const format_char_info
> > > gcc_gfc_char_table[] =
> > >    { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> > >  };
> > >
> > > +static const format_char_info gcc_dump_printf_char_table[] =
> > > +{
> > > +  /* The conversion specifiers implemented within pp_format.  */
> > > +  PP_FORMAT_CHAR_TABLE,
> > > +
> > > +  /* Custom conversion specifiers implemented by
> > > dump_pretty_printer.  */
> > > +
> > > +  /* E and G require a "gimple *" argument at runtime.  */
> > > +  { "EG",   1, STD_C89, {
> > > T89_GIMPLE,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,
> > >  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> > > +
> > > +  /* T requires a "tree" at runtime.  */
> > > +  { "T",   1, STD_C89, {
> > > T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADL
> > > EN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> > > +
> > > +  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> > > +};
> > > +
> > >  static const format_char_info scan_char_table[] =
> > >  {
> > >    /* C89 conversion specifiers.  */
> > > @@ -885,6 +906,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,
> > > @@ -3838,6 +3866,29 @@ init_dynamic_diag_info (void)
> > >         local_tree_type_node = void_type_node;
> > >      }
> > >
> > > +  /* Similar to the above but for gimple *.  */
> > > +  if (!local_gimple_ptr_node
> > > +      || local_gimple_ptr_node == void_type_node)
> > > +    {
> > > +      if ((local_gimple_ptr_node = maybe_get_identifier
> > > ("gimple")))
> > > +       {
> > > +         local_gimple_ptr_node
> > > +           = identifier_global_value (local_gimple_ptr_node);
> > > +         if (local_gimple_ptr_node)
> > > +           {
> > > +             if (TREE_CODE (local_gimple_ptr_node) != TYPE_DECL)
> > > +               {
> > > +                 error ("%<gimple%> is not defined as a type");
> > > +                 local_gimple_ptr_node = 0;
> > > +               }
> > > +             else
> > > +               local_gimple_ptr_node = TREE_TYPE
> > > (local_gimple_ptr_node);
> > > +           }
> > > +       }
> > > +      else
> > > +       local_gimple_ptr_node = void_type_node;
> > > +    }
> > > +
> > >    /* Similar to the above but for gcall*.  */
> > >    if (!local_gcall_ptr_node
> > >        || local_gcall_ptr_node == void_type_node)
> > > @@ -3905,6 +3956,7 @@ init_dynamic_diag_info (void)
> > >           dynamic_format_types[gcc_tdiag_format_type].length_char_s
> > > pecs =
> > >           dynamic_format_types[gcc_cdiag_format_type].length_char_s
> > > pecs =
> > >           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),
> > > @@ -3931,6 +3983,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_spe
> > > cs =
> > > +    gcc_dump_printf_char_table;
> > >  }
> > >
> > >  #ifdef TARGET_FORMAT_TYPES
> > > @@ -4085,7 +4139,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.  */
> > > @@ -4107,7 +4162,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/c-family/c-format.h b/gcc/c-family/c-format.h
> > > index f828e77..4dfdbdb 100644
> > > --- a/gcc/c-family/c-format.h
> > > +++ b/gcc/c-family/c-format.h
> > > @@ -298,6 +298,7 @@ struct format_kind_info
> > >  #define T_UC   &unsigned_char_type_node
> > >  #define T99_UC { STD_C99, NULL, T_UC }
> > >  #define T_V    &void_type_node
> > > +#define T89_GIMPLE   { STD_C89, NULL, &local_gimple_ptr_node }
> > >  #define T89_G   { STD_C89, NULL, &local_gcall_ptr_node }
> > >  #define T89_T   { STD_C89, NULL, &local_tree_type_node }
> > >  #define T89_V  { STD_C89, NULL, T_V }
> > > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > > index f40ea14..54095d3 100644
> > > --- a/gcc/dump-context.h
> > > +++ b/gcc/dump-context.h
> > > @@ -22,6 +22,7 @@ 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
> > >  #include "pretty-print.h"
> > >
> > >  /* A class for handling the various dump_* calls.
> > > @@ -73,11 +74,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);
> > > +                          const char *format, va_list *ap)
> > > +    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 10e9cab..7131f56 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.  */
> > > @@ -681,56 +682,262 @@ dump_context::dump_generic_expr_loc
> > > (dump_flags_t dump_kind,
> > >    dump_generic_expr (dump_kind, extra_dump_flags, t);
> > >  }
> > >
> > > -/* Make an item for the given dump call.  */
> > > +/* A subclass of pretty_printer for implementing
> > > dump_context::dump_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.  */
> > >
> > > -static optinfo_item *
> > > -make_item_for_dump_printf_va (const char *format, va_list ap)
> > > -  ATTRIBUTE_PRINTF (1, 0);
> > > +class dump_pretty_printer : public pretty_printer
> > > +{
> > > +public:
> > > +  dump_pretty_printer (dump_context *context, dump_flags_t
> > > dump_kind);
> > >
> > > -static optinfo_item *
> > > -make_item_for_dump_printf_va (const char *format, va_list ap)
> > > +  void emit_items (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 emit_any_pending_textual_chunks (optinfo *dest);
> > > +
> > > +  void emit_item (optinfo_item *item, optinfo *dest);
> > > +
> > > +  dump_context *m_context;
> > > +  dump_flags_t m_dump_kind;
> > > +  auto_vec<stashed_item> m_stashed_items;
> > > +};
> > > +
> > > +/* dump_pretty_printer's ctor.  */
> > > +
> > > +dump_pretty_printer::dump_pretty_printer (dump_context *context,
> > > +                                         dump_flags_t dump_kind)
> > > +: pretty_printer (), m_context (context), m_dump_kind (dump_kind),
> > > +  m_stashed_items ()
> > > +{
> > > +  pp_format_decoder (this) = format_decoder_cb;
> > > +}
> > > +
> > > +/* Phase 3 of formatting; compare with pp_output_formatted_text.
> > > +
> > > +   Emit optinfo_item instances for the various formatted chunks
> > > from phases
> > > +   1 and 2 (i.e. pp_format).
> > > +
> > > +   Some chunks may already have had their items built (during
> > > decode_format).
> > > +   These chunks have been stashed into m_stashed_items; we emit
> > > them here.
> > > +
> > > +   For all other purely textual chunks, they are printed into
> > > +   buffer->formatted_obstack, and then emitted as a textual
> > > optinfo_item.
> > > +   This consolidates multiple adjacent text chunks into a single
> > > text
> > > +   optinfo_item.  */
> > > +
> > > +void
> > > +dump_pretty_printer::emit_items (optinfo *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)
> > > +       {
> > > +         emit_any_pending_textual_chunks (dest);
> > > +         /* This chunk has a stashed item: use it.  */
> > > +         emit_item (m_stashed_items[stashed_item_idx++].item,
> > > dest);
> > > +       }
> > > +      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]);
> > > +    }
> > > +
> > > +  emit_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 dump_pretty_printer::emit_items
> > > +   for consolidating multiple adjacent pure-text chunks into
> > > single
> > > +   optinfo_items (in phase 3).  */
> > > +
> > > +void
> > > +dump_pretty_printer::emit_any_pending_textual_chunks (optinfo
> > > *dest)
> > >  {
> > > -  char *formatted_text = xvasprintf (format, ap);
> > > +  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
> > > +
> > > +  /* Don't emit 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);
> > > -  return item;
> > > +  emit_item (item, dest);
> > > +
> > > +  /* Clear the pending text by unwinding formatted_text back to
> > > the start
> > > +     of the buffer (without deallocating).  */
> > > +  obstack_free (&buffer->formatted_obstack,
> > > +               buffer->formatted_obstack.object_base);
> > >  }
> > >
> > > -/* Make an item for the given dump call.  */
> > > +/* Emit ITEM and take ownership of it.  If DEST is non-NULL, add
> > > ITEM
> > > +   to DEST; otherwise delete ITEM.  */
> > >
> > > -static optinfo_item *
> > > -make_item_for_dump_printf (const char *format, ...)
> > > -  ATTRIBUTE_PRINTF (1, 2);
> > > +void
> > > +dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest)
> > > +{
> > > +  m_context->emit_item (item, m_dump_kind);
> > > +  if (dest)
> > > +    dest->add_item (item);
> > > +  else
> > > +    delete item;
> > > +}
> > >
> > > -static optinfo_item *
> > > -make_item_for_dump_printf (const char *format, ...)
> > > +/* Record that ITEM (generated in phase 2 of formatting) is to be
> > > used for
> > > +   the chunk at BUFFER_PTR in phase 3 (by emit_items).  */
> > > +
> > > +void
> > > +dump_pretty_printer::stash_item (const char **buffer_ptr,
> > > optinfo_item *item)
> > >  {
> > > -  va_list ap;
> > > -  va_start (ap, format);
> > > -  optinfo_item *item
> > > -    = make_item_for_dump_printf_va (format, ap);
> > > -  va_end (ap);
> > > -  return item;
> > > +  gcc_assert (buffer_ptr);
> > > +  gcc_assert (item);
> > > +
> > > +  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
> > > +}
> > > +
> > > +/* pp_format_decoder callback for dump_pretty_printer, and thus
> > > for
> > > +   dump_printf and dump_printf_loc.
> > > +
> > > +   A wrapper around decode_format, for type-safety.  */
> > > +
> > > +bool
> > > +dump_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)
> > > +{
> > > +  dump_pretty_printer *opp = static_cast <dump_pretty_printer *>
> > > (pp);
> > > +  return opp->decode_format (text, spec, buffer_ptr);
> > > +}
> > > +
> > > +/* Format decoder for dump_pretty_printer, 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 (MSG_*, arg, TDF_SLIM).
> > > +
> > > +   FIXME: add symtab_node?
> > > +
> > > +   These format codes build optinfo_item instances, thus capturing
> > > metadata
> > > +   about the arguments being dumped, as well as the textual
> > > output.  */
> > > +
> > > +bool
> > > +dump_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).  */
> > > +  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_dump_gimple_expr (stmt,
> > > 0, TDF_SLIM);
> > > +       stash_item (buffer_ptr, item);
> > > +       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_dump_gimple_stmt (stmt,
> > > 0, TDF_SLIM);
> > > +       stash_item (buffer_ptr, item);
> > > +       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_dump_generic_expr (t,
> > > TDF_SLIM);
> > > +       stash_item (buffer_ptr, item);
> > > +       return true;
> > > +      }
> > > +
> > > +    default:
> > > +      return false;
> > > +    }
> > >  }
> > >
> > >  /* Output a formatted message using FORMAT on appropriate dump
> > > streams.  */
> > >
> > >  void
> > >  dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > > *format,
> > > -                             va_list ap)
> > > +                             va_list *ap)
> > >  {
> > > -  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> > > -  emit_item (item, dump_kind);
> > > +  dump_pretty_printer pp (this, dump_kind);
> > >
> > > +  text_info text;
> > > +  text.err_no = errno;
> > > +  text.args_ptr = ap;
> > > +  text.format_spec = format;
> > > +
> > > +  /* Phases 1 and 2, using pp_format.  */
> > > +  pp_format (&pp, &text);
> > > +
> > > +  /* Phase 3.  */
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > >        info.handle_dump_file_kind (dump_kind);
> > > -      info.add_item (item);
> > > +      pp.emit_items (&info);
> > >      }
> > >    else
> > > -    delete item;
> > > +    pp.emit_items (NULL);
> > >  }
> > >
> > >  /* Similar to dump_printf, except source location is also printed,
> > > and
> > > @@ -739,7 +946,7 @@ dump_context::dump_printf_va (dump_flags_t
> > > dump_kind, const char *format,
> > >  void
> > >  dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
> > >                                   const dump_location_t &loc,
> > > -                                 const char *format, va_list ap)
> > > +                                 const char *format, va_list *ap)
> > >  {
> > >    dump_loc (dump_kind, loc);
> > >    dump_printf_va (dump_kind, format, ap);
> > > @@ -851,7 +1058,11 @@ dump_context::begin_scope (const char *name,
> > > const dump_location_t &loc)
> > >    if (m_test_pp)
> > >      ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
> > >
> > > -  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n",
> > > name);
> > > +  pretty_printer pp;
> > > +  pp_printf (&pp, "=== %s ===\n", name);
> > > +  optinfo_item *item
> > > +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > > +                       xstrdup (pp_formatted_text (&pp)));
> > >    emit_item (item, MSG_NOTE);
> > >
> > >    if (optinfo_enabled_p ())
> > > @@ -859,7 +1070,6 @@ dump_context::begin_scope (const char *name,
> > > const dump_location_t &loc)
> > >        optinfo &info = begin_next_optinfo (loc);
> > >        info.m_kind = OPTINFO_KIND_SCOPE;
> > >        info.add_item (item);
> > > -      end_any_optinfo ();
> > >      }
> > >    else
> > >      delete item;
> > > @@ -1006,7 +1216,7 @@ dump_printf (dump_flags_t dump_kind, const
> > > char *format, ...)
> > >  {
> > >    va_list ap;
> > >    va_start (ap, format);
> > > -  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> > > +  dump_context::get ().dump_printf_va (dump_kind, format, &ap);
> > >    va_end (ap);
> > >  }
> > >
> > > @@ -1019,7 +1229,7 @@ dump_printf_loc (dump_flags_t dump_kind,
> > > const dump_location_t &loc,
> > >  {
> > >    va_list ap;
> > >    va_start (ap, format);
> > > -  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > > ap);
> > > +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > > &ap);
> > >    va_end (ap);
> > >  }
> > >
> > > @@ -1808,9 +2018,12 @@ test_capture_of_dump_calls (const
> > > line_table_case &case_)
> > >
> > >    dump_location_t loc = dump_location_t::from_location_t (where);
> > >
> > > -  greturn *stmt = gimple_build_return (NULL);
> > > +  gimple *stmt = gimple_build_return (NULL);
> > >    gimple_set_location (stmt, where);
> > >
> > > +  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> > > +                              get_identifier ("test_decl"),
> > > +                              integer_type_node);
> > >    /* Run all tests twice, with and then without optinfo enabled,
> > > to ensure
> > >       that immediate destinations vs optinfo-based destinations
> > > both
> > >       work, independently of each other, with no leaks.  */
> > > @@ -1834,6 +2047,89 @@ test_capture_of_dump_calls (const
> > > line_table_case &case_)
> > >           }
> > >        }
> > >
> > > +      /* Test of dump_printf with %T.  */
> > > +      {
> > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +       dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "tree: 0");
> > > +       if (with_optinfo)
> > > +         {
> > > +           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 (with_optinfo, MSG_ALL);
> > > +       dump_printf (MSG_NOTE, "gimple: %E", stmt);
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;");
> > > +       if (with_optinfo)
> > > +         {
> > > +           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 (with_optinfo, MSG_ALL);
> > > +       dump_printf (MSG_NOTE, "gimple: %G", stmt);
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;\n");
> > > +       if (with_optinfo)
> > > +         {
> > > +           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 (with_optinfo, MSG_ALL);
> > > +       dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
> > > +                        " %i consecutive %E%E after\n",
> > > +                        integer_zero_node, test_decl, 42, stmt,
> > > stmt);
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp,
> > > +                              "test.txt:5:10: note: before 0 and
> > > test_decl"
> > > +                              " 42 consecutive return;return;
> > > after\n");
> > > +       if (with_optinfo)
> > > +         {
> > > +           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 (with_optinfo, MSG_ALL);
> > > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > > index 2b174e5..1fe64cb 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 dumpfile.c).  */
> > > +
> > > +#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
> > > @@ -476,9 +489,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/testsuite/gcc.dg/format/gcc_diag-1.c
> > > b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > > index 034e097..8761456 100644
> > > --- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > > +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > > @@ -1,4 +1,4 @@
> > > -/* Test for GCC diagnositc formats.  */
> > > +/* Test for GCC diagnostic formats.  */
> > >  /* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
> > >  /* { dg-do compile } */
> > >  /* { dg-options "-Wformat" } */
> > > @@ -24,6 +24,7 @@ extern int diag (const char *, ...)
> > > ATTRIBUTE_DIAG(__gcc_diag__);
> > >  extern int tdiag (const char *, ...)
> > > ATTRIBUTE_DIAG(__gcc_tdiag__);
> > >  extern int cdiag (const char *, ...)
> > > ATTRIBUTE_DIAG(__gcc_cdiag__);
> > >  extern int cxxdiag (const char *, ...)
> > > ATTRIBUTE_DIAG(__gcc_cxxdiag__);
> > > +extern int dump (const char *, ...)
> > > ATTRIBUTE_DIAG(__gcc_dump_printf__);
> > >
> > >  void
> > >  foo (int i, int i1, int i2, unsigned int u, double d, char *s,
> > > void *p,
> > > @@ -39,36 +40,44 @@ foo (int i, int i1, int i2, unsigned int u,
> > > double d, char *s, void *p,
> > >    tdiag ("%%");
> > >    cdiag ("%%");
> > >    cxxdiag ("%%");
> > > +  dump ("%%");
> > >    diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > >    tdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > >    cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > >    cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > > +  dump ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > >    diag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > p);
> > >    tdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > p);
> > >    cdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > p);
> > >    cxxdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i,
> > > s, p);
> > > +  dump ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > p);
> > >    diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > >    tdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > >    cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > >    cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > > +  dump ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > >    diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > >    tdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > >    cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > >    cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > > +  dump ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > >    diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > >    tdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > >    cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > >    cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > > +  dump ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > >    diag ("%.*s", i, s);
> > >    tdiag ("%.*s", i, s);
> > >    cdiag ("%.*s", i, s);
> > >    cxxdiag ("%.*s", i, s);
> > > +  dump ("%.*s", i, s);
> > >
> > >    /* Extensions provided in the diagnostic framework.  */
> > >    diag ("%m");
> > >    tdiag ("%m");
> > >    cdiag ("%m");
> > >    cxxdiag ("%m");
> > > +  dump ("%m");
> > >
> > >    /* Quote directives to avoid "warning: conversion used
> > > unquoted." */
> > >    tdiag ("%<%D%F%T%V%>", t1, t1, t1, t1);
> > > @@ -94,20 +103,24 @@ foo (int i, int i1, int i2, unsigned int u,
> > > double d, char *s, void *p,
> > >    tdiag ("%Z", v, v_len);
> > >    cdiag ("%Z", v, v_len);
> > >    cxxdiag ("%Z", v, v_len);
> > > +  dump ("%Z", v, v_len);
> > >
> > >    /* Bad stuff with extensions.  */
> > >    diag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > >    tdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > >    cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > >    cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > > +  dump ("%m", i); /* { dg-warning "format" "extra arg" } */
> > >    diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > >    tdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > >    cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > >    cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > > +  dump ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > >    diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > >    tdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > >    cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > >    cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > > +  dump ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > >    diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
> > >    tdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
> > >    tdiag ("%E", t1);
> > > diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > index ab9bc2f..8b5aadf1 100644
> > > --- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > @@ -19,12 +19,16 @@ typedef union tree_node *tree;
> > >     the C test to find the symbol.  */
> > >  typedef struct gcall gcall;
> > >
> > > +/* Likewise for gimple.  */
> > > +typedef struct gimple gimple;
> > > +
> > >  #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__,
> > > 1, 2)))
> > >
> > >  void diag (const char*, ...) FORMAT (diag);
> > >  void cdiag (const char*, ...) FORMAT (cdiag);
> > >  void tdiag (const char*, ...) FORMAT (tdiag);
> > >  void cxxdiag (const char*, ...) FORMAT (cxxdiag);
> > > +void dump (const char*, ...) FORMAT (dump_printf);
> > >
> > >  void test_diag (tree t, gcall *gc)
> > >  {
> > > @@ -157,3 +161,25 @@ void test_cxxdiag (tree t, gcall *gc)
> > >    cxxdiag ("%<%V%>", t);
> > >    cxxdiag ("%<%X%>", t);
> > >  }
> > > +
> > > +void test_dump (tree t, gimple *stmt)
> > > +{
> > > +  dump ("%<");   /* { dg-warning "unterminated quoting directive"
> > > } */
> > > +  dump ("%>");   /* { dg-warning "unmatched quoting directive " }
> > > */
> > > +  dump ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting
> > > directive" } */
> > > +
> > > +  dump ("%R");       /* { dg-warning "unmatched color reset
> > > directive" } */
> > > +  dump ("%r", "");   /* { dg-warning "unterminated color
> > > directive" } */
> > > +  dump ("%r%r", "", "");   /* { dg-warning "unterminated color
> > > directive" } */
> > > +  dump ("%r%R", "");
> > > +  dump ("%r%r%R", "", "");
> > > +  dump ("%r%R%r%R", "", "");
> > > +
> > > +  dump ("%<%R%>");      /* { dg-warning "unmatched color reset
> > > directive" } */
> > > +  dump ("%<%r%>", "");  /* { dg-warning "unterminated color
> > > directive" } */
> > > +  dump ("%<%r%R%>", "");
> > > +
> > > +  dump ("%E", stmt);
> > > +  dump ("%T", t);
> > > +  dump ("%G", stmt);
> > > +}
> > > --
> > > 1.8.5.3
> > >

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 5/5] Formatted printing for dump_* in the middle-end
  2018-07-31 14:21       ` Richard Biener
@ 2018-07-31 14:33         ` Richard Biener
  0 siblings, 0 replies; 29+ messages in thread
From: Richard Biener @ 2018-07-31 14:33 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jul 31, 2018 at 4:21 PM Richard Biener
<richard.guenther@gmail.com> wrote:
>
> On Tue, Jul 31, 2018 at 4:19 PM David Malcolm <dmalcolm@redhat.com> wrote:
> >
> > On Tue, 2018-07-31 at 15:03 +0200, Richard Biener wrote:
> > > On Fri, Jul 27, 2018 at 11:49 PM David Malcolm <dmalcolm@redhat.com>
> > > wrote:
> > > >
> > > > This patch converts dump_print and dump_printf_loc from using
> > > > printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
> > > > based on pp_format, which supports formatting middle-end types.
> > > >
> > > > In particular, the following codes are implemented (in addition
> > > > to the standard pretty_printer ones):
> > > >
> > > >    %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 (MSG_*, arg, TDF_SLIM).
> > > >
> > > > Hence it becomes possible to convert e.g.:
> > > >
> > > >   if (dump_enabled_p ())
> > > >     {
> > > >       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> > > >                        "not vectorized: different sized vector "
> > > >                        "types in statement, ");
> > > >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > > > vectype);
> > > >       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
> > > >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > > > nunits_vectype);
> > > >       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
> > > >     }
> > > >
> > > > into a one-liner:
> > > >
> > > >   if (dump_enabled_p ())
> > > >     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> > > >                      "not vectorized: different sized vector "
> > > >                      "types in statement, %T and %T\n",
> > > >                      vectype, nunits_vectype);
> > > >
> > > > Unlike regular pretty-printers, this one captures optinfo_item
> > > > instances for the formatted chunks as appropriate, so that when
> > > > written out to a JSON optimization record, the relevant parts of
> > > > the message are labelled by type, and by source location (so that
> > > > e.g. %G is entirely equivalent to using dump_gimple_stmt).
> > > >
> > > > dump_printf and dump_printf_loc become marked with
> > > > ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.
> > > >
> > > > gcc/c-family/ChangeLog:
> > > >         * c-format.c (enum format_type): Add
> > > > gcc_dump_printf_format_type.
> > > >         (local_gimple_ptr_node): New decl.
> > > >         (gcc_dump_printf_length_specs): New.
> > > >         (gcc_dump_printf_flag_pairs): New.
> > > >         (gcc_dump_printf_flag_specs): New.
> > > >         (gcc_dump_printf_char_table): New.
> > > >         (format_types_orig): Add entry for "gcc_dump_printf".
> > > >         (init_dynamic_diag_info): Create local_gimple_ptr_node.
> > > >         Set up length_char_specs and conversion_specs for
> > > >         gcc_dump_printf_format_type.
> > > >         (handle_format_attribute): Handle
> > > > gcc_dump_printf_format_type.
> > > >         * c-format.h (T89_GIMPLE): New macro.
> > >
> > > Iff the c-family changes are neccessary (are they?) then how does
> > > this
> > > work for non-c-family languages which do not link c-family/c-
> > > format.o?
> >
> > The c-family changes are necessary for bootstrap, so that -Wformat
> > works cleanly after changing dump_printf_loc etc from
> >   ATTRIBUTE_PRINTF_3;
> > to
> >   ATTRIBUTE_GCC_DUMP_PRINTF (3, 0);
> > i.e. they're just the changes to -Wformat to teach it how to verify the
> > new:
> >   __attribute__ ((__format__ (__gcc_dump_printf__, m ,n)))
> > (hence the cleanups to c-format.c earlier in the patch kit, to avoid
> > yet more copy-and-paste there for the new format decoder callback).
>
> Ah, thanks for clarifying.
>
> > The implementation itself is all within dumpfile.c, hence the non-c-
> > family languages ought to work.  My testing was with:
> >   --enable-languages=c,c++,objc,obj-c++,fortran,ada,go,lto,jit,brig
> > (and with cloog and isl, fwiw).
> >
> > (I kept the alphabetization of the ChangeLog files from my generate-
> > changelog.py script, which put the gcc/c-family/ChangeLog before the
> > gcc/ChangeLog and thus may have made this confusing to read, sorry).
> >
> > I didn't exhaustively check every callsite to the changed calls; I'm
> > assuming that -Wformat during bootstrap has effectively checked that
> > for me.  Though now I think about it, I note that we use
> > HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to be a
> > valid input to pp_format on all of our configurations?
>
> I hope so ... returning to the patch now.

The patch is OK if C family maintainers agree on their parts.

Thanks,
Richard.

> Richard.
>
> > Dave
> >
> > >
> > > > gcc/ChangeLog:
> > > >         * dump-context.h: Include "dumpfile.h".
> > > >         (dump_context::dump_printf_va): Convert final param from
> > > > va_list
> > > >         to va_list *.  Convert from ATTRIBUTE_PRINTF to
> > > >         ATTRIBUTE_GCC_DUMP_PRINTF.
> > > >         (dump_context::dump_printf_loc_va): Likewise.
> > > >         * dumpfile.c: Include "stringpool.h".
> > > >         (make_item_for_dump_printf_va): Delete.
> > > >         (make_item_for_dump_printf): Delete.
> > > >         (class dump_pretty_printer): New class.
> > > >         (dump_pretty_printer::dump_pretty_printer): New ctor.
> > > >         (dump_pretty_printer::emit_items): New member function.
> > > >         (dump_pretty_printer::emit_any_pending_textual_chunks): New
> > > > member
> > > >         function.
> > > >         (dump_pretty_printer::emit_item): New member function.
> > > >         (dump_pretty_printer::stash_item): New member function.
> > > >         (dump_pretty_printer::format_decoder_cb): New member
> > > > function.
> > > >         (dump_pretty_printer::decode_format): New member function.
> > > >         (dump_context::dump_printf_va): Reimplement in terms of
> > > >         dump_pretty_printer.
> > > >         (dump_context::dump_printf_loc_va): Convert final param
> > > > from va_list
> > > >         to va_list *.
> > > >         (dump_context::begin_scope): Reimplement call to
> > > >         make_item_for_dump_printf.
> > > >         (dump_printf): Update for change to dump_printf_va.
> > > >         (dump_printf_loc): Likewise.
> > > >         (selftest::test_capture_of_dump_calls): Convert "stmt" from
> > > >         greturn * to gimple *.  Add a test_decl.  Add tests of
> > > > dump_printf
> > > >         with %T, %E, and %G.
> > > >         * dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
> > > >         (dump_printf): Replace ATTRIBUTE_PRINTF_2 with
> > > >         ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
> > > >         (dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
> > > >         ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
> > > >
> > > > gcc/testsuite/ChangeLog:
> > > >         * gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage
> > > > for
> > > >         gcc_dump_printf.
> > > >         * gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add
> > > > test
> > > >         coverage for gcc_dump_printf.
> > > > ---
> > > >  gcc/c-family/c-format.c                   |  60 ++++-
> > > >  gcc/c-family/c-format.h                   |   1 +
> > > >  gcc/dump-context.h                        |   7 +-
> > > >  gcc/dumpfile.c                            | 358
> > > > +++++++++++++++++++++++++++---
> > > >  gcc/dumpfile.h                            |  20 +-
> > > >  gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  15 +-
> > > >  gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  26 +++
> > > >  7 files changed, 448 insertions(+), 39 deletions(-)
> > > >
> > > > diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
> > > > index 82841e4..b524468 100644
> > > > --- a/gcc/c-family/c-format.c
> > > > +++ b/gcc/c-family/c-format.c
> > > > @@ -44,6 +44,7 @@ enum format_type { printf_format_type,
> > > > asm_fprintf_format_type,
> > > >                    gcc_diag_format_type, gcc_tdiag_format_type,
> > > >                    gcc_cdiag_format_type,
> > > >                    gcc_cxxdiag_format_type, gcc_gfc_format_type,
> > > > +                  gcc_dump_printf_format_type,
> > > >                    gcc_objc_string_format_type,
> > > >                    format_type_error = -1};
> > > >
> > > > @@ -56,6 +57,7 @@ struct function_format_info
> > > >
> > > >  /* Initialized in init_dynamic_diag_info.  */
> > > >  static GTY(()) tree local_tree_type_node;
> > > > +static GTY(()) tree local_gimple_ptr_node;
> > > >  static GTY(()) tree local_gcall_ptr_node;
> > > >  static GTY(()) tree locus;
> > > >
> > > > @@ -461,6 +463,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 +553,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 +569,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[] =
> > > >  {
> > > > @@ -786,6 +791,22 @@ static const format_char_info
> > > > gcc_gfc_char_table[] =
> > > >    { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> > > >  };
> > > >
> > > > +static const format_char_info gcc_dump_printf_char_table[] =
> > > > +{
> > > > +  /* The conversion specifiers implemented within pp_format.  */
> > > > +  PP_FORMAT_CHAR_TABLE,
> > > > +
> > > > +  /* Custom conversion specifiers implemented by
> > > > dump_pretty_printer.  */
> > > > +
> > > > +  /* E and G require a "gimple *" argument at runtime.  */
> > > > +  { "EG",   1, STD_C89, {
> > > > T89_GIMPLE,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,
> > > >  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> > > > +
> > > > +  /* T requires a "tree" at runtime.  */
> > > > +  { "T",   1, STD_C89, {
> > > > T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADL
> > > > EN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
> > > > +
> > > > +  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
> > > > +};
> > > > +
> > > >  static const format_char_info scan_char_table[] =
> > > >  {
> > > >    /* C89 conversion specifiers.  */
> > > > @@ -885,6 +906,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,
> > > > @@ -3838,6 +3866,29 @@ init_dynamic_diag_info (void)
> > > >         local_tree_type_node = void_type_node;
> > > >      }
> > > >
> > > > +  /* Similar to the above but for gimple *.  */
> > > > +  if (!local_gimple_ptr_node
> > > > +      || local_gimple_ptr_node == void_type_node)
> > > > +    {
> > > > +      if ((local_gimple_ptr_node = maybe_get_identifier
> > > > ("gimple")))
> > > > +       {
> > > > +         local_gimple_ptr_node
> > > > +           = identifier_global_value (local_gimple_ptr_node);
> > > > +         if (local_gimple_ptr_node)
> > > > +           {
> > > > +             if (TREE_CODE (local_gimple_ptr_node) != TYPE_DECL)
> > > > +               {
> > > > +                 error ("%<gimple%> is not defined as a type");
> > > > +                 local_gimple_ptr_node = 0;
> > > > +               }
> > > > +             else
> > > > +               local_gimple_ptr_node = TREE_TYPE
> > > > (local_gimple_ptr_node);
> > > > +           }
> > > > +       }
> > > > +      else
> > > > +       local_gimple_ptr_node = void_type_node;
> > > > +    }
> > > > +
> > > >    /* Similar to the above but for gcall*.  */
> > > >    if (!local_gcall_ptr_node
> > > >        || local_gcall_ptr_node == void_type_node)
> > > > @@ -3905,6 +3956,7 @@ init_dynamic_diag_info (void)
> > > >           dynamic_format_types[gcc_tdiag_format_type].length_char_s
> > > > pecs =
> > > >           dynamic_format_types[gcc_cdiag_format_type].length_char_s
> > > > pecs =
> > > >           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),
> > > > @@ -3931,6 +3983,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_spe
> > > > cs =
> > > > +    gcc_dump_printf_char_table;
> > > >  }
> > > >
> > > >  #ifdef TARGET_FORMAT_TYPES
> > > > @@ -4085,7 +4139,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.  */
> > > > @@ -4107,7 +4162,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/c-family/c-format.h b/gcc/c-family/c-format.h
> > > > index f828e77..4dfdbdb 100644
> > > > --- a/gcc/c-family/c-format.h
> > > > +++ b/gcc/c-family/c-format.h
> > > > @@ -298,6 +298,7 @@ struct format_kind_info
> > > >  #define T_UC   &unsigned_char_type_node
> > > >  #define T99_UC { STD_C99, NULL, T_UC }
> > > >  #define T_V    &void_type_node
> > > > +#define T89_GIMPLE   { STD_C89, NULL, &local_gimple_ptr_node }
> > > >  #define T89_G   { STD_C89, NULL, &local_gcall_ptr_node }
> > > >  #define T89_T   { STD_C89, NULL, &local_tree_type_node }
> > > >  #define T89_V  { STD_C89, NULL, T_V }
> > > > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > > > index f40ea14..54095d3 100644
> > > > --- a/gcc/dump-context.h
> > > > +++ b/gcc/dump-context.h
> > > > @@ -22,6 +22,7 @@ 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
> > > >  #include "pretty-print.h"
> > > >
> > > >  /* A class for handling the various dump_* calls.
> > > > @@ -73,11 +74,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);
> > > > +                          const char *format, va_list *ap)
> > > > +    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 10e9cab..7131f56 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.  */
> > > > @@ -681,56 +682,262 @@ dump_context::dump_generic_expr_loc
> > > > (dump_flags_t dump_kind,
> > > >    dump_generic_expr (dump_kind, extra_dump_flags, t);
> > > >  }
> > > >
> > > > -/* Make an item for the given dump call.  */
> > > > +/* A subclass of pretty_printer for implementing
> > > > dump_context::dump_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.  */
> > > >
> > > > -static optinfo_item *
> > > > -make_item_for_dump_printf_va (const char *format, va_list ap)
> > > > -  ATTRIBUTE_PRINTF (1, 0);
> > > > +class dump_pretty_printer : public pretty_printer
> > > > +{
> > > > +public:
> > > > +  dump_pretty_printer (dump_context *context, dump_flags_t
> > > > dump_kind);
> > > >
> > > > -static optinfo_item *
> > > > -make_item_for_dump_printf_va (const char *format, va_list ap)
> > > > +  void emit_items (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 emit_any_pending_textual_chunks (optinfo *dest);
> > > > +
> > > > +  void emit_item (optinfo_item *item, optinfo *dest);
> > > > +
> > > > +  dump_context *m_context;
> > > > +  dump_flags_t m_dump_kind;
> > > > +  auto_vec<stashed_item> m_stashed_items;
> > > > +};
> > > > +
> > > > +/* dump_pretty_printer's ctor.  */
> > > > +
> > > > +dump_pretty_printer::dump_pretty_printer (dump_context *context,
> > > > +                                         dump_flags_t dump_kind)
> > > > +: pretty_printer (), m_context (context), m_dump_kind (dump_kind),
> > > > +  m_stashed_items ()
> > > > +{
> > > > +  pp_format_decoder (this) = format_decoder_cb;
> > > > +}
> > > > +
> > > > +/* Phase 3 of formatting; compare with pp_output_formatted_text.
> > > > +
> > > > +   Emit optinfo_item instances for the various formatted chunks
> > > > from phases
> > > > +   1 and 2 (i.e. pp_format).
> > > > +
> > > > +   Some chunks may already have had their items built (during
> > > > decode_format).
> > > > +   These chunks have been stashed into m_stashed_items; we emit
> > > > them here.
> > > > +
> > > > +   For all other purely textual chunks, they are printed into
> > > > +   buffer->formatted_obstack, and then emitted as a textual
> > > > optinfo_item.
> > > > +   This consolidates multiple adjacent text chunks into a single
> > > > text
> > > > +   optinfo_item.  */
> > > > +
> > > > +void
> > > > +dump_pretty_printer::emit_items (optinfo *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)
> > > > +       {
> > > > +         emit_any_pending_textual_chunks (dest);
> > > > +         /* This chunk has a stashed item: use it.  */
> > > > +         emit_item (m_stashed_items[stashed_item_idx++].item,
> > > > dest);
> > > > +       }
> > > > +      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]);
> > > > +    }
> > > > +
> > > > +  emit_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 dump_pretty_printer::emit_items
> > > > +   for consolidating multiple adjacent pure-text chunks into
> > > > single
> > > > +   optinfo_items (in phase 3).  */
> > > > +
> > > > +void
> > > > +dump_pretty_printer::emit_any_pending_textual_chunks (optinfo
> > > > *dest)
> > > >  {
> > > > -  char *formatted_text = xvasprintf (format, ap);
> > > > +  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
> > > > +
> > > > +  /* Don't emit 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);
> > > > -  return item;
> > > > +  emit_item (item, dest);
> > > > +
> > > > +  /* Clear the pending text by unwinding formatted_text back to
> > > > the start
> > > > +     of the buffer (without deallocating).  */
> > > > +  obstack_free (&buffer->formatted_obstack,
> > > > +               buffer->formatted_obstack.object_base);
> > > >  }
> > > >
> > > > -/* Make an item for the given dump call.  */
> > > > +/* Emit ITEM and take ownership of it.  If DEST is non-NULL, add
> > > > ITEM
> > > > +   to DEST; otherwise delete ITEM.  */
> > > >
> > > > -static optinfo_item *
> > > > -make_item_for_dump_printf (const char *format, ...)
> > > > -  ATTRIBUTE_PRINTF (1, 2);
> > > > +void
> > > > +dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest)
> > > > +{
> > > > +  m_context->emit_item (item, m_dump_kind);
> > > > +  if (dest)
> > > > +    dest->add_item (item);
> > > > +  else
> > > > +    delete item;
> > > > +}
> > > >
> > > > -static optinfo_item *
> > > > -make_item_for_dump_printf (const char *format, ...)
> > > > +/* Record that ITEM (generated in phase 2 of formatting) is to be
> > > > used for
> > > > +   the chunk at BUFFER_PTR in phase 3 (by emit_items).  */
> > > > +
> > > > +void
> > > > +dump_pretty_printer::stash_item (const char **buffer_ptr,
> > > > optinfo_item *item)
> > > >  {
> > > > -  va_list ap;
> > > > -  va_start (ap, format);
> > > > -  optinfo_item *item
> > > > -    = make_item_for_dump_printf_va (format, ap);
> > > > -  va_end (ap);
> > > > -  return item;
> > > > +  gcc_assert (buffer_ptr);
> > > > +  gcc_assert (item);
> > > > +
> > > > +  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
> > > > +}
> > > > +
> > > > +/* pp_format_decoder callback for dump_pretty_printer, and thus
> > > > for
> > > > +   dump_printf and dump_printf_loc.
> > > > +
> > > > +   A wrapper around decode_format, for type-safety.  */
> > > > +
> > > > +bool
> > > > +dump_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)
> > > > +{
> > > > +  dump_pretty_printer *opp = static_cast <dump_pretty_printer *>
> > > > (pp);
> > > > +  return opp->decode_format (text, spec, buffer_ptr);
> > > > +}
> > > > +
> > > > +/* Format decoder for dump_pretty_printer, 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 (MSG_*, arg, TDF_SLIM).
> > > > +
> > > > +   FIXME: add symtab_node?
> > > > +
> > > > +   These format codes build optinfo_item instances, thus capturing
> > > > metadata
> > > > +   about the arguments being dumped, as well as the textual
> > > > output.  */
> > > > +
> > > > +bool
> > > > +dump_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).  */
> > > > +  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_dump_gimple_expr (stmt,
> > > > 0, TDF_SLIM);
> > > > +       stash_item (buffer_ptr, item);
> > > > +       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_dump_gimple_stmt (stmt,
> > > > 0, TDF_SLIM);
> > > > +       stash_item (buffer_ptr, item);
> > > > +       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_dump_generic_expr (t,
> > > > TDF_SLIM);
> > > > +       stash_item (buffer_ptr, item);
> > > > +       return true;
> > > > +      }
> > > > +
> > > > +    default:
> > > > +      return false;
> > > > +    }
> > > >  }
> > > >
> > > >  /* Output a formatted message using FORMAT on appropriate dump
> > > > streams.  */
> > > >
> > > >  void
> > > >  dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > > > *format,
> > > > -                             va_list ap)
> > > > +                             va_list *ap)
> > > >  {
> > > > -  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> > > > -  emit_item (item, dump_kind);
> > > > +  dump_pretty_printer pp (this, dump_kind);
> > > >
> > > > +  text_info text;
> > > > +  text.err_no = errno;
> > > > +  text.args_ptr = ap;
> > > > +  text.format_spec = format;
> > > > +
> > > > +  /* Phases 1 and 2, using pp_format.  */
> > > > +  pp_format (&pp, &text);
> > > > +
> > > > +  /* Phase 3.  */
> > > >    if (optinfo_enabled_p ())
> > > >      {
> > > >        optinfo &info = ensure_pending_optinfo ();
> > > >        info.handle_dump_file_kind (dump_kind);
> > > > -      info.add_item (item);
> > > > +      pp.emit_items (&info);
> > > >      }
> > > >    else
> > > > -    delete item;
> > > > +    pp.emit_items (NULL);
> > > >  }
> > > >
> > > >  /* Similar to dump_printf, except source location is also printed,
> > > > and
> > > > @@ -739,7 +946,7 @@ dump_context::dump_printf_va (dump_flags_t
> > > > dump_kind, const char *format,
> > > >  void
> > > >  dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
> > > >                                   const dump_location_t &loc,
> > > > -                                 const char *format, va_list ap)
> > > > +                                 const char *format, va_list *ap)
> > > >  {
> > > >    dump_loc (dump_kind, loc);
> > > >    dump_printf_va (dump_kind, format, ap);
> > > > @@ -851,7 +1058,11 @@ dump_context::begin_scope (const char *name,
> > > > const dump_location_t &loc)
> > > >    if (m_test_pp)
> > > >      ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
> > > >
> > > > -  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n",
> > > > name);
> > > > +  pretty_printer pp;
> > > > +  pp_printf (&pp, "=== %s ===\n", name);
> > > > +  optinfo_item *item
> > > > +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > > > +                       xstrdup (pp_formatted_text (&pp)));
> > > >    emit_item (item, MSG_NOTE);
> > > >
> > > >    if (optinfo_enabled_p ())
> > > > @@ -859,7 +1070,6 @@ dump_context::begin_scope (const char *name,
> > > > const dump_location_t &loc)
> > > >        optinfo &info = begin_next_optinfo (loc);
> > > >        info.m_kind = OPTINFO_KIND_SCOPE;
> > > >        info.add_item (item);
> > > > -      end_any_optinfo ();
> > > >      }
> > > >    else
> > > >      delete item;
> > > > @@ -1006,7 +1216,7 @@ dump_printf (dump_flags_t dump_kind, const
> > > > char *format, ...)
> > > >  {
> > > >    va_list ap;
> > > >    va_start (ap, format);
> > > > -  dump_context::get ().dump_printf_va (dump_kind, format, ap);
> > > > +  dump_context::get ().dump_printf_va (dump_kind, format, &ap);
> > > >    va_end (ap);
> > > >  }
> > > >
> > > > @@ -1019,7 +1229,7 @@ dump_printf_loc (dump_flags_t dump_kind,
> > > > const dump_location_t &loc,
> > > >  {
> > > >    va_list ap;
> > > >    va_start (ap, format);
> > > > -  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > > > ap);
> > > > +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> > > > &ap);
> > > >    va_end (ap);
> > > >  }
> > > >
> > > > @@ -1808,9 +2018,12 @@ test_capture_of_dump_calls (const
> > > > line_table_case &case_)
> > > >
> > > >    dump_location_t loc = dump_location_t::from_location_t (where);
> > > >
> > > > -  greturn *stmt = gimple_build_return (NULL);
> > > > +  gimple *stmt = gimple_build_return (NULL);
> > > >    gimple_set_location (stmt, where);
> > > >
> > > > +  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
> > > > +                              get_identifier ("test_decl"),
> > > > +                              integer_type_node);
> > > >    /* Run all tests twice, with and then without optinfo enabled,
> > > > to ensure
> > > >       that immediate destinations vs optinfo-based destinations
> > > > both
> > > >       work, independently of each other, with no leaks.  */
> > > > @@ -1834,6 +2047,89 @@ test_capture_of_dump_calls (const
> > > > line_table_case &case_)
> > > >           }
> > > >        }
> > > >
> > > > +      /* Test of dump_printf with %T.  */
> > > > +      {
> > > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > > +       dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
> > > > +
> > > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "tree: 0");
> > > > +       if (with_optinfo)
> > > > +         {
> > > > +           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 (with_optinfo, MSG_ALL);
> > > > +       dump_printf (MSG_NOTE, "gimple: %E", stmt);
> > > > +
> > > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;");
> > > > +       if (with_optinfo)
> > > > +         {
> > > > +           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 (with_optinfo, MSG_ALL);
> > > > +       dump_printf (MSG_NOTE, "gimple: %G", stmt);
> > > > +
> > > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;\n");
> > > > +       if (with_optinfo)
> > > > +         {
> > > > +           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 (with_optinfo, MSG_ALL);
> > > > +       dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
> > > > +                        " %i consecutive %E%E after\n",
> > > > +                        integer_zero_node, test_decl, 42, stmt,
> > > > stmt);
> > > > +
> > > > +       ASSERT_DUMPED_TEXT_EQ (tmp,
> > > > +                              "test.txt:5:10: note: before 0 and
> > > > test_decl"
> > > > +                              " 42 consecutive return;return;
> > > > after\n");
> > > > +       if (with_optinfo)
> > > > +         {
> > > > +           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 (with_optinfo, MSG_ALL);
> > > > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > > > index 2b174e5..1fe64cb 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 dumpfile.c).  */
> > > > +
> > > > +#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
> > > > @@ -476,9 +489,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/testsuite/gcc.dg/format/gcc_diag-1.c
> > > > b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > > > index 034e097..8761456 100644
> > > > --- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > > > +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
> > > > @@ -1,4 +1,4 @@
> > > > -/* Test for GCC diagnositc formats.  */
> > > > +/* Test for GCC diagnostic formats.  */
> > > >  /* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
> > > >  /* { dg-do compile } */
> > > >  /* { dg-options "-Wformat" } */
> > > > @@ -24,6 +24,7 @@ extern int diag (const char *, ...)
> > > > ATTRIBUTE_DIAG(__gcc_diag__);
> > > >  extern int tdiag (const char *, ...)
> > > > ATTRIBUTE_DIAG(__gcc_tdiag__);
> > > >  extern int cdiag (const char *, ...)
> > > > ATTRIBUTE_DIAG(__gcc_cdiag__);
> > > >  extern int cxxdiag (const char *, ...)
> > > > ATTRIBUTE_DIAG(__gcc_cxxdiag__);
> > > > +extern int dump (const char *, ...)
> > > > ATTRIBUTE_DIAG(__gcc_dump_printf__);
> > > >
> > > >  void
> > > >  foo (int i, int i1, int i2, unsigned int u, double d, char *s,
> > > > void *p,
> > > > @@ -39,36 +40,44 @@ foo (int i, int i1, int i2, unsigned int u,
> > > > double d, char *s, void *p,
> > > >    tdiag ("%%");
> > > >    cdiag ("%%");
> > > >    cxxdiag ("%%");
> > > > +  dump ("%%");
> > > >    diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > > >    tdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > > >    cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > > >    cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > > > +  dump ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
> > > >    diag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > > p);
> > > >    tdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > > p);
> > > >    cdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > > p);
> > > >    cxxdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i,
> > > > s, p);
> > > > +  dump ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s,
> > > > p);
> > > >    diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > > >    tdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > > >    cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > > >    cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > > > +  dump ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
> > > >    diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > > >    tdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > > >    cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > > >    cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > > > +  dump ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
> > > >    diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > > >    tdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > > >    cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > > >    cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > > > +  dump ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
> > > >    diag ("%.*s", i, s);
> > > >    tdiag ("%.*s", i, s);
> > > >    cdiag ("%.*s", i, s);
> > > >    cxxdiag ("%.*s", i, s);
> > > > +  dump ("%.*s", i, s);
> > > >
> > > >    /* Extensions provided in the diagnostic framework.  */
> > > >    diag ("%m");
> > > >    tdiag ("%m");
> > > >    cdiag ("%m");
> > > >    cxxdiag ("%m");
> > > > +  dump ("%m");
> > > >
> > > >    /* Quote directives to avoid "warning: conversion used
> > > > unquoted." */
> > > >    tdiag ("%<%D%F%T%V%>", t1, t1, t1, t1);
> > > > @@ -94,20 +103,24 @@ foo (int i, int i1, int i2, unsigned int u,
> > > > double d, char *s, void *p,
> > > >    tdiag ("%Z", v, v_len);
> > > >    cdiag ("%Z", v, v_len);
> > > >    cxxdiag ("%Z", v, v_len);
> > > > +  dump ("%Z", v, v_len);
> > > >
> > > >    /* Bad stuff with extensions.  */
> > > >    diag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > > >    tdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > > >    cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > > >    cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
> > > > +  dump ("%m", i); /* { dg-warning "format" "extra arg" } */
> > > >    diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    tdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > > > +  dump ("%#m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    tdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > > > +  dump ("%+m"); /* { dg-warning "format" "bogus modifier" } */
> > > >    diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
> > > >    tdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
> > > >    tdiag ("%E", t1);
> > > > diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > > b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > > index ab9bc2f..8b5aadf1 100644
> > > > --- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > > +++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
> > > > @@ -19,12 +19,16 @@ typedef union tree_node *tree;
> > > >     the C test to find the symbol.  */
> > > >  typedef struct gcall gcall;
> > > >
> > > > +/* Likewise for gimple.  */
> > > > +typedef struct gimple gimple;
> > > > +
> > > >  #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__,
> > > > 1, 2)))
> > > >
> > > >  void diag (const char*, ...) FORMAT (diag);
> > > >  void cdiag (const char*, ...) FORMAT (cdiag);
> > > >  void tdiag (const char*, ...) FORMAT (tdiag);
> > > >  void cxxdiag (const char*, ...) FORMAT (cxxdiag);
> > > > +void dump (const char*, ...) FORMAT (dump_printf);
> > > >
> > > >  void test_diag (tree t, gcall *gc)
> > > >  {
> > > > @@ -157,3 +161,25 @@ void test_cxxdiag (tree t, gcall *gc)
> > > >    cxxdiag ("%<%V%>", t);
> > > >    cxxdiag ("%<%X%>", t);
> > > >  }
> > > > +
> > > > +void test_dump (tree t, gimple *stmt)
> > > > +{
> > > > +  dump ("%<");   /* { dg-warning "unterminated quoting directive"
> > > > } */
> > > > +  dump ("%>");   /* { dg-warning "unmatched quoting directive " }
> > > > */
> > > > +  dump ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting
> > > > directive" } */
> > > > +
> > > > +  dump ("%R");       /* { dg-warning "unmatched color reset
> > > > directive" } */
> > > > +  dump ("%r", "");   /* { dg-warning "unterminated color
> > > > directive" } */
> > > > +  dump ("%r%r", "", "");   /* { dg-warning "unterminated color
> > > > directive" } */
> > > > +  dump ("%r%R", "");
> > > > +  dump ("%r%r%R", "", "");
> > > > +  dump ("%r%R%r%R", "", "");
> > > > +
> > > > +  dump ("%<%R%>");      /* { dg-warning "unmatched color reset
> > > > directive" } */
> > > > +  dump ("%<%r%>", "");  /* { dg-warning "unterminated color
> > > > directive" } */
> > > > +  dump ("%<%r%R%>", "");
> > > > +
> > > > +  dump ("%E", stmt);
> > > > +  dump ("%T", t);
> > > > +  dump ("%G", stmt);
> > > > +}
> > > > --
> > > > 1.8.5.3
> > > >

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
  2018-07-31 12:54   ` Richard Biener
@ 2018-07-31 15:34     ` David Malcolm
  2018-07-31 15:37       ` Richard Biener
  0 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-07-31 15:34 UTC (permalink / raw)
  To: Richard Biener; +Cc: GCC Patches

On Tue, 2018-07-31 at 14:53 +0200, Richard Biener wrote:
> On Fri, Jul 27, 2018 at 11:48 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > With the addition of optinfo, the various dump_* calls had three
> > parts:
> > - optionally print to dump_file
> > - optionally print to alt_dump_file
> > - optionally make an optinfo_item and add it to the pending
> > optinfo,
> >   creating it for dump_*_loc calls.
> > 
> > However, this split makes it difficult to implement the formatted
> > dumps
> > later in patch kit, so as enabling work towards that, this patch
> > removes
> > the above split, so that all dumping within the dump_* API goes
> > through
> > optinfo_item.
> > 
> > In order to ensure that the dumps to dump_file and alt_dump_file
> > are
> > processed immediately (rather than being buffered within the
> > pending
> > optinfo for consolidation), this patch introduces the idea of
> > "immediate"
> > optinfo_item destinations vs "non-immediate" destinations.
> > 
> > The patch also adds selftest coverage of what's printed, and of
> > scopes.
> > 
> > This adds two allocations per dump_* call when dumping is enabled.
> > I'm assuming that this isn't a problem, as dump_enabled_p is
> > normally
> > false.  There are ways of optimizing it if it is an issue (by
> > making
> > optinfo_item instances become temporaries that borrow the
> > underlying
> > buffer), but they require nontrivial changes, so I'd prefer to
> > leave
> > that for another patch kit, if it becomes necessary.
> 
> Yeah, I guess that's OK given we can consolidate quite some calls
> after
> your patch anyways. 

We can, but FWIW my plan is to only touch the calls that I need to to
implement the  "Higher-level reporting of vectorization problems" idea
here:
   https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html

where the explicit dump calls become implicit within calls to things
like:

   return opt_result::failure_at (stmt,
                                  "not vectorized: different sized vector "
                                  "types in statement, %T and %T\n",
                                  vectype, nunits_vectype);

But if you think it's worthwhile, I can do a big patch that uses these
format codes throughout.

> Using alloca + placement new would be possible
> as well I guess?

Maybe.  I think the underlying question here is "what should the
lifetimes of the optinfo_items (and their text buffers) be?"

In the initial version of the optinfo patch kit, I had optinfo_items
being created in response to the various dump_* calls, and them being
added to an optinfo (which takes ownership of them), before the optinfo
is eventually emitted to various destinations; the optinfo is then
deleted, deleting the owned items.

This lifetime approach (having the optinfos own the optinfo_items) was
necessary because one of the destinations was through the diagnostics
system; they needed consolidation so that all of the items could be
alive at the point of emission.  (I think the JSON output also required
it at one point).

Hence the above approach needs the items and thus their underlying text
strings to live as long as the optinfo that owns them - the
destinations assume that the optinfo_items are all alive at the point
of emission.

Hence this requires new/delete pairs for the items, and also the
xstrdup around the text buffer, so that the items can own a copy.

But the dump_file and alt_dump_file destinations don't need the items
to be long-lived: they can be temporary wrappers.

Similarly, the optimization record destination could simply work in
terms of temporary items: when an optinfo_item is added, the
corresponding JSON could be added immediately.

So I think the only things that are requiring optinfo_items to be long-
lived are:
* the -fremarks idea from an earlier patch kit - and I'm not sure what
our plans for that should be, in terms of how it should interact with
alt_dump_file/-fopt-info
* the selftests within dumpfile.c itself.

So the other approach would be to rewrite dumpfile.c so that
optinfo_item instances (or maybe "dump_item" instances) are implicitly
temporary wrappers around a text buffer; the various emit destinations
make no assumptions that the items will stick around; any that do need 
them to (e.g. for dumpfile.c's selftests) make a copy, perhaps with a
optinfo_items_need_saving_p () function to guard adding a copy of each
item into the optinfo.

That would avoid the new/delete pair for all of the optinfo_item
instances, and the xstrdup for each one, apart from during selftests.

But it's a rewrite of this code (and has interactions with the rest of
the kit, which is why I didn't do it).

Is this something you'd want me to pursue as a followup?  (it's an
optimization of the dump_enabled_p branch.  Maybe it might become more
necessary for people using -fdump-optimization-record on large
codebases???)

> OK.

Thanks
Dave


> Richard.
> 
> > gcc/ChangeLog:
> >         * dump-context.h: Include "pretty-print.h".
> >         (dump_context::refresh_dumps_are_enabled): New decl.
> >         (dump_context::emit_item): New decl.
> >         (class dump_context): Add fields "m_test_pp" and
> >         "m_test_pp_flags".
> >         (temp_dump_context::temp_dump_context): Add param
> > "test_pp_flags".
> >         (temp_dump_context::get_dumped_text): New decl.
> >         (class temp_dump_context): Add field "m_pp".
> >         * dumpfile.c (refresh_dumps_are_enabled): Convert to...
> >         (dump_context::refresh_dumps_are_enabled): ...and add a
> > test for
> >         m_test_pp.
> >         (set_dump_file): Update for above change.
> >         (set_alt_dump_file): Likewise.
> >         (dump_loc): New overload, taking a pretty_printer *.
> >         (dump_context::dump_loc): Call end_any_optinfo.  Dump the
> > location
> >         to any test pretty-printer.
> >         (make_item_for_dump_gimple_stmt): New function, adapted
> > from
> >         optinfo::add_gimple_stmt.
> >         (dump_context::dump_gimple_stmt): Call it, and use the
> > result,
> >         eliminating the direct usage of dump_file and alt_dump_file
> > in
> >         favor of indirectly using them via emit_item.
> >         (make_item_for_dump_gimple_expr): New function, adapted
> > from
> >         optinfo::add_gimple_expr.
> >         (dump_context::dump_gimple_expr): Call it, and use the
> > result,
> >         eliminating the direct usage of dump_file and alt_dump_file
> > in
> >         favor of indirectly using them via emit_item.
> >         (make_item_for_dump_generic_expr): New function, adapted
> > from
> >         optinfo::add_tree.
> >         (dump_context::dump_generic_expr): Call it, and use the
> > result,
> >         eliminating the direct usage of dump_file and alt_dump_file
> > in
> >         favor of indirectly using them via emit_item.
> >         (make_item_for_dump_printf_va): New function, adapted from
> >         optinfo::add_printf_va.
> >         (make_item_for_dump_printf): New function.
> >         (dump_context::dump_printf_va): Call
> > make_item_for_dump_printf_va,
> >         and use the result, eliminating the direct usage of
> > dump_file and
> >         alt_dump_file in favor of indirectly using them via
> > emit_item.
> >         (make_item_for_dump_dec): New function.
> >         (dump_context::dump_dec): Call it, and use the result,
> >         eliminating the direct usage of dump_file and alt_dump_file
> > in
> >         favor of indirectly using them via emit_item.
> >         (make_item_for_dump_symtab_node): New function, adapted
> > from
> >         optinfo::add_symtab_node.
> >         (dump_context::dump_symtab_node): Call it, and use the
> > result,
> >         eliminating the direct usage of dump_file and alt_dump_file
> > in
> >         favor of indirectly using them via emit_item.
> >         (dump_context::begin_scope): Reimplement, avoiding direct
> > usage
> >         of dump_file and alt_dump_file in favor of indirectly using
> > them
> >         via emit_item.
> >         (dump_context::emit_item): New member function.
> >         (temp_dump_context::temp_dump_context): Add param
> > "test_pp_flags".
> >         Set up test pretty-printer on the underlying context.  Call
> >         refresh_dumps_are_enabled.
> >         (temp_dump_context::~temp_dump_context): Call
> >         refresh_dumps_are_enabled.
> >         (temp_dump_context::get_dumped_text): New member function.
> >         (selftest::verify_dumped_text): New function.
> >         (ASSERT_DUMPED_TEXT_EQ): New macro.
> >         (selftest::test_capture_of_dump_calls): Run all tests
> > twice, with
> >         and then without optinfo enabled.  Add uses of
> >         ASSERT_DUMPED_TEXT_EQ to all tests.  Add test of nested
> > scopes.
> >         * dumpfile.h: Update comment for the dump_* API.
> >         * optinfo-emit-json.cc
> >         (selftest::test_building_json_from_dump_calls): Update for
> > new
> >         param for temp_dump_context ctor.
> >         * optinfo.cc (optinfo_item::optinfo_item): Remove "owned"
> > param
> >         and "m_owned" field.
> >         (optinfo_item::~optinfo_item): Likewise.
> >         (optinfo::add_item): New member function.
> >         (optinfo::emit): Update comment.
> >         (optinfo::add_string): Delete.
> >         (optinfo::add_printf): Delete.
> >         (optinfo::add_printf_va): Delete.
> >         (optinfo::add_gimple_stmt): Delete.
> >         (optinfo::add_gimple_expr): Delete.
> >         (optinfo::add_tree): Delete.
> >         (optinfo::add_symtab_node): Delete.
> >         (optinfo::add_dec): Delete.
> >         * optinfo.h (class dump_context): New forward decl.
> >         (optinfo::add_item): New decl.
> >         (optinfo::add_string): Delete.
> >         (optinfo::add_printf): Delete.
> >         (optinfo::add_printf_va): Delete.
> >         (optinfo::add_gimple_stmt): Delete.
> >         (optinfo::add_gimple_expr): Delete.
> >         (optinfo::add_tree): Delete.
> >         (optinfo::add_symtab_node): Delete.
> >         (optinfo::add_dec): Delete.
> >         (optinfo::add_poly_int): Delete.
> >         (optinfo_item::optinfo_item): Remove "owned" param.
> >         (class optinfo_item): Remove field "m_owned".
> > ---
> >  gcc/dump-context.h       |  16 +-
> >  gcc/dumpfile.c           | 620 ++++++++++++++++++++++++++++++++++-
> > ------------
> >  gcc/dumpfile.h           |  34 ++-
> >  gcc/optinfo-emit-json.cc |   2 +-
> >  gcc/optinfo.cc           | 135 ++---------
> >  gcc/optinfo.h            |  38 +--
> >  6 files changed, 505 insertions(+), 340 deletions(-)
> > 
> > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > index f6df0b4..f40ea14 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 "pretty-print.h"
> > +
> >  /* A class for handling the various dump_* calls.
> > 
> >     In particular, this class has responsibility for consolidating
> > @@ -39,6 +41,8 @@ class dump_context
> > 
> >    ~dump_context ();
> > 
> > +  void refresh_dumps_are_enabled ();
> > +
> >    void dump_loc (dump_flags_t dump_kind, const dump_location_t
> > &loc);
> > 
> >    void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > extra_dump_flags,
> > @@ -93,6 +97,8 @@ class dump_context
> > 
> >    void end_any_optinfo ();
> > 
> > +  void emit_item (optinfo_item *item, dump_flags_t dump_kind);
> > +
> >   private:
> >    optinfo &ensure_pending_optinfo ();
> >    optinfo &begin_next_optinfo (const dump_location_t &loc);
> > @@ -108,6 +114,11 @@ class dump_context
> >       if any.  */
> >    optinfo *m_pending;
> > 
> > +  /* For use in selftests: if non-NULL, then items are to be
> > printed
> > +     to this, using the given flags.  */
> > +  pretty_printer *m_test_pp;
> > +  dump_flags_t m_test_pp_flags;
> > +
> >    /* The currently active dump_context, for use by the dump_* API
> > calls.  */
> >    static dump_context *s_current;
> > 
> > @@ -123,13 +134,16 @@ class dump_context
> >  class temp_dump_context
> >  {
> >   public:
> > -  temp_dump_context (bool forcibly_enable_optinfo);
> > +  temp_dump_context (bool forcibly_enable_optinfo,
> > +                    dump_flags_t test_pp_flags);
> >    ~temp_dump_context ();
> > 
> >    /* Support for selftests.  */
> >    optinfo *get_pending_optinfo () const { return
> > m_context.m_pending; }
> > +  const char *get_dumped_text ();
> > 
> >   private:
> > +  pretty_printer m_pp;
> >    dump_context m_context;
> >    dump_context *m_saved;
> >    bool m_saved_flag_remarks;
> > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > index 3c8bc38..10e9cab 100644
> > --- a/gcc/dumpfile.c
> > +++ b/gcc/dumpfile.c
> > @@ -63,15 +63,6 @@ dump_flags_t dump_flags;
> >  bool dumps_are_enabled = false;
> > 
> > 
> > -/* Update the "dumps_are_enabled" global; to be called whenever
> > dump_file
> > -   or alt_dump_file change.  */
> > -
> > -static void
> > -refresh_dumps_are_enabled ()
> > -{
> > -  dumps_are_enabled = (dump_file || alt_dump_file ||
> > optinfo_enabled_p ());
> > -}
> > -
> >  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the
> > "dumps_are_enabled"
> >     global.  */
> > 
> > @@ -80,7 +71,7 @@ set_dump_file (FILE *new_dump_file)
> >  {
> >    dumpfile_ensure_any_optinfo_are_flushed ();
> >    dump_file = new_dump_file;
> > -  refresh_dumps_are_enabled ();
> > +  dump_context::get ().refresh_dumps_are_enabled ();
> >  }
> > 
> >  /* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the
> > "dumps_are_enabled"
> > @@ -91,7 +82,7 @@ set_alt_dump_file (FILE *new_alt_dump_file)
> >  {
> >    dumpfile_ensure_any_optinfo_are_flushed ();
> >    alt_dump_file = new_alt_dump_file;
> > -  refresh_dumps_are_enabled ();
> > +  dump_context::get ().refresh_dumps_are_enabled ();
> >  }
> > 
> >  #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
> > @@ -465,6 +456,27 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile,
> > source_location loc)
> >      }
> >  }
> > 
> > +/* Print source location to PP if enabled.  */
> > +
> > +static void
> > +dump_loc (dump_flags_t dump_kind, pretty_printer *pp,
> > source_location loc)
> > +{
> > +  if (dump_kind)
> > +    {
> > +      if (LOCATION_LOCUS (loc) > BUILTINS_LOCATION)
> > +       pp_printf (pp, "%s:%d:%d: note: ", LOCATION_FILE (loc),
> > +                  LOCATION_LINE (loc), LOCATION_COLUMN (loc));
> > +      else if (current_function_decl)
> > +       pp_printf (pp, "%s:%d:%d: note: ",
> > +                  DECL_SOURCE_FILE (current_function_decl),
> > +                  DECL_SOURCE_LINE (current_function_decl),
> > +                  DECL_SOURCE_COLUMN (current_function_decl));
> > +      /* Indentation based on scope depth.  */
> > +      for (unsigned i = 0; i < get_dump_scope_depth (); i++)
> > +       pp_character (pp, ' ');
> > +    }
> > +}
> > +
> >  /* Implementation of dump_context member functions.  */
> > 
> >  /* dump_context's dtor.  */
> > @@ -474,12 +486,24 @@ dump_context::~dump_context ()
> >    delete m_pending;
> >  }
> > 
> > +/* Update the "dumps_are_enabled" global; to be called whenever
> > dump_file
> > +   or alt_dump_file change, or when changing dump_context in
> > selftests.  */
> > +
> > +void
> > +dump_context::refresh_dumps_are_enabled ()
> > +{
> > +  dumps_are_enabled = (dump_file || alt_dump_file ||
> > optinfo_enabled_p ()
> > +                      || m_test_pp);
> > +}
> > +
> >  /* Print LOC to the appropriate dump destinations, given
> > DUMP_KIND.
> >     If optinfos are enabled, begin a new optinfo.  */
> > 
> >  void
> >  dump_context::dump_loc (dump_flags_t dump_kind, const
> > dump_location_t &loc)
> >  {
> > +  end_any_optinfo ();
> > +
> >    location_t srcloc = loc.get_location_t ();
> > 
> >    if (dump_file && (dump_kind & pflags))
> > @@ -488,6 +512,10 @@ dump_context::dump_loc (dump_flags_t
> > dump_kind, const dump_location_t &loc)
> >    if (alt_dump_file && (dump_kind & alt_flags))
> >      ::dump_loc (dump_kind, alt_dump_file, srcloc);
> > 
> > +  /* Support for temp_dump_context in selftests.  */
> > +  if (m_test_pp && (dump_kind & m_test_pp_flags))
> > +    ::dump_loc (dump_kind, m_test_pp, srcloc);
> > +
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = begin_next_optinfo (loc);
> > @@ -495,6 +523,22 @@ dump_context::dump_loc (dump_flags_t
> > dump_kind, const dump_location_t &loc)
> >      }
> >  }
> > 
> > +/* Make an item for the given dump call, equivalent to
> > print_gimple_stmt.  */
> > +
> > +static optinfo_item *
> > +make_item_for_dump_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)));
> > +  return item;
> > +}
> > +
> >  /* Dump gimple statement GS with SPC indentation spaces and
> >     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is
> > enabled.  */
> > 
> > @@ -503,18 +547,18 @@ dump_context::dump_gimple_stmt (dump_flags_t
> > dump_kind,
> >                                 dump_flags_t extra_dump_flags,
> >                                 gimple *gs, int spc)
> >  {
> > -  if (dump_file && (dump_kind & pflags))
> > -    print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> > -
> > -  if (alt_dump_file && (dump_kind & alt_flags))
> > -    print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> > +  optinfo_item *item
> > +    = make_item_for_dump_gimple_stmt (gs, spc, dump_flags |
> > extra_dump_flags);
> > +  emit_item (item, dump_kind);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> >        info.handle_dump_file_kind (dump_kind);
> > -      info.add_gimple_stmt (gs, spc, dump_flags |
> > extra_dump_flags);
> > +      info.add_item (item);
> >      }
> > +  else
> > +    delete item;
> >  }
> > 
> >  /* Similar to dump_gimple_stmt, except additionally print source
> > location.  */
> > @@ -529,6 +573,22 @@ dump_context::dump_gimple_stmt_loc
> > (dump_flags_t dump_kind,
> >    dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
> >  }
> > 
> > +/* Make an item for the given dump call, equivalent to
> > print_gimple_expr.  */
> > +
> > +static optinfo_item *
> > +make_item_for_dump_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)));
> > +  return item;
> > +}
> > +
> >  /* Dump gimple statement GS with SPC indentation spaces and
> >     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
> >     Do not terminate with a newline or semicolon.  */
> > @@ -538,18 +598,18 @@ dump_context::dump_gimple_expr (dump_flags_t
> > dump_kind,
> >                                 dump_flags_t extra_dump_flags,
> >                                 gimple *gs, int spc)
> >  {
> > -  if (dump_file && (dump_kind & pflags))
> > -    print_gimple_expr (dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> > -
> > -  if (alt_dump_file && (dump_kind & alt_flags))
> > -    print_gimple_expr (alt_dump_file, gs, spc, dump_flags |
> > extra_dump_flags);
> > +  optinfo_item *item
> > +    = make_item_for_dump_gimple_expr (gs, spc, dump_flags |
> > extra_dump_flags);
> > +  emit_item (item, dump_kind);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> >        info.handle_dump_file_kind (dump_kind);
> > -      info.add_gimple_expr (gs, spc, dump_flags |
> > extra_dump_flags);
> > +      info.add_item (item);
> >      }
> > +  else
> > +    delete item;
> >  }
> > 
> >  /* Similar to dump_gimple_expr, except additionally print source
> > location.  */
> > @@ -565,6 +625,25 @@ dump_context::dump_gimple_expr_loc
> > (dump_flags_t dump_kind,
> >    dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
> >  }
> > 
> > +/* Make an item for the given dump call, equivalent to
> > print_generic_expr.  */
> > +
> > +static optinfo_item *
> > +make_item_for_dump_generic_expr (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)));
> > +  return item;
> > +}
> > 
> >  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams
> > if
> >     DUMP_KIND is enabled.  */
> > @@ -574,18 +653,18 @@ dump_context::dump_generic_expr (dump_flags_t
> > dump_kind,
> >                                  dump_flags_t extra_dump_flags,
> >                                  tree t)
> >  {
> > -  if (dump_file && (dump_kind & pflags))
> > -      print_generic_expr (dump_file, t, dump_flags |
> > extra_dump_flags);
> > -
> > -  if (alt_dump_file && (dump_kind & alt_flags))
> > -      print_generic_expr (alt_dump_file, t, dump_flags |
> > extra_dump_flags);
> > +  optinfo_item *item
> > +    = make_item_for_dump_generic_expr (t, dump_flags |
> > extra_dump_flags);
> > +  emit_item (item, dump_kind);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> >        info.handle_dump_file_kind (dump_kind);
> > -      info.add_tree (t, dump_flags | extra_dump_flags);
> > +      info.add_item (item);
> >      }
> > +  else
> > +    delete item;
> >  }
> > 
> > 
> > @@ -602,36 +681,56 @@ dump_context::dump_generic_expr_loc
> > (dump_flags_t dump_kind,
> >    dump_generic_expr (dump_kind, extra_dump_flags, t);
> >  }
> > 
> > +/* Make an item for the given dump call.  */
> > +
> > +static optinfo_item *
> > +make_item_for_dump_printf_va (const char *format, va_list ap)
> > +  ATTRIBUTE_PRINTF (1, 0);
> > +
> > +static optinfo_item *
> > +make_item_for_dump_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);
> > +  return item;
> > +}
> > +
> > +/* Make an item for the given dump call.  */
> > +
> > +static optinfo_item *
> > +make_item_for_dump_printf (const char *format, ...)
> > +  ATTRIBUTE_PRINTF (1, 2);
> > +
> > +static optinfo_item *
> > +make_item_for_dump_printf (const char *format, ...)
> > +{
> > +  va_list ap;
> > +  va_start (ap, format);
> > +  optinfo_item *item
> > +    = make_item_for_dump_printf_va (format, ap);
> > +  va_end (ap);
> > +  return item;
> > +}
> > +
> >  /* Output a formatted message using FORMAT on appropriate dump
> > streams.  */
> > 
> >  void
> >  dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > *format,
> >                               va_list ap)
> >  {
> > -  if (dump_file && (dump_kind & pflags))
> > -    {
> > -      va_list aq;
> > -      va_copy (aq, ap);
> > -      vfprintf (dump_file, format, aq);
> > -      va_end (aq);
> > -    }
> > -
> > -  if (alt_dump_file && (dump_kind & alt_flags))
> > -    {
> > -      va_list aq;
> > -      va_copy (aq, ap);
> > -      vfprintf (alt_dump_file, format, aq);
> > -      va_end (aq);
> > -    }
> > +  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> > +  emit_item (item, dump_kind);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> > -      va_list aq;
> > -      va_copy (aq, ap);
> > -      info.add_printf_va (format, aq);
> > -      va_end (aq);
> > +      info.handle_dump_file_kind (dump_kind);
> > +      info.add_item (item);
> >      }
> > +  else
> > +    delete item;
> >  }
> > 
> >  /* Similar to dump_printf, except source location is also printed,
> > and
> > @@ -646,26 +745,64 @@ dump_context::dump_printf_loc_va
> > (dump_flags_t dump_kind,
> >    dump_printf_va (dump_kind, format, ap);
> >  }
> > 
> > -/* Output VALUE in decimal to appropriate dump streams.  */
> > +/* Make an item for the given dump call, equivalent to
> > print_dec.  */
> > 
> >  template<unsigned int N, typename C>
> > -void
> > -dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N,
> > C> &value)
> > +static optinfo_item *
> > +make_item_for_dump_dec (const poly_int<N, C> &value)
> >  {
> >    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> >    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > UNSIGNED;
> > -  if (dump_file && (dump_kind & pflags))
> > -    print_dec (value, dump_file, sgn);
> > 
> > -  if (alt_dump_file && (dump_kind & alt_flags))
> > -    print_dec (value, alt_dump_file, sgn);
> > +  pretty_printer pp;
> > +
> > +  if (value.is_constant ())
> > +    pp_wide_int (&pp, value.coeffs[0], sgn);
> > +  else
> > +    {
> > +      pp_character (&pp, '[');
> > +      for (unsigned int i = 0; i < N; ++i)
> > +       {
> > +         pp_wide_int (&pp, value.coeffs[i], sgn);
> > +         pp_character (&pp, i == N - 1 ? ']' : ',');
> > +       }
> > +    }
> > +
> > +  optinfo_item *item
> > +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > +                       xstrdup (pp_formatted_text (&pp)));
> > +  return item;
> > +}
> > +
> > +/* Output VALUE in decimal to appropriate dump streams.  */
> > +
> > +template<unsigned int N, typename C>
> > +void
> > +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N,
> > C> &value)
> > +{
> > +  optinfo_item *item = make_item_for_dump_dec (value);
> > +  emit_item (item, dump_kind);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> >        info.handle_dump_file_kind (dump_kind);
> > -      info.add_poly_int<N,C> (value);
> > +      info.add_item (item);
> >      }
> > +  else
> > +    delete item;
> > +}
> > +
> > +/* Make an item for the given dump call.  */
> > +
> > +static optinfo_item *
> > +make_item_for_dump_symtab_node (symtab_node *node)
> > +{
> > +  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> > +  optinfo_item *item
> > +    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> > +                       xstrdup (node->dump_name ()));
> > +  return item;
> >  }
> > 
> >  /* Output the name of NODE on appropriate dump streams.  */
> > @@ -673,18 +810,17 @@ dump_context::dump_dec (dump_flags_t
> > dump_kind, const poly_int<N, C> &value)
> >  void
> >  dump_context::dump_symtab_node (dump_flags_t dump_kind,
> > symtab_node *node)
> >  {
> > -  if (dump_file && (dump_kind & pflags))
> > -    fprintf (dump_file, "%s", node->dump_name ());
> > -
> > -  if (alt_dump_file && (dump_kind & alt_flags))
> > -    fprintf (alt_dump_file, "%s", node->dump_name ());
> > +  optinfo_item *item = make_item_for_dump_symtab_node (node);
> > +  emit_item (item, dump_kind);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> >        optinfo &info = ensure_pending_optinfo ();
> >        info.handle_dump_file_kind (dump_kind);
> > -      info.add_symtab_node (node);
> > +      info.add_item (item);
> >      }
> > +  else
> > +    delete item;
> >  }
> > 
> >  /* Get the current dump scope-nesting depth.
> > @@ -705,28 +841,28 @@ dump_context::get_scope_depth () const
> >  void
> >  dump_context::begin_scope (const char *name, const dump_location_t
> > &loc)
> >  {
> > -  /* Specialcase, to avoid going through dump_printf_loc,
> > -     so that we can create a optinfo of kind
> > OPTINFO_KIND_SCOPE.  */
> > -
> >    if (dump_file)
> > -    {
> > -      ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> > -      fprintf (dump_file, "=== %s ===\n", name);
> > -    }
> > +    ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> > 
> >    if (alt_dump_file)
> > -    {
> > -      ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> > -      fprintf (alt_dump_file, "=== %s ===\n", name);
> > -    }
> > +    ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> > +
> > +  /* Support for temp_dump_context in selftests.  */
> > +  if (m_test_pp)
> > +    ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
> > +
> > +  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n",
> > name);
> > +  emit_item (item, MSG_NOTE);
> > 
> >    if (optinfo_enabled_p ())
> >      {
> > +      optinfo &info = begin_next_optinfo (loc);
> > +      info.m_kind = OPTINFO_KIND_SCOPE;
> > +      info.add_item (item);
> >        end_any_optinfo ();
> > -      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> > -      info.add_printf ("=== %s ===", name);
> > -      info.emit ();
> >      }
> > +  else
> > +    delete item;
> > 
> >    m_scope_depth++;
> >  }
> > @@ -776,6 +912,23 @@ dump_context::end_any_optinfo ()
> >    m_pending = NULL;
> >  }
> > 
> > +/* Emit ITEM to all item destinations (those that don't require
> > +   consolidation into optinfo instances).  */
> > +
> > +void
> > +dump_context::emit_item (optinfo_item *item, dump_flags_t
> > dump_kind)
> > +{
> > +  if (dump_file && (dump_kind & pflags))
> > +    fprintf (dump_file, "%s", item->get_text ());
> > +
> > +  if (alt_dump_file && (dump_kind & alt_flags))
> > +    fprintf (alt_dump_file, "%s", item->get_text ());
> > +
> > +  /* Support for temp_dump_context in selftests.  */
> > +  if (m_test_pp && (dump_kind & m_test_pp_flags))
> > +    pp_string (m_test_pp, item->get_text ());
> > +}
> > +
> >  /* The current singleton dump_context, and its default.  */
> > 
> >  dump_context *dump_context::s_current = &dump_context::s_default;
> > @@ -1510,12 +1663,18 @@ enable_rtl_dump_file (void)
> >  /* temp_dump_context's ctor.  Temporarily override the
> > dump_context
> >     (to forcibly enable optinfo-generation).  */
> > 
> > -temp_dump_context::temp_dump_context (bool
> > forcibly_enable_optinfo)
> > +temp_dump_context::temp_dump_context (bool
> > forcibly_enable_optinfo,
> > +                                     dump_flags_t test_pp_flags)
> > +
> >  : m_context (),
> >    m_saved (&dump_context ().get ())
> >  {
> >    dump_context::s_current = &m_context;
> >    m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> > +  m_context.m_test_pp = &m_pp;
> > +  m_context.m_test_pp_flags = test_pp_flags;
> > +
> > +  dump_context::get ().refresh_dumps_are_enabled ();
> >  }
> > 
> >  /* temp_dump_context's dtor.  Restore the saved dump_context.  */
> > @@ -1523,6 +1682,16 @@ temp_dump_context::temp_dump_context (bool
> > forcibly_enable_optinfo)
> >  temp_dump_context::~temp_dump_context ()
> >  {
> >    dump_context::s_current = m_saved;
> > +
> > +  dump_context::get ().refresh_dumps_are_enabled ();
> > +}
> > +
> > +/* 0-terminate the text dumped so far, and return it.  */
> > +
> > +const char *
> > +temp_dump_context::get_dumped_text ()
> > +{
> > +  return pp_formatted_text (&m_pp);
> >  }
> > 
> >  namespace selftest {
> > @@ -1561,6 +1730,29 @@ test_impl_location ()
> >  #endif
> >  }
> > 
> > +/* Verify that the text dumped so far in CONTEXT equals
> > +   EXPECTED_TEXT, using LOC for the location of any failure.
> > +   As a side-effect, the internal buffer is 0-terminated.  */
> > +
> > +static void
> > +verify_dumped_text (const location &loc,
> > +                   temp_dump_context *context,
> > +                   const char *expected_text)
> > +{
> > +  gcc_assert (context);
> > +  ASSERT_STREQ_AT (loc, context->get_dumped_text (),
> > +                  expected_text);
> > +}
> > +
> > +/* Verify that the text dumped so far in CONTEXT equals
> > +   EXPECTED_TEXT.
> > +   As a side-effect, the internal buffer is 0-terminated.  */
> > +
> > +#define ASSERT_DUMPED_TEXT_EQ(CONTEXT,
> > EXPECTED_TEXT)                  \
> > +  SELFTEST_BEGIN_STMT                                             
> >      \
> > +    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT),
> > (EXPECTED_TEXT)); \
> > +  SELFTEST_END_STMT
> > +
> >  /* Verify that ITEM has the expected values.  */
> > 
> >  static void
> > @@ -1611,116 +1803,198 @@ test_capture_of_dump_calls (const
> > line_table_case &case_)
> >    linemap_line_start (line_table, 5, 100);
> >    linemap_add (line_table, LC_LEAVE, false, NULL, 0);
> >    location_t where = linemap_position_for_column (line_table, 10);
> > +  if (where > LINE_MAP_MAX_LOCATION_WITH_COLS)
> > +    return;
> > 
> >    dump_location_t loc = dump_location_t::from_location_t (where);
> > 
> > -  /* Test of dump_printf.  */
> > -  {
> > -    temp_dump_context tmp (true);
> > -    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> > -
> > -    optinfo *info = tmp.get_pending_optinfo ();
> > -    ASSERT_TRUE (info != NULL);
> > -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > -    ASSERT_EQ (info->num_items (), 1);
> > -    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
> > -  }
> > -
> > -  /* Tree, via dump_generic_expr.  */
> > -  {
> > -    temp_dump_context tmp (true);
> > -    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > -    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > -
> > -    optinfo *info = tmp.get_pending_optinfo ();
> > -    ASSERT_TRUE (info != NULL);
> > -    ASSERT_EQ (info->get_location_t (), where);
> > -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > -    ASSERT_EQ (info->num_items (), 2);
> > -    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> > -    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
> > -  }
> > -
> > -  /* Tree, via dump_generic_expr_loc.  */
> > -  {
> > -    temp_dump_context tmp (true);
> > -    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> > integer_one_node);
> > -
> > -    optinfo *info = tmp.get_pending_optinfo ();
> > -    ASSERT_TRUE (info != NULL);
> > -    ASSERT_EQ (info->get_location_t (), where);
> > -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > -    ASSERT_EQ (info->num_items (), 1);
> > -    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
> > -  }
> > -
> > -  /* Gimple.  */
> > -  {
> > -    greturn *stmt = gimple_build_return (NULL);
> > -    gimple_set_location (stmt, where);
> > -
> > -    /* dump_gimple_stmt_loc.  */
> > -    {
> > -      temp_dump_context tmp (true);
> > -      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > -
> > -      optinfo *info = tmp.get_pending_optinfo ();
> > -      ASSERT_TRUE (info != NULL);
> > -      ASSERT_EQ (info->num_items (), 1);
> > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> > -    }
> > -
> > -    /* dump_gimple_stmt.  */
> > -    {
> > -      temp_dump_context tmp (true);
> > -      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> > +  greturn *stmt = gimple_build_return (NULL);
> > +  gimple_set_location (stmt, where);
> > 
> > -      optinfo *info = tmp.get_pending_optinfo ();
> > -      ASSERT_TRUE (info != NULL);
> > -      ASSERT_EQ (info->num_items (), 1);
> > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> > -    }
> > -
> > -    /* dump_gimple_expr_loc.  */
> > +  /* Run all tests twice, with and then without optinfo enabled,
> > to ensure
> > +     that immediate destinations vs optinfo-based destinations
> > both
> > +     work, independently of each other, with no leaks.  */
> > +  for (int i = 0 ; i < 2; i++)
> >      {
> > -      temp_dump_context tmp (true);
> > -      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > +      bool with_optinfo = (i == 0);
> > +
> > +      /* Test of dump_printf.  */
> > +      {
> > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +       dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "int: 42 str: foo");
> > +       if (with_optinfo)
> > +         {
> > +           optinfo *info = tmp.get_pending_optinfo ();
> > +           ASSERT_TRUE (info != NULL);
> > +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > +           ASSERT_EQ (info->num_items (), 1);
> > +           ASSERT_IS_TEXT (info->get_item (0), "int: 42 str:
> > foo");
> > +         }
> > +      }
> > +
> > +      /* Tree, via dump_generic_expr.  */
> > +      {
> > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +       dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > +       dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: test of
> > tree: 0");
> > +       if (with_optinfo)
> > +         {
> > +           optinfo *info = tmp.get_pending_optinfo ();
> > +           ASSERT_TRUE (info != NULL);
> > +           ASSERT_EQ (info->get_location_t (), where);
> > +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > +           ASSERT_EQ (info->num_items (), 2);
> > +           ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> > +           ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION,
> > "0");
> > +         }
> > +      }
> > +
> > +      /* Tree, via dump_generic_expr_loc.  */
> > +      {
> > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +       dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> > integer_one_node);
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: 1");
> > +       if (with_optinfo)
> > +         {
> > +           optinfo *info = tmp.get_pending_optinfo ();
> > +           ASSERT_TRUE (info != NULL);
> > +           ASSERT_EQ (info->get_location_t (), where);
> > +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > +           ASSERT_EQ (info->num_items (), 1);
> > +           ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION,
> > "1");
> > +         }
> > +      }
> > +
> > +      /* Gimple.  */
> > +      {
> > +       /* dump_gimple_stmt_loc.  */
> > +       {
> > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +         dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > +
> > +         ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note:
> > return;\n");
> > +         if (with_optinfo)
> > +           {
> > +             optinfo *info = tmp.get_pending_optinfo ();
> > +             ASSERT_TRUE (info != NULL);
> > +             ASSERT_EQ (info->num_items (), 1);
> > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > "return;\n");
> > +           }
> > +       }
> > 
> > -      optinfo *info = tmp.get_pending_optinfo ();
> > -      ASSERT_TRUE (info != NULL);
> > -      ASSERT_EQ (info->num_items (), 1);
> > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> > -    }
> > +       /* dump_gimple_stmt.  */
> > +       {
> > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +         dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> > +
> > +         ASSERT_DUMPED_TEXT_EQ (tmp, "return;\n");
> > +         if (with_optinfo)
> > +           {
> > +             optinfo *info = tmp.get_pending_optinfo ();
> > +             ASSERT_TRUE (info != NULL);
> > +             ASSERT_EQ (info->num_items (), 1);
> > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > "return;\n");
> > +           }
> > +       }
> > 
> > -    /* dump_gimple_expr.  */
> > -    {
> > -      temp_dump_context tmp (true);
> > -      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> > +       /* dump_gimple_expr_loc.  */
> > +       {
> > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +         dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > +
> > +         ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note:
> > return;");
> > +         if (with_optinfo)
> > +           {
> > +             optinfo *info = tmp.get_pending_optinfo ();
> > +             ASSERT_TRUE (info != NULL);
> > +             ASSERT_EQ (info->num_items (), 1);
> > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > "return;");
> > +           }
> > +       }
> > 
> > -      optinfo *info = tmp.get_pending_optinfo ();
> > -      ASSERT_TRUE (info != NULL);
> > -      ASSERT_EQ (info->num_items (), 1);
> > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> > +       /* dump_gimple_expr.  */
> > +       {
> > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +         dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> > +
> > +         ASSERT_DUMPED_TEXT_EQ (tmp, "return;");
> > +         if (with_optinfo)
> > +           {
> > +             optinfo *info = tmp.get_pending_optinfo ();
> > +             ASSERT_TRUE (info != NULL);
> > +             ASSERT_EQ (info->num_items (), 1);
> > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > "return;");
> > +           }
> > +       }
> > +      }
> > +
> > +      /* poly_int.  */
> > +      {
> > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +       dump_dec (MSG_NOTE, poly_int64 (42));
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp, "42");
> > +       if (with_optinfo)
> > +         {
> > +           optinfo *info = tmp.get_pending_optinfo ();
> > +           ASSERT_TRUE (info != NULL);
> > +           ASSERT_EQ (info->num_items (), 1);
> > +           ASSERT_IS_TEXT (info->get_item (0), "42");
> > +         }
> > +      }
> > +
> > +      /* scopes.  */
> > +      {
> > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > +       dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");
> > +       {
> > +         AUTO_DUMP_SCOPE ("outer scope", stmt);
> > +         dump_printf_loc (MSG_NOTE, stmt, "msg 2\n");
> > +         {
> > +           AUTO_DUMP_SCOPE ("middle scope", stmt);
> > +           dump_printf_loc (MSG_NOTE, stmt, "msg 3\n");
> > +           {
> > +             AUTO_DUMP_SCOPE ("inner scope", stmt);
> > +             dump_printf_loc (MSG_NOTE, stmt, "msg 4\n");
> > +           }
> > +           dump_printf_loc (MSG_NOTE, stmt, "msg 5\n");
> > +         }
> > +         dump_printf_loc (MSG_NOTE, stmt, "msg 6\n");
> > +       }
> > +       dump_printf_loc (MSG_NOTE, stmt, "msg 7\n");
> > +
> > +       ASSERT_DUMPED_TEXT_EQ (tmp,
> > +                              "test.txt:5:10: note: msg 1\n"
> > +                              "test.txt:5:10: note: === outer
> > scope ===\n"
> > +                              "test.txt:5:10: note:  msg 2\n"
> > +                              "test.txt:5:10: note:  === middle
> > scope ===\n"
> > +                              "test.txt:5:10: note:   msg 3\n"
> > +                              "test.txt:5:10: note:   === inner
> > scope ===\n"
> > +                              "test.txt:5:10: note:    msg 4\n"
> > +                              "test.txt:5:10: note:   msg 5\n"
> > +                              "test.txt:5:10: note:  msg 6\n"
> > +                              "test.txt:5:10: note: msg 7\n");
> > +       if (with_optinfo)
> > +         {
> > +           optinfo *info = tmp.get_pending_optinfo ();
> > +           ASSERT_TRUE (info != NULL);
> > +           ASSERT_EQ (info->num_items (), 1);
> > +           ASSERT_IS_TEXT (info->get_item (0), "msg 7\n");
> > +         }
> > +      }
> >      }
> > -  }
> > -
> > -  /* poly_int.  */
> > -  {
> > -    temp_dump_context tmp (true);
> > -    dump_dec (MSG_NOTE, poly_int64 (42));
> > -
> > -    optinfo *info = tmp.get_pending_optinfo ();
> > -    ASSERT_TRUE (info != NULL);
> > -    ASSERT_EQ (info->num_items (), 1);
> > -    ASSERT_IS_TEXT (info->get_item (0), "42");
> > -  }
> > 
> >    /* Verify that MSG_* affects optinfo->get_kind (); we tested
> > MSG_NOTE
> >       above.  */
> >    {
> >      /* MSG_OPTIMIZED_LOCATIONS.  */
> >      {
> > -      temp_dump_context tmp (true);
> > +      temp_dump_context tmp (true, MSG_ALL);
> >        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> >        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> >                  OPTINFO_KIND_SUCCESS);
> > @@ -1728,7 +2002,7 @@ test_capture_of_dump_calls (const
> > line_table_case &case_)
> > 
> >      /* MSG_MISSED_OPTIMIZATION.  */
> >      {
> > -      temp_dump_context tmp (true);
> > +      temp_dump_context tmp (true, MSG_ALL);
> >        dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> >        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> >                  OPTINFO_KIND_FAILURE);
> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > index 1dbe3b8..2b174e5 100644
> > --- a/gcc/dumpfile.h
> > +++ b/gcc/dumpfile.h
> > @@ -442,19 +442,27 @@ dump_enabled_p (void)
> >  }
> > 
> >  /* The following API calls (which *don't* take a "FILE *")
> > -   write the output to zero or more locations:
> > -   (a) the active dump_file, if any
> > -   (b) the -fopt-info destination, if any
> > -   (c) to the "optinfo" destinations, if any:
> > -       (c.1) as optimization records
> > -
> > -   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> > -                                   |
> > -                                   +--> (b) alt_dump_file
> > -                                   |
> > -                                   `--> (c) optinfo
> > -                                            `---> optinfo
> > destinations
> > -                                                  (c.1)
> > optimization records
> > +   write the output to zero or more locations.
> > +
> > +   Some destinations are written to immediately as dump_* calls
> > +   are made; for others, the output is consolidated into an
> > "optinfo"
> > +   instance (with its own metadata), and only emitted once the
> > optinfo
> > +   is complete.
> > +
> > +   The destinations are:
> > +
> > +   (a) the "immediate" destinations:
> > +       (a.1) the active dump_file, if any
> > +       (a.2) the -fopt-info destination, if any
> > +   (b) the "optinfo" destinations, if any:
> > +       (b.1) as optimization records
> > +
> > +   dump_* (MSG_*) --> dumpfile.c --> items --> (a.1) dump_file
> > +                                       |   `-> (a.2) alt_dump_file
> > +                                       |
> > +                                       `--> (b) optinfo
> > +                                                `---> optinfo
> > destinations
> > +                                                      (b.1)
> > optimization records
> > 
> >     For optinfos, the dump_*_loc mark the beginning of an optinfo
> >     instance: all subsequent dump_* calls are consolidated into
> > diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> > index 2199d52..992960e 100644
> > --- a/gcc/optinfo-emit-json.cc
> > +++ b/gcc/optinfo-emit-json.cc
> > @@ -537,7 +537,7 @@ namespace selftest {
> >  static void
> >  test_building_json_from_dump_calls ()
> >  {
> > -  temp_dump_context tmp (true);
> > +  temp_dump_context tmp (true, MSG_NOTE);
> >    dump_location_t loc;
> >    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> >    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> > index 93de9d9..b858c3c 100644
> > --- a/gcc/optinfo.cc
> > +++ b/gcc/optinfo.cc
> > @@ -34,11 +34,11 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "cgraph.h"
> >  #include "selftest.h"
> > 
> > -/* optinfo_item's ctor.  */
> > +/* optinfo_item's ctor.  Takes ownership of TEXT.  */
> > 
> >  optinfo_item::optinfo_item (enum optinfo_item_kind kind,
> > location_t location,
> > -                           char *text, bool owned)
> > -: m_kind (kind), m_location (location), m_text (text), m_owned
> > (owned)
> > +                           char *text)
> > +: m_kind (kind), m_location (location), m_text (text)
> >  {
> >  }
> > 
> > @@ -46,8 +46,7 @@ optinfo_item::optinfo_item (enum
> > optinfo_item_kind kind, location_t location,
> > 
> >  optinfo_item::~optinfo_item ()
> >  {
> > -  if (m_owned)
> > -    free (m_text);
> > +  free (m_text);
> >  }
> > 
> >  /* Get a string from KIND.  */
> > @@ -81,7 +80,17 @@ optinfo::~optinfo ()
> >      delete item;
> >  }
> > 
> > -/* Emit the optinfo to all of the active destinations.  */
> > +/* Add ITEM to this optinfo.  */
> > +
> > +void
> > +optinfo::add_item (optinfo_item *item)
> > +{
> > +  gcc_assert (item);
> > +  m_items.safe_push (item);
> > +}
> > +
> > +/* Emit the optinfo to all of the "non-immediate" destinations
> > +   (emission to "immediate" destinations is done by
> > emit_item).  */
> > 
> >  void
> >  optinfo::emit ()
> > @@ -103,120 +112,6 @@ optinfo::handle_dump_file_kind (dump_flags_t
> > dump_kind)
> >      m_kind = OPTINFO_KIND_NOTE;
> >  }
> > 
> > -/* Append a string literal to this optinfo.  */
> > -
> > -void
> > -optinfo::add_string (const char *str)
> > -{
> > -  optinfo_item *item
> > -    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > -                       const_cast <char *> (str), false);
> > -  m_items.safe_push (item);
> > -}
> > -
> > -/* Append printf-formatted text to this optinfo.  */
> > -
> > -void
> > -optinfo::add_printf (const char *format, ...)
> > -{
> > -  va_list ap;
> > -  va_start (ap, format);
> > -  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)
> > -{
> > -  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);
> > -}
> > -
> > -/* Append a gimple statement to this optinfo, equivalent to
> > -   print_gimple_stmt.  */
> > -
> > -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);
> > -  m_items.safe_push (item);
> > -}
> > -
> > -/* Append a gimple statement to this optinfo, equivalent to
> > -   print_gimple_expr.  */
> > -
> > -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);
> > -  m_items.safe_push (item);
> > -}
> > -
> > -/* Append a tree node to this optinfo, equivalent to
> > print_generic_expr.  */
> > -
> > -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);
> > -  m_items.safe_push (item);
> > -}
> > -
> > -/* Append a symbol table node to this optinfo.  */
> > -
> > -void
> > -optinfo::add_symtab_node (symtab_node *node)
> > -{
> > -  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> > -  optinfo_item *item
> > -    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> > -                       xstrdup (node->dump_name ()), true);
> > -  m_items.safe_push (item);
> > -}
> > -
> > -/* Append the decimal represenation of a wide_int_ref to this
> > -   optinfo.  */
> > -
> > -void
> > -optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> > -{
> > -  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> > -  print_dec (wi, buf, sgn);
> > -  optinfo_item *item
> > -    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > -                       xstrdup (buf), true);
> > -  m_items.safe_push (item);
> > -}
> > -
> >  /* Should optinfo instances be created?
> >     All creation of optinfos should be guarded by this predicate.
> >     Return true if any optinfo destinations are active.  */
> > diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> > index c4cf8ad..8ac961c 100644
> > --- a/gcc/optinfo.h
> > +++ b/gcc/optinfo.h
> > @@ -92,6 +92,8 @@ enum optinfo_kind
> > 
> >  extern const char *optinfo_kind_to_string (enum optinfo_kind
> > kind);
> > 
> > +class dump_context;
> > +
> >  /* A bundle of information describing part of an optimization.  */
> > 
> >  class optinfo
> > @@ -120,41 +122,14 @@ 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 add_item (optinfo_item *item);
> > +
> >   private:
> >    void emit ();
> > 
> >    /* Pre-canned ways of manipulating the optinfo, for use by
> > friend class
> >       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_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);
> > -  void add_symtab_node (symtab_node *node);
> > -  void add_dec (const wide_int_ref &wi, signop sgn);
> > -
> > -  template<unsigned int N, typename C>
> > -  void add_poly_int (const poly_int<N, C> &value)
> > -  {
> > -    /* Compare with dump_dec (MSG_NOTE, ).  */
> > -
> > -    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> > -    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > UNSIGNED;
> > -
> > -    if (value.is_constant ())
> > -      add_dec (value.coeffs[0], sgn);
> > -    else
> > -      {
> > -       add_string ("[");
> > -       for (unsigned int i = 0; i < N; ++i)
> > -         {
> > -           add_dec (value.coeffs[i], sgn);
> > -           add_string (i == N - 1 ? "]" : ",");
> > -         }
> > -      }
> > -  }
> > 
> >   private:
> >    dump_location_t m_loc;
> > @@ -179,7 +154,7 @@ class optinfo_item
> >  {
> >   public:
> >    optinfo_item (enum optinfo_item_kind kind, location_t location,
> > -               char *text, bool owned);
> > +               char *text);
> >    ~optinfo_item ();
> > 
> >    enum optinfo_item_kind get_kind () const { return m_kind; }
> > @@ -191,9 +166,8 @@ class optinfo_item
> >    enum optinfo_item_kind m_kind;
> >    location_t m_location;
> > 
> > -  /* The textual form of the item.  */
> > +  /* The textual form of the item, owned by the item.  */
> >    char *m_text;
> > -  bool m_owned;
> >  };
> > 
> >  #endif /* #ifndef GCC_OPTINFO_H */
> > --
> > 1.8.5.3
> > 

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file
  2018-07-31 15:34     ` David Malcolm
@ 2018-07-31 15:37       ` Richard Biener
  0 siblings, 0 replies; 29+ messages in thread
From: Richard Biener @ 2018-07-31 15:37 UTC (permalink / raw)
  To: David Malcolm; +Cc: GCC Patches

On Tue, Jul 31, 2018 at 5:34 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> On Tue, 2018-07-31 at 14:53 +0200, Richard Biener wrote:
> > On Fri, Jul 27, 2018 at 11:48 PM David Malcolm <dmalcolm@redhat.com>
> > wrote:
> > >
> > > With the addition of optinfo, the various dump_* calls had three
> > > parts:
> > > - optionally print to dump_file
> > > - optionally print to alt_dump_file
> > > - optionally make an optinfo_item and add it to the pending
> > > optinfo,
> > >   creating it for dump_*_loc calls.
> > >
> > > However, this split makes it difficult to implement the formatted
> > > dumps
> > > later in patch kit, so as enabling work towards that, this patch
> > > removes
> > > the above split, so that all dumping within the dump_* API goes
> > > through
> > > optinfo_item.
> > >
> > > In order to ensure that the dumps to dump_file and alt_dump_file
> > > are
> > > processed immediately (rather than being buffered within the
> > > pending
> > > optinfo for consolidation), this patch introduces the idea of
> > > "immediate"
> > > optinfo_item destinations vs "non-immediate" destinations.
> > >
> > > The patch also adds selftest coverage of what's printed, and of
> > > scopes.
> > >
> > > This adds two allocations per dump_* call when dumping is enabled.
> > > I'm assuming that this isn't a problem, as dump_enabled_p is
> > > normally
> > > false.  There are ways of optimizing it if it is an issue (by
> > > making
> > > optinfo_item instances become temporaries that borrow the
> > > underlying
> > > buffer), but they require nontrivial changes, so I'd prefer to
> > > leave
> > > that for another patch kit, if it becomes necessary.
> >
> > Yeah, I guess that's OK given we can consolidate quite some calls
> > after
> > your patch anyways.
>
> We can, but FWIW my plan is to only touch the calls that I need to to
> implement the  "Higher-level reporting of vectorization problems" idea
> here:
>    https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00446.html
>
> where the explicit dump calls become implicit within calls to things
> like:
>
>    return opt_result::failure_at (stmt,
>                                   "not vectorized: different sized vector "
>                                   "types in statement, %T and %T\n",
>                                   vectype, nunits_vectype);
>
> But if you think it's worthwhile, I can do a big patch that uses these
> format codes throughout.
>
> > Using alloca + placement new would be possible
> > as well I guess?
>
> Maybe.  I think the underlying question here is "what should the
> lifetimes of the optinfo_items (and their text buffers) be?"
>
> In the initial version of the optinfo patch kit, I had optinfo_items
> being created in response to the various dump_* calls, and them being
> added to an optinfo (which takes ownership of them), before the optinfo
> is eventually emitted to various destinations; the optinfo is then
> deleted, deleting the owned items.
>
> This lifetime approach (having the optinfos own the optinfo_items) was
> necessary because one of the destinations was through the diagnostics
> system; they needed consolidation so that all of the items could be
> alive at the point of emission.  (I think the JSON output also required
> it at one point).
>
> Hence the above approach needs the items and thus their underlying text
> strings to live as long as the optinfo that owns them - the
> destinations assume that the optinfo_items are all alive at the point
> of emission.
>
> Hence this requires new/delete pairs for the items, and also the
> xstrdup around the text buffer, so that the items can own a copy.
>
> But the dump_file and alt_dump_file destinations don't need the items
> to be long-lived: they can be temporary wrappers.
>
> Similarly, the optimization record destination could simply work in
> terms of temporary items: when an optinfo_item is added, the
> corresponding JSON could be added immediately.
>
> So I think the only things that are requiring optinfo_items to be long-
> lived are:
> * the -fremarks idea from an earlier patch kit - and I'm not sure what
> our plans for that should be, in terms of how it should interact with
> alt_dump_file/-fopt-info
> * the selftests within dumpfile.c itself.
>
> So the other approach would be to rewrite dumpfile.c so that
> optinfo_item instances (or maybe "dump_item" instances) are implicitly
> temporary wrappers around a text buffer; the various emit destinations
> make no assumptions that the items will stick around; any that do need
> them to (e.g. for dumpfile.c's selftests) make a copy, perhaps with a
> optinfo_items_need_saving_p () function to guard adding a copy of each
> item into the optinfo.
>
> That would avoid the new/delete pair for all of the optinfo_item
> instances, and the xstrdup for each one, apart from during selftests.
>
> But it's a rewrite of this code (and has interactions with the rest of
> the kit, which is why I didn't do it).
>
> Is this something you'd want me to pursue as a followup?  (it's an
> optimization of the dump_enabled_p branch.  Maybe it might become more
> necessary for people using -fdump-optimization-record on large
> codebases???)

I think unless problems arise it should be a low-priority task.  I'd rather
see the new codes used throughout the codebase to simplify dump stuff.

Richard.

> > OK.
>
> Thanks
> Dave
>
>
> > Richard.
> >
> > > gcc/ChangeLog:
> > >         * dump-context.h: Include "pretty-print.h".
> > >         (dump_context::refresh_dumps_are_enabled): New decl.
> > >         (dump_context::emit_item): New decl.
> > >         (class dump_context): Add fields "m_test_pp" and
> > >         "m_test_pp_flags".
> > >         (temp_dump_context::temp_dump_context): Add param
> > > "test_pp_flags".
> > >         (temp_dump_context::get_dumped_text): New decl.
> > >         (class temp_dump_context): Add field "m_pp".
> > >         * dumpfile.c (refresh_dumps_are_enabled): Convert to...
> > >         (dump_context::refresh_dumps_are_enabled): ...and add a
> > > test for
> > >         m_test_pp.
> > >         (set_dump_file): Update for above change.
> > >         (set_alt_dump_file): Likewise.
> > >         (dump_loc): New overload, taking a pretty_printer *.
> > >         (dump_context::dump_loc): Call end_any_optinfo.  Dump the
> > > location
> > >         to any test pretty-printer.
> > >         (make_item_for_dump_gimple_stmt): New function, adapted
> > > from
> > >         optinfo::add_gimple_stmt.
> > >         (dump_context::dump_gimple_stmt): Call it, and use the
> > > result,
> > >         eliminating the direct usage of dump_file and alt_dump_file
> > > in
> > >         favor of indirectly using them via emit_item.
> > >         (make_item_for_dump_gimple_expr): New function, adapted
> > > from
> > >         optinfo::add_gimple_expr.
> > >         (dump_context::dump_gimple_expr): Call it, and use the
> > > result,
> > >         eliminating the direct usage of dump_file and alt_dump_file
> > > in
> > >         favor of indirectly using them via emit_item.
> > >         (make_item_for_dump_generic_expr): New function, adapted
> > > from
> > >         optinfo::add_tree.
> > >         (dump_context::dump_generic_expr): Call it, and use the
> > > result,
> > >         eliminating the direct usage of dump_file and alt_dump_file
> > > in
> > >         favor of indirectly using them via emit_item.
> > >         (make_item_for_dump_printf_va): New function, adapted from
> > >         optinfo::add_printf_va.
> > >         (make_item_for_dump_printf): New function.
> > >         (dump_context::dump_printf_va): Call
> > > make_item_for_dump_printf_va,
> > >         and use the result, eliminating the direct usage of
> > > dump_file and
> > >         alt_dump_file in favor of indirectly using them via
> > > emit_item.
> > >         (make_item_for_dump_dec): New function.
> > >         (dump_context::dump_dec): Call it, and use the result,
> > >         eliminating the direct usage of dump_file and alt_dump_file
> > > in
> > >         favor of indirectly using them via emit_item.
> > >         (make_item_for_dump_symtab_node): New function, adapted
> > > from
> > >         optinfo::add_symtab_node.
> > >         (dump_context::dump_symtab_node): Call it, and use the
> > > result,
> > >         eliminating the direct usage of dump_file and alt_dump_file
> > > in
> > >         favor of indirectly using them via emit_item.
> > >         (dump_context::begin_scope): Reimplement, avoiding direct
> > > usage
> > >         of dump_file and alt_dump_file in favor of indirectly using
> > > them
> > >         via emit_item.
> > >         (dump_context::emit_item): New member function.
> > >         (temp_dump_context::temp_dump_context): Add param
> > > "test_pp_flags".
> > >         Set up test pretty-printer on the underlying context.  Call
> > >         refresh_dumps_are_enabled.
> > >         (temp_dump_context::~temp_dump_context): Call
> > >         refresh_dumps_are_enabled.
> > >         (temp_dump_context::get_dumped_text): New member function.
> > >         (selftest::verify_dumped_text): New function.
> > >         (ASSERT_DUMPED_TEXT_EQ): New macro.
> > >         (selftest::test_capture_of_dump_calls): Run all tests
> > > twice, with
> > >         and then without optinfo enabled.  Add uses of
> > >         ASSERT_DUMPED_TEXT_EQ to all tests.  Add test of nested
> > > scopes.
> > >         * dumpfile.h: Update comment for the dump_* API.
> > >         * optinfo-emit-json.cc
> > >         (selftest::test_building_json_from_dump_calls): Update for
> > > new
> > >         param for temp_dump_context ctor.
> > >         * optinfo.cc (optinfo_item::optinfo_item): Remove "owned"
> > > param
> > >         and "m_owned" field.
> > >         (optinfo_item::~optinfo_item): Likewise.
> > >         (optinfo::add_item): New member function.
> > >         (optinfo::emit): Update comment.
> > >         (optinfo::add_string): Delete.
> > >         (optinfo::add_printf): Delete.
> > >         (optinfo::add_printf_va): Delete.
> > >         (optinfo::add_gimple_stmt): Delete.
> > >         (optinfo::add_gimple_expr): Delete.
> > >         (optinfo::add_tree): Delete.
> > >         (optinfo::add_symtab_node): Delete.
> > >         (optinfo::add_dec): Delete.
> > >         * optinfo.h (class dump_context): New forward decl.
> > >         (optinfo::add_item): New decl.
> > >         (optinfo::add_string): Delete.
> > >         (optinfo::add_printf): Delete.
> > >         (optinfo::add_printf_va): Delete.
> > >         (optinfo::add_gimple_stmt): Delete.
> > >         (optinfo::add_gimple_expr): Delete.
> > >         (optinfo::add_tree): Delete.
> > >         (optinfo::add_symtab_node): Delete.
> > >         (optinfo::add_dec): Delete.
> > >         (optinfo::add_poly_int): Delete.
> > >         (optinfo_item::optinfo_item): Remove "owned" param.
> > >         (class optinfo_item): Remove field "m_owned".
> > > ---
> > >  gcc/dump-context.h       |  16 +-
> > >  gcc/dumpfile.c           | 620 ++++++++++++++++++++++++++++++++++-
> > > ------------
> > >  gcc/dumpfile.h           |  34 ++-
> > >  gcc/optinfo-emit-json.cc |   2 +-
> > >  gcc/optinfo.cc           | 135 ++---------
> > >  gcc/optinfo.h            |  38 +--
> > >  6 files changed, 505 insertions(+), 340 deletions(-)
> > >
> > > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > > index f6df0b4..f40ea14 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 "pretty-print.h"
> > > +
> > >  /* A class for handling the various dump_* calls.
> > >
> > >     In particular, this class has responsibility for consolidating
> > > @@ -39,6 +41,8 @@ class dump_context
> > >
> > >    ~dump_context ();
> > >
> > > +  void refresh_dumps_are_enabled ();
> > > +
> > >    void dump_loc (dump_flags_t dump_kind, const dump_location_t
> > > &loc);
> > >
> > >    void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> > > extra_dump_flags,
> > > @@ -93,6 +97,8 @@ class dump_context
> > >
> > >    void end_any_optinfo ();
> > >
> > > +  void emit_item (optinfo_item *item, dump_flags_t dump_kind);
> > > +
> > >   private:
> > >    optinfo &ensure_pending_optinfo ();
> > >    optinfo &begin_next_optinfo (const dump_location_t &loc);
> > > @@ -108,6 +114,11 @@ class dump_context
> > >       if any.  */
> > >    optinfo *m_pending;
> > >
> > > +  /* For use in selftests: if non-NULL, then items are to be
> > > printed
> > > +     to this, using the given flags.  */
> > > +  pretty_printer *m_test_pp;
> > > +  dump_flags_t m_test_pp_flags;
> > > +
> > >    /* The currently active dump_context, for use by the dump_* API
> > > calls.  */
> > >    static dump_context *s_current;
> > >
> > > @@ -123,13 +134,16 @@ class dump_context
> > >  class temp_dump_context
> > >  {
> > >   public:
> > > -  temp_dump_context (bool forcibly_enable_optinfo);
> > > +  temp_dump_context (bool forcibly_enable_optinfo,
> > > +                    dump_flags_t test_pp_flags);
> > >    ~temp_dump_context ();
> > >
> > >    /* Support for selftests.  */
> > >    optinfo *get_pending_optinfo () const { return
> > > m_context.m_pending; }
> > > +  const char *get_dumped_text ();
> > >
> > >   private:
> > > +  pretty_printer m_pp;
> > >    dump_context m_context;
> > >    dump_context *m_saved;
> > >    bool m_saved_flag_remarks;
> > > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > > index 3c8bc38..10e9cab 100644
> > > --- a/gcc/dumpfile.c
> > > +++ b/gcc/dumpfile.c
> > > @@ -63,15 +63,6 @@ dump_flags_t dump_flags;
> > >  bool dumps_are_enabled = false;
> > >
> > >
> > > -/* Update the "dumps_are_enabled" global; to be called whenever
> > > dump_file
> > > -   or alt_dump_file change.  */
> > > -
> > > -static void
> > > -refresh_dumps_are_enabled ()
> > > -{
> > > -  dumps_are_enabled = (dump_file || alt_dump_file ||
> > > optinfo_enabled_p ());
> > > -}
> > > -
> > >  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the
> > > "dumps_are_enabled"
> > >     global.  */
> > >
> > > @@ -80,7 +71,7 @@ set_dump_file (FILE *new_dump_file)
> > >  {
> > >    dumpfile_ensure_any_optinfo_are_flushed ();
> > >    dump_file = new_dump_file;
> > > -  refresh_dumps_are_enabled ();
> > > +  dump_context::get ().refresh_dumps_are_enabled ();
> > >  }
> > >
> > >  /* Set "alt_dump_file" to NEW_ALT_DUMP_FILE, refreshing the
> > > "dumps_are_enabled"
> > > @@ -91,7 +82,7 @@ set_alt_dump_file (FILE *new_alt_dump_file)
> > >  {
> > >    dumpfile_ensure_any_optinfo_are_flushed ();
> > >    alt_dump_file = new_alt_dump_file;
> > > -  refresh_dumps_are_enabled ();
> > > +  dump_context::get ().refresh_dumps_are_enabled ();
> > >  }
> > >
> > >  #define DUMP_FILE_INFO(suffix, swtch, dkind, num) \
> > > @@ -465,6 +456,27 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile,
> > > source_location loc)
> > >      }
> > >  }
> > >
> > > +/* Print source location to PP if enabled.  */
> > > +
> > > +static void
> > > +dump_loc (dump_flags_t dump_kind, pretty_printer *pp,
> > > source_location loc)
> > > +{
> > > +  if (dump_kind)
> > > +    {
> > > +      if (LOCATION_LOCUS (loc) > BUILTINS_LOCATION)
> > > +       pp_printf (pp, "%s:%d:%d: note: ", LOCATION_FILE (loc),
> > > +                  LOCATION_LINE (loc), LOCATION_COLUMN (loc));
> > > +      else if (current_function_decl)
> > > +       pp_printf (pp, "%s:%d:%d: note: ",
> > > +                  DECL_SOURCE_FILE (current_function_decl),
> > > +                  DECL_SOURCE_LINE (current_function_decl),
> > > +                  DECL_SOURCE_COLUMN (current_function_decl));
> > > +      /* Indentation based on scope depth.  */
> > > +      for (unsigned i = 0; i < get_dump_scope_depth (); i++)
> > > +       pp_character (pp, ' ');
> > > +    }
> > > +}
> > > +
> > >  /* Implementation of dump_context member functions.  */
> > >
> > >  /* dump_context's dtor.  */
> > > @@ -474,12 +486,24 @@ dump_context::~dump_context ()
> > >    delete m_pending;
> > >  }
> > >
> > > +/* Update the "dumps_are_enabled" global; to be called whenever
> > > dump_file
> > > +   or alt_dump_file change, or when changing dump_context in
> > > selftests.  */
> > > +
> > > +void
> > > +dump_context::refresh_dumps_are_enabled ()
> > > +{
> > > +  dumps_are_enabled = (dump_file || alt_dump_file ||
> > > optinfo_enabled_p ()
> > > +                      || m_test_pp);
> > > +}
> > > +
> > >  /* Print LOC to the appropriate dump destinations, given
> > > DUMP_KIND.
> > >     If optinfos are enabled, begin a new optinfo.  */
> > >
> > >  void
> > >  dump_context::dump_loc (dump_flags_t dump_kind, const
> > > dump_location_t &loc)
> > >  {
> > > +  end_any_optinfo ();
> > > +
> > >    location_t srcloc = loc.get_location_t ();
> > >
> > >    if (dump_file && (dump_kind & pflags))
> > > @@ -488,6 +512,10 @@ dump_context::dump_loc (dump_flags_t
> > > dump_kind, const dump_location_t &loc)
> > >    if (alt_dump_file && (dump_kind & alt_flags))
> > >      ::dump_loc (dump_kind, alt_dump_file, srcloc);
> > >
> > > +  /* Support for temp_dump_context in selftests.  */
> > > +  if (m_test_pp && (dump_kind & m_test_pp_flags))
> > > +    ::dump_loc (dump_kind, m_test_pp, srcloc);
> > > +
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = begin_next_optinfo (loc);
> > > @@ -495,6 +523,22 @@ dump_context::dump_loc (dump_flags_t
> > > dump_kind, const dump_location_t &loc)
> > >      }
> > >  }
> > >
> > > +/* Make an item for the given dump call, equivalent to
> > > print_gimple_stmt.  */
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_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)));
> > > +  return item;
> > > +}
> > > +
> > >  /* Dump gimple statement GS with SPC indentation spaces and
> > >     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is
> > > enabled.  */
> > >
> > > @@ -503,18 +547,18 @@ dump_context::dump_gimple_stmt (dump_flags_t
> > > dump_kind,
> > >                                 dump_flags_t extra_dump_flags,
> > >                                 gimple *gs, int spc)
> > >  {
> > > -  if (dump_file && (dump_kind & pflags))
> > > -    print_gimple_stmt (dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > > -
> > > -  if (alt_dump_file && (dump_kind & alt_flags))
> > > -    print_gimple_stmt (alt_dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +  optinfo_item *item
> > > +    = make_item_for_dump_gimple_stmt (gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +  emit_item (item, dump_kind);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > >        info.handle_dump_file_kind (dump_kind);
> > > -      info.add_gimple_stmt (gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +      info.add_item (item);
> > >      }
> > > +  else
> > > +    delete item;
> > >  }
> > >
> > >  /* Similar to dump_gimple_stmt, except additionally print source
> > > location.  */
> > > @@ -529,6 +573,22 @@ dump_context::dump_gimple_stmt_loc
> > > (dump_flags_t dump_kind,
> > >    dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
> > >  }
> > >
> > > +/* Make an item for the given dump call, equivalent to
> > > print_gimple_expr.  */
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_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)));
> > > +  return item;
> > > +}
> > > +
> > >  /* Dump gimple statement GS with SPC indentation spaces and
> > >     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.
> > >     Do not terminate with a newline or semicolon.  */
> > > @@ -538,18 +598,18 @@ dump_context::dump_gimple_expr (dump_flags_t
> > > dump_kind,
> > >                                 dump_flags_t extra_dump_flags,
> > >                                 gimple *gs, int spc)
> > >  {
> > > -  if (dump_file && (dump_kind & pflags))
> > > -    print_gimple_expr (dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > > -
> > > -  if (alt_dump_file && (dump_kind & alt_flags))
> > > -    print_gimple_expr (alt_dump_file, gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +  optinfo_item *item
> > > +    = make_item_for_dump_gimple_expr (gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +  emit_item (item, dump_kind);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > >        info.handle_dump_file_kind (dump_kind);
> > > -      info.add_gimple_expr (gs, spc, dump_flags |
> > > extra_dump_flags);
> > > +      info.add_item (item);
> > >      }
> > > +  else
> > > +    delete item;
> > >  }
> > >
> > >  /* Similar to dump_gimple_expr, except additionally print source
> > > location.  */
> > > @@ -565,6 +625,25 @@ dump_context::dump_gimple_expr_loc
> > > (dump_flags_t dump_kind,
> > >    dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
> > >  }
> > >
> > > +/* Make an item for the given dump call, equivalent to
> > > print_generic_expr.  */
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_generic_expr (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)));
> > > +  return item;
> > > +}
> > >
> > >  /* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams
> > > if
> > >     DUMP_KIND is enabled.  */
> > > @@ -574,18 +653,18 @@ dump_context::dump_generic_expr (dump_flags_t
> > > dump_kind,
> > >                                  dump_flags_t extra_dump_flags,
> > >                                  tree t)
> > >  {
> > > -  if (dump_file && (dump_kind & pflags))
> > > -      print_generic_expr (dump_file, t, dump_flags |
> > > extra_dump_flags);
> > > -
> > > -  if (alt_dump_file && (dump_kind & alt_flags))
> > > -      print_generic_expr (alt_dump_file, t, dump_flags |
> > > extra_dump_flags);
> > > +  optinfo_item *item
> > > +    = make_item_for_dump_generic_expr (t, dump_flags |
> > > extra_dump_flags);
> > > +  emit_item (item, dump_kind);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > >        info.handle_dump_file_kind (dump_kind);
> > > -      info.add_tree (t, dump_flags | extra_dump_flags);
> > > +      info.add_item (item);
> > >      }
> > > +  else
> > > +    delete item;
> > >  }
> > >
> > >
> > > @@ -602,36 +681,56 @@ dump_context::dump_generic_expr_loc
> > > (dump_flags_t dump_kind,
> > >    dump_generic_expr (dump_kind, extra_dump_flags, t);
> > >  }
> > >
> > > +/* Make an item for the given dump call.  */
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_printf_va (const char *format, va_list ap)
> > > +  ATTRIBUTE_PRINTF (1, 0);
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_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);
> > > +  return item;
> > > +}
> > > +
> > > +/* Make an item for the given dump call.  */
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_printf (const char *format, ...)
> > > +  ATTRIBUTE_PRINTF (1, 2);
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_printf (const char *format, ...)
> > > +{
> > > +  va_list ap;
> > > +  va_start (ap, format);
> > > +  optinfo_item *item
> > > +    = make_item_for_dump_printf_va (format, ap);
> > > +  va_end (ap);
> > > +  return item;
> > > +}
> > > +
> > >  /* Output a formatted message using FORMAT on appropriate dump
> > > streams.  */
> > >
> > >  void
> > >  dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> > > *format,
> > >                               va_list ap)
> > >  {
> > > -  if (dump_file && (dump_kind & pflags))
> > > -    {
> > > -      va_list aq;
> > > -      va_copy (aq, ap);
> > > -      vfprintf (dump_file, format, aq);
> > > -      va_end (aq);
> > > -    }
> > > -
> > > -  if (alt_dump_file && (dump_kind & alt_flags))
> > > -    {
> > > -      va_list aq;
> > > -      va_copy (aq, ap);
> > > -      vfprintf (alt_dump_file, format, aq);
> > > -      va_end (aq);
> > > -    }
> > > +  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
> > > +  emit_item (item, dump_kind);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > > -      va_list aq;
> > > -      va_copy (aq, ap);
> > > -      info.add_printf_va (format, aq);
> > > -      va_end (aq);
> > > +      info.handle_dump_file_kind (dump_kind);
> > > +      info.add_item (item);
> > >      }
> > > +  else
> > > +    delete item;
> > >  }
> > >
> > >  /* Similar to dump_printf, except source location is also printed,
> > > and
> > > @@ -646,26 +745,64 @@ dump_context::dump_printf_loc_va
> > > (dump_flags_t dump_kind,
> > >    dump_printf_va (dump_kind, format, ap);
> > >  }
> > >
> > > -/* Output VALUE in decimal to appropriate dump streams.  */
> > > +/* Make an item for the given dump call, equivalent to
> > > print_dec.  */
> > >
> > >  template<unsigned int N, typename C>
> > > -void
> > > -dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N,
> > > C> &value)
> > > +static optinfo_item *
> > > +make_item_for_dump_dec (const poly_int<N, C> &value)
> > >  {
> > >    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> > >    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > > UNSIGNED;
> > > -  if (dump_file && (dump_kind & pflags))
> > > -    print_dec (value, dump_file, sgn);
> > >
> > > -  if (alt_dump_file && (dump_kind & alt_flags))
> > > -    print_dec (value, alt_dump_file, sgn);
> > > +  pretty_printer pp;
> > > +
> > > +  if (value.is_constant ())
> > > +    pp_wide_int (&pp, value.coeffs[0], sgn);
> > > +  else
> > > +    {
> > > +      pp_character (&pp, '[');
> > > +      for (unsigned int i = 0; i < N; ++i)
> > > +       {
> > > +         pp_wide_int (&pp, value.coeffs[i], sgn);
> > > +         pp_character (&pp, i == N - 1 ? ']' : ',');
> > > +       }
> > > +    }
> > > +
> > > +  optinfo_item *item
> > > +    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > > +                       xstrdup (pp_formatted_text (&pp)));
> > > +  return item;
> > > +}
> > > +
> > > +/* Output VALUE in decimal to appropriate dump streams.  */
> > > +
> > > +template<unsigned int N, typename C>
> > > +void
> > > +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N,
> > > C> &value)
> > > +{
> > > +  optinfo_item *item = make_item_for_dump_dec (value);
> > > +  emit_item (item, dump_kind);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > >        info.handle_dump_file_kind (dump_kind);
> > > -      info.add_poly_int<N,C> (value);
> > > +      info.add_item (item);
> > >      }
> > > +  else
> > > +    delete item;
> > > +}
> > > +
> > > +/* Make an item for the given dump call.  */
> > > +
> > > +static optinfo_item *
> > > +make_item_for_dump_symtab_node (symtab_node *node)
> > > +{
> > > +  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> > > +  optinfo_item *item
> > > +    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> > > +                       xstrdup (node->dump_name ()));
> > > +  return item;
> > >  }
> > >
> > >  /* Output the name of NODE on appropriate dump streams.  */
> > > @@ -673,18 +810,17 @@ dump_context::dump_dec (dump_flags_t
> > > dump_kind, const poly_int<N, C> &value)
> > >  void
> > >  dump_context::dump_symtab_node (dump_flags_t dump_kind,
> > > symtab_node *node)
> > >  {
> > > -  if (dump_file && (dump_kind & pflags))
> > > -    fprintf (dump_file, "%s", node->dump_name ());
> > > -
> > > -  if (alt_dump_file && (dump_kind & alt_flags))
> > > -    fprintf (alt_dump_file, "%s", node->dump_name ());
> > > +  optinfo_item *item = make_item_for_dump_symtab_node (node);
> > > +  emit_item (item, dump_kind);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > >        optinfo &info = ensure_pending_optinfo ();
> > >        info.handle_dump_file_kind (dump_kind);
> > > -      info.add_symtab_node (node);
> > > +      info.add_item (item);
> > >      }
> > > +  else
> > > +    delete item;
> > >  }
> > >
> > >  /* Get the current dump scope-nesting depth.
> > > @@ -705,28 +841,28 @@ dump_context::get_scope_depth () const
> > >  void
> > >  dump_context::begin_scope (const char *name, const dump_location_t
> > > &loc)
> > >  {
> > > -  /* Specialcase, to avoid going through dump_printf_loc,
> > > -     so that we can create a optinfo of kind
> > > OPTINFO_KIND_SCOPE.  */
> > > -
> > >    if (dump_file)
> > > -    {
> > > -      ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> > > -      fprintf (dump_file, "=== %s ===\n", name);
> > > -    }
> > > +    ::dump_loc (MSG_NOTE, dump_file, loc.get_location_t ());
> > >
> > >    if (alt_dump_file)
> > > -    {
> > > -      ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> > > -      fprintf (alt_dump_file, "=== %s ===\n", name);
> > > -    }
> > > +    ::dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> > > +
> > > +  /* Support for temp_dump_context in selftests.  */
> > > +  if (m_test_pp)
> > > +    ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
> > > +
> > > +  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n",
> > > name);
> > > +  emit_item (item, MSG_NOTE);
> > >
> > >    if (optinfo_enabled_p ())
> > >      {
> > > +      optinfo &info = begin_next_optinfo (loc);
> > > +      info.m_kind = OPTINFO_KIND_SCOPE;
> > > +      info.add_item (item);
> > >        end_any_optinfo ();
> > > -      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> > > -      info.add_printf ("=== %s ===", name);
> > > -      info.emit ();
> > >      }
> > > +  else
> > > +    delete item;
> > >
> > >    m_scope_depth++;
> > >  }
> > > @@ -776,6 +912,23 @@ dump_context::end_any_optinfo ()
> > >    m_pending = NULL;
> > >  }
> > >
> > > +/* Emit ITEM to all item destinations (those that don't require
> > > +   consolidation into optinfo instances).  */
> > > +
> > > +void
> > > +dump_context::emit_item (optinfo_item *item, dump_flags_t
> > > dump_kind)
> > > +{
> > > +  if (dump_file && (dump_kind & pflags))
> > > +    fprintf (dump_file, "%s", item->get_text ());
> > > +
> > > +  if (alt_dump_file && (dump_kind & alt_flags))
> > > +    fprintf (alt_dump_file, "%s", item->get_text ());
> > > +
> > > +  /* Support for temp_dump_context in selftests.  */
> > > +  if (m_test_pp && (dump_kind & m_test_pp_flags))
> > > +    pp_string (m_test_pp, item->get_text ());
> > > +}
> > > +
> > >  /* The current singleton dump_context, and its default.  */
> > >
> > >  dump_context *dump_context::s_current = &dump_context::s_default;
> > > @@ -1510,12 +1663,18 @@ enable_rtl_dump_file (void)
> > >  /* temp_dump_context's ctor.  Temporarily override the
> > > dump_context
> > >     (to forcibly enable optinfo-generation).  */
> > >
> > > -temp_dump_context::temp_dump_context (bool
> > > forcibly_enable_optinfo)
> > > +temp_dump_context::temp_dump_context (bool
> > > forcibly_enable_optinfo,
> > > +                                     dump_flags_t test_pp_flags)
> > > +
> > >  : m_context (),
> > >    m_saved (&dump_context ().get ())
> > >  {
> > >    dump_context::s_current = &m_context;
> > >    m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> > > +  m_context.m_test_pp = &m_pp;
> > > +  m_context.m_test_pp_flags = test_pp_flags;
> > > +
> > > +  dump_context::get ().refresh_dumps_are_enabled ();
> > >  }
> > >
> > >  /* temp_dump_context's dtor.  Restore the saved dump_context.  */
> > > @@ -1523,6 +1682,16 @@ temp_dump_context::temp_dump_context (bool
> > > forcibly_enable_optinfo)
> > >  temp_dump_context::~temp_dump_context ()
> > >  {
> > >    dump_context::s_current = m_saved;
> > > +
> > > +  dump_context::get ().refresh_dumps_are_enabled ();
> > > +}
> > > +
> > > +/* 0-terminate the text dumped so far, and return it.  */
> > > +
> > > +const char *
> > > +temp_dump_context::get_dumped_text ()
> > > +{
> > > +  return pp_formatted_text (&m_pp);
> > >  }
> > >
> > >  namespace selftest {
> > > @@ -1561,6 +1730,29 @@ test_impl_location ()
> > >  #endif
> > >  }
> > >
> > > +/* Verify that the text dumped so far in CONTEXT equals
> > > +   EXPECTED_TEXT, using LOC for the location of any failure.
> > > +   As a side-effect, the internal buffer is 0-terminated.  */
> > > +
> > > +static void
> > > +verify_dumped_text (const location &loc,
> > > +                   temp_dump_context *context,
> > > +                   const char *expected_text)
> > > +{
> > > +  gcc_assert (context);
> > > +  ASSERT_STREQ_AT (loc, context->get_dumped_text (),
> > > +                  expected_text);
> > > +}
> > > +
> > > +/* Verify that the text dumped so far in CONTEXT equals
> > > +   EXPECTED_TEXT.
> > > +   As a side-effect, the internal buffer is 0-terminated.  */
> > > +
> > > +#define ASSERT_DUMPED_TEXT_EQ(CONTEXT,
> > > EXPECTED_TEXT)                  \
> > > +  SELFTEST_BEGIN_STMT
> > >      \
> > > +    verify_dumped_text (SELFTEST_LOCATION, &(CONTEXT),
> > > (EXPECTED_TEXT)); \
> > > +  SELFTEST_END_STMT
> > > +
> > >  /* Verify that ITEM has the expected values.  */
> > >
> > >  static void
> > > @@ -1611,116 +1803,198 @@ test_capture_of_dump_calls (const
> > > line_table_case &case_)
> > >    linemap_line_start (line_table, 5, 100);
> > >    linemap_add (line_table, LC_LEAVE, false, NULL, 0);
> > >    location_t where = linemap_position_for_column (line_table, 10);
> > > +  if (where > LINE_MAP_MAX_LOCATION_WITH_COLS)
> > > +    return;
> > >
> > >    dump_location_t loc = dump_location_t::from_location_t (where);
> > >
> > > -  /* Test of dump_printf.  */
> > > -  {
> > > -    temp_dump_context tmp (true);
> > > -    dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> > > -
> > > -    optinfo *info = tmp.get_pending_optinfo ();
> > > -    ASSERT_TRUE (info != NULL);
> > > -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > -    ASSERT_EQ (info->num_items (), 1);
> > > -    ASSERT_IS_TEXT (info->get_item (0), "int: 42 str: foo");
> > > -  }
> > > -
> > > -  /* Tree, via dump_generic_expr.  */
> > > -  {
> > > -    temp_dump_context tmp (true);
> > > -    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > > -    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > > -
> > > -    optinfo *info = tmp.get_pending_optinfo ();
> > > -    ASSERT_TRUE (info != NULL);
> > > -    ASSERT_EQ (info->get_location_t (), where);
> > > -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > -    ASSERT_EQ (info->num_items (), 2);
> > > -    ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> > > -    ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION, "0");
> > > -  }
> > > -
> > > -  /* Tree, via dump_generic_expr_loc.  */
> > > -  {
> > > -    temp_dump_context tmp (true);
> > > -    dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> > > integer_one_node);
> > > -
> > > -    optinfo *info = tmp.get_pending_optinfo ();
> > > -    ASSERT_TRUE (info != NULL);
> > > -    ASSERT_EQ (info->get_location_t (), where);
> > > -    ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > -    ASSERT_EQ (info->num_items (), 1);
> > > -    ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION, "1");
> > > -  }
> > > -
> > > -  /* Gimple.  */
> > > -  {
> > > -    greturn *stmt = gimple_build_return (NULL);
> > > -    gimple_set_location (stmt, where);
> > > -
> > > -    /* dump_gimple_stmt_loc.  */
> > > -    {
> > > -      temp_dump_context tmp (true);
> > > -      dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > > -
> > > -      optinfo *info = tmp.get_pending_optinfo ();
> > > -      ASSERT_TRUE (info != NULL);
> > > -      ASSERT_EQ (info->num_items (), 1);
> > > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> > > -    }
> > > -
> > > -    /* dump_gimple_stmt.  */
> > > -    {
> > > -      temp_dump_context tmp (true);
> > > -      dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> > > +  greturn *stmt = gimple_build_return (NULL);
> > > +  gimple_set_location (stmt, where);
> > >
> > > -      optinfo *info = tmp.get_pending_optinfo ();
> > > -      ASSERT_TRUE (info != NULL);
> > > -      ASSERT_EQ (info->num_items (), 1);
> > > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;\n");
> > > -    }
> > > -
> > > -    /* dump_gimple_expr_loc.  */
> > > +  /* Run all tests twice, with and then without optinfo enabled,
> > > to ensure
> > > +     that immediate destinations vs optinfo-based destinations
> > > both
> > > +     work, independently of each other, with no leaks.  */
> > > +  for (int i = 0 ; i < 2; i++)
> > >      {
> > > -      temp_dump_context tmp (true);
> > > -      dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > > +      bool with_optinfo = (i == 0);
> > > +
> > > +      /* Test of dump_printf.  */
> > > +      {
> > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +       dump_printf (MSG_NOTE, "int: %i str: %s", 42, "foo");
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "int: 42 str: foo");
> > > +       if (with_optinfo)
> > > +         {
> > > +           optinfo *info = tmp.get_pending_optinfo ();
> > > +           ASSERT_TRUE (info != NULL);
> > > +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > +           ASSERT_EQ (info->num_items (), 1);
> > > +           ASSERT_IS_TEXT (info->get_item (0), "int: 42 str:
> > > foo");
> > > +         }
> > > +      }
> > > +
> > > +      /* Tree, via dump_generic_expr.  */
> > > +      {
> > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +       dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > > +       dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: test of
> > > tree: 0");
> > > +       if (with_optinfo)
> > > +         {
> > > +           optinfo *info = tmp.get_pending_optinfo ();
> > > +           ASSERT_TRUE (info != NULL);
> > > +           ASSERT_EQ (info->get_location_t (), where);
> > > +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > +           ASSERT_EQ (info->num_items (), 2);
> > > +           ASSERT_IS_TEXT (info->get_item (0), "test of tree: ");
> > > +           ASSERT_IS_TREE (info->get_item (1), UNKNOWN_LOCATION,
> > > "0");
> > > +         }
> > > +      }
> > > +
> > > +      /* Tree, via dump_generic_expr_loc.  */
> > > +      {
> > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +       dump_generic_expr_loc (MSG_NOTE, loc, TDF_SLIM,
> > > integer_one_node);
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note: 1");
> > > +       if (with_optinfo)
> > > +         {
> > > +           optinfo *info = tmp.get_pending_optinfo ();
> > > +           ASSERT_TRUE (info != NULL);
> > > +           ASSERT_EQ (info->get_location_t (), where);
> > > +           ASSERT_EQ (info->get_kind (), OPTINFO_KIND_NOTE);
> > > +           ASSERT_EQ (info->num_items (), 1);
> > > +           ASSERT_IS_TREE (info->get_item (0), UNKNOWN_LOCATION,
> > > "1");
> > > +         }
> > > +      }
> > > +
> > > +      /* Gimple.  */
> > > +      {
> > > +       /* dump_gimple_stmt_loc.  */
> > > +       {
> > > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +         dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > > +
> > > +         ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note:
> > > return;\n");
> > > +         if (with_optinfo)
> > > +           {
> > > +             optinfo *info = tmp.get_pending_optinfo ();
> > > +             ASSERT_TRUE (info != NULL);
> > > +             ASSERT_EQ (info->num_items (), 1);
> > > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > > "return;\n");
> > > +           }
> > > +       }
> > >
> > > -      optinfo *info = tmp.get_pending_optinfo ();
> > > -      ASSERT_TRUE (info != NULL);
> > > -      ASSERT_EQ (info->num_items (), 1);
> > > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> > > -    }
> > > +       /* dump_gimple_stmt.  */
> > > +       {
> > > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +         dump_gimple_stmt (MSG_NOTE, TDF_SLIM, stmt, 2);
> > > +
> > > +         ASSERT_DUMPED_TEXT_EQ (tmp, "return;\n");
> > > +         if (with_optinfo)
> > > +           {
> > > +             optinfo *info = tmp.get_pending_optinfo ();
> > > +             ASSERT_TRUE (info != NULL);
> > > +             ASSERT_EQ (info->num_items (), 1);
> > > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > > "return;\n");
> > > +           }
> > > +       }
> > >
> > > -    /* dump_gimple_expr.  */
> > > -    {
> > > -      temp_dump_context tmp (true);
> > > -      dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> > > +       /* dump_gimple_expr_loc.  */
> > > +       {
> > > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +         dump_gimple_expr_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 2);
> > > +
> > > +         ASSERT_DUMPED_TEXT_EQ (tmp, "test.txt:5:10: note:
> > > return;");
> > > +         if (with_optinfo)
> > > +           {
> > > +             optinfo *info = tmp.get_pending_optinfo ();
> > > +             ASSERT_TRUE (info != NULL);
> > > +             ASSERT_EQ (info->num_items (), 1);
> > > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > > "return;");
> > > +           }
> > > +       }
> > >
> > > -      optinfo *info = tmp.get_pending_optinfo ();
> > > -      ASSERT_TRUE (info != NULL);
> > > -      ASSERT_EQ (info->num_items (), 1);
> > > -      ASSERT_IS_GIMPLE (info->get_item (0), where, "return;");
> > > +       /* dump_gimple_expr.  */
> > > +       {
> > > +         temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +         dump_gimple_expr (MSG_NOTE, TDF_SLIM, stmt, 2);
> > > +
> > > +         ASSERT_DUMPED_TEXT_EQ (tmp, "return;");
> > > +         if (with_optinfo)
> > > +           {
> > > +             optinfo *info = tmp.get_pending_optinfo ();
> > > +             ASSERT_TRUE (info != NULL);
> > > +             ASSERT_EQ (info->num_items (), 1);
> > > +             ASSERT_IS_GIMPLE (info->get_item (0), where,
> > > "return;");
> > > +           }
> > > +       }
> > > +      }
> > > +
> > > +      /* poly_int.  */
> > > +      {
> > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +       dump_dec (MSG_NOTE, poly_int64 (42));
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp, "42");
> > > +       if (with_optinfo)
> > > +         {
> > > +           optinfo *info = tmp.get_pending_optinfo ();
> > > +           ASSERT_TRUE (info != NULL);
> > > +           ASSERT_EQ (info->num_items (), 1);
> > > +           ASSERT_IS_TEXT (info->get_item (0), "42");
> > > +         }
> > > +      }
> > > +
> > > +      /* scopes.  */
> > > +      {
> > > +       temp_dump_context tmp (with_optinfo, MSG_ALL);
> > > +       dump_printf_loc (MSG_NOTE, stmt, "msg 1\n");
> > > +       {
> > > +         AUTO_DUMP_SCOPE ("outer scope", stmt);
> > > +         dump_printf_loc (MSG_NOTE, stmt, "msg 2\n");
> > > +         {
> > > +           AUTO_DUMP_SCOPE ("middle scope", stmt);
> > > +           dump_printf_loc (MSG_NOTE, stmt, "msg 3\n");
> > > +           {
> > > +             AUTO_DUMP_SCOPE ("inner scope", stmt);
> > > +             dump_printf_loc (MSG_NOTE, stmt, "msg 4\n");
> > > +           }
> > > +           dump_printf_loc (MSG_NOTE, stmt, "msg 5\n");
> > > +         }
> > > +         dump_printf_loc (MSG_NOTE, stmt, "msg 6\n");
> > > +       }
> > > +       dump_printf_loc (MSG_NOTE, stmt, "msg 7\n");
> > > +
> > > +       ASSERT_DUMPED_TEXT_EQ (tmp,
> > > +                              "test.txt:5:10: note: msg 1\n"
> > > +                              "test.txt:5:10: note: === outer
> > > scope ===\n"
> > > +                              "test.txt:5:10: note:  msg 2\n"
> > > +                              "test.txt:5:10: note:  === middle
> > > scope ===\n"
> > > +                              "test.txt:5:10: note:   msg 3\n"
> > > +                              "test.txt:5:10: note:   === inner
> > > scope ===\n"
> > > +                              "test.txt:5:10: note:    msg 4\n"
> > > +                              "test.txt:5:10: note:   msg 5\n"
> > > +                              "test.txt:5:10: note:  msg 6\n"
> > > +                              "test.txt:5:10: note: msg 7\n");
> > > +       if (with_optinfo)
> > > +         {
> > > +           optinfo *info = tmp.get_pending_optinfo ();
> > > +           ASSERT_TRUE (info != NULL);
> > > +           ASSERT_EQ (info->num_items (), 1);
> > > +           ASSERT_IS_TEXT (info->get_item (0), "msg 7\n");
> > > +         }
> > > +      }
> > >      }
> > > -  }
> > > -
> > > -  /* poly_int.  */
> > > -  {
> > > -    temp_dump_context tmp (true);
> > > -    dump_dec (MSG_NOTE, poly_int64 (42));
> > > -
> > > -    optinfo *info = tmp.get_pending_optinfo ();
> > > -    ASSERT_TRUE (info != NULL);
> > > -    ASSERT_EQ (info->num_items (), 1);
> > > -    ASSERT_IS_TEXT (info->get_item (0), "42");
> > > -  }
> > >
> > >    /* Verify that MSG_* affects optinfo->get_kind (); we tested
> > > MSG_NOTE
> > >       above.  */
> > >    {
> > >      /* MSG_OPTIMIZED_LOCATIONS.  */
> > >      {
> > > -      temp_dump_context tmp (true);
> > > +      temp_dump_context tmp (true, MSG_ALL);
> > >        dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> > >        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> > >                  OPTINFO_KIND_SUCCESS);
> > > @@ -1728,7 +2002,7 @@ test_capture_of_dump_calls (const
> > > line_table_case &case_)
> > >
> > >      /* MSG_MISSED_OPTIMIZATION.  */
> > >      {
> > > -      temp_dump_context tmp (true);
> > > +      temp_dump_context tmp (true, MSG_ALL);
> > >        dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> > >        ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> > >                  OPTINFO_KIND_FAILURE);
> > > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > > index 1dbe3b8..2b174e5 100644
> > > --- a/gcc/dumpfile.h
> > > +++ b/gcc/dumpfile.h
> > > @@ -442,19 +442,27 @@ dump_enabled_p (void)
> > >  }
> > >
> > >  /* The following API calls (which *don't* take a "FILE *")
> > > -   write the output to zero or more locations:
> > > -   (a) the active dump_file, if any
> > > -   (b) the -fopt-info destination, if any
> > > -   (c) to the "optinfo" destinations, if any:
> > > -       (c.1) as optimization records
> > > -
> > > -   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> > > -                                   |
> > > -                                   +--> (b) alt_dump_file
> > > -                                   |
> > > -                                   `--> (c) optinfo
> > > -                                            `---> optinfo
> > > destinations
> > > -                                                  (c.1)
> > > optimization records
> > > +   write the output to zero or more locations.
> > > +
> > > +   Some destinations are written to immediately as dump_* calls
> > > +   are made; for others, the output is consolidated into an
> > > "optinfo"
> > > +   instance (with its own metadata), and only emitted once the
> > > optinfo
> > > +   is complete.
> > > +
> > > +   The destinations are:
> > > +
> > > +   (a) the "immediate" destinations:
> > > +       (a.1) the active dump_file, if any
> > > +       (a.2) the -fopt-info destination, if any
> > > +   (b) the "optinfo" destinations, if any:
> > > +       (b.1) as optimization records
> > > +
> > > +   dump_* (MSG_*) --> dumpfile.c --> items --> (a.1) dump_file
> > > +                                       |   `-> (a.2) alt_dump_file
> > > +                                       |
> > > +                                       `--> (b) optinfo
> > > +                                                `---> optinfo
> > > destinations
> > > +                                                      (b.1)
> > > optimization records
> > >
> > >     For optinfos, the dump_*_loc mark the beginning of an optinfo
> > >     instance: all subsequent dump_* calls are consolidated into
> > > diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> > > index 2199d52..992960e 100644
> > > --- a/gcc/optinfo-emit-json.cc
> > > +++ b/gcc/optinfo-emit-json.cc
> > > @@ -537,7 +537,7 @@ namespace selftest {
> > >  static void
> > >  test_building_json_from_dump_calls ()
> > >  {
> > > -  temp_dump_context tmp (true);
> > > +  temp_dump_context tmp (true, MSG_NOTE);
> > >    dump_location_t loc;
> > >    dump_printf_loc (MSG_NOTE, loc, "test of tree: ");
> > >    dump_generic_expr (MSG_NOTE, TDF_SLIM, integer_zero_node);
> > > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> > > index 93de9d9..b858c3c 100644
> > > --- a/gcc/optinfo.cc
> > > +++ b/gcc/optinfo.cc
> > > @@ -34,11 +34,11 @@ along with GCC; see the file COPYING3.  If not
> > > see
> > >  #include "cgraph.h"
> > >  #include "selftest.h"
> > >
> > > -/* optinfo_item's ctor.  */
> > > +/* optinfo_item's ctor.  Takes ownership of TEXT.  */
> > >
> > >  optinfo_item::optinfo_item (enum optinfo_item_kind kind,
> > > location_t location,
> > > -                           char *text, bool owned)
> > > -: m_kind (kind), m_location (location), m_text (text), m_owned
> > > (owned)
> > > +                           char *text)
> > > +: m_kind (kind), m_location (location), m_text (text)
> > >  {
> > >  }
> > >
> > > @@ -46,8 +46,7 @@ optinfo_item::optinfo_item (enum
> > > optinfo_item_kind kind, location_t location,
> > >
> > >  optinfo_item::~optinfo_item ()
> > >  {
> > > -  if (m_owned)
> > > -    free (m_text);
> > > +  free (m_text);
> > >  }
> > >
> > >  /* Get a string from KIND.  */
> > > @@ -81,7 +80,17 @@ optinfo::~optinfo ()
> > >      delete item;
> > >  }
> > >
> > > -/* Emit the optinfo to all of the active destinations.  */
> > > +/* Add ITEM to this optinfo.  */
> > > +
> > > +void
> > > +optinfo::add_item (optinfo_item *item)
> > > +{
> > > +  gcc_assert (item);
> > > +  m_items.safe_push (item);
> > > +}
> > > +
> > > +/* Emit the optinfo to all of the "non-immediate" destinations
> > > +   (emission to "immediate" destinations is done by
> > > emit_item).  */
> > >
> > >  void
> > >  optinfo::emit ()
> > > @@ -103,120 +112,6 @@ optinfo::handle_dump_file_kind (dump_flags_t
> > > dump_kind)
> > >      m_kind = OPTINFO_KIND_NOTE;
> > >  }
> > >
> > > -/* Append a string literal to this optinfo.  */
> > > -
> > > -void
> > > -optinfo::add_string (const char *str)
> > > -{
> > > -  optinfo_item *item
> > > -    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > > -                       const_cast <char *> (str), false);
> > > -  m_items.safe_push (item);
> > > -}
> > > -
> > > -/* Append printf-formatted text to this optinfo.  */
> > > -
> > > -void
> > > -optinfo::add_printf (const char *format, ...)
> > > -{
> > > -  va_list ap;
> > > -  va_start (ap, format);
> > > -  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)
> > > -{
> > > -  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);
> > > -}
> > > -
> > > -/* Append a gimple statement to this optinfo, equivalent to
> > > -   print_gimple_stmt.  */
> > > -
> > > -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);
> > > -  m_items.safe_push (item);
> > > -}
> > > -
> > > -/* Append a gimple statement to this optinfo, equivalent to
> > > -   print_gimple_expr.  */
> > > -
> > > -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);
> > > -  m_items.safe_push (item);
> > > -}
> > > -
> > > -/* Append a tree node to this optinfo, equivalent to
> > > print_generic_expr.  */
> > > -
> > > -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);
> > > -  m_items.safe_push (item);
> > > -}
> > > -
> > > -/* Append a symbol table node to this optinfo.  */
> > > -
> > > -void
> > > -optinfo::add_symtab_node (symtab_node *node)
> > > -{
> > > -  location_t loc = DECL_SOURCE_LOCATION (node->decl);
> > > -  optinfo_item *item
> > > -    = new optinfo_item (OPTINFO_ITEM_KIND_SYMTAB_NODE, loc,
> > > -                       xstrdup (node->dump_name ()), true);
> > > -  m_items.safe_push (item);
> > > -}
> > > -
> > > -/* Append the decimal represenation of a wide_int_ref to this
> > > -   optinfo.  */
> > > -
> > > -void
> > > -optinfo::add_dec (const wide_int_ref &wi, signop sgn)
> > > -{
> > > -  char buf[WIDE_INT_PRINT_BUFFER_SIZE];
> > > -  print_dec (wi, buf, sgn);
> > > -  optinfo_item *item
> > > -    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
> > > -                       xstrdup (buf), true);
> > > -  m_items.safe_push (item);
> > > -}
> > > -
> > >  /* Should optinfo instances be created?
> > >     All creation of optinfos should be guarded by this predicate.
> > >     Return true if any optinfo destinations are active.  */
> > > diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> > > index c4cf8ad..8ac961c 100644
> > > --- a/gcc/optinfo.h
> > > +++ b/gcc/optinfo.h
> > > @@ -92,6 +92,8 @@ enum optinfo_kind
> > >
> > >  extern const char *optinfo_kind_to_string (enum optinfo_kind
> > > kind);
> > >
> > > +class dump_context;
> > > +
> > >  /* A bundle of information describing part of an optimization.  */
> > >
> > >  class optinfo
> > > @@ -120,41 +122,14 @@ 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 add_item (optinfo_item *item);
> > > +
> > >   private:
> > >    void emit ();
> > >
> > >    /* Pre-canned ways of manipulating the optinfo, for use by
> > > friend class
> > >       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_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);
> > > -  void add_symtab_node (symtab_node *node);
> > > -  void add_dec (const wide_int_ref &wi, signop sgn);
> > > -
> > > -  template<unsigned int N, typename C>
> > > -  void add_poly_int (const poly_int<N, C> &value)
> > > -  {
> > > -    /* Compare with dump_dec (MSG_NOTE, ).  */
> > > -
> > > -    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
> > > -    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED :
> > > UNSIGNED;
> > > -
> > > -    if (value.is_constant ())
> > > -      add_dec (value.coeffs[0], sgn);
> > > -    else
> > > -      {
> > > -       add_string ("[");
> > > -       for (unsigned int i = 0; i < N; ++i)
> > > -         {
> > > -           add_dec (value.coeffs[i], sgn);
> > > -           add_string (i == N - 1 ? "]" : ",");
> > > -         }
> > > -      }
> > > -  }
> > >
> > >   private:
> > >    dump_location_t m_loc;
> > > @@ -179,7 +154,7 @@ class optinfo_item
> > >  {
> > >   public:
> > >    optinfo_item (enum optinfo_item_kind kind, location_t location,
> > > -               char *text, bool owned);
> > > +               char *text);
> > >    ~optinfo_item ();
> > >
> > >    enum optinfo_item_kind get_kind () const { return m_kind; }
> > > @@ -191,9 +166,8 @@ class optinfo_item
> > >    enum optinfo_item_kind m_kind;
> > >    location_t m_location;
> > >
> > > -  /* The textual form of the item.  */
> > > +  /* The textual form of the item, owned by the item.  */
> > >    char *m_text;
> > > -  bool m_owned;
> > >  };
> > >
> > >  #endif /* #ifndef GCC_OPTINFO_H */
> > > --
> > > 1.8.5.3
> > >

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH 5/5] Formatted printing for dump_* in the middle-end
  2018-07-31 14:19     ` David Malcolm
  2018-07-31 14:21       ` Richard Biener
@ 2018-07-31 19:56       ` Joseph Myers
  2018-08-02 17:09         ` [PATCH] v2: " David Malcolm
  1 sibling, 1 reply; 29+ messages in thread
From: Joseph Myers @ 2018-07-31 19:56 UTC (permalink / raw)
  To: David Malcolm; +Cc: Richard Biener, GCC Patches

On Tue, 31 Jul 2018, David Malcolm wrote:

> I didn't exhaustively check every callsite to the changed calls; I'm
> assuming that -Wformat during bootstrap has effectively checked that
> for me.  Though now I think about it, I note that we use
> HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to be a
> valid input to pp_format on all of our configurations?

HOST_WIDE_INT_PRINT_DEC should not be considered safe with pp_format 
(although since r197049 may have effectively stopped using %I64 on MinGW 
hosts, I'm not sure if there are current cases where it won't work).  
Rather, it is the job of pp_format to map the 'w' length specifier to 
HOST_WIDE_INT_PRINT_DEC etc.

I think it clearly makes for cleaner code to limit use of 
HOST_WIDE_INT_PRINT_* to as few places as possible and to prefer use of 
internal printf-like functions that accept formats such as %wd where 
possible.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] v2: Formatted printing for dump_* in the middle-end
  2018-07-31 19:56       ` Joseph Myers
@ 2018-08-02 17:09         ` David Malcolm
  2018-08-09 22:11           ` Joseph Myers
                             ` (2 more replies)
  0 siblings, 3 replies; 29+ messages in thread
From: David Malcolm @ 2018-08-02 17:09 UTC (permalink / raw)
  To: Joseph Myers, Richard Biener; +Cc: GCC Patches, David Malcolm

On Tue, 2018-07-31 at 19:56 +0000, Joseph Myers wrote:
> On Tue, 31 Jul 2018, David Malcolm wrote:
> 
> > I didn't exhaustively check every callsite to the changed calls;
> > I'm
> > assuming that -Wformat during bootstrap has effectively checked
> > that
> > for me.  Though now I think about it, I note that we use
> > HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to be a
> > valid input to pp_format on all of our configurations?
> 
> HOST_WIDE_INT_PRINT_DEC should not be considered safe with pp_format 
> (although since r197049 may have effectively stopped using %I64 on
> MinGW 
> hosts, I'm not sure if there are current cases where it won't
> work).  
> Rather, it is the job of pp_format to map the 'w' length specifier
> to 
> HOST_WIDE_INT_PRINT_DEC etc.
> 
> I think it clearly makes for cleaner code to limit use of 
> HOST_WIDE_INT_PRINT_* to as few places as possible and to prefer use
> of 
> internal printf-like functions that accept formats such as %wd where 
> possible.

Thanks.

I grepped the tree for every use of HOST_WIDE_INT_PRINT* and found
all that were within dump_printf[_loc] calls.  All such uses were
within files of the form "gcc/tree-vect*.c".

Here's an updated version of the patch (v2) which fixes those
callsites to use "%wd" or "%wu"; the dumpfile.c changes are as
per v1.

Changed in v2:
* rebased to after r263239 (which also touched c-format.c/h)
* avoid HOST_WIDE_INT_PRINT* within dump_printf* calls (in
  gcc/tree-vect*.c)

I didn't add test coverage for %wd and %wu, as pretty-print.c already
has selftest coverage for these.

Richard's review of the v1 patch was:
"The patch is OK if C family maintainers agree on their parts."
so I'm looking for review of:
(a) the c-format.c changes (Joseph?), and
(b) the tree-vect*.c changes (Richard?  Joseph?)

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

Is this patch OK for trunk?

Thanks
Dave


Blurb from v1, for reference:

This patch converts dump_print and dump_printf_loc from using
printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
based on pp_format, which supports formatting middle-end types.

In particular, the following codes are implemented (in addition
to the standard pretty_printer ones):

   %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 (MSG_*, arg, TDF_SLIM).

Hence it becomes possible to convert e.g.:

  if (dump_enabled_p ())
    {
      dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                       "not vectorized: different sized vector "
                       "types in statement, ");
      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
      dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
      dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, nunits_vectype);
      dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
    }

into a one-liner:

  if (dump_enabled_p ())
    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
                     "not vectorized: different sized vector "
                     "types in statement, %T and %T\n",
                     vectype, nunits_vectype);

Unlike regular pretty-printers, this one captures optinfo_item
instances for the formatted chunks as appropriate, so that when
written out to a JSON optimization record, the relevant parts of
the message are labelled by type, and by source location (so that
e.g. %G is entirely equivalent to using dump_gimple_stmt).

dump_printf and dump_printf_loc become marked with
ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.

gcc/c-family/ChangeLog:
	* c-format.c (enum format_type): Add gcc_dump_printf_format_type.
	(gcc_dump_printf_length_specs): New.
	(gcc_dump_printf_flag_pairs): New.
	(gcc_dump_printf_flag_specs): New.
	(gcc_dump_printf_char_table): New.
	(format_types_orig): Add entry for "gcc_dump_printf".
	(init_dynamic_diag_info): Set up length_char_specs and
	conversion_specs for gcc_dump_printf_format_type.
	(handle_format_attribute): Handle gcc_dump_printf_format_type.

gcc/ChangeLog:
	* dump-context.h: Include "dumpfile.h".
	(dump_context::dump_printf_va): Convert final param from va_list
	to va_list *.  Convert from ATTRIBUTE_PRINTF to
	ATTRIBUTE_GCC_DUMP_PRINTF.
	(dump_context::dump_printf_loc_va): Likewise.
	* dumpfile.c: Include "stringpool.h".
	(make_item_for_dump_printf_va): Delete.
	(make_item_for_dump_printf): Delete.
	(class dump_pretty_printer): New class.
	(dump_pretty_printer::dump_pretty_printer): New ctor.
	(dump_pretty_printer::emit_items): New member function.
	(dump_pretty_printer::emit_any_pending_textual_chunks): New member
	function.
	(dump_pretty_printer::emit_item): New member function.
	(dump_pretty_printer::stash_item): New member function.
	(dump_pretty_printer::format_decoder_cb): New member function.
	(dump_pretty_printer::decode_format): New member function.
	(dump_context::dump_printf_va): Reimplement in terms of
	dump_pretty_printer.
	(dump_context::dump_printf_loc_va): Convert final param from va_list
	to va_list *.
	(dump_context::begin_scope): Reimplement call to
	make_item_for_dump_printf.
	(dump_printf): Update for change to dump_printf_va.
	(dump_printf_loc): Likewise.
	(selftest::test_capture_of_dump_calls): Convert "stmt" from
	greturn * to gimple *.  Add a test_decl.  Add tests of dump_printf
	with %T, %E, and %G.
	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
	(dump_printf): Replace ATTRIBUTE_PRINTF_2 with
	ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
	(dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
	ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
	* tree-vect-data-refs.c (vect_lanes_optab_supported_p): Convert
	use of HOST_WIDE_INT_PRINT_DEC on unsigned HOST_WIDE_INT "count"
	within a dump_printf_loc call to "%wu".
	(vector_alignment_reachable_p): Merge two dump_printf[_loc] calls,
	converting a use of HOST_WIDE_INT_PRINT_DEC to "%wd".  Add a
	missing space after "=".
	* tree-vect-loop.c (vect_analyze_loop_2) Within a dump_printf
	call, convert use of HOST_WIDE_INT_PRINT_DEC to "%wd".
	* tree-vect-slp.c (vect_slp_bb): Within a dump_printf_loc call,
	convert use of HOST_WIDE_INT_PRINT_UNSIGNED to "%wu".
	* tree-vectorizer.c (try_vectorize_loop_1): Likewise.  Remove
	duplicate "vectorized" from message.

gcc/testsuite/ChangeLog:
	* gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage for
	gcc_dump_printf.
	* gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add test
	coverage for gcc_dump_printf.
---
 gcc/c-family/c-format.c                   |  36 ++-
 gcc/dump-context.h                        |   7 +-
 gcc/dumpfile.c                            | 358 +++++++++++++++++++++++++++---
 gcc/dumpfile.h                            |  20 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-1.c  |  15 +-
 gcc/testsuite/gcc.dg/format/gcc_diag-10.c |  26 +++
 gcc/tree-vect-data-refs.c                 |   8 +-
 gcc/tree-vect-loop.c                      |   2 +-
 gcc/tree-vect-slp.c                       |   3 +-
 gcc/tree-vectorizer.c                     |   4 +-
 10 files changed, 429 insertions(+), 50 deletions(-)

diff --git a/gcc/c-family/c-format.c b/gcc/c-family/c-format.c
index dc0e756..d590805 100644
--- a/gcc/c-family/c-format.c
+++ b/gcc/c-family/c-format.c
@@ -44,6 +44,7 @@ enum format_type { printf_format_type, asm_fprintf_format_type,
 		   gcc_diag_format_type, gcc_tdiag_format_type,
 		   gcc_cdiag_format_type,
 		   gcc_cxxdiag_format_type, gcc_gfc_format_type,
+		   gcc_dump_printf_format_type,
 		   gcc_objc_string_format_type,
 		   format_type_error = -1};
 
@@ -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[] =
 {
@@ -786,6 +790,22 @@ static const format_char_info gcc_gfc_char_table[] =
   { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
 };
 
+static const format_char_info gcc_dump_printf_char_table[] =
+{
+  /* The conversion specifiers implemented within pp_format.  */
+  PP_FORMAT_CHAR_TABLE,
+
+  /* Custom conversion specifiers implemented by dump_pretty_printer.  */
+
+  /* E and G require a "gimple *" argument at runtime.  */
+  { "EG",   1, STD_C89, { T89_G,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
+  /* T requires a "tree" at runtime.  */
+  { "T",   1, STD_C89, { T89_T,   BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN,  BADLEN  }, "", "\"",   NULL },
+
+  { NULL,  0, STD_C89, NOLENGTHS, NULL, NULL, NULL }
+};
+
 static const format_char_info scan_char_table[] =
 {
   /* C89 conversion specifiers.  */
@@ -885,6 +905,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,
@@ -3905,6 +3932,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),
@@ -3931,6 +3959,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
@@ -4085,7 +4115,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.  */
@@ -4107,7 +4138,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 f40ea14..54095d3 100644
--- a/gcc/dump-context.h
+++ b/gcc/dump-context.h
@@ -22,6 +22,7 @@ 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
 #include "pretty-print.h"
 
 /* A class for handling the various dump_* calls.
@@ -73,11 +74,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);
+			   const char *format, va_list *ap)
+    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 76a2ee8..a81ab3e 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.  */
@@ -681,56 +682,262 @@ dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
   dump_generic_expr (dump_kind, extra_dump_flags, t);
 }
 
-/* Make an item for the given dump call.  */
+/* A subclass of pretty_printer for implementing dump_context::dump_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.  */
 
-static optinfo_item *
-make_item_for_dump_printf_va (const char *format, va_list ap)
-  ATTRIBUTE_PRINTF (1, 0);
+class dump_pretty_printer : public pretty_printer
+{
+public:
+  dump_pretty_printer (dump_context *context, dump_flags_t dump_kind);
 
-static optinfo_item *
-make_item_for_dump_printf_va (const char *format, va_list ap)
+  void emit_items (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 emit_any_pending_textual_chunks (optinfo *dest);
+
+  void emit_item (optinfo_item *item, optinfo *dest);
+
+  dump_context *m_context;
+  dump_flags_t m_dump_kind;
+  auto_vec<stashed_item> m_stashed_items;
+};
+
+/* dump_pretty_printer's ctor.  */
+
+dump_pretty_printer::dump_pretty_printer (dump_context *context,
+					  dump_flags_t dump_kind)
+: pretty_printer (), m_context (context), m_dump_kind (dump_kind),
+  m_stashed_items ()
+{
+  pp_format_decoder (this) = format_decoder_cb;
+}
+
+/* Phase 3 of formatting; compare with pp_output_formatted_text.
+
+   Emit optinfo_item instances for the various formatted chunks from phases
+   1 and 2 (i.e. pp_format).
+
+   Some chunks may already have had their items built (during decode_format).
+   These chunks have been stashed into m_stashed_items; we emit them here.
+
+   For all other purely textual chunks, they are printed into
+   buffer->formatted_obstack, and then emitted as a textual optinfo_item.
+   This consolidates multiple adjacent text chunks into a single text
+   optinfo_item.  */
+
+void
+dump_pretty_printer::emit_items (optinfo *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)
+	{
+	  emit_any_pending_textual_chunks (dest);
+	  /* This chunk has a stashed item: use it.  */
+	  emit_item (m_stashed_items[stashed_item_idx++].item, dest);
+	}
+      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]);
+    }
+
+  emit_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 dump_pretty_printer::emit_items
+   for consolidating multiple adjacent pure-text chunks into single
+   optinfo_items (in phase 3).  */
+
+void
+dump_pretty_printer::emit_any_pending_textual_chunks (optinfo *dest)
 {
-  char *formatted_text = xvasprintf (format, ap);
+  gcc_assert (buffer->obstack == &buffer->formatted_obstack);
+
+  /* Don't emit 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);
-  return item;
+  emit_item (item, dest);
+
+  /* Clear the pending text by unwinding formatted_text back to the start
+     of the buffer (without deallocating).  */
+  obstack_free (&buffer->formatted_obstack,
+		buffer->formatted_obstack.object_base);
 }
 
-/* Make an item for the given dump call.  */
+/* Emit ITEM and take ownership of it.  If DEST is non-NULL, add ITEM
+   to DEST; otherwise delete ITEM.  */
 
-static optinfo_item *
-make_item_for_dump_printf (const char *format, ...)
-  ATTRIBUTE_PRINTF (1, 2);
+void
+dump_pretty_printer::emit_item (optinfo_item *item, optinfo *dest)
+{
+  m_context->emit_item (item, m_dump_kind);
+  if (dest)
+    dest->add_item (item);
+  else
+    delete item;
+}
 
-static optinfo_item *
-make_item_for_dump_printf (const char *format, ...)
+/* Record that ITEM (generated in phase 2 of formatting) is to be used for
+   the chunk at BUFFER_PTR in phase 3 (by emit_items).  */
+
+void
+dump_pretty_printer::stash_item (const char **buffer_ptr, optinfo_item *item)
 {
-  va_list ap;
-  va_start (ap, format);
-  optinfo_item *item
-    = make_item_for_dump_printf_va (format, ap);
-  va_end (ap);
-  return item;
+  gcc_assert (buffer_ptr);
+  gcc_assert (item);
+
+  m_stashed_items.safe_push (stashed_item (buffer_ptr, item));
+}
+
+/* pp_format_decoder callback for dump_pretty_printer, and thus for
+   dump_printf and dump_printf_loc.
+
+   A wrapper around decode_format, for type-safety.  */
+
+bool
+dump_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)
+{
+  dump_pretty_printer *opp = static_cast <dump_pretty_printer *> (pp);
+  return opp->decode_format (text, spec, buffer_ptr);
+}
+
+/* Format decoder for dump_pretty_printer, 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 (MSG_*, arg, TDF_SLIM).
+
+   FIXME: add symtab_node?
+
+   These format codes build optinfo_item instances, thus capturing metadata
+   about the arguments being dumped, as well as the textual output.  */
+
+bool
+dump_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).  */
+  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_dump_gimple_expr (stmt, 0, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	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_dump_gimple_stmt (stmt, 0, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	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_dump_generic_expr (t, TDF_SLIM);
+	stash_item (buffer_ptr, item);
+	return true;
+      }
+
+    default:
+      return false;
+    }
 }
 
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
 dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
-			      va_list ap)
+			      va_list *ap)
 {
-  optinfo_item *item = make_item_for_dump_printf_va (format, ap);
-  emit_item (item, dump_kind);
+  dump_pretty_printer pp (this, dump_kind);
 
+  text_info text;
+  text.err_no = errno;
+  text.args_ptr = ap;
+  text.format_spec = format;
+
+  /* Phases 1 and 2, using pp_format.  */
+  pp_format (&pp, &text);
+
+  /* Phase 3.  */
   if (optinfo_enabled_p ())
     {
       optinfo &info = ensure_pending_optinfo ();
       info.handle_dump_file_kind (dump_kind);
-      info.add_item (item);
+      pp.emit_items (&info);
     }
   else
-    delete item;
+    pp.emit_items (NULL);
 }
 
 /* Similar to dump_printf, except source location is also printed, and
@@ -739,7 +946,7 @@ dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
 void
 dump_context::dump_printf_loc_va (dump_flags_t dump_kind,
 				  const dump_location_t &loc,
-				  const char *format, va_list ap)
+				  const char *format, va_list *ap)
 {
   dump_loc (dump_kind, loc);
   dump_printf_va (dump_kind, format, ap);
@@ -851,7 +1058,11 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
   if (m_test_pp)
     ::dump_loc (MSG_NOTE, m_test_pp, loc.get_location_t ());
 
-  optinfo_item *item = make_item_for_dump_printf ("=== %s ===\n", name);
+  pretty_printer pp;
+  pp_printf (&pp, "=== %s ===\n", name);
+  optinfo_item *item
+    = new optinfo_item (OPTINFO_ITEM_KIND_TEXT, UNKNOWN_LOCATION,
+			xstrdup (pp_formatted_text (&pp)));
   emit_item (item, MSG_NOTE);
 
   if (optinfo_enabled_p ())
@@ -859,7 +1070,6 @@ dump_context::begin_scope (const char *name, const dump_location_t &loc)
       optinfo &info = begin_next_optinfo (loc);
       info.m_kind = OPTINFO_KIND_SCOPE;
       info.add_item (item);
-      end_any_optinfo ();
     }
   else
     delete item;
@@ -1006,7 +1216,7 @@ dump_printf (dump_flags_t dump_kind, const char *format, ...)
 {
   va_list ap;
   va_start (ap, format);
-  dump_context::get ().dump_printf_va (dump_kind, format, ap);
+  dump_context::get ().dump_printf_va (dump_kind, format, &ap);
   va_end (ap);
 }
 
@@ -1019,7 +1229,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 {
   va_list ap;
   va_start (ap, format);
-  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, &ap);
   va_end (ap);
 }
 
@@ -1808,9 +2018,12 @@ test_capture_of_dump_calls (const line_table_case &case_)
 
   dump_location_t loc = dump_location_t::from_location_t (where);
 
-  greturn *stmt = gimple_build_return (NULL);
+  gimple *stmt = gimple_build_return (NULL);
   gimple_set_location (stmt, where);
 
+  tree test_decl = build_decl (UNKNOWN_LOCATION, VAR_DECL,
+			       get_identifier ("test_decl"),
+			       integer_type_node);
   /* Run all tests twice, with and then without optinfo enabled, to ensure
      that immediate destinations vs optinfo-based destinations both
      work, independently of each other, with no leaks.  */
@@ -1834,6 +2047,89 @@ test_capture_of_dump_calls (const line_table_case &case_)
 	  }
       }
 
+      /* Test of dump_printf with %T.  */
+      {
+	temp_dump_context tmp (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "tree: %T", integer_zero_node);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "tree: 0");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "gimple: %E", stmt);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
+	dump_printf (MSG_NOTE, "gimple: %G", stmt);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp, "gimple: return;\n");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
+	dump_printf_loc (MSG_NOTE, loc, "before %T and %T"
+			 " %i consecutive %E%E after\n",
+			 integer_zero_node, test_decl, 42, stmt, stmt);
+
+	ASSERT_DUMPED_TEXT_EQ (tmp,
+			       "test.txt:5:10: note: before 0 and test_decl"
+			       " 42 consecutive return;return; after\n");
+	if (with_optinfo)
+	  {
+	    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 (with_optinfo, MSG_ALL);
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 8de001d..0305d36 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 dumpfile.c).  */
+
+#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
@@ -476,9 +489,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/testsuite/gcc.dg/format/gcc_diag-1.c b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
index 034e097..8761456 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-1.c
@@ -1,4 +1,4 @@
-/* Test for GCC diagnositc formats.  */
+/* Test for GCC diagnostic formats.  */
 /* Origin: Kaveh Ghazi <ghazi@caip.rutgers.edu> */
 /* { dg-do compile } */
 /* { dg-options "-Wformat" } */
@@ -24,6 +24,7 @@ extern int diag (const char *, ...) ATTRIBUTE_DIAG(__gcc_diag__);
 extern int tdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_tdiag__);
 extern int cdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cdiag__);
 extern int cxxdiag (const char *, ...) ATTRIBUTE_DIAG(__gcc_cxxdiag__);
+extern int dump (const char *, ...) ATTRIBUTE_DIAG(__gcc_dump_printf__);
 
 void
 foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
@@ -39,36 +40,44 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   tdiag ("%%");
   cdiag ("%%");
   cxxdiag ("%%");
+  dump ("%%");
   diag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   tdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   cdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   cxxdiag ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
+  dump ("%d%i%o%u%x%c%s%p%%", i, i, u, u, u, i, s, p);
   diag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   tdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   cdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   cxxdiag ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
+  dump ("%qd%qi%qo%qu%qx%qc%qs%qp%<%%%'%>", i, i, u, u, u, i, s, p);
   diag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   tdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   cdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   cxxdiag ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
+  dump ("%ld%li%lo%lu%lx", l, l, ul, ul, ul);
   diag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   tdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   cdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   cxxdiag ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
+  dump ("%lld%lli%llo%llu%llx", ll, ll, ull, ull, ull);
   diag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   tdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   cdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   cxxdiag ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
+  dump ("%wd%wi%wo%wu%wx", ll, ll, ull, ull, ull);
   diag ("%.*s", i, s);
   tdiag ("%.*s", i, s);
   cdiag ("%.*s", i, s);
   cxxdiag ("%.*s", i, s);
+  dump ("%.*s", i, s);
 
   /* Extensions provided in the diagnostic framework.  */
   diag ("%m");
   tdiag ("%m");
   cdiag ("%m");
   cxxdiag ("%m");
+  dump ("%m");
 
   /* Quote directives to avoid "warning: conversion used unquoted." */
   tdiag ("%<%D%F%T%V%>", t1, t1, t1, t1);
@@ -94,20 +103,24 @@ foo (int i, int i1, int i2, unsigned int u, double d, char *s, void *p,
   tdiag ("%Z", v, v_len);
   cdiag ("%Z", v, v_len);
   cxxdiag ("%Z", v, v_len);
+  dump ("%Z", v, v_len);
 
   /* Bad stuff with extensions.  */
   diag ("%m", i); /* { dg-warning "format" "extra arg" } */
   tdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
   cdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
   cxxdiag ("%m", i); /* { dg-warning "format" "extra arg" } */
+  dump ("%m", i); /* { dg-warning "format" "extra arg" } */
   diag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   tdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   cdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   cxxdiag ("%#m"); /* { dg-warning "format" "bogus modifier" } */
+  dump ("%#m"); /* { dg-warning "format" "bogus modifier" } */
   diag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   tdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   cdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   cxxdiag ("%+m"); /* { dg-warning "format" "bogus modifier" } */
+  dump ("%+m"); /* { dg-warning "format" "bogus modifier" } */
   diag ("%D", t1); /* { dg-warning "format" "bogus tree" } */
   tdiag ("%A", t1); /* { dg-warning "format" "bogus tree" } */
   tdiag ("%E", t1);
diff --git a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
index 2965509..2f6a002 100644
--- a/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
+++ b/gcc/testsuite/gcc.dg/format/gcc_diag-10.c
@@ -19,12 +19,16 @@ typedef union tree_node *tree;
    the C test to find the symbol.  */
 typedef struct gimple gimple;
 
+/* Likewise for gimple.  */
+typedef struct gimple gimple;
+
 #define FORMAT(kind) __attribute__ ((format (__gcc_## kind ##__, 1, 2)))
 
 void diag (const char*, ...) FORMAT (diag);
 void cdiag (const char*, ...) FORMAT (cdiag);
 void tdiag (const char*, ...) FORMAT (tdiag);
 void cxxdiag (const char*, ...) FORMAT (cxxdiag);
+void dump (const char*, ...) FORMAT (dump_printf);
 
 void test_diag (tree t, gimple *gc)
 {
@@ -157,3 +161,25 @@ void test_cxxdiag (tree t, gimple *gc)
   cxxdiag ("%<%V%>", t);
   cxxdiag ("%<%X%>", t);
 }
+
+void test_dump (tree t, gimple *stmt)
+{
+  dump ("%<");   /* { dg-warning "unterminated quoting directive" } */
+  dump ("%>");   /* { dg-warning "unmatched quoting directive " } */
+  dump ("%<foo%<bar%>%>");   /* { dg-warning "nested quoting directive" } */
+
+  dump ("%R");       /* { dg-warning "unmatched color reset directive" } */
+  dump ("%r", "");   /* { dg-warning "unterminated color directive" } */
+  dump ("%r%r", "", "");   /* { dg-warning "unterminated color directive" } */
+  dump ("%r%R", "");
+  dump ("%r%r%R", "", "");
+  dump ("%r%R%r%R", "", "");
+
+  dump ("%<%R%>");      /* { dg-warning "unmatched color reset directive" } */
+  dump ("%<%r%>", "");  /* { dg-warning "unterminated color directive" } */
+  dump ("%<%r%R%>", "");
+
+  dump ("%E", stmt);
+  dump ("%T", t);
+  dump ("%G", stmt);
+}
diff --git a/gcc/tree-vect-data-refs.c b/gcc/tree-vect-data-refs.c
index e20b502..d70d207 100644
--- a/gcc/tree-vect-data-refs.c
+++ b/gcc/tree-vect-data-refs.c
@@ -74,8 +74,7 @@ vect_lanes_optab_supported_p (const char *name, convert_optab optab,
 	{
 	  if (dump_enabled_p ())
 	    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			     "no array mode for %s["
-			     HOST_WIDE_INT_PRINT_DEC "]\n",
+			     "no array mode for %s[%wu]\n",
 			     GET_MODE_NAME (mode), count);
 	  return false;
 	}
@@ -1249,9 +1248,8 @@ vector_alignment_reachable_p (dr_vec_info *dr_info)
       if (dump_enabled_p ())
 	{
 	  dump_printf_loc (MSG_NOTE, vect_location,
-	                   "data size =" HOST_WIDE_INT_PRINT_DEC, elmsize);
-	  dump_printf (MSG_NOTE,
-	               ". misalignment = %d.\n", DR_MISALIGNMENT (dr_info));
+	                   "data size = %wd. misalignment = %d.\n", elmsize,
+			   DR_MISALIGNMENT (dr_info));
 	}
       if (DR_MISALIGNMENT (dr_info) % elmsize)
 	{
diff --git a/gcc/tree-vect-loop.c b/gcc/tree-vect-loop.c
index 92c01a2..985f25e 100644
--- a/gcc/tree-vect-loop.c
+++ b/gcc/tree-vect-loop.c
@@ -1958,7 +1958,7 @@ start_over:
       dump_printf_loc (MSG_NOTE, vect_location,
 		       "vectorization_factor = ");
       dump_dec (MSG_NOTE, vectorization_factor);
-      dump_printf (MSG_NOTE, ", niters = " HOST_WIDE_INT_PRINT_DEC "\n",
+      dump_printf (MSG_NOTE, ", niters = %wd\n",
 		   LOOP_VINFO_INT_NITERS (loop_vinfo));
     }
 
diff --git a/gcc/tree-vect-slp.c b/gcc/tree-vect-slp.c
index d0f6da4..88ec230 100644
--- a/gcc/tree-vect-slp.c
+++ b/gcc/tree-vect-slp.c
@@ -3033,8 +3033,7 @@ vect_slp_bb (basic_block bb)
 	  unsigned HOST_WIDE_INT bytes;
 	  if (current_vector_size.is_constant (&bytes))
 	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-			     "basic block part vectorized using "
-			     HOST_WIDE_INT_PRINT_UNSIGNED " byte "
+			     "basic block part vectorized using %wu byte "
 			     "vectors\n", bytes);
 	  else
 	    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
diff --git a/gcc/tree-vectorizer.c b/gcc/tree-vectorizer.c
index d58729b..db4fb76 100644
--- a/gcc/tree-vectorizer.c
+++ b/gcc/tree-vectorizer.c
@@ -935,9 +935,7 @@ try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
   unsigned HOST_WIDE_INT bytes;
   if (current_vector_size.is_constant (&bytes))
     dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
-		     "loop vectorized vectorized using "
-		     HOST_WIDE_INT_PRINT_UNSIGNED " byte "
-		     "vectors\n", bytes);
+		     "loop vectorized using %wu byte vectors\n", bytes);
   else
     dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, vect_location,
 		     "loop vectorized using variable length vectors\n");
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] v2: Formatted printing for dump_* in the middle-end
  2018-08-02 17:09         ` [PATCH] v2: " David Malcolm
@ 2018-08-09 22:11           ` Joseph Myers
  2018-08-17  4:08           ` Jeff Law
  2018-08-27  6:58           ` Jakub Jelinek
  2 siblings, 0 replies; 29+ messages in thread
From: Joseph Myers @ 2018-08-09 22:11 UTC (permalink / raw)
  To: David Malcolm; +Cc: Richard Biener, GCC Patches

On Thu, 2 Aug 2018, David Malcolm wrote:

> (a) the c-format.c changes (Joseph?), and

The c-format.c changes are OK.

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] v2: Formatted printing for dump_* in the middle-end
  2018-08-02 17:09         ` [PATCH] v2: " David Malcolm
  2018-08-09 22:11           ` Joseph Myers
@ 2018-08-17  4:08           ` Jeff Law
  2018-08-17 18:24             ` David Malcolm
  2018-08-27  6:58           ` Jakub Jelinek
  2 siblings, 1 reply; 29+ messages in thread
From: Jeff Law @ 2018-08-17  4:08 UTC (permalink / raw)
  To: David Malcolm, Joseph Myers, Richard Biener; +Cc: GCC Patches

On 08/02/2018 11:54 AM, David Malcolm wrote:
> On Tue, 2018-07-31 at 19:56 +0000, Joseph Myers wrote:
>> On Tue, 31 Jul 2018, David Malcolm wrote:
>>
>>> I didn't exhaustively check every callsite to the changed calls;
>>> I'm
>>> assuming that -Wformat during bootstrap has effectively checked
>>> that
>>> for me.  Though now I think about it, I note that we use
>>> HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to be a
>>> valid input to pp_format on all of our configurations?
>> HOST_WIDE_INT_PRINT_DEC should not be considered safe with pp_format 
>> (although since r197049 may have effectively stopped using %I64 on
>> MinGW 
>> hosts, I'm not sure if there are current cases where it won't
>> work).  
>> Rather, it is the job of pp_format to map the 'w' length specifier
>> to 
>> HOST_WIDE_INT_PRINT_DEC etc.
>>
>> I think it clearly makes for cleaner code to limit use of 
>> HOST_WIDE_INT_PRINT_* to as few places as possible and to prefer use
>> of 
>> internal printf-like functions that accept formats such as %wd where 
>> possible.
> Thanks.
> 
> I grepped the tree for every use of HOST_WIDE_INT_PRINT* and found
> all that were within dump_printf[_loc] calls.  All such uses were
> within files of the form "gcc/tree-vect*.c".
> 
> Here's an updated version of the patch (v2) which fixes those
> callsites to use "%wd" or "%wu"; the dumpfile.c changes are as
> per v1.
> 
> Changed in v2:
> * rebased to after r263239 (which also touched c-format.c/h)
> * avoid HOST_WIDE_INT_PRINT* within dump_printf* calls (in
>   gcc/tree-vect*.c)
> 
> I didn't add test coverage for %wd and %wu, as pretty-print.c already
> has selftest coverage for these.
> 
> Richard's review of the v1 patch was:
> "The patch is OK if C family maintainers agree on their parts."
> so I'm looking for review of:
> (a) the c-format.c changes (Joseph?), and
> (b) the tree-vect*.c changes (Richard?  Joseph?)
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> 
> Is this patch OK for trunk?
> 
> Thanks
> Dave
> 
> 
> Blurb from v1, for reference:
> 
> This patch converts dump_print and dump_printf_loc from using
> printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
> based on pp_format, which supports formatting middle-end types.
> 
> In particular, the following codes are implemented (in addition
> to the standard pretty_printer ones):
> 
>    %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 (MSG_*, arg, TDF_SLIM).
> 
> Hence it becomes possible to convert e.g.:
> 
>   if (dump_enabled_p ())
>     {
>       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
>                        "not vectorized: different sized vector "
>                        "types in statement, ");
>       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, vectype);
>       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
>       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM, nunits_vectype);
>       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
>     }
> 
> into a one-liner:
> 
>   if (dump_enabled_p ())
>     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
>                      "not vectorized: different sized vector "
>                      "types in statement, %T and %T\n",
>                      vectype, nunits_vectype);
> 
> Unlike regular pretty-printers, this one captures optinfo_item
> instances for the formatted chunks as appropriate, so that when
> written out to a JSON optimization record, the relevant parts of
> the message are labelled by type, and by source location (so that
> e.g. %G is entirely equivalent to using dump_gimple_stmt).
> 
> dump_printf and dump_printf_loc become marked with
> ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.
> 
> gcc/c-family/ChangeLog:
> 	* c-format.c (enum format_type): Add gcc_dump_printf_format_type.
> 	(gcc_dump_printf_length_specs): New.
> 	(gcc_dump_printf_flag_pairs): New.
> 	(gcc_dump_printf_flag_specs): New.
> 	(gcc_dump_printf_char_table): New.
> 	(format_types_orig): Add entry for "gcc_dump_printf".
> 	(init_dynamic_diag_info): Set up length_char_specs and
> 	conversion_specs for gcc_dump_printf_format_type.
> 	(handle_format_attribute): Handle gcc_dump_printf_format_type.
> 
> gcc/ChangeLog:
> 	* dump-context.h: Include "dumpfile.h".
> 	(dump_context::dump_printf_va): Convert final param from va_list
> 	to va_list *.  Convert from ATTRIBUTE_PRINTF to
> 	ATTRIBUTE_GCC_DUMP_PRINTF.
> 	(dump_context::dump_printf_loc_va): Likewise.
> 	* dumpfile.c: Include "stringpool.h".
> 	(make_item_for_dump_printf_va): Delete.
> 	(make_item_for_dump_printf): Delete.
> 	(class dump_pretty_printer): New class.
> 	(dump_pretty_printer::dump_pretty_printer): New ctor.
> 	(dump_pretty_printer::emit_items): New member function.
> 	(dump_pretty_printer::emit_any_pending_textual_chunks): New member
> 	function.
> 	(dump_pretty_printer::emit_item): New member function.
> 	(dump_pretty_printer::stash_item): New member function.
> 	(dump_pretty_printer::format_decoder_cb): New member function.
> 	(dump_pretty_printer::decode_format): New member function.
> 	(dump_context::dump_printf_va): Reimplement in terms of
> 	dump_pretty_printer.
> 	(dump_context::dump_printf_loc_va): Convert final param from va_list
> 	to va_list *.
> 	(dump_context::begin_scope): Reimplement call to
> 	make_item_for_dump_printf.
> 	(dump_printf): Update for change to dump_printf_va.
> 	(dump_printf_loc): Likewise.
> 	(selftest::test_capture_of_dump_calls): Convert "stmt" from
> 	greturn * to gimple *.  Add a test_decl.  Add tests of dump_printf
> 	with %T, %E, and %G.
> 	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
> 	(dump_printf): Replace ATTRIBUTE_PRINTF_2 with
> 	ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
> 	(dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
> 	ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
> 	* tree-vect-data-refs.c (vect_lanes_optab_supported_p): Convert
> 	use of HOST_WIDE_INT_PRINT_DEC on unsigned HOST_WIDE_INT "count"
> 	within a dump_printf_loc call to "%wu".
> 	(vector_alignment_reachable_p): Merge two dump_printf[_loc] calls,
> 	converting a use of HOST_WIDE_INT_PRINT_DEC to "%wd".  Add a
> 	missing space after "=".
> 	* tree-vect-loop.c (vect_analyze_loop_2) Within a dump_printf
> 	call, convert use of HOST_WIDE_INT_PRINT_DEC to "%wd".
> 	* tree-vect-slp.c (vect_slp_bb): Within a dump_printf_loc call,
> 	convert use of HOST_WIDE_INT_PRINT_UNSIGNED to "%wu".
> 	* tree-vectorizer.c (try_vectorize_loop_1): Likewise.  Remove
> 	duplicate "vectorized" from message.
> 
> gcc/testsuite/ChangeLog:
> 	* gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage for
> 	gcc_dump_printf.
> 	* gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add test
> 	coverage for gcc_dump_printf.

> 
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> index f40ea14..54095d3 100644
> --- a/gcc/dump-context.h
> +++ b/gcc/dump-context.h
> @@ -22,6 +22,7 @@ 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
No real need to document why you include something.  Just include what
you need.  If the need goes away, we do have tools which will help
identify unnecessary and duplicated #includes.

Per your comments above, I really only looked at the tree-vect* changes,
which are fine.  So I think you're covered now.  OK for the trunk.

jeff

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] v2: Formatted printing for dump_* in the middle-end
  2018-08-17  4:08           ` Jeff Law
@ 2018-08-17 18:24             ` David Malcolm
  0 siblings, 0 replies; 29+ messages in thread
From: David Malcolm @ 2018-08-17 18:24 UTC (permalink / raw)
  To: Jeff Law, Joseph Myers, Richard Biener; +Cc: GCC Patches

On Thu, 2018-08-16 at 22:08 -0600, Jeff Law wrote:
> On 08/02/2018 11:54 AM, David Malcolm wrote:
> > On Tue, 2018-07-31 at 19:56 +0000, Joseph Myers wrote:
> > > On Tue, 31 Jul 2018, David Malcolm wrote:
> > > 
> > > > I didn't exhaustively check every callsite to the changed
> > > > calls;
> > > > I'm
> > > > assuming that -Wformat during bootstrap has effectively checked
> > > > that
> > > > for me.  Though now I think about it, I note that we use
> > > > HOST_WIDE_INT_PRINT_DEC in many places: is this guaranteed to
> > > > be a
> > > > valid input to pp_format on all of our configurations?
> > > 
> > > HOST_WIDE_INT_PRINT_DEC should not be considered safe with
> > > pp_format 
> > > (although since r197049 may have effectively stopped using %I64
> > > on
> > > MinGW 
> > > hosts, I'm not sure if there are current cases where it won't
> > > work).  
> > > Rather, it is the job of pp_format to map the 'w' length
> > > specifier
> > > to 
> > > HOST_WIDE_INT_PRINT_DEC etc.
> > > 
> > > I think it clearly makes for cleaner code to limit use of 
> > > HOST_WIDE_INT_PRINT_* to as few places as possible and to prefer
> > > use
> > > of 
> > > internal printf-like functions that accept formats such as %wd
> > > where 
> > > possible.
> > 
> > Thanks.
> > 
> > I grepped the tree for every use of HOST_WIDE_INT_PRINT* and found
> > all that were within dump_printf[_loc] calls.  All such uses were
> > within files of the form "gcc/tree-vect*.c".
> > 
> > Here's an updated version of the patch (v2) which fixes those
> > callsites to use "%wd" or "%wu"; the dumpfile.c changes are as
> > per v1.
> > 
> > Changed in v2:
> > * rebased to after r263239 (which also touched c-format.c/h)
> > * avoid HOST_WIDE_INT_PRINT* within dump_printf* calls (in
> >   gcc/tree-vect*.c)
> > 
> > I didn't add test coverage for %wd and %wu, as pretty-print.c
> > already
> > has selftest coverage for these.
> > 
> > Richard's review of the v1 patch was:
> > "The patch is OK if C family maintainers agree on their parts."
> > so I'm looking for review of:
> > (a) the c-format.c changes (Joseph?), and
> > (b) the tree-vect*.c changes (Richard?  Joseph?)
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > 
> > Is this patch OK for trunk?
> > 
> > Thanks
> > Dave
> > 
> > 
> > Blurb from v1, for reference:
> > 
> > This patch converts dump_print and dump_printf_loc from using
> > printf (and thus ATTRIBUTE_PRINTF) to using a new pretty-printer
> > based on pp_format, which supports formatting middle-end types.
> > 
> > In particular, the following codes are implemented (in addition
> > to the standard pretty_printer ones):
> > 
> >    %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 (MSG_*, arg, TDF_SLIM).
> > 
> > Hence it becomes possible to convert e.g.:
> > 
> >   if (dump_enabled_p ())
> >     {
> >       dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> >                        "not vectorized: different sized vector "
> >                        "types in statement, ");
> >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > vectype);
> >       dump_printf (MSG_MISSED_OPTIMIZATION, " and ");
> >       dump_generic_expr (MSG_MISSED_OPTIMIZATION, TDF_SLIM,
> > nunits_vectype);
> >       dump_printf (MSG_MISSED_OPTIMIZATION, "\n");
> >     }
> > 
> > into a one-liner:
> > 
> >   if (dump_enabled_p ())
> >     dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> >                      "not vectorized: different sized vector "
> >                      "types in statement, %T and %T\n",
> >                      vectype, nunits_vectype);
> > 
> > Unlike regular pretty-printers, this one captures optinfo_item
> > instances for the formatted chunks as appropriate, so that when
> > written out to a JSON optimization record, the relevant parts of
> > the message are labelled by type, and by source location (so that
> > e.g. %G is entirely equivalent to using dump_gimple_stmt).
> > 
> > dump_printf and dump_printf_loc become marked with
> > ATTRIBUTE_GCC_DUMP_PRINTF, which the patch also implements.
> > 
> > gcc/c-family/ChangeLog:
> > 	* c-format.c (enum format_type): Add
> > gcc_dump_printf_format_type.
> > 	(gcc_dump_printf_length_specs): New.
> > 	(gcc_dump_printf_flag_pairs): New.
> > 	(gcc_dump_printf_flag_specs): New.
> > 	(gcc_dump_printf_char_table): New.
> > 	(format_types_orig): Add entry for "gcc_dump_printf".
> > 	(init_dynamic_diag_info): Set up length_char_specs and
> > 	conversion_specs for gcc_dump_printf_format_type.
> > 	(handle_format_attribute): Handle gcc_dump_printf_format_type.
> > 
> > gcc/ChangeLog:
> > 	* dump-context.h: Include "dumpfile.h".
> > 	(dump_context::dump_printf_va): Convert final param from
> > va_list
> > 	to va_list *.  Convert from ATTRIBUTE_PRINTF to
> > 	ATTRIBUTE_GCC_DUMP_PRINTF.
> > 	(dump_context::dump_printf_loc_va): Likewise.
> > 	* dumpfile.c: Include "stringpool.h".
> > 	(make_item_for_dump_printf_va): Delete.
> > 	(make_item_for_dump_printf): Delete.
> > 	(class dump_pretty_printer): New class.
> > 	(dump_pretty_printer::dump_pretty_printer): New ctor.
> > 	(dump_pretty_printer::emit_items): New member function.
> > 	(dump_pretty_printer::emit_any_pending_textual_chunks): New
> > member
> > 	function.
> > 	(dump_pretty_printer::emit_item): New member function.
> > 	(dump_pretty_printer::stash_item): New member function.
> > 	(dump_pretty_printer::format_decoder_cb): New member function.
> > 	(dump_pretty_printer::decode_format): New member function.
> > 	(dump_context::dump_printf_va): Reimplement in terms of
> > 	dump_pretty_printer.
> > 	(dump_context::dump_printf_loc_va): Convert final param from
> > va_list
> > 	to va_list *.
> > 	(dump_context::begin_scope): Reimplement call to
> > 	make_item_for_dump_printf.
> > 	(dump_printf): Update for change to dump_printf_va.
> > 	(dump_printf_loc): Likewise.
> > 	(selftest::test_capture_of_dump_calls): Convert "stmt" from
> > 	greturn * to gimple *.  Add a test_decl.  Add tests of
> > dump_printf
> > 	with %T, %E, and %G.
> > 	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): New macro.
> > 	(dump_printf): Replace ATTRIBUTE_PRINTF_2 with
> > 	ATTRIBUTE_GCC_DUMP_PRINTF (2, 3).
> > 	(dump_printf_loc): Replace ATTRIBUTE_PRINTF_3 with
> > 	ATTRIBUTE_GCC_DUMP_PRINTF (3, 0).
> > 	* tree-vect-data-refs.c (vect_lanes_optab_supported_p): Convert
> > 	use of HOST_WIDE_INT_PRINT_DEC on unsigned HOST_WIDE_INT
> > "count"
> > 	within a dump_printf_loc call to "%wu".
> > 	(vector_alignment_reachable_p): Merge two dump_printf[_loc]
> > calls,
> > 	converting a use of HOST_WIDE_INT_PRINT_DEC to "%wd".  Add a
> > 	missing space after "=".
> > 	* tree-vect-loop.c (vect_analyze_loop_2) Within a dump_printf
> > 	call, convert use of HOST_WIDE_INT_PRINT_DEC to "%wd".
> > 	* tree-vect-slp.c (vect_slp_bb): Within a dump_printf_loc call,
> > 	convert use of HOST_WIDE_INT_PRINT_UNSIGNED to "%wu".
> > 	* tree-vectorizer.c (try_vectorize_loop_1): Likewise.  Remove
> > 	duplicate "vectorized" from message.
> > 
> > gcc/testsuite/ChangeLog:
> > 	* gcc.dg/format/gcc_diag-1.c: Fix typo.  Add test coverage for
> > 	gcc_dump_printf.
> > 	* gcc.dg/format/gcc_diag-10.c: Add gimple typedef.  Add test
> > 	coverage for gcc_dump_printf.
> > 
> > diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> > index f40ea14..54095d3 100644
> > --- a/gcc/dump-context.h
> > +++ b/gcc/dump-context.h
> > @@ -22,6 +22,7 @@ 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
> 
> No real need to document why you include something.  Just include
> what
> you need.  If the need goes away, we do have tools which will help
> identify unnecessary and duplicated #includes.

I've removed the comment.

> Per your comments above, I really only looked at the tree-vect*
> changes,
> which are fine.  So I think you're covered now.  OK for the trunk.

Thanks.

This is now in trunk as r263626 (I rebased and retested it before
committing).

Dave

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] v2: Formatted printing for dump_* in the middle-end
  2018-08-02 17:09         ` [PATCH] v2: " David Malcolm
  2018-08-09 22:11           ` Joseph Myers
  2018-08-17  4:08           ` Jeff Law
@ 2018-08-27  6:58           ` Jakub Jelinek
  2018-08-27 23:46             ` [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF David Malcolm
  2 siblings, 1 reply; 29+ messages in thread
From: Jakub Jelinek @ 2018-08-27  6:58 UTC (permalink / raw)
  To: David Malcolm; +Cc: Joseph Myers, Richard Biener, GCC Patches

On Thu, Aug 02, 2018 at 01:54:07PM -0400, David Malcolm wrote:
> +/* 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 dumpfile.c).  */
> +
> +#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

Why >= 3005 rather than >= 9000 ?
GCC 8 and earlier will not handle that format attribute anyway and will just
loudly complain.

	Jakub

^ permalink raw reply	[flat|nested] 29+ messages in thread

* [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF
  2018-08-27  6:58           ` Jakub Jelinek
@ 2018-08-27 23:46             ` David Malcolm
  2018-08-28  6:44               ` Jakub Jelinek
  0 siblings, 1 reply; 29+ messages in thread
From: David Malcolm @ 2018-08-27 23:46 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph Myers, Richard Biener, GCC Patches, David Malcolm

On Mon, 2018-08-27 at 08:57 +0200, Jakub Jelinek wrote:
> On Thu, Aug 02, 2018 at 01:54:07PM -0400, David Malcolm wrote:
> > +/* 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 dumpfile.c).  */
> > +
> > +#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
> 
> Why >= 3005 rather than >= 9000 ?

I believe I copied the logic from one of the existing __format__
attribute macros.  Maybe my thinking was that it expressed the
version in which format checking was added.

Yes, that's clearly the wrong version to use; sorry about that.

> GCC 8 and earlier will not handle that format attribute anyway and
> will just
> loudly complain.
> 
> 	Jakub

Here a patch that does what you suggest.

Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.

OK for trunk?

gcc/ChangeLog:
	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): Change version check on
	GCC_VERSION for usage of "__gcc_dump_printf__" format from
	>= 3005 to >= 9000.
---
 gcc/dumpfile.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 0305d36..671b7b9 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
    format codes (see pretty-print.c), with additional codes for middle-end
    specific entities (see dumpfile.c).  */
 
-#if GCC_VERSION >= 3005
+#if GCC_VERSION >= 9000
 #define ATTRIBUTE_GCC_DUMP_PRINTF(m, n) \
   __attribute__ ((__format__ (__gcc_dump_printf__, m ,n))) \
   ATTRIBUTE_NONNULL(m)
-- 
1.8.5.3

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF
  2018-08-27 23:46             ` [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF David Malcolm
@ 2018-08-28  6:44               ` Jakub Jelinek
  2018-08-28 12:26                 ` Jakub Jelinek
  0 siblings, 1 reply; 29+ messages in thread
From: Jakub Jelinek @ 2018-08-28  6:44 UTC (permalink / raw)
  To: David Malcolm; +Cc: Joseph Myers, Richard Biener, GCC Patches

On Mon, Aug 27, 2018 at 08:32:11PM -0400, David Malcolm wrote:
> gcc/ChangeLog:
> 	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): Change version check on
> 	GCC_VERSION for usage of "__gcc_dump_printf__" format from
> 	>= 3005 to >= 9000.

Ok, thanks.

> ---
>  gcc/dumpfile.h | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 0305d36..671b7b9 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -28,7 +28,7 @@ along with GCC; see the file COPYING3.  If not see
>     format codes (see pretty-print.c), with additional codes for middle-end
>     specific entities (see dumpfile.c).  */
>  
> -#if GCC_VERSION >= 3005
> +#if GCC_VERSION >= 9000
>  #define ATTRIBUTE_GCC_DUMP_PRINTF(m, n) \
>    __attribute__ ((__format__ (__gcc_dump_printf__, m ,n))) \
>    ATTRIBUTE_NONNULL(m)
> -- 
> 1.8.5.3

	Jakub

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF
  2018-08-28  6:44               ` Jakub Jelinek
@ 2018-08-28 12:26                 ` Jakub Jelinek
  2018-08-28 14:19                   ` David Malcolm
  0 siblings, 1 reply; 29+ messages in thread
From: Jakub Jelinek @ 2018-08-28 12:26 UTC (permalink / raw)
  To: David Malcolm; +Cc: Joseph Myers, Richard Biener, GCC Patches

On Tue, Aug 28, 2018 at 08:43:59AM +0200, Jakub Jelinek wrote:
> On Mon, Aug 27, 2018 at 08:32:11PM -0400, David Malcolm wrote:
> > gcc/ChangeLog:
> > 	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): Change version check on
> > 	GCC_VERSION for usage of "__gcc_dump_printf__" format from
> > 	>= 3005 to >= 9000.
> 
> Ok, thanks.

Another option would be to use __gcc_tdiag__ for GCC_VERSION >= 3005 and <
9000, that differs from __gcc_dump_printf__ only in %E (tree vs. gimple *)
and %D/%F/%V/%K added to __gcc_tdiag__, so worst case one would get a couple
of rare warnings when %E is used somewhere; certainly better than having a
warning on every TU that includes dumpfile.h.

	Jakub

^ permalink raw reply	[flat|nested] 29+ messages in thread

* Re: [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF
  2018-08-28 12:26                 ` Jakub Jelinek
@ 2018-08-28 14:19                   ` David Malcolm
  0 siblings, 0 replies; 29+ messages in thread
From: David Malcolm @ 2018-08-28 14:19 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Joseph Myers, Richard Biener, GCC Patches

On Tue, 2018-08-28 at 14:26 +0200, Jakub Jelinek wrote:
> On Tue, Aug 28, 2018 at 08:43:59AM +0200, Jakub Jelinek wrote:
> > On Mon, Aug 27, 2018 at 08:32:11PM -0400, David Malcolm wrote:
> > > gcc/ChangeLog:
> > > 	* dumpfile.h (ATTRIBUTE_GCC_DUMP_PRINTF): Change version check
> > > on
> > > 	GCC_VERSION for usage of "__gcc_dump_printf__" format from
> > > 	>= 3005 to >= 9000.
> > 
> > Ok, thanks.
> 
> Another option would be to use __gcc_tdiag__ for GCC_VERSION >= 3005
> and <
> 9000, that differs from __gcc_dump_printf__ only in %E (tree vs.
> gimple *)
> and %D/%F/%V/%K added to __gcc_tdiag__, so worst case one would get a
> couple
> of rare warnings when %E is used somewhere; certainly better than
> having a
> warning on every TU that includes dumpfile.h.

Maybe, but I think that trying to decipher warnings based on something
that's close to the rules but not the same as them could get confusing
(especially if we add some new codes to __gcc_dump_printf__).

I've gone ahead with the simpler fix, as posted above, to stop the
flood of warnings (r263920).

Sorry again.

Dave

^ permalink raw reply	[flat|nested] 29+ messages in thread

end of thread, other threads:[~2018-08-28 14:19 UTC | newest]

Thread overview: 29+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-07-27 21:47 [PATCH 0/5] dump_printf support for middle-end types David Malcolm
2018-07-27 21:47 ` [PATCH 2/5] dumpfile.c: eliminate special-casing of dump_file/alt_dump_file David Malcolm
2018-07-31 12:54   ` Richard Biener
2018-07-31 15:34     ` David Malcolm
2018-07-31 15:37       ` Richard Biener
2018-07-27 21:47 ` [PATCH 4/5] c-family: clean up the data tables in c-format.c David Malcolm
2018-07-31 12:56   ` Richard Biener
2018-07-31 13:08     ` Marek Polacek
2018-07-27 21:47 ` [PATCH 1/5] Simplify dump_context by adding a dump_loc member function David Malcolm
2018-07-31 12:51   ` Richard Biener
2018-07-27 21:47 ` [PATCH 5/5] Formatted printing for dump_* in the middle-end David Malcolm
2018-07-31 13:03   ` Richard Biener
2018-07-31 14:19     ` David Malcolm
2018-07-31 14:21       ` Richard Biener
2018-07-31 14:33         ` Richard Biener
2018-07-31 19:56       ` Joseph Myers
2018-08-02 17:09         ` [PATCH] v2: " David Malcolm
2018-08-09 22:11           ` Joseph Myers
2018-08-17  4:08           ` Jeff Law
2018-08-17 18:24             ` David Malcolm
2018-08-27  6:58           ` Jakub Jelinek
2018-08-27 23:46             ` [PATCH] Fix version check for ATTRIBUTE_GCC_DUMP_PRINTF David Malcolm
2018-08-28  6:44               ` Jakub Jelinek
2018-08-28 12:26                 ` Jakub Jelinek
2018-08-28 14:19                   ` David Malcolm
2018-07-27 21:47 ` [PATCH 3/5] C++: clean up cp_printer David Malcolm
2018-07-28 14:06   ` Jason Merrill
2018-07-31 12:50 ` [PATCH 0/5] dump_printf support for middle-end types Richard Biener
2018-07-31 14:01   ` David Malcolm

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).