[1/2] v5: Add "optinfo" framework

Message ID 1531309047-48505-1-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series
  • [1/2] v5: Add "optinfo" framework
Related show

Commit Message

David Malcolm July 11, 2018, 11:37 a.m.
Changes relative to v4:
* eliminated optinfo subclasses as discussed
* eliminated optinfo-internal.h, moving what remained into optinfo.h
* added support for dump_gimple_expr_loc and dump_gimple_expr
* more selftests

This patch implements a way to consolidate dump_* calls into
optinfo objects, as enabling work towards being able to write out
optimization records to a file (I'm focussing on that destination
in this patch kit, rather than diagnostic remarks).

The patch adds the support for building optinfo instances from dump_*
calls, but leaves implementing any *users* of them to followup patches.

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

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add optinfo.o.
	* coretypes.h (class symtab_node): New forward decl.
	(struct cgraph_node): New forward decl.
	(class varpool_node): New forward decl.
	* dump-context.h: New file.
	* dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h",
	"tree-pass.h".
	(refresh_dumps_are_enabled): Use optinfo_enabled_p.
	(set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
	(set_alt_dump_file): Likewise.
	(dump_context::~dump_context): New dtor.
	(dump_gimple_stmt): Move implementation to...
	(dump_context::dump_gimple_stmt): ...this new member function.
	Add the stmt to any pending optinfo, creating one if need be.
	(dump_gimple_stmt_loc): Move implementation to...
	(dump_context::dump_gimple_stmt_loc): ...this new member function.
	Start a new optinfo and add the stmt to it.
	(dump_gimple_expr): Move implementation to...
	(dump_context::dump_gimple_expr): ...this new member function.
	Add the stmt to any pending optinfo, creating one if need be.
	(dump_gimple_expr_loc): Move implementation to...
	(dump_context::dump_gimple_expr_loc): ...this new member function.
	Start a new optinfo and add the stmt to it.
	(dump_generic_expr): Move implementation to...
	(dump_context::dump_generic_expr): ...this new member function.
	Add the tree to any pending optinfo, creating one if need be.
	(dump_generic_expr_loc): Move implementation to...
	(dump_context::dump_generic_expr_loc): ...this new member
	function.  Add the tree to any pending optinfo, creating one if
	need be.
	(dump_printf): Move implementation to...
	(dump_context::dump_printf_va): ...this new member function.  Add
	the text to any pending optinfo, creating one if need be.
	(dump_printf_loc): Move implementation to...
	(dump_context::dump_printf_loc_va): ...this new member function.
	Start a new optinfo and add the stmt to it.
	(dump_dec): Move implementation to...
	(dump_context::dump_dec): ...this new member function.  Add the
	value to any pending optinfo, creating one if need be.
	(dump_context::dump_symtab_node): New member function.
	(dump_context::get_scope_depth): New member function.
	(dump_context::begin_scope): New member function.
	(dump_context::end_scope): New member function.
	(dump_context::ensure_pending_optinfo): New member function.
	(dump_context::begin_next_optinfo): New member function.
	(dump_context::end_any_optinfo): New member function.
	(dump_context::s_current): New global.
	(dump_context::s_default): New global.
	(dump_scope_depth): Delete global.
	(dumpfile_ensure_any_optinfo_are_flushed): New function.
	(dump_symtab_node): New function.
	(get_dump_scope_depth): Reimplement in terms of dump_context.
	(dump_begin_scope): Likewise.
	(dump_end_scope): Likewise.
	(selftest::temp_dump_context::temp_dump_context): New ctor.
	(selftest::temp_dump_context::~temp_dump_context): New dtor.
	(selftest::verify_item): New function.
	(ASSERT_IS_TEXT): New macro.
	(ASSERT_IS_TREE): New macro.
	(ASSERT_IS_GIMPLE): New macro.
	(selftest::test_capture_of_dump_calls): New test.
	(selftest::dumpfile_c_tests): Call it.
	* dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block)
	(dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc)
	(dump_gimple_stmt, dump_dec): Gather these related decls and add a
	descriptive comment.
	(dump_function, print_combine_total_stats, enable_rtl_dump_file)
	(dump_node, dump_bb): Move these unrelated decls.
	(class dump_manager): Add leading comment.
	* optinfo.cc: New file.
	* optinfo.h: New file.
---
 gcc/Makefile.in    |   1 +
 gcc/coretypes.h    |   7 +
 gcc/dump-context.h | 138 +++++++++++++
 gcc/dumpfile.c     | 597 +++++++++++++++++++++++++++++++++++++++++++++++++----
 gcc/dumpfile.h     |  84 +++++---
 gcc/optinfo.cc     | 236 +++++++++++++++++++++
 gcc/optinfo.h      | 203 ++++++++++++++++++
 7 files changed, 1200 insertions(+), 66 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h

Comments

David Malcolm July 18, 2018, 8:20 p.m. | #1
Ping, re these patches:

"[PATCH 1/2] v5: Add "optinfo" framework"
  https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00535.html
 
"[PATCH 2/2] Add "-fsave-optimization-record""
  https://gcc.gnu.org/ml/gcc-patches/2018-07/msg00536.html

Thanks
Dave

