[1/2] Add "optinfo" framework

Message ID 1530567295-64306-2-git-send-email-dmalcolm@redhat.com
State New
Headers show
Series
  • v4: optinfo framework and remarks
Related show

Commit Message

David Malcolm July 2, 2018, 9:34 p.m.
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, or emit them as 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", "optinfo-internal.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.
	Convert param "loc" from location_t to const dump_location_t &.
	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.
	Convert param "loc" from location_t to const dump_location_t &.
	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::assert_is_text): New support function.
	(selftest::assert_is_tree): New support function.
	(selftest::assert_is_gimple): New support function.
	(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.
	* ggc-page.c (ggc_collect): Call
	dumpfile_ensure_any_optinfo_are_flushed.
	* optinfo-internal.h: New file.
	* optinfo.cc: New file.
	* optinfo.h: New file.
	* selftest-run-tests.c (selftest::run_tests): Call
	selftest::optinfo_cc_tests.
	* selftest.h (selftest::optinfo_cc_tests): New decl.
---
 gcc/Makefile.in          |   1 +
 gcc/coretypes.h          |   7 +
 gcc/dump-context.h       | 128 ++++++++++++
 gcc/dumpfile.c           | 498 +++++++++++++++++++++++++++++++++++++++++++----
 gcc/dumpfile.h           |  82 +++++---
 gcc/ggc-page.c           |   2 +
 gcc/optinfo-internal.h   | 148 ++++++++++++++
 gcc/optinfo.cc           | 251 ++++++++++++++++++++++++
 gcc/optinfo.h            | 175 +++++++++++++++++
 gcc/selftest-run-tests.c |   1 +
 gcc/selftest.h           |   1 +
 11 files changed, 1233 insertions(+), 61 deletions(-)
 create mode 100644 gcc/dump-context.h
 create mode 100644 gcc/optinfo-internal.h
 create mode 100644 gcc/optinfo.cc
 create mode 100644 gcc/optinfo.h

-- 
1.8.5.3

Comments

Richard Biener July 9, 2018, 1 p.m. | #1
On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com> wrote:
>

> 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, or emit them as 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?


Looks good overall, but ...

To "fix" the GC issue you'd need to capture all possibly interesting
information from tree/gimple while it is still in flight.  This _may_ be
necessary anyway since I remember writing code like

  fprintf (dump_file, "old: ");
  print_gimple_stmt (..., stmt);
  gimple_set_rhs1 (stmt, op);
  fprintf (dump_file, "new: ");
  print_gmple_stmt (..., stmt);
  fprintf (dump_file, "\n");

capturing interesting information means we know all targeted
optinfo channels, right?  And the optinfo consumers
need to handle "streams" of input and may not look back.

I've yet have to look at the 2nd patch but can you comment on
this?  How difficult is it to re-wire how the data flows to make
stmt re-use like the above possible?

Thanks,
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", "optinfo-internal.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.

>         Convert param "loc" from location_t to const dump_location_t &.

>         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.

>         Convert param "loc" from location_t to const dump_location_t &.

>         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::assert_is_text): New support function.

>         (selftest::assert_is_tree): New support function.

>         (selftest::assert_is_gimple): New support function.

>         (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.

>         * ggc-page.c (ggc_collect): Call

>         dumpfile_ensure_any_optinfo_are_flushed.

>         * optinfo-internal.h: New file.

>         * optinfo.cc: New file.

>         * optinfo.h: New file.

>         * selftest-run-tests.c (selftest::run_tests): Call

>         selftest::optinfo_cc_tests.

>         * selftest.h (selftest::optinfo_cc_tests): New decl.

> ---

>  gcc/Makefile.in          |   1 +

>  gcc/coretypes.h          |   7 +

>  gcc/dump-context.h       | 128 ++++++++++++

>  gcc/dumpfile.c           | 498 +++++++++++++++++++++++++++++++++++++++++++----

>  gcc/dumpfile.h           |  82 +++++---

>  gcc/ggc-page.c           |   2 +

>  gcc/optinfo-internal.h   | 148 ++++++++++++++

>  gcc/optinfo.cc           | 251 ++++++++++++++++++++++++

>  gcc/optinfo.h            | 175 +++++++++++++++++

>  gcc/selftest-run-tests.c |   1 +

>  gcc/selftest.h           |   1 +

>  11 files changed, 1233 insertions(+), 61 deletions(-)

>  create mode 100644 gcc/dump-context.h

>  create mode 100644 gcc/optinfo-internal.h

>  create mode 100644 gcc/optinfo.cc

>  create mode 100644 gcc/optinfo.h

>

> diff --git a/gcc/Makefile.in b/gcc/Makefile.in

> index 66c8b6e..7d36a77 100644

> --- a/gcc/Makefile.in

> +++ b/gcc/Makefile.in

> @@ -1426,6 +1426,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..753f714

> --- /dev/null

> +++ b/gcc/dump-context.h

> @@ -0,0 +1,128 @@

> +/* 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_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 5f69f9b..6e089ef 100644

> --- a/gcc/dumpfile.c

> +++ b/gcc/dumpfile.c

> @@ -33,6 +33,11 @@ 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".  */

> +#include "optinfo-internal.h" /* for selftests.  */

>

>  /* If non-NULL, return one past-the-end of the matching SUBPART of

>     the WHOLE string.  */

> @@ -64,7 +69,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 +78,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 +89,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 +465,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_stmt (gs, 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,20 +516,35 @@ 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_stmt (gs, extra_dump_flags);

> +    }

>  }

>

>  /* 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::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, extra_dump_flags);

> +    }

>  }

>

>

> @@ -511,8 +552,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))

> @@ -526,53 +569,82 @@ 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, 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);

>      }

>  }

>

> @@ -580,7 +652,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;

> @@ -589,6 +661,203 @@ 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 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 &);

> @@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const poly_uint64 &);

>  template void dump_dec (dump_flags_t, const poly_offset_int &);

>  template void dump_dec (dump_flags_t, const poly_widest_int &);

>

> -/* 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.  */

> @@ -627,7 +909,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

> @@ -1180,6 +1462,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

> @@ -1216,12 +1516,136 @@ test_impl_location ()

>  #endif

>  }

>

> +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */

> +

> +static void

> +assert_is_text (const optinfo_item *item, const char *expected_text)

> +{

> +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);

> +  const optinfo_item_text * text_item

> +    = static_cast <const optinfo_item_text *> (item);

> +  ASSERT_STREQ (text_item->get_text (), expected_text);

> +}

> +

> +/* Verify that ITEM is a tree item, with the expected values.  */

> +

> +static void

> +assert_is_tree (const optinfo_item *item, tree expected_tree,

> +               dump_flags_t expected_dump_flags)

> +{

> +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);

> +  const optinfo_item_tree * tree_item

> +    = static_cast <const optinfo_item_tree *> (item);

> +  ASSERT_EQ (tree_item->get_node (), expected_tree);

> +  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);

> +}

> +

> +/* Verify that ITEM is a gimple item, with the expected values.  */

> +

> +static void

> +assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,

> +                 dump_flags_t expected_dump_flags)

> +{

> +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);

> +  const optinfo_item_gimple * gimple_item

> +    = static_cast <const optinfo_item_gimple *> (item);

> +  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);

> +  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);

> +}

> +

> +/* Verify that calls to the dump_* API are captured and consolidated into

> +   optimization records. */

> +

> +static void

> +test_capture_of_dump_calls ()

> +{

> +  dump_location_t loc;

> +

> +  /* 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_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), integer_zero_node, TDF_SLIM);

> +  }

> +

> +  /* 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_kind (), OPTINFO_KIND_NOTE);

> +    ASSERT_EQ (info->num_items (), 1);

> +    assert_is_tree (info->get_item (0), integer_one_node, TDF_SLIM);

> +  }

> +

> +  /* Gimple.  */

> +  {

> +    greturn *stmt = gimple_build_return (NULL);

> +

> +    temp_dump_context tmp (true);

> +    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);

> +

> +    optinfo *info = tmp.get_pending_optinfo ();

> +    ASSERT_TRUE (info != NULL);

> +    ASSERT_EQ (info->num_items (), 1);

> +    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);

> +

> +    /* Verify that optinfo instances are flushed if a GC is about to

> +       happen (and thus don't need to be GTY-marked).

> +       We don't want them in the PCH file, but we don't want the

> +       items to have their data collected from under them.  */

> +    selftest::forcibly_ggc_collect ();

> +    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);

> +  }

> +

> +  /* 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 ();

> +  test_capture_of_dump_calls ();

>  }

>

>  } // namespace selftest

> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h

> index 0e588a6..899bb89 100644

> --- a/gcc/dumpfile.h

> +++ b/gcc/dumpfile.h

> @@ -420,30 +420,6 @@ 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);

> -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;

> -extern void dump_function (int phase, tree fn);

> -extern void dump_basic_block (dump_flags_t, basic_block, int);

> -extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,

> -                                  dump_flags_t, tree);

> -extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);

> -extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,

> -                                 dump_flags_t, gimple *, int);

> -extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);

> -extern void print_combine_total_stats (void);

> -extern bool enable_rtl_dump_file (void);

> -

> -template<unsigned int N, typename C>

> -void dump_dec (dump_flags_t, const poly_int<N, C> &);

> -

> -/* 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;

> @@ -461,6 +437,49 @@ 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;

> +extern void dump_function (int phase, tree fn);

> +extern void dump_basic_block (dump_flags_t, basic_block, int);

> +extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);

> +extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,

> +                                  dump_flags_t, tree);

> +extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,

> +                                 dump_flags_t, gimple *, int);

> +extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);

> +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 dumpfile_ensure_any_optinfo_are_flushed ();

> +

>  /* Managing nested scopes, so that dumps can express the call chain

>     leading to a dump message.  */

