diff mbox series

[2/2] Add "-fsave-optimization-record"

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

Commit Message

David Malcolm July 11, 2018, 11:37 a.m. UTC
This patch implements a -fsave-optimization-record option, which
leads to a JSON file being written out, recording the dump_* calls
made (via the optinfo infrastructure in the previous patch).

The patch includes a minimal version of the JSON patch I posted last
year, with just enough support needed for optimization records (I
removed all of the parser code, leaving just the code for building
in-memory JSON trees and writing them to a pretty_printer).

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

OK for trunk?

gcc/ChangeLog:
	* Makefile.in (OBJS): Add json.o and optinfo-emit-json.o.
	(CFLAGS-optinfo-emit-json.o): Define TARGET_NAME.
	* common.opt (fsave-optimization-record): New option.
	* coretypes.h (struct kv_pair): Move here from dumpfile.c.
	* doc/invoke.texi (-fsave-optimization-record): New option.
	* dumpfile.c: Include "optinfo-emit-json.h".
	(struct kv_pair): Move to coretypes.h.
	(optgroup_options): Make non-static.
	(dump_context::end_scope): Call
	optimization_records_maybe_pop_dump_scope.
	* dumpfile.h (optgroup_options): New decl.
	* json.cc: New file.
	* json.h: New file.
	* optinfo-emit-json.cc: New file.
	* optinfo-emit-json.h: New file.
	* optinfo.cc: Include "optinfo-emit-json.h".
	(optinfo::emit): Call optimization_records_maybe_record_optinfo.
	(optinfo_enabled_p): Check optimization_records_enabled_p.
	(optinfo_wants_inlining_info_p): Likewise.
	* optinfo.h: Update comment.
	* profile-count.c (profile_quality_as_string): New function.
	* profile-count.h (profile_quality_as_string): New decl.
	(profile_count::quality): New accessor.
	* selftest-run-tests.c (selftest::run_tests): Call json_cc_tests
	and optinfo_emit_json_cc_tests.
	* selftest.h (selftest::json_cc_tests): New decl.
	(selftest::optinfo_emit_json_cc_tests): New decl.
	* toplev.c: Include "optinfo-emit-json.h".
	(compile_file): Call optimization_records_finish.
	(do_compile): Call optimization_records_start.
	* tree-ssa-live.c: Include optinfo.h.
	(remove_unused_scope_block_p): Retain inlining information if
	optinfo_wants_inlining_info_p returns true.
---
 gcc/Makefile.in          |   3 +
 gcc/common.opt           |   4 +
 gcc/coretypes.h          |   8 +
 gcc/doc/invoke.texi      |   8 +-
 gcc/dumpfile.c           |  15 +-
 gcc/dumpfile.h           |   3 +
 gcc/json.cc              | 293 ++++++++++++++++++++++++
 gcc/json.h               | 166 ++++++++++++++
 gcc/optinfo-emit-json.cc | 568 +++++++++++++++++++++++++++++++++++++++++++++++
 gcc/optinfo-emit-json.h  |  36 +++
 gcc/optinfo.cc           |  11 +-
 gcc/optinfo.h            |   4 -
 gcc/profile-count.c      |  28 +++
 gcc/profile-count.h      |   5 +
 gcc/selftest-run-tests.c |   2 +
 gcc/selftest.h           |   2 +
 gcc/toplev.c             |   5 +
 gcc/tree-ssa-live.c      |   4 +-
 18 files changed, 1143 insertions(+), 22 deletions(-)
 create mode 100644 gcc/json.cc
 create mode 100644 gcc/json.h
 create mode 100644 gcc/optinfo-emit-json.cc
 create mode 100644 gcc/optinfo-emit-json.h

Comments

Richard Biener July 19, 2018, 12:39 p.m. UTC | #1
On Wed, Jul 11, 2018 at 12:53 PM David Malcolm <dmalcolm@redhat.com> wrote:
>
> This patch implements a -fsave-optimization-record option, which
> leads to a JSON file being written out, recording the dump_* calls
> made (via the optinfo infrastructure in the previous patch).
>
> The patch includes a minimal version of the JSON patch I posted last
> year, with just enough support needed for optimization records (I
> removed all of the parser code, leaving just the code for building
> in-memory JSON trees and writing them to a pretty_printer).
>
> Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
>
> OK for trunk?

+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+

I guess this should note that it is experimental and in no way
complete.  Maybe list areas where reports will be generated,
like vectorization?

Did you check what happens with -flto -fsave-optimization-record?
Will the compile-phase emit a json file for each source (expected,
like with early inlining decisions)?  Will the WPA phase emit one
(IPA decisions?) or will IPA decisions be recorded in the LTRANS
one?  How will the LTRANS ones be named and where can they
be found?  You don't need to solve all the issues with this patch
but they should be eventually addressed somehow.

I don't question the use or implementation of JSON, I'll just
approve it.

The rest looks obvious enough, thus OK.

Some overall blurb in the documentation or changes.html
on how to use this would be nice of course.

Thanks,
Richard.