On Wed, 2018-07-11 at 07:37 -0400, David Malcolm wrote:
> Changes relative to v4:
> * eliminated optinfo subclasses as discussed
> * eliminated optinfo-internal.h, moving what remained into optinfo.h
> * added support for dump_gimple_expr_loc and dump_gimple_expr
> * more selftests
> 
> This patch implements a way to consolidate dump_* calls into
> optinfo objects, as enabling work towards being able to write out
> optimization records to a file (I'm focussing on that destination
> in this patch kit, rather than diagnostic remarks).
> 
> The patch adds the support for building optinfo instances from dump_*
> calls, but leaves implementing any *users* of them to followup
> patches.
> 
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> 
> OK for trunk?
> 
> gcc/ChangeLog:
> 	* Makefile.in (OBJS): Add optinfo.o.
> 	* coretypes.h (class symtab_node): New forward decl.
> 	(struct cgraph_node): New forward decl.
> 	(class varpool_node): New forward decl.
> 	* dump-context.h: New file.
> 	* dumpfile.c: Include "optinfo.h", "dump-context.h",
> "cgraph.h",
> 	"tree-pass.h".
> 	(refresh_dumps_are_enabled): Use optinfo_enabled_p.
> 	(set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
> 	(set_alt_dump_file): Likewise.
> 	(dump_context::~dump_context): New dtor.
> 	(dump_gimple_stmt): Move implementation to...
> 	(dump_context::dump_gimple_stmt): ...this new member function.
> 	Add the stmt to any pending optinfo, creating one if need be.
> 	(dump_gimple_stmt_loc): Move implementation to...
> 	(dump_context::dump_gimple_stmt_loc): ...this new member
> function.
> 	Start a new optinfo and add the stmt to it.
> 	(dump_gimple_expr): Move implementation to...
> 	(dump_context::dump_gimple_expr): ...this new member function.
> 	Add the stmt to any pending optinfo, creating one if need be.
> 	(dump_gimple_expr_loc): Move implementation to...
> 	(dump_context::dump_gimple_expr_loc): ...this new member
> function.
> 	Start a new optinfo and add the stmt to it.
> 	(dump_generic_expr): Move implementation to...
> 	(dump_context::dump_generic_expr): ...this new member function.
> 	Add the tree to any pending optinfo, creating one if need be.
> 	(dump_generic_expr_loc): Move implementation to...
> 	(dump_context::dump_generic_expr_loc): ...this new member
> 	function.  Add the tree to any pending optinfo, creating one if
> 	need be.
> 	(dump_printf): Move implementation to...
> 	(dump_context::dump_printf_va): ...this new member
> function.  Add
> 	the text to any pending optinfo, creating one if need be.
> 	(dump_printf_loc): Move implementation to...
> 	(dump_context::dump_printf_loc_va): ...this new member
> function.
> 	Start a new optinfo and add the stmt to it.
> 	(dump_dec): Move implementation to...
> 	(dump_context::dump_dec): ...this new member function.  Add the
> 	value to any pending optinfo, creating one if need be.
> 	(dump_context::dump_symtab_node): New member function.
> 	(dump_context::get_scope_depth): New member function.
> 	(dump_context::begin_scope): New member function.
> 	(dump_context::end_scope): New member function.
> 	(dump_context::ensure_pending_optinfo): New member function.
> 	(dump_context::begin_next_optinfo): New member function.
> 	(dump_context::end_any_optinfo): New member function.
> 	(dump_context::s_current): New global.
> 	(dump_context::s_default): New global.
> 	(dump_scope_depth): Delete global.
> 	(dumpfile_ensure_any_optinfo_are_flushed): New function.
> 	(dump_symtab_node): New function.
> 	(get_dump_scope_depth): Reimplement in terms of dump_context.
> 	(dump_begin_scope): Likewise.
> 	(dump_end_scope): Likewise.
> 	(selftest::temp_dump_context::temp_dump_context): New ctor.
> 	(selftest::temp_dump_context::~temp_dump_context): New dtor.
> 	(selftest::verify_item): New function.
> 	(ASSERT_IS_TEXT): New macro.
> 	(ASSERT_IS_TREE): New macro.
> 	(ASSERT_IS_GIMPLE): New macro.
> 	(selftest::test_capture_of_dump_calls): New test.
> 	(selftest::dumpfile_c_tests): Call it.
> 	* dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block)
> 	(dump_generic_expr_loc, dump_generic_expr,
> dump_gimple_stmt_loc)
> 	(dump_gimple_stmt, dump_dec): Gather these related decls and
> add a
> 	descriptive comment.
> 	(dump_function, print_combine_total_stats,
> enable_rtl_dump_file)
> 	(dump_node, dump_bb): Move these unrelated decls.
> 	(class dump_manager): Add leading comment.
> 	* optinfo.cc: New file.
> 	* optinfo.h: New file.
> ---
>  gcc/Makefile.in    |   1 +
>  gcc/coretypes.h    |   7 +
>  gcc/dump-context.h | 138 +++++++++++++
>  gcc/dumpfile.c     | 597
> +++++++++++++++++++++++++++++++++++++++++++++++++----
>  gcc/dumpfile.h     |  84 +++++---
>  gcc/optinfo.cc     | 236 +++++++++++++++++++++
>  gcc/optinfo.h      | 203 ++++++++++++++++++
>  7 files changed, 1200 insertions(+), 66 deletions(-)
>  create mode 100644 gcc/dump-context.h
>  create mode 100644 gcc/optinfo.cc
>  create mode 100644 gcc/optinfo.h
> 
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 2a05a66..dd1dfc1 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1427,6 +1427,7 @@ OBJS = \
>  	optabs-libfuncs.o \
>  	optabs-query.o \
>  	optabs-tree.o \
> +	optinfo.o \
>  	options-save.o \
>  	opts-global.o \
>  	passes.o \
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 283b4eb..ed0e825 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -134,6 +134,13 @@ struct gomp_single;
>  struct gomp_target;
>  struct gomp_teams;
>  
> +/* Subclasses of symtab_node, using indentation to show the class
> +   hierarchy.  */
> +
> +class symtab_node;
> +  struct cgraph_node;
> +  class varpool_node;
> +
>  union section;
>  typedef union section section;
>  struct gcc_options;
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> new file mode 100644
> index 0000000..a191e3a
> --- /dev/null
> +++ b/gcc/dump-context.h
> @@ -0,0 +1,138 @@
> +/* Support code for handling the various dump_* calls in dumpfile.h
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 3, or (at your option)
> +any later version.
> +
> +GCC is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +
> +#ifndef GCC_DUMP_CONTEXT_H
> +#define GCC_DUMP_CONTEXT_H 1
> +
> +/* A class for handling the various dump_* calls.
> +
> +   In particular, this class has responsibility for consolidating
> +   the "dump_*" calls into optinfo instances (delimited by
> "dump_*_loc"
> +   calls), and emitting them.
> +
> +   Putting this in a class (rather than as global state) allows
> +   for selftesting of this code.  */
> +
> +class dump_context
> +{
> +  friend class temp_dump_context;
> + public:
> +  static dump_context &get () { return *s_current; }
> +
> +  ~dump_context ();
> +
> +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +			 gimple *gs, int spc);
> +
> +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +			     const dump_location_t &loc,
> +			     dump_flags_t extra_dump_flags,
> +			     gimple *gs, int spc);
> +
> +  void dump_gimple_expr (dump_flags_t dump_kind,
> +			 dump_flags_t extra_dump_flags,
> +			 gimple *gs, int spc);
> +
> +  void dump_gimple_expr_loc (dump_flags_t dump_kind,
> +			    const dump_location_t &loc,
> +			    dump_flags_t extra_dump_flags,
> +			    gimple *gs,
> +			    int spc);
> +
> +  void dump_generic_expr (dump_flags_t dump_kind,
> +			  dump_flags_t extra_dump_flags,
> +			  tree t);
> +
> +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> +			      const dump_location_t &loc,
> +			      dump_flags_t extra_dump_flags,
> +			      tree t);
> +
> +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> +		       va_list ap) ATTRIBUTE_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);
> +
> +  template<unsigned int N, typename C>
> +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C>
> &value);
> +
> +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
> +
> +  /* Managing nested scopes.  */
> +  unsigned int get_scope_depth () const;
> +  void begin_scope (const char *name, const dump_location_t &loc);
> +  void end_scope ();
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is
> true.  */
> +  bool forcibly_enable_optinfo_p () const
> +  {
> +    return m_forcibly_enable_optinfo;
> +  }
> +
> +  void end_any_optinfo ();
> +
> + private:
> +  optinfo &ensure_pending_optinfo ();
> +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is
> true.  */
> +  bool m_forcibly_enable_optinfo;
> +
> +  /* The current nesting depth of dump scopes, for showing nesting
> +     via indentation).  */
> +  unsigned int m_scope_depth;
> +
> +  /* The optinfo currently being accumulated since the last
> dump_*_loc call,
> +     if any.  */
> +  optinfo *m_pending;
> +
> +  /* The currently active dump_context, for use by the dump_* API
> calls.  */
> +  static dump_context *s_current;
> +
> +  /* The default active context.  */
> +  static dump_context s_default;
> +};
> +
> +#if CHECKING_P
> +
> +/* An RAII-style class for use in selftests for temporarily using a
> different
> +   dump_context.  */
> +
> +class temp_dump_context
> +{
> + public:
> +  temp_dump_context (bool forcibly_enable_optinfo);
> +  ~temp_dump_context ();
> +
> +  /* Support for selftests.  */
> +  optinfo *get_pending_optinfo () const { return
> m_context.m_pending; }
> +
> + private:
> +  dump_context m_context;
> +  dump_context *m_saved;
> +  bool m_saved_flag_remarks;
> +};
> +
> +#endif /* CHECKING_P */
> +
> +#endif /* GCC_DUMP_CONTEXT_H */
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 7ed1796..38f9539 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -33,6 +33,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h" /* for dump_user_location_t ctor.  */
>  #include "rtl.h" /* for dump_user_location_t ctor.  */
>  #include "selftest.h"
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "cgraph.h"
> +#include "tree-pass.h" /* for "current_pass".  */
>  
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -64,7 +68,7 @@ bool dumps_are_enabled = false;
>  static void
>  refresh_dumps_are_enabled ()
>  {
> -  dumps_are_enabled = (dump_file || alt_dump_file);
> +  dumps_are_enabled = (dump_file || alt_dump_file ||
> optinfo_enabled_p ());
>  }
>  
>  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the
> "dumps_are_enabled"
> @@ -73,6 +77,7 @@ refresh_dumps_are_enabled ()
>  void
>  set_dump_file (FILE *new_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    dump_file = new_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -83,6 +88,7 @@ set_dump_file (FILE *new_dump_file)
>  static void
>  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 ();
>  }
> @@ -458,25 +464,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile,
> source_location loc)
>      }
>  }
>  
> +/* Implementation of dump_context member functions.  */
> +
> +/* dump_context's dtor.  */
> +
> +dump_context::~dump_context ()
> +{
> +  delete m_pending;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>  
>  void
> -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> -		  gimple *gs, int spc)
> +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);
> +
> +  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);
> +    }
>  }
>  
>  /* Similar to dump_gimple_stmt, except additionally print source
> location.  */
>  
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> -		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +				    const dump_location_t &loc,
> +				    dump_flags_t extra_dump_flags,
> +				    gimple *gs, int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -490,6 +515,13 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>        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 gimple statement GS with SPC indentation spaces and
> @@ -497,21 +529,32 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>     Do not terminate with a newline or semicolon.  */
>  
>  void
> -dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> -		  gimple *gs, int spc)
> +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);
> +
> +  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);
> +    }
>  }
>  
>  /* Similar to dump_gimple_expr, except additionally print source
> location.  */
>  
>  void
> -dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> -		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
> +				    const dump_location_t &loc,
> +				    dump_flags_t extra_dump_flags,
> +				    gimple *gs,
> +				    int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -525,6 +568,13 @@ dump_gimple_expr_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>        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);
> +    }
>  }
>  
>  
> @@ -532,14 +582,22 @@ dump_gimple_expr_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>     DUMP_KIND is enabled.  */
>  
>  void
> -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> -		   tree t)
> +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);
> +
> +  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);
> +    }
>  }
>  
>  
> @@ -547,8 +605,10 @@ dump_generic_expr (dump_flags_t dump_kind,
> dump_flags_t extra_dump_flags,
>     location.  */
>  
>  void
> -dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> -		       dump_flags_t extra_dump_flags, tree t)
> +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> +				     const dump_location_t &loc,
> +				     dump_flags_t extra_dump_flags,
> +				     tree t)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -562,53 +622,83 @@ dump_generic_expr_loc (dump_flags_t dump_kind,
> const dump_location_t &loc,
>        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);
> +    }
>  }
>  
>  /* Output a formatted message using FORMAT on appropriate dump
> streams.  */
>  
>  void
> -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +dump_context::dump_printf_va (dump_flags_t dump_kind, const char
> *format,
> +			      va_list ap)
>  {
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      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 ap;
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  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);
>      }
>  }
>  
> -/* Similar to dump_printf, except source location is also
> printed.  */
> +/* Similar to dump_printf, except source location is also printed,
> and
> +   dump location captured.  */
>  
>  void
> -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -		 const char *format, ...)
> +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))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      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 ap;
>        dump_loc (dump_kind, alt_dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      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);
>      }
>  }
>  
> @@ -616,7 +706,7 @@ dump_printf_loc (dump_flags_t dump_kind, const
> dump_location_t &loc,
>  
>  template<unsigned int N, typename C>
>  void
> -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C>
> &value)
>  {
>    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
>    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> @@ -625,6 +715,224 @@ dump_dec (dump_flags_t dump_kind, const
> poly_int<N, C> &value)
>  
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_dec (value, alt_dump_file, sgn);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_poly_int<N,C> (value);
> +    }
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +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 ());
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_symtab_node (node);
> +    }
> +}
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +dump_context::get_scope_depth () const
> +{
> +  return m_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-
> info
> +   destination, if any.
> +   Emit a "scope" optinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +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);
> +    }
> +
> +  if (alt_dump_file)
> +    {
> +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      fprintf (alt_dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      end_any_optinfo ();
> +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> +      info.add_printf ("=== %s ===", name);
> +      info.emit ();
> +    }
> +
> +  m_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_context::end_scope ()
> +{
> +  end_any_optinfo ();
> +  m_scope_depth--;
> +}
> +
> +/* Return the optinfo currently being accumulated, creating one if
> +   necessary.  */
> +
> +optinfo &
> +dump_context::ensure_pending_optinfo ()
> +{
> +  if (!m_pending)
> +    return begin_next_optinfo (dump_location_t (dump_user_location_t
> ()));
> +  return *m_pending;
> +}
> +
> +/* Start a new optinfo and return it, ending any optinfo that was
> already
> +   accumulated.  */
> +
> +optinfo &
> +dump_context::begin_next_optinfo (const dump_location_t &loc)
> +{
> +  end_any_optinfo ();
> +  gcc_assert (m_pending == NULL);
> +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> +  return *m_pending;
> +}
> +
> +/* End any optinfo that has been accumulated within this context;
> emitting
> +   it to any destinations as appropriate - though none have
> currently been
> +   implemented.  */
> +
> +void
> +dump_context::end_any_optinfo ()
> +{
> +  if (m_pending)
> +    m_pending->emit ();
> +  delete m_pending;
> +  m_pending = NULL;
> +}
> +
> +/* The current singleton dump_context, and its default.  */
> +
> +dump_context *dump_context::s_current = &dump_context::s_default;
> +dump_context dump_context::s_default;
> +
> +/* Implementation of dump_* API calls, calling into dump_context
> +   member functions.  */
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
> +
> +void
> +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +		  gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt (dump_kind,
> extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_stmt, except additionally print source
> location.  */
> +
> +void
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> +		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +{
> +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc,
> extra_dump_flags,
> +					     gs, spc);
> +}
> +
> +/* 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.  */
> +
> +void
> +dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +		  gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_expr (dump_kind,
> extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_expr, except additionally print source
> location.  */
> +
> +void
> +dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> +		      dump_flags_t extra_dump_flags, gimple *gs, int
> spc)
> +{
> +  dump_context::get ().dump_gimple_expr_loc (dump_kind, loc,
> extra_dump_flags,
> +					     gs, spc);
> +}
> +
> +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
> +   DUMP_KIND is enabled.  */
> +
> +void
> +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t
> extra_dump_flags,
> +		   tree t)
> +{
> +  dump_context::get ().dump_generic_expr (dump_kind,
> extra_dump_flags, t);
> +}
> +
> +/* Similar to dump_generic_expr, except additionally print the
> source
> +   location.  */
> +
> +void
> +dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t
> &loc,
> +		       dump_flags_t extra_dump_flags, tree t)
> +{
> +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc,
> extra_dump_flags,
> +					      t);
> +}
> +
> +/* Output a formatted message using FORMAT on appropriate dump
> streams.  */
> +
> +void
> +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);
> +  va_end (ap);
> +}
> +
> +/* Similar to dump_printf, except source location is also printed,
> and
> +   dump location captured.  */
> +
> +void
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +		 const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format,
> ap);
> +  va_end (ap);
> +}
> +
> +/* Output VALUE in decimal to appropriate dump streams.  */
> +
> +template<unsigned int N, typename C>
> +void
> +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +{
> +  dump_context::get ().dump_dec (dump_kind, value);
>  }
>  
>  template void dump_dec (dump_flags_t, const poly_uint16 &);
> @@ -655,29 +963,42 @@ dump_hex (dump_flags_t dump_kind, const
> poly_wide_int &value)
>      print_hex (value, alt_dump_file);
>  }
>  
> -/* The current dump scope-nesting depth.  */
> +/* Emit and delete the currently pending optinfo, if there is one,
> +   without the caller needing to know about class dump_context.  */
> +
> +void
> +dumpfile_ensure_any_optinfo_are_flushed ()
> +{
> +  dump_context::get().end_any_optinfo ();
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
>  
> -static int dump_scope_depth;
> +void
> +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  dump_context::get ().dump_symtab_node (dump_kind, node);
> +}
>  
>  /* Get the current dump scope-nesting depth.
> -   For use by dump_*_loc (for showing nesting via indentation).  */
> +   For use by -fopt-info (for showing nesting via indentation).  */
>  
>  unsigned int
>  get_dump_scope_depth ()
>  {
> -  return dump_scope_depth;
> +  return dump_context::get ().get_scope_depth ();
>  }
>  
>  /* Push a nested dump scope.
>     Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-
> info
>     destination, if any.
> +   Emit a "scope" opinfo if optinfos are enabled.
>     Increment the scope depth.  */
>  
>  void
>  dump_begin_scope (const char *name, const dump_location_t &loc)
>  {
> -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> -  dump_scope_depth++;
> +  dump_context::get ().begin_scope (name, loc);
>  }
>  
>  /* Pop a nested dump scope.  */
> @@ -685,7 +1006,7 @@ dump_begin_scope (const char *name, const
> dump_location_t &loc)
>  void
>  dump_end_scope ()
>  {
> -  dump_scope_depth--;
> +  dump_context::get ().end_scope ();
>  }
>  
>  /* Start a dump for PHASE. Store user-supplied dump flags in
> @@ -1238,6 +1559,24 @@ enable_rtl_dump_file (void)
>  
>  #if CHECKING_P
>  
> +/* 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)
> +: m_context (),
> +  m_saved (&dump_context ().get ())
> +{
> +  dump_context::s_current = &m_context;
> +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> +}
> +
> +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> +
> +temp_dump_context::~temp_dump_context ()
> +{
> +  dump_context::s_current = m_saved;
> +}
> +
>  namespace selftest {
>  
>  /* Verify that the dump_location_t constructors capture the source
> location
> @@ -1274,12 +1613,188 @@ test_impl_location ()
>  #endif
>  }
>  
> +/* Verify that ITEM has the expected values.  */
> +
> +static void
> +verify_item (const location &loc,
> +	     const optinfo_item *item,
> +	     enum optinfo_item_kind expected_kind,
> +	     location_t expected_location,
> +	     const char *expected_text)
> +{
> +  ASSERT_EQ_AT (loc, item->get_kind (), expected_kind);
> +  ASSERT_EQ_AT (loc, item->get_location (), expected_location);
> +  ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
> +}
> +
> +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> +
> +#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT						
>     \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT,
> \
> +		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		
>     \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a tree item, with the expected values.  */
> +
> +#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT						
>     \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE,
> \
> +		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a gimple item, with the expected values.  */
> +
> +#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT						
>     \
> +    verify_item (SELFTEST_LOCATION, (ITEM),
> OPTINFO_ITEM_KIND_GIMPLE, \
> +		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
> +  SELFTEST_END_STMT
> +
> +/* Verify that calls to the dump_* API are captured and consolidated
> into
> +   optimization records. */
> +
> +static void
> +test_capture_of_dump_calls (const line_table_case &case_)
> +{
> +  /* Generate a location_t for testing.  */
> +  line_table_test ltt (case_);
> +  linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
> +  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);
> +
> +  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);
> +
> +      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.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr_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;");
> +    }
> +
> +    /* dump_gimple_expr.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr (MSG_NOTE, 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;");
> +    }
> +  }
> +
> +  /* 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);
> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +		 OPTINFO_KIND_SUCCESS);
> +    }
> +
> +    /* MSG_MISSED_OPTIMIZATION.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +		 OPTINFO_KIND_FAILURE);
> +    }
> +  }
> +}
> +
>  /* Run all of the selftests within this file.  */
>  
>  void
>  dumpfile_c_tests ()
>  {
>    test_impl_location ();
> +  for_each_line_table_case (test_capture_of_dump_calls);
>  }
>  
>  } // namespace selftest
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 4a71ef7..3096a89 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,6 +420,48 @@ extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
> +
> +/* Global variables used to communicate with passes.  */
> +extern FILE *dump_file;
> +extern dump_flags_t dump_flags;
> +extern const char *dump_file_name;
> +
> +extern bool dumps_are_enabled;
> +
> +extern void set_dump_file (FILE *new_dump_file);
> +
> +/* Return true if any of the dumps is enabled, false otherwise. */
> +static inline bool
> +dump_enabled_p (void)
> +{
> +  return dumps_are_enabled;
> +}
> +
> +/* 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:
> +
> +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> +                                   |
> +                                   +--> (b) alt_dump_file
> +                                   |
> +                                   `--> (c) optinfo
> +                                            `---> optinfo
> destinations
> +
> +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> +   instance: all subsequent dump_* calls are consolidated into
> +   that optinfo, until the next dump_*_loc call (or a change in
> +   dump scope, or a call to
> dumpfile_ensure_any_optinfo_are_flushed).
> +
> +   A group of dump_* calls should be guarded by:
> +
> +     if (dump_enabled_p ())
> +
> +   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_loc (dump_flags_t, const dump_location_t &,
>  			     const char *, ...) ATTRIBUTE_PRINTF_3;
> @@ -434,37 +476,14 @@ extern void dump_gimple_stmt (dump_flags_t,
> dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr_loc (dump_flags_t, const
> dump_location_t &,
>  				  dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *,
> int);
> -extern void print_combine_total_stats (void);
> -extern bool enable_rtl_dump_file (void);
> +extern void dump_symtab_node (dump_flags_t, symtab_node *);
>  
>  template<unsigned int N, typename C>
>  void dump_dec (dump_flags_t, const poly_int<N, C> &);
>  extern void dump_dec (dump_flags_t, const poly_wide_int &, signop);
>  extern void dump_hex (dump_flags_t, const poly_wide_int &);
>  
> -/* In tree-dump.c  */
> -extern void dump_node (const_tree, dump_flags_t, FILE *);
> -
> -/* In combine.c  */
> -extern void dump_combine_total_stats (FILE *);
> -/* In cfghooks.c  */
> -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> -
> -/* Global variables used to communicate with passes.  */
> -extern FILE *dump_file;
> -extern dump_flags_t dump_flags;
> -extern const char *dump_file_name;
> -
> -extern bool dumps_are_enabled;
> -
> -extern void set_dump_file (FILE *new_dump_file);
> -
> -/* Return true if any of the dumps is enabled, false otherwise. */
> -static inline bool
> -dump_enabled_p (void)
> -{
> -  return dumps_are_enabled;
> -}
> +extern void dumpfile_ensure_any_optinfo_are_flushed ();
>  
>  /* Managing nested scopes, so that dumps can express the call chain
>     leading to a dump message.  */
> @@ -505,8 +524,23 @@ class auto_dump_scope
>  #define AUTO_DUMP_SCOPE(NAME, LOC) \
>    auto_dump_scope scope (NAME, LOC)
>  
> +extern void dump_function (int phase, tree fn);
> +extern void print_combine_total_stats (void);
> +extern bool enable_rtl_dump_file (void);
> +
> +/* In tree-dump.c  */
> +extern void dump_node (const_tree, dump_flags_t, FILE *);
> +
> +/* In combine.c  */
> +extern void dump_combine_total_stats (FILE *);
> +/* In cfghooks.c  */
> +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> +
>  namespace gcc {
>  
> +/* A class for managing all of the various dump files used by the
> +   optimization passes.  */
> +
>  class dump_manager
>  {
>  public:
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> new file mode 100644
> index 0000000..6f224bc
> --- /dev/null
> +++ b/gcc/optinfo.cc
> @@ -0,0 +1,236 @@
> +/* Optimization information.
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT
> ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "cgraph.h"
> +#include "selftest.h"
> +
> +/* optinfo_item's ctor.  */
> +
> +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)
> +{
> +}
> +
> +/* optinfo_item's dtor.  */
> +
> +optinfo_item::~optinfo_item ()
> +{
> +  if (m_owned)
> +    free (m_text);
> +}
> +
> +/* Get a string from KIND.  */
> +
> +const char *
> +optinfo_kind_to_string (enum optinfo_kind kind)
> +{
> +  switch (kind)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case OPTINFO_KIND_SUCCESS:
> +      return "success";
> +    case OPTINFO_KIND_FAILURE:
> +      return "failure";
> +    case OPTINFO_KIND_NOTE:
> +      return "note";
> +    case OPTINFO_KIND_SCOPE:
> +      return "scope";
> +    }
> +}
> +
> +/* optinfo's dtor.  */
> +
> +optinfo::~optinfo ()
> +{
> +  /* Cleanup.  */
> +  unsigned i;
> +  optinfo_item *item;
> +  FOR_EACH_VEC_ELT (m_items, i, item)
> +    delete item;
> +}
> +
> +/* Emit the optinfo to all of the active destinations.  */
> +
> +void
> +optinfo::emit ()
> +{
> +  /* currently this is a no-op.  */
> +}
> +
> +/* Update the optinfo's kind based on DUMP_KIND.  */
> +
> +void
> +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> +{
> +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> +    m_kind = OPTINFO_KIND_SUCCESS;
> +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> +    m_kind = OPTINFO_KIND_FAILURE;
> +  else if (dump_kind & MSG_NOTE)
> +    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.  */
> +
> +bool optinfo_enabled_p ()
> +{
> +  /* Currently no destinations are implemented, just a hook for
> +     selftests.  */
> +  return dump_context::get ().forcibly_enable_optinfo_p ();
> +}
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +bool optinfo_wants_inlining_info_p ()
> +{
> +  return false;
> +}
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> new file mode 100644
> index 0000000..5bdb9eb
> --- /dev/null
> +++ b/gcc/optinfo.h
> @@ -0,0 +1,203 @@
> +/* Optimization information.
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT
> ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
> License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_OPTINFO_H
> +#define GCC_OPTINFO_H
> +
> +/* An "optinfo" is a bundle of information describing part of an
> +   optimization, which can be emitted to zero or more of several
> +   destinations, such as:
> +
> +   * as a "remark" through the diagnostics subsystem
> +
> +   * saved to a file as an "optimization record"
> +
> +   Currently no such destinations are implemented.
> +
> +   They are generated in response to calls to the "dump_*" API in
> +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> +   into a pending optinfo instance, with a "dump_*_loc" starting a
> new
> +   optinfo instance.
> +
> +   The data sent to the dump calls are captured within the pending
> optinfo
> +   instance as a sequence of optinfo_items.  For example, given:
> +
> +      if (dump_enabled_p ())
> +        {
> +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> +                           "not vectorized: live stmt not supported:
> ");
> +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt,
> 0);
> +        }
> +
> +   the "dump_printf_loc" call begins a new optinfo containing two
> items:
> +   (1) a text item containing "not vectorized: live stmt not
> supported: "
> +   (2) a gimple item for "stmt"
> +
> +   Dump destinations are thus able to access rich metadata about the
> +   items when the optinfo is emitted to them, rather than just
> having plain
> +   text.  For example, when saving the above optinfo to a file as an
> +   "optimization record", the record could capture the source
> location of
> +   "stmt" above, rather than just its textual form.
> +
> +   The currently pending optinfo is emitted and deleted:
> +   * each time a "dump_*_loc" call occurs (which starts the next
> optinfo), or
> +   * when the dump files are changed (at the end of a pass)
> +
> +   Dumping to an optinfo instance is non-trivial (due to building
> optinfo_item
> +   instances), so all usage should be guarded by
> +
> +     if (optinfo_enabled_p ())
> +
> +   which is off by default.  */
> +
> +
> +/* Forward decls.  */
> +struct opt_pass;
> +class optinfo_item; /* optinfo-internal.h.  */
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +extern bool optinfo_enabled_p ();
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +extern bool optinfo_wants_inlining_info_p ();
> +
> +/* The various kinds of optinfo.  */
> +
> +enum optinfo_kind
> +{
> +  OPTINFO_KIND_SUCCESS,
> +  OPTINFO_KIND_FAILURE,
> +  OPTINFO_KIND_NOTE,
> +  OPTINFO_KIND_SCOPE
> +};
> +
> +extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
> +
> +/* A bundle of information describing part of an optimization.  */
> +
> +class optinfo
> +{
> +  friend class dump_context;
> +
> + public:
> +  optinfo (const dump_location_t &loc,
> +	   enum optinfo_kind kind,
> +	   opt_pass *pass)
> +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> +  {}
> +  ~optinfo ();
> +
> +  const dump_user_location_t &
> +  get_user_location () const { return m_loc.get_user_location (); }
> +
> +  const dump_impl_location_t &
> +  get_impl_location () const { return m_loc.get_impl_location (); }
> +
> +  enum optinfo_kind get_kind () const { return m_kind; }
> +  opt_pass *get_pass () const { return m_pass; }
> +  unsigned int num_items () const { return m_items.length (); }
> +  const optinfo_item *get_item (unsigned int i) const { return
> m_items[i]; }
> +
> +  location_t get_location_t () const { return m_loc.get_location_t
> (); }
> +  profile_count get_count () const { return m_loc.get_count (); }
> +
> + 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;
> +  enum optinfo_kind m_kind;
> +  opt_pass *m_pass;
> +  auto_vec <optinfo_item *> m_items;
> +};
> +
> +/* An enum for discriminating between different kinds of
> optinfo_item.  */
> +
> +enum optinfo_item_kind
> +{
> +  OPTINFO_ITEM_KIND_TEXT,
> +  OPTINFO_ITEM_KIND_TREE,
> +  OPTINFO_ITEM_KIND_GIMPLE,
> +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> +};
> +
> +/* An item within an optinfo.  */
> +
> +class optinfo_item
> +{
> + public:
> +  optinfo_item (enum optinfo_item_kind kind, location_t location,
> +		char *text, bool owned);
> +  ~optinfo_item ();
> +
> +  enum optinfo_item_kind get_kind () const { return m_kind; }
> +  location_t get_location () const { return m_location; }
> +  const char *get_text () const { return m_text; }
> +
> + private:
> +  /* Metadata (e.g. for optimization records).  */
> +  enum optinfo_item_kind m_kind;
> +  location_t m_location;
> +
> +  /* The textual form of the item.  */
> +  char *m_text;
> +  bool m_owned;
> +};
> +
> +#endif /* #ifndef GCC_OPTINFO_H */
Richard Biener July 19, 2018, 12:17 p.m. | #2
On Wed, Jul 11, 2018 at 12:53 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> Changes relative to v4:
> * eliminated optinfo subclasses as discussed
> * eliminated optinfo-internal.h, moving what remained into optinfo.h
> * added support for dump_gimple_expr_loc and dump_gimple_expr
> * more selftests
>
> This patch implements a way to consolidate dump_* calls into
> optinfo objects, as enabling work towards being able to write out
> optimization records to a file (I'm focussing on that destination
> in this patch kit, rather than diagnostic remarks).
>
> The patch adds the support for building optinfo instances from dump_*
> calls, but leaves implementing any *users* of them to followup patches.
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

dump_context::get ().dump_symtab_node and friends is a bit
visually disturbing.  They are well-hidden so I guess I simply
look away for a second ;)