>

> @@ -500,8 +519,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/ggc-page.c b/gcc/ggc-page.c

> index 51783e5..f3a2119 100644

> --- a/gcc/ggc-page.c

> +++ b/gcc/ggc-page.c

> @@ -2177,6 +2177,8 @@ ggc_collect (void)

>    if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect)

>      return;

>

> +  dumpfile_ensure_any_optinfo_are_flushed ();

> +

>    timevar_push (TV_GC);

>    if (!quiet_flag)

>      fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024);

> diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h

> new file mode 100644

> index 0000000..8144174

> --- /dev/null

> +++ b/gcc/optinfo-internal.h

> @@ -0,0 +1,148 @@

> +/* Implementation details of optinfo.

> +   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_INTERNAL_H

> +#define GCC_OPTINFO_INTERNAL_H

> +

> +/* Multiple dispatch: there are various kinds of items within an optinfo,

> +   and various destinations to send optinfo to.

> +

> +   Handling this for now by exposing all of the item subclasses,

> +   and having the destinations handle them with "switch" statements.  */

> +

> +/* An enum for discriminating between optinfo_item subclasses.  */

> +

> +enum optinfo_item_kind

> +{

> +  OPTINFO_ITEM_KIND_TEXT,

> +  OPTINFO_ITEM_KIND_TREE,

> +  OPTINFO_ITEM_KIND_GIMPLE,

> +  OPTINFO_ITEM_KIND_SYMTAB_NODE

> +};

> +

> +/* Abstract base class for items within an optinfo.  */

> +

> +class optinfo_item

> +{

> + public:

> +  virtual ~optinfo_item () {}

> +

> +  virtual enum optinfo_item_kind get_kind () const = 0;

> +};

> +

> +/* optinfo_item subclasses.  */

> +

> +/* Item within an optinfo: text, either owned by the item

> +   (for optinfo_printf), or borrowed (for string literals).  */

> +

> +class optinfo_item_text : public optinfo_item

> +{

> + public:

> +  optinfo_item_text (char *text, bool owned)

> +  : m_text (text), m_owned (owned)

> +  {}

> +  ~optinfo_item_text ()

> +  {

> +    if (m_owned)

> +      free (m_text);

> +  }

> +

> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> +  {

> +    return OPTINFO_ITEM_KIND_TEXT;

> +  }

> +

> +  const char *get_text () const { return m_text; }

> +

> +  void trim_trailing_whitespace ();

> +

> + private:

> +  char *m_text;

> +  bool m_owned;

> +};

> +

> +/* Item within an optinfo: a tree, with dump flags.

> +   Note that this is not GTY-marked; see the description of

> +   class optinfo for a discussion of the interaction with the

> +   garbage-collector.  */

> +

> +class optinfo_item_tree : public optinfo_item

> +{

> + public:

> +  optinfo_item_tree (tree node, dump_flags_t dump_flags)

> +    : m_node (node), m_dump_flags (dump_flags)

> +  {}

> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> +  {

> +    return OPTINFO_ITEM_KIND_TREE;

> +  }

> +

> +  tree get_node () const { return m_node; }

> +  dump_flags_t get_flags () const { return m_dump_flags; }

> +

> + private:

> +  tree m_node;

> +  dump_flags_t m_dump_flags;

> +};

> +

> +/* Item within an optinfo: a gimple statement.

> +   Note that this is not GTY-marked; see the description of

> +   class optinfo for a discussion of the interaction with the

> +   garbage-collector.  */

> +

> +class optinfo_item_gimple : public optinfo_item

> +{

> + public:

> +  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)

> +    : m_stmt (stmt), m_dump_flags (dump_flags) {}

> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> +  {

> +    return OPTINFO_ITEM_KIND_GIMPLE;

> +  }

> +

> +  gimple *get_stmt () const { return m_stmt; }

> +  dump_flags_t get_flags () const { return m_dump_flags; }

> +

> + private:

> +  gimple *m_stmt;

> +  dump_flags_t m_dump_flags;

> +};

> +

> +/* Item within an optinfo: a symbol table node.

> +   Note that this is not GTY-marked; see the description of

> +   class optinfo for a discussion of the interaction with the

> +   garbage-collector.  */

> +

> +class optinfo_item_symtab_node : public optinfo_item

> +{

> + public:

> +  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}

> +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> +  {

> +    return OPTINFO_ITEM_KIND_SYMTAB_NODE;

> +  }

> +

> +  symtab_node *get_node () const { return m_node; }

> +

> + private:

> +  symtab_node *m_node;

> +};

> +

> +#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */

> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc

> new file mode 100644

> index 0000000..1da7d37

> --- /dev/null

> +++ b/gcc/optinfo.cc

> @@ -0,0 +1,251 @@

> +/* 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 "optinfo-internal.h"

> +#include "dump-context.h"

> +#include "selftest.h"

> +

> +/* Remove any trailing whitespace characters from this text item.

> +   Primarily for use in stripping trailing newline characters when

> +   emitting remarks (since the diagnostic subsystem doesn't expect

> +   trailing newlines in messages).  */

> +

> +void

> +optinfo_item_text::trim_trailing_whitespace ()

> +{

> +  size_t len = strlen (m_text);

> +  if (len == 0)

> +    return;

> +

> +  size_t new_len = len;

> +  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))

> +    new_len--;

> +

> +  if (new_len == len)

> +    return;

> +

> +  if (m_owned)

> +    m_text[new_len] = '\0';

> +  else

> +    {

> +      m_text = xstrndup (m_text, new_len);

> +      m_owned = true;

> +    }

> +}

> +

> +/* 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 ()

> +{

> +  /* Eliminate any trailing whitespace.  */

> +  while (m_items.length () > 0)

> +    {

> +      optinfo_item *last_item = m_items[m_items.length () - 1];

> +      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)

> +       break;

> +

> +      optinfo_item_text *last_text = (optinfo_item_text *)last_item;

> +      last_text->trim_trailing_whitespace ();

> +

> +      if (strlen (last_text->get_text ()) > 0)

> +       break;

> +

> +      m_items.pop ();

> +      delete last_item;

> +    }

> +

> +  /* 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_text (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_text (formatted_text, true);

> +  m_items.safe_push (item);

> +}

> +

> +/* Append a gimple statement to this optinfo.  */

> +

> +void

> +optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)

> +{

> +  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));

> +}

> +

> +/* Append a tree node to this optinfo.  */

> +

> +void

> +optinfo::add_tree (tree node, dump_flags_t dump_flags)

> +{

> +  m_items.safe_push (new optinfo_item_tree (node, dump_flags));

> +}

> +

> +/* Append a symbol table node to this optinfo.  */

> +

> +void

> +optinfo::add_symtab_node (symtab_node *node)

> +{

> +  m_items.safe_push (new optinfo_item_symtab_node (node));

> +}

> +

> +/* 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_text (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;

> +}

> +

> +#if CHECKING_P

> +

> +namespace selftest {

> +

> +/* Verify that optinfo_item_text::trim_trailing_whitespace turns

> +   INPUT into EXPECTED.  */

> +

> +static void

> +test_trim_trailing_whitespace (const char *input, const char *expected)

> +{

> +  optinfo_item_text item (const_cast <char *> (input), false);

> +  item.trim_trailing_whitespace ();

> +  ASSERT_STREQ (item.get_text (), expected);

> +}

> +

> +/* Run all of the selftests within this file.  */

> +

> +void

> +optinfo_cc_tests ()

> +{

> +  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */

> +  test_trim_trailing_whitespace ("", "");

> +  test_trim_trailing_whitespace ("\n", "");

> +  test_trim_trailing_whitespace ("foo", "foo");

> +  test_trim_trailing_whitespace ("foo\n", "foo");

> +  test_trim_trailing_whitespace ("foo\n\n", "foo");

> +  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");

> +}

> +

> +} // namespace selftest

> +

> +#endif /* CHECKING_P */

> diff --git a/gcc/optinfo.h b/gcc/optinfo.h

> new file mode 100644

> index 0000000..0d49823

> --- /dev/null

> +++ b/gcc/optinfo.h

> @@ -0,0 +1,175 @@

> +/* 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), or

> +   * when a garbage collection is about to happen.  This safety measure

> +     ensures that no GC happens during the lifetime of an optinfo instance,

> +     and thus that any optinfo_items within the optinfo instances can refer

> +     to GC-allocated objects without needing to be GTY-marked: they will never

> +     refer to collected garbage.  optinfo and optinfo_item are not GTY-marked

> +     as it would make no sense for them to be in PCH files.

> +

> +   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_stmt (gimple *stmt, 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;

> +};

> +

> +#endif /* #ifndef GCC_OPTINFO_H */

> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c

> index 7f4d6f3..989c50a 100644

> --- a/gcc/selftest-run-tests.c

> +++ b/gcc/selftest-run-tests.c

> @@ -72,6 +72,7 @@ selftest::run_tests ()

>    typed_splay_tree_c_tests ();

>    unique_ptr_tests_cc_tests ();

>    opt_proposer_c_tests ();

> +  optinfo_cc_tests ();

>

>    /* Mid-level data structures.  */