> gcc/ChangeLog:
>         * Makefile.in (OBJS): Add json.o and optinfo-emit-json.o.
>         (CFLAGS-optinfo-emit-json.o): Define TARGET_NAME.
>         * common.opt (fsave-optimization-record): New option.
>         * coretypes.h (struct kv_pair): Move here from dumpfile.c.
>         * doc/invoke.texi (-fsave-optimization-record): New option.
>         * dumpfile.c: Include "optinfo-emit-json.h".
>         (struct kv_pair): Move to coretypes.h.
>         (optgroup_options): Make non-static.
>         (dump_context::end_scope): Call
>         optimization_records_maybe_pop_dump_scope.
>         * dumpfile.h (optgroup_options): New decl.
>         * json.cc: New file.
>         * json.h: New file.
>         * optinfo-emit-json.cc: New file.
>         * optinfo-emit-json.h: New file.
>         * optinfo.cc: Include "optinfo-emit-json.h".
>         (optinfo::emit): Call optimization_records_maybe_record_optinfo.
>         (optinfo_enabled_p): Check optimization_records_enabled_p.
>         (optinfo_wants_inlining_info_p): Likewise.
>         * optinfo.h: Update comment.
>         * profile-count.c (profile_quality_as_string): New function.
>         * profile-count.h (profile_quality_as_string): New decl.
>         (profile_count::quality): New accessor.
>         * selftest-run-tests.c (selftest::run_tests): Call json_cc_tests
>         and optinfo_emit_json_cc_tests.
>         * selftest.h (selftest::json_cc_tests): New decl.
>         (selftest::optinfo_emit_json_cc_tests): New decl.
>         * toplev.c: Include "optinfo-emit-json.h".
>         (compile_file): Call optimization_records_finish.
>         (do_compile): Call optimization_records_start.
>         * tree-ssa-live.c: Include optinfo.h.
>         (remove_unused_scope_block_p): Retain inlining information if
>         optinfo_wants_inlining_info_p returns true.
> ---
>  gcc/Makefile.in          |   3 +
>  gcc/common.opt           |   4 +
>  gcc/coretypes.h          |   8 +
>  gcc/doc/invoke.texi      |   8 +-
>  gcc/dumpfile.c           |  15 +-
>  gcc/dumpfile.h           |   3 +
>  gcc/json.cc              | 293 ++++++++++++++++++++++++
>  gcc/json.h               | 166 ++++++++++++++
>  gcc/optinfo-emit-json.cc | 568 +++++++++++++++++++++++++++++++++++++++++++++++
>  gcc/optinfo-emit-json.h  |  36 +++
>  gcc/optinfo.cc           |  11 +-
>  gcc/optinfo.h            |   4 -
>  gcc/profile-count.c      |  28 +++
>  gcc/profile-count.h      |   5 +
>  gcc/selftest-run-tests.c |   2 +
>  gcc/selftest.h           |   2 +
>  gcc/toplev.c             |   5 +
>  gcc/tree-ssa-live.c      |   4 +-
>  18 files changed, 1143 insertions(+), 22 deletions(-)
>  create mode 100644 gcc/json.cc
>  create mode 100644 gcc/json.h
>  create mode 100644 gcc/optinfo-emit-json.cc
>  create mode 100644 gcc/optinfo-emit-json.h
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index dd1dfc1..b871640 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -1387,6 +1387,7 @@ OBJS = \
>         ira-color.o \
>         ira-emit.o \
>         ira-lives.o \
> +       json.o \
>         jump.o \
>         langhooks.o \
>         lcm.o \
> @@ -1428,6 +1429,7 @@ OBJS = \
>         optabs-query.o \
>         optabs-tree.o \
>         optinfo.o \
> +       optinfo-emit-json.o \
>         options-save.o \
>         opts-global.o \
>         passes.o \
> @@ -2251,6 +2253,7 @@ s-bversion: BASE-VER
>         $(STAMP) s-bversion
>
>  CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
> +CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
>
>  pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
>                     $(srcdir)/gen-pass-instances.awk
> diff --git a/gcc/common.opt b/gcc/common.opt
> index 5a50bc27..a13c709 100644
> --- a/gcc/common.opt
> +++ b/gcc/common.opt
> @@ -1950,6 +1950,10 @@ fopt-info-
>  Common Joined RejectNegative Var(common_deferred_options) Defer
>  -fopt-info[-<type>=filename]   Dump compiler optimization details.
>
> +fsave-optimization-record
> +Common Report Var(flag_save_optimization_record) Optimization
> +Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
> +
>  foptimize-register-move
>  Common Ignore
>  Does nothing. Preserved for backward compatibility.
> diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> index ed0e825..2fd20e4 100644
> --- a/gcc/coretypes.h
> +++ b/gcc/coretypes.h
> @@ -332,6 +332,14 @@ namespace gcc {
>
>  typedef std::pair <tree, tree> tree_pair;
>
> +/* Define a name->value mapping.  */
> +template <typename ValueType>
> +struct kv_pair
> +{
> +  const char *const name;      /* the name of the value */
> +  const ValueType value;       /* the value of the name */
> +};
> +
>  #else
>
>  struct _dont_use_rtx_here_;
> diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> index 56cd122..c239c53 100644
> --- a/gcc/doc/invoke.texi
> +++ b/gcc/doc/invoke.texi
> @@ -417,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
>  -freorder-blocks-algorithm=@var{algorithm} @gol
>  -freorder-blocks-and-partition  -freorder-functions @gol
>  -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
> --frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
> +-frounding-math  -fsave-optimization-record @gol
> +-fsched2-use-superblocks  -fsched-pressure @gol
>  -fsched-spec-load  -fsched-spec-load-dangerous @gol
>  -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
>  -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
> @@ -9907,6 +9908,11 @@ Future versions of GCC may provide finer control of this setting
>  using C99's @code{FENV_ACCESS} pragma.  This command-line option
>  will be used to specify the default state for @code{FENV_ACCESS}.
>
> +@item -fsave-optimization-record
> +@opindex fsave-optimization-record
> +Write a SRCFILE.opt-record.json file detailing what optimizations
> +were performed.
> +
>  @item -fsignaling-nans
>  @opindex fsignaling-nans
>  Compile code assuming that IEEE signaling NaNs may generate user-visible
> diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> index 38f9539..331b7fb 100644
> --- a/gcc/dumpfile.c
> +++ b/gcc/dumpfile.c
> @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "dump-context.h"
>  #include "cgraph.h"
>  #include "tree-pass.h" /* for "current_pass".  */
> +#include "optinfo-emit-json.h"
>
>  /* If non-NULL, return one past-the-end of the matching SUBPART of
>     the WHOLE string.  */
> @@ -118,14 +119,6 @@ static struct dump_file_info dump_files[TDI_end] =
>    DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
>  };
>
> -/* Define a name->number mapping for a dump flag value.  */
> -template <typename ValueType>
> -struct kv_pair
> -{
> -  const char *const name;      /* the name of the value */
> -  const ValueType value;       /* the value of the name */
> -};
> -
>  /* Table of dump options. This must be consistent with the TDF_* flags
>     in dumpfile.h and opt_info_options below. */
>  static const kv_pair<dump_flags_t> dump_options[] =
> @@ -176,7 +169,7 @@ static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
>  };
>
>  /* Flags used for -fopt-info groups.  */
> -static const kv_pair<optgroup_flags_t> optgroup_options[] =
> +const kv_pair<optgroup_flags_t> optgroup_options[] =
>  {
>    {"ipa", OPTGROUP_IPA},
>    {"loop", OPTGROUP_LOOP},
> @@ -794,6 +787,7 @@ dump_context::end_scope ()
>  {
>    end_any_optinfo ();
>    m_scope_depth--;
> +  optimization_records_maybe_pop_dump_scope ();
>  }
>
>  /* Return the optinfo currently being accumulated, creating one if
> @@ -820,8 +814,7 @@ dump_context::begin_next_optinfo (const dump_location_t &loc)
>  }
>
>  /* End any optinfo that has been accumulated within this context; emitting
> -   it to any destinations as appropriate - though none have currently been
> -   implemented.  */
> +   it to any destinations as appropriate, such as optimization records.  */
>
>  void
>  dump_context::end_any_optinfo ()
> diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> index 3096a89..e4823f8 100644
> --- a/gcc/dumpfile.h
> +++ b/gcc/dumpfile.h
> @@ -420,6 +420,7 @@ 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 const kv_pair<optgroup_flags_t> optgroup_options[];
>
>  /* Global variables used to communicate with passes.  */
>  extern FILE *dump_file;
> @@ -442,6 +443,7 @@ dump_enabled_p (void)
>     (a) the active dump_file, if any
>     (b) the -fopt-info destination, if any
>     (c) to the "optinfo" destinations, if any:
> +       (c.1) as optimization records
>
>     dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
>                                     |
> @@ -449,6 +451,7 @@ dump_enabled_p (void)
>                                     |
>                                     `--> (c) optinfo
>                                              `---> optinfo destinations
> +                                                  (c.1) optimization records
>
>     For optinfos, the dump_*_loc mark the beginning of an optinfo
>     instance: all subsequent dump_* calls are consolidated into
> diff --git a/gcc/json.cc b/gcc/json.cc
> new file mode 100644
> index 0000000..3c2aa77
> --- /dev/null
> +++ b/gcc/json.cc
> @@ -0,0 +1,293 @@
> +/* JSON trees
> +   Copyright (C) 2017-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 "json.h"
> +#include "pretty-print.h"
> +#include "math.h"
> +#include "selftest.h"
> +
> +using namespace json;
> +
> +/* class json::value.  */
> +
> +/* Dump this json::value tree to OUTF.
> +   No formatting is done.  There are no guarantees about the order
> +   in which the key/value pairs of json::objects are printed.  */
> +
> +void
> +value::dump (FILE *outf) const
> +{
> +  pretty_printer pp;
> +  pp_buffer (&pp)->stream = outf;
> +  print (&pp);
> +  pp_flush (&pp);
> +}
> +
> +/* class json::object, a subclass of json::value, representing
> +   an unordered collection of key/value pairs.  */
> +
> +/* json:object's dtor.  */
> +
> +object::~object ()
> +{
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      free (const_cast <char *>((*it).first));
> +      delete ((*it).second);
> +    }
> +}
> +
> +/* Implementation of json::value::print for json::object.  */
> +
> +void
> +object::print (pretty_printer *pp) const
> +{
> +  /* Note that the order is not guaranteed.  */
> +  pp_character (pp, '{');
> +  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
> +    {
> +      if (it != m_map.begin ())
> +       pp_string (pp, ", ");
> +      const char *key = const_cast <char *>((*it).first);
> +      value *value = (*it).second;
> +      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
> +      value->print (pp);
> +    }
> +  pp_character (pp, '}');
> +}
> +
> +/* Set the json::value * for KEY, taking ownership of VALUE
> +   (and taking a copy of KEY if necessary).  */
> +
> +void
> +object::set (const char *key, value *v)
> +{
> +  value **ptr = m_map.get (key);
> +  if (ptr)
> +    {
> +      /* If the key is already present, delete the existing value
> +        and overwrite it.  */
> +      delete *ptr;
> +      *ptr = v;
> +    }
> +  else
> +    /* If the key wasn't already present, take a copy of the key,
> +       and store the value.  */
> +    m_map.put (xstrdup (key), v);
> +}
> +
> +/* class json::array, a subclass of json::value, representing
> +   an ordered collection of values.  */
> +
> +/* json::array's dtor.  */
> +
> +array::~array ()
> +{
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    delete v;
> +}
> +
> +/* Implementation of json::value::print for json::array.  */
> +
> +void
> +array::print (pretty_printer *pp) const
> +{
> +  pp_character (pp, '[');
> +  unsigned i;
> +  value *v;
> +  FOR_EACH_VEC_ELT (m_elements, i, v)
> +    {
> +      if (i)
> +       pp_string (pp, ", ");
> +      v->print (pp);
> +    }
> +  pp_character (pp, ']');
> +}
> +
> +/* class json::number, a subclass of json::value, wrapping a double.  */
> +
> +/* Implementation of json::value::print for json::number.  */
> +
> +void
> +number::print (pretty_printer *pp) const
> +{
> +  char tmp[1024];
> +  snprintf (tmp, sizeof (tmp), "%g", m_value);
> +  pp_string (pp, tmp);
> +}
> +
> +/* class json::string, a subclass of json::value.  */
> +
> +void
> +string::print (pretty_printer *pp) const
> +{
> +  pp_character (pp, '"');
> +  for (const char *ptr = m_utf8; *ptr; ptr++)
> +    {
> +      char ch = *ptr;
> +      switch (ch)
> +       {
> +       case '"':
> +         pp_string (pp, "\\\"");
> +         break;
> +       case '\\':
> +         pp_string (pp, "\\n");
> +         break;
> +       case '\b':
> +         pp_string (pp, "\\b");
> +         break;
> +       case '\f':
> +         pp_string (pp, "\\f");
> +         break;
> +       case '\n':
> +         pp_string (pp, "\\n");
> +         break;
> +       case '\r':
> +         pp_string (pp, "\\r");
> +         break;
> +       case '\t':
> +         pp_string (pp, "\\t");
> +         break;
> +
> +       default:
> +         pp_character (pp, ch);
> +       }
> +    }
> +  pp_character (pp, '"');
> +}
> +
> +/* class json::literal, a subclass of json::value.  */
> +
> +/* Implementation of json::value::print for json::literal.  */
> +
> +void
> +literal::print (pretty_printer *pp) const
> +{
> +  switch (m_kind)
> +    {
> +    case JSON_TRUE:
> +      pp_string (pp, "true");
> +      break;
> +    case JSON_FALSE:
> +      pp_string (pp, "false");
> +      break;
> +    case JSON_NULL:
> +      pp_string (pp, "null");
> +      break;
> +    default:
> +      gcc_unreachable ();
> +    }
> +}
> +
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Selftests.  */
> +
> +/* Verify that JV->print () prints EXPECTED_JSON.  */
> +
> +static void
> +assert_print_eq (const json::value &jv, const char *expected_json)
> +{
> +  pretty_printer pp;
> +  jv.print (&pp);
> +  ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
> +}
> +
> +/* Verify that JSON objects are written correctly.  We can't test more than
> +   one key/value pair, as we don't impose a guaranteed ordering.  */
> +
> +static void
> +test_writing_objects ()
> +{
> +  object obj;
> +  obj.set ("foo", new json::string ("bar"));
> +  assert_print_eq (obj, "{\"foo\": \"bar\"}");
> +}
> +
> +/* Verify that JSON arrays are written correctly.  */
> +
> +static void
> +test_writing_arrays ()
> +{
> +  array arr;
> +  assert_print_eq (arr, "[]");
> +
> +  arr.append (new json::string ("foo"));
> +  assert_print_eq (arr, "[\"foo\"]");
> +
> +  arr.append (new json::string ("bar"));
> +  assert_print_eq (arr, "[\"foo\", \"bar\"]");
> +}
> +
> +/* Verify that JSON numbers are written correctly.  */
> +
> +static void
> +test_writing_numbers ()
> +{
> +  assert_print_eq (number (0), "0");
> +  assert_print_eq (number (42), "42");
> +  assert_print_eq (number (-100), "-100");
> +}
> +
> +/* Verify that JSON strings are written correctly.  */
> +
> +static void
> +test_writing_strings ()
> +{
> +  string foo ("foo");
> +  assert_print_eq (foo, "\"foo\"");
> +
> +  string contains_quotes ("before \"quoted\" after");
> +  assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\"");
> +}
> +
> +/* Verify that JSON strings are written correctly.  */
> +
> +static void
> +test_writing_literals ()
> +{
> +  assert_print_eq (literal (JSON_TRUE), "true");
> +  assert_print_eq (literal (JSON_FALSE), "false");
> +  assert_print_eq (literal (JSON_NULL), "null");
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +json_cc_tests ()
> +{
> +  test_writing_objects ();
> +  test_writing_arrays ();
> +  test_writing_numbers ();
> +  test_writing_strings ();
> +  test_writing_literals ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* #if CHECKING_P */
> diff --git a/gcc/json.h b/gcc/json.h
> new file mode 100644
> index 0000000..5c3274c
> --- /dev/null
> +++ b/gcc/json.h
> @@ -0,0 +1,166 @@
> +/* JSON trees
> +   Copyright (C) 2017-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_JSON_H
> +#define GCC_JSON_H
> +
> +/* Implementation of JSON, a lightweight data-interchange format.
> +
> +   See http://www.json.org/
> +   and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
> +   and https://tools.ietf.org/html/rfc7159
> +
> +   Supports creating a DOM-like tree of json::value *, and then dumping
> +   json::value * to text.  */
> +
> +namespace json
> +{
> +
> +/* Forward decls of json::value and its subclasses (using indentation
> +   to denote inheritance.  */
> +
> +class value;
> +  class object;
> +  class array;
> +  class number;
> +  class string;
> +  class literal;
> +
> +/* An enum for discriminating the subclasses of json::value.  */
> +
> +enum kind
> +{
> +  /* class json::object.  */
> +  JSON_OBJECT,
> +
> +  /* class json::array.  */
> +  JSON_ARRAY,
> +
> +  /* class json::number.  */
> +  JSON_NUMBER,
> +
> +  /* class json::string.  */
> +  JSON_STRING,
> +
> +  /* class json::literal uses these three values to identify the
> +     particular literal.  */
> +  JSON_TRUE,
> +  JSON_FALSE,
> +  JSON_NULL
> +};
> +
> +/* Base class of JSON value.  */
> +
> +class value
> +{
> + public:
> +  virtual ~value () {}
> +  virtual enum kind get_kind () const = 0;
> +  virtual void print (pretty_printer *pp) const = 0;
> +
> +  void dump (FILE *) const;
> +};
> +
> +/* Subclass of value for objects: an unordered collection of
> +   key/value pairs.  */
> +
> +class object : public value
> +{
> + public:
> +  ~object ();
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  void set (const char *key, value *v);
> +
> + private:
> +  typedef hash_map <char *, value *,
> +    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
> +  map_t m_map;
> +};
> +
> +/* Subclass of value for arrays.  */
> +
> +class array : public value
> +{
> + public:
> +  ~array ();
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  void append (value *v) { m_elements.safe_push (v); }
> +
> + private:
> +  auto_vec<value *> m_elements;
> +};
> +
> +/* Subclass of value for numbers.  */
> +
> +class number : public value
> +{
> + public:
> +  number (double value) : m_value (value) {}
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  double get () const { return m_value; }
> +
> + private:
> +  double m_value;
> +};
> +
> +/* Subclass of value for strings.  */
> +
> +class string : public value
> +{
> + public:
> +  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
> +  ~string () { free (m_utf8); }
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> +  const char *get_string () const { return m_utf8; }
> +
> + private:
> +  char *m_utf8;
> +};
> +
> +/* Subclass of value for the three JSON literals "true", "false",
> +   and "null".  */
> +
> +class literal : public value
> +{
> + public:
> +  literal (enum kind kind) : m_kind (kind) {}
> +
> +  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
> +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> +
> + private:
> +  enum kind m_kind;
> +};
> +
> +} // namespace json
> +
> +#endif  /* GCC_JSON_H  */
> diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> new file mode 100644
> index 0000000..bf1172a
> --- /dev/null
> +++ b/gcc/optinfo-emit-json.cc
> @@ -0,0 +1,568 @@
> +/* Emit optimization information as JSON files.
> +   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 "diagnostic-core.h"
> +
> +#include "profile.h"
> +#include "output.h"
> +#include "tree-pass.h"
> +
> +#include "optinfo.h"
> +#include "optinfo-emit-json.h"
> +#include "json.h"
> +#include "pretty-print.h"
> +#include "tree-pretty-print.h"
> +#include "gimple-pretty-print.h"
> +#include "cgraph.h"
> +
> +#include "langhooks.h"
> +#include "version.h"
> +#include "context.h"
> +#include "pass_manager.h"
> +#include "selftest.h"
> +#include "dump-context.h"
> +
> +/* A class for writing out optimization records in JSON format.  */
> +
> +class optrecord_json_writer
> +{
> +public:
> +  optrecord_json_writer ();
> +  ~optrecord_json_writer ();
> +  void write () const;
> +  void add_record (const optinfo *optinfo);
> +  void pop_scope ();
> +
> +  void add_record (json::object *obj);
> +  json::object *impl_location_to_json (dump_impl_location_t loc);
> +  json::object *location_to_json (location_t loc);
> +  json::object *profile_count_to_json (profile_count count);
> +  json::string *get_id_value_for_pass (opt_pass *pass);
> +  json::object *pass_to_json (opt_pass *pass);
> +  json::value *inlining_chain_to_json (location_t loc);
> +  json::object *optinfo_to_json (const optinfo *optinfo);
> +  void add_pass_list (json::array *arr, opt_pass *pass);
> +
> +private:
> +  /* The root value for the JSON file.
> +     Currently the JSON values are stored in memory, and flushed when the
> +     compiler exits.  It would probably be better to simply write out
> +     the JSON as we go.  */
> +  json::array *m_root_tuple;
> +
> +  /* The currently open scopes, for expressing nested optimization records.  */
> +  vec<json::array *> m_scopes;
> +};
> +
> +/* optrecord_json_writer's ctor.  Populate the top-level parts of the
> +   in-memory JSON representation.  */
> +
> +optrecord_json_writer::optrecord_json_writer ()
> +  : m_root_tuple (NULL), m_scopes ()
> +{
> +  m_root_tuple = new json::array ();
> +
> +  /* Populate with metadata; compare with toplev.c: print_version.  */
> +  json::object *metadata = new json::object ();
> +  m_root_tuple->append (metadata);
> +  metadata->set ("format", new json::string ("1"));
> +  json::object *generator = new json::object ();
> +  metadata->set ("generator", generator);
> +  generator->set ("name", new json::string (lang_hooks.name));
> +  generator->set ("pkgversion", new json::string (pkgversion_string));
> +  generator->set ("version", new json::string (version_string));
> +  /* TARGET_NAME is passed in by the Makefile.  */
> +  generator->set ("target", new json::string (TARGET_NAME));
> +
> +  /* TODO: capture command-line?
> +     see gen_producer_string in dwarf2out.c (currently static).  */
> +
> +  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
> +
> +  json::array *passes = new json::array ();
> +  m_root_tuple->append (passes);
> +
> +  /* Call add_pass_list for all of the pass lists.  */
> +  {
> +#define DEF_PASS_LIST(LIST) \
> +    add_pass_list (passes, g->get_passes ()->LIST);
> +    GCC_PASS_LISTS
> +#undef DEF_PASS_LIST
> +  }
> +
> +  json::array *records = new json::array ();
> +  m_root_tuple->append (records);
> +
> +  m_scopes.safe_push (records);
> +}
> +
> +/* optrecord_json_writer's ctor.
> +   Delete the in-memory JSON representation.  */
> +
> +optrecord_json_writer::~optrecord_json_writer ()
> +{
> +  delete m_root_tuple;
> +}
> +
> +/* Choose an appropriate filename, and write the saved records to it.  */
> +
> +void
> +optrecord_json_writer::write () const
> +{
> +  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
> +  FILE *outfile = fopen (filename, "w");
> +  if (outfile)
> +    {
> +      m_root_tuple->dump (outfile);
> +      fclose (outfile);
> +    }
> +  else
> +    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
> +             filename); // FIXME: more info?
> +  free (filename);
> +}
> +
> +/* Add a record for OPTINFO to the queue of records to be written.  */
> +
> +void
> +optrecord_json_writer::add_record (const optinfo *optinfo)
> +{
> +  json::object *obj = optinfo_to_json (optinfo);
> +
> +  add_record (obj);
> +
> +  /* Potentially push the scope.  */
> +  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
> +    {
> +      json::array *children = new json::array ();
> +      obj->set ("children", children);
> +      m_scopes.safe_push (children);
> +    }
> +}
> +
> +/* Private methods of optrecord_json_writer.  */
> +
> +/* Add record OBJ to the the innermost scope.  */
> +
> +void
> +optrecord_json_writer::add_record (json::object *obj)
> +{
> +  /* Add to innermost scope.  */
> +  gcc_assert (m_scopes.length () > 0);
> +  m_scopes[m_scopes.length () - 1]->append (obj);
> +}
> +
> +/* Pop the innermost scope.  */
> +
> +void
> +optrecord_json_writer::pop_scope ()
> +{
> +  m_scopes.pop ();
> +}
> +
> +/* Create a JSON object representing LOC.  */
> +
> +json::object *
> +optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
> +{
> +  json::object *obj = new json::object ();
> +  obj->set ("file", new json::string (loc.m_file));
> +  obj->set ("line", new json::number (loc.m_line));
> +  if (loc.m_function)
> +    obj->set ("function", new json::string (loc.m_function));
> +  return obj;
> +}
> +
> +/* Create a JSON object representing LOC.  */
> +
> +json::object *
> +optrecord_json_writer::location_to_json (location_t loc)
> +{
> +  json::object *obj = new json::object ();
> +  obj->set ("file", new json::string (LOCATION_FILE (loc)));
> +  obj->set ("line", new json::number (LOCATION_LINE (loc)));
> +  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
> +  return obj;
> +}
> +
> +/* Create a JSON object representing COUNT.  */
> +
> +json::object *
> +optrecord_json_writer::profile_count_to_json (profile_count count)
> +{
> +  json::object *obj = new json::object ();
> +  obj->set ("value", new json::number (count.to_gcov_type ()));
> +  obj->set ("quality",
> +           new json::string (profile_quality_as_string (count.quality ())));
> +  return obj;
> +}
> +
> +/* Get a string for use when referring to PASS in the saved optimization
> +   records.  */
> +
> +json::string *
> +optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
> +{
> +  pretty_printer pp;
> +  /* this is host-dependent, but will be consistent for a given host.  */
> +  pp_pointer (&pp, static_cast<void *> (pass));
> +  return new json::string (pp_formatted_text (&pp));
> +}
> +
> +/* Create a JSON object representing PASS.  */
> +
> +json::object *
> +optrecord_json_writer::pass_to_json (opt_pass *pass)
> +{
> +  json::object *obj = new json::object ();
> +  const char *type = NULL;
> +  switch (pass->type)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case GIMPLE_PASS:
> +      type = "gimple";
> +      break;
> +    case RTL_PASS:
> +      type = "rtl";
> +      break;
> +    case SIMPLE_IPA_PASS:
> +      type = "simple_ipa";
> +      break;
> +    case IPA_PASS:
> +      type = "ipa";
> +      break;
> +    }
> +  obj->set ("id", get_id_value_for_pass (pass));
> +  obj->set ("type", new json::string (type));
> +  obj->set ("name", new json::string (pass->name));
> +  /* Represent the optgroup flags as an array.  */
> +  {
> +    json::array *optgroups = new json::array ();
> +    obj->set ("optgroups", optgroups);
> +    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
> +        optgroup->name != NULL; optgroup++)
> +      if (optgroup->value != OPTGROUP_ALL
> +         && (pass->optinfo_flags & optgroup->value))
> +       optgroups->append (new json::string (optgroup->name));
> +  }
> +  obj->set ("num", new json::number (pass->static_pass_number));
> +  return obj;
> +}
> +
> +/* Create a JSON array for LOC representing the chain of inlining
> +   locations.
> +   Compare with lhd_print_error_function and cp_print_error_function.  */
> +
> +json::value *
> +optrecord_json_writer::inlining_chain_to_json (location_t loc)
> +{
> +  json::array *array = new json::array ();
> +
> +  tree abstract_origin = LOCATION_BLOCK (loc);
> +
> +  while (abstract_origin)
> +    {
> +      location_t *locus;
> +      tree block = abstract_origin;
> +
> +      locus = &BLOCK_SOURCE_LOCATION (block);
> +      tree fndecl = NULL;
> +      block = BLOCK_SUPERCONTEXT (block);
> +      while (block && TREE_CODE (block) == BLOCK
> +            && BLOCK_ABSTRACT_ORIGIN (block))
> +       {
> +         tree ao = BLOCK_ABSTRACT_ORIGIN (block);
> +
> +         while (TREE_CODE (ao) == BLOCK
> +                && BLOCK_ABSTRACT_ORIGIN (ao)
> +                && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
> +           ao = BLOCK_ABSTRACT_ORIGIN (ao);
> +
> +         if (TREE_CODE (ao) == FUNCTION_DECL)
> +           {
> +             fndecl = ao;
> +             break;
> +           }
> +         else if (TREE_CODE (ao) != BLOCK)
> +           break;
> +
> +         block = BLOCK_SUPERCONTEXT (block);
> +       }
> +      if (fndecl)
> +       abstract_origin = block;
> +      else
> +       {
> +         while (block && TREE_CODE (block) == BLOCK)
> +           block = BLOCK_SUPERCONTEXT (block);
> +
> +         if (block && TREE_CODE (block) == FUNCTION_DECL)
> +           fndecl = block;
> +         abstract_origin = NULL;
> +       }
> +      if (fndecl)
> +       {
> +         json::object *obj = new json::object ();
> +         const char *printable_name
> +           = lang_hooks.decl_printable_name (fndecl, 2);
> +         obj->set ("fndecl", new json::string (printable_name));
> +         if (*locus != UNKNOWN_LOCATION)
> +           obj->set ("site", location_to_json (*locus));
> +         array->append (obj);
> +       }
> +    }
> +
> +  return array;
> +}
> +
> +/* Create a JSON object representing OPTINFO.  */
> +
> +json::object *
> +optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
> +{
> +  json::object *obj = new json::object ();
> +
> +  obj->set ("impl_location",
> +           impl_location_to_json (optinfo->get_impl_location ()));
> +
> +  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
> +  obj->set ("kind", new json::string (kind_str));
> +  json::array *message = new json::array ();
> +  obj->set ("message", message);
> +  for (unsigned i = 0; i < optinfo->num_items (); i++)
> +    {
> +      const optinfo_item *item = optinfo->get_item (i);
> +      switch (item->get_kind ())
> +       {
> +       default:
> +         gcc_unreachable ();
> +       case OPTINFO_ITEM_KIND_TEXT:
> +         {
> +           message->append (new json::string (item->get_text ()));
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_TREE:
> +         {
> +           json::object *json_item = new json::object ();
> +           json_item->set ("expr", new json::string (item->get_text ()));
> +
> +           /* Capture any location for the node.  */
> +           if (item->get_location () != UNKNOWN_LOCATION)
> +             json_item->set ("location", location_to_json (item->get_location ()));
> +
> +           message->append (json_item);
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_GIMPLE:
> +         {
> +           json::object *json_item = new json::object ();
> +           json_item->set ("stmt", new json::string (item->get_text ()));
> +
> +           /* Capture any location for the stmt.  */
> +           if (item->get_location () != UNKNOWN_LOCATION)
> +             json_item->set ("location", location_to_json (item->get_location ()));
> +
> +           message->append (json_item);
> +         }
> +         break;
> +       case OPTINFO_ITEM_KIND_SYMTAB_NODE:
> +         {
> +           json::object *json_item = new json::object ();
> +           json_item->set ("symtab_node", new json::string (item->get_text ()));
> +
> +           /* Capture any location for the node.  */
> +           if (item->get_location () != UNKNOWN_LOCATION)
> +             json_item->set ("location", location_to_json (item->get_location ()));
> +           message->append (json_item);
> +         }
> +         break;
> +       }
> +   }
> +
> +  if (optinfo->get_pass ())
> +    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
> +
> +  profile_count count = optinfo->get_count ();
> +  if (count.initialized_p ())
> +    obj->set ("count", profile_count_to_json (count));
> +
> +  /* Record any location, handling the case where of an UNKNOWN_LOCATION
> +     within an inlined block.  */
> +  location_t loc = optinfo->get_location_t ();
> +  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
> +    {
> +      // TOOD: record the location (just caret for now)
> +      // TODO: start/finish also?
> +      obj->set ("location", location_to_json (loc));
> +    }
> +
> +  if (current_function_decl)
> +    {
> +      const char *fnname = get_fnname_from_decl (current_function_decl);
> +      obj->set ("function", new json::string (fnname));
> +    }
> +
> +  if (loc != UNKNOWN_LOCATION)
> +    obj->set ("inlining_chain", inlining_chain_to_json (loc));
> +
> +  return obj;
> +}
> +
> +/* Add a json description of PASS and its siblings to ARR, recursing into
> +   child passes (adding their descriptions within a "children" array).  */
> +
> +void
> +optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
> +{
> +  do
> +    {
> +      json::object *pass_obj = pass_to_json (pass);
> +      arr->append (pass_obj);
> +      if (pass->sub)
> +       {
> +         json::array *sub = new json::array ();
> +         pass_obj->set ("children", sub);
> +         add_pass_list (sub, pass->sub);
> +       }
> +      pass = pass->next;
> +    }
> +  while (pass);
> +}
> +
> +/* File-level interface to rest of compiler (to avoid exposing
> +   class optrecord_json_writer outside of this file).  */
> +
> +static optrecord_json_writer *the_json_writer;
> +
> +/* Perform startup activity for -fsave-optimization-record.  */
> +
> +void
> +optimization_records_start ()
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!flag_save_optimization_record)
> +    return;
> +
> +  the_json_writer = new optrecord_json_writer ();
> +}
> +
> +/* Perform cleanup activity for -fsave-optimization-record.
> +
> +   Currently, the file is written out here in one go, before cleaning
> +   up.  */
> +
> +void
> +optimization_records_finish ()
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!the_json_writer)
> +    return;
> +
> +  the_json_writer->write ();
> +
> +  delete the_json_writer;
> +  the_json_writer = NULL;
> +}
> +
> +/* Did the user request optimization records to be written out?  */
> +
> +bool
> +optimization_records_enabled_p ()
> +{
> +  return the_json_writer != NULL;
> +}
> +
> +/* If optimization records were requested, then add a record for OPTINFO
> +   to the queue of records to be written.  */
> +
> +void
> +optimization_records_maybe_record_optinfo (const optinfo *optinfo)
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!the_json_writer)
> +    return;
> +
> +  the_json_writer->add_record (optinfo);
> +}
> +
> +/* Handling for the end of a dump scope for the
> +   optimization records sink.  */
> +
> +void
> +optimization_records_maybe_pop_dump_scope ()
> +{
> +  /* Bail if recording not enabled.  */
> +  if (!the_json_writer)
> +    return;
> +
> +  the_json_writer->pop_scope ();
> +}
> +
> +#if CHECKING_P
> +
> +namespace selftest {
> +
> +/* Verify that we can build a JSON optimization record from dump_*
> +   calls.  */
> +
> +static void
> +test_building_json_from_dump_calls ()
> +{
> +  temp_dump_context tmp (true);
> +  dump_location_t loc;
> +  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->num_items (), 2);
> +
> +  optrecord_json_writer writer;
> +  json::object *json_obj = writer.optinfo_to_json (info);
> +  ASSERT_TRUE (json_obj != NULL);
> +
> +  /* Verify that the json is sane.  */
> +  pretty_printer pp;
> +  json_obj->print (&pp);
> +  const char *json_str = pp_formatted_text (&pp);
> +  ASSERT_STR_CONTAINS (json_str, "impl_location");
> +  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
> +  ASSERT_STR_CONTAINS (json_str,
> +                      "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
> +  delete json_obj;
> +}
> +
> +/* Run all of the selftests within this file.  */
> +
> +void
> +optinfo_emit_json_cc_tests ()
> +{
> +  test_building_json_from_dump_calls ();
> +}
> +
> +} // namespace selftest
> +
> +#endif /* CHECKING_P */
> diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
> new file mode 100644
> index 0000000..3628d56
> --- /dev/null
> +++ b/gcc/optinfo-emit-json.h
> @@ -0,0 +1,36 @@
> +/* Emit optimization information as JSON files.
> +   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_EMIT_JSON_H
> +#define GCC_OPTINFO_EMIT_JSON_H
> +
> +class optinfo;
> +struct opt_pass;
> +
> +extern void optimization_records_start ();
> +extern void optimization_records_finish ();
> +
> +extern bool optimization_records_enabled_p ();
> +
> +extern void optimization_records_maybe_record_optinfo (const optinfo *);
> +extern void optimization_records_maybe_pop_dump_scope ();
> +
> +
> +#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
> diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> index 6f224bc..93de9d9 100644
> --- a/gcc/optinfo.cc
> +++ b/gcc/optinfo.cc
> @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "gimple.h"
>
>  #include "optinfo.h"
> +#include "optinfo-emit-json.h"
>  #include "dump-context.h"
>  #include "pretty-print.h"
>  #include "gimple-pretty-print.h"
> @@ -85,7 +86,8 @@ optinfo::~optinfo ()
>  void
>  optinfo::emit ()
>  {
> -  /* currently this is a no-op.  */
> +  /* -fsave-optimization-record.  */
> +  optimization_records_maybe_record_optinfo (this);
>  }
>
>  /* Update the optinfo's kind based on DUMP_KIND.  */
> @@ -221,9 +223,8 @@ optinfo::add_dec (const wide_int_ref &wi, signop sgn)
>
>  bool optinfo_enabled_p ()
>  {
> -  /* Currently no destinations are implemented, just a hook for
> -     selftests.  */
> -  return dump_context::get ().forcibly_enable_optinfo_p ();
> +  return (dump_context::get ().forcibly_enable_optinfo_p ()
> +         || optimization_records_enabled_p ());
>  }
>
>  /* Return true if any of the active optinfo destinations make use
> @@ -232,5 +233,5 @@ bool optinfo_enabled_p ()
>
>  bool optinfo_wants_inlining_info_p ()
>  {
> -  return false;
> +  return optimization_records_enabled_p ();
>  }
> diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> index 5bdb9eb..5f09022 100644
> --- a/gcc/optinfo.h
> +++ b/gcc/optinfo.h
> @@ -25,12 +25,8 @@ along with GCC; see the file COPYING3.  If not see
>     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
> diff --git a/gcc/profile-count.c b/gcc/profile-count.c
> index 3d411cf..6a17f5e 100644
> --- a/gcc/profile-count.c
> +++ b/gcc/profile-count.c
> @@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not see
>  #include "wide-int.h"
>  #include "sreal.h"
>
> +/* Get a string describing QUALITY.  */
> +
> +const char *
> +profile_quality_as_string (enum profile_quality quality)
> +{
> +  switch (quality)
> +    {
> +    default:
> +      gcc_unreachable ();
> +    case profile_uninitialized:
> +      return "uninitialized";
> +    case profile_guessed_local:
> +      return "guessed_local";
> +    case profile_guessed_global0:
> +      return "guessed_global0";
> +    case profile_guessed_global0adjusted:
> +      return "guessed_global0adjusted";
> +    case profile_guessed:
> +      return "guessed";
> +    case profile_afdo:
> +      return "afdo";
> +    case profile_adjusted:
> +      return "adjusted";
> +    case profile_precise:
> +      return "precise";
> +    }
> +}
> +
>  /* Dump THIS to F.  */
>
>  void
> diff --git a/gcc/profile-count.h b/gcc/profile-count.h
> index c83fa3b..f4d0c340 100644
> --- a/gcc/profile-count.h
> +++ b/gcc/profile-count.h
> @@ -59,6 +59,8 @@ enum profile_quality {
>    profile_precise
>  };
>
> +extern const char *profile_quality_as_string (enum profile_quality);
> +
>  /* The base value for branch probability notes and edge probabilities.  */
>  #define REG_BR_PROB_BASE  10000
>
> @@ -721,6 +723,9 @@ public:
>        return m_quality == profile_precise;
>      }
>
> +  /* Get the quality of the count.  */
> +  enum profile_quality quality () const { return m_quality; }
> +
>    /* When merging basic blocks, the two different profile counts are unified.
>       Return true if this can be done without losing info about profile.
>       The only case we care about here is when first BB contains something
> diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> index 7f4d6f3..5adb033 100644
> --- a/gcc/selftest-run-tests.c
> +++ b/gcc/selftest-run-tests.c
> @@ -72,6 +72,8 @@ selftest::run_tests ()
>    typed_splay_tree_c_tests ();
>    unique_ptr_tests_cc_tests ();
>    opt_proposer_c_tests ();
> +  json_cc_tests ();
> +  optinfo_emit_json_cc_tests ();
>
>    /* Mid-level data structures.  */
>    input_c_tests ();
> diff --git a/gcc/selftest.h b/gcc/selftest.h
> index 54fc488..ede7732 100644
> --- a/gcc/selftest.h
> +++ b/gcc/selftest.h
> @@ -228,6 +228,8 @@ 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 json_cc_tests ();
> +extern void optinfo_emit_json_cc_tests ();
>  extern void predict_c_tests ();
>  extern void pretty_print_c_tests ();
>  extern void read_rtl_function_c_tests ();
> diff --git a/gcc/toplev.c b/gcc/toplev.c
> index d108096..a047390 100644
> --- a/gcc/toplev.c
> +++ b/gcc/toplev.c
> @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "tree-pass.h"
>  #include "dumpfile.h"
>  #include "ipa-fnsummary.h"
> +#include "optinfo-emit-json.h"
>
>  #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
>  #include "dbxout.h"
> @@ -487,6 +488,8 @@ compile_file (void)
>    if (lang_hooks.decls.post_compilation_parsing_cleanups)
>      lang_hooks.decls.post_compilation_parsing_cleanups ();
>
> +  optimization_records_finish ();
> +
>    if (seen_error ())
>      return;
>
> @@ -2048,6 +2051,8 @@ do_compile ()
>
>        timevar_start (TV_PHASE_SETUP);
>
> +      optimization_records_start ();
> +
>        /* This must be run always, because it is needed to compute the FP
>          predefined macros, such as __LDBL_MAX__, for targets using non
>          default FP formats.  */
> diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
> index 7447f7a..2623d9b 100644
> --- a/gcc/tree-ssa-live.c
> +++ b/gcc/tree-ssa-live.c
> @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not see
>  #include "cfgloop.h"
>  #include "stringpool.h"
>  #include "attribs.h"
> +#include "optinfo.h"
>
>  static void verify_live_on_entry (tree_live_info_p);
>
> @@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
>       ;
>     /* When not generating debug info we can eliminate info on unused
>        variables.  */
> -   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
> +   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
> +           && !optinfo_wants_inlining_info_p ())
>       {
>         /* Even for -g0 don't prune outer scopes from artificial
>           functions, otherwise diagnostics using tree_nonartificial_location
> --
> 1.8.5.3
>
David Malcolm July 20, 2018, 3:45 p.m. UTC | #2
On Thu, 2018-07-19 at 14:39 +0200, Richard Biener wrote:
> On Wed, Jul 11, 2018 at 12:53 PM David Malcolm <dmalcolm@redhat.com>
> wrote:
> > 
> > This patch implements a -fsave-optimization-record option, which
> > leads to a JSON file being written out, recording the dump_* calls
> > made (via the optinfo infrastructure in the previous patch).
> > 
> > The patch includes a minimal version of the JSON patch I posted
> > last
> > year, with just enough support needed for optimization records (I
> > removed all of the parser code, leaving just the code for building
> > in-memory JSON trees and writing them to a pretty_printer).
> > 
> > Successfully bootstrapped & regrtested on x86_64-pc-linux-gnu.
> > 
> > OK for trunk?
> 
> +@item -fsave-optimization-record
> +@opindex fsave-optimization-record
> +Write a SRCFILE.opt-record.json file detailing what optimizations
> +were performed.
> +
> 
> I guess this should note that it is experimental and in no way
> complete.  Maybe list areas where reports will be generated,
> like vectorization?

Indeed. I also noticed that for some reason my patch had this option
between
  @item -frounding-math
and
  @item -fsignaling-nans
in the section on floating-point arithmetic options, so I've moved it
to the "GCC Developer Options" section for now, immediately after the
documentation of -fopt-info.

Here's what I've committed to invoke.texi (r262905):

BEGIN QUOTE:

@item -fsave-optimization-record
@opindex fsave-optimization-record
Write a SRCFILE.opt-record.json file detailing what optimizations
were performed, for those optimizations that support @option{-fopt-info}.

This option is experimental and the format of the data within the JSON
file is subject to change.

It is roughly equivalent to a machine-readable version of
@option{-fopt-info-all}, as a collection of messages with source file,
line number and column number, with the following additional data for
each message:

@itemize @bullet

@item
the execution count of the code being optimized, along with metadata about
whether this was from actual profile data, or just an estimate, allowing
consumers to prioritize messages by code hotness,

@item
the function name of the code being optimized, where applicable,

@item
the ``inlining chain'' for the code being optimized, so that when
a function is inlined into several different places (which might
themselves be inlined), the reader can distinguish between the copies,

@item
objects identifying those parts of the message that refer to expressions,
statements or symbol-table nodes, which of these categories they are, and,
when available, their source code location,

@item
the GCC pass that emitted the message, and

@item
the location in GCC's own code from which the message was emitted

@end itemize

Additionally, some messages are logically nested within other
messages, reflecting implementation details of the optimization
passes.

END QUOTE


> Did you check what happens with -flto -fsave-optimization-record?
> Will the compile-phase emit a json file for each source (expected,
> like with early inlining decisions)?  Will the WPA phase emit one
> (IPA decisions?) or will IPA decisions be recorded in the LTRANS
> one?  How will the LTRANS ones be named and where can they
> be found?  You don't need to solve all the issues with this patch
> but they should be eventually addressed somehow.

I'm looking at that now.

> I don't question the use or implementation of JSON, I'll just
> approve it.
> 
> The rest looks obvious enough, thus OK.

> 
> Some overall blurb in the documentation or changes.html
> on how to use this would be nice of course.

Thanks; will do.

Dave

> Thanks,
> Richard.
> 
> 
> > gcc/ChangeLog:
> >         * Makefile.in (OBJS): Add json.o and optinfo-emit-json.o.
> >         (CFLAGS-optinfo-emit-json.o): Define TARGET_NAME.
> >         * common.opt (fsave-optimization-record): New option.
> >         * coretypes.h (struct kv_pair): Move here from dumpfile.c.
> >         * doc/invoke.texi (-fsave-optimization-record): New option.
> >         * dumpfile.c: Include "optinfo-emit-json.h".
> >         (struct kv_pair): Move to coretypes.h.
> >         (optgroup_options): Make non-static.
> >         (dump_context::end_scope): Call
> >         optimization_records_maybe_pop_dump_scope.
> >         * dumpfile.h (optgroup_options): New decl.
> >         * json.cc: New file.
> >         * json.h: New file.
> >         * optinfo-emit-json.cc: New file.
> >         * optinfo-emit-json.h: New file.
> >         * optinfo.cc: Include "optinfo-emit-json.h".
> >         (optinfo::emit): Call
> > optimization_records_maybe_record_optinfo.
> >         (optinfo_enabled_p): Check optimization_records_enabled_p.
> >         (optinfo_wants_inlining_info_p): Likewise.
> >         * optinfo.h: Update comment.
> >         * profile-count.c (profile_quality_as_string): New
> > function.
> >         * profile-count.h (profile_quality_as_string): New decl.
> >         (profile_count::quality): New accessor.
> >         * selftest-run-tests.c (selftest::run_tests): Call
> > json_cc_tests
> >         and optinfo_emit_json_cc_tests.
> >         * selftest.h (selftest::json_cc_tests): New decl.
> >         (selftest::optinfo_emit_json_cc_tests): New decl.
> >         * toplev.c: Include "optinfo-emit-json.h".
> >         (compile_file): Call optimization_records_finish.
> >         (do_compile): Call optimization_records_start.
> >         * tree-ssa-live.c: Include optinfo.h.
> >         (remove_unused_scope_block_p): Retain inlining information
> > if
> >         optinfo_wants_inlining_info_p returns true.
> > ---
> >  gcc/Makefile.in          |   3 +
> >  gcc/common.opt           |   4 +
> >  gcc/coretypes.h          |   8 +
> >  gcc/doc/invoke.texi      |   8 +-
> >  gcc/dumpfile.c           |  15 +-
> >  gcc/dumpfile.h           |   3 +
> >  gcc/json.cc              | 293 ++++++++++++++++++++++++
> >  gcc/json.h               | 166 ++++++++++++++
> >  gcc/optinfo-emit-json.cc | 568
> > +++++++++++++++++++++++++++++++++++++++++++++++
> >  gcc/optinfo-emit-json.h  |  36 +++
> >  gcc/optinfo.cc           |  11 +-
> >  gcc/optinfo.h            |   4 -
> >  gcc/profile-count.c      |  28 +++
> >  gcc/profile-count.h      |   5 +
> >  gcc/selftest-run-tests.c |   2 +
> >  gcc/selftest.h           |   2 +
> >  gcc/toplev.c             |   5 +
> >  gcc/tree-ssa-live.c      |   4 +-
> >  18 files changed, 1143 insertions(+), 22 deletions(-)
> >  create mode 100644 gcc/json.cc
> >  create mode 100644 gcc/json.h
> >  create mode 100644 gcc/optinfo-emit-json.cc
> >  create mode 100644 gcc/optinfo-emit-json.h
> > 
> > diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> > index dd1dfc1..b871640 100644
> > --- a/gcc/Makefile.in
> > +++ b/gcc/Makefile.in
> > @@ -1387,6 +1387,7 @@ OBJS = \
> >         ira-color.o \
> >         ira-emit.o \
> >         ira-lives.o \
> > +       json.o \
> >         jump.o \
> >         langhooks.o \
> >         lcm.o \
> > @@ -1428,6 +1429,7 @@ OBJS = \
> >         optabs-query.o \
> >         optabs-tree.o \
> >         optinfo.o \
> > +       optinfo-emit-json.o \
> >         options-save.o \
> >         opts-global.o \
> >         passes.o \
> > @@ -2251,6 +2253,7 @@ s-bversion: BASE-VER
> >         $(STAMP) s-bversion
> > 
> >  CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
> > +CFLAGS-optinfo-emit-json.o +=
> > -DTARGET_NAME=\"$(target_noncanonical)\"
> > 
> >  pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
> >                     $(srcdir)/gen-pass-instances.awk
> > diff --git a/gcc/common.opt b/gcc/common.opt
> > index 5a50bc27..a13c709 100644
> > --- a/gcc/common.opt
> > +++ b/gcc/common.opt
> > @@ -1950,6 +1950,10 @@ fopt-info-
> >  Common Joined RejectNegative Var(common_deferred_options) Defer
> >  -fopt-info[-<type>=filename]   Dump compiler optimization details.
> > 
> > +fsave-optimization-record
> > +Common Report Var(flag_save_optimization_record) Optimization
> > +Write a SRCFILE.opt-record.json file detailing what optimizations
> > were performed.
> > +
> >  foptimize-register-move
> >  Common Ignore
> >  Does nothing. Preserved for backward compatibility.
> > diff --git a/gcc/coretypes.h b/gcc/coretypes.h
> > index ed0e825..2fd20e4 100644
> > --- a/gcc/coretypes.h
> > +++ b/gcc/coretypes.h
> > @@ -332,6 +332,14 @@ namespace gcc {
> > 
> >  typedef std::pair <tree, tree> tree_pair;
> > 
> > +/* Define a name->value mapping.  */
> > +template <typename ValueType>
> > +struct kv_pair
> > +{
> > +  const char *const name;      /* the name of the value */
> > +  const ValueType value;       /* the value of the name */
> > +};
> > +
> >  #else
> > 
> >  struct _dont_use_rtx_here_;
> > diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
> > index 56cd122..c239c53 100644
> > --- a/gcc/doc/invoke.texi
> > +++ b/gcc/doc/invoke.texi
> > @@ -417,7 +417,8 @@ Objective-C and Objective-C++ Dialects}.
> >  -freorder-blocks-algorithm=@var{algorithm} @gol
> >  -freorder-blocks-and-partition  -freorder-functions @gol
> >  -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
> > --frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
> > +-frounding-math  -fsave-optimization-record @gol
> > +-fsched2-use-superblocks  -fsched-pressure @gol
> >  -fsched-spec-load  -fsched-spec-load-dangerous @gol
> >  -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n
> > }] @gol
> >  -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
> > @@ -9907,6 +9908,11 @@ Future versions of GCC may provide finer
> > control of this setting
> >  using C99's @code{FENV_ACCESS} pragma.  This command-line option
> >  will be used to specify the default state for @code{FENV_ACCESS}.
> > 
> > +@item -fsave-optimization-record
> > +@opindex fsave-optimization-record
> > +Write a SRCFILE.opt-record.json file detailing what optimizations
> > +were performed.
> > +
> >  @item -fsignaling-nans
> >  @opindex fsignaling-nans
> >  Compile code assuming that IEEE signaling NaNs may generate user-
> > visible
> > diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
> > index 38f9539..331b7fb 100644
> > --- a/gcc/dumpfile.c
> > +++ b/gcc/dumpfile.c
> > @@ -37,6 +37,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "dump-context.h"
> >  #include "cgraph.h"
> >  #include "tree-pass.h" /* for "current_pass".  */
> > +#include "optinfo-emit-json.h"
> > 
> >  /* If non-NULL, return one past-the-end of the matching SUBPART of
> >     the WHOLE string.  */
> > @@ -118,14 +119,6 @@ static struct dump_file_info
> > dump_files[TDI_end] =
> >    DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
> >  };
> > 
> > -/* Define a name->number mapping for a dump flag value.  */
> > -template <typename ValueType>
> > -struct kv_pair
> > -{
> > -  const char *const name;      /* the name of the value */
> > -  const ValueType value;       /* the value of the name */
> > -};
> > -
> >  /* Table of dump options. This must be consistent with the TDF_*
> > flags
> >     in dumpfile.h and opt_info_options below. */
> >  static const kv_pair<dump_flags_t> dump_options[] =
> > @@ -176,7 +169,7 @@ static const kv_pair<dump_flags_t>
> > optinfo_verbosity_options[] =
> >  };
> > 
> >  /* Flags used for -fopt-info groups.  */
> > -static const kv_pair<optgroup_flags_t> optgroup_options[] =
> > +const kv_pair<optgroup_flags_t> optgroup_options[] =
> >  {
> >    {"ipa", OPTGROUP_IPA},
> >    {"loop", OPTGROUP_LOOP},
> > @@ -794,6 +787,7 @@ dump_context::end_scope ()
> >  {
> >    end_any_optinfo ();
> >    m_scope_depth--;
> > +  optimization_records_maybe_pop_dump_scope ();
> >  }
> > 
> >  /* Return the optinfo currently being accumulated, creating one if
> > @@ -820,8 +814,7 @@ dump_context::begin_next_optinfo (const
> > dump_location_t &loc)
> >  }
> > 
> >  /* End any optinfo that has been accumulated within this context;
> > emitting
> > -   it to any destinations as appropriate - though none have
> > currently been
> > -   implemented.  */
> > +   it to any destinations as appropriate, such as optimization
> > records.  */
> > 
> >  void
> >  dump_context::end_any_optinfo ()
> > diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
> > index 3096a89..e4823f8 100644
> > --- a/gcc/dumpfile.h
> > +++ b/gcc/dumpfile.h
> > @@ -420,6 +420,7 @@ 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 const kv_pair<optgroup_flags_t> optgroup_options[];
> > 
> >  /* Global variables used to communicate with passes.  */
> >  extern FILE *dump_file;
> > @@ -442,6 +443,7 @@ dump_enabled_p (void)
> >     (a) the active dump_file, if any
> >     (b) the -fopt-info destination, if any
> >     (c) to the "optinfo" destinations, if any:
> > +       (c.1) as optimization records
> > 
> >     dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
> >                                     |
> > @@ -449,6 +451,7 @@ dump_enabled_p (void)
> >                                     |
> >                                     `--> (c) optinfo
> >                                              `---> optinfo
> > destinations
> > +                                                  (c.1)
> > optimization records
> > 
> >     For optinfos, the dump_*_loc mark the beginning of an optinfo
> >     instance: all subsequent dump_* calls are consolidated into
> > diff --git a/gcc/json.cc b/gcc/json.cc
> > new file mode 100644
> > index 0000000..3c2aa77
> > --- /dev/null
> > +++ b/gcc/json.cc
> > @@ -0,0 +1,293 @@
> > +/* JSON trees
> > +   Copyright (C) 2017-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 "json.h"
> > +#include "pretty-print.h"
> > +#include "math.h"
> > +#include "selftest.h"
> > +
> > +using namespace json;
> > +
> > +/* class json::value.  */
> > +
> > +/* Dump this json::value tree to OUTF.
> > +   No formatting is done.  There are no guarantees about the order
> > +   in which the key/value pairs of json::objects are printed.  */
> > +
> > +void
> > +value::dump (FILE *outf) const
> > +{
> > +  pretty_printer pp;
> > +  pp_buffer (&pp)->stream = outf;
> > +  print (&pp);
> > +  pp_flush (&pp);
> > +}
> > +
> > +/* class json::object, a subclass of json::value, representing
> > +   an unordered collection of key/value pairs.  */
> > +
> > +/* json:object's dtor.  */
> > +
> > +object::~object ()
> > +{
> > +  for (map_t::iterator it = m_map.begin (); it != m_map.end ();
> > ++it)
> > +    {
> > +      free (const_cast <char *>((*it).first));
> > +      delete ((*it).second);
> > +    }
> > +}
> > +
> > +/* Implementation of json::value::print for json::object.  */
> > +
> > +void
> > +object::print (pretty_printer *pp) const
> > +{
> > +  /* Note that the order is not guaranteed.  */
> > +  pp_character (pp, '{');
> > +  for (map_t::iterator it = m_map.begin (); it != m_map.end ();
> > ++it)
> > +    {
> > +      if (it != m_map.begin ())
> > +       pp_string (pp, ", ");
> > +      const char *key = const_cast <char *>((*it).first);
> > +      value *value = (*it).second;
> > +      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
> > +      value->print (pp);
> > +    }
> > +  pp_character (pp, '}');
> > +}
> > +
> > +/* Set the json::value * for KEY, taking ownership of VALUE
> > +   (and taking a copy of KEY if necessary).  */
> > +
> > +void
> > +object::set (const char *key, value *v)
> > +{
> > +  value **ptr = m_map.get (key);
> > +  if (ptr)
> > +    {
> > +      /* If the key is already present, delete the existing value
> > +        and overwrite it.  */
> > +      delete *ptr;
> > +      *ptr = v;
> > +    }
> > +  else
> > +    /* If the key wasn't already present, take a copy of the key,
> > +       and store the value.  */
> > +    m_map.put (xstrdup (key), v);
> > +}
> > +
> > +/* class json::array, a subclass of json::value, representing
> > +   an ordered collection of values.  */
> > +
> > +/* json::array's dtor.  */
> > +
> > +array::~array ()
> > +{
> > +  unsigned i;
> > +  value *v;
> > +  FOR_EACH_VEC_ELT (m_elements, i, v)
> > +    delete v;
> > +}
> > +
> > +/* Implementation of json::value::print for json::array.  */
> > +
> > +void
> > +array::print (pretty_printer *pp) const
> > +{
> > +  pp_character (pp, '[');
> > +  unsigned i;
> > +  value *v;
> > +  FOR_EACH_VEC_ELT (m_elements, i, v)
> > +    {
> > +      if (i)
> > +       pp_string (pp, ", ");
> > +      v->print (pp);
> > +    }
> > +  pp_character (pp, ']');
> > +}
> > +
> > +/* class json::number, a subclass of json::value, wrapping a
> > double.  */
> > +
> > +/* Implementation of json::value::print for json::number.  */
> > +
> > +void
> > +number::print (pretty_printer *pp) const
> > +{
> > +  char tmp[1024];
> > +  snprintf (tmp, sizeof (tmp), "%g", m_value);
> > +  pp_string (pp, tmp);
> > +}
> > +
> > +/* class json::string, a subclass of json::value.  */
> > +
> > +void
> > +string::print (pretty_printer *pp) const
> > +{
> > +  pp_character (pp, '"');
> > +  for (const char *ptr = m_utf8; *ptr; ptr++)
> > +    {
> > +      char ch = *ptr;
> > +      switch (ch)
> > +       {
> > +       case '"':
> > +         pp_string (pp, "\\\"");
> > +         break;
> > +       case '\\':
> > +         pp_string (pp, "\\n");
> > +         break;
> > +       case '\b':
> > +         pp_string (pp, "\\b");
> > +         break;
> > +       case '\f':
> > +         pp_string (pp, "\\f");
> > +         break;
> > +       case '\n':
> > +         pp_string (pp, "\\n");
> > +         break;
> > +       case '\r':
> > +         pp_string (pp, "\\r");
> > +         break;
> > +       case '\t':
> > +         pp_string (pp, "\\t");
> > +         break;
> > +
> > +       default:
> > +         pp_character (pp, ch);
> > +       }
> > +    }
> > +  pp_character (pp, '"');
> > +}
> > +
> > +/* class json::literal, a subclass of json::value.  */
> > +
> > +/* Implementation of json::value::print for json::literal.  */
> > +
> > +void
> > +literal::print (pretty_printer *pp) const
> > +{
> > +  switch (m_kind)
> > +    {
> > +    case JSON_TRUE:
> > +      pp_string (pp, "true");
> > +      break;
> > +    case JSON_FALSE:
> > +      pp_string (pp, "false");
> > +      break;
> > +    case JSON_NULL:
> > +      pp_string (pp, "null");
> > +      break;
> > +    default:
> > +      gcc_unreachable ();
> > +    }
> > +}
> > +
> > +
> > +#if CHECKING_P
> > +
> > +namespace selftest {
> > +
> > +/* Selftests.  */
> > +
> > +/* Verify that JV->print () prints EXPECTED_JSON.  */
> > +
> > +static void
> > +assert_print_eq (const json::value &jv, const char *expected_json)
> > +{
> > +  pretty_printer pp;
> > +  jv.print (&pp);
> > +  ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
> > +}
> > +
> > +/* Verify that JSON objects are written correctly.  We can't test
> > more than
> > +   one key/value pair, as we don't impose a guaranteed
> > ordering.  */
> > +
> > +static void
> > +test_writing_objects ()
> > +{
> > +  object obj;
> > +  obj.set ("foo", new json::string ("bar"));
> > +  assert_print_eq (obj, "{\"foo\": \"bar\"}");
> > +}
> > +
> > +/* Verify that JSON arrays are written correctly.  */
> > +
> > +static void
> > +test_writing_arrays ()
> > +{
> > +  array arr;
> > +  assert_print_eq (arr, "[]");
> > +
> > +  arr.append (new json::string ("foo"));
> > +  assert_print_eq (arr, "[\"foo\"]");
> > +
> > +  arr.append (new json::string ("bar"));
> > +  assert_print_eq (arr, "[\"foo\", \"bar\"]");
> > +}
> > +
> > +/* Verify that JSON numbers are written correctly.  */
> > +
> > +static void
> > +test_writing_numbers ()
> > +{
> > +  assert_print_eq (number (0), "0");
> > +  assert_print_eq (number (42), "42");
> > +  assert_print_eq (number (-100), "-100");
> > +}
> > +
> > +/* Verify that JSON strings are written correctly.  */
> > +
> > +static void
> > +test_writing_strings ()
> > +{
> > +  string foo ("foo");
> > +  assert_print_eq (foo, "\"foo\"");
> > +
> > +  string contains_quotes ("before \"quoted\" after");
> > +  assert_print_eq (contains_quotes, "\"before \\\"quoted\\\"
> > after\"");
> > +}
> > +
> > +/* Verify that JSON strings are written correctly.  */
> > +
> > +static void
> > +test_writing_literals ()
> > +{
> > +  assert_print_eq (literal (JSON_TRUE), "true");
> > +  assert_print_eq (literal (JSON_FALSE), "false");
> > +  assert_print_eq (literal (JSON_NULL), "null");
> > +}
> > +
> > +/* Run all of the selftests within this file.  */
> > +
> > +void
> > +json_cc_tests ()
> > +{
> > +  test_writing_objects ();
> > +  test_writing_arrays ();
> > +  test_writing_numbers ();
> > +  test_writing_strings ();
> > +  test_writing_literals ();
> > +}
> > +
> > +} // namespace selftest
> > +
> > +#endif /* #if CHECKING_P */
> > diff --git a/gcc/json.h b/gcc/json.h
> > new file mode 100644
> > index 0000000..5c3274c
> > --- /dev/null
> > +++ b/gcc/json.h
> > @@ -0,0 +1,166 @@
> > +/* JSON trees
> > +   Copyright (C) 2017-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_JSON_H
> > +#define GCC_JSON_H
> > +
> > +/* Implementation of JSON, a lightweight data-interchange format.
> > +
> > +   See http://www.json.org/
> > +   and http://www.ecma-international.org/publications/files/ECMA-S
> > T/ECMA-404.pdf
> > +   and https://tools.ietf.org/html/rfc7159
> > +
> > +   Supports creating a DOM-like tree of json::value *, and then
> > dumping
> > +   json::value * to text.  */
> > +
> > +namespace json
> > +{
> > +
> > +/* Forward decls of json::value and its subclasses (using
> > indentation
> > +   to denote inheritance.  */
> > +
> > +class value;
> > +  class object;
> > +  class array;
> > +  class number;
> > +  class string;
> > +  class literal;
> > +
> > +/* An enum for discriminating the subclasses of json::value.  */
> > +
> > +enum kind
> > +{
> > +  /* class json::object.  */
> > +  JSON_OBJECT,
> > +
> > +  /* class json::array.  */
> > +  JSON_ARRAY,
> > +
> > +  /* class json::number.  */
> > +  JSON_NUMBER,
> > +
> > +  /* class json::string.  */
> > +  JSON_STRING,
> > +
> > +  /* class json::literal uses these three values to identify the
> > +     particular literal.  */
> > +  JSON_TRUE,
> > +  JSON_FALSE,
> > +  JSON_NULL
> > +};
> > +
> > +/* Base class of JSON value.  */
> > +
> > +class value
> > +{
> > + public:
> > +  virtual ~value () {}
> > +  virtual enum kind get_kind () const = 0;
> > +  virtual void print (pretty_printer *pp) const = 0;
> > +
> > +  void dump (FILE *) const;
> > +};
> > +
> > +/* Subclass of value for objects: an unordered collection of
> > +   key/value pairs.  */
> > +
> > +class object : public value
> > +{
> > + public:
> > +  ~object ();
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  void set (const char *key, value *v);
> > +
> > + private:
> > +  typedef hash_map <char *, value *,
> > +    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
> > +  map_t m_map;
> > +};
> > +
> > +/* Subclass of value for arrays.  */
> > +
> > +class array : public value
> > +{
> > + public:
> > +  ~array ();
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  void append (value *v) { m_elements.safe_push (v); }
> > +
> > + private:
> > +  auto_vec<value *> m_elements;
> > +};
> > +
> > +/* Subclass of value for numbers.  */
> > +
> > +class number : public value
> > +{
> > + public:
> > +  number (double value) : m_value (value) {}
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  double get () const { return m_value; }
> > +
> > + private:
> > +  double m_value;
> > +};
> > +
> > +/* Subclass of value for strings.  */
> > +
> > +class string : public value
> > +{
> > + public:
> > +  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
> > +  ~string () { free (m_utf8); }
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING;
> > }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > +  const char *get_string () const { return m_utf8; }
> > +
> > + private:
> > +  char *m_utf8;
> > +};
> > +
> > +/* Subclass of value for the three JSON literals "true", "false",
> > +   and "null".  */
> > +
> > +class literal : public value
> > +{
> > + public:
> > +  literal (enum kind kind) : m_kind (kind) {}
> > +
> > +  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
> > +  void print (pretty_printer *pp) const FINAL OVERRIDE;
> > +
> > + private:
> > +  enum kind m_kind;
> > +};
> > +
> > +} // namespace json
> > +
> > +#endif  /* GCC_JSON_H  */
> > diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
> > new file mode 100644
> > index 0000000..bf1172a
> > --- /dev/null
> > +++ b/gcc/optinfo-emit-json.cc
> > @@ -0,0 +1,568 @@
> > +/* Emit optimization information as JSON files.
> > +   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 "diagnostic-core.h"
> > +
> > +#include "profile.h"
> > +#include "output.h"
> > +#include "tree-pass.h"
> > +
> > +#include "optinfo.h"
> > +#include "optinfo-emit-json.h"
> > +#include "json.h"
> > +#include "pretty-print.h"
> > +#include "tree-pretty-print.h"
> > +#include "gimple-pretty-print.h"
> > +#include "cgraph.h"
> > +
> > +#include "langhooks.h"
> > +#include "version.h"
> > +#include "context.h"
> > +#include "pass_manager.h"
> > +#include "selftest.h"
> > +#include "dump-context.h"
> > +
> > +/* A class for writing out optimization records in JSON
> > format.  */
> > +
> > +class optrecord_json_writer
> > +{
> > +public:
> > +  optrecord_json_writer ();
> > +  ~optrecord_json_writer ();
> > +  void write () const;
> > +  void add_record (const optinfo *optinfo);
> > +  void pop_scope ();
> > +
> > +  void add_record (json::object *obj);
> > +  json::object *impl_location_to_json (dump_impl_location_t loc);
> > +  json::object *location_to_json (location_t loc);
> > +  json::object *profile_count_to_json (profile_count count);
> > +  json::string *get_id_value_for_pass (opt_pass *pass);
> > +  json::object *pass_to_json (opt_pass *pass);
> > +  json::value *inlining_chain_to_json (location_t loc);
> > +  json::object *optinfo_to_json (const optinfo *optinfo);
> > +  void add_pass_list (json::array *arr, opt_pass *pass);
> > +
> > +private:
> > +  /* The root value for the JSON file.
> > +     Currently the JSON values are stored in memory, and flushed
> > when the
> > +     compiler exits.  It would probably be better to simply write
> > out
> > +     the JSON as we go.  */
> > +  json::array *m_root_tuple;
> > +
> > +  /* The currently open scopes, for expressing nested optimization
> > records.  */
> > +  vec<json::array *> m_scopes;
> > +};
> > +
> > +/* optrecord_json_writer's ctor.  Populate the top-level parts of
> > the
> > +   in-memory JSON representation.  */
> > +
> > +optrecord_json_writer::optrecord_json_writer ()
> > +  : m_root_tuple (NULL), m_scopes ()
> > +{
> > +  m_root_tuple = new json::array ();
> > +
> > +  /* Populate with metadata; compare with toplev.c:
> > print_version.  */
> > +  json::object *metadata = new json::object ();
> > +  m_root_tuple->append (metadata);
> > +  metadata->set ("format", new json::string ("1"));
> > +  json::object *generator = new json::object ();
> > +  metadata->set ("generator", generator);
> > +  generator->set ("name", new json::string (lang_hooks.name));
> > +  generator->set ("pkgversion", new json::string
> > (pkgversion_string));
> > +  generator->set ("version", new json::string (version_string));
> > +  /* TARGET_NAME is passed in by the Makefile.  */
> > +  generator->set ("target", new json::string (TARGET_NAME));
> > +
> > +  /* TODO: capture command-line?
> > +     see gen_producer_string in dwarf2out.c (currently
> > static).  */
> > +
> > +  /* TODO: capture "any plugins?" flag (or the plugins
> > themselves).  */
> > +
> > +  json::array *passes = new json::array ();
> > +  m_root_tuple->append (passes);
> > +
> > +  /* Call add_pass_list for all of the pass lists.  */
> > +  {
> > +#define DEF_PASS_LIST(LIST) \
> > +    add_pass_list (passes, g->get_passes ()->LIST);
> > +    GCC_PASS_LISTS
> > +#undef DEF_PASS_LIST
> > +  }
> > +
> > +  json::array *records = new json::array ();
> > +  m_root_tuple->append (records);
> > +
> > +  m_scopes.safe_push (records);
> > +}
> > +
> > +/* optrecord_json_writer's ctor.
> > +   Delete the in-memory JSON representation.  */
> > +
> > +optrecord_json_writer::~optrecord_json_writer ()
> > +{
> > +  delete m_root_tuple;
> > +}
> > +
> > +/* Choose an appropriate filename, and write the saved records to
> > it.  */
> > +
> > +void
> > +optrecord_json_writer::write () const
> > +{
> > +  char *filename = concat (dump_base_name, ".opt-record.json",
> > NULL);
> > +  FILE *outfile = fopen (filename, "w");
> > +  if (outfile)
> > +    {
> > +      m_root_tuple->dump (outfile);
> > +      fclose (outfile);
> > +    }
> > +  else
> > +    error_at (UNKNOWN_LOCATION, "unable to write optimization
> > records to %qs",
> > +             filename); // FIXME: more info?
> > +  free (filename);
> > +}
> > +
> > +/* Add a record for OPTINFO to the queue of records to be
> > written.  */
> > +
> > +void
> > +optrecord_json_writer::add_record (const optinfo *optinfo)
> > +{
> > +  json::object *obj = optinfo_to_json (optinfo);
> > +
> > +  add_record (obj);
> > +
> > +  /* Potentially push the scope.  */
> > +  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
> > +    {
> > +      json::array *children = new json::array ();
> > +      obj->set ("children", children);
> > +      m_scopes.safe_push (children);
> > +    }
> > +}
> > +
> > +/* Private methods of optrecord_json_writer.  */
> > +
> > +/* Add record OBJ to the the innermost scope.  */
> > +
> > +void
> > +optrecord_json_writer::add_record (json::object *obj)
> > +{
> > +  /* Add to innermost scope.  */
> > +  gcc_assert (m_scopes.length () > 0);
> > +  m_scopes[m_scopes.length () - 1]->append (obj);
> > +}
> > +
> > +/* Pop the innermost scope.  */
> > +
> > +void
> > +optrecord_json_writer::pop_scope ()
> > +{
> > +  m_scopes.pop ();
> > +}
> > +
> > +/* Create a JSON object representing LOC.  */
> > +
> > +json::object *
> > +optrecord_json_writer::impl_location_to_json (dump_impl_location_t
> > loc)
> > +{
> > +  json::object *obj = new json::object ();
> > +  obj->set ("file", new json::string (loc.m_file));
> > +  obj->set ("line", new json::number (loc.m_line));
> > +  if (loc.m_function)
> > +    obj->set ("function", new json::string (loc.m_function));
> > +  return obj;
> > +}
> > +
> > +/* Create a JSON object representing LOC.  */
> > +
> > +json::object *
> > +optrecord_json_writer::location_to_json (location_t loc)
> > +{
> > +  json::object *obj = new json::object ();
> > +  obj->set ("file", new json::string (LOCATION_FILE (loc)));
> > +  obj->set ("line", new json::number (LOCATION_LINE (loc)));
> > +  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
> > +  return obj;
> > +}
> > +
> > +/* Create a JSON object representing COUNT.  */
> > +
> > +json::object *
> > +optrecord_json_writer::profile_count_to_json (profile_count count)
> > +{
> > +  json::object *obj = new json::object ();
> > +  obj->set ("value", new json::number (count.to_gcov_type ()));
> > +  obj->set ("quality",
> > +           new json::string (profile_quality_as_string
> > (count.quality ())));
> > +  return obj;
> > +}
> > +
> > +/* Get a string for use when referring to PASS in the saved
> > optimization
> > +   records.  */
> > +
> > +json::string *
> > +optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
> > +{
> > +  pretty_printer pp;
> > +  /* this is host-dependent, but will be consistent for a given
> > host.  */
> > +  pp_pointer (&pp, static_cast<void *> (pass));
> > +  return new json::string (pp_formatted_text (&pp));
> > +}
> > +
> > +/* Create a JSON object representing PASS.  */
> > +
> > +json::object *
> > +optrecord_json_writer::pass_to_json (opt_pass *pass)
> > +{
> > +  json::object *obj = new json::object ();
> > +  const char *type = NULL;
> > +  switch (pass->type)
> > +    {
> > +    default:
> > +      gcc_unreachable ();
> > +    case GIMPLE_PASS:
> > +      type = "gimple";
> > +      break;
> > +    case RTL_PASS:
> > +      type = "rtl";
> > +      break;
> > +    case SIMPLE_IPA_PASS:
> > +      type = "simple_ipa";
> > +      break;
> > +    case IPA_PASS:
> > +      type = "ipa";
> > +      break;
> > +    }
> > +  obj->set ("id", get_id_value_for_pass (pass));
> > +  obj->set ("type", new json::string (type));
> > +  obj->set ("name", new json::string (pass->name));
> > +  /* Represent the optgroup flags as an array.  */
> > +  {
> > +    json::array *optgroups = new json::array ();
> > +    obj->set ("optgroups", optgroups);
> > +    for (const kv_pair<optgroup_flags_t> *optgroup =
> > optgroup_options;
> > +        optgroup->name != NULL; optgroup++)
> > +      if (optgroup->value != OPTGROUP_ALL
> > +         && (pass->optinfo_flags & optgroup->value))
> > +       optgroups->append (new json::string (optgroup->name));
> > +  }
> > +  obj->set ("num", new json::number (pass->static_pass_number));
> > +  return obj;
> > +}
> > +
> > +/* Create a JSON array for LOC representing the chain of inlining
> > +   locations.
> > +   Compare with lhd_print_error_function and
> > cp_print_error_function.  */
> > +
> > +json::value *
> > +optrecord_json_writer::inlining_chain_to_json (location_t loc)
> > +{
> > +  json::array *array = new json::array ();
> > +
> > +  tree abstract_origin = LOCATION_BLOCK (loc);
> > +
> > +  while (abstract_origin)
> > +    {
> > +      location_t *locus;
> > +      tree block = abstract_origin;
> > +
> > +      locus = &BLOCK_SOURCE_LOCATION (block);
> > +      tree fndecl = NULL;
> > +      block = BLOCK_SUPERCONTEXT (block);
> > +      while (block && TREE_CODE (block) == BLOCK
> > +            && BLOCK_ABSTRACT_ORIGIN (block))
> > +       {
> > +         tree ao = BLOCK_ABSTRACT_ORIGIN (block);
> > +
> > +         while (TREE_CODE (ao) == BLOCK
> > +                && BLOCK_ABSTRACT_ORIGIN (ao)
> > +                && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
> > +           ao = BLOCK_ABSTRACT_ORIGIN (ao);
> > +
> > +         if (TREE_CODE (ao) == FUNCTION_DECL)
> > +           {
> > +             fndecl = ao;
> > +             break;
> > +           }
> > +         else if (TREE_CODE (ao) != BLOCK)
> > +           break;
> > +
> > +         block = BLOCK_SUPERCONTEXT (block);
> > +       }
> > +      if (fndecl)
> > +       abstract_origin = block;
> > +      else
> > +       {
> > +         while (block && TREE_CODE (block) == BLOCK)
> > +           block = BLOCK_SUPERCONTEXT (block);
> > +
> > +         if (block && TREE_CODE (block) == FUNCTION_DECL)
> > +           fndecl = block;
> > +         abstract_origin = NULL;
> > +       }
> > +      if (fndecl)
> > +       {
> > +         json::object *obj = new json::object ();
> > +         const char *printable_name
> > +           = lang_hooks.decl_printable_name (fndecl, 2);
> > +         obj->set ("fndecl", new json::string (printable_name));
> > +         if (*locus != UNKNOWN_LOCATION)
> > +           obj->set ("site", location_to_json (*locus));
> > +         array->append (obj);
> > +       }
> > +    }
> > +
> > +  return array;
> > +}
> > +
> > +/* Create a JSON object representing OPTINFO.  */
> > +
> > +json::object *
> > +optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
> > +{
> > +  json::object *obj = new json::object ();
> > +
> > +  obj->set ("impl_location",
> > +           impl_location_to_json (optinfo->get_impl_location ()));
> > +
> > +  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind 
> > ());
> > +  obj->set ("kind", new json::string (kind_str));
> > +  json::array *message = new json::array ();
> > +  obj->set ("message", message);
> > +  for (unsigned i = 0; i < optinfo->num_items (); i++)
> > +    {
> > +      const optinfo_item *item = optinfo->get_item (i);
> > +      switch (item->get_kind ())
> > +       {
> > +       default:
> > +         gcc_unreachable ();
> > +       case OPTINFO_ITEM_KIND_TEXT:
> > +         {
> > +           message->append (new json::string (item->get_text ()));
> > +         }
> > +         break;
> > +       case OPTINFO_ITEM_KIND_TREE:
> > +         {
> > +           json::object *json_item = new json::object ();
> > +           json_item->set ("expr", new json::string (item-
> > >get_text ()));
> > +
> > +           /* Capture any location for the node.  */
> > +           if (item->get_location () != UNKNOWN_LOCATION)
> > +             json_item->set ("location", location_to_json (item-
> > >get_location ()));
> > +
> > +           message->append (json_item);
> > +         }
> > +         break;
> > +       case OPTINFO_ITEM_KIND_GIMPLE:
> > +         {
> > +           json::object *json_item = new json::object ();
> > +           json_item->set ("stmt", new json::string (item-
> > >get_text ()));
> > +
> > +           /* Capture any location for the stmt.  */
> > +           if (item->get_location () != UNKNOWN_LOCATION)
> > +             json_item->set ("location", location_to_json (item-
> > >get_location ()));
> > +
> > +           message->append (json_item);
> > +         }
> > +         break;
> > +       case OPTINFO_ITEM_KIND_SYMTAB_NODE:
> > +         {
> > +           json::object *json_item = new json::object ();
> > +           json_item->set ("symtab_node", new json::string (item-
> > >get_text ()));
> > +
> > +           /* Capture any location for the node.  */
> > +           if (item->get_location () != UNKNOWN_LOCATION)
> > +             json_item->set ("location", location_to_json (item-
> > >get_location ()));
> > +           message->append (json_item);
> > +         }
> > +         break;
> > +       }
> > +   }
> > +
> > +  if (optinfo->get_pass ())
> > +    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass
> > ()));
> > +
> > +  profile_count count = optinfo->get_count ();
> > +  if (count.initialized_p ())
> > +    obj->set ("count", profile_count_to_json (count));
> > +
> > +  /* Record any location, handling the case where of an
> > UNKNOWN_LOCATION
> > +     within an inlined block.  */
> > +  location_t loc = optinfo->get_location_t ();
> > +  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
> > +    {
> > +      // TOOD: record the location (just caret for now)
> > +      // TODO: start/finish also?
> > +      obj->set ("location", location_to_json (loc));
> > +    }
> > +
> > +  if (current_function_decl)
> > +    {
> > +      const char *fnname = get_fnname_from_decl
> > (current_function_decl);
> > +      obj->set ("function", new json::string (fnname));
> > +    }
> > +
> > +  if (loc != UNKNOWN_LOCATION)
> > +    obj->set ("inlining_chain", inlining_chain_to_json (loc));
> > +
> > +  return obj;
> > +}
> > +
> > +/* Add a json description of PASS and its siblings to ARR,
> > recursing into
> > +   child passes (adding their descriptions within a "children"
> > array).  */
> > +
> > +void
> > +optrecord_json_writer::add_pass_list (json::array *arr, opt_pass
> > *pass)
> > +{
> > +  do
> > +    {
> > +      json::object *pass_obj = pass_to_json (pass);
> > +      arr->append (pass_obj);
> > +      if (pass->sub)
> > +       {
> > +         json::array *sub = new json::array ();
> > +         pass_obj->set ("children", sub);
> > +         add_pass_list (sub, pass->sub);
> > +       }
> > +      pass = pass->next;
> > +    }
> > +  while (pass);
> > +}
> > +
> > +/* File-level interface to rest of compiler (to avoid exposing
> > +   class optrecord_json_writer outside of this file).  */
> > +
> > +static optrecord_json_writer *the_json_writer;
> > +
> > +/* Perform startup activity for -fsave-optimization-record.  */
> > +
> > +void
> > +optimization_records_start ()
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!flag_save_optimization_record)
> > +    return;
> > +
> > +  the_json_writer = new optrecord_json_writer ();
> > +}
> > +
> > +/* Perform cleanup activity for -fsave-optimization-record.
> > +
> > +   Currently, the file is written out here in one go, before
> > cleaning
> > +   up.  */
> > +
> > +void
> > +optimization_records_finish ()
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!the_json_writer)
> > +    return;
> > +
> > +  the_json_writer->write ();
> > +
> > +  delete the_json_writer;
> > +  the_json_writer = NULL;
> > +}
> > +
> > +/* Did the user request optimization records to be written
> > out?  */
> > +
> > +bool
> > +optimization_records_enabled_p ()
> > +{
> > +  return the_json_writer != NULL;
> > +}
> > +
> > +/* If optimization records were requested, then add a record for
> > OPTINFO
> > +   to the queue of records to be written.  */
> > +
> > +void
> > +optimization_records_maybe_record_optinfo (const optinfo *optinfo)
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!the_json_writer)
> > +    return;
> > +
> > +  the_json_writer->add_record (optinfo);
> > +}
> > +
> > +/* Handling for the end of a dump scope for the
> > +   optimization records sink.  */
> > +
> > +void
> > +optimization_records_maybe_pop_dump_scope ()
> > +{
> > +  /* Bail if recording not enabled.  */
> > +  if (!the_json_writer)
> > +    return;
> > +
> > +  the_json_writer->pop_scope ();
> > +}
> > +
> > +#if CHECKING_P
> > +
> > +namespace selftest {
> > +
> > +/* Verify that we can build a JSON optimization record from dump_*
> > +   calls.  */
> > +
> > +static void
> > +test_building_json_from_dump_calls ()
> > +{
> > +  temp_dump_context tmp (true);
> > +  dump_location_t loc;
> > +  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->num_items (), 2);
> > +
> > +  optrecord_json_writer writer;
> > +  json::object *json_obj = writer.optinfo_to_json (info);
> > +  ASSERT_TRUE (json_obj != NULL);
> > +
> > +  /* Verify that the json is sane.  */
> > +  pretty_printer pp;
> > +  json_obj->print (&pp);
> > +  const char *json_str = pp_formatted_text (&pp);
> > +  ASSERT_STR_CONTAINS (json_str, "impl_location");
> > +  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
> > +  ASSERT_STR_CONTAINS (json_str,
> > +                      "\"message\": [\"test of tree: \",
> > {\"expr\": \"0\"}]");
> > +  delete json_obj;
> > +}
> > +
> > +/* Run all of the selftests within this file.  */
> > +
> > +void
> > +optinfo_emit_json_cc_tests ()
> > +{
> > +  test_building_json_from_dump_calls ();
> > +}
> > +
> > +} // namespace selftest
> > +
> > +#endif /* CHECKING_P */
> > diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
> > new file mode 100644
> > index 0000000..3628d56
> > --- /dev/null
> > +++ b/gcc/optinfo-emit-json.h
> > @@ -0,0 +1,36 @@
> > +/* Emit optimization information as JSON files.
> > +   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_EMIT_JSON_H
> > +#define GCC_OPTINFO_EMIT_JSON_H
> > +
> > +class optinfo;
> > +struct opt_pass;
> > +
> > +extern void optimization_records_start ();
> > +extern void optimization_records_finish ();
> > +
> > +extern bool optimization_records_enabled_p ();
> > +
> > +extern void optimization_records_maybe_record_optinfo (const
> > optinfo *);
> > +extern void optimization_records_maybe_pop_dump_scope ();
> > +
> > +
> > +#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
> > diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
> > index 6f224bc..93de9d9 100644
> > --- a/gcc/optinfo.cc
> > +++ b/gcc/optinfo.cc
> > @@ -27,6 +27,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "gimple.h"
> > 
> >  #include "optinfo.h"
> > +#include "optinfo-emit-json.h"
> >  #include "dump-context.h"
> >  #include "pretty-print.h"
> >  #include "gimple-pretty-print.h"
> > @@ -85,7 +86,8 @@ optinfo::~optinfo ()
> >  void
> >  optinfo::emit ()
> >  {
> > -  /* currently this is a no-op.  */
> > +  /* -fsave-optimization-record.  */
> > +  optimization_records_maybe_record_optinfo (this);
> >  }
> > 
> >  /* Update the optinfo's kind based on DUMP_KIND.  */
> > @@ -221,9 +223,8 @@ optinfo::add_dec (const wide_int_ref &wi,
> > signop sgn)
> > 
> >  bool optinfo_enabled_p ()
> >  {
> > -  /* Currently no destinations are implemented, just a hook for
> > -     selftests.  */
> > -  return dump_context::get ().forcibly_enable_optinfo_p ();
> > +  return (dump_context::get ().forcibly_enable_optinfo_p ()
> > +         || optimization_records_enabled_p ());
> >  }
> > 
> >  /* Return true if any of the active optinfo destinations make use
> > @@ -232,5 +233,5 @@ bool optinfo_enabled_p ()
> > 
> >  bool optinfo_wants_inlining_info_p ()
> >  {
> > -  return false;
> > +  return optimization_records_enabled_p ();
> >  }
> > diff --git a/gcc/optinfo.h b/gcc/optinfo.h
> > index 5bdb9eb..5f09022 100644
> > --- a/gcc/optinfo.h
> > +++ b/gcc/optinfo.h
> > @@ -25,12 +25,8 @@ along with GCC; see the file COPYING3.  If not
> > see
> >     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
> > diff --git a/gcc/profile-count.c b/gcc/profile-count.c
> > index 3d411cf..6a17f5e 100644
> > --- a/gcc/profile-count.c
> > +++ b/gcc/profile-count.c
> > @@ -33,6 +33,34 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "wide-int.h"
> >  #include "sreal.h"
> > 
> > +/* Get a string describing QUALITY.  */
> > +
> > +const char *
> > +profile_quality_as_string (enum profile_quality quality)
> > +{
> > +  switch (quality)
> > +    {
> > +    default:
> > +      gcc_unreachable ();
> > +    case profile_uninitialized:
> > +      return "uninitialized";
> > +    case profile_guessed_local:
> > +      return "guessed_local";
> > +    case profile_guessed_global0:
> > +      return "guessed_global0";
> > +    case profile_guessed_global0adjusted:
> > +      return "guessed_global0adjusted";
> > +    case profile_guessed:
> > +      return "guessed";
> > +    case profile_afdo:
> > +      return "afdo";
> > +    case profile_adjusted:
> > +      return "adjusted";
> > +    case profile_precise:
> > +      return "precise";
> > +    }
> > +}
> > +
> >  /* Dump THIS to F.  */
> > 
> >  void
> > diff --git a/gcc/profile-count.h b/gcc/profile-count.h
> > index c83fa3b..f4d0c340 100644
> > --- a/gcc/profile-count.h
> > +++ b/gcc/profile-count.h
> > @@ -59,6 +59,8 @@ enum profile_quality {
> >    profile_precise
> >  };
> > 
> > +extern const char *profile_quality_as_string (enum
> > profile_quality);
> > +
> >  /* The base value for branch probability notes and edge
> > probabilities.  */
> >  #define REG_BR_PROB_BASE  10000
> > 
> > @@ -721,6 +723,9 @@ public:
> >        return m_quality == profile_precise;
> >      }
> > 
> > +  /* Get the quality of the count.  */
> > +  enum profile_quality quality () const { return m_quality; }
> > +
> >    /* When merging basic blocks, the two different profile counts
> > are unified.
> >       Return true if this can be done without losing info about
> > profile.
> >       The only case we care about here is when first BB contains
> > something
> > diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
> > index 7f4d6f3..5adb033 100644
> > --- a/gcc/selftest-run-tests.c
> > +++ b/gcc/selftest-run-tests.c
> > @@ -72,6 +72,8 @@ selftest::run_tests ()
> >    typed_splay_tree_c_tests ();
> >    unique_ptr_tests_cc_tests ();
> >    opt_proposer_c_tests ();
> > +  json_cc_tests ();
> > +  optinfo_emit_json_cc_tests ();
> > 
> >    /* Mid-level data structures.  */
> >    input_c_tests ();
> > diff --git a/gcc/selftest.h b/gcc/selftest.h
> > index 54fc488..ede7732 100644
> > --- a/gcc/selftest.h
> > +++ b/gcc/selftest.h
> > @@ -228,6 +228,8 @@ 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 json_cc_tests ();
> > +extern void optinfo_emit_json_cc_tests ();
> >  extern void predict_c_tests ();
> >  extern void pretty_print_c_tests ();
> >  extern void read_rtl_function_c_tests ();
> > diff --git a/gcc/toplev.c b/gcc/toplev.c
> > index d108096..a047390 100644
> > --- a/gcc/toplev.c
> > +++ b/gcc/toplev.c
> > @@ -83,6 +83,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "tree-pass.h"
> >  #include "dumpfile.h"
> >  #include "ipa-fnsummary.h"
> > +#include "optinfo-emit-json.h"
> > 
> >  #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
> >  #include "dbxout.h"
> > @@ -487,6 +488,8 @@ compile_file (void)
> >    if (lang_hooks.decls.post_compilation_parsing_cleanups)
> >      lang_hooks.decls.post_compilation_parsing_cleanups ();
> > 
> > +  optimization_records_finish ();
> > +
> >    if (seen_error ())
> >      return;
> > 
> > @@ -2048,6 +2051,8 @@ do_compile ()
> > 
> >        timevar_start (TV_PHASE_SETUP);
> > 
> > +      optimization_records_start ();
> > +
> >        /* This must be run always, because it is needed to compute
> > the FP
> >          predefined macros, such as __LDBL_MAX__, for targets using
> > non
> >          default FP formats.  */
> > diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
> > index 7447f7a..2623d9b 100644
> > --- a/gcc/tree-ssa-live.c
> > +++ b/gcc/tree-ssa-live.c
> > @@ -40,6 +40,7 @@ along with GCC; see the file COPYING3.  If not
> > see
> >  #include "cfgloop.h"
> >  #include "stringpool.h"
> >  #include "attribs.h"
> > +#include "optinfo.h"
> > 
> >  static void verify_live_on_entry (tree_live_info_p);
> > 
> > @@ -552,7 +553,8 @@ remove_unused_scope_block_p (tree scope, bool
> > in_ctor_dtor_block)
> >       ;
> >     /* When not generating debug info we can eliminate info on
> > unused
> >        variables.  */
> > -   else if (!flag_auto_profile && debug_info_level ==
> > DINFO_LEVEL_NONE)
> > +   else if (!flag_auto_profile && debug_info_level ==
> > DINFO_LEVEL_NONE
> > +           && !optinfo_wants_inlining_info_p ())
> >       {
> >         /* Even for -g0 don't prune outer scopes from artificial
> >           functions, otherwise diagnostics using
> > tree_nonartificial_location
> > --
> > 1.8.5.3
> >
diff mbox series

Patch

diff --git a/gcc/Makefile.in b/gcc/Makefile.in
index dd1dfc1..b871640 100644
--- a/gcc/Makefile.in
+++ b/gcc/Makefile.in
@@ -1387,6 +1387,7 @@  OBJS = \
 	ira-color.o \
 	ira-emit.o \
 	ira-lives.o \
+	json.o \
 	jump.o \
 	langhooks.o \
 	lcm.o \
@@ -1428,6 +1429,7 @@  OBJS = \
 	optabs-query.o \
 	optabs-tree.o \
 	optinfo.o \
+	optinfo-emit-json.o \
 	options-save.o \
 	opts-global.o \
 	passes.o \
@@ -2251,6 +2253,7 @@  s-bversion: BASE-VER
 	$(STAMP) s-bversion
 
 CFLAGS-toplev.o += -DTARGET_NAME=\"$(target_noncanonical)\"
+CFLAGS-optinfo-emit-json.o += -DTARGET_NAME=\"$(target_noncanonical)\"
 
 pass-instances.def: $(srcdir)/passes.def $(PASSES_EXTRA) \
 		    $(srcdir)/gen-pass-instances.awk
diff --git a/gcc/common.opt b/gcc/common.opt
index 5a50bc27..a13c709 100644
--- a/gcc/common.opt
+++ b/gcc/common.opt
@@ -1950,6 +1950,10 @@  fopt-info-
 Common Joined RejectNegative Var(common_deferred_options) Defer
 -fopt-info[-<type>=filename]	Dump compiler optimization details.
 
+fsave-optimization-record
+Common Report Var(flag_save_optimization_record) Optimization
+Write a SRCFILE.opt-record.json file detailing what optimizations were performed.
+
 foptimize-register-move
 Common Ignore
 Does nothing. Preserved for backward compatibility.
diff --git a/gcc/coretypes.h b/gcc/coretypes.h
index ed0e825..2fd20e4 100644
--- a/gcc/coretypes.h
+++ b/gcc/coretypes.h
@@ -332,6 +332,14 @@  namespace gcc {
 
 typedef std::pair <tree, tree> tree_pair;
 
+/* Define a name->value mapping.  */
+template <typename ValueType>
+struct kv_pair
+{
+  const char *const name;	/* the name of the value */
+  const ValueType value;	/* the value of the name */
+};
+
 #else
 
 struct _dont_use_rtx_here_;
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 56cd122..c239c53 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -417,7 +417,8 @@  Objective-C and Objective-C++ Dialects}.
 -freorder-blocks-algorithm=@var{algorithm} @gol
 -freorder-blocks-and-partition  -freorder-functions @gol
 -frerun-cse-after-loop  -freschedule-modulo-scheduled-loops @gol