Otherwise looks very good now, thus...

... OK.

Thanks and sorry for the delay in reviewing.
Richard.

> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add optinfo.o.
>         * coretypes.h (class symtab_node): New forward decl.
>         (struct cgraph_node): New forward decl.
>         (class varpool_node): New forward decl.
>         * dump-context.h: New file.
>         * dumpfile.c: Include "optinfo.h", "dump-context.h", "cgraph.h",
>         "tree-pass.h".
>         (refresh_dumps_are_enabled): Use optinfo_enabled_p.
>         (set_dump_file): Call dumpfile_ensure_any_optinfo_are_flushed.
>         (set_alt_dump_file): Likewise.
>         (dump_context::~dump_context): New dtor.
>         (dump_gimple_stmt): Move implementation to...
>         (dump_context::dump_gimple_stmt): ...this new member function.
>         Add the stmt to any pending optinfo, creating one if need be.
>         (dump_gimple_stmt_loc): Move implementation to...
>         (dump_context::dump_gimple_stmt_loc): ...this new member function.
>         Start a new optinfo and add the stmt to it.
>         (dump_gimple_expr): Move implementation to...
>         (dump_context::dump_gimple_expr): ...this new member function.
>         Add the stmt to any pending optinfo, creating one if need be.
>         (dump_gimple_expr_loc): Move implementation to...
>         (dump_context::dump_gimple_expr_loc): ...this new member function.
>         Start a new optinfo and add the stmt to it.
>         (dump_generic_expr): Move implementation to...
>         (dump_context::dump_generic_expr): ...this new member function.
>         Add the tree to any pending optinfo, creating one if need be.
>         (dump_generic_expr_loc): Move implementation to...
>         (dump_context::dump_generic_expr_loc): ...this new member
>         function.  Add the tree to any pending optinfo, creating one if
>         need be.
>         (dump_printf): Move implementation to...
>         (dump_context::dump_printf_va): ...this new member function.  Add
>         the text to any pending optinfo, creating one if need be.
>         (dump_printf_loc): Move implementation to...
>         (dump_context::dump_printf_loc_va): ...this new member function.
>         Start a new optinfo and add the stmt to it.
>         (dump_dec): Move implementation to...
>         (dump_context::dump_dec): ...this new member function.  Add the
>         value to any pending optinfo, creating one if need be.
>         (dump_context::dump_symtab_node): New member function.
>         (dump_context::get_scope_depth): New member function.
>         (dump_context::begin_scope): New member function.
>         (dump_context::end_scope): New member function.
>         (dump_context::ensure_pending_optinfo): New member function.
>         (dump_context::begin_next_optinfo): New member function.
>         (dump_context::end_any_optinfo): New member function.
>         (dump_context::s_current): New global.
>         (dump_context::s_default): New global.
>         (dump_scope_depth): Delete global.
>         (dumpfile_ensure_any_optinfo_are_flushed): New function.
>         (dump_symtab_node): New function.
>         (get_dump_scope_depth): Reimplement in terms of dump_context.
>         (dump_begin_scope): Likewise.
>         (dump_end_scope): Likewise.
>         (selftest::temp_dump_context::temp_dump_context): New ctor.
>         (selftest::temp_dump_context::~temp_dump_context): New dtor.
>         (selftest::verify_item): New function.
>         (ASSERT_IS_TEXT): New macro.
>         (ASSERT_IS_TREE): New macro.
>         (ASSERT_IS_GIMPLE): New macro.
>         (selftest::test_capture_of_dump_calls): New test.
>         (selftest::dumpfile_c_tests): Call it.
>         * dumpfile.h (dump_printf, dump_printf_loc, dump_basic_block)
>         (dump_generic_expr_loc, dump_generic_expr, dump_gimple_stmt_loc)
>         (dump_gimple_stmt, dump_dec): Gather these related decls and add a
>         descriptive comment.
>         (dump_function, print_combine_total_stats, enable_rtl_dump_file)
>         (dump_node, dump_bb): Move these unrelated decls.
>         (class dump_manager): Add leading comment.
>         * optinfo.cc: New file.
>         * optinfo.h: New file.
> ---
>  gcc/Makefile.in    |   1 +
>  gcc/coretypes.h    |   7 +
>  gcc/dump-context.h | 138 +++++++++++++
>  gcc/dumpfile.c     | 597 +++++++++++++++++++++++++++++++++++++++++++++++++----
>  gcc/dumpfile.h     |  84 +++++---
>  gcc/optinfo.cc     | 236 +++++++++++++++++++++
>  gcc/optinfo.h      | 203 ++++++++++++++++++
>  7 files changed, 1200 insertions(+), 66 deletions(-)
>  create mode 100644 gcc/dump-context.h
>  create mode 100644 gcc/optinfo.cc
>  create mode 100644 gcc/optinfo.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 2a05a66..dd1dfc1 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1427,6 +1427,7 @@ OBJS = \
>         optabs-libfuncs.o \
>         optabs-query.o \
>         optabs-tree.o \
> +       optinfo.o \
>         options-save.o \
>         opts-global.o \
>         passes.o \
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index 283b4eb..ed0e825 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -134,6 +134,13 @@ struct gomp_single;
>  struct gomp_target;
>  struct gomp_teams;
>
> +/* Subclasses of symtab_node, using indentation to show the class
> +   hierarchy.  */
> +
> +class symtab_node;
> +  struct cgraph_node;
> +  class varpool_node;
> +
>  union section;
>  typedef union section section;
>  struct gcc_options;
> diff --git a/gcc/dump-context.h b/gcc/dump-context.h
> new file mode 100644
> index 0000000..a191e3a
> --- /dev/null
> +++ b/gcc/dump-context.h
> @@ -0,0 +1,138 @@
> +/* Support code for handling the various dump_* calls in dumpfile.h
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify
> +it under the terms of the GNU General Public License as published by
> +the Free Software Foundation; either version 3, or (at your option)
> +any later version.
> +
> +GCC is distributed in the hope that it will be useful,
> +but WITHOUT ANY WARRANTY; without even the implied warranty of
> +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> +GNU General Public License for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +
> +#ifndef GCC_DUMP_CONTEXT_H
> +#define GCC_DUMP_CONTEXT_H 1
> +
> +/* A class for handling the various dump_* calls.
> +
> +   In particular, this class has responsibility for consolidating
> +   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
> +   calls), and emitting them.
> +
> +   Putting this in a class (rather than as global state) allows
> +   for selftesting of this code.  */
> +
> +class dump_context
> +{
> +  friend class temp_dump_context;
> + public:
> +  static dump_context &get () { return *s_current; }
> +
> +  ~dump_context ();
> +
> +  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                        gimple *gs, int spc);
> +
> +  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                            const dump_location_t &loc,
> +                            dump_flags_t extra_dump_flags,
> +                            gimple *gs, int spc);
> +
> +  void dump_gimple_expr (dump_flags_t dump_kind,
> +                        dump_flags_t extra_dump_flags,
> +                        gimple *gs, int spc);
> +
> +  void dump_gimple_expr_loc (dump_flags_t dump_kind,
> +                           const dump_location_t &loc,
> +                           dump_flags_t extra_dump_flags,
> +                           gimple *gs,
> +                           int spc);
> +
> +  void dump_generic_expr (dump_flags_t dump_kind,
> +                         dump_flags_t extra_dump_flags,
> +                         tree t);
> +
> +  void dump_generic_expr_loc (dump_flags_t dump_kind,
> +                             const dump_location_t &loc,
> +                             dump_flags_t extra_dump_flags,
> +                             tree t);
> +
> +  void dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                      va_list ap) ATTRIBUTE_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);
> +
> +  template<unsigned int N, typename C>
> +  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
> +
> +  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
> +
> +  /* Managing nested scopes.  */
> +  unsigned int get_scope_depth () const;
> +  void begin_scope (const char *name, const dump_location_t &loc);
> +  void end_scope ();
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is true.  */
> +  bool forcibly_enable_optinfo_p () const
> +  {
> +    return m_forcibly_enable_optinfo;
> +  }
> +
> +  void end_any_optinfo ();
> +
> + private:
> +  optinfo &ensure_pending_optinfo ();
> +  optinfo &begin_next_optinfo (const dump_location_t &loc);
> +
> +  /* For use in selftests; if true then optinfo_enabled_p is true.  */
> +  bool m_forcibly_enable_optinfo;
> +
> +  /* The current nesting depth of dump scopes, for showing nesting
> +     via indentation).  */
> +  unsigned int m_scope_depth;
> +
> +  /* The optinfo currently being accumulated since the last dump_*_loc call,
> +     if any.  */
> +  optinfo *m_pending;
> +
> +  /* The currently active dump_context, for use by the dump_* API calls.  */
> +  static dump_context *s_current;
> +
> +  /* The default active context.  */
> +  static dump_context s_default;
> +};
> +
> +#if CHECKING_P
> +
> +/* An RAII-style class for use in selftests for temporarily using a different
> +   dump_context.  */
> +
> +class temp_dump_context
> +{
> + public:
> +  temp_dump_context (bool forcibly_enable_optinfo);
> +  ~temp_dump_context ();
> +
> +  /* Support for selftests.  */
> +  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
> +
> + private:
> +  dump_context m_context;
> +  dump_context *m_saved;
> +  bool m_saved_flag_remarks;
> +};
> +
> +#endif /* CHECKING_P */
> +
> +#endif /* GCC_DUMP_CONTEXT_H */
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 7ed1796..38f9539 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -33,6 +33,10 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h" /* for dump_user_location_t ctor.  */
>  #include "rtl.h" /* for dump_user_location_t ctor.  */
>  #include "selftest.h"
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "cgraph.h"
> +#include "tree-pass.h" /* for "current_pass".  */
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -64,7 +68,7 @@ bool dumps_are_enabled = false;
>  static void
>  refresh_dumps_are_enabled ()
>  {
> -  dumps_are_enabled = (dump_file || alt_dump_file);
> +  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
>  }
>
>  /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
> @@ -73,6 +77,7 @@ refresh_dumps_are_enabled ()
>  void
>  set_dump_file (FILE *new_dump_file)
>  {
> +  dumpfile_ensure_any_optinfo_are_flushed ();
>    dump_file = new_dump_file;
>    refresh_dumps_are_enabled ();
>  }
> @@ -83,6 +88,7 @@ set_dump_file (FILE *new_dump_file)
>  static void
>  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 ();
>  }
> @@ -458,25 +464,44 @@ dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
>      }
>  }
>
> +/* Implementation of dump_context member functions.  */
> +
> +/* dump_context's dtor.  */
> +
> +dump_context::~dump_context ()
> +{
> +  delete m_pending;
> +}
> +
>  /* Dump gimple statement GS with SPC indentation spaces and
>     EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
>
>  void
> -dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                 gimple *gs, int spc)
> +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);
> +
> +  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);
> +    }
>  }
>
>  /* Similar to dump_gimple_stmt, except additionally print source location.  */
>
>  void
> -dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
> +                                   const dump_location_t &loc,
> +                                   dump_flags_t extra_dump_flags,
> +                                   gimple *gs, int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -490,6 +515,13 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        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 gimple statement GS with SPC indentation spaces and
> @@ -497,21 +529,32 @@ dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>     Do not terminate with a newline or semicolon.  */
>
>  void
> -dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                 gimple *gs, int spc)
> +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);
> +
> +  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);
> +    }
>  }
>
>  /* Similar to dump_gimple_expr, except additionally print source location.  */
>
>  void
> -dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
> +                                   const dump_location_t &loc,
> +                                   dump_flags_t extra_dump_flags,
> +                                   gimple *gs,
> +                                   int spc)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -525,6 +568,13 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        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);
> +    }
>  }
>
>
> @@ -532,14 +582,22 @@ dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>     DUMP_KIND is enabled.  */
>
>  void
> -dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> -                  tree t)
> +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);
> +
> +  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);
> +    }
>  }
>
>
> @@ -547,8 +605,10 @@ dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
>     location.  */
>
>  void
> -dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                      dump_flags_t extra_dump_flags, tree t)
> +dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
> +                                    const dump_location_t &loc,
> +                                    dump_flags_t extra_dump_flags,
> +                                    tree t)
>  {
>    location_t srcloc = loc.get_location_t ();
>    if (dump_file && (dump_kind & pflags))
> @@ -562,53 +622,83 @@ dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>        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);
> +    }
>  }
>
>  /* Output a formatted message using FORMAT on appropriate dump streams.  */
>
>  void
> -dump_printf (dump_flags_t dump_kind, const char *format, ...)
> +dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
> +                             va_list ap)
>  {
>    if (dump_file && (dump_kind & pflags))
>      {
> -      va_list ap;
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      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 ap;
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      va_list aq;
> +      va_copy (aq, ap);
> +      vfprintf (alt_dump_file, format, aq);
> +      va_end (aq);
> +    }
> +
> +  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);
>      }
>  }
>
> -/* Similar to dump_printf, except source location is also printed.  */
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
>
>  void
> -dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> -                const char *format, ...)
> +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))
>      {
> -      va_list ap;
>        dump_loc (dump_kind, dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (dump_file, format, ap);
> -      va_end (ap);
> +      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 ap;
>        dump_loc (dump_kind, alt_dump_file, srcloc);
> -      va_start (ap, format);
> -      vfprintf (alt_dump_file, format, ap);
> -      va_end (ap);
> +      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);
>      }
>  }
>
> @@ -616,7 +706,7 @@ dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
>
>  template<unsigned int N, typename C>
>  void
> -dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>  {
>    STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
>    signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
> @@ -625,6 +715,224 @@ dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
>
>    if (alt_dump_file && (dump_kind & alt_flags))
>      print_dec (value, alt_dump_file, sgn);
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_poly_int<N,C> (value);
> +    }
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
> +
> +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 ());
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      optinfo &info = ensure_pending_optinfo ();
> +      info.handle_dump_file_kind (dump_kind);
> +      info.add_symtab_node (node);
> +    }
> +}
> +
> +/* Get the current dump scope-nesting depth.
> +   For use by -fopt-info (for showing nesting via indentation).  */
> +
> +unsigned int
> +dump_context::get_scope_depth () const
> +{
> +  return m_scope_depth;
> +}
> +
> +/* Push a nested dump scope.
> +   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
> +   destination, if any.
> +   Emit a "scope" optinfo if optinfos are enabled.
> +   Increment the scope depth.  */
> +
> +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);
> +    }
> +
> +  if (alt_dump_file)
> +    {
> +      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
> +      fprintf (alt_dump_file, "=== %s ===\n", name);
> +    }
> +
> +  if (optinfo_enabled_p ())
> +    {
> +      end_any_optinfo ();
> +      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
> +      info.add_printf ("=== %s ===", name);
> +      info.emit ();
> +    }
> +
> +  m_scope_depth++;
> +}
> +
> +/* Pop a nested dump scope.  */
> +
> +void
> +dump_context::end_scope ()
> +{
> +  end_any_optinfo ();
> +  m_scope_depth--;
> +}
> +
> +/* Return the optinfo currently being accumulated, creating one if
> +   necessary.  */
> +
> +optinfo &
> +dump_context::ensure_pending_optinfo ()
> +{
> +  if (!m_pending)
> +    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
> +  return *m_pending;
> +}
> +
> +/* Start a new optinfo and return it, ending any optinfo that was already
> +   accumulated.  */
> +
> +optinfo &
> +dump_context::begin_next_optinfo (const dump_location_t &loc)
> +{
> +  end_any_optinfo ();
> +  gcc_assert (m_pending == NULL);
> +  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
> +  return *m_pending;
> +}
> +
> +/* End any optinfo that has been accumulated within this context; emitting
> +   it to any destinations as appropriate - though none have currently been
> +   implemented.  */
> +
> +void
> +dump_context::end_any_optinfo ()
> +{
> +  if (m_pending)
> +    m_pending->emit ();
> +  delete m_pending;
> +  m_pending = NULL;
> +}
> +
> +/* The current singleton dump_context, and its default.  */
> +
> +dump_context *dump_context::s_current = &dump_context::s_default;
> +dump_context dump_context::s_default;
> +
> +/* Implementation of dump_* API calls, calling into dump_context
> +   member functions.  */
> +
> +/* Dump gimple statement GS with SPC indentation spaces and
> +   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
> +
> +void
> +dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                 gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_stmt, except additionally print source location.  */
> +
> +void
> +dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
> +                                            gs, spc);
> +}
> +
> +/* 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.  */
> +
> +void
> +dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                 gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
> +}
> +
> +/* Similar to dump_gimple_expr, except additionally print source location.  */
> +
> +void
> +dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                     dump_flags_t extra_dump_flags, gimple *gs, int spc)
> +{
> +  dump_context::get ().dump_gimple_expr_loc (dump_kind, loc, extra_dump_flags,
> +                                            gs, spc);
> +}
> +
> +/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
> +   DUMP_KIND is enabled.  */
> +
> +void
> +dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
> +                  tree t)
> +{
> +  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
> +}
> +
> +/* Similar to dump_generic_expr, except additionally print the source
> +   location.  */
> +
> +void
> +dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                      dump_flags_t extra_dump_flags, tree t)
> +{
> +  dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags,
> +                                             t);
> +}
> +
> +/* Output a formatted message using FORMAT on appropriate dump streams.  */
> +
> +void
> +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);
> +  va_end (ap);
> +}
> +
> +/* Similar to dump_printf, except source location is also printed, and
> +   dump location captured.  */
> +
> +void
> +dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
> +                const char *format, ...)
> +{
> +  va_list ap;
> +  va_start (ap, format);
> +  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
> +  va_end (ap);
> +}
> +
> +/* Output VALUE in decimal to appropriate dump streams.  */
> +
> +template<unsigned int N, typename C>
> +void
> +dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
> +{
> +  dump_context::get ().dump_dec (dump_kind, value);
>  }
>
>  template void dump_dec (dump_flags_t, const poly_uint16 &);
> @@ -655,29 +963,42 @@ dump_hex (dump_flags_t dump_kind, const poly_wide_int &value)
>      print_hex (value, alt_dump_file);
>  }
>
> -/* The current dump scope-nesting depth.  */
> +/* Emit and delete the currently pending optinfo, if there is one,
> +   without the caller needing to know about class dump_context.  */
> +
> +void
> +dumpfile_ensure_any_optinfo_are_flushed ()
> +{
> +  dump_context::get().end_any_optinfo ();
> +}
> +
> +/* Output the name of NODE on appropriate dump streams.  */
>
> -static int dump_scope_depth;
> +void
> +dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
> +{
> +  dump_context::get ().dump_symtab_node (dump_kind, node);
> +}
>
>  /* Get the current dump scope-nesting depth.
> -   For use by dump_*_loc (for showing nesting via indentation).  */
> +   For use by -fopt-info (for showing nesting via indentation).  */
>
>  unsigned int
>  get_dump_scope_depth ()
>  {
> -  return dump_scope_depth;
> +  return dump_context::get ().get_scope_depth ();
>  }
>
>  /* Push a nested dump scope.
>     Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
>     destination, if any.
> +   Emit a "scope" opinfo if optinfos are enabled.
>     Increment the scope depth.  */
>
>  void
>  dump_begin_scope (const char *name, const dump_location_t &loc)
>  {
> -  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
> -  dump_scope_depth++;
> +  dump_context::get ().begin_scope (name, loc);
>  }
>
>  /* Pop a nested dump scope.  */
> @@ -685,7 +1006,7 @@ dump_begin_scope (const char *name, const dump_location_t &loc)
>  void
>  dump_end_scope ()
>  {
> -  dump_scope_depth--;
> +  dump_context::get ().end_scope ();
>  }
>
>  /* Start a dump for PHASE. Store user-supplied dump flags in
> @@ -1238,6 +1559,24 @@ enable_rtl_dump_file (void)
>
>  #if CHECKING_P
>
> +/* 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)
> +: m_context (),
> +  m_saved (&dump_context ().get ())
> +{
> +  dump_context::s_current = &m_context;
> +  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
> +}
> +
> +/* temp_dump_context's dtor.  Restore the saved dump_context.  */
> +
> +temp_dump_context::~temp_dump_context ()
> +{
> +  dump_context::s_current = m_saved;
> +}
> +
>  namespace selftest {
>
>  /* Verify that the dump_location_t constructors capture the source location
> @@ -1274,12 +1613,188 @@ test_impl_location ()
>  #endif
>  }
>
> +/* Verify that ITEM has the expected values.  */
> +
> +static void
> +verify_item (const location &loc,
> +            const optinfo_item *item,
> +            enum optinfo_item_kind expected_kind,
> +            location_t expected_location,
> +            const char *expected_text)
> +{
> +  ASSERT_EQ_AT (loc, item->get_kind (), expected_kind);
> +  ASSERT_EQ_AT (loc, item->get_location (), expected_location);
> +  ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
> +}
> +
> +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
> +
> +#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT                                              \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \
> +                UNKNOWN_LOCATION, (EXPECTED_TEXT));                \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a tree item, with the expected values.  */
> +
> +#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT                                              \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \
> +                (EXPECTED_LOCATION), (EXPECTED_TEXT));     \
> +  SELFTEST_END_STMT
> +
> +/* Verify that ITEM is a gimple item, with the expected values.  */
> +
> +#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
> +  SELFTEST_BEGIN_STMT                                              \
> +    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \
> +                (EXPECTED_LOCATION), (EXPECTED_TEXT));     \
> +  SELFTEST_END_STMT
> +
> +/* Verify that calls to the dump_* API are captured and consolidated into
> +   optimization records. */
> +
> +static void
> +test_capture_of_dump_calls (const line_table_case &case_)
> +{
> +  /* Generate a location_t for testing.  */
> +  line_table_test ltt (case_);
> +  linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
> +  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);
> +
> +  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);
> +
> +      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.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr_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;");
> +    }
> +
> +    /* dump_gimple_expr.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_gimple_expr (MSG_NOTE, 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;");
> +    }
> +  }
> +
> +  /* 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);
> +      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +                OPTINFO_KIND_SUCCESS);
> +    }
> +
> +    /* MSG_MISSED_OPTIMIZATION.  */
> +    {
> +      temp_dump_context tmp (true);
> +      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
> +      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
> +                OPTINFO_KIND_FAILURE);
> +    }
> +  }
> +}
> +
>  /* Run all of the selftests within this file.  */
>
>  void
>  dumpfile_c_tests ()
>  {
>    test_impl_location ();
> +  for_each_line_table_case (test_capture_of_dump_calls);
>  }
>
>  } // namespace selftest
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 4a71ef7..3096a89 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,6 +420,48 @@ extern FILE *dump_begin (int, dump_flags_t *);
>  extern void dump_end (int, FILE *);
>  extern int opt_info_switch_p (const char *);
>  extern const char *dump_flag_name (int);
> +
> +/* Global variables used to communicate with passes.  */
> +extern FILE *dump_file;
> +extern dump_flags_t dump_flags;
> +extern const char *dump_file_name;
> +
> +extern bool dumps_are_enabled;
> +
> +extern void set_dump_file (FILE *new_dump_file);
> +
> +/* Return true if any of the dumps is enabled, false otherwise. */
> +static inline bool
> +dump_enabled_p (void)
> +{
> +  return dumps_are_enabled;
> +}
> +
> +/* 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:
> +
> +   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> +                                   |
> +                                   +--> (b) alt_dump_file
> +                                   |
> +                                   `--> (c) optinfo
> +                                            `---> optinfo destinations
> +
> +   For optinfos, the dump_*_loc mark the beginning of an optinfo
> +   instance: all subsequent dump_* calls are consolidated into
> +   that optinfo, until the next dump_*_loc call (or a change in
> +   dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed).
> +
> +   A group of dump_* calls should be guarded by:
> +
> +     if (dump_enabled_p ())
> +
> +   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_loc (dump_flags_t, const dump_location_t &,
>                              const char *, ...) ATTRIBUTE_PRINTF_3;
> @@ -434,37 +476,14 @@ extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr_loc (dump_flags_t, const dump_location_t &,
>                                   dump_flags_t, gimple *, int);
>  extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *, int);
> -extern void print_combine_total_stats (void);
> -extern bool enable_rtl_dump_file (void);
> +extern void dump_symtab_node (dump_flags_t, symtab_node *);
>
>  template<unsigned int N, typename C>
>  void dump_dec (dump_flags_t, const poly_int<N, C> &);
>  extern void dump_dec (dump_flags_t, const poly_wide_int &, signop);
>  extern void dump_hex (dump_flags_t, const poly_wide_int &);
>
> -/* In tree-dump.c  */
> -extern void dump_node (const_tree, dump_flags_t, FILE *);
> -
> -/* In combine.c  */
> -extern void dump_combine_total_stats (FILE *);
> -/* In cfghooks.c  */
> -extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> -
> -/* Global variables used to communicate with passes.  */
> -extern FILE *dump_file;
> -extern dump_flags_t dump_flags;
> -extern const char *dump_file_name;
> -
> -extern bool dumps_are_enabled;
> -
> -extern void set_dump_file (FILE *new_dump_file);
> -
> -/* Return true if any of the dumps is enabled, false otherwise. */
> -static inline bool
> -dump_enabled_p (void)
> -{
> -  return dumps_are_enabled;
> -}
> +extern void dumpfile_ensure_any_optinfo_are_flushed ();
>
>  /* Managing nested scopes, so that dumps can express the call chain
>     leading to a dump message.  */
> @@ -505,8 +524,23 @@ class auto_dump_scope
>  #define AUTO_DUMP_SCOPE(NAME, LOC) \
>    auto_dump_scope scope (NAME, LOC)
>
> +extern void dump_function (int phase, tree fn);
> +extern void print_combine_total_stats (void);
> +extern bool enable_rtl_dump_file (void);
> +
> +/* In tree-dump.c  */
> +extern void dump_node (const_tree, dump_flags_t, FILE *);
> +
> +/* In combine.c  */
> +extern void dump_combine_total_stats (FILE *);
> +/* In cfghooks.c  */
> +extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
> +
>  namespace gcc {
>
> +/* A class for managing all of the various dump files used by the
> +   optimization passes.  */
> +
>  class dump_manager
>  {
>  public:
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> new file mode 100644
> index 0000000..6f224bc
> --- /dev/null
> +++ b/gcc/optinfo.cc
> @@ -0,0 +1,236 @@
> +/* Optimization information.
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +
> +#include "optinfo.h"
> +#include "dump-context.h"
> +#include "pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "cgraph.h"
> +#include "selftest.h"
> +
> +/* optinfo_item's ctor.  */
> +
> +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)
> +{
> +}
> +
> +/* optinfo_item's dtor.  */
> +
> +optinfo_item::~optinfo_item ()
> +{
> +  if (m_owned)
> +    free (m_text);
> +}
> +
> +/* Get a string from KIND.  */
> +
> +const char *
> +optinfo_kind_to_string (enum optinfo_kind kind)
> +{
> +  switch (kind)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case OPTINFO_KIND_SUCCESS:
> +      return "success";
> +    case OPTINFO_KIND_FAILURE:
> +      return "failure";
> +    case OPTINFO_KIND_NOTE:
> +      return "note";
> +    case OPTINFO_KIND_SCOPE:
> +      return "scope";
> +    }
> +}
> +
> +/* optinfo's dtor.  */
> +
> +optinfo::~optinfo ()
> +{
> +  /* Cleanup.  */
> +  unsigned i;
> +  optinfo_item *item;
> +  FOR_EACH_VEC_ELT (m_items, i, item)
> +    delete item;
> +}
> +
> +/* Emit the optinfo to all of the active destinations.  */
> +
> +void
> +optinfo::emit ()
> +{
> +  /* currently this is a no-op.  */
> +}
> +
> +/* Update the optinfo's kind based on DUMP_KIND.  */
> +
> +void
> +optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
> +{
> +  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
> +    m_kind = OPTINFO_KIND_SUCCESS;
> +  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
> +    m_kind = OPTINFO_KIND_FAILURE;
> +  else if (dump_kind & MSG_NOTE)
> +    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.  */
> +
> +bool optinfo_enabled_p ()
> +{
> +  /* Currently no destinations are implemented, just a hook for
> +     selftests.  */
> +  return dump_context::get ().forcibly_enable_optinfo_p ();
> +}
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +bool optinfo_wants_inlining_info_p ()
> +{
> +  return false;
> +}
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> new file mode 100644
> index 0000000..5bdb9eb
> --- /dev/null
> +++ b/gcc/optinfo.h
> @@ -0,0 +1,203 @@
> +/* Optimization information.
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +   Contributed by David Malcolm <dmalcolm@redhat.com>.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it under
> +the terms of the GNU General Public License as published by the Free
> +Software Foundation; either version 3, or (at your option) any later
> +version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT ANY
> +WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#ifndef GCC_OPTINFO_H
> +#define GCC_OPTINFO_H
> +
> +/* An "optinfo" is a bundle of information describing part of an
> +   optimization, which can be emitted to zero or more of several
> +   destinations, such as:
> +
> +   * as a "remark" through the diagnostics subsystem
> +
> +   * saved to a file as an "optimization record"
> +
> +   Currently no such destinations are implemented.
> +
> +   They are generated in response to calls to the "dump_*" API in
> +   dumpfile.h; repeated calls to the "dump_*" API are consolidated
> +   into a pending optinfo instance, with a "dump_*_loc" starting a new
> +   optinfo instance.
> +
> +   The data sent to the dump calls are captured within the pending optinfo
> +   instance as a sequence of optinfo_items.  For example, given:
> +
> +      if (dump_enabled_p ())
> +        {
> +          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
> +                           "not vectorized: live stmt not supported: ");
> +          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
> +        }
> +
> +   the "dump_printf_loc" call begins a new optinfo containing two items:
> +   (1) a text item containing "not vectorized: live stmt not supported: "
> +   (2) a gimple item for "stmt"
> +
> +   Dump destinations are thus able to access rich metadata about the
> +   items when the optinfo is emitted to them, rather than just having plain
> +   text.  For example, when saving the above optinfo to a file as an
> +   "optimization record", the record could capture the source location of
> +   "stmt" above, rather than just its textual form.
> +
> +   The currently pending optinfo is emitted and deleted:
> +   * each time a "dump_*_loc" call occurs (which starts the next optinfo), or
> +   * when the dump files are changed (at the end of a pass)
> +
> +   Dumping to an optinfo instance is non-trivial (due to building optinfo_item
> +   instances), so all usage should be guarded by
> +
> +     if (optinfo_enabled_p ())
> +
> +   which is off by default.  */
> +
> +
> +/* Forward decls.  */
> +struct opt_pass;
> +class optinfo_item; /* optinfo-internal.h.  */
> +
> +/* Should optinfo instances be created?
> +   All creation of optinfos should be guarded by this predicate.
> +   Return true if any optinfo destinations are active.  */
> +
> +extern bool optinfo_enabled_p ();
> +
> +/* Return true if any of the active optinfo destinations make use
> +   of inlining information.
> +   (if true, then the information is preserved).  */
> +
> +extern bool optinfo_wants_inlining_info_p ();
> +
> +/* The various kinds of optinfo.  */
> +
> +enum optinfo_kind
> +{
> +  OPTINFO_KIND_SUCCESS,
> +  OPTINFO_KIND_FAILURE,
> +  OPTINFO_KIND_NOTE,
> +  OPTINFO_KIND_SCOPE
> +};
> +
> +extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
> +
> +/* A bundle of information describing part of an optimization.  */
> +
> +class optinfo
> +{
> +  friend class dump_context;
> +
> + public:
> +  optinfo (const dump_location_t &loc,
> +          enum optinfo_kind kind,
> +          opt_pass *pass)
> +  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
> +  {}
> +  ~optinfo ();
> +
> +  const dump_user_location_t &
> +  get_user_location () const { return m_loc.get_user_location (); }
> +
> +  const dump_impl_location_t &
> +  get_impl_location () const { return m_loc.get_impl_location (); }
> +
> +  enum optinfo_kind get_kind () const { return m_kind; }
> +  opt_pass *get_pass () const { return m_pass; }
> +  unsigned int num_items () const { return m_items.length (); }
> +  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
> +
> +  location_t get_location_t () const { return m_loc.get_location_t (); }
> +  profile_count get_count () const { return m_loc.get_count (); }
> +
> + 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;
> +  enum optinfo_kind m_kind;
> +  opt_pass *m_pass;
> +  auto_vec <optinfo_item *> m_items;
> +};
> +
> +/* An enum for discriminating between different kinds of optinfo_item.  */
> +
> +enum optinfo_item_kind
> +{
> +  OPTINFO_ITEM_KIND_TEXT,
> +  OPTINFO_ITEM_KIND_TREE,
> +  OPTINFO_ITEM_KIND_GIMPLE,
> +  OPTINFO_ITEM_KIND_SYMTAB_NODE
> +};
> +
> +/* An item within an optinfo.  */
> +
> +class optinfo_item
> +{
> + public:
> +  optinfo_item (enum optinfo_item_kind kind, location_t location,
> +               char *text, bool owned);
> +  ~optinfo_item ();
> +
> +  enum optinfo_item_kind get_kind () const { return m_kind; }
> +  location_t get_location () const { return m_location; }
> +  const char *get_text () const { return m_text; }
> +
> + private:
> +  /* Metadata (e.g. for optimization records).  */
> +  enum optinfo_item_kind m_kind;
> +  location_t m_location;
> +
> +  /* The textual form of the item.  */
> +  char *m_text;
> +  bool m_owned;
> +};
> +
> +#endif /* #ifndef GCC_OPTINFO_H */
> --
> 1.8.5.3
>

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 2a05a66..dd1dfc1 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1427,6 +1427,7 @@  OBJS = \
 	optabs-libfuncs.o \
 	optabs-query.o \
 	optabs-tree.o \
+	optinfo.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index 283b4eb..ed0e825 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -134,6 +134,13 @@  struct gomp_single;
 struct gomp_target;
 struct gomp_teams;
 
+/* Subclasses of symtab_node, using indentation to show the class
+   hierarchy.  */
+
+class symtab_node;
+  struct cgraph_node;
+  class varpool_node;
+
 union section;
 typedef union section section;
 struct gcc_options;
diff --git a/gcc/dump-context.h b/gcc/dump-context.h
new file mode 100644
index 0000000..a191e3a
--- /dev/null
+++ b/gcc/dump-context.h
@@ -0,0 +1,138 @@ 
+/* Support code for handling the various dump_* calls in dumpfile.h
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+
+#ifndef GCC_DUMP_CONTEXT_H
+#define GCC_DUMP_CONTEXT_H 1
+
+/* A class for handling the various dump_* calls.
+
+   In particular, this class has responsibility for consolidating
+   the "dump_*" calls into optinfo instances (delimited by "dump_*_loc"
+   calls), and emitting them.
+
+   Putting this in a class (rather than as global state) allows
+   for selftesting of this code.  */
+
+class dump_context
+{
+  friend class temp_dump_context;
+ public:
+  static dump_context &get () { return *s_current; }
+
+  ~dump_context ();
+
+  void dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_stmt_loc (dump_flags_t dump_kind,
+			     const dump_location_t &loc,
+			     dump_flags_t extra_dump_flags,
+			     gimple *gs, int spc);
+
+  void dump_gimple_expr (dump_flags_t dump_kind,
+			 dump_flags_t extra_dump_flags,
+			 gimple *gs, int spc);
+
+  void dump_gimple_expr_loc (dump_flags_t dump_kind,
+			    const dump_location_t &loc,
+			    dump_flags_t extra_dump_flags,
+			    gimple *gs,
+			    int spc);
+
+  void dump_generic_expr (dump_flags_t dump_kind,
+			  dump_flags_t extra_dump_flags,
+			  tree t);
+
+  void dump_generic_expr_loc (dump_flags_t dump_kind,
+			      const dump_location_t &loc,
+			      dump_flags_t extra_dump_flags,
+			      tree t);
+
+  void dump_printf_va (dump_flags_t dump_kind, const char *format,
+		       va_list ap) ATTRIBUTE_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);
+
+  template<unsigned int N, typename C>
+  void dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value);
+
+  void dump_symtab_node (dump_flags_t dump_kind, symtab_node *node);
+
+  /* Managing nested scopes.  */
+  unsigned int get_scope_depth () const;
+  void begin_scope (const char *name, const dump_location_t &loc);
+  void end_scope ();
+
+  /* For use in selftests; if true then optinfo_enabled_p is true.  */
+  bool forcibly_enable_optinfo_p () const
+  {
+    return m_forcibly_enable_optinfo;
+  }
+
+  void end_any_optinfo ();
+
+ private:
+  optinfo &ensure_pending_optinfo ();
+  optinfo &begin_next_optinfo (const dump_location_t &loc);
+
+  /* For use in selftests; if true then optinfo_enabled_p is true.  */
+  bool m_forcibly_enable_optinfo;
+
+  /* The current nesting depth of dump scopes, for showing nesting
+     via indentation).  */
+  unsigned int m_scope_depth;
+
+  /* The optinfo currently being accumulated since the last dump_*_loc call,
+     if any.  */
+  optinfo *m_pending;
+
+  /* The currently active dump_context, for use by the dump_* API calls.  */
+  static dump_context *s_current;
+
+  /* The default active context.  */
+  static dump_context s_default;
+};
+
+#if CHECKING_P
+
+/* An RAII-style class for use in selftests for temporarily using a different
+   dump_context.  */
+
+class temp_dump_context
+{
+ public:
+  temp_dump_context (bool forcibly_enable_optinfo);
+  ~temp_dump_context ();
+
+  /* Support for selftests.  */
+  optinfo *get_pending_optinfo () const { return m_context.m_pending; }
+
+ private:
+  dump_context m_context;
+  dump_context *m_saved;
+  bool m_saved_flag_remarks;
+};
+
+#endif /* CHECKING_P */
+
+#endif /* GCC_DUMP_CONTEXT_H */
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 7ed1796..38f9539 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -33,6 +33,10 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple.h" /* for dump_user_location_t ctor.  */
 #include "rtl.h" /* for dump_user_location_t ctor.  */
 #include "selftest.h"