>    input_c_tests ();

> diff --git a/gcc/selftest.h b/gcc/selftest.h

> index 54fc488..48881c9 100644

> --- a/gcc/selftest.h

> +++ b/gcc/selftest.h

> @@ -228,6 +228,7 @@ extern void gimple_c_tests ();

>  extern void hash_map_tests_c_tests ();

>  extern void hash_set_tests_c_tests ();

>  extern void input_c_tests ();

> +extern void optinfo_cc_tests ();

>  extern void predict_c_tests ();

>  extern void pretty_print_c_tests ();

>  extern void read_rtl_function_c_tests ();

> --

> 1.8.5.3

>
David Malcolm July 10, 2018, 11 a.m. | #2
On Mon, 2018-07-09 at 15:00 +0200, Richard Biener wrote:
> On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com>

> wrote:

> > 

> > 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, or emit them as 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?

> 

> Looks good overall, but ...

> 

> To "fix" the GC issue you'd need to capture all possibly interesting

> information from tree/gimple while it is still in flight.  This _may_

> be

> necessary anyway since I remember writing code like

> 

>   fprintf (dump_file, "old: ");

>   print_gimple_stmt (..., stmt);

>   gimple_set_rhs1 (stmt, op);

>   fprintf (dump_file, "new: ");

>   print_gmple_stmt (..., stmt);

>   fprintf (dump_file, "\n");

> 

> capturing interesting information means we know all targeted

> optinfo channels, right?  And the optinfo consumers

> need to handle "streams" of input and may not look back.


> I've yet have to look at the 2nd patch but can you comment on

> this?  How difficult is it to re-wire how the data flows to make

> stmt re-use like the above possible?


I *think* it's doable: rather than capture, say, a gimple *, the
optinfo_item would capture the result of pp_gimple_stmt_1, plus some
metadata.  In fact, it would probably allow for removing the
optinfo_item subclasses, making optinfo_item concrete, containing
something like:

  /* Textual form.  */
  char *m_text;
  bool m_ownership_of_text;

  /* Metadata for optimization records.  */
  enum optinfo_item_kind m_kind;
  location_t m_location;

or somesuch.

I'll have a go at implementing this.

Thanks
Dave

> Thanks,

> 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", "optinfo-internal.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.

> >         Convert param "loc" from location_t to const

> > dump_location_t &.

> >         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.

> >         Convert param "loc" from location_t to const

> > dump_location_t &.

> >         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::assert_is_text): New support function.

> >         (selftest::assert_is_tree): New support function.

> >         (selftest::assert_is_gimple): New support function.

> >         (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.

> >         * ggc-page.c (ggc_collect): Call

> >         dumpfile_ensure_any_optinfo_are_flushed.

> >         * optinfo-internal.h: New file.

> >         * optinfo.cc: New file.

> >         * optinfo.h: New file.

> >         * selftest-run-tests.c (selftest::run_tests): Call

> >         selftest::optinfo_cc_tests.

> >         * selftest.h (selftest::optinfo_cc_tests): New decl.

> > ---

> >  gcc/Makefile.in          |   1 +

> >  gcc/coretypes.h          |   7 +

> >  gcc/dump-context.h       | 128 ++++++++++++

> >  gcc/dumpfile.c           | 498

> > +++++++++++++++++++++++++++++++++++++++++++----

> >  gcc/dumpfile.h           |  82 +++++---

> >  gcc/ggc-page.c           |   2 +

> >  gcc/optinfo-internal.h   | 148 ++++++++++++++

> >  gcc/optinfo.cc           | 251 ++++++++++++++++++++++++

> >  gcc/optinfo.h            | 175 +++++++++++++++++

> >  gcc/selftest-run-tests.c |   1 +

> >  gcc/selftest.h           |   1 +

> >  11 files changed, 1233 insertions(+), 61 deletions(-)

> >  create mode 100644 gcc/dump-context.h

> >  create mode 100644 gcc/optinfo-internal.h

> >  create mode 100644 gcc/optinfo.cc

> >  create mode 100644 gcc/optinfo.h

> > 

> > diff --git a/gcc/Makefile.in b/gcc/Makefile.in

> > index 66c8b6e..7d36a77 100644

> > --- a/gcc/Makefile.in

> > +++ b/gcc/Makefile.in

> > @@ -1426,6 +1426,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..753f714

> > --- /dev/null

> > +++ b/gcc/dump-context.h

> > @@ -0,0 +1,128 @@

> > +/* 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_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 5f69f9b..6e089ef 100644

> > --- a/gcc/dumpfile.c

> > +++ b/gcc/dumpfile.c

> > @@ -33,6 +33,11 @@ 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".  */

> > +#include "optinfo-internal.h" /* for selftests.  */

> > 

> >  /* If non-NULL, return one past-the-end of the matching SUBPART of

> >     the WHOLE string.  */

> > @@ -64,7 +69,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 +78,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 +89,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 +465,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_stmt (gs, 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,20 +516,35 @@ 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_stmt (gs, extra_dump_flags);

> > +    }

> >  }

> > 

> >  /* 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::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, extra_dump_flags);

> > +    }

> >  }

> > 

> > 

> > @@ -511,8 +552,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))

> > @@ -526,53 +569,82 @@ 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, 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);

> >      }

> >  }

> > 

> > @@ -580,7 +652,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;

> > @@ -589,6 +661,203 @@ 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 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 &);

> > @@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const

> > poly_uint64 &);

> >  template void dump_dec (dump_flags_t, const poly_offset_int &);

> >  template void dump_dec (dump_flags_t, const poly_widest_int &);

> > 

> > -/* 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.  */

> > @@ -627,7 +909,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

> > @@ -1180,6 +1462,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

> > @@ -1216,12 +1516,136 @@ test_impl_location ()

> >  #endif

> >  }

> > 

> > +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */

> > +

> > +static void

> > +assert_is_text (const optinfo_item *item, const char

> > *expected_text)

> > +{

> > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);

> > +  const optinfo_item_text * text_item

> > +    = static_cast <const optinfo_item_text *> (item);

> > +  ASSERT_STREQ (text_item->get_text (), expected_text);

> > +}

> > +

> > +/* Verify that ITEM is a tree item, with the expected values.  */

> > +

> > +static void

> > +assert_is_tree (const optinfo_item *item, tree expected_tree,

> > +               dump_flags_t expected_dump_flags)

> > +{

> > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);

> > +  const optinfo_item_tree * tree_item

> > +    = static_cast <const optinfo_item_tree *> (item);

> > +  ASSERT_EQ (tree_item->get_node (), expected_tree);

> > +  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);

> > +}

> > +

> > +/* Verify that ITEM is a gimple item, with the expected

> > values.  */

> > +

> > +static void

> > +assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,

> > +                 dump_flags_t expected_dump_flags)

> > +{

> > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);

> > +  const optinfo_item_gimple * gimple_item

> > +    = static_cast <const optinfo_item_gimple *> (item);

> > +  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);

> > +  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);

> > +}

> > +

> > +/* Verify that calls to the dump_* API are captured and

> > consolidated into

> > +   optimization records. */

> > +

> > +static void

> > +test_capture_of_dump_calls ()

> > +{

> > +  dump_location_t loc;

> > +

> > +  /* 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_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), integer_zero_node,

> > TDF_SLIM);

> > +  }

> > +

> > +  /* 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_kind (), OPTINFO_KIND_NOTE);

> > +    ASSERT_EQ (info->num_items (), 1);

> > +    assert_is_tree (info->get_item (0), integer_one_node,

> > TDF_SLIM);

> > +  }

> > +

> > +  /* Gimple.  */

> > +  {

> > +    greturn *stmt = gimple_build_return (NULL);

> > +

> > +    temp_dump_context tmp (true);

> > +    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);

> > +

> > +    optinfo *info = tmp.get_pending_optinfo ();

> > +    ASSERT_TRUE (info != NULL);

> > +    ASSERT_EQ (info->num_items (), 1);

> > +    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);

> > +

> > +    /* Verify that optinfo instances are flushed if a GC is about

> > to

> > +       happen (and thus don't need to be GTY-marked).

> > +       We don't want them in the PCH file, but we don't want the

> > +       items to have their data collected from under them.  */

> > +    selftest::forcibly_ggc_collect ();

> > +    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);

> > +  }

> > +

> > +  /* 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 ();

> > +  test_capture_of_dump_calls ();

> >  }

> > 

> >  } // namespace selftest

> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h

> > index 0e588a6..899bb89 100644

> > --- a/gcc/dumpfile.h

> > +++ b/gcc/dumpfile.h

> > @@ -420,30 +420,6 @@ 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);

> > -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;

> > -extern void dump_function (int phase, tree fn);

> > -extern void dump_basic_block (dump_flags_t, basic_block, int);

> > -extern void dump_generic_expr_loc (dump_flags_t, const

> > dump_location_t &,

> > -                                  dump_flags_t, tree);

> > -extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);

> > -extern void dump_gimple_stmt_loc (dump_flags_t, const

> > dump_location_t &,

> > -                                 dump_flags_t, gimple *, int);

> > -extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple

> > *, int);

> > -extern void print_combine_total_stats (void);

> > -extern bool enable_rtl_dump_file (void);

> > -

> > -template<unsigned int N, typename C>

> > -void dump_dec (dump_flags_t, const poly_int<N, C> &);

> > -

> > -/* 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;

> > @@ -461,6 +437,49 @@ 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;

> > +extern void dump_function (int phase, tree fn);

> > +extern void dump_basic_block (dump_flags_t, basic_block, int);

> > +extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);

> > +extern void dump_generic_expr_loc (dump_flags_t, const

> > dump_location_t &,

> > +                                  dump_flags_t, tree);

> > +extern void dump_gimple_stmt_loc (dump_flags_t, const

> > dump_location_t &,

> > +                                 dump_flags_t, gimple *, int);

> > +extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple

> > *, int);

> > +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 dumpfile_ensure_any_optinfo_are_flushed ();

> > +

> >  /* Managing nested scopes, so that dumps can express the call

> > chain

> >     leading to a dump message.  */