--frounding-math  -fsched2-use-superblocks  -fsched-pressure @gol
+-frounding-math  -fsave-optimization-record @gol
+-fsched2-use-superblocks  -fsched-pressure @gol
 -fsched-spec-load  -fsched-spec-load-dangerous @gol
 -fsched-stalled-insns-dep[=@var{n}]  -fsched-stalled-insns[=@var{n}] @gol
 -fsched-group-heuristic  -fsched-critical-path-heuristic @gol
@@ -9907,6 +9908,11 @@  Future versions of GCC may provide finer control of this setting
 using C99's @code{FENV_ACCESS} pragma.  This command-line option
 will be used to specify the default state for @code{FENV_ACCESS}.
 
+@item -fsave-optimization-record
+@opindex fsave-optimization-record
+Write a SRCFILE.opt-record.json file detailing what optimizations
+were performed.
+
 @item -fsignaling-nans
 @opindex fsignaling-nans
 Compile code assuming that IEEE signaling NaNs may generate user-visible
diff --git a/gcc/dumpfile.c b/gcc/dumpfile.c
index 38f9539..331b7fb 100644
--- a/gcc/dumpfile.c
+++ b/gcc/dumpfile.c
@@ -37,6 +37,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "dump-context.h"
 #include "cgraph.h"
 #include "tree-pass.h" /* for "current_pass".  */