+#include "optinfo.h"
+#include "dump-context.h"
+#include "cgraph.h"
+#include "tree-pass.h" /* for "current_pass".  */
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -64,7 +68,7 @@  bool dumps_are_enabled = false;
 static void
 refresh_dumps_are_enabled ()
 {
-  dumps_are_enabled = (dump_file || alt_dump_file);
+  dumps_are_enabled = (dump_file || alt_dump_file || optinfo_enabled_p ());
 }
 
 /* Set global "dump_file" to NEW_DUMP_FILE, refreshing the "dumps_are_enabled"
@@ -73,6 +77,7 @@  refresh_dumps_are_enabled ()
 void
 set_dump_file (FILE *new_dump_file)
 {
+  dumpfile_ensure_any_optinfo_are_flushed ();
   dump_file = new_dump_file;
   refresh_dumps_are_enabled ();
 }
@@ -83,6 +88,7 @@  set_dump_file (FILE *new_dump_file)
 static void
 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 ();
 }
@@ -458,25 +464,44 @@  dump_loc (dump_flags_t dump_kind, FILE *dfile, source_location loc)
     }
 }
 
+/* Implementation of dump_context member functions.  */
+
+/* dump_context's dtor.  */
+
+dump_context::~dump_context ()
+{
+  delete m_pending;
+}
+
 /* Dump gimple statement GS with SPC indentation spaces and
    EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
 
 void
-dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+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);
+
+  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);
+    }
 }
 
 /* Similar to dump_gimple_stmt, except additionally print source location.  */
 
 void
-dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_stmt_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs, int spc)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -490,6 +515,13 @@  dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       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 gimple statement GS with SPC indentation spaces and
@@ -497,21 +529,32 @@  dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
    Do not terminate with a newline or semicolon.  */
 
 void
-dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		  gimple *gs, int spc)
+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);
+
+  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);
+    }
 }
 
 /* Similar to dump_gimple_expr, except additionally print source location.  */
 
 void
-dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+dump_context::dump_gimple_expr_loc (dump_flags_t dump_kind,
+				    const dump_location_t &loc,
+				    dump_flags_t extra_dump_flags,
+				    gimple *gs,
+				    int spc)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -525,6 +568,13 @@  dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       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);
+    }
 }
 
 
@@ -532,14 +582,22 @@  dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
    DUMP_KIND is enabled.  */
 
 void
-dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
-		   tree t)
+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);
+
+  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);
+    }
 }
 
 
@@ -547,8 +605,10 @@  dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
    location.  */
 
 void
-dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		       dump_flags_t extra_dump_flags, tree t)
+dump_context::dump_generic_expr_loc (dump_flags_t dump_kind,
+				     const dump_location_t &loc,
+				     dump_flags_t extra_dump_flags,
+				     tree t)
 {
   location_t srcloc = loc.get_location_t ();
   if (dump_file && (dump_kind & pflags))
@@ -562,53 +622,83 @@  dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
       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);