> > 

> > @@ -500,8 +519,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/ggc-page.c b/gcc/ggc-page.c

> > index 51783e5..f3a2119 100644

> > --- a/gcc/ggc-page.c

> > +++ b/gcc/ggc-page.c

> > @@ -2177,6 +2177,8 @@ ggc_collect (void)

> >    if (G.allocated < allocated_last_gc + min_expand &&

> > !ggc_force_collect)

> >      return;

> > 

> > +  dumpfile_ensure_any_optinfo_are_flushed ();

> > +

> >    timevar_push (TV_GC);

> >    if (!quiet_flag)

> >      fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated

> > / 1024);

> > diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h

> > new file mode 100644

> > index 0000000..8144174

> > --- /dev/null

> > +++ b/gcc/optinfo-internal.h

> > @@ -0,0 +1,148 @@

> > +/* Implementation details of optinfo.

> > +   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_INTERNAL_H

> > +#define GCC_OPTINFO_INTERNAL_H

> > +

> > +/* Multiple dispatch: there are various kinds of items within an

> > optinfo,

> > +   and various destinations to send optinfo to.

> > +

> > +   Handling this for now by exposing all of the item subclasses,

> > +   and having the destinations handle them with "switch"

> > statements.  */

> > +

> > +/* An enum for discriminating between optinfo_item subclasses.  */

> > +

> > +enum optinfo_item_kind

> > +{

> > +  OPTINFO_ITEM_KIND_TEXT,

> > +  OPTINFO_ITEM_KIND_TREE,

> > +  OPTINFO_ITEM_KIND_GIMPLE,

> > +  OPTINFO_ITEM_KIND_SYMTAB_NODE

> > +};

> > +

> > +/* Abstract base class for items within an optinfo.  */

> > +

> > +class optinfo_item

> > +{

> > + public:

> > +  virtual ~optinfo_item () {}

> > +

> > +  virtual enum optinfo_item_kind get_kind () const = 0;

> > +};

> > +

> > +/* optinfo_item subclasses.  */

> > +

> > +/* Item within an optinfo: text, either owned by the item

> > +   (for optinfo_printf), or borrowed (for string literals).  */

> > +

> > +class optinfo_item_text : public optinfo_item

> > +{

> > + public:

> > +  optinfo_item_text (char *text, bool owned)

> > +  : m_text (text), m_owned (owned)

> > +  {}

> > +  ~optinfo_item_text ()

> > +  {

> > +    if (m_owned)

> > +      free (m_text);

> > +  }

> > +

> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > +  {

> > +    return OPTINFO_ITEM_KIND_TEXT;

> > +  }

> > +

> > +  const char *get_text () const { return m_text; }

> > +

> > +  void trim_trailing_whitespace ();

> > +

> > + private:

> > +  char *m_text;

> > +  bool m_owned;

> > +};

> > +

> > +/* Item within an optinfo: a tree, with dump flags.

> > +   Note that this is not GTY-marked; see the description of

> > +   class optinfo for a discussion of the interaction with the

> > +   garbage-collector.  */

> > +

> > +class optinfo_item_tree : public optinfo_item

> > +{

> > + public:

> > +  optinfo_item_tree (tree node, dump_flags_t dump_flags)

> > +    : m_node (node), m_dump_flags (dump_flags)

> > +  {}

> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > +  {

> > +    return OPTINFO_ITEM_KIND_TREE;

> > +  }

> > +

> > +  tree get_node () const { return m_node; }

> > +  dump_flags_t get_flags () const { return m_dump_flags; }

> > +

> > + private:

> > +  tree m_node;

> > +  dump_flags_t m_dump_flags;

> > +};

> > +

> > +/* Item within an optinfo: a gimple statement.

> > +   Note that this is not GTY-marked; see the description of

> > +   class optinfo for a discussion of the interaction with the

> > +   garbage-collector.  */

> > +

> > +class optinfo_item_gimple : public optinfo_item

> > +{

> > + public:

> > +  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)

> > +    : m_stmt (stmt), m_dump_flags (dump_flags) {}

> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > +  {

> > +    return OPTINFO_ITEM_KIND_GIMPLE;

> > +  }

> > +

> > +  gimple *get_stmt () const { return m_stmt; }

> > +  dump_flags_t get_flags () const { return m_dump_flags; }

> > +

> > + private:

> > +  gimple *m_stmt;

> > +  dump_flags_t m_dump_flags;

> > +};

> > +

> > +/* Item within an optinfo: a symbol table node.

> > +   Note that this is not GTY-marked; see the description of

> > +   class optinfo for a discussion of the interaction with the

> > +   garbage-collector.  */

> > +

> > +class optinfo_item_symtab_node : public optinfo_item

> > +{

> > + public:

> > +  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}

> > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > +  {

> > +    return OPTINFO_ITEM_KIND_SYMTAB_NODE;

> > +  }

> > +

> > +  symtab_node *get_node () const { return m_node; }

> > +

> > + private:

> > +  symtab_node *m_node;

> > +};

> > +

> > +#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */

> > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc

> > new file mode 100644

> > index 0000000..1da7d37

> > --- /dev/null

> > +++ b/gcc/optinfo.cc

> > @@ -0,0 +1,251 @@

> > +/* 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 "optinfo-internal.h"

> > +#include "dump-context.h"

> > +#include "selftest.h"

> > +

> > +/* Remove any trailing whitespace characters from this text item.

> > +   Primarily for use in stripping trailing newline characters when

> > +   emitting remarks (since the diagnostic subsystem doesn't expect

> > +   trailing newlines in messages).  */

> > +

> > +void

> > +optinfo_item_text::trim_trailing_whitespace ()

> > +{

> > +  size_t len = strlen (m_text);

> > +  if (len == 0)

> > +    return;

> > +

> > +  size_t new_len = len;

> > +  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))

> > +    new_len--;

> > +

> > +  if (new_len == len)

> > +    return;

> > +

> > +  if (m_owned)

> > +    m_text[new_len] = '\0';

> > +  else

> > +    {

> > +      m_text = xstrndup (m_text, new_len);

> > +      m_owned = true;

> > +    }

> > +}

> > +

> > +/* 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 ()

> > +{

> > +  /* Eliminate any trailing whitespace.  */

> > +  while (m_items.length () > 0)

> > +    {

> > +      optinfo_item *last_item = m_items[m_items.length () - 1];

> > +      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)

> > +       break;

> > +

> > +      optinfo_item_text *last_text = (optinfo_item_text

> > *)last_item;

> > +      last_text->trim_trailing_whitespace ();

> > +

> > +      if (strlen (last_text->get_text ()) > 0)

> > +       break;

> > +

> > +      m_items.pop ();

> > +      delete last_item;

> > +    }

> > +

> > +  /* 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_text (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_text (formatted_text, true);

> > +  m_items.safe_push (item);

> > +}

> > +

> > +/* Append a gimple statement to this optinfo.  */

> > +

> > +void

> > +optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)

> > +{

> > +  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));

> > +}

> > +

> > +/* Append a tree node to this optinfo.  */

> > +

> > +void

> > +optinfo::add_tree (tree node, dump_flags_t dump_flags)

> > +{

> > +  m_items.safe_push (new optinfo_item_tree (node, dump_flags));

> > +}

> > +

> > +/* Append a symbol table node to this optinfo.  */

> > +

> > +void

> > +optinfo::add_symtab_node (symtab_node *node)

> > +{

> > +  m_items.safe_push (new optinfo_item_symtab_node (node));

> > +}

> > +

> > +/* 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_text (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;

> > +}

> > +

> > +#if CHECKING_P

> > +

> > +namespace selftest {

> > +

> > +/* Verify that optinfo_item_text::trim_trailing_whitespace turns

> > +   INPUT into EXPECTED.  */

> > +

> > +static void

> > +test_trim_trailing_whitespace (const char *input, const char

> > *expected)

> > +{

> > +  optinfo_item_text item (const_cast <char *> (input), false);

> > +  item.trim_trailing_whitespace ();

> > +  ASSERT_STREQ (item.get_text (), expected);

> > +}

> > +

> > +/* Run all of the selftests within this file.  */

> > +

> > +void

> > +optinfo_cc_tests ()

> > +{

> > +  /* Verify that optinfo_item_text::trim_trailing_whitespace

> > works.  */

> > +  test_trim_trailing_whitespace ("", "");

> > +  test_trim_trailing_whitespace ("\n", "");

> > +  test_trim_trailing_whitespace ("foo", "foo");

> > +  test_trim_trailing_whitespace ("foo\n", "foo");

> > +  test_trim_trailing_whitespace ("foo\n\n", "foo");

> > +  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");

> > +}

> > +

> > +} // namespace selftest

> > +

> > +#endif /* CHECKING_P */

> > diff --git a/gcc/optinfo.h b/gcc/optinfo.h

> > new file mode 100644