+#include "optinfo-emit-json.h"
 
 /* If non-NULL, return one past-the-end of the matching SUBPART of
    the WHOLE string.  */
@@ -118,14 +119,6 @@  static struct dump_file_info dump_files[TDI_end] =
   DUMP_FILE_INFO (NULL, "ipa-all", DK_ipa, 0),
 };
 
-/* Define a name->number mapping for a dump flag value.  */
-template <typename ValueType>
-struct kv_pair
-{
-  const char *const name;	/* the name of the value */
-  const ValueType value;	/* the value of the name */
-};
-
 /* Table of dump options. This must be consistent with the TDF_* flags
    in dumpfile.h and opt_info_options below. */
 static const kv_pair<dump_flags_t> dump_options[] =
@@ -176,7 +169,7 @@  static const kv_pair<dump_flags_t> optinfo_verbosity_options[] =
 };
 
 /* Flags used for -fopt-info groups.  */
-static const kv_pair<optgroup_flags_t> optgroup_options[] =
+const kv_pair<optgroup_flags_t> optgroup_options[] =
 {
   {"ipa", OPTGROUP_IPA},
   {"loop", OPTGROUP_LOOP},
@@ -794,6 +787,7 @@  dump_context::end_scope ()
 {
   end_any_optinfo ();
   m_scope_depth--;
+  optimization_records_maybe_pop_dump_scope ();
 }
 
 /* Return the optinfo currently being accumulated, creating one if
@@ -820,8 +814,7 @@  dump_context::begin_next_optinfo (const dump_location_t &loc)
 }
 
 /* End any optinfo that has been accumulated within this context; emitting
-   it to any destinations as appropriate - though none have currently been
-   implemented.  */
+   it to any destinations as appropriate, such as optimization records.  */
 
 void
 dump_context::end_any_optinfo ()
diff --git a/gcc/dumpfile.h b/gcc/dumpfile.h
index 3096a89..e4823f8 100644
--- a/gcc/dumpfile.h
+++ b/gcc/dumpfile.h
@@ -420,6 +420,7 @@  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 const kv_pair<optgroup_flags_t> optgroup_options[];
 
 /* Global variables used to communicate with passes.  */
 extern FILE *dump_file;
@@ -442,6 +443,7 @@  dump_enabled_p (void)
    (a) the active dump_file, if any
    (b) the -fopt-info destination, if any
    (c) to the "optinfo" destinations, if any:
+       (c.1) as optimization records
 
    dump_* (MSG_*) --> dumpfile.c --+--> (a) dump_file
                                    |
@@ -449,6 +451,7 @@  dump_enabled_p (void)
                                    |
                                    `--> (c) optinfo
                                             `---> optinfo destinations
+                                                  (c.1) optimization records
 
    For optinfos, the dump_*_loc mark the beginning of an optinfo
    instance: all subsequent dump_* calls are consolidated into
diff --git a/gcc/json.cc b/gcc/json.cc
new file mode 100644
index 0000000..3c2aa77
--- /dev/null
+++ b/gcc/json.cc
@@ -0,0 +1,293 @@ 
+/* JSON trees
+   Copyright (C) 2017-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 "json.h"
+#include "pretty-print.h"
+#include "math.h"
+#include "selftest.h"
+
+using namespace json;
+
+/* class json::value.  */
+
+/* Dump this json::value tree to OUTF.
+   No formatting is done.  There are no guarantees about the order
+   in which the key/value pairs of json::objects are printed.  */
+
+void
+value::dump (FILE *outf) const
+{
+  pretty_printer pp;
+  pp_buffer (&pp)->stream = outf;
+  print (&pp);
+  pp_flush (&pp);
+}
+
+/* class json::object, a subclass of json::value, representing
+   an unordered collection of key/value pairs.  */
+
+/* json:object's dtor.  */
+
+object::~object ()
+{
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      free (const_cast <char *>((*it).first));
+      delete ((*it).second);
+    }
+}
+
+/* Implementation of json::value::print for json::object.  */
+
+void
+object::print (pretty_printer *pp) const
+{
+  /* Note that the order is not guaranteed.  */
+  pp_character (pp, '{');
+  for (map_t::iterator it = m_map.begin (); it != m_map.end (); ++it)
+    {
+      if (it != m_map.begin ())
+	pp_string (pp, ", ");
+      const char *key = const_cast <char *>((*it).first);
+      value *value = (*it).second;
+      pp_printf (pp, "\"%s\": ", key); // FIXME: escaping?
+      value->print (pp);
+    }
+  pp_character (pp, '}');
+}
+
+/* Set the json::value * for KEY, taking ownership of VALUE
+   (and taking a copy of KEY if necessary).  */
+
+void
+object::set (const char *key, value *v)
+{
+  value **ptr = m_map.get (key);
+  if (ptr)
+    {
+      /* If the key is already present, delete the existing value
+	 and overwrite it.  */
+      delete *ptr;
+      *ptr = v;
+    }
+  else
+    /* If the key wasn't already present, take a copy of the key,
+       and store the value.  */
+    m_map.put (xstrdup (key), v);
+}
+
+/* class json::array, a subclass of json::value, representing
+   an ordered collection of values.  */
+
+/* json::array's dtor.  */
+
+array::~array ()
+{
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    delete v;
+}
+
+/* Implementation of json::value::print for json::array.  */
+
+void
+array::print (pretty_printer *pp) const
+{
+  pp_character (pp, '[');
+  unsigned i;
+  value *v;
+  FOR_EACH_VEC_ELT (m_elements, i, v)
+    {
+      if (i)
+	pp_string (pp, ", ");
+      v->print (pp);
+    }
+  pp_character (pp, ']');
+}
+
+/* class json::number, a subclass of json::value, wrapping a double.  */
+
+/* Implementation of json::value::print for json::number.  */
+
+void
+number::print (pretty_printer *pp) const
+{
+  char tmp[1024];
+  snprintf (tmp, sizeof (tmp), "%g", m_value);
+  pp_string (pp, tmp);
+}
+
+/* class json::string, a subclass of json::value.  */
+
+void
+string::print (pretty_printer *pp) const
+{
+  pp_character (pp, '"');
+  for (const char *ptr = m_utf8; *ptr; ptr++)
+    {
+      char ch = *ptr;
+      switch (ch)
+	{
+	case '"':
+	  pp_string (pp, "\\\"");
+	  break;
+	case '\\':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\b':
+	  pp_string (pp, "\\b");
+	  break;
+	case '\f':
+	  pp_string (pp, "\\f");
+	  break;
+	case '\n':
+	  pp_string (pp, "\\n");
+	  break;
+	case '\r':
+	  pp_string (pp, "\\r");
+	  break;
+	case '\t':
+	  pp_string (pp, "\\t");
+	  break;
+
+	default:
+	  pp_character (pp, ch);
+	}
+    }
+  pp_character (pp, '"');
+}
+
+/* class json::literal, a subclass of json::value.  */
+
+/* Implementation of json::value::print for json::literal.  */
+
+void
+literal::print (pretty_printer *pp) const
+{
+  switch (m_kind)
+    {
+    case JSON_TRUE:
+      pp_string (pp, "true");
+      break;
+    case JSON_FALSE:
+      pp_string (pp, "false");
+      break;
+    case JSON_NULL:
+      pp_string (pp, "null");
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Selftests.  */
+
+/* Verify that JV->print () prints EXPECTED_JSON.  */
+
+static void
+assert_print_eq (const json::value &jv, const char *expected_json)
+{
+  pretty_printer pp;
+  jv.print (&pp);
+  ASSERT_STREQ (expected_json, pp_formatted_text (&pp));
+}
+
+/* Verify that JSON objects are written correctly.  We can't test more than
+   one key/value pair, as we don't impose a guaranteed ordering.  */
+
+static void
+test_writing_objects ()
+{
+  object obj;
+  obj.set ("foo", new json::string ("bar"));
+  assert_print_eq (obj, "{\"foo\": \"bar\"}");
+}
+
+/* Verify that JSON arrays are written correctly.  */
+
+static void
+test_writing_arrays ()
+{
+  array arr;
+  assert_print_eq (arr, "[]");
+
+  arr.append (new json::string ("foo"));
+  assert_print_eq (arr, "[\"foo\"]");
+
+  arr.append (new json::string ("bar"));
+  assert_print_eq (arr, "[\"foo\", \"bar\"]");
+}
+
+/* Verify that JSON numbers are written correctly.  */
+
+static void
+test_writing_numbers ()
+{
+  assert_print_eq (number (0), "0");
+  assert_print_eq (number (42), "42");
+  assert_print_eq (number (-100), "-100");
+}
+
+/* Verify that JSON strings are written correctly.  */
+
+static void
+test_writing_strings ()
+{
+  string foo ("foo");
+  assert_print_eq (foo, "\"foo\"");
+
+  string contains_quotes ("before \"quoted\" after");
+  assert_print_eq (contains_quotes, "\"before \\\"quoted\\\" after\"");
+}
+
+/* Verify that JSON strings are written correctly.  */
+
+static void
+test_writing_literals ()
+{
+  assert_print_eq (literal (JSON_TRUE), "true");
+  assert_print_eq (literal (JSON_FALSE), "false");
+  assert_print_eq (literal (JSON_NULL), "null");
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+json_cc_tests ()
+{
+  test_writing_objects ();
+  test_writing_arrays ();
+  test_writing_numbers ();
+  test_writing_strings ();
+  test_writing_literals ();
+}
+
+} // namespace selftest
+
+#endif /* #if CHECKING_P */
diff --git a/gcc/json.h b/gcc/json.h
new file mode 100644
index 0000000..5c3274c
--- /dev/null
+++ b/gcc/json.h
@@ -0,0 +1,166 @@ 
+/* JSON trees
+   Copyright (C) 2017-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_JSON_H
+#define GCC_JSON_H
+
+/* Implementation of JSON, a lightweight data-interchange format.
+
+   See http://www.json.org/
+   and http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf
+   and https://tools.ietf.org/html/rfc7159
+
+   Supports creating a DOM-like tree of json::value *, and then dumping
+   json::value * to text.  */
+
+namespace json
+{
+
+/* Forward decls of json::value and its subclasses (using indentation
+   to denote inheritance.  */
+
+class value;
+  class object;
+  class array;
+  class number;
+  class string;
+  class literal;
+
+/* An enum for discriminating the subclasses of json::value.  */
+
+enum kind
+{
+  /* class json::object.  */
+  JSON_OBJECT,
+
+  /* class json::array.  */
+  JSON_ARRAY,
+
+  /* class json::number.  */
+  JSON_NUMBER,
+
+  /* class json::string.  */
+  JSON_STRING,
+
+  /* class json::literal uses these three values to identify the
+     particular literal.  */
+  JSON_TRUE,
+  JSON_FALSE,
+  JSON_NULL
+};
+
+/* Base class of JSON value.  */
+
+class value
+{
+ public:
+  virtual ~value () {}
+  virtual enum kind get_kind () const = 0;
+  virtual void print (pretty_printer *pp) const = 0;
+
+  void dump (FILE *) const;
+};
+
+/* Subclass of value for objects: an unordered collection of
+   key/value pairs.  */
+
+class object : public value
+{
+ public:
+  ~object ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_OBJECT; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  void set (const char *key, value *v);
+
+ private:
+  typedef hash_map <char *, value *,
+    simple_hashmap_traits<nofree_string_hash, value *> > map_t;
+  map_t m_map;
+};
+
+/* Subclass of value for arrays.  */
+
+class array : public value
+{
+ public:
+  ~array ();
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_ARRAY; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  void append (value *v) { m_elements.safe_push (v); }
+
+ private:
+  auto_vec<value *> m_elements;
+};
+
+/* Subclass of value for numbers.  */
+
+class number : public value
+{
+ public:
+  number (double value) : m_value (value) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_NUMBER; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  double get () const { return m_value; }
+
+ private:
+  double m_value;
+};
+
+/* Subclass of value for strings.  */
+
+class string : public value
+{
+ public:
+  string (const char *utf8) : m_utf8 (xstrdup (utf8)) {}
+  ~string () { free (m_utf8); }
+
+  enum kind get_kind () const FINAL OVERRIDE { return JSON_STRING; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+  const char *get_string () const { return m_utf8; }
+
+ private:
+  char *m_utf8;
+};
+
+/* Subclass of value for the three JSON literals "true", "false",
+   and "null".  */
+
+class literal : public value
+{
+ public:
+  literal (enum kind kind) : m_kind (kind) {}
+
+  enum kind get_kind () const FINAL OVERRIDE { return m_kind; }
+  void print (pretty_printer *pp) const FINAL OVERRIDE;
+
+ private:
+  enum kind m_kind;
+};
+
+} // namespace json
+
+#endif  /* GCC_JSON_H  */
diff --git a/gcc/optinfo-emit-json.cc b/gcc/optinfo-emit-json.cc
new file mode 100644
index 0000000..bf1172a
--- /dev/null
+++ b/gcc/optinfo-emit-json.cc
@@ -0,0 +1,568 @@ 
+/* Emit optimization information as JSON files.
+   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 "diagnostic-core.h"
+
+#include "profile.h"
+#include "output.h"
+#include "tree-pass.h"
+
+#include "optinfo.h"
+#include "optinfo-emit-json.h"
+#include "json.h"
+#include "pretty-print.h"
+#include "tree-pretty-print.h"
+#include "gimple-pretty-print.h"
+#include "cgraph.h"
+
+#include "langhooks.h"
+#include "version.h"
+#include "context.h"
+#include "pass_manager.h"
+#include "selftest.h"
+#include "dump-context.h"
+
+/* A class for writing out optimization records in JSON format.  */
+
+class optrecord_json_writer
+{
+public:
+  optrecord_json_writer ();
+  ~optrecord_json_writer ();
+  void write () const;
+  void add_record (const optinfo *optinfo);
+  void pop_scope ();
+
+  void add_record (json::object *obj);
+  json::object *impl_location_to_json (dump_impl_location_t loc);
+  json::object *location_to_json (location_t loc);
+  json::object *profile_count_to_json (profile_count count);
+  json::string *get_id_value_for_pass (opt_pass *pass);
+  json::object *pass_to_json (opt_pass *pass);
+  json::value *inlining_chain_to_json (location_t loc);
+  json::object *optinfo_to_json (const optinfo *optinfo);
+  void add_pass_list (json::array *arr, opt_pass *pass);
+
+private:
+  /* The root value for the JSON file.
+     Currently the JSON values are stored in memory, and flushed when the
+     compiler exits.  It would probably be better to simply write out
+     the JSON as we go.  */
+  json::array *m_root_tuple;
+
+  /* The currently open scopes, for expressing nested optimization records.  */
+  vec<json::array *> m_scopes;
+};
+
+/* optrecord_json_writer's ctor.  Populate the top-level parts of the
+   in-memory JSON representation.  */
+
+optrecord_json_writer::optrecord_json_writer ()
+  : m_root_tuple (NULL), m_scopes ()
+{
+  m_root_tuple = new json::array ();
+
+  /* Populate with metadata; compare with toplev.c: print_version.  */
+  json::object *metadata = new json::object ();
+  m_root_tuple->append (metadata);
+  metadata->set ("format", new json::string ("1"));
+  json::object *generator = new json::object ();
+  metadata->set ("generator", generator);
+  generator->set ("name", new json::string (lang_hooks.name));
+  generator->set ("pkgversion", new json::string (pkgversion_string));
+  generator->set ("version", new json::string (version_string));
+  /* TARGET_NAME is passed in by the Makefile.  */
+  generator->set ("target", new json::string (TARGET_NAME));
+
+  /* TODO: capture command-line?
+     see gen_producer_string in dwarf2out.c (currently static).  */
+
+  /* TODO: capture "any plugins?" flag (or the plugins themselves).  */
+
+  json::array *passes = new json::array ();
+  m_root_tuple->append (passes);
+
+  /* Call add_pass_list for all of the pass lists.  */
+  {
+#define DEF_PASS_LIST(LIST) \
+    add_pass_list (passes, g->get_passes ()->LIST);
+    GCC_PASS_LISTS
+#undef DEF_PASS_LIST
+  }
+
+  json::array *records = new json::array ();
+  m_root_tuple->append (records);
+
+  m_scopes.safe_push (records);
+}
+
+/* optrecord_json_writer's ctor.
+   Delete the in-memory JSON representation.  */
+
+optrecord_json_writer::~optrecord_json_writer ()
+{
+  delete m_root_tuple;
+}
+
+/* Choose an appropriate filename, and write the saved records to it.  */
+
+void
+optrecord_json_writer::write () const
+{
+  char *filename = concat (dump_base_name, ".opt-record.json", NULL);
+  FILE *outfile = fopen (filename, "w");
+  if (outfile)
+    {
+      m_root_tuple->dump (outfile);
+      fclose (outfile);
+    }
+  else
+    error_at (UNKNOWN_LOCATION, "unable to write optimization records to %qs",
+	      filename); // FIXME: more info?
+  free (filename);
+}
+
+/* Add a record for OPTINFO to the queue of records to be written.  */
+
+void
+optrecord_json_writer::add_record (const optinfo *optinfo)
+{
+  json::object *obj = optinfo_to_json (optinfo);
+
+  add_record (obj);
+
+  /* Potentially push the scope.  */
+  if (optinfo->get_kind () == OPTINFO_KIND_SCOPE)
+    {
+      json::array *children = new json::array ();
+      obj->set ("children", children);
+      m_scopes.safe_push (children);
+    }
+}
+
+/* Private methods of optrecord_json_writer.  */
+
+/* Add record OBJ to the the innermost scope.  */
+
+void
+optrecord_json_writer::add_record (json::object *obj)
+{
+  /* Add to innermost scope.  */
+  gcc_assert (m_scopes.length () > 0);
+  m_scopes[m_scopes.length () - 1]->append (obj);
+}
+
+/* Pop the innermost scope.  */
+
+void
+optrecord_json_writer::pop_scope ()
+{
+  m_scopes.pop ();
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::impl_location_to_json (dump_impl_location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (loc.m_file));
+  obj->set ("line", new json::number (loc.m_line));
+  if (loc.m_function)
+    obj->set ("function", new json::string (loc.m_function));
+  return obj;
+}
+
+/* Create a JSON object representing LOC.  */
+
+json::object *
+optrecord_json_writer::location_to_json (location_t loc)
+{
+  json::object *obj = new json::object ();
+  obj->set ("file", new json::string (LOCATION_FILE (loc)));
+  obj->set ("line", new json::number (LOCATION_LINE (loc)));
+  obj->set ("column", new json::number (LOCATION_COLUMN (loc)));
+  return obj;
+}
+
+/* Create a JSON object representing COUNT.  */
+
+json::object *
+optrecord_json_writer::profile_count_to_json (profile_count count)
+{
+  json::object *obj = new json::object ();
+  obj->set ("value", new json::number (count.to_gcov_type ()));
+  obj->set ("quality",
+	    new json::string (profile_quality_as_string (count.quality ())));
+  return obj;
+}
+
+/* Get a string for use when referring to PASS in the saved optimization
+   records.  */
+
+json::string *
+optrecord_json_writer::get_id_value_for_pass (opt_pass *pass)
+{
+  pretty_printer pp;
+  /* this is host-dependent, but will be consistent for a given host.  */
+  pp_pointer (&pp, static_cast<void *> (pass));
+  return new json::string (pp_formatted_text (&pp));
+}
+
+/* Create a JSON object representing PASS.  */
+
+json::object *
+optrecord_json_writer::pass_to_json (opt_pass *pass)
+{
+  json::object *obj = new json::object ();
+  const char *type = NULL;
+  switch (pass->type)
+    {
+    default:
+      gcc_unreachable ();
+    case GIMPLE_PASS:
+      type = "gimple";
+      break;
+    case RTL_PASS:
+      type = "rtl";
+      break;
+    case SIMPLE_IPA_PASS:
+      type = "simple_ipa";
+      break;
+    case IPA_PASS:
+      type = "ipa";
+      break;
+    }
+  obj->set ("id", get_id_value_for_pass (pass));
+  obj->set ("type", new json::string (type));
+  obj->set ("name", new json::string (pass->name));
+  /* Represent the optgroup flags as an array.  */
+  {
+    json::array *optgroups = new json::array ();
+    obj->set ("optgroups", optgroups);
+    for (const kv_pair<optgroup_flags_t> *optgroup = optgroup_options;
+	 optgroup->name != NULL; optgroup++)
+      if (optgroup->value != OPTGROUP_ALL
+	  && (pass->optinfo_flags & optgroup->value))
+	optgroups->append (new json::string (optgroup->name));
+  }
+  obj->set ("num", new json::number (pass->static_pass_number));
+  return obj;
+}
+
+/* Create a JSON array for LOC representing the chain of inlining
+   locations.
+   Compare with lhd_print_error_function and cp_print_error_function.  */
+
+json::value *
+optrecord_json_writer::inlining_chain_to_json (location_t loc)
+{
+  json::array *array = new json::array ();
+
+  tree abstract_origin = LOCATION_BLOCK (loc);
+
+  while (abstract_origin)
+    {
+      location_t *locus;
+      tree block = abstract_origin;
+
+      locus = &BLOCK_SOURCE_LOCATION (block);
+      tree fndecl = NULL;
+      block = BLOCK_SUPERCONTEXT (block);
+      while (block && TREE_CODE (block) == BLOCK
+	     && BLOCK_ABSTRACT_ORIGIN (block))
+	{
+	  tree ao = BLOCK_ABSTRACT_ORIGIN (block);
+
+	  while (TREE_CODE (ao) == BLOCK
+		 && BLOCK_ABSTRACT_ORIGIN (ao)
+		 && BLOCK_ABSTRACT_ORIGIN (ao) != ao)
+	    ao = BLOCK_ABSTRACT_ORIGIN (ao);
+
+	  if (TREE_CODE (ao) == FUNCTION_DECL)
+	    {
+	      fndecl = ao;
+	      break;
+	    }
+	  else if (TREE_CODE (ao) != BLOCK)
+	    break;
+
+	  block = BLOCK_SUPERCONTEXT (block);
+	}
+      if (fndecl)
+	abstract_origin = block;
+      else
+	{
+	  while (block && TREE_CODE (block) == BLOCK)
+	    block = BLOCK_SUPERCONTEXT (block);
+
+	  if (block && TREE_CODE (block) == FUNCTION_DECL)
+	    fndecl = block;
+	  abstract_origin = NULL;
+	}
+      if (fndecl)
+	{
+	  json::object *obj = new json::object ();
+	  const char *printable_name
+	    = lang_hooks.decl_printable_name (fndecl, 2);
+	  obj->set ("fndecl", new json::string (printable_name));
+	  if (*locus != UNKNOWN_LOCATION)
+	    obj->set ("site", location_to_json (*locus));
+	  array->append (obj);
+	}
+    }
+
+  return array;
+}
+
+/* Create a JSON object representing OPTINFO.  */
+
+json::object *
+optrecord_json_writer::optinfo_to_json (const optinfo *optinfo)
+{
+  json::object *obj = new json::object ();
+
+  obj->set ("impl_location",
+	    impl_location_to_json (optinfo->get_impl_location ()));
+
+  const char *kind_str = optinfo_kind_to_string (optinfo->get_kind ());
+  obj->set ("kind", new json::string (kind_str));
+  json::array *message = new json::array ();
+  obj->set ("message", message);
+  for (unsigned i = 0; i < optinfo->num_items (); i++)
+    {
+      const optinfo_item *item = optinfo->get_item (i);
+      switch (item->get_kind ())
+	{
+	default:
+	  gcc_unreachable ();
+	case OPTINFO_ITEM_KIND_TEXT:
+	  {
+	    message->append (new json::string (item->get_text ()));
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_TREE:
+	  {
+	    json::object *json_item = new json::object ();
+	    json_item->set ("expr", new json::string (item->get_text ()));
+
+	    /* Capture any location for the node.  */
+	    if (item->get_location () != UNKNOWN_LOCATION)
+	      json_item->set ("location", location_to_json (item->get_location ()));
+
+	    message->append (json_item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_GIMPLE:
+	  {
+	    json::object *json_item = new json::object ();
+	    json_item->set ("stmt", new json::string (item->get_text ()));
+
+	    /* Capture any location for the stmt.  */
+	    if (item->get_location () != UNKNOWN_LOCATION)
+	      json_item->set ("location", location_to_json (item->get_location ()));
+
+	    message->append (json_item);
+	  }
+	  break;
+	case OPTINFO_ITEM_KIND_SYMTAB_NODE:
+	  {
+	    json::object *json_item = new json::object ();
+	    json_item->set ("symtab_node", new json::string (item->get_text ()));
+
+	    /* Capture any location for the node.  */
+	    if (item->get_location () != UNKNOWN_LOCATION)
+	      json_item->set ("location", location_to_json (item->get_location ()));
+	    message->append (json_item);
+	  }
+	  break;
+	}
+   }
+
+  if (optinfo->get_pass ())
+    obj->set ("pass", get_id_value_for_pass (optinfo->get_pass ()));
+
+  profile_count count = optinfo->get_count ();
+  if (count.initialized_p ())
+    obj->set ("count", profile_count_to_json (count));
+
+  /* Record any location, handling the case where of an UNKNOWN_LOCATION
+     within an inlined block.  */
+  location_t loc = optinfo->get_location_t ();
+  if (get_pure_location (line_table, loc) != UNKNOWN_LOCATION)
+    {
+      // TOOD: record the location (just caret for now)
+      // TODO: start/finish also?
+      obj->set ("location", location_to_json (loc));
+    }
+
+  if (current_function_decl)
+    {
+      const char *fnname = get_fnname_from_decl (current_function_decl);
+      obj->set ("function", new json::string (fnname));
+    }
+
+  if (loc != UNKNOWN_LOCATION)
+    obj->set ("inlining_chain", inlining_chain_to_json (loc));
+
+  return obj;
+}
+
+/* Add a json description of PASS and its siblings to ARR, recursing into
+   child passes (adding their descriptions within a "children" array).  */
+
+void
+optrecord_json_writer::add_pass_list (json::array *arr, opt_pass *pass)
+{
+  do
+    {
+      json::object *pass_obj = pass_to_json (pass);
+      arr->append (pass_obj);
+      if (pass->sub)
+	{
+	  json::array *sub = new json::array ();
+	  pass_obj->set ("children", sub);
+	  add_pass_list (sub, pass->sub);
+	}
+      pass = pass->next;
+    }
+  while (pass);
+}
+
+/* File-level interface to rest of compiler (to avoid exposing
+   class optrecord_json_writer outside of this file).  */
+
+static optrecord_json_writer *the_json_writer;
+
+/* Perform startup activity for -fsave-optimization-record.  */
+
+void
+optimization_records_start ()
+{
+  /* Bail if recording not enabled.  */
+  if (!flag_save_optimization_record)
+    return;
+
+  the_json_writer = new optrecord_json_writer ();
+}
+
+/* Perform cleanup activity for -fsave-optimization-record.
+
+   Currently, the file is written out here in one go, before cleaning
+   up.  */
+
+void
+optimization_records_finish ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->write ();
+
+  delete the_json_writer;
+  the_json_writer = NULL;
+}
+
+/* Did the user request optimization records to be written out?  */
+
+bool
+optimization_records_enabled_p ()
+{
+  return the_json_writer != NULL;
+}
+
+/* If optimization records were requested, then add a record for OPTINFO
+   to the queue of records to be written.  */
+
+void
+optimization_records_maybe_record_optinfo (const optinfo *optinfo)
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->add_record (optinfo);
+}
+
+/* Handling for the end of a dump scope for the
+   optimization records sink.  */
+
+void
+optimization_records_maybe_pop_dump_scope ()
+{
+  /* Bail if recording not enabled.  */
+  if (!the_json_writer)
+    return;
+
+  the_json_writer->pop_scope ();
+}
+
+#if CHECKING_P
+
+namespace selftest {
+
+/* Verify that we can build a JSON optimization record from dump_*
+   calls.  */
+
+static void
+test_building_json_from_dump_calls ()
+{
+  temp_dump_context tmp (true);
+  dump_location_t loc;
+  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->num_items (), 2);
+
+  optrecord_json_writer writer;
+  json::object *json_obj = writer.optinfo_to_json (info);
+  ASSERT_TRUE (json_obj != NULL);
+
+  /* Verify that the json is sane.  */
+  pretty_printer pp;
+  json_obj->print (&pp);
+  const char *json_str = pp_formatted_text (&pp);
+  ASSERT_STR_CONTAINS (json_str, "impl_location");
+  ASSERT_STR_CONTAINS (json_str, "\"kind\": \"note\"");
+  ASSERT_STR_CONTAINS (json_str,
+		       "\"message\": [\"test of tree: \", {\"expr\": \"0\"}]");
+  delete json_obj;
+}
+
+/* Run all of the selftests within this file.  */
+
+void
+optinfo_emit_json_cc_tests ()
+{
+  test_building_json_from_dump_calls ();
+}
+
+} // namespace selftest
+
+#endif /* CHECKING_P */
diff --git a/gcc/optinfo-emit-json.h b/gcc/optinfo-emit-json.h
new file mode 100644
index 0000000..3628d56
--- /dev/null
+++ b/gcc/optinfo-emit-json.h
@@ -0,0 +1,36 @@ 
+/* Emit optimization information as JSON files.
+   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_EMIT_JSON_H
+#define GCC_OPTINFO_EMIT_JSON_H
+
+class optinfo;
+struct opt_pass;
+
+extern void optimization_records_start ();
+extern void optimization_records_finish ();
+
+extern bool optimization_records_enabled_p ();
+
+extern void optimization_records_maybe_record_optinfo (const optinfo *);
+extern void optimization_records_maybe_pop_dump_scope ();
+
+
+#endif /* #ifndef GCC_OPTINFO_EMIT_JSON_H */
diff --git a/gcc/optinfo.cc b/gcc/optinfo.cc
index 6f224bc..93de9d9 100644
--- a/gcc/optinfo.cc
+++ b/gcc/optinfo.cc
@@ -27,6 +27,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "gimple.h"
 
 #include "optinfo.h"
+#include "optinfo-emit-json.h"
 #include "dump-context.h"
 #include "pretty-print.h"
 #include "gimple-pretty-print.h"
@@ -85,7 +86,8 @@  optinfo::~optinfo ()
 void
 optinfo::emit ()
 {
-  /* currently this is a no-op.  */
+  /* -fsave-optimization-record.  */
+  optimization_records_maybe_record_optinfo (this);
 }
 
 /* Update the optinfo's kind based on DUMP_KIND.  */
@@ -221,9 +223,8 @@  optinfo::add_dec (const wide_int_ref &wi, signop sgn)
 
 bool optinfo_enabled_p ()
 {
-  /* Currently no destinations are implemented, just a hook for
-     selftests.  */
-  return dump_context::get ().forcibly_enable_optinfo_p ();
+  return (dump_context::get ().forcibly_enable_optinfo_p ()
+	  || optimization_records_enabled_p ());
 }
 
 /* Return true if any of the active optinfo destinations make use
@@ -232,5 +233,5 @@  bool optinfo_enabled_p ()
 
 bool optinfo_wants_inlining_info_p ()
 {
-  return false;
+  return optimization_records_enabled_p ();
 }
diff --git a/gcc/optinfo.h b/gcc/optinfo.h
index 5bdb9eb..5f09022 100644
--- a/gcc/optinfo.h
+++ b/gcc/optinfo.h
@@ -25,12 +25,8 @@  along with GCC; see the file COPYING3.  If not see
    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
diff --git a/gcc/profile-count.c b/gcc/profile-count.c
index 3d411cf..6a17f5e 100644
--- a/gcc/profile-count.c
+++ b/gcc/profile-count.c
@@ -33,6 +33,34 @@  along with GCC; see the file COPYING3.  If not see
 #include "wide-int.h"
 #include "sreal.h"
 
+/* Get a string describing QUALITY.  */
+
+const char *
+profile_quality_as_string (enum profile_quality quality)
+{
+  switch (quality)
+    {
+    default:
+      gcc_unreachable ();
+    case profile_uninitialized:
+      return "uninitialized";
+    case profile_guessed_local:
+      return "guessed_local";
+    case profile_guessed_global0:
+      return "guessed_global0";
+    case profile_guessed_global0adjusted:
+      return "guessed_global0adjusted";
+    case profile_guessed:
+      return "guessed";
+    case profile_afdo:
+      return "afdo";
+    case profile_adjusted:
+      return "adjusted";
+    case profile_precise:
+      return "precise";
+    }
+}
+
 /* Dump THIS to F.  */
 
 void
diff --git a/gcc/profile-count.h b/gcc/profile-count.h
index c83fa3b..f4d0c340 100644
--- a/gcc/profile-count.h
+++ b/gcc/profile-count.h
@@ -59,6 +59,8 @@  enum profile_quality {
   profile_precise
 };
 
+extern const char *profile_quality_as_string (enum profile_quality);
+
 /* The base value for branch probability notes and edge probabilities.  */
 #define REG_BR_PROB_BASE  10000
 
@@ -721,6 +723,9 @@  public:
       return m_quality == profile_precise;
     }
 
+  /* Get the quality of the count.  */
+  enum profile_quality quality () const { return m_quality; }
+
   /* When merging basic blocks, the two different profile counts are unified.
      Return true if this can be done without losing info about profile.
      The only case we care about here is when first BB contains something
diff --git a/gcc/selftest-run-tests.c b/gcc/selftest-run-tests.c
index 7f4d6f3..5adb033 100644
--- a/gcc/selftest-run-tests.c
+++ b/gcc/selftest-run-tests.c
@@ -72,6 +72,8 @@  selftest::run_tests ()
   typed_splay_tree_c_tests ();
   unique_ptr_tests_cc_tests ();
   opt_proposer_c_tests ();
+  json_cc_tests ();
+  optinfo_emit_json_cc_tests ();
 
   /* Mid-level data structures.  */
   input_c_tests ();
diff --git a/gcc/selftest.h b/gcc/selftest.h
index 54fc488..ede7732 100644
--- a/gcc/selftest.h
+++ b/gcc/selftest.h
@@ -228,6 +228,8 @@  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 json_cc_tests ();
+extern void optinfo_emit_json_cc_tests ();
 extern void predict_c_tests ();
 extern void pretty_print_c_tests ();
 extern void read_rtl_function_c_tests ();
diff --git a/gcc/toplev.c b/gcc/toplev.c
index d108096..a047390 100644
--- a/gcc/toplev.c
+++ b/gcc/toplev.c
@@ -83,6 +83,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "tree-pass.h"
 #include "dumpfile.h"
 #include "ipa-fnsummary.h"
+#include "optinfo-emit-json.h"
 
 #if defined(DBX_DEBUGGING_INFO) || defined(XCOFF_DEBUGGING_INFO)
 #include "dbxout.h"
@@ -487,6 +488,8 @@  compile_file (void)
   if (lang_hooks.decls.post_compilation_parsing_cleanups)
     lang_hooks.decls.post_compilation_parsing_cleanups ();
 
+  optimization_records_finish ();
+
   if (seen_error ())
     return;
 
@@ -2048,6 +2051,8 @@  do_compile ()
 
       timevar_start (TV_PHASE_SETUP);
 
+      optimization_records_start ();
+
       /* This must be run always, because it is needed to compute the FP
 	 predefined macros, such as __LDBL_MAX__, for targets using non
 	 default FP formats.  */
diff --git a/gcc/tree-ssa-live.c b/gcc/tree-ssa-live.c
index 7447f7a..2623d9b 100644
--- a/gcc/tree-ssa-live.c
+++ b/gcc/tree-ssa-live.c
@@ -40,6 +40,7 @@  along with GCC; see the file COPYING3.  If not see
 #include "cfgloop.h"
 #include "stringpool.h"
 #include "attribs.h"
+#include "optinfo.h"
 
 static void verify_live_on_entry (tree_live_info_p);
 
@@ -552,7 +553,8 @@  remove_unused_scope_block_p (tree scope, bool in_ctor_dtor_block)
      ;
    /* When not generating debug info we can eliminate info on unused
       variables.  */
-   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE)
+   else if (!flag_auto_profile && debug_info_level == DINFO_LEVEL_NONE
+	    && !optinfo_wants_inlining_info_p ())
      {
        /* Even for -g0 don't prune outer scopes from artificial
 	  functions, otherwise diagnostics using tree_nonartificial_location