+    }
 }
 
 /* Output a formatted message using FORMAT on appropriate dump streams.  */
 
 void
-dump_printf (dump_flags_t dump_kind, const char *format, ...)
+dump_context::dump_printf_va (dump_flags_t dump_kind, const char *format,
+			      va_list ap)
 {
   if (dump_file && (dump_kind & pflags))
     {
-      va_list ap;
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      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 ap;
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      va_list aq;
+      va_copy (aq, ap);
+      vfprintf (alt_dump_file, format, aq);
+      va_end (aq);
+    }
+
+  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);
     }
 }
 
-/* Similar to dump_printf, except source location is also printed.  */
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
 
 void
-dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
-		 const char *format, ...)
+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))
     {
-      va_list ap;
       dump_loc (dump_kind, dump_file, srcloc);
-      va_start (ap, format);
-      vfprintf (dump_file, format, ap);
-      va_end (ap);
+      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 ap;
       dump_loc (dump_kind, alt_dump_file, srcloc);
-      va_start (ap, format);
-      vfprintf (alt_dump_file, format, ap);
-      va_end (ap);
+      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);
     }
 }
 
@@ -616,7 +706,7 @@  dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
 
 template<unsigned int N, typename C>
 void
-dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+dump_context::dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 {
   STATIC_ASSERT (poly_coeff_traits<C>::signedness >= 0);
   signop sgn = poly_coeff_traits<C>::signedness ? SIGNED : UNSIGNED;
@@ -625,6 +715,224 @@  dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
 
   if (alt_dump_file && (dump_kind & alt_flags))
     print_dec (value, alt_dump_file, sgn);
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_poly_int<N,C> (value);
+    }
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
+
+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 ());
+
+  if (optinfo_enabled_p ())
+    {
+      optinfo &info = ensure_pending_optinfo ();
+      info.handle_dump_file_kind (dump_kind);
+      info.add_symtab_node (node);
+    }
+}
+
+/* Get the current dump scope-nesting depth.
+   For use by -fopt-info (for showing nesting via indentation).  */
+
+unsigned int
+dump_context::get_scope_depth () const
+{
+  return m_scope_depth;
+}
+
+/* Push a nested dump scope.
+   Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
+   destination, if any.
+   Emit a "scope" optinfo if optinfos are enabled.
+   Increment the scope depth.  */
+
+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);
+    }
+
+  if (alt_dump_file)
+    {
+      dump_loc (MSG_NOTE, alt_dump_file, loc.get_location_t ());
+      fprintf (alt_dump_file, "=== %s ===\n", name);
+    }
+
+  if (optinfo_enabled_p ())
+    {
+      end_any_optinfo ();
+      optinfo info (loc, OPTINFO_KIND_SCOPE, current_pass);
+      info.add_printf ("=== %s ===", name);
+      info.emit ();
+    }
+
+  m_scope_depth++;
+}
+
+/* Pop a nested dump scope.  */
+
+void
+dump_context::end_scope ()
+{
+  end_any_optinfo ();
+  m_scope_depth--;
+}
+
+/* Return the optinfo currently being accumulated, creating one if
+   necessary.  */
+
+optinfo &
+dump_context::ensure_pending_optinfo ()
+{
+  if (!m_pending)
+    return begin_next_optinfo (dump_location_t (dump_user_location_t ()));
+  return *m_pending;
+}
+
+/* Start a new optinfo and return it, ending any optinfo that was already
+   accumulated.  */
+
+optinfo &
+dump_context::begin_next_optinfo (const dump_location_t &loc)
+{
+  end_any_optinfo ();
+  gcc_assert (m_pending == NULL);
+  m_pending = new optinfo (loc, OPTINFO_KIND_NOTE, current_pass);
+  return *m_pending;
+}
+
+/* End any optinfo that has been accumulated within this context; emitting
+   it to any destinations as appropriate - though none have currently been
+   implemented.  */
+
+void
+dump_context::end_any_optinfo ()
+{
+  if (m_pending)
+    m_pending->emit ();
+  delete m_pending;
+  m_pending = NULL;
+}
+
+/* The current singleton dump_context, and its default.  */
+
+dump_context *dump_context::s_current = &dump_context::s_default;
+dump_context dump_context::s_default;
+
+/* Implementation of dump_* API calls, calling into dump_context
+   member functions.  */
+
+/* Dump gimple statement GS with SPC indentation spaces and
+   EXTRA_DUMP_FLAGS on the dump streams if DUMP_KIND is enabled.  */
+
+void
+dump_gimple_stmt (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_stmt, except additionally print source location.  */
+
+void
+dump_gimple_stmt_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_stmt_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* 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.  */
+
+void
+dump_gimple_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		  gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_expr (dump_kind, extra_dump_flags, gs, spc);
+}
+
+/* Similar to dump_gimple_expr, except additionally print source location.  */
+
+void
+dump_gimple_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		      dump_flags_t extra_dump_flags, gimple *gs, int spc)
+{
+  dump_context::get ().dump_gimple_expr_loc (dump_kind, loc, extra_dump_flags,
+					     gs, spc);
+}
+
+/* Dump expression tree T using EXTRA_DUMP_FLAGS on dump streams if
+   DUMP_KIND is enabled.  */
+
+void
+dump_generic_expr (dump_flags_t dump_kind, dump_flags_t extra_dump_flags,
+		   tree t)
+{
+  dump_context::get ().dump_generic_expr (dump_kind, extra_dump_flags, t);
+}
+
+/* Similar to dump_generic_expr, except additionally print the source
+   location.  */
+
+void
+dump_generic_expr_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		       dump_flags_t extra_dump_flags, tree t)
+{
+  dump_context::get ().dump_generic_expr_loc (dump_kind, loc, extra_dump_flags,
+					      t);
+}
+
+/* Output a formatted message using FORMAT on appropriate dump streams.  */
+
+void
+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);
+  va_end (ap);
+}
+
+/* Similar to dump_printf, except source location is also printed, and
+   dump location captured.  */
+
+void
+dump_printf_loc (dump_flags_t dump_kind, const dump_location_t &loc,
+		 const char *format, ...)
+{
+  va_list ap;
+  va_start (ap, format);
+  dump_context::get ().dump_printf_loc_va (dump_kind, loc, format, ap);
+  va_end (ap);
+}
+
+/* Output VALUE in decimal to appropriate dump streams.  */
+
+template<unsigned int N, typename C>
+void
+dump_dec (dump_flags_t dump_kind, const poly_int<N, C> &value)
+{
+  dump_context::get ().dump_dec (dump_kind, value);
 }
 
 template void dump_dec (dump_flags_t, const poly_uint16 &);