> > index 0000000..0d49823

> > --- /dev/null

> > +++ b/gcc/optinfo.h

> > @@ -0,0 +1,175 @@

> > +/* 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), or

> > +   * when a garbage collection is about to happen.  This safety

> > measure

> > +     ensures that no GC happens during the lifetime of an optinfo

> > instance,

> > +     and thus that any optinfo_items within the optinfo instances

> > can refer

> > +     to GC-allocated objects without needing to be GTY-marked:

> > they will never

> > +     refer to collected garbage.  optinfo and optinfo_item are not

> > GTY-marked

> > +     as it would make no sense for them to be in PCH files.

> > +

> > +   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_stmt (gimple *stmt, 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;

> > +};

> > +

> > +#endif /* #ifndef GCC_OPTINFO_H */

> > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c

> > index 7f4d6f3..989c50a 100644

> > --- a/gcc/selftest-run-tests.c

> > +++ b/gcc/selftest-run-tests.c

> > @@ -72,6 +72,7 @@ selftest::run_tests ()

> >    typed_splay_tree_c_tests ();

> >    unique_ptr_tests_cc_tests ();

> >    opt_proposer_c_tests ();

> > +  optinfo_cc_tests ();

> > 

> >    /* Mid-level data structures.  */

> >    input_c_tests ();

> > diff --git a/gcc/selftest.h b/gcc/selftest.h

> > index 54fc488..48881c9 100644

> > --- a/gcc/selftest.h

> > +++ b/gcc/selftest.h

> > @@ -228,6 +228,7 @@ extern void gimple_c_tests ();

> >  extern void hash_map_tests_c_tests ();

> >  extern void hash_set_tests_c_tests ();

> >  extern void input_c_tests ();

> > +extern void optinfo_cc_tests ();

> >  extern void predict_c_tests ();

> >  extern void pretty_print_c_tests ();

> >  extern void read_rtl_function_c_tests ();

> > --

> > 1.8.5.3

> >
Richard Biener July 10, 2018, 11:22 a.m. | #3
On Tue, Jul 10, 2018 at 1:00 PM David Malcolm <dmalcolm@redhat.com> wrote:
>

> On Mon, 2018-07-09 at 15:00 +0200, Richard Biener wrote:

> > On Mon, Jul 2, 2018 at 10:51 PM David Malcolm <dmalcolm@redhat.com>

> > wrote:

> > >

> > > 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, or emit them as 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?

> >

> > Looks good overall, but ...

> >

> > To "fix" the GC issue you'd need to capture all possibly interesting

> > information from tree/gimple while it is still in flight.  This _may_

> > be

> > necessary anyway since I remember writing code like

> >

> >   fprintf (dump_file, "old: ");

> >   print_gimple_stmt (..., stmt);

> >   gimple_set_rhs1 (stmt, op);

> >   fprintf (dump_file, "new: ");

> >   print_gmple_stmt (..., stmt);

> >   fprintf (dump_file, "\n");

> >

> > capturing interesting information means we know all targeted

> > optinfo channels, right?  And the optinfo consumers

> > need to handle "streams" of input and may not look back.

>

> > I've yet have to look at the 2nd patch but can you comment on

> > this?  How difficult is it to re-wire how the data flows to make

> > stmt re-use like the above possible?

>

> I *think* it's doable: rather than capture, say, a gimple *, the

> optinfo_item would capture the result of pp_gimple_stmt_1, plus some

> metadata.  In fact, it would probably allow for removing the

> optinfo_item subclasses, making optinfo_item concrete, containing

> something like:

>

>   /* Textual form.  */

>   char *m_text;

>   bool m_ownership_of_text;

>

>   /* Metadata for optimization records.  */

>   enum optinfo_item_kind m_kind;

>   location_t m_location;

>

> or somesuch.

>

> I'll have a go at implementing this.


Thanks, that would be much cleaner (if also a bit more fugly
when you need to debug things)

Richard.

> Thanks

> Dave

>

> > Thanks,

> > 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", "optinfo-internal.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.

> > >         Convert param "loc" from location_t to const

> > > dump_location_t &.

> > >         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.

> > >         Convert param "loc" from location_t to const

> > > dump_location_t &.

> > >         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::assert_is_text): New support function.

> > >         (selftest::assert_is_tree): New support function.

> > >         (selftest::assert_is_gimple): New support function.

> > >         (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.

> > >         * ggc-page.c (ggc_collect): Call

> > >         dumpfile_ensure_any_optinfo_are_flushed.

> > >         * optinfo-internal.h: New file.

> > >         * optinfo.cc: New file.

> > >         * optinfo.h: New file.

> > >         * selftest-run-tests.c (selftest::run_tests): Call

> > >         selftest::optinfo_cc_tests.

> > >         * selftest.h (selftest::optinfo_cc_tests): New decl.

> > > ---

> > >  gcc/Makefile.in          |   1 +

> > >  gcc/coretypes.h          |   7 +

> > >  gcc/dump-context.h       | 128 ++++++++++++

> > >  gcc/dumpfile.c           | 498

> > > +++++++++++++++++++++++++++++++++++++++++++----

> > >  gcc/dumpfile.h           |  82 +++++---

> > >  gcc/ggc-page.c           |   2 +

> > >  gcc/optinfo-internal.h   | 148 ++++++++++++++

> > >  gcc/optinfo.cc           | 251 ++++++++++++++++++++++++

> > >  gcc/optinfo.h            | 175 +++++++++++++++++

> > >  gcc/selftest-run-tests.c |   1 +

> > >  gcc/selftest.h           |   1 +

> > >  11 files changed, 1233 insertions(+), 61 deletions(-)

> > >  create mode 100644 gcc/dump-context.h

> > >  create mode 100644 gcc/optinfo-internal.h

> > >  create mode 100644 gcc/optinfo.cc

> > >  create mode 100644 gcc/optinfo.h

> > >

> > > diff --git a/gcc/Makefile.in b/gcc/Makefile.in

> > > index 66c8b6e..7d36a77 100644

> > > --- a/gcc/Makefile.in

> > > +++ b/gcc/Makefile.in

> > > @@ -1426,6 +1426,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..753f714

> > > --- /dev/null

> > > +++ b/gcc/dump-context.h

> > > @@ -0,0 +1,128 @@

> > > +/* 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_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 5f69f9b..6e089ef 100644

> > > --- a/gcc/dumpfile.c

> > > +++ b/gcc/dumpfile.c

> > > @@ -33,6 +33,11 @@ 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".  */

> > > +#include "optinfo-internal.h" /* for selftests.  */

> > >

> > >  /* If non-NULL, return one past-the-end of the matching SUBPART of

> > >     the WHOLE string.  */

> > > @@ -64,7 +69,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 +78,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 +89,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 +465,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_stmt (gs, 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,20 +516,35 @@ 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_stmt (gs, extra_dump_flags);

> > > +    }

> > >  }

> > >

> > >  /* 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::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, extra_dump_flags);

> > > +    }

> > >  }

> > >

> > >

> > > @@ -511,8 +552,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))

> > > @@ -526,53 +569,82 @@ 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, 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);

> > >      }

> > >  }

> > >

> > > @@ -580,7 +652,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;

> > > @@ -589,6 +661,203 @@ 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 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 &);

> > > @@ -597,29 +866,42 @@ template void dump_dec (dump_flags_t, const

> > > poly_uint64 &);

> > >  template void dump_dec (dump_flags_t, const poly_offset_int &);

> > >  template void dump_dec (dump_flags_t, const poly_widest_int &);

> > >

> > > -/* 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.  */

> > > @@ -627,7 +909,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

> > > @@ -1180,6 +1462,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

> > > @@ -1216,12 +1516,136 @@ test_impl_location ()

> > >  #endif

> > >  }

> > >

> > > +/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */

> > > +

> > > +static void

> > > +assert_is_text (const optinfo_item *item, const char

> > > *expected_text)

> > > +{

> > > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);

> > > +  const optinfo_item_text * text_item

> > > +    = static_cast <const optinfo_item_text *> (item);

> > > +  ASSERT_STREQ (text_item->get_text (), expected_text);

> > > +}

> > > +

> > > +/* Verify that ITEM is a tree item, with the expected values.  */

> > > +

> > > +static void

> > > +assert_is_tree (const optinfo_item *item, tree expected_tree,

> > > +               dump_flags_t expected_dump_flags)

> > > +{

> > > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);

> > > +  const optinfo_item_tree * tree_item

> > > +    = static_cast <const optinfo_item_tree *> (item);

> > > +  ASSERT_EQ (tree_item->get_node (), expected_tree);

> > > +  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);

> > > +}

> > > +

> > > +/* Verify that ITEM is a gimple item, with the expected

> > > values.  */

> > > +

> > > +static void

> > > +assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,

> > > +                 dump_flags_t expected_dump_flags)

> > > +{

> > > +  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);

> > > +  const optinfo_item_gimple * gimple_item

> > > +    = static_cast <const optinfo_item_gimple *> (item);

> > > +  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);

> > > +  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);

> > > +}

> > > +

> > > +/* Verify that calls to the dump_* API are captured and

> > > consolidated into

> > > +   optimization records. */

> > > +

> > > +static void

> > > +test_capture_of_dump_calls ()

> > > +{

> > > +  dump_location_t loc;

> > > +

> > > +  /* 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_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), integer_zero_node,

> > > TDF_SLIM);

> > > +  }

> > > +

> > > +  /* 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_kind (), OPTINFO_KIND_NOTE);

> > > +    ASSERT_EQ (info->num_items (), 1);

> > > +    assert_is_tree (info->get_item (0), integer_one_node,

> > > TDF_SLIM);

> > > +  }

> > > +

> > > +  /* Gimple.  */

> > > +  {

> > > +    greturn *stmt = gimple_build_return (NULL);

> > > +

> > > +    temp_dump_context tmp (true);

> > > +    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);

> > > +

> > > +    optinfo *info = tmp.get_pending_optinfo ();

> > > +    ASSERT_TRUE (info != NULL);

> > > +    ASSERT_EQ (info->num_items (), 1);

> > > +    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);

> > > +

> > > +    /* Verify that optinfo instances are flushed if a GC is about

> > > to

> > > +       happen (and thus don't need to be GTY-marked).

> > > +       We don't want them in the PCH file, but we don't want the

> > > +       items to have their data collected from under them.  */

> > > +    selftest::forcibly_ggc_collect ();

> > > +    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);

> > > +  }

> > > +

> > > +  /* 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 ();

> > > +  test_capture_of_dump_calls ();

> > >  }

> > >

> > >  } // namespace selftest

> > > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h

> > > index 0e588a6..899bb89 100644

> > > --- a/gcc/dumpfile.h

> > > +++ b/gcc/dumpfile.h

> > > @@ -420,30 +420,6 @@ 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);

> > > -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;

> > > -extern void dump_function (int phase, tree fn);

> > > -extern void dump_basic_block (dump_flags_t, basic_block, int);

> > > -extern void dump_generic_expr_loc (dump_flags_t, const

> > > dump_location_t &,

> > > -                                  dump_flags_t, tree);

> > > -extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);

> > > -extern void dump_gimple_stmt_loc (dump_flags_t, const

> > > dump_location_t &,

> > > -                                 dump_flags_t, gimple *, int);

> > > -extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple

> > > *, int);

> > > -extern void print_combine_total_stats (void);

> > > -extern bool enable_rtl_dump_file (void);

> > > -

> > > -template<unsigned int N, typename C>

> > > -void dump_dec (dump_flags_t, const poly_int<N, C> &);

> > > -

> > > -/* 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;

> > > @@ -461,6 +437,49 @@ 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;

> > > +extern void dump_function (int phase, tree fn);

> > > +extern void dump_basic_block (dump_flags_t, basic_block, int);

> > > +extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);

> > > +extern void dump_generic_expr_loc (dump_flags_t, const

> > > dump_location_t &,

> > > +                                  dump_flags_t, tree);

> > > +extern void dump_gimple_stmt_loc (dump_flags_t, const

> > > dump_location_t &,

> > > +                                 dump_flags_t, gimple *, int);

> > > +extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple

> > > *, int);

> > > +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 dumpfile_ensure_any_optinfo_are_flushed ();

> > > +

> > >  /* Managing nested scopes, so that dumps can express the call

> > > chain

> > >     leading to a dump message.  */

> > >

> > > @@ -500,8 +519,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/ggc-page.c b/gcc/ggc-page.c

> > > index 51783e5..f3a2119 100644

> > > --- a/gcc/ggc-page.c

> > > +++ b/gcc/ggc-page.c

> > > @@ -2177,6 +2177,8 @@ ggc_collect (void)

> > >    if (G.allocated < allocated_last_gc + min_expand &&

> > > !ggc_force_collect)

> > >      return;

> > >

> > > +  dumpfile_ensure_any_optinfo_are_flushed ();

> > > +

> > >    timevar_push (TV_GC);

> > >    if (!quiet_flag)

> > >      fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated

> > > / 1024);

> > > diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h

> > > new file mode 100644

> > > index 0000000..8144174

> > > --- /dev/null

> > > +++ b/gcc/optinfo-internal.h

> > > @@ -0,0 +1,148 @@

> > > +/* Implementation details of optinfo.

> > > +   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_INTERNAL_H

> > > +#define GCC_OPTINFO_INTERNAL_H

> > > +

> > > +/* Multiple dispatch: there are various kinds of items within an

> > > optinfo,

> > > +   and various destinations to send optinfo to.

> > > +

> > > +   Handling this for now by exposing all of the item subclasses,

> > > +   and having the destinations handle them with "switch"

> > > statements.  */

> > > +

> > > +/* An enum for discriminating between optinfo_item subclasses.  */

> > > +

> > > +enum optinfo_item_kind

> > > +{

> > > +  OPTINFO_ITEM_KIND_TEXT,

> > > +  OPTINFO_ITEM_KIND_TREE,

> > > +  OPTINFO_ITEM_KIND_GIMPLE,

> > > +  OPTINFO_ITEM_KIND_SYMTAB_NODE

> > > +};

> > > +

> > > +/* Abstract base class for items within an optinfo.  */

> > > +

> > > +class optinfo_item

> > > +{

> > > + public:

> > > +  virtual ~optinfo_item () {}

> > > +

> > > +  virtual enum optinfo_item_kind get_kind () const = 0;

> > > +};

> > > +

> > > +/* optinfo_item subclasses.  */

> > > +

> > > +/* Item within an optinfo: text, either owned by the item

> > > +   (for optinfo_printf), or borrowed (for string literals).  */

> > > +

> > > +class optinfo_item_text : public optinfo_item

> > > +{

> > > + public:

> > > +  optinfo_item_text (char *text, bool owned)

> > > +  : m_text (text), m_owned (owned)

> > > +  {}

> > > +  ~optinfo_item_text ()

> > > +  {

> > > +    if (m_owned)

> > > +      free (m_text);

> > > +  }

> > > +

> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > > +  {

> > > +    return OPTINFO_ITEM_KIND_TEXT;

> > > +  }

> > > +

> > > +  const char *get_text () const { return m_text; }

> > > +

> > > +  void trim_trailing_whitespace ();

> > > +

> > > + private:

> > > +  char *m_text;

> > > +  bool m_owned;

> > > +};

> > > +

> > > +/* Item within an optinfo: a tree, with dump flags.

> > > +   Note that this is not GTY-marked; see the description of

> > > +   class optinfo for a discussion of the interaction with the

> > > +   garbage-collector.  */

> > > +

> > > +class optinfo_item_tree : public optinfo_item

> > > +{

> > > + public:

> > > +  optinfo_item_tree (tree node, dump_flags_t dump_flags)

> > > +    : m_node (node), m_dump_flags (dump_flags)

> > > +  {}

> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > > +  {

> > > +    return OPTINFO_ITEM_KIND_TREE;

> > > +  }

> > > +

> > > +  tree get_node () const { return m_node; }

> > > +  dump_flags_t get_flags () const { return m_dump_flags; }

> > > +

> > > + private:

> > > +  tree m_node;

> > > +  dump_flags_t m_dump_flags;

> > > +};

> > > +

> > > +/* Item within an optinfo: a gimple statement.

> > > +   Note that this is not GTY-marked; see the description of

> > > +   class optinfo for a discussion of the interaction with the

> > > +   garbage-collector.  */

> > > +

> > > +class optinfo_item_gimple : public optinfo_item

> > > +{

> > > + public:

> > > +  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)

> > > +    : m_stmt (stmt), m_dump_flags (dump_flags) {}

> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > > +  {

> > > +    return OPTINFO_ITEM_KIND_GIMPLE;

> > > +  }

> > > +

> > > +  gimple *get_stmt () const { return m_stmt; }

> > > +  dump_flags_t get_flags () const { return m_dump_flags; }

> > > +

> > > + private:

> > > +  gimple *m_stmt;

> > > +  dump_flags_t m_dump_flags;

> > > +};

> > > +

> > > +/* Item within an optinfo: a symbol table node.

> > > +   Note that this is not GTY-marked; see the description of

> > > +   class optinfo for a discussion of the interaction with the

> > > +   garbage-collector.  */

> > > +

> > > +class optinfo_item_symtab_node : public optinfo_item

> > > +{

> > > + public:

> > > +  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}

> > > +  enum optinfo_item_kind get_kind () const FINAL OVERRIDE

> > > +  {

> > > +    return OPTINFO_ITEM_KIND_SYMTAB_NODE;

> > > +  }

> > > +

> > > +  symtab_node *get_node () const { return m_node; }

> > > +

> > > + private:

> > > +  symtab_node *m_node;

> > > +};

> > > +

> > > +#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */

> > > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc

> > > new file mode 100644

> > > index 0000000..1da7d37

> > > --- /dev/null

> > > +++ b/gcc/optinfo.cc

> > > @@ -0,0 +1,251 @@

> > > +/* 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 "optinfo-internal.h"

> > > +#include "dump-context.h"

> > > +#include "selftest.h"

> > > +

> > > +/* Remove any trailing whitespace characters from this text item.

> > > +   Primarily for use in stripping trailing newline characters when

> > > +   emitting remarks (since the diagnostic subsystem doesn't expect

> > > +   trailing newlines in messages).  */

> > > +

> > > +void

> > > +optinfo_item_text::trim_trailing_whitespace ()

> > > +{

> > > +  size_t len = strlen (m_text);

> > > +  if (len == 0)

> > > +    return;

> > > +

> > > +  size_t new_len = len;

> > > +  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))

> > > +    new_len--;

> > > +

> > > +  if (new_len == len)

> > > +    return;

> > > +

> > > +  if (m_owned)

> > > +    m_text[new_len] = '\0';

> > > +  else

> > > +    {

> > > +      m_text = xstrndup (m_text, new_len);

> > > +      m_owned = true;

> > > +    }

> > > +}

> > > +

> > > +/* 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 ()

> > > +{

> > > +  /* Eliminate any trailing whitespace.  */

> > > +  while (m_items.length () > 0)

> > > +    {

> > > +      optinfo_item *last_item = m_items[m_items.length () - 1];

> > > +      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)

> > > +       break;

> > > +

> > > +      optinfo_item_text *last_text = (optinfo_item_text

> > > *)last_item;

> > > +      last_text->trim_trailing_whitespace ();

> > > +

> > > +      if (strlen (last_text->get_text ()) > 0)

> > > +       break;

> > > +

> > > +      m_items.pop ();

> > > +      delete last_item;

> > > +    }

> > > +

> > > +  /* 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_text (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_text (formatted_text, true);

> > > +  m_items.safe_push (item);

> > > +}

> > > +

> > > +/* Append a gimple statement to this optinfo.  */

> > > +

> > > +void

> > > +optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)

> > > +{

> > > +  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));

> > > +}

> > > +

> > > +/* Append a tree node to this optinfo.  */

> > > +

> > > +void

> > > +optinfo::add_tree (tree node, dump_flags_t dump_flags)

> > > +{

> > > +  m_items.safe_push (new optinfo_item_tree (node, dump_flags));

> > > +}

> > > +

> > > +/* Append a symbol table node to this optinfo.  */

> > > +

> > > +void

> > > +optinfo::add_symtab_node (symtab_node *node)

> > > +{

> > > +  m_items.safe_push (new optinfo_item_symtab_node (node));

> > > +}

> > > +

> > > +/* 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_text (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;

> > > +}

> > > +

> > > +#if CHECKING_P

> > > +

> > > +namespace selftest {

> > > +

> > > +/* Verify that optinfo_item_text::trim_trailing_whitespace turns

> > > +   INPUT into EXPECTED.  */

> > > +

> > > +static void

> > > +test_trim_trailing_whitespace (const char *input, const char

> > > *expected)

> > > +{

> > > +  optinfo_item_text item (const_cast <char *> (input), false);

> > > +  item.trim_trailing_whitespace ();

> > > +  ASSERT_STREQ (item.get_text (), expected);

> > > +}

> > > +

> > > +/* Run all of the selftests within this file.  */

> > > +

> > > +void

> > > +optinfo_cc_tests ()

> > > +{

> > > +  /* Verify that optinfo_item_text::trim_trailing_whitespace

> > > works.  */

> > > +  test_trim_trailing_whitespace ("", "");

> > > +  test_trim_trailing_whitespace ("\n", "");

> > > +  test_trim_trailing_whitespace ("foo", "foo");

> > > +  test_trim_trailing_whitespace ("foo\n", "foo");

> > > +  test_trim_trailing_whitespace ("foo\n\n", "foo");

> > > +  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");

> > > +}

> > > +

> > > +} // namespace selftest

> > > +

> > > +#endif /* CHECKING_P */

> > > diff --git a/gcc/optinfo.h b/gcc/optinfo.h

> > > new file mode 100644

> > > index 0000000..0d49823

> > > --- /dev/null

> > > +++ b/gcc/optinfo.h

> > > @@ -0,0 +1,175 @@

> > > +/* 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), or

> > > +   * when a garbage collection is about to happen.  This safety

> > > measure

> > > +     ensures that no GC happens during the lifetime of an optinfo

> > > instance,

> > > +     and thus that any optinfo_items within the optinfo instances

> > > can refer

> > > +     to GC-allocated objects without needing to be GTY-marked:

> > > they will never

> > > +     refer to collected garbage.  optinfo and optinfo_item are not

> > > GTY-marked

> > > +     as it would make no sense for them to be in PCH files.

> > > +

> > > +   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_stmt (gimple *stmt, 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;

> > > +};

> > > +

> > > +#endif /* #ifndef GCC_OPTINFO_H */

> > > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c

> > > index 7f4d6f3..989c50a 100644

> > > --- a/gcc/selftest-run-tests.c

> > > +++ b/gcc/selftest-run-tests.c

> > > @@ -72,6 +72,7 @@ selftest::run_tests ()

> > >    typed_splay_tree_c_tests ();

> > >    unique_ptr_tests_cc_tests ();

> > >    opt_proposer_c_tests ();

> > > +  optinfo_cc_tests ();

> > >

> > >    /* Mid-level data structures.  */

> > >    input_c_tests ();

> > > diff --git a/gcc/selftest.h b/gcc/selftest.h

> > > index 54fc488..48881c9 100644

> > > --- a/gcc/selftest.h

> > > +++ b/gcc/selftest.h

> > > @@ -228,6 +228,7 @@ extern void gimple_c_tests ();

> > >  extern void hash_map_tests_c_tests ();

> > >  extern void hash_set_tests_c_tests ();

> > >  extern void input_c_tests ();

> > > +extern void optinfo_cc_tests ();

> > >  extern void predict_c_tests ();

> > >  extern void pretty_print_c_tests ();

> > >  extern void read_rtl_function_c_tests ();

> > > --

> > > 1.8.5.3

> > >

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index 66c8b6e..7d36a77 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1426,6 +1426,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..753f714
--- /dev/null
+++ b/gcc/dump-context.h
@@ -0,0 +1,128 @@ 
+/* 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_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 5f69f9b..6e089ef 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -33,6 +33,11 @@  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".  */
+#include "optinfo-internal.h" /* for selftests.  */
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -64,7 +69,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 +78,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 +89,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 +465,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_stmt (gs, 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,20 +516,35 @@  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_stmt (gs, extra_dump_flags);
+    }
 }
 
 /* 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::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, extra_dump_flags);
+    }
 }
 
 
@@ -511,8 +552,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))
@@ -526,53 +569,82 @@  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, 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);
     }
 }
 
@@ -580,7 +652,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;
@@ -589,6 +661,203 @@  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 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 &);
@@ -597,29 +866,42 @@  template void dump_dec (dump_flags_t, const poly_uint64 &);
 template void dump_dec (dump_flags_t, const poly_offset_int &);
 template void dump_dec (dump_flags_t, const poly_widest_int &);
 
-/* 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.  */
@@ -627,7 +909,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
@@ -1180,6 +1462,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
@@ -1216,12 +1516,136 @@  test_impl_location ()
 #endif
 }
 
+/* Verify that ITEM is a text item, with EXPECTED_TEXT.  */
+
+static void
+assert_is_text (const optinfo_item *item, const char *expected_text)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TEXT);
+  const optinfo_item_text * text_item
+    = static_cast <const optinfo_item_text *> (item);
+  ASSERT_STREQ (text_item->get_text (), expected_text);
+}
+
+/* Verify that ITEM is a tree item, with the expected values.  */
+
+static void
+assert_is_tree (const optinfo_item *item, tree expected_tree,
+		dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_TREE);
+  const optinfo_item_tree * tree_item
+    = static_cast <const optinfo_item_tree *> (item);
+  ASSERT_EQ (tree_item->get_node (), expected_tree);
+  ASSERT_EQ (tree_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that ITEM is a gimple item, with the expected values.  */
+
+static void
+assert_is_gimple (const optinfo_item *item, gimple *expected_stmt,
+		  dump_flags_t expected_dump_flags)
+{
+  ASSERT_EQ (item->get_kind (), OPTINFO_ITEM_KIND_GIMPLE);
+  const optinfo_item_gimple * gimple_item
+    = static_cast <const optinfo_item_gimple *> (item);
+  ASSERT_EQ (gimple_item->get_stmt (), expected_stmt);
+  ASSERT_EQ (gimple_item->get_flags (), expected_dump_flags);
+}
+
+/* Verify that calls to the dump_* API are captured and consolidated into
+   optimization records. */
+
+static void
+test_capture_of_dump_calls ()
+{
+  dump_location_t loc;
+
+  /* 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_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), integer_zero_node, TDF_SLIM);
+  }
+
+  /* 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_kind (), OPTINFO_KIND_NOTE);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_tree (info->get_item (0), integer_one_node, TDF_SLIM);
+  }
+
+  /* Gimple.  */
+  {
+    greturn *stmt = gimple_build_return (NULL);
+
+    temp_dump_context tmp (true);
+    dump_gimple_stmt_loc (MSG_NOTE, loc, TDF_SLIM, stmt, 0);
+
+    optinfo *info = tmp.get_pending_optinfo ();
+    ASSERT_TRUE (info != NULL);
+    ASSERT_EQ (info->num_items (), 1);
+    assert_is_gimple (info->get_item (0), stmt, TDF_SLIM);
+
+    /* Verify that optinfo instances are flushed if a GC is about to
+       happen (and thus don't need to be GTY-marked).
+       We don't want them in the PCH file, but we don't want the
+       items to have their data collected from under them.  */
+    selftest::forcibly_ggc_collect ();
+    ASSERT_TRUE (tmp.get_pending_optinfo () == NULL);
+  }
+
+  /* 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 ();
+  test_capture_of_dump_calls ();
 }
 
 } // namespace selftest
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 0e588a6..899bb89 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -420,30 +420,6 @@  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);
-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;
-extern void dump_function (int phase, tree fn);
-extern void dump_basic_block (dump_flags_t, basic_block, int);
-extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
-				   dump_flags_t, tree);
-extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
-extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
-				  dump_flags_t, gimple *, int);
-extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
-extern void print_combine_total_stats (void);
-extern bool enable_rtl_dump_file (void);
-
-template<unsigned int N, typename C>
-void dump_dec (dump_flags_t, const poly_int<N, C> &);
-
-/* 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;
@@ -461,6 +437,49 @@  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;
+extern void dump_function (int phase, tree fn);
+extern void dump_basic_block (dump_flags_t, basic_block, int);
+extern void dump_generic_expr (dump_flags_t, dump_flags_t, tree);
+extern void dump_generic_expr_loc (dump_flags_t, const dump_location_t &,
+				   dump_flags_t, tree);
+extern void dump_gimple_stmt_loc (dump_flags_t, const dump_location_t &,
+				  dump_flags_t, gimple *, int);
+extern void dump_gimple_stmt (dump_flags_t, dump_flags_t, gimple *, int);
+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 dumpfile_ensure_any_optinfo_are_flushed ();
+
 /* Managing nested scopes, so that dumps can express the call chain
    leading to a dump message.  */
 
@@ -500,8 +519,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/ggc-page.c b/gcc/ggc-page.c
index 51783e5..f3a2119 100644
--- a/gcc/ggc-page.c
+++ b/gcc/ggc-page.c
@@ -2177,6 +2177,8 @@  ggc_collect (void)
   if (G.allocated < allocated_last_gc + min_expand && !ggc_force_collect)
     return;
 
+  dumpfile_ensure_any_optinfo_are_flushed ();
+
   timevar_push (TV_GC);
   if (!quiet_flag)
     fprintf (stderr, " {GC %luk -> ", (unsigned long) G.allocated / 1024);
diff --git a/gcc/optinfo-internal.h b/gcc/optinfo-internal.h
new file mode 100644
index 0000000..8144174
--- /dev/null
+++ b/gcc/optinfo-internal.h
@@ -0,0 +1,148 @@ 
+/* Implementation details of optinfo.
+   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_INTERNAL_H
+#define GCC_OPTINFO_INTERNAL_H
+
+/* Multiple dispatch: there are various kinds of items within an optinfo,
+   and various destinations to send optinfo to.
+
+   Handling this for now by exposing all of the item subclasses,
+   and having the destinations handle them with "switch" statements.  */
+
+/* An enum for discriminating between optinfo_item subclasses.  */
+
+enum optinfo_item_kind
+{
+  OPTINFO_ITEM_KIND_TEXT,
+  OPTINFO_ITEM_KIND_TREE,
+  OPTINFO_ITEM_KIND_GIMPLE,
+  OPTINFO_ITEM_KIND_SYMTAB_NODE
+};
+
+/* Abstract base class for items within an optinfo.  */
+
+class optinfo_item
+{
+ public:
+  virtual ~optinfo_item () {}
+
+  virtual enum optinfo_item_kind get_kind () const = 0;
+};
+
+/* optinfo_item subclasses.  */
+
+/* Item within an optinfo: text, either owned by the item
+   (for optinfo_printf), or borrowed (for string literals).  */
+
+class optinfo_item_text : public optinfo_item
+{
+ public:
+  optinfo_item_text (char *text, bool owned)
+  : m_text (text), m_owned (owned)
+  {}
+  ~optinfo_item_text ()
+  {
+    if (m_owned)
+      free (m_text);
+  }
+
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TEXT;
+  }
+
+  const char *get_text () const { return m_text; }
+
+  void trim_trailing_whitespace ();
+
+ private:
+  char *m_text;
+  bool m_owned;
+};
+
+/* Item within an optinfo: a tree, with dump flags.
+   Note that this is not GTY-marked; see the description of
+   class optinfo for a discussion of the interaction with the
+   garbage-collector.  */
+
+class optinfo_item_tree : public optinfo_item
+{
+ public:
+  optinfo_item_tree (tree node, dump_flags_t dump_flags)
+    : m_node (node), m_dump_flags (dump_flags)
+  {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_TREE;
+  }
+
+  tree get_node () const { return m_node; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  tree m_node;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a gimple statement.
+   Note that this is not GTY-marked; see the description of
+   class optinfo for a discussion of the interaction with the
+   garbage-collector.  */
+
+class optinfo_item_gimple : public optinfo_item
+{
+ public:
+  optinfo_item_gimple (gimple *stmt, dump_flags_t dump_flags)
+    : m_stmt (stmt), m_dump_flags (dump_flags) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_GIMPLE;
+  }
+
+  gimple *get_stmt () const { return m_stmt; }
+  dump_flags_t get_flags () const { return m_dump_flags; }
+
+ private:
+  gimple *m_stmt;
+  dump_flags_t m_dump_flags;
+};
+
+/* Item within an optinfo: a symbol table node.
+   Note that this is not GTY-marked; see the description of
+   class optinfo for a discussion of the interaction with the
+   garbage-collector.  */
+
+class optinfo_item_symtab_node : public optinfo_item
+{
+ public:
+  optinfo_item_symtab_node (symtab_node *node) : m_node (node) {}
+  enum optinfo_item_kind get_kind () const FINAL OVERRIDE
+  {
+    return OPTINFO_ITEM_KIND_SYMTAB_NODE;
+  }
+
+  symtab_node *get_node () const { return m_node; }
+
+ private:
+  symtab_node *m_node;
+};
+
+#endif /* #ifndef GCC_OPTINFO_INTERNAL_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
new file mode 100644
index 0000000..1da7d37
--- /dev/null
+++ b/gcc/optinfo.cc
@@ -0,0 +1,251 @@ 
+/* 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 "optinfo-internal.h"
+#include "dump-context.h"
+#include "selftest.h"
+
+/* Remove any trailing whitespace characters from this text item.
+   Primarily for use in stripping trailing newline characters when
+   emitting remarks (since the diagnostic subsystem doesn't expect
+   trailing newlines in messages).  */
+
+void
+optinfo_item_text::trim_trailing_whitespace ()
+{
+  size_t len = strlen (m_text);
+  if (len == 0)
+    return;
+
+  size_t new_len = len;
+  while (new_len > 0 && ISSPACE (m_text[new_len - 1]))
+    new_len--;
+
+  if (new_len == len)
+    return;
+
+  if (m_owned)
+    m_text[new_len] = '\0';
+  else
+    {
+      m_text = xstrndup (m_text, new_len);
+      m_owned = true;
+    }
+}
+
+/* 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 ()
+{
+  /* Eliminate any trailing whitespace.  */
+  while (m_items.length () > 0)
+    {
+      optinfo_item *last_item = m_items[m_items.length () - 1];
+      if (last_item->get_kind () != OPTINFO_ITEM_KIND_TEXT)
+	break;
+
+      optinfo_item_text *last_text = (optinfo_item_text *)last_item;
+      last_text->trim_trailing_whitespace ();
+
+      if (strlen (last_text->get_text ()) > 0)
+	break;
+
+      m_items.pop ();
+      delete last_item;
+    }
+
+  /* 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_text (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_text (formatted_text, true);
+  m_items.safe_push (item);
+}
+
+/* Append a gimple statement to this optinfo.  */
+
+void
+optinfo::add_stmt (gimple *stmt, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_gimple (stmt, dump_flags));
+}
+
+/* Append a tree node to this optinfo.  */
+
+void
+optinfo::add_tree (tree node, dump_flags_t dump_flags)
+{
+  m_items.safe_push (new optinfo_item_tree (node, dump_flags));
+}
+
+/* Append a symbol table node to this optinfo.  */
+
+void
+optinfo::add_symtab_node (symtab_node *node)
+{
+  m_items.safe_push (new optinfo_item_symtab_node (node));
+}
+
+/* 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_text (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;
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that optinfo_item_text::trim_trailing_whitespace turns
+   INPUT into EXPECTED.  */
+
+static void
+test_trim_trailing_whitespace (const char *input, const char *expected)
+{
+  optinfo_item_text item (const_cast <char *> (input), false);
+  item.trim_trailing_whitespace ();
+  ASSERT_STREQ (item.get_text (), expected);
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_cc_tests ()
+{
+  /* Verify that optinfo_item_text::trim_trailing_whitespace works.  */
+  test_trim_trailing_whitespace ("", "");
+  test_trim_trailing_whitespace ("\n", "");
+  test_trim_trailing_whitespace ("foo", "foo");
+  test_trim_trailing_whitespace ("foo\n", "foo");
+  test_trim_trailing_whitespace ("foo\n\n", "foo");
+  test_trim_trailing_whitespace ("foo bar \n\n", "foo bar");
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
new file mode 100644
index 0000000..0d49823
--- /dev/null
+++ b/gcc/optinfo.h
@@ -0,0 +1,175 @@ 
+/* 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), or
+   * when a garbage collection is about to happen.  This safety measure
+     ensures that no GC happens during the lifetime of an optinfo instance,
+     and thus that any optinfo_items within the optinfo instances can refer
+     to GC-allocated objects without needing to be GTY-marked: they will never
+     refer to collected garbage.  optinfo and optinfo_item are not GTY-marked
+     as it would make no sense for them to be in PCH files.
+
+   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_stmt (gimple *stmt, 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;
+};
+
+#endif /* #ifndef GCC_OPTINFO_H */
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 7f4d6f3..989c50a 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -72,6 +72,7 @@  selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   opt_proposer_c_tests ();
+  optinfo_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 54fc488..48881c9 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -228,6 +228,7 @@  extern void gimple_c_tests ();
 extern void hash_map_tests_c_tests ();
 extern void hash_set_tests_c_tests ();
 extern void input_c_tests ();
+extern void optinfo_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();