@@ -655,29 +963,42 @@  dump_hex (dump_flags_t dump_kind, const poly_wide_int &value)
     print_hex (value, alt_dump_file);
 }
 
-/* The current dump scope-nesting depth.  */
+/* Emit and delete the currently pending optinfo, if there is one,
+   without the caller needing to know about class dump_context.  */
+
+void
+dumpfile_ensure_any_optinfo_are_flushed ()
+{
+  dump_context::get().end_any_optinfo ();
+}
+
+/* Output the name of NODE on appropriate dump streams.  */
 
-static int dump_scope_depth;
+void
+dump_symtab_node (dump_flags_t dump_kind, symtab_node *node)
+{
+  dump_context::get ().dump_symtab_node (dump_kind, node);
+}
 
 /* Get the current dump scope-nesting depth.
-   For use by dump_*_loc (for showing nesting via indentation).  */
+   For use by -fopt-info (for showing nesting via indentation).  */
 
 unsigned int
 get_dump_scope_depth ()
 {
-  return dump_scope_depth;
+  return dump_context::get ().get_scope_depth ();
 }
 
 /* Push a nested dump scope.
    Print "=== NAME ===\n" to the dumpfile, if any, and to the -fopt-info
    destination, if any.
+   Emit a "scope" opinfo if optinfos are enabled.
    Increment the scope depth.  */
 
 void
 dump_begin_scope (const char *name, const dump_location_t &loc)
 {
-  dump_printf_loc (MSG_NOTE, loc, "=== %s ===\n", name);
-  dump_scope_depth++;
+  dump_context::get ().begin_scope (name, loc);
 }
 
 /* Pop a nested dump scope.  */
@@ -685,7 +1006,7 @@  dump_begin_scope (const char *name, const dump_location_t &loc)
 void
 dump_end_scope ()
 {
-  dump_scope_depth--;
+  dump_context::get ().end_scope ();
 }
 
 /* Start a dump for PHASE. Store user-supplied dump flags in
@@ -1238,6 +1559,24 @@  enable_rtl_dump_file (void)
 
 #if CHECKING_P
 
+/* 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)
+: m_context (),
+  m_saved (&dump_context ().get ())
+{
+  dump_context::s_current = &m_context;
+  m_context.m_forcibly_enable_optinfo = forcibly_enable_optinfo;
+}
+
+/* temp_dump_context's dtor.  Restore the saved dump_context.  */
+
+temp_dump_context::~temp_dump_context ()
+{
+  dump_context::s_current = m_saved;
+}
+
 namespace selftest {
 
 /* Verify that the dump_location_t constructors capture the source location
@@ -1274,12 +1613,188 @@  test_impl_location ()
 #endif
 }
 
+/* Verify that ITEM has the expected values.  */
+
+static void
+verify_item (const location &loc,
+	     const optinfo_item *item,
+	     enum optinfo_item_kind expected_kind,
+	     location_t expected_location,
+	     const char *expected_text)
+{
+  ASSERT_EQ_AT (loc, item->get_kind (), expected_kind);
+  ASSERT_EQ_AT (loc, item->get_location (), expected_location);
+  ASSERT_STREQ_AT (loc, item->get_text (), expected_text);
+}
+
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+#define ASSERT_IS_TEXT(ITEM, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TEXT, \
+		 UNKNOWN_LOCATION, (EXPECTED_TEXT));		    \
+  SELFTEST_END_STMT
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+#define ASSERT_IS_TREE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_TREE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+#define ASSERT_IS_GIMPLE(ITEM, EXPECTED_LOCATION, EXPECTED_TEXT) \
+  SELFTEST_BEGIN_STMT						    \
+    verify_item (SELFTEST_LOCATION, (ITEM), OPTINFO_ITEM_KIND_GIMPLE, \
+		 (EXPECTED_LOCATION), (EXPECTED_TEXT));	    \
+  SELFTEST_END_STMT
+
+/* Verify that calls to the dump_* API are captured and consolidated into
+   optimization records. */
+
+static void
+test_capture_of_dump_calls (const line_table_case &case_)
+{
+  /* Generate a location_t for testing.  */
+  line_table_test ltt (case_);
+  linemap_add (line_table, LC_ENTER, false, "test.txt", 0);
+  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);
+
+  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);
+
+      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.  */
+    {
+      temp_dump_context tmp (true);
+      dump_gimple_expr_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;");
+    }
+
+    /* dump_gimple_expr.  */
+    {
+      temp_dump_context tmp (true);
+      dump_gimple_expr (MSG_NOTE, 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;");
+    }
+  }
+
+  /* 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);
+      dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_SUCCESS);
+    }
+
+    /* MSG_MISSED_OPTIMIZATION.  */
+    {
+      temp_dump_context tmp (true);
+      dump_printf_loc (MSG_MISSED_OPTIMIZATION, loc, "test");
+      ASSERT_EQ (tmp.get_pending_optinfo ()->get_kind (),
+		 OPTINFO_KIND_FAILURE);
+    }
+  }
+}
+
 /* Run all of the selftests within this file.  */
 
 void
 dumpfile_c_tests ()
 {
   test_impl_location ();
+  for_each_line_table_case (test_capture_of_dump_calls);
 }
 
 } // namespace selftest
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 4a71ef7..3096a89 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -420,6 +420,48 @@  extern FILE *dump_begin (int, dump_flags_t *);
 extern void dump_end (int, FILE *);
 extern int opt_info_switch_p (const char *);
 extern const char *dump_flag_name (int);
+
+/* Global variables used to communicate with passes.  */
+extern FILE *dump_file;
+extern dump_flags_t dump_flags;
+extern const char *dump_file_name;
+
+extern bool dumps_are_enabled;
+
+extern void set_dump_file (FILE *new_dump_file);
+
+/* Return true if any of the dumps is enabled, false otherwise. */
+static inline bool
+dump_enabled_p (void)
+{
+  return dumps_are_enabled;
+}
+
+/* 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:
+
+   dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
+                                   |
+                                   +--> (b) alt_dump_file
+                                   |
+                                   `--> (c) optinfo
+                                            `---> optinfo destinations
+
+   For optinfos, the dump_*_loc mark the beginning of an optinfo
+   instance: all subsequent dump_* calls are consolidated into
+   that optinfo, until the next dump_*_loc call (or a change in
+   dump scope, or a call to dumpfile_ensure_any_optinfo_are_flushed).
+
+   A group of dump_* calls should be guarded by:
+
+     if (dump_enabled_p ())
+
+   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_loc (dump_flags_t, const dump_location_t &,
 			     const char *, ...) ATTRIBUTE_PRINTF_3;
@@ -434,37 +476,14 @@  extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
 extern void dump_gimple_expr_loc (dump_flags_t, const dump_location_t &,
 				  dump_flags_t, gimple *, int);
 extern void dump_gimple_expr (dump_flags_t, dump_flags_t, gimple *, int);
-extern void print_combine_total_stats (void);
-extern bool enable_rtl_dump_file (void);
+extern void dump_symtab_node (dump_flags_t, symtab_node *);
 
 template<unsigned int N, typename C>
 void dump_dec (dump_flags_t, const poly_int<N, C> &);
 extern void dump_dec (dump_flags_t, const poly_wide_int &, signop);
 extern void dump_hex (dump_flags_t, const poly_wide_int &);
 
-/* In tree-dump.c  */
-extern void dump_node (const_tree, dump_flags_t, FILE *);
-
-/* In combine.c  */
-extern void dump_combine_total_stats (FILE *);
-/* In cfghooks.c  */
-extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
-
-/* Global variables used to communicate with passes.  */
-extern FILE *dump_file;
-extern dump_flags_t dump_flags;
-extern const char *dump_file_name;
-
-extern bool dumps_are_enabled;
-
-extern void set_dump_file (FILE *new_dump_file);
-
-/* Return true if any of the dumps is enabled, false otherwise. */
-static inline bool
-dump_enabled_p (void)
-{
-  return dumps_are_enabled;
-}
+extern void dumpfile_ensure_any_optinfo_are_flushed ();
 
 /* Managing nested scopes, so that dumps can express the call chain
    leading to a dump message.  */
@@ -505,8 +524,23 @@  class auto_dump_scope
 #define AUTO_DUMP_SCOPE(NAME, LOC) \
   auto_dump_scope scope (NAME, LOC)
 
+extern void dump_function (int phase, tree fn);
+extern void print_combine_total_stats (void);
+extern bool enable_rtl_dump_file (void);
+
+/* In tree-dump.c  */
+extern void dump_node (const_tree, dump_flags_t, FILE *);
+
+/* In combine.c  */
+extern void dump_combine_total_stats (FILE *);
+/* In cfghooks.c  */
+extern void dump_bb (FILE *, basic_block, int, dump_flags_t);
+
 namespace gcc {
 
+/* A class for managing all of the various dump files used by the
+   optimization passes.  */
+
 class dump_manager
 {
 public:
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..6f224bc
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,236 @@ 
+/* Optimization information.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+
+#include "backend.h"
+#include "tree.h"
+#include "gimple.h"
+
+#include "optinfo.h"
+#include "dump-context.h"
+#include "pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+#include "selftest.h"
+
+/* optinfo_item's ctor.  */
+
+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)
+{
+}
+
+/* optinfo_item's dtor.  */
+
+optinfo_item::~optinfo_item ()
+{
+  if (m_owned)
+    free (m_text);
+}
+
+/* Get a string from KIND.  */
+
+const char *
+optinfo_kind_to_string (enum optinfo_kind kind)
+{
+  switch (kind)
+    {
+    default:
+      gcc_unreachable ();
+    case OPTINFO_KIND_SUCCESS:
+      return "success";
+    case OPTINFO_KIND_FAILURE:
+      return "failure";
+    case OPTINFO_KIND_NOTE:
+      return "note";
+    case OPTINFO_KIND_SCOPE:
+      return "scope";
+    }
+}
+
+/* optinfo's dtor.  */
+
+optinfo::~optinfo ()
+{
+  /* Cleanup.  */
+  unsigned i;
+  optinfo_item *item;
+  FOR_EACH_VEC_ELT (m_items, i, item)
+    delete item;
+}
+
+/* Emit the optinfo to all of the active destinations.  */
+
+void
+optinfo::emit ()
+{
+  /* currently this is a no-op.  */
+}
+
+/* Update the optinfo's kind based on DUMP_KIND.  */
+
+void
+optinfo::handle_dump_file_kind (dump_flags_t dump_kind)
+{
+  if (dump_kind & MSG_OPTIMIZED_LOCATIONS)
+    m_kind = OPTINFO_KIND_SUCCESS;
+  else if (dump_kind & MSG_MISSED_OPTIMIZATION)
+    m_kind = OPTINFO_KIND_FAILURE;
+  else if (dump_kind & MSG_NOTE)
+    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.  */
+
+bool optinfo_enabled_p ()
+{
+  /* Currently no destinations are implemented, just a hook for
+     selftests.  */
+  return dump_context::get ().forcibly_enable_optinfo_p ();
+}
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+bool optinfo_wants_inlining_info_p ()
+{
+  return false;
+}
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..5bdb9eb
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,203 @@ 
+/* Optimization information.
+   Copyright (C) 2018 Free Software Foundation, Inc.
+   Contributed by David Malcolm <dmalcolm@redhat.com>.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+#ifndef GCC_OPTINFO_H
+#define GCC_OPTINFO_H
+
+/* An "optinfo" is a bundle of information describing part of an
+   optimization, which can be emitted to zero or more of several
+   destinations, such as:
+
+   * as a "remark" through the diagnostics subsystem
+
+   * saved to a file as an "optimization record"
+
+   Currently no such destinations are implemented.
+
+   They are generated in response to calls to the "dump_*" API in
+   dumpfile.h; repeated calls to the "dump_*" API are consolidated
+   into a pending optinfo instance, with a "dump_*_loc" starting a new
+   optinfo instance.
+
+   The data sent to the dump calls are captured within the pending optinfo
+   instance as a sequence of optinfo_items.  For example, given:
+
+      if (dump_enabled_p ())
+        {
+          dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                           "not vectorized: live stmt not supported: ");
+          dump_gimple_stmt (MSG_MISSED_OPTIMIZATION, TDF_SLIM, stmt, 0);
+        }
+
+   the "dump_printf_loc" call begins a new optinfo containing two items:
+   (1) a text item containing "not vectorized: live stmt not supported: "
+   (2) a gimple item for "stmt"
+
+   Dump destinations are thus able to access rich metadata about the
+   items when the optinfo is emitted to them, rather than just having plain
+   text.  For example, when saving the above optinfo to a file as an
+   "optimization record", the record could capture the source location of
+   "stmt" above, rather than just its textual form.
+
+   The currently pending optinfo is emitted and deleted:
+   * each time a "dump_*_loc" call occurs (which starts the next optinfo), or
+   * when the dump files are changed (at the end of a pass)
+
+   Dumping to an optinfo instance is non-trivial (due to building optinfo_item
+   instances), so all usage should be guarded by
+
+     if (optinfo_enabled_p ())
+
+   which is off by default.  */
+
+
+/* Forward decls.  */
+struct opt_pass;
+class optinfo_item; /* optinfo-internal.h.  */
+
+/* Should optinfo instances be created?
+   All creation of optinfos should be guarded by this predicate.
+   Return true if any optinfo destinations are active.  */
+
+extern bool optinfo_enabled_p ();
+
+/* Return true if any of the active optinfo destinations make use
+   of inlining information.
+   (if true, then the information is preserved).  */
+
+extern bool optinfo_wants_inlining_info_p ();
+
+/* The various kinds of optinfo.  */
+
+enum optinfo_kind
+{
+  OPTINFO_KIND_SUCCESS,
+  OPTINFO_KIND_FAILURE,
+  OPTINFO_KIND_NOTE,
+  OPTINFO_KIND_SCOPE
+};
+
+extern const char *optinfo_kind_to_string (enum optinfo_kind kind);
+
+/* A bundle of information describing part of an optimization.  */
+
+class optinfo
+{
+  friend class dump_context;
+
+ public:
+  optinfo (const dump_location_t &loc,
+	   enum optinfo_kind kind,
+	   opt_pass *pass)
+  : m_loc (loc), m_kind (kind), m_pass (pass), m_items ()
+  {}
+  ~optinfo ();
+
+  const dump_user_location_t &
+  get_user_location () const { return m_loc.get_user_location (); }
+
+  const dump_impl_location_t &
+  get_impl_location () const { return m_loc.get_impl_location (); }
+
+  enum optinfo_kind get_kind () const { return m_kind; }
+  opt_pass *get_pass () const { return m_pass; }
+  unsigned int num_items () const { return m_items.length (); }
+  const optinfo_item *get_item (unsigned int i) const { return m_items[i]; }
+
+  location_t get_location_t () const { return m_loc.get_location_t (); }
+  profile_count get_count () const { return m_loc.get_count (); }
+
+ 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;
+  enum optinfo_kind m_kind;
+  opt_pass *m_pass;
+  auto_vec <optinfo_item *> m_items;
+};
+
+/* An enum for discriminating between different kinds of optinfo_item.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* An item within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  optinfo_item (enum optinfo_item_kind kind, location_t location,
+		char *text, bool owned);
+  ~optinfo_item ();
+
+  enum optinfo_item_kind get_kind () const { return m_kind; }
+  location_t get_location () const { return m_location; }
+  const char *get_text () const { return m_text; }
+
+ private:
+  /* Metadata (e.g. for optimization records).  */
+  enum optinfo_item_kind m_kind;
+  location_t m_location;
+
+  /* The textual form of the item.  */
+  char *m_text;
+  bool m_owned;